Initial implementation of the new Base Game Template and some starting modules.

This makes some tweaks to the engine to support this, specifically, it tweaks the hardcoded shaderpaths to defer to a pref variable, so none of the shader paths are hardcoded.

Also tweaks how post effects read in texture files, removing a bizzare filepath interpretation choice, where if the file path didn't start with "/" it forcefully appended the script's file path. This made it impossible to have images not in the same dir as the script file defining the post effect.

This was changed and the existing template's post effects tweaked for now to just add "./" to those few paths impacted, as well as the perf vars to support the non-hardcoded shader paths in the engine.
This commit is contained in:
Areloch 2017-02-24 02:40:56 -06:00
parent 5c8a82180b
commit 1ed8b05169
1572 changed files with 146699 additions and 85 deletions

View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function ServerPlay2D(%profile)
{
// Play the given sound profile on every client.
// The sounds will be transmitted as an event, not attached to any object.
for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)
ClientGroup.getObject(%idx).play2D(%profile);
}
function ServerPlay3D(%profile,%transform)
{
// Play the given sound profile at the given position on every client
// The sound will be transmitted as an event, not attached to any object.
for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)
ClientGroup.getObject(%idx).play3D(%profile,%transform);
}

View file

@ -0,0 +1,35 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Misc. server commands avialable to clients
//-----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Debug commands
//----------------------------------------------------------------------------
function serverCmdNetSimulateLag( %client, %msDelay, %packetLossPercent )
{
if ( %client.isAdmin )
%client.setSimulatedNetParams( %packetLossPercent / 100.0, %msDelay );
}

View file

