Browse Source

Added field to ModuleDefinition for priority, which can be used to process/sort them in priority order
Added logic to ModuleManager's findModules method to allow priority sorting as well as pre-filtering by a given module group
Adjusts the %isFine argument for the onMapLoadFailed callback events to %canContinueOnFail for a bit more clarity on what the arg conveys
Shifts the setSpawnObjectType, setSpawnPoint and onPostSpawn call stack to utilize an event manager to allow the setup process for spawners and gamemode prepwork to run in it's own time, if needbe. Such as if a gamemode has to generate a map and there's no guarantees on when it'll b e done for one client vs another
Added getModulesAndGameModesList, callOnObjectList and getNumCanCallOnObjectList utility functions

JeffR 8 months ago
parent
commit
46f6f6a9da

+ 4 - 1
Engine/source/module/moduleDefinition.cpp

@@ -68,7 +68,8 @@ mModuleId(StringTable->EmptyString()),
     mLoadCount( 0 ),
     mScopeSet( 0 ),
     mLocked( false ),
-    mpModuleManager( NULL )
+    mpModuleManager( NULL ),
+    mPriority(0.0f)
 {
     // Set Vector Associations.
     VECTOR_SET_ASSOCIATION( mDependencies );
@@ -111,6 +112,8 @@ void ModuleDefinition::initPersistFields()
 
     /// Misc.
     addProtectedField( "Signature", TypeString, 0, &defaultProtectedNotSetFn, &getSignature, &defaultProtectedNotWriteFn, "A unique signature of the module definition based upon its Id, version and build.  This is read-only and is available only after the module has been registered by a module manager." );
+    addProtectedField( "Priority",  TypeF32, 0, &setPriority, &defaultProtectedGetFn, &defaultProtectedNotWriteFn, "A numeric value indicating execution priority for certain callback commands. 0 has the highest priority and is then sorted from there ascending in value.  This is read-only and is available only after the module has been registered by a module manager.");
+
 }
 
 //-----------------------------------------------------------------------------

+ 6 - 0
Engine/source/module/moduleDefinition.h

@@ -115,6 +115,7 @@ private:
     SimObjectId                     mScopeSet;
     bool                            mLocked;
     ModuleManager*                  mpModuleManager;
+    F32                             mPriority;
 
 private:
     inline bool             checkUnlocked( void ) const { if ( mLocked )        { Con::warnf("Ignoring changes for locked module definition."); } return !mLocked; }
@@ -195,6 +196,9 @@ public:
     inline bool             getModuleLocked( void ) const                       { return mLocked; }
     inline ModuleManager*   getModuleManager( void ) const                      { return mpModuleManager; }
 
+    inline void             setPriority(const F32 pPriority)                    { if (checkUnlocked()) { mPriority = pPriority; } }
+    inline F32              getPriority(void) const                             { return mPriority; }
+
     using Parent::save;
     bool                    save( void );
 
@@ -332,6 +336,8 @@ protected:
     }
     static bool             writeDependencies( void* obj, StringTableEntry pFieldName ) { return static_cast<ModuleDefinition*>(obj)->getDependencies().size() > 0; }
     static const char*      getSignature(void* obj, const char* data)                   { return static_cast<ModuleDefinition*>(obj)->getSignature(); }
+
+    static bool             setPriority(void* obj, const char* index, const char* data) { static_cast<ModuleDefinition*>(obj)->setPriority((F32)dAtof(data)); return false; }
 };
 
 #endif // _MODULE_DEFINITION_H

+ 18 - 1
Engine/source/module/moduleManager_ScriptBinding.h

