simManager.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "platform/threads/mutex.h"
  24. #include "console/simBase.h"
  25. #include "console/simPersistID.h"
  26. #include "core/stringTable.h"
  27. #include "console/console.h"
  28. #include "core/stream/fileStream.h"
  29. #include "core/fileObject.h"
  30. #include "console/consoleInternal.h"
  31. #include "console/engineAPI.h"
  32. #include "core/idGenerator.h"
  33. #include "core/util/safeDelete.h"
  34. #include "platform/platformIntrinsics.h"
  35. #include "platform/profiler.h"
  36. #include "math/mMathFn.h"
  37. extern ExprEvalState gEvalState;
  38. //---------------------------------------------------------------------------
  39. //---------------------------------------------------------------------------
  40. // We comment out the implementation of the Con namespace when doxygenizing because
  41. // otherwise Doxygen decides to ignore our docs in console.h
  42. #ifndef DOXYGENIZING
  43. namespace Sim
  44. {
  45. //---------------------------------------------------------------------------
  46. //---------------------------------------------------------------------------
  47. // event queue variables:
  48. SimTime gCurrentTime;
  49. SimTime gTargetTime;
  50. void *gEventQueueMutex;
  51. SimEvent *gEventQueue;
  52. U32 gEventSequence;
  53. //---------------------------------------------------------------------------
  54. // event queue init/shutdown
  55. static void initEventQueue()
  56. {
  57. gCurrentTime = 0;
  58. gTargetTime = 0;
  59. gEventSequence = 1;
  60. gEventQueue = NULL;
  61. gEventQueueMutex = Mutex::createMutex();
  62. }
  63. static void shutdownEventQueue()
  64. {
  65. // Delete all pending events
  66. Mutex::lockMutex(gEventQueueMutex);
  67. SimEvent *walk = gEventQueue;
  68. while(walk)
  69. {
  70. SimEvent *temp = walk->nextEvent;
  71. delete walk;
  72. walk = temp;
  73. }
  74. Mutex::unlockMutex(gEventQueueMutex);
  75. Mutex::destroyMutex(gEventQueueMutex);
  76. }
  77. //---------------------------------------------------------------------------
  78. // event post
  79. U32 postEvent(SimObject *destObject, SimEvent* event,U32 time)
  80. {
  81. AssertFatal(time == -1 || time >= getCurrentTime(),
  82. "Sim::postEvent() - Event time must be greater than or equal to the current time." );
  83. AssertFatal(destObject, "Sim::postEvent() - Destination object for event doesn't exist.");
  84. Mutex::lockMutex(gEventQueueMutex);
  85. if( time == -1 ) // FIXME: a smart compiler will remove this check. - see http://garagegames.com/community/resources/view/19785 for a fix
  86. time = gCurrentTime;
  87. event->time = time;
  88. event->startTime = gCurrentTime;
  89. event->destObject = destObject;
  90. if(!destObject)
  91. {
  92. delete event;
  93. Mutex::unlockMutex(gEventQueueMutex);
  94. return InvalidEventId;
  95. }
  96. event->sequenceCount = gEventSequence++;
  97. SimEvent **walk = &gEventQueue;
  98. SimEvent *current;
  99. while((current = *walk) != NULL && (current->time < event->time))
  100. walk = &(current->nextEvent);
  101. // [tom, 6/24/2005] This ensures that SimEvents are dispatched in the same order that they are posted.
  102. // This is needed to ensure Con::threadSafeExecute() executes script code in the correct order.
  103. while((current = *walk) != NULL && (current->time == event->time))
  104. walk = &(current->nextEvent);
  105. event->nextEvent = current;
  106. *walk = event;
  107. U32 seqCount = event->sequenceCount;
  108. Mutex::unlockMutex(gEventQueueMutex);
  109. return seqCount;
  110. }
  111. //---------------------------------------------------------------------------
  112. // event cancellation
  113. void cancelEvent(U32 eventSequence)
  114. {
  115. Mutex::lockMutex(gEventQueueMutex);
  116. SimEvent **walk = &gEventQueue;
  117. SimEvent *current;
  118. while((current = *walk) != NULL)
  119. {
  120. if(current->sequenceCount == eventSequence)
  121. {
  122. *walk = current->nextEvent;
  123. delete current;
  124. Mutex::unlockMutex(gEventQueueMutex);
  125. return;
  126. }
  127. else
  128. walk = &(current->nextEvent);
  129. }
  130. Mutex::unlockMutex(gEventQueueMutex);
  131. }
  132. void cancelPendingEvents(SimObject *obj)
  133. {
  134. Mutex::lockMutex(gEventQueueMutex);
  135. SimEvent **walk = &gEventQueue;
  136. SimEvent *current;
  137. while((current = *walk) != NULL)
  138. {
  139. if(current->destObject == obj)
  140. {
  141. *walk = current->nextEvent;
  142. delete current;
  143. }
  144. else
  145. walk = &(current->nextEvent);
  146. }
  147. Mutex::unlockMutex(gEventQueueMutex);
  148. }
  149. //---------------------------------------------------------------------------
  150. // event pending test
  151. bool isEventPending(U32 eventSequence)
  152. {
  153. Mutex::lockMutex(gEventQueueMutex);
  154. for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
  155. if(walk->sequenceCount == eventSequence)
  156. {
  157. Mutex::unlockMutex(gEventQueueMutex);
  158. return true;
  159. }
  160. Mutex::unlockMutex(gEventQueueMutex);
  161. return false;
  162. }
  163. U32 getEventTimeLeft(U32 eventSequence)
  164. {
  165. Mutex::lockMutex(gEventQueueMutex);
  166. for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
  167. if(walk->sequenceCount == eventSequence)
  168. {
  169. SimTime t = walk->time - getCurrentTime();
  170. Mutex::unlockMutex(gEventQueueMutex);
  171. return t;
  172. }
  173. Mutex::unlockMutex(gEventQueueMutex);
  174. return 0;
  175. }
  176. U32 getScheduleDuration(U32 eventSequence)
  177. {
  178. for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
  179. if(walk->sequenceCount == eventSequence)
  180. return (walk->time-walk->startTime);
  181. return 0;
  182. }
  183. U32 getTimeSinceStart(U32 eventSequence)
  184. {
  185. for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)
  186. if(walk->sequenceCount == eventSequence)
  187. return (getCurrentTime()-walk->startTime);
  188. return 0;
  189. }
  190. //---------------------------------------------------------------------------
  191. // event timing
  192. void advanceToTime(SimTime targetTime)
  193. {
  194. AssertFatal(targetTime >= getCurrentTime(),
  195. "Sim::advanceToTime() - Target time is less than the current time." );
  196. Mutex::lockMutex(gEventQueueMutex);
  197. gTargetTime = targetTime;
  198. while(gEventQueue && gEventQueue->time <= targetTime)
  199. {
  200. SimEvent *event = gEventQueue;
  201. gEventQueue = gEventQueue->nextEvent;
  202. AssertFatal(event->time >= gCurrentTime,
  203. "Sim::advanceToTime() - Event time is less than current time.");
  204. gCurrentTime = event->time;
  205. SimObject *obj = event->destObject;
  206. if(!obj->isDeleted())
  207. event->process(obj);
  208. delete event;
  209. }
  210. gCurrentTime = targetTime;
  211. Mutex::unlockMutex(gEventQueueMutex);
  212. }
  213. void advanceTime(SimTime delta)
  214. {
  215. advanceToTime(getCurrentTime() + delta);
  216. }
  217. U32 getCurrentTime()
  218. {
  219. return dAtomicRead( gCurrentTime);
  220. }
  221. U32 getTargetTime()
  222. {
  223. return dAtomicRead( gTargetTime );
  224. }
  225. //---------------------------------------------------------------------------
  226. //---------------------------------------------------------------------------
  227. SimGroup *gRootGroup = NULL;
  228. SimManagerNameDictionary *gNameDictionary;
  229. SimIdDictionary *gIdDictionary;
  230. U32 gNextObjectId;
  231. static void initRoot()
  232. {
  233. gIdDictionary = new SimIdDictionary;
  234. gNameDictionary = new SimManagerNameDictionary;
  235. gRootGroup = new SimGroup();
  236. gRootGroup->incRefCount();
  237. gRootGroup->setId(RootGroupId);
  238. gRootGroup->assignName("RootGroup");
  239. gRootGroup->registerObject();
  240. gNextObjectId = DynamicObjectIdFirst;
  241. }
  242. static void shutdownRoot()
  243. {
  244. gRootGroup->decRefCount();
  245. if( engineAPI::gUseConsoleInterop )
  246. gRootGroup->deleteObject();
  247. gRootGroup = NULL;
  248. SAFE_DELETE(gNameDictionary);
  249. SAFE_DELETE(gIdDictionary);
  250. }
  251. //---------------------------------------------------------------------------
  252. SimObject* findObject(const char* fileName, S32 declarationLine)
  253. {
  254. PROFILE_SCOPE(SimFindObjectByLine);
  255. if (!fileName)
  256. return NULL;
  257. if (declarationLine < 0)
  258. return NULL;
  259. if (!gRootGroup)
  260. return NULL;
  261. return gRootGroup->findObjectByLineNumber(fileName, declarationLine, true);
  262. }
  263. SimObject* findObject(const char* name)
  264. {
  265. PROFILE_SCOPE(SimFindObject);
  266. // Play nice with bad code - JDD
  267. if( !name )
  268. return NULL;
  269. SimObject *obj;
  270. char c = *name;
  271. if (c == '%')
  272. {
  273. if (gEvalState.getStackDepth())
  274. {
  275. Dictionary::Entry* ent = gEvalState.getCurrentFrame().lookup(StringTable->insert(name));
  276. if (ent)
  277. return Sim::findObject(ent->getIntValue());
  278. }
  279. }
  280. if(c == '/')
  281. return gRootGroup->findObject(name + 1 );
  282. if(c >= '0' && c <= '9')
  283. {
  284. // it's an id group
  285. const char* temp = name + 1;
  286. for(;;)
  287. {
  288. c = *temp++;
  289. if(!c)
  290. return findObject(dAtoi(name));
  291. else if(c == '/')
  292. {
  293. obj = findObject(dAtoi(name));
  294. if(!obj)
  295. return NULL;
  296. return obj->findObject(temp);
  297. }
  298. else if (c < '0' || c > '9')
  299. return NULL;
  300. }
  301. }
  302. S32 len;
  303. for(len = 0; name[len] != 0 && name[len] != '/'; len++)
  304. ;
  305. StringTableEntry stName = StringTable->lookupn(name, len);
  306. if(!stName)
  307. return NULL;
  308. obj = gNameDictionary->find(stName);
  309. if(!name[len])
  310. return obj;
  311. if(!obj)
  312. return NULL;
  313. return obj->findObject(name + len + 1);
  314. }
  315. SimObject* findObject(const ConsoleValue &val)
  316. {
  317. if (val.getType() == ConsoleValueType::cvInteger)
  318. return findObject((SimObjectId)val.getFastInt());
  319. return findObject(val.getString());
  320. }
  321. SimObject* findObject(ConsoleValue* val)
  322. {
  323. if (val->getType() == ConsoleValueType::cvInteger)
  324. return findObject((SimObjectId)val->getFastInt());
  325. return findObject(val->getString());
  326. }
  327. SimObject* findObject(SimObjectId id)
  328. {
  329. return gIdDictionary->find(id);
  330. }
  331. SimObject *spawnObject(String spawnClass, String spawnDataBlock, String spawnName,
  332. String spawnProperties, String spawnScript)
  333. {
  334. if (spawnClass.isEmpty())
  335. {
  336. Con::errorf("Unable to spawn an object without a spawnClass");
  337. return NULL;
  338. }
  339. String spawnString;
  340. spawnString += "$SpawnObject = new " + spawnClass + "(" + spawnName + ") { ";
  341. if (spawnDataBlock.isNotEmpty() && !spawnDataBlock.equal( "None", String::NoCase ) )
  342. spawnString += "datablock = " + spawnDataBlock + "; ";
  343. if (spawnProperties.isNotEmpty())
  344. spawnString += spawnProperties + " ";
  345. spawnString += "};";
  346. // Evaluate our spawn string
  347. Con::evaluate(spawnString.c_str());
  348. // Get our spawnObject id
  349. const char* spawnObjectId = Con::getVariable("$SpawnObject");
  350. // Get the actual spawnObject
  351. SimObject* spawnObject = findObject(spawnObjectId);
  352. // If we have a spawn script go ahead and execute it last
  353. if (spawnScript.isNotEmpty())
  354. Con::evaluate(spawnScript.c_str(), true);
  355. return spawnObject;
  356. }
  357. SimGroup *getRootGroup()
  358. {
  359. return gRootGroup;
  360. }
  361. String getUniqueName( const char *inName )
  362. {
  363. String outName( inName );
  364. if ( outName.isEmpty() )
  365. return String::EmptyString;
  366. SimObject *dummy;
  367. if ( !Sim::findObject( outName, dummy ) )
  368. return outName;
  369. S32 suffixNumb = -1;
  370. String nameStr( String::GetTrailingNumber( outName, suffixNumb ) );
  371. suffixNumb = mAbs( suffixNumb ) + 1;
  372. #define MAX_TRIES 100
  373. for ( U32 i = 0; i < MAX_TRIES; i++ )
  374. {
  375. outName = String::ToString( "%s%d", nameStr.c_str(), suffixNumb );
  376. if ( !Sim::findObject( outName, dummy ) )
  377. return outName;
  378. suffixNumb++;
  379. }
  380. Con::errorf( "Sim::getUniqueName( %s ) - failed after %d attempts", inName, MAX_TRIES );
  381. return String::EmptyString;
  382. }
  383. String getUniqueInternalName( const char *inName, SimSet *inSet, bool searchChildren )
  384. {
  385. // Since SimSet::findObjectByInternalName operates with StringTableEntry(s)
  386. // we have to muck up the StringTable with our attempts.
  387. // But then again, so does everywhere else.
  388. StringTableEntry outName = StringTable->insert( inName );
  389. if ( !outName || !outName[0] )
  390. return String::EmptyString;
  391. if ( !inSet->findObjectByInternalName( outName, searchChildren ) )
  392. return String(outName);
  393. S32 suffixNumb = -1;
  394. String nameStr( String::GetTrailingNumber( outName, suffixNumb ) );
  395. suffixNumb++;
  396. static char tempStr[512];
  397. #define MAX_TRIES 100
  398. for ( U32 i = 0; i < MAX_TRIES; i++ )
  399. {
  400. dSprintf( tempStr, 512, "%s%d", nameStr.c_str(), suffixNumb );
  401. outName = StringTable->insert( tempStr );
  402. if ( !inSet->findObjectByInternalName( outName, searchChildren ) )
  403. return String(outName);
  404. suffixNumb++;
  405. }
  406. Con::errorf( "Sim::getUniqueInternalName( %s ) - failed after %d attempts", inName, MAX_TRIES );
  407. return String::EmptyString;
  408. }
  409. bool isValidObjectName( const char* name )
  410. {
  411. if( !name || !name[ 0 ] )
  412. return true; // Anonymous object.
  413. if( !dIsalpha( name[ 0 ] ) && name[ 0 ] != '_' )
  414. return false;
  415. for( U32 i = 1; name[ i ]; ++ i )
  416. if( !dIsalnum( name[ i ] ) && name[ i ] != '_' )
  417. return false;
  418. return true;
  419. }
  420. //---------------------------------------------------------------------------
  421. //---------------------------------------------------------------------------
  422. #define InstantiateNamedSet(set) g##set = new SimSet; g##set->registerObject(#set); g##set->setNameChangeAllowed(false); gRootGroup->addObject(g##set); SIMSET_SET_ASSOCIATION((*g##set))
  423. #define InstantiateNamedGroup(set) g##set = new SimGroup; g##set->registerObject(#set); g##set->setNameChangeAllowed(false); gRootGroup->addObject(g##set); SIMSET_SET_ASSOCIATION((*g##set))
  424. static bool sgIsShuttingDown;
  425. SimDataBlockGroup *gDataBlockGroup;
  426. SimDataBlockGroup *getDataBlockGroup()
  427. {
  428. return gDataBlockGroup;
  429. }
  430. void init()
  431. {
  432. initEventQueue();
  433. initRoot();
  434. InstantiateNamedSet(ActiveActionMapSet);
  435. InstantiateNamedSet(GhostAlwaysSet);
  436. InstantiateNamedSet(WayPointSet);
  437. InstantiateNamedSet(fxReplicatorSet);
  438. InstantiateNamedSet(fxFoliageSet);
  439. InstantiateNamedSet(MaterialSet);
  440. InstantiateNamedSet(SFXSourceSet);
  441. InstantiateNamedSet(SFXDescriptionSet);
  442. InstantiateNamedSet(SFXTrackSet);
  443. InstantiateNamedSet(SFXEnvironmentSet);
  444. InstantiateNamedSet(SFXStateSet);
  445. InstantiateNamedSet(SFXAmbienceSet);
  446. InstantiateNamedSet(TerrainMaterialSet);
  447. InstantiateNamedSet(DataBlockSet);
  448. InstantiateNamedSet(ForestBrushSet);
  449. InstantiateNamedSet(ForestItemDataSet);
  450. InstantiateNamedGroup(ActionMapGroup);
  451. InstantiateNamedGroup(ClientGroup);
  452. InstantiateNamedGroup(GuiGroup);
  453. InstantiateNamedGroup(GuiDataGroup);
  454. InstantiateNamedGroup(TCPGroup);
  455. InstantiateNamedGroup(ClientConnectionGroup);
  456. InstantiateNamedGroup(SFXParameterGroup);
  457. InstantiateNamedSet(BehaviorSet);
  458. InstantiateNamedSet(sgMissionLightingFilterSet);
  459. gDataBlockGroup = new SimDataBlockGroup();
  460. gDataBlockGroup->registerObject("DataBlockGroup");
  461. gRootGroup->addObject(gDataBlockGroup);
  462. SimPersistID::init();
  463. }
  464. void shutdown()
  465. {
  466. sgIsShuttingDown = true;
  467. shutdownRoot();
  468. shutdownEventQueue();
  469. SimPersistID::shutdown();
  470. }
  471. bool isShuttingDown()
  472. {
  473. return sgIsShuttingDown;
  474. }
  475. }
  476. #endif // DOXYGENIZING.
  477. SimDataBlockGroup::SimDataBlockGroup()
  478. {
  479. mLastModifiedKey = 0;
  480. }
  481. S32 QSORT_CALLBACK SimDataBlockGroup::compareModifiedKey(const void* a,const void* b)
  482. {
  483. const SimDataBlock* dba = *((const SimDataBlock**)a);
  484. const SimDataBlock* dbb = *((const SimDataBlock**)b);
  485. return dba->getModifiedKey() - dbb->getModifiedKey();
  486. }
  487. void SimDataBlockGroup::sort()
  488. {
  489. if(mLastModifiedKey != SimDataBlock::getNextModifiedKey())
  490. {
  491. mLastModifiedKey = SimDataBlock::getNextModifiedKey();
  492. dQsort(mObjectList.address(), mObjectList.size(),sizeof(SimObject *),compareModifiedKey);
  493. }
  494. }