@ -0,0 +1,161 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// This script function is called before a client connection
// is accepted. Returning "" will accept the connection,
// 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 )
{
echo("Connect request from: " @ %netAddress);
if($Server::PlayerCount >= $pref::Server::MaxPlayers)
return "CR_SERVERFULL";
return "";
}
//-----------------------------------------------------------------------------
// This script function is the first called on a client accept
function GameConnection::onConnect( %this, %clientData )
{
// Send down the connection error info, the client is responsible for
// displaying this message if a connection error occurs.
messageClient(%this, 'MsgConnectionError', "", $Pref::Server::ConnectionError);
// Send mission information to the client
sendLoadInfoToClient(%this);
// Simulated client lag for testing...
// %client.setSimulatedNetParams(0.1, 30);
// Get the client's unique id:
// %authInfo = %client.getAuthInfo();
// %client.guid = getField(%authInfo, 3);
%this.guid = 0;
addToServerGuidList(%this.guid);
// Set admin status
if (%this.getAddress() $= "local")
{
%this.isAdmin = true;
%this.isSuperAdmin = true;
}
else
{
%this.isAdmin = false;
%this.isSuperAdmin = false;
}
echo("CADD: "@ %this @" "@ %this.getAddress());
// If the mission is running, go ahead download it to the client
if ($missionRunning)
{
%this.loadMission();
}
else if ($Server::LoadFailMsg !$= "")
{
messageClient(%this, 'MsgLoadFailed', $Server::LoadFailMsg);
}
%this.connectData = %clientData;
$Server::PlayerCount++;
}
//-----------------------------------------------------------------------------
// 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)
{
%client.sendGuid = 0;
// Minimum length requirements
%name = trim( strToPlayerName( %name ) );
if ( strlen( %name ) < 3 )
%name = "Poser";
// Make sure the alias is unique, we'll hit something eventually
if (!isNameUnique(%name))
{
%isUnique = false;
for (%suffix = 1; !%isUnique; %suffix++) {
%nameTry = %name @ "." @ %suffix;
%isUnique = isNameUnique(%nameTry);
}
%name = %nameTry;
}
// Tag the name with the "smurf" color:
%client.nameBase = %name;
%client.playerName = addTaggedString("\cp\c8" @ %name @ "\co");
}
function isNameUnique(%name)
{
%count = ClientGroup.getCount();
for ( %i = 0; %i < %count; %i++ )
{
%test = ClientGroup.getObject( %i );
%rawName = stripChars( detag( getTaggedString( %test.playerName ) ), "\cp\co\c6\c7\c8\c9" );
if ( strcmp( %name, %rawName ) == 0 )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// This function is called when a client drops for any reason
//
function GameConnection::onDrop(%client, %reason)
{
if($missionRunning)
theLevelInfo.onClientLeaveGame();
removeFromServerGuidList( %client.guid );
$Server::PlayerCount--;
}
//-----------------------------------------------------------------------------
function GameConnection::startMission(%this)
{
// Inform the client the mission starting
commandToClient(%this, 'MissionStart', $missionSequence);
}
function GameConnection::endMission(%this)
{
// Inform the client the mission is done. Note that if this is
// called as part of the server destruction routine, the client will
// actually never see this comment since the client connection will
// be destroyed before another round of command processing occurs.
// In this case, the client will only see the disconnect from the server
// and should manually trigger a mission cleanup.
commandToClient(%this, 'MissionEnd', $missionSequence);
}

View file

@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//Firstly, set up our standard server prefs
// List of master servers to query, each one is tried in order
// until one responds
$Pref::Server::RegionMask = 2;
$pref::Master[0] = "2:master.garagegames.com:28002";
// Information about the server
$Pref::Server::Name = "Torque 3D Server";
$Pref::Server::Info = "This is a Torque 3D server.";
// The connection error message is transmitted to the client immediatly
// on connection, if any further error occures during the connection
// process, such as network traffic mismatch, or missing files, this error
// message is display. This message should be replaced with information
// usefull to the client, such as the url or ftp address of where the
// latest version of the game can be obtained.
$Pref::Server::ConnectionError =
"You do not have the correct version of "@$appName@" or "@
"the related art needed to play on this server, please contact "@
"the server administrator.";
// The network port is also defined by the client, this value
// overrides pref::net::port for dedicated servers
$Pref::Server::Port = 28000;
// If the password is set, clients must provide it in order
// to connect to the server
$Pref::Server::Password = "";
// Password for admin clients
$Pref::Server::AdminPassword = "";
// Misc server settings.
$Pref::Server::MaxPlayers = 64;
$Pref::Server::TimeLimit = 20; // In minutes
$Pref::Server::KickBanTime = 300; // specified in seconds
$Pref::Server::BanTime = 1800; // specified in seconds
$Pref::Server::FloodProtectionEnabled = 1;
$Pref::Server::MaxChatLen = 120;

View file

@ -0,0 +1,171 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Mission Loading
// The server portion of the client/server mission loading process
//-----------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Loading Phases:
// Phase 1: Transmit Datablocks
// Transmit targets
// Phase 2: Transmit Ghost Objects
// Phase 3: Start Game
//
// The server invokes the client MissionStartPhase[1-3] function to request
// permission to start each phase. When a client is ready for a phase,
// it responds with MissionStartPhase[1-3]Ack.
//----------------------------------------------------------------------------
// Phase 1
//----------------------------------------------------------------------------
function GameConnection::loadMission(%this)
{
// Send over the information that will display the server info
// when we learn it got there, we'll send the data blocks
%this.currentPhase = 0;
if (%this.isAIControlled())
{
// Cut to the chase...
theLevelInfo.onEnterGame(%this);
}
else
{
commandToClient(%this, 'MissionStartPhase1', $missionSequence, $Server::MissionFile);
echo("*** Sending mission load to client: " @ $Server::MissionFile);
}
}
function serverCmdMissionStartPhase1Ack(%client, %seq)
{
// Make sure to ignore calls from a previous mission load
if (%seq != $missionSequence || !$MissionRunning || %client.currentPhase != 0)
return;
%client.currentPhase = 1;
// Start with the CRC
%client.setMissionCRC( $missionCRC );
// Send over the datablocks...
// OnDataBlocksDone will get called when have confirmation
// that they've all been received.
%client.transmitDataBlocks($missionSequence);
}
function GameConnection::onDataBlocksDone( %this, %missionSequence )
{
// Make sure to ignore calls from a previous mission load
if (%missionSequence != $missionSequence || %this.currentPhase != 1)
return;
%this.currentPhase = 1.5;
// On to the next phase
commandToClient(%this, 'MissionStartPhase2', $missionSequence, $Server::MissionFile);
}
//----------------------------------------------------------------------------
// Phase 2
//----------------------------------------------------------------------------
function serverCmdMissionStartPhase2Ack(%client, %seq)
{
// Make sure to ignore calls from a previous mission load
if (%seq != $missionSequence || !$MissionRunning || %client.currentPhase != 1.5)
return;
%client.currentPhase = 2;
// Update mod paths, this needs to get there before the objects.
%client.transmitPaths();
// Start ghosting objects to the client
%client.activateGhosting();
}
function GameConnection::clientWantsGhostAlwaysRetry(%client)
{
if($MissionRunning)
%client.activateGhosting();
}
function GameConnection::onGhostAlwaysFailed(%client)
{
}
function GameConnection::onGhostAlwaysObjectsReceived(%client)
{
// Ready for next phase.
commandToClient(%client, 'MissionStartPhase3', $missionSequence, $Server::MissionFile);
}
//----------------------------------------------------------------------------
// Phase 3
//----------------------------------------------------------------------------
function serverCmdMissionStartPhase3Ack(%client, %seq)
{
// Make sure to ignore calls from a previous mission load
if(%seq != $missionSequence || !$MissionRunning || %client.currentPhase != 2)
return;
%client.currentPhase = 3;
// Server is ready to drop into the game
//Have any special game-play handling here
if(theLevelInfo.isMethod("onClientEnterGame"))
{
theLevelInfo.onClientEnterGame(%client);
}
else
{
//No Game mode class for the level info, so just spawn a default camera
// Set the control object to the default camera
if (!isObject(%client.camera))
{
if(!isObject(Observer))
{
datablock CameraData(Observer)
{
mode = "Observer";
};
}
if (isDefined("$Game::DefaultCameraClass"))
%client.camera = spawnObject("Camera", Observer);
}
// If we have a camera then set up some properties
if (isObject(%client.camera))
{
MissionCleanup.add( %this.camera );
%client.camera.scopeToClient(%client);
%client.setControlObject(%client.camera);
%client.camera.setTransform("0 0 1 0 0 0 0");
}
}
%client.startMission();
}

View file

@ -0,0 +1,197 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Loading info is text displayed on the client side while the mission
// is being loaded. This information is extracted from the mission file
// and sent to each the client as it joins.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// clearLoadInfo
//
// Clears the mission info stored
//------------------------------------------------------------------------------
function clearLoadInfo()
{
if (isObject(theLevelInfo))
theLevelInfo.delete();
}
//------------------------------------------------------------------------------
// buildLoadInfo
//
// Extract the map description from the .mis file
//------------------------------------------------------------------------------
function buildLoadInfo( %mission )
{
clearLoadInfo();
%infoObject = "";
%file = new FileObject();
if ( %file.openForRead( %mission ) ) {
%inInfoBlock = false;
while ( !%file.isEOF() ) {
%line = %file.readLine();
%line = trim( %line );
if( %line $= "new ScriptObject(MissionInfo) {" )
%inInfoBlock = true;
else if( %line $= "new LevelInfo(theLevelInfo) {" )
%inInfoBlock = true;
else if( %inInfoBlock && %line $= "};" ) {
%inInfoBlock = false;
%infoObject = %infoObject @ %line;
break;
}
if( %inInfoBlock )
%infoObject = %infoObject @ %line @ " ";
}
%file.close();
}
else
error("Level file " @ %mission @ " not found.");
// Will create the object "MissionInfo"
eval( %infoObject );
%file.delete();
}
//------------------------------------------------------------------------------
// dumpLoadInfo
//
// Echo the mission information to the console
//------------------------------------------------------------------------------
function dumpLoadInfo()
{
echo( "Level Name: " @ theLevelInfo.name );
echo( "Level Description:" );
for( %i = 0; theLevelInfo.desc[%i] !$= ""; %i++ )
echo (" " @ theLevelInfo.desc[%i]);
}
//------------------------------------------------------------------------------
// sendLoadInfoToClient
//
// Sends mission description to the client
//------------------------------------------------------------------------------
function sendLoadInfoToClient( %client )
{
messageClient( %client, 'MsgLoadInfo', "", theLevelInfo.levelName );
// Send Mission Description a line at a time
for( %i = 0; theLevelInfo.desc[%i] !$= ""; %i++ )
messageClient( %client, 'MsgLoadDescripition', "", theLevelInfo.desc[%i] );
messageClient( %client, 'MsgLoadInfoDone' );
}
// A function used in order to easily parse the MissionGroup for classes . I'm pretty
// sure at this point the function can be easily modified to search the any group as well.
function parseMissionGroup( %className, %childGroup )
{
if( getWordCount( %childGroup ) == 0)
%currentGroup = "MissionGroup";
else
%currentGroup = %childGroup;
for(%i = 0; %i < (%currentGroup).getCount(); %i++)
{
if( (%currentGroup).getObject(%i).getClassName() $= %className )
return true;
if( (%currentGroup).getObject(%i).getClassName() $= "SimGroup" )
{
if( parseMissionGroup( %className, (%currentGroup).getObject(%i).getId() ) )
return true;
}
}
}
//
function parseMissionGroupForIds( %className, %childGroup )
{
if( getWordCount( %childGroup ) == 0)
%currentGroup = $Game::MissionGroup;
else
%currentGroup = %childGroup;
for(%i = 0; %i < (%currentGroup).getCount(); %i++)
{
if( (%currentGroup).getObject(%i).getClassName() $= %className )
%classIds = %classIds @ (%currentGroup).getObject(%i).getId() @ " ";
if( (%currentGroup).getObject(%i).getClassName() $= "SimGroup" )
%classIds = %classIds @ parseMissionGroupForIds( %className, (%currentGroup).getObject(%i).getId());
}
return %classIds;
}
function getLevelInfo( %missionFile )
{
clearLoadInfo();
%file = new FileObject();
%LevelInfoObject = "";
if ( %file.openForRead( %missionFile ) ) {
%inInfoBlock = false;
while ( !%file.isEOF() ) {
%line = %file.readLine();
%line = trim( %line );
if( %line $= "new ScriptObject(LevelInfo) {" )
%inInfoBlock = true;
else if( %line $= "new LevelInfo(theLevelInfo) {" )
%inInfoBlock = true;
else if( %inInfoBlock && %line $= "};" ) {
%inInfoBlock = false;
%LevelInfoObject = %LevelInfoObject @ %line;
break;
}
if( %inInfoBlock )
%LevelInfoObject = %LevelInfoObject @ %line @ " ";
}
%file.close();
}
%file.delete();
if( %LevelInfoObject !$= "" )
{
%LevelInfoObject = "%LevelInfoObject = " @ %LevelInfoObject;
eval( %LevelInfoObject );
return %LevelInfoObject;
}
// Didn't find our LevelInfo
return 0;
}

View file

@ -0,0 +1,181 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Mission Loading
// The server portion of the client/server mission loading process
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Server mission loading
//-----------------------------------------------------------------------------
// On every mission load except the first, there is a pause after
// the initial mission info is downloaded to the client.
$MissionLoadPause = 5000;
//-----------------------------------------------------------------------------
//This is the first call made by the server to kick the loading process off
function loadMission( %missionName, %isFirstMission )
{
endMission();
echo("*** LOADING MISSION: " @ %missionName);
echo("*** Stage 1 load");
// increment the mission sequence (used for ghost sequencing)
$missionSequence++;
$missionRunning = false;
$Server::MissionFile = %missionName;
$Server::LoadFailMsg = "";
// Extract mission info from the mission file,
// including the display name and stuff to send
// to the client.
buildLoadInfo( %missionName );
// Download mission info to the clients
%count = ClientGroup.getCount();
for( %cl = 0; %cl < %count; %cl++ )
{
%client = ClientGroup.getObject( %cl );
if (!%client.isAIControlled())
sendLoadInfoToClient(%client);
}
// Now that we've sent the LevelInfo to the clients
// clear it so that it won't conflict with the actual
// LevelInfo loaded in the level
clearLoadInfo();
// if this isn't the first mission, allow some time for the server
// to transmit information to the clients:
if( %isFirstMission || $Server::ServerType $= "SinglePlayer" )
loadMissionStage2();
else
schedule( $MissionLoadPause, ServerGroup, loadMissionStage2 );
}
//-----------------------------------------------------------------------------
function loadMissionStage2()
{
echo("*** Stage 2 load");
// Create the mission group off the ServerGroup
$instantGroup = ServerGroup;
// Make sure the mission exists
%file = $Server::MissionFile;
if( !isFile( %file ) )
{
$Server::LoadFailMsg = "Could not find mission \"" @ %file @ "\"";
}
else
{
// Calculate the mission CRC. The CRC is used by the clients
// to caching mission lighting.
$missionCRC = getFileCRC( %file );
// Exec the mission. The MissionGroup (loaded components) is added to the ServerGroup
exec(%file);
if( !isObject(MissionGroup) )
{
$Server::LoadFailMsg = "No 'MissionGroup' found in mission \"" @ %file @ "\".";
}
}
if( $Server::LoadFailMsg !$= "" )
{
// Inform clients that are already connected
for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)
messageClient(ClientGroup.getObject(%clientIndex), 'MsgLoadFailed', $Server::LoadFailMsg);
return;
}
// Set mission name.
if( isObject( theLevelInfo ) )
$Server::MissionName = theLevelInfo.levelName;
// Mission cleanup group. This is where run time components will reside. The MissionCleanup
// group will be added to the ServerGroup.
new SimGroup( MissionCleanup );
// Make the MissionCleanup group the place where all new objects will automatically be added.
$instantGroup = MissionCleanup;
// Construct MOD paths
pathOnMissionLoadDone();
// Mission loading done...
echo("*** Mission loaded");
// Start all the clients in the mission
$missionRunning = true;
for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ )
ClientGroup.getObject(%clientIndex).loadMission();
// Go ahead and launch the game
if(TheLevelInfo.isMethod("onMissionStart"))
TheLevelInfo.onMissionStart();
}
function endMission()
{
if (!isObject( MissionGroup ))
return;
echo("*** ENDING MISSION");
// Inform the game code we're done.
TheLevelInfo.onMissionEnded();
// Inform the clients
for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) {
// clear ghosts and paths from all clients
%cl = ClientGroup.getObject( %clientIndex );
%cl.endMission();
%cl.resetGhosting();
%cl.clearPaths();
}
// Delete everything
MissionGroup.delete();
MissionCleanup.delete();
clearServerPaths();
}
function resetMission()
{
echo("*** MISSION RESET");
// Remove any temporary mission objects
MissionCleanup.delete();
$instantGroup = ServerGroup;
new SimGroup( MissionCleanup );
$instantGroup = MissionCleanup;
clearServerPaths();
//
TheLevelInfo.onMissionReset();
}