@@ -150,8 +150,14 @@ DefineEngineMethod(ModuleManager, findModuleByFilePath, String, (const char* fil
 }
 
 //-----------------------------------------------------------------------------
+static S32 QSORT_CALLBACK _findModulesSortByPriority(ModuleDefinition* const* a, ModuleDefinition* const* b)
+{
+   F32 diff = (*a)->getPriority() - (*b)->getPriority();
+   return diff > 0 ? 1 : diff < 0 ? -1 : 0;
+}
 
-DefineEngineMethod(ModuleManager, findModules, String, (bool loadedOnly), (true),
+
+DefineEngineMethod(ModuleManager, findModules, String, (bool loadedOnly, bool sortByPriority, const char* moduleGroup), (true, false, ""),
    "Find all the modules registered with the specified loaded state.\n"
    "@param loadedOnly Whether to return only modules that are loaded or not.\n"
    "@return A list of space - separated module definition object Ids.\n")
@@ -174,12 +180,23 @@ DefineEngineMethod(ModuleManager, findModules, String, (bool loadedOnly), (true)
     char* pReturnBuffer = Con::getReturnBuffer( bufferSize );
     char* pBufferWrite = pReturnBuffer;
 
+    if (sortByPriority)
+       moduleDefinitions.sort(_findModulesSortByPriority);
+
+    StringTableEntry moduleGroupStr = StringTable->insert(moduleGroup);
+
     // Iterate module definitions.
     for ( ModuleManager::typeConstModuleDefinitionVector::const_iterator moduleDefinitionItr = moduleDefinitions.begin(); moduleDefinitionItr != moduleDefinitions.end(); ++moduleDefinitionItr )
     {
         // Fetch module definition.
         const ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
 
+        if(moduleGroupStr != StringTable->EmptyString())
+        {
+           if (pModuleDefinition->getModuleGroup() != moduleGroupStr)
+              continue;
+        }
+
         // Format module definition.
         const U32 offset = dSprintf( pBufferWrite, bufferSize, "%d ", pModuleDefinition->getId() );
         pBufferWrite += offset;

+ 4 - 4
Templates/BaseGame/game/core/clientServer/Core_ClientServer.tscript

@@ -27,10 +27,10 @@ function Core_ClientServer::finishMapLoad(%this)
     Core_ClientServer.GetEventManager().postEvent( "mapLoadComplete" );
 }
 
-function Core_ClientServer::FailMapLoad(%this, %moduleName, %isFine)
+function Core_ClientServer::FailMapLoad(%this, %moduleName, %canContinueOnFail)
 {    
     Core_ClientServer.failedModuleName = %moduleName;
-    Core_ClientServer.GetEventManager().postEvent( "mapLoadFail", %isFine );
+    Core_ClientServer.GetEventManager().postEvent( "mapLoadFail", %canContinueOnFail );
 }
 
 function Core_ClientServerListener::onMapLoadComplete(%this)
@@ -50,9 +50,9 @@ function Core_ClientServerListener::onMapLoadComplete(%this)
     }
 }
 
-function Core_ClientServerListener::onmapLoadFail(%this, %isFine)
+function Core_ClientServerListener::onMapLoadFail(%this, %canContinueOnFail)
 {   
-    if (%isFine) 
+    if (%canContinueOnFail) 
     {
         %this.onMapLoadComplete();
         return;

+ 104 - 25
Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript

@@ -81,24 +81,64 @@ function GameConnection::onConnect( %this, %clientData )
 	
 	%this.connectData = %clientData;
 	
+	//Signal and listener logic for the spawn config/processing here
+	%this.GetEventManager().registerEvent("setSpawnObjectTypeComplete");
+   %this.GetEventManager().registerEvent("setSpawnObjectTypeFailed");
+   %this.GetEventManager().registerEvent("setSpawnPointComplete");
+   %this.GetEventManager().registerEvent("setSpawnPointFailed");
+   %this.GetEventManager().registerEvent("postSpawnComplete");
+   
+   %this.listener = new ScriptMsgListener() {
+      class = GameConnectionListener;
+   }; 
+   %this.GetEventManager().subscribe( %this.listener, "setSpawnObjectTypeComplete" ); 
+   %this.GetEventManager().subscribe( %this.listener, "setSpawnObjectTypeFailed" ); 
+   %this.GetEventManager().subscribe( %this.listener, "setSpawnPointComplete" );
+   %this.GetEventManager().subscribe( %this.listener, "setSpawnPointFailed" );
+   %this.GetEventManager().subscribe( %this.listener, "postSpawnComplete" );
+	
 	callGamemodeFunction("onClientConnect", %this);
 	
 	$Server::PlayerCount++;
 }
 
+function GameConnection::GetEventManager(%this)
+{
+   if( !isObject( %this.eventManager ) )
+      %this.eventManager = new EventManager() { 
+         queue = "GameConnectionEventManager";
+      };
+      
+   return %this.eventManager;
+}
+
 function GameConnection::spawnControlObject( %this )
 {
     //baseline controlObject spawn type with extention points
     %this.spawnClass = "Camera";
     %this.spawnDBType = "CameraData";
     %this.spawnDataBlock = "Observer";
-    callOnModules("setSpawnObjectType", "Game", %this);
-	 callGamemodeFunction("setSpawnObjectType", %this);
+    
+    %this.numModsNeedingLoaded = 0;
+    %this.moduleLoadedDone = 0;
+    %modulesIDList = getModulesAndGameModesList(true, "Game");
+    
+    %this.numModsNeedingLoaded = getNumCanCallOnObjectList("setSpawnObjectType", %modulesIDList);
+    
+    if (%this.numModsNeedingLoaded)
+       callOnObjectList("setSpawnObjectType", %modulesIdList, %this);
+    else
+        %this.GetEventManager().onSetSpawnObjectTypeComplete(); //just jump to progress
 }
 
-function GameConnection::onSetSpawnObjectTypeComplete( %this )
+function GameConnectionListener::onSetSpawnObjectTypeComplete( %this, %client )
 {
-    if (isObject(%this.player))
+    %client.moduleLoadedDone++;
+
+    if (%client.moduleLoadedDone < %client.numModsNeedingLoaded)
+        return; //continue to wait  
+    
+    if (isObject(%client.player))
     {
         // The client should not already have a player. Assigning
         // a new one could result in an uncontrolled player object.
@@ -106,36 +146,41 @@ function GameConnection::onSetSpawnObjectTypeComplete( %this )
     }
    
     // Spawn with the engine's Sim::spawnObject() function
-    %this.player = spawnObject(%this.spawnClass, %this.spawnDataBlock);
+    %client.player = spawnObject(%client.spawnClass, %client.spawnDataBlock);
     
-    if (!%this.player.isMemberOfClass(%this.spawnClass))
-        warn("Trying to spawn a class that does not derive from "@ %this.spawnClass);
+    if (!%client.player.isMemberOfClass(%client.spawnClass))
+        warn("Trying to spawn a class that does not derive from "@ %client.spawnClass);
 
     // Add the player object to MissionCleanup so that it
     // won't get saved into the level files and will get
     // cleaned up properly
-    MissionCleanup.add(%this.player);
+    MissionCleanup.add(%client.player);
    
     // Store the client object on the player object for
     // future reference
-    %this.player.client = %this;
+    %client.player.client = %client;
    
-    %this.setSpawnPoint();
+    %client.setSpawnPoint();
     
    // Give the client control of the camera if in the editor
    if( $startWorldEditor )
    {
-      %control = %this.camera;
+      %control = %client.camera;
       %control.mode = "Fly";
       EditorGui.syncCameraGui();
    }
    else
-      %control = %this.player;
+      %control = %client.player;
       
    // Allow the player/camera to receive move data from the GameConnection.  Without this
    // the user is unable to control the player/camera.
    if (!isDefined("%noControl"))
-      %this.setControlObject(%control);
+      %client.setControlObject(%control);
+}
+
+function GameConnectionListener::onSetSpawnObjectTypeFailed( %this, %client, %canContinueOnFail )
+{
+   errorf("Failed to properly set Spawn Object Type for client: " @ %client);
 }
 
 function GameConnection::setSpawnPoint( %this )
@@ -144,14 +189,27 @@ function GameConnection::setSpawnPoint( %this )
     %this.playerSpawnGroups = "PlayerSpawnPoints PlayerDropPoints";
     %this.spawnPoint = "";
     %this.spawnLocation = "0 0 0";
-    callOnModules("setSpawnPoint", "Game", %this);
-	 callGamemodeFunction("setSpawnPoint", %this);    
+    
+    %this.numModsNeedingLoaded = 0;
+    %this.moduleLoadedDone = 0;
+    %modulesIDList = getModulesAndGameModesList(true, "Game");
+    
+    %this.numModsNeedingLoaded = getNumCanCallOnObjectList("setSpawnPoint", %modulesIDList);
+    
+    if (%this.numModsNeedingLoaded)
+        callOnObjectList("setSpawnPoint", %modulesIdList, %this);
+    else
+        %this.GetEventManager().onSetSpawnPointComplete();   
 }
 
-function GameConnection::onSetSpawnPointComplete( %this )
+function GameConnectionListener::onSetSpawnPointComplete( %this, %client )
 {
-    if (isObject(%this.player))
-        %this.player.setTransform(%this.spawnLocation);
+    %client.moduleLoadedDone++;
+    if (%client.moduleLoadedDone < %client.numModsNeedingLoaded)
+        return; //continue to wait 
+    
+    if (isObject(%client.player))
+        %client.player.setTransform(%client.spawnLocation);
     else
     {
         // If we weren't able to create the player object then warn the user
@@ -160,26 +218,47 @@ function GameConnection::onSetSpawnPointComplete( %this )
         if (isDefined("%this.spawnDataBlock"))
         {
             MessageBoxOK("Spawn Failed",
-                "Unable to create a player with class " @ %this.spawnClass @
-                " and datablock " @ %this.spawnDataBlock @ ".\n\nStarting as an Observer instead.",
+                "Unable to create a player with class " @ %client.spawnClass @
+                " and datablock " @ %client.spawnDataBlock @ ".\n\nStarting as an Observer instead.",
                 "");
         }
         else
         {
             MessageBoxOK("Spawn Failed",
-                "Unable to create a player with class " @ %this.spawnClass @
+                "Unable to create a player with class " @ %client.spawnClass @
                 ".\n\nStarting as an Observer instead.",
                 "");
         }
     }
-    %this.onPostSpawn();
+    %client.onPostSpawn();
+}
+
+function GameConnectionListener::onSetSpawnPointFailed( %this, %client, %canContinueOnFail )
+{
+   errorf("Failed to properly set Spawn Object Type for client: " @ %client);
 }
 
 function GameConnection::onPostSpawn( %this )
 {
-    //post controlObject create extention points
-    callOnModules("onPostSpawn", "Game", %this);
-	callGamemodeFunction("onPostSpawn", %this);    
+    %this.numModsNeedingLoaded = 0;
+    %this.moduleLoadedDone = 0;
+    %modulesIDList = getModulesAndGameModesList(true, "Game");
+    
+    %this.numModsNeedingLoaded = getNumCanCallOnObjectList("onPostSpawn", %modulesIDList);
+    
+    if (%this.numModsNeedingLoaded)
+        callOnObjectList("onPostSpawn", %modulesIdList, %this);
+    else
+        %this.GetEventManager().onPostSpawnComplete();    
+}
+
+function GameConnectionListener::onPostSpawnComplete(%this, %client)
+{
+    %client.moduleLoadedDone++;
+    if (%client.moduleLoadedDone < %client.numModsNeedingLoaded)
+        return; //continue to wait 
+        
+    //Continue on. Room for special handling here if needbe but not expressly required
 }
 
 //-----------------------------------------------------------------------------

+ 84 - 0
Templates/BaseGame/game/core/utility/scripts/helperFunctions.tscript

@@ -698,4 +698,88 @@ function playSoundAsset(%soundAssetId,%pos)
   }
   AssetDatabase.releaseAsset(%soundAssetId);
   return %handle;
+}
+
+//------------------------------------------------------------------------------
+function getModulesAndGameModesList(%usePriority, %group)
+{
+   %modulesList = ModuleDatabase.findModules(true, %usePriority, %group);
+   %modulesIDList = "";
+    
+   for(%i=0; %i < getWordCount(%modulesList); %i++)
+   {
+      %module = getWord(%modulesList, %i);
+      
+      %modulesIDList = %modulesIDList SPC %module.ModuleId;
+   }    
+   
+   %gamemodeList = getGameModesList();
+   %gameModeCount = %gamemodeList.count();
+   for(%i=0; %i < %gameModeCount; %i++)
+   {
+      %gameModeObj = %gamemodeList.getKey(%i);
+      %active = %gamemodeList.getValue(%i);
+      
+      if(!isObject(%gameModeObj) || !%active)
+         continue;
+         
+      %modulesIDList = %modulesIDList SPC %gameModeObj;
+   }
+   
+   %modulesIDList = strreplace(%modulesIDList, "  "," ");
+   %modulesIDList = trim(%modulesIDList);
+   return %modulesIDList;
+}
+
+function callOnObjectList(%functionName, %objectsList, %var0, %var1, %var2, %var3, %var4, %var5, %var6)
+{   
+   //Get our modules so we can exec any specific client-side loading/handling
+   %echoList = "Called List:";
+   for(%i=0; %i < getWordCount(%objectsList); %i++)
+   {
+      %obj = getWord(%objectsList, %i);
+      %objName = %obj.getName();
+      
+      if(!isObject(%obj))
+      {
+         //could be a moduleID we're trying to call against, so try a lookup
+         %module = ModuleDatabase.findModule(%obj);
+         if(isObject(%module))
+         {
+            %obj = %module.scopeSet;  
+            %objName = %module.ModuleId;
+         }
+      }
+      
+      %echoList = %echoList SPC %objName;
+
+      // match this to i/o signature
+      if(isObject(%obj) && %obj.isMethod(%functionName))
+       %obj.call(%functionName, %var0, %var1, %var2, %var3, %var4, %var5, %var6);
+   }
+   
+   if ($reportModuleOrder)
+      warn(%echoList);  
+}
+
+function getNumCanCallOnObjectList(%functionName, %objectsList)
+{ 
+   %numberWithFunction = 0;
+   for(%i=0; %i < getWordCount(%objectsList); %i++)
+   {
+      %obj = getWord(%objectsList, %i);
+      if(!isObject(%obj))
+      {
+         //could be a moduleID we're trying to call against, so try a lookup
+         %module = ModuleDatabase.findModule(%obj);
+         if(isObject(%module))
+            %obj = %module.scopeSet;  
+      }
+      
+      // match this to i/o signature
+      if(isObject(%obj) && %obj.isMethod(%functionName))
+         %numberWithFunction++;
+   }
+   
+   return %numberWithFunction;
 }