123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628 |
- //-----------------------------------------------------------------------------
- // 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 DOXYGENIZING
- namespace Sim
- {
- //---------------------------------------------------------------------------
- //---------------------------------------------------------------------------
- // event queue variables:
- SimTime gCurrentTime;
- SimTime gTargetTime;
- void *gEventQueueMutex;
- SimEvent *gEventQueue;
- U32 gEventSequence;
- //---------------------------------------------------------------------------
- // event queue init/shutdown
- static 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 post
- U32 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 ) // FIXME: a smart compiler will remove this check. - see http://garagegames.com/community/resources/view/19785 for a fix
- 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 cancellation
- void 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 test
- bool 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 timing
- void 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(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(const ConsoleValue &val)
- {
- if (val.getType() == ConsoleValueType::cvInteger)
- return findObject((SimObjectId)val.getFastInt());
- return findObject(val.getString());
- }
- SimObject* findObject(ConsoleValue* val)
- {
- if (val->getType() == ConsoleValueType::cvInteger)
- return findObject((SimObjectId)val->getFastInt());
- return findObject(val->getString());
- }
- 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);
- InstantiateNamedSet(ForestBrushSet);
- InstantiateNamedSet(ForestItemDataSet);
- 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(mObjectList.address(), mObjectList.size(),sizeof(SimObject *),compareModifiedKey);
- }
- }
|