Torque3D/Engine/source/console/simManager.cpp
JeffR bfe3d4d02b Shifts handling of forest brush and item elements into standard simsets for consistency
Updates the forest editor tooling to utilize the new sets, and adjusts the creation of new Brushes in the forest editor to have user select a target module first.
This ensures all a module's brushes are grouped into the new ForestBrushGroup class which auto-registers into the ForestBrushSet, thus allowing modules to have their own sets of brushes that automatically hook into the editor workflow.
2022-04-07 18:19:13 -05:00

629 lines
16 KiB
C++

//-----------------------------------------------------------------------------
// 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);
}
}