1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/logic/logic_state_machine.h"
24 #include "nel/net/service.h"
27 using namespace NLMISC
;
28 using namespace NLNET
;
33 // test if a string is valid considering a filter and a motif
34 bool testNameWithFilter( sint8 filter
, string motif
, string varName
);
36 void xmlCheckNodeName (xmlNodePtr
&node
, const char *nodeName
)
39 if ( node
== NULL
|| ((const char*)node
->name
== NULL
) || (strcmp ((const char*)node
->name
, nodeName
) != 0) )
42 // try to find a child
45 node
= CIXml::getFirstChildNode (node
, nodeName
);
46 if ( node
!= NULL
&& ((const char*)node
->name
!= NULL
) && (strcmp ((const char*)node
->name
, nodeName
) == 0) )
48 nlinfo ("check node %s ok in the child", nodeName
);
53 // Make an error message
55 smprintf (tmp
, 512, "LogicStateMachine STATE_MACHINE XML Syntax error in block line %d, node %s should be %s",
56 node
? (int)node
->line
:-1, node
? (const char *)node
->name
: "NULL", nodeName
);
60 throw EXmlParsingError (tmp
);
63 nlinfo ("check node %s ok", nodeName
);
66 std::string
getXMLProp (xmlNodePtr node
, const char *propName
)
68 const char *name
= (const char*)xmlGetProp (node
, (xmlChar
*)propName
);
71 nlinfo ("get prop %s = %s", propName
, name
);
73 xmlFree ((void*)name
);
78 // Make an error message
80 smprintf (tmp
, 512, "LogicStateMachine XML Syntax error in block %s line %d, aguments Name not found",
81 node
->name
, (int)node
->line
);
82 throw EXmlParsingError (tmp
);
88 //---------------------------------------------------
91 //---------------------------------------------------
92 void CLogicStateMachine::setCurrentState( string stateName
)
94 map
<string
,CLogicState
>::iterator itStates
= _States
.find( stateName
);
95 if( itStates
!= _States
.end() )
97 (*itStates
).second
.exitState();
99 _CurrentState
= stateName
;
101 (*itStates
).second
.enterState();
103 nlinfo("Switching to state \"%s\"",_CurrentState
.c_str());
107 nlwarning("(LOGIC)<CLogicStateMachine::setCurrentState> The state \"%s\" is not in the state machine \"%s\"",stateName
.c_str(),_Name
.c_str());
110 } // setCurrentState //
114 //---------------------------------------------------
117 //---------------------------------------------------
118 void CLogicStateMachine::addCondition( CLogicCondition condition
)
120 condition
.setLogicStateMachine(this);
121 _Conditions
.insert(make_pair(condition
.getName(),condition
));
127 //---------------------------------------------------
130 //---------------------------------------------------
131 void CLogicStateMachine::addState( CLogicState logicState
)
133 logicState
.setLogicStateMachine( this );
134 _States
.insert( std::make_pair(logicState
.getName(),logicState
) );
141 //---------------------------------------------------
144 //---------------------------------------------------
145 void CLogicStateMachine::addSIdMap( const TSIdMap
& sIdMap
)
147 // call addSIdMap for each state
148 map
<string
,CLogicState
>::iterator itStates
;
149 for( itStates
= _States
.begin(); itStates
!= _States
.end(); ++itStates
)
151 (*itStates
).second
.addSIdMap( sIdMap
);
158 //---------------------------------------------------
161 //---------------------------------------------------
162 void CLogicStateMachine::processLogic()
164 // call processLogic for the current state
165 map
<string
,CLogicState
>::iterator itStates
= _States
.find( _CurrentState
);
166 nlassert( itStates
!= _States
.end() );
167 (*itStates
).second
.processLogic();
169 // update the counters
170 map
<string
,CLogicCounter
>::iterator itCount
;
171 for( itCount
= _Counters
.begin(); itCount
!= _Counters
.end(); ++itCount
)
173 (*itCount
).second
.update();
180 //---------------------------------------------------
181 // getMessagesToSend :
183 //---------------------------------------------------
184 void CLogicStateMachine::getMessagesToSend( multimap
<CEntityId
,CMessage
>& msgs
)
186 map
<std::string
, CLogicState
>::iterator itState
;
187 for( itState
= _States
.begin(); itState
!= _States
.end(); ++itState
)
189 (*itState
).second
.getMessagesToSend( msgs
);
192 } // getMessagesToSend //
197 //---------------------------------------------------
200 //---------------------------------------------------
201 bool CLogicStateMachine::getVariable( std::string
& varName
, CLogicVariable
& var
)
203 map
<string
,CLogicVariable
>::iterator itVar
= _Variables
.find( varName
);
204 if( itVar
!= _Variables
.end() )
206 var
= (*itVar
).second
;
210 map
<string
,CLogicCounter
>::iterator itCount
= _Counters
.find( varName
);
211 if( itCount
!= _Counters
.end() )
213 var
= (*itCount
).second
;
223 //---------------------------------------------------
226 //---------------------------------------------------
227 bool CLogicStateMachine::getCondition( const std::string
& condName
, CLogicCondition
& cond
)
229 map
<string
,CLogicCondition
>::iterator itCond
= _Conditions
.find( condName
);
230 if( itCond
!= _Conditions
.end() )
232 cond
= (*itCond
).second
;
243 //---------------------------------------------------
246 //---------------------------------------------------
247 void CLogicStateMachine::modifyVariable( string varName
, string modifOperator
, sint64 value
)
249 map
<string
,CLogicVariable
>::iterator itVar
= _Variables
.find( varName
);
250 if( itVar
!= _Variables
.end() )
252 (*itVar
).second
.applyModification( modifOperator
, value
);
255 map
<string
,CLogicCounter
>::iterator itCount
= _Counters
.find( varName
);
256 if( itCount
!= _Counters
.end() )
258 (*itCount
).second
.applyModification( modifOperator
, value
);
262 nlwarning("(LOGIC)<CLogicStateMachine::modifyVariable> The variable \"%s\" is not in the state machine \"%s\"",varName
.c_str(),_Name
.c_str());
264 } // modifyVariable //
268 //---------------------------------------------------
271 //---------------------------------------------------
272 /*void CLogicStateMachine::serial( IStream &f )
274 f.xmlPush("STATE_MACHINE");
277 f.serialCont( _Variables );
278 f.serialCont( _Counters );
279 f.serialCont( _Conditions );
280 f.serialCont( _States );
281 f.serial( _CurrentState );
286 // set the logic state machine addr in each state
287 map<string,CLogicState>::iterator itStates;
288 for( itStates = _States.begin(); itStates != _States.end(); ++itStates )
290 (*itStates).second.setLogicStateMachine( this );
293 // set the logic state machine addr in each conditions
294 map<string,CLogicCondition>::iterator itCond;
295 for( itCond = _Conditions.begin(); itCond != _Conditions.end(); ++itCond )
297 (*itCond).second.setLogicStateMachine( this );
306 //---------------------------------------------------
307 // Display the variables
309 //---------------------------------------------------
310 void CLogicStateMachine::displayVariables()
312 multimap
<CEntityId
,string
> allVariables
;
314 // // get vars referenced in the states
315 map
<string
, CLogicState
>::iterator itS
;
316 for( itS
= _States
.begin(); itS
!= _States
.end(); ++itS
)
318 (*itS
).second
.fillVarMap( allVariables
);
321 // extract the unclaimed variables from all the variables
322 vector
<string
> unclaimedVariables
;
324 unknown
.setType( 0xfe );
325 unknown
.setCreatorId( 0 );
326 unknown
.setDynamicId( 0 );
327 pair
<multimap
<CEntityId
,string
>::iterator
,multimap
<CEntityId
,string
>::iterator
> itVarsRng
= allVariables
.equal_range(unknown
);
328 multimap
<CEntityId
,string
>::iterator itVars
;
330 for( itVars
= itVarsRng
.first
; itVars
!= itVarsRng
.second
; )
332 multimap
<CEntityId
,string
>::iterator itDel
= itVars
++;
333 unclaimedVariables
.push_back( (*itDel
).second
);
334 allVariables
.erase( itDel
);
337 if( itVarsRng.first != allVariables.end() )
339 itVars = itVarsRng.first;
342 multimap<CEntityId,string>::iterator itDel = itVars++;
343 unclaimedVariables.push_back( (*itDel).second );
344 allVariables.erase( itDel );
346 while( itVars != itVarsRng.second );
351 nlinfo("VARIABLES/COUNTERS in %s : %d/%d are registered : ",_Name
.c_str(),allVariables
.size(),allVariables
.size()+unclaimedVariables
.size());
352 // display the registered variables
353 for( itVars
= allVariables
.begin(); itVars
!= allVariables
.end(); ++itVars
)
355 map
<string
, CLogicVariable
>::const_iterator itV
= _Variables
.find( (*itVars
).second
);
356 nlinfo("[%d] %s = %f",(uint8
)(*itVars
).first
.getDynamicId(),(*itV
).first
.c_str(),(double)(*itV
).second
.getValue());
359 // display the unclaimed variables
360 sort( unclaimedVariables
.begin(), unclaimedVariables
.end() );
361 vector
<string
>::iterator itUV
;
362 for( itUV
= unclaimedVariables
.begin(); itUV
!= unclaimedVariables
.end(); ++itUV
)
364 map
<string
, CLogicVariable
>::const_iterator itV
= _Variables
.find( *itUV
);
365 nlinfo("(-)%s = %f",(*itV
).first
.c_str(),(double)(*itV
).second
.getValue());
368 } // displayVariables //
371 //---------------------------------------------------
372 // Display the states
374 //---------------------------------------------------
375 void CLogicStateMachine::displayStates()
377 nlinfo("There are %d STATES in the state machine \"%s\": ",_States
.size(),_Name
.c_str());
378 map
<string
, CLogicState
>::const_iterator itS
;
379 for( itS
= _States
.begin(); itS
!= _States
.end(); ++itS
)
381 nlinfo("%s",(*itS
).first
.c_str());
383 nlinfo("The current state is : \"%s\"",_CurrentState
.c_str());
385 } // displayStates //
388 //---------------------------------------------------
389 // Set the verbose mode for the variable
391 //---------------------------------------------------
392 void CLogicStateMachine::setVerbose( string varName
, bool b
)
394 if( varName
== "all" )
396 map
<string
, CLogicVariable
>::iterator itV
;
397 for( itV
= _Variables
.begin(); itV
!= _Variables
.end(); ++itV
)
399 (*itV
).second
.setVerbose( b
);
401 map
<string
, CLogicCounter
>::iterator itC
;
402 for( itC
= _Counters
.begin(); itC
!= _Counters
.end(); ++itC
)
404 (*itC
).second
.setVerbose( b
);
408 nlinfo("the verbose mode has been activated for all the variables",varName
.c_str());
412 nlinfo("the verbose mode has been desactivated for all the variables",varName
.c_str());
419 // *xxx* => we look for a string with xxx inside
420 if( varName
[0]=='*' && varName
[varName
.size()-1]=='*')
422 motif
= varName
.substr(1,varName
.size()-2);
426 // *xxx => we look for a string with xxx at the end
427 if( varName
[0]=='*' )
429 motif
= varName
.substr(1,varName
.size()-1);
433 // xxx* => we look for a string with xxx at the beginning
434 if( varName
[varName
.size()-1]=='*' )
436 motif
= varName
.substr(0,varName
.size()-1);
443 map
<string
, CLogicVariable
>::iterator itV
= _Variables
.find( varName
);
444 if( itV
!= _Variables
.end() )
446 (*itV
).second
.setVerbose( b
);
448 map
<string
, CLogicCounter
>::iterator itC
= _Counters
.find( varName
);
449 if( itC
!= _Counters
.end() || varName
=="all" )
451 (*itC
).second
.setVerbose( b
);
457 map
<string
, CLogicVariable
>::iterator itV
;
458 for( itV
= _Variables
.begin(); itV
!= _Variables
.end(); ++itV
)
460 if( testNameWithFilter( filter
, motif
,(*itV
).second
.getName()) )
462 (*itV
).second
.setVerbose( b
);
465 map
<string
, CLogicCounter
>::iterator itC
;
466 for( itC
= _Counters
.begin(); itC
!= _Counters
.end(); ++itC
)
468 if( testNameWithFilter( filter
, motif
,(*itC
).second
.getName()) )
470 (*itC
).second
.setVerbose( b
);
476 nlinfo("the verbose mode for variable \"%s\" has been activated",varName
.c_str());
480 nlinfo("the verbose mode for variable \"%s\" has been desactivated",varName
.c_str());
487 //---------------------------------------------------
488 // testNameWithFilter :
490 //---------------------------------------------------
491 bool testNameWithFilter( sint8 filter
, string motif
, string varName
)
493 if( varName
.size() > motif
.size() )
500 if(varName
.find(motif
) != string::npos
)
510 sint beginIndex
= (sint
)(varName
.size() - motif
.size() - 1);
511 string endOfVarName
= varName
.substr(beginIndex
,motif
.size());
512 if( endOfVarName
== motif
)
522 string beginOfVarName
= varName
.substr(0,motif
.size());
523 if( beginOfVarName
== motif
)
534 } // testNameWithFilter //
536 void CLogicStateMachine::write (xmlDocPtr doc
) const
538 // Create the first node
539 xmlNodePtr node
= xmlNewDocNode (doc
, NULL
, (const xmlChar
*)"STATE_MACHINE", NULL
);
540 xmlDocSetRootElement (doc
, node
);
541 xmlSetProp (node
, (const xmlChar
*)"Name", (const xmlChar
*)_Name
.c_str());
542 xmlSetProp (node
, (const xmlChar
*)"CurrentState", (const xmlChar
*)_CurrentState
.c_str());
544 for (std::map
<std::string
, CLogicVariable
>::const_iterator vit
= _Variables
.begin(); vit
!= _Variables
.end(); vit
++)
546 (*vit
).second
.write(node
);
549 for (std::map
<std::string
, CLogicCounter
>::const_iterator cit
= _Counters
.begin(); cit
!= _Counters
.end(); cit
++)
551 (*cit
).second
.write(node
);
554 for (std::map
<std::string
, CLogicCondition
>::const_iterator c2it
= _Conditions
.begin(); c2it
!= _Conditions
.end(); c2it
++)
556 (*c2it
).second
.write(node
);
559 for (std::map
<std::string
, CLogicState
>::const_iterator sit
= _States
.begin(); sit
!= _States
.end(); sit
++)
561 (*sit
).second
.write(node
);
565 void CLogicStateMachine::read (xmlNodePtr node
)
567 xmlCheckNodeName (node
, "STATE_MACHINE");
569 setName (getXMLProp (node
, "Name"));
573 uint nb
= CIXml::countChildren (node
, "VARIABLE");
575 xmlNodePtr parent
= CIXml::getFirstChildNode (node
, "VARIABLE");
580 _Variables
.insert (make_pair(v
.getName(), v
));
583 parent
= CIXml::getNextChildNode (parent
, "VARIABLE");
590 uint nb
= CIXml::countChildren (node
, "COUNTER");
592 xmlNodePtr parent
= CIXml::getFirstChildNode (node
, "COUNTER");
597 _Counters
.insert (make_pair(v
.getName(), v
));
600 parent
= CIXml::getNextChildNode (parent
, "COUNTER");
607 uint nb
= CIXml::countChildren (node
, "CONDITION");
609 xmlNodePtr parent
= CIXml::getFirstChildNode (node
, "CONDITION");
614 _Conditions
.insert (make_pair(v
.getName(), v
));
617 parent
= CIXml::getNextChildNode (parent
, "CONDITION");
624 uint nb
= CIXml::countChildren (node
, "STATE");
626 xmlNodePtr parent
= CIXml::getFirstChildNode (node
, "STATE");
631 _States
.insert (make_pair(v
.getName(), v
));
634 parent
= CIXml::getNextChildNode (parent
, "STATE");
639 setCurrentState (getXMLProp (node
, "CurrentState"));