View file

@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
commandToClient(%client, 'ServerMessage', %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
}
function messageAll(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
%count = ClientGroup.getCount();
for(%cl = 0; %cl < %count; %cl++)
{
%client = ClientGroup.getObject(%cl);
messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
}
}
function messageAllExcept(%client, %team, %msgtype, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13)
{
//can exclude a client, a team or both. A -1 value in either field will ignore that exclusion, so
//messageAllExcept(-1, -1, $Mesblah, 'Blah!'); will message everyone (since there shouldn't be a client -1 or client on team -1).
%count = ClientGroup.getCount();
for(%cl= 0; %cl < %count; %cl++)
{
%recipient = ClientGroup.getObject(%cl);
if((%recipient != %client) && (%recipient.team != %team))
messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13);
}
}

View file

@ -0,0 +1,291 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
function initServer()
{
echo("\n--------- Initializing " @ $appName @ ": Server Scripts ---------");
//load prefs
%prefPath = getPrefpath();
if ( isFile( %prefPath @ "/serverPrefs.cs" ) )
exec( %prefPath @ "/serverPrefs.cs" );
else
exec( "data/clientServer/scripts/server/defaults.cs" );
exec( "data/clientServer/scripts/server/audio.cs" );
exec( "data/clientServer/scripts/server/commands.cs" );
exec( "data/clientServer/scripts/server/message.cs" );
exec( "data/clientServer/scripts/server/levelDownload.cs" );
exec( "data/clientServer/scripts/server/levelLoad.cs" );
exec( "data/clientServer/scripts/server/levelInfo.cs" );
exec( "data/clientServer/scripts/server/connectionToClient.cs" );
// Server::Status is returned in the Game Info Query and represents the
// current status of the server. This string sould be very short.
$Server::Status = "Unknown";
// Turn on testing/debug script functions
$Server::TestCheats = false;
// Specify where the mission files are.
$Server::MissionFileSpec = "data/levels/*.mis";
}
//-----------------------------------------------------------------------------
function initDedicated()
{
enableWinConsole(true);
echo("\n--------- Starting Dedicated Server ---------");
// Make sure this variable reflects the correct state.
$Server::Dedicated = true;
// The server isn't started unless a mission has been specified.
if ($missionArg !$= "") {
createServer("MultiPlayer", $missionArg);
}
else
echo("No mission specified (use -mission filename)");
}
/// Attempt to find an open port to initialize the server with
function portInit(%port)
{
%failCount = 0;
while(%failCount < 10 && !setNetPort(%port))
{
echo("Port init failed on port " @ %port @ " trying next port.");
%port++; %failCount++;
}
}
/// Create a server of the given type, load the given level, and then
/// create a local client connection to the server.
//
/// @return true if successful.
function createAndConnectToLocalServer( %serverType, %level )
{
if( !createServer( %serverType, %level ) )
return false;
%conn = new GameConnection( ServerConnection );
RootGroup.add( ServerConnection );
%conn.setConnectArgs( $pref::Player::Name );
%conn.setJoinPassword( $Client::Password );
%result = %conn.connectLocal();
if( %result !$= "" )
{
%conn.delete();
destroyServer();
return false;
}
return true;
}
/// Create a server with either a "SinglePlayer" or "MultiPlayer" type
/// Specify the level to load on the server
function createServer(%serverType, %level)
{
// Increase the server session number. This is used to make sure we're
// working with the server session we think we are.
$Server::Session++;
if (%level $= "")
{
error("createServer(): level name unspecified");
return false;
}
// Make sure our level name is relative so that it can send
// across the network correctly
%level = makeRelativePath(%level, getWorkingDirectory());
destroyServer();
$missionSequence = 0;
$Server::PlayerCount = 0;
$Server::ServerType = %serverType;
$Server::LoadFailMsg = "";
$Physics::isSinglePlayer = true;
// Setup for multi-player, the network must have been
// initialized before now.
if (%serverType $= "MultiPlayer")
{
$Physics::isSinglePlayer = false;
echo("Starting multiplayer mode");
// Make sure the network port is set to the correct pref.
portInit($Pref::Server::Port);
allowConnections(true);
if ($pref::Net::DisplayOnMaster !$= "Never" )
schedule(0,0,startHeartbeat);
}
// Let the game initialize some things now that the
// the server has been created
onServerCreated();
loadMission(%level, true);
$Game::running = true;
return true;
}
function onServerCreated()
{
// Server::GameType is sent to the master server.
// This variable should uniquely identify your game and/or mod.
$Server::GameType = $appName;
// Server::MissionType sent to the master server. Clients can
// filter servers based on mission type.
// $Server::MissionType = "Deathmatch";
// GameStartTime is the sim time the game started. Used to calculated
// game elapsed time.
$Game::StartTime = 0;
// Create the server physics world.
physicsInitWorld( "server" );
physicsStartSimulation("server");
%cnt = DatablockFilesList.count();
loadDatablockFiles( DatablockFilesList, true );
%cnt = DatablockFilesList.count();
// Keep track of when the game started
$Game::StartTime = $Sim::Time;
}
/// Shut down the server
function destroyServer()
{
$Server::ServerType = "";
$Server::Running = false;
allowConnections(false);
stopHeartbeat();
$missionRunning = false;
// End any running levels and shut down the physics sim
onServerDestroyed();
physicsDestroy();
// Delete all the server objects
if (isObject(ServerGroup))
ServerGroup.delete();
// Delete all the connections:
while (ClientGroup.getCount())
{
%client = ClientGroup.getObject(0);
%client.delete();
}
$Server::GuidList = "";
// Delete all the data blocks...
deleteDataBlocks();
// Save any server settings
echo( "Exporting server prefs..." );
export( "$Pref::Server::*", "data/clientServer/scripts/server/prefs.cs", false );
// Increase the server session number. This is used to make sure we're
// working with the server session we think we are.
$Server::Session++;
}
function onServerDestroyed()
{
physicsStopSimulation("server");
if (!isObject( MissionGroup ))
return;
echo("*** ENDING MISSION");
// Inform the game code we're done.
if(TheLevelInfo.isMethod("onMissionEnded"))
TheLevelInfo.onMissionEnded();
// Inform the clients
for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) {
// clear ghosts and paths from all clients
%cl = ClientGroup.getObject( %clientIndex );
%cl.endMission();
%cl.resetGhosting();
%cl.clearPaths();
}
// Delete everything
MissionGroup.delete();
MissionCleanup.delete();
clearServerPaths();
}
/// Guid list maintenance functions
function addToServerGuidList( %guid )
{
%count = getFieldCount( $Server::GuidList );
for ( %i = 0; %i < %count; %i++ )
{
if ( getField( $Server::GuidList, %i ) == %guid )
return;
}
$Server::GuidList = $Server::GuidList $= "" ? %guid : $Server::GuidList TAB %guid;
}
function removeFromServerGuidList( %guid )
{
%count = getFieldCount( $Server::GuidList );
for ( %i = 0; %i < %count; %i++ )
{
if ( getField( $Server::GuidList, %i ) == %guid )
{
$Server::GuidList = removeField( $Server::GuidList, %i );
return;
}
}
}
/// When the server is queried for information, the value of this function is
/// returned as the status field of the query packet. This information is
/// accessible as the ServerInfo::State variable.
function onServerInfoQuery()
{
return "Doing Ok";
}