From c5ae9af0ae96855c6de3ac7765da0e92c32eae00 Mon Sep 17 00:00:00 2001 From: JeffR Date: Sat, 21 Dec 2024 02:11:35 -0600 Subject: [PATCH] Core now spawns a ControlObject directly, with callbacks allowing overriding of values to control what spawns (and what occurs afterwards) to tie several tracking variables to a given client connection. By default this list of variables would be: %this.spawnClass = "Camera"; %this.spawnDBType = "CameraData"; %this.spawnDataBlock = "Observer"; %this.playerSpawnGroups = "PlayerSpawnPoints PlayerDropPoints"; %this.spawnPoint = ""; %this.spawnLocation = "0 0 0"; Also adds several callbacks so that these values can be overridden by modules and gamemodes, kicked off from the %client.spawnControlObject(); command : callOnModules("setSpawnObjectType", "Game", %this); callGamemodeFunction("setSpawnObjectType", %this); callOnModules("setSpawnPoint", "Game", %this); callGamemodeFunction("setSpawnPoint", %this); callOnModules("onPostSpawn", "Game", %this); callGamemodeFunction("onPostSpawn", %this); This is to ensure that a game mode can supersede modules, but even modules can dictate spawn behaviors for minimalist implementations and drop-in interop support --- .../scripts/server/connectionToClient.tscript | 120 ++++++++++++++++-- .../scripts/server/levelDownload.tscript | 2 + .../scripts/shared/ExampleGameMode.tscript | 96 -------------- 3 files changed, 110 insertions(+), 108 deletions(-) diff --git a/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript b/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript index a87a250cf..10d47f8c1 100644 --- a/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript +++ b/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript @@ -27,7 +27,7 @@ // anything else will be sent back as an error to the client. // All the connect args are passed also to onConnectRequest // -function GameConnection::onConnectRequest( %client, %netAddress, %name ) +function GameConnection::onConnectRequest( %this, %netAddress, %name ) { echo("Connect request from: " @ %netAddress); if($Server::PlayerCount >= $pref::Server::MaxPlayers) @@ -47,11 +47,11 @@ function GameConnection::onConnect( %this, %clientData ) sendLoadInfoToClient(%this); // Simulated client lag for testing... - // %client.setSimulatedNetParams(0.1, 30); + // %this.setSimulatedNetParams(0.1, 30); // Get the client's unique id: - // %authInfo = %client.getAuthInfo(); - // %client.guid = getField(%authInfo, 3); + // %authInfo = %this.getAuthInfo(); + // %this.guid = getField(%authInfo, 3); %this.guid = 0; addToServerGuidList(%this.guid); @@ -86,14 +86,110 @@ function GameConnection::onConnect( %this, %clientData ) $Server::PlayerCount++; } +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); +} + +function GameConnection::onSetSpawnObjectTypeComplete( %this ) +{ + if (isObject(%this.player)) + { + // The client should not already have a player. Assigning + // a new one could result in an uncontrolled player object. + error("Attempting to create a player for a client that already has one!"); + } + + // Spawn with the engine's Sim::spawnObject() function + %this.player = spawnObject(%this.spawnClass, %this.spawnDataBlock); + + if (!%this.player.isMemberOfClass(%this.spawnClass)) + warn("Trying to spawn a class that does not derive from "@ %this.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); + + // Store the client object on the player object for + // future reference + %this.player.client = %this; + + %this.setSpawnPoint(); + + // Give the client control of the camera if in the editor + if( $startWorldEditor ) + { + %control = %this.camera; + %control.mode = "Fly"; + EditorGui.syncCameraGui(); + } + else + %control = %this.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); +} + +function GameConnection::setSpawnPoint( %this ) +{ + //baseline spawn point config rules with extention points + %this.playerSpawnGroups = "PlayerSpawnPoints PlayerDropPoints"; + %this.spawnPoint = ""; + %this.spawnLocation = "0 0 0"; + callOnModules("setSpawnPoint", "Game", %this); + callGamemodeFunction("setSpawnPoint", %this); +} + +function GameConnection::onSetSpawnPointComplete( %this ) +{ + if (isObject(%this.player)) + %this.player.setTransform(%this.spawnLocation); + else + { + // If we weren't able to create the player object then warn the user + // When the player clicks OK in one of these message boxes, we will fall through + // to the "if (!isObject(%player))" check below. + 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.", + ""); + } + else + { + MessageBoxOK("Spawn Failed", + "Unable to create a player with class " @ %this.spawnClass @ + ".\n\nStarting as an Observer instead.", + ""); + } + } + %this.onPostSpawn(); +} + +function GameConnection::onPostSpawn( %this ) +{ + //post controlObject create extention points + callOnModules("onPostSpawn", "Game", %this); + callGamemodeFunction("onPostSpawn", %this); +} + //----------------------------------------------------------------------------- // A player's name could be obtained from the auth server, but for // now we use the one passed from the client. // %realName = getField( %authInfo, 0 ); // -function GameConnection::setPlayerName(%client,%name) +function GameConnection::setPlayerName(%this,%name) { - %client.sendGuid = 0; + %this.sendGuid = 0; // Minimum length requirements %name = trim( strToPlayerName( %name ) ); @@ -112,8 +208,8 @@ function GameConnection::setPlayerName(%client,%name) } // Tag the name with the "smurf" color: - %client.nameBase = %name; - %client.playerName = addTaggedString("\cp\c8" @ %name @ "\co"); + %this.nameBase = %name; + %this.playerName = addTaggedString("\cp\c8" @ %name @ "\co"); } function isNameUnique(%name) @@ -132,7 +228,7 @@ function isNameUnique(%name) //----------------------------------------------------------------------------- // This function is called when a client drops for any reason // -function GameConnection::onDrop(%client, %reason) +function GameConnection::onDrop(%this, %reason) { %entityIds = parseMissionGroupForIds("Entity", ""); %entityCount = getWordCount(%entityIds); @@ -148,15 +244,15 @@ function GameConnection::onDrop(%client, %reason) %entityIds = %entityIds SPC %child.getID(); } - %entity.notify("onClientDisconnect", %client); + %entity.notify("onClientDisconnect", %this); } if($missionRunning) { - %hasGameMode = callGamemodeFunction("onClientLeaveGame", %client); + %hasGameMode = callGamemodeFunction("onClientLeaveGame", %this); } - removeFromServerGuidList( %client.guid ); + removeFromServerGuidList( %this.guid ); $Server::PlayerCount--; } diff --git a/Templates/BaseGame/game/core/clientServer/scripts/server/levelDownload.tscript b/Templates/BaseGame/game/core/clientServer/scripts/server/levelDownload.tscript index 555448602..002891ffa 100644 --- a/Templates/BaseGame/game/core/clientServer/scripts/server/levelDownload.tscript +++ b/Templates/BaseGame/game/core/clientServer/scripts/server/levelDownload.tscript @@ -178,6 +178,8 @@ function serverCmdMissionStartPhase3Ack(%client, %seq) %client.currentPhase = 3; %hasGameMode = callGamemodeFunction("onClientEnterGame", %client); + + %client.spawnControlObject(); //if that also failed, just spawn a camera if(%hasGameMode == 0) diff --git a/Templates/BaseGame/game/data/ExampleModule/scripts/shared/ExampleGameMode.tscript b/Templates/BaseGame/game/data/ExampleModule/scripts/shared/ExampleGameMode.tscript index 9c6d7942e..0589d01e2 100644 --- a/Templates/BaseGame/game/data/ExampleModule/scripts/shared/ExampleGameMode.tscript +++ b/Templates/BaseGame/game/data/ExampleModule/scripts/shared/ExampleGameMode.tscript @@ -51,16 +51,6 @@ function ExampleGameMode::onMissionReset(%this) function ExampleGameMode::initGameVars(%this) { - //----------------------------------------------------------------------------- - // What kind of "camera" is spawned is either controlled directly by the - // SpawnSphere or it defaults back to the values set here. This also controls - // which SimGroups to attempt to select the spawn sphere's from by walking down - // the list of SpawnGroups till it finds a valid spawn object. - // These override the values set in core/scripts/server/spawn.cs - //----------------------------------------------------------------------------- - %this.defaultCameraClass = "Camera"; - %this.defaultCameraDataBlock = "Observer"; - %this.defaultCameraSpawnGroups = "CameraSpawnPoints PlayerSpawnPoints PlayerDropPoints"; } function ExampleGameMode::onGameDurationEnd(%this) @@ -82,15 +72,6 @@ function ExampleGameMode::onClientEnterGame(%this, %client) //Set the player name based on the client's connection data %client.setPlayerName(%client.connectData); - // Find a spawn point for the camera - // This function currently relies on some helper functions defined in - // core/scripts/server/spawn.cs. For custom spawn behaviors one can either - // override the properties on the SpawnSphere's or directly override the - // functions themselves. - %cameraSpawnPoint = %this.pickCameraSpawnPoint(%this.DefaultCameraSpawnGroups); - // Spawn a camera for this client using the found %spawnPoint - %this.spawnCamera(%client, %cameraSpawnPoint); - // Inform the client of all the other clients %count = ClientGroup.getCount(); for (%cl = 0; %cl < %count; %cl++) @@ -175,81 +156,4 @@ function ExampleGameMode::onSubsceneUnloaded(%this) { echo("==================================="); echo("ExampleGameMode - Subscene is unloaded"); -} - -function ExampleGameMode::spawnCamera(%this, %client, %spawnPoint) -{ - // Set the control object to the default camera - if (!isObject(%client.camera)) - { - if (%this.defaultCameraClass !$= "") - %client.camera = spawnObject(%this.defaultCameraClass, %this.defaultCameraDataBlock); - } - - // If we have a camera then set up some properties - if (isObject(%client.camera)) - { - MissionCleanup.add( %client.camera ); - %client.camera.scopeToClient(%client); - - %client.setControlObject(%client.camera); - - if(!isObject(%spawnPoint)) - %spawnPoint = %this.pickCameraSpawnPoint(%this.defaultCameraSpawnGroups); - - if (isObject(%spawnPoint)) - { - // Attempt to treat %spawnPoint as an object - if (getWordCount(%spawnPoint) == 1 && isObject(%spawnPoint)) - { - %client.camera.setTransform(%spawnPoint.getTransform()); - } - else - { - // Treat %spawnPoint as an AxisAngle transform - %client.camera.setTransform(%spawnPoint); - } - } - } -} - -//----------------------------------------------------------------------------- -// pickCameraSpawnPoint() is responsible for finding a valid spawn point for a -// camera. -//----------------------------------------------------------------------------- -function ExampleGameMode::pickCameraSpawnPoint(%this, %spawnGroups) -{ - // Walk through the groups until we find a valid object - for (%i = 0; %i < getWordCount(%spawnGroups); %i++) - { - %group = getWord(%spawnGroups, %i); - - %count = getWordCount(%group); - - if (isObject(%group)) - %spawnPoint = %group.getRandom(); - - if (isObject(%spawnPoint)) - return %spawnPoint; - } - - // Didn't find a spawn point by looking for the groups - // so let's return the "default" SpawnSphere - // First create it if it doesn't already exist - if (!isObject(DefaultCameraSpawnSphere)) - { - %spawn = new SpawnSphere(DefaultCameraSpawnSphere) - { - dataBlock = "SpawnSphereMarker"; - spawnClass = $Game::DefaultCameraClass; - spawnDatablock = $Game::DefaultCameraDataBlock; - }; - - // Add it to the MissionCleanup group so that it - // doesn't get saved to the Mission (and gets cleaned - // up of course) - MissionCleanup.add(%spawn); - } - - return DefaultCameraSpawnSphere; } \ No newline at end of file