| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 | //-----------------------------------------------------------------------------// Copyright (c) 2012 GarageGames, LLC//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to// deal in the Software without restriction, including without limitation the// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or// sell copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS// IN THE SOFTWARE.//-----------------------------------------------------------------------------#include "platform/platform.h"#include "platform/threads/mutex.h"#include "console/simBase.h"#include "console/simPersistID.h"#include "core/stringTable.h"#include "console/console.h"#include "core/stream/fileStream.h"#include "core/fileObject.h"#include "console/consoleInternal.h"#include "console/engineAPI.h"#include "core/idGenerator.h"#include "core/util/safeDelete.h"#include "platform/platformIntrinsics.h"#include "platform/profiler.h"#include "math/mMathFn.h"extern ExprEvalState gEvalState;//---------------------------------------------------------------------------//---------------------------------------------------------------------------// We comment out the implementation of the Con namespace when doxygenizing because// otherwise Doxygen decides to ignore our docs in console.h#ifndef DOXYGENIZINGnamespace Sim{//---------------------------------------------------------------------------//---------------------------------------------------------------------------// event queue variables:SimTime gCurrentTime;SimTime gTargetTime;void *gEventQueueMutex;SimEvent *gEventQueue;U32 gEventSequence;//---------------------------------------------------------------------------// event queue init/shutdownstatic void initEventQueue(){   gCurrentTime = 0;   gTargetTime = 0;   gEventSequence = 1;   gEventQueue = NULL;   gEventQueueMutex = Mutex::createMutex();}static void shutdownEventQueue(){   // Delete all pending events   Mutex::lockMutex(gEventQueueMutex);   SimEvent *walk = gEventQueue;   while(walk)   {      SimEvent *temp = walk->nextEvent;      delete walk;      walk = temp;   }   Mutex::unlockMutex(gEventQueueMutex);   Mutex::destroyMutex(gEventQueueMutex);}//---------------------------------------------------------------------------// event postU32 postEvent(SimObject *destObject, SimEvent* event,U32 time){   AssertFatal(time == -1 || time >= getCurrentTime(),      "Sim::postEvent() - Event time must be greater than or equal to the current time." );   AssertFatal(destObject, "Sim::postEvent() - Destination object for event doesn't exist.");   Mutex::lockMutex(gEventQueueMutex);   if( time == -1 )      time = gCurrentTime;   event->time = time;   event->startTime = gCurrentTime;   event->destObject = destObject;   if(!destObject)   {      delete event;      Mutex::unlockMutex(gEventQueueMutex);      return InvalidEventId;   }   event->sequenceCount = gEventSequence++;   SimEvent **walk = &gEventQueue;   SimEvent *current;      while((current = *walk) != NULL && (current->time < event->time))      walk = &(current->nextEvent);      // [tom, 6/24/2005] This ensures that SimEvents are dispatched in the same order that they are posted.   // This is needed to ensure Con::threadSafeExecute() executes script code in the correct order.   while((current = *walk) != NULL && (current->time == event->time))      walk = &(current->nextEvent);      event->nextEvent = current;   *walk = event;   U32 seqCount = event->sequenceCount;   Mutex::unlockMutex(gEventQueueMutex);   return seqCount;}//---------------------------------------------------------------------------// event cancellationvoid cancelEvent(U32 eventSequence){   Mutex::lockMutex(gEventQueueMutex);   SimEvent **walk = &gEventQueue;   SimEvent *current;      while((current = *walk) != NULL)   {      if(current->sequenceCount == eventSequence)      {         *walk = current->nextEvent;         delete current;         Mutex::unlockMutex(gEventQueueMutex);         return;      }      else         walk = &(current->nextEvent);   }   Mutex::unlockMutex(gEventQueueMutex);}void cancelPendingEvents(SimObject *obj){   Mutex::lockMutex(gEventQueueMutex);   SimEvent **walk = &gEventQueue;   SimEvent *current;      while((current = *walk) != NULL)   {      if(current->destObject == obj)      {         *walk = current->nextEvent;         delete current;      }      else         walk = &(current->nextEvent);   }   Mutex::unlockMutex(gEventQueueMutex);}//---------------------------------------------------------------------------// event pending testbool isEventPending(U32 eventSequence){   Mutex::lockMutex(gEventQueueMutex);   for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)      if(walk->sequenceCount == eventSequence)      {         Mutex::unlockMutex(gEventQueueMutex);         return true;      }   Mutex::unlockMutex(gEventQueueMutex);   return false;}U32 getEventTimeLeft(U32 eventSequence){   Mutex::lockMutex(gEventQueueMutex);   for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)      if(walk->sequenceCount == eventSequence)      {         SimTime t = walk->time - getCurrentTime();         Mutex::unlockMutex(gEventQueueMutex);         return t;      }   Mutex::unlockMutex(gEventQueueMutex);   return 0;   }U32 getScheduleDuration(U32 eventSequence){   for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)      if(walk->sequenceCount == eventSequence)         return (walk->time-walk->startTime);   return 0;}U32 getTimeSinceStart(U32 eventSequence){   for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent)      if(walk->sequenceCount == eventSequence)         return (getCurrentTime()-walk->startTime);   return 0;}//---------------------------------------------------------------------------// event timingvoid advanceToTime(SimTime targetTime){   AssertFatal(targetTime >= getCurrentTime(),       "Sim::advanceToTime() - Target time is less than the current time." );   Mutex::lockMutex(gEventQueueMutex);   gTargetTime = targetTime;   while(gEventQueue && gEventQueue->time <= targetTime)   {      SimEvent *event = gEventQueue;      gEventQueue = gEventQueue->nextEvent;      AssertFatal(event->time >= gCurrentTime,         "Sim::advanceToTime() - Event time is less than current time.");      gCurrentTime = event->time;      SimObject *obj = event->destObject;      if(!obj->isDeleted())         event->process(obj);      delete event;   }   gCurrentTime = targetTime;   Mutex::unlockMutex(gEventQueueMutex);}void advanceTime(SimTime delta){   advanceToTime(getCurrentTime() + delta);}U32 getCurrentTime(){   return dAtomicRead( gCurrentTime);}U32 getTargetTime(){   return dAtomicRead( gTargetTime );}//---------------------------------------------------------------------------//---------------------------------------------------------------------------SimGroup *gRootGroup = NULL;SimManagerNameDictionary *gNameDictionary;SimIdDictionary *gIdDictionary;U32 gNextObjectId;static void initRoot(){   gIdDictionary = new SimIdDictionary;   gNameDictionary = new SimManagerNameDictionary;   gRootGroup = new SimGroup();   gRootGroup->incRefCount();    gRootGroup->setId(RootGroupId);   gRootGroup->assignName("RootGroup");   gRootGroup->registerObject();   gNextObjectId = DynamicObjectIdFirst;}static void shutdownRoot(){   gRootGroup->decRefCount();   if( engineAPI::gUseConsoleInterop )      gRootGroup->deleteObject();   gRootGroup = NULL;   SAFE_DELETE(gNameDictionary);   SAFE_DELETE(gIdDictionary);}//---------------------------------------------------------------------------SimObject* findObject(const char* fileName, S32 declarationLine){   PROFILE_SCOPE(SimFindObjectByLine);   if (!fileName)      return NULL;   if (declarationLine < 0)      return NULL;   if (!gRootGroup)      return NULL;   return gRootGroup->findObjectByLineNumber(fileName, declarationLine, true);}SimObject* findObject(ConsoleValueRef &ref){   return findObject((const char*)ref);}SimObject* findObject(const char* name){   PROFILE_SCOPE(SimFindObject);   // Play nice with bad code - JDD   if( !name )      return NULL;   SimObject *obj;   char c = *name;   if (c == '%')   {      if (gEvalState.getStackDepth())      {         Dictionary::Entry* ent = gEvalState.getCurrentFrame().lookup(StringTable->insert(name));         if (ent)            return Sim::findObject(ent->getIntValue());      }   }   if(c == '/')      return gRootGroup->findObject(name + 1 );   if(c >= '0' && c <= '9')   {      // it's an id group      const char* temp = name + 1;      for(;;)      {         c = *temp++;         if(!c)            return findObject(dAtoi(name));         else if(c == '/')         {            obj = findObject(dAtoi(name));            if(!obj)               return NULL;            return obj->findObject(temp);         }         else if (c < '0' || c > '9')            return NULL;      }   }   S32 len;   for(len = 0; name[len] != 0 && name[len] != '/'; len++)      ;   StringTableEntry stName = StringTable->lookupn(name, len);   if(!stName)      return NULL;   obj = gNameDictionary->find(stName);   if(!name[len])      return obj;   if(!obj)      return NULL;   return obj->findObject(name + len + 1);}SimObject* findObject(SimObjectId id){   return gIdDictionary->find(id);}SimObject *spawnObject(String spawnClass, String spawnDataBlock, String spawnName,                       String spawnProperties, String spawnScript){   if (spawnClass.isEmpty())   {      Con::errorf("Unable to spawn an object without a spawnClass");      return NULL;   }   String spawnString;   spawnString += "$SpawnObject = new " + spawnClass + "(" + spawnName + ") { ";   if (spawnDataBlock.isNotEmpty() && !spawnDataBlock.equal( "None", String::NoCase ) )      spawnString += "datablock = " + spawnDataBlock + "; ";   if (spawnProperties.isNotEmpty())      spawnString += spawnProperties + " ";   spawnString += "};";   // Evaluate our spawn string   Con::evaluate(spawnString.c_str());   // Get our spawnObject id   const char* spawnObjectId = Con::getVariable("$SpawnObject");   // Get the actual spawnObject   SimObject* spawnObject = findObject(spawnObjectId);   // If we have a spawn script go ahead and execute it last   if (spawnScript.isNotEmpty())      Con::evaluate(spawnScript.c_str(), true);   return spawnObject;}SimGroup *getRootGroup(){   return gRootGroup;}String getUniqueName( const char *inName ){   String outName( inName );   if ( outName.isEmpty() )      return String::EmptyString;   SimObject *dummy;   if ( !Sim::findObject( outName, dummy ) )      return outName;   S32 suffixNumb = -1;   String nameStr( String::GetTrailingNumber( outName, suffixNumb ) );   suffixNumb = mAbs( suffixNumb ) + 1;   #define MAX_TRIES 100   for ( U32 i = 0; i < MAX_TRIES; i++ )   {         outName = String::ToString( "%s%d", nameStr.c_str(), suffixNumb );      if ( !Sim::findObject( outName, dummy ) )         return outName;               suffixNumb++;   }   Con::errorf( "Sim::getUniqueName( %s ) - failed after %d attempts", inName, MAX_TRIES );   return String::EmptyString;}String getUniqueInternalName( const char *inName, SimSet *inSet, bool searchChildren ){   // Since SimSet::findObjectByInternalName operates with StringTableEntry(s)    // we have to muck up the StringTable with our attempts.    // But then again, so does everywhere else.   StringTableEntry outName = StringTable->insert( inName );   if ( !outName || !outName[0] )         return String::EmptyString;   if ( !inSet->findObjectByInternalName( outName, searchChildren ) )         return String(outName);   S32 suffixNumb = -1;   String nameStr( String::GetTrailingNumber( outName, suffixNumb ) );   suffixNumb++;      static char tempStr[512];#define MAX_TRIES 100   for ( U32 i = 0; i < MAX_TRIES; i++ )   {         dSprintf( tempStr, 512, "%s%d", nameStr.c_str(), suffixNumb );      outName = StringTable->insert( tempStr );      if ( !inSet->findObjectByInternalName( outName, searchChildren ) )         return String(outName);               suffixNumb++;   }   Con::errorf( "Sim::getUniqueInternalName( %s ) - failed after %d attempts", inName, MAX_TRIES );   return String::EmptyString;}bool isValidObjectName( const char* name ){   if( !name || !name[ 0 ] )      return true; // Anonymous object.         if( !dIsalpha( name[ 0 ] ) && name[ 0 ] != '_' )      return false;         for( U32 i = 1; name[ i ]; ++ i )      if( !dIsalnum( name[ i ] ) && name[ i ] != '_' )         return false;            return true;}//---------------------------------------------------------------------------//---------------------------------------------------------------------------#define InstantiateNamedSet(set) g##set = new SimSet; g##set->registerObject(#set); g##set->setNameChangeAllowed(false); gRootGroup->addObject(g##set); SIMSET_SET_ASSOCIATION((*g##set))#define InstantiateNamedGroup(set) g##set = new SimGroup; g##set->registerObject(#set); g##set->setNameChangeAllowed(false); gRootGroup->addObject(g##set); SIMSET_SET_ASSOCIATION((*g##set))static bool sgIsShuttingDown;SimDataBlockGroup *gDataBlockGroup;SimDataBlockGroup *getDataBlockGroup(){   return gDataBlockGroup;}void init(){   initEventQueue();   initRoot();   InstantiateNamedSet(ActiveActionMapSet);   InstantiateNamedSet(GhostAlwaysSet);   InstantiateNamedSet(WayPointSet);   InstantiateNamedSet(fxReplicatorSet);   InstantiateNamedSet(fxFoliageSet);   InstantiateNamedSet(MaterialSet);   InstantiateNamedSet(SFXSourceSet);   InstantiateNamedSet(SFXDescriptionSet);   InstantiateNamedSet(SFXTrackSet);   InstantiateNamedSet(SFXEnvironmentSet);   InstantiateNamedSet(SFXStateSet);   InstantiateNamedSet(SFXAmbienceSet);   InstantiateNamedSet(TerrainMaterialSet);   InstantiateNamedSet(DataBlockSet);   InstantiateNamedGroup(ActionMapGroup);   InstantiateNamedGroup(ClientGroup);   InstantiateNamedGroup(GuiGroup);   InstantiateNamedGroup(GuiDataGroup);   InstantiateNamedGroup(TCPGroup);   InstantiateNamedGroup(ClientConnectionGroup);   InstantiateNamedGroup(SFXParameterGroup);   InstantiateNamedSet(BehaviorSet);   InstantiateNamedSet(sgMissionLightingFilterSet);   gDataBlockGroup = new SimDataBlockGroup();   gDataBlockGroup->registerObject("DataBlockGroup");   gRootGroup->addObject(gDataBlockGroup);      SimPersistID::init();}void shutdown(){   sgIsShuttingDown = true;      shutdownRoot();   shutdownEventQueue();      SimPersistID::shutdown();}bool isShuttingDown(){   return sgIsShuttingDown;}}#endif // DOXYGENIZING.SimDataBlockGroup::SimDataBlockGroup(){   mLastModifiedKey = 0;}S32 QSORT_CALLBACK SimDataBlockGroup::compareModifiedKey(const void* a,const void* b){   const SimDataBlock* dba = *((const SimDataBlock**)a);   const SimDataBlock* dbb = *((const SimDataBlock**)b);   return dba->getModifiedKey() - dbb->getModifiedKey();}void SimDataBlockGroup::sort(){   if(mLastModifiedKey != SimDataBlock::getNextModifiedKey())   {      mLastModifiedKey = SimDataBlock::getNextModifiedKey();      dQsort(objectList.address(),objectList.size(),sizeof(SimObject *),compareModifiedKey);   }}
 |