3D Game Programming All In One (2004)
.pdf248 Chapter 7 ■ Common Scripts
//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)
return;
if (%this.currentPhase != 1) return;
%this.currentPhase = 1.5;
//On to the next phase
CommandToClient(%this, 'MissionStartPhase2', $missionSequence, $Server::MissionFile);
}
function ServerCmdMissionStartPhase2Ack(%client, %seq)
{
//Make sure to ignore calls from a previous mission load if (%seq != $missionSequence || !$MissionRunning)
return;
if (%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();
}
Team LRN
Selected Common Server Modules 249
function GameConnection::OnGhostAlwaysFailed(%client)
{
}
function GameConnection::OnGhostAlwaysObjectsReceived(%client)
{
// Ready for next phase.
CommandToClient(%client, 'MissionStartPhase3', $missionSequence, $Server::Mission-
File);
}
function ServerCmdMissionStartPhase3Ack(%client, %seq)
{
//Make sure to ignore calls from a previous mission load if(%seq != $missionSequence || !$MissionRunning)
return; if(%client.currentPhase != 2)
return; %client.currentPhase = 3;
//Server is ready to drop into the game %client.StartMission(); %client.OnClientEnterGame();
}
The following functions and GameConnection methods are defined in the MissionDownload module:
GameConnection::LoadMission
GameConnection::OnDataBlocksDone
GameConnection::ClientWantsGhostAlwaysRetry
GameConnection::OnGhostAlwaysFailed
GameConnection::OnGhostAlwaysObjectsReceived
ServerCmdMissionStartPhase1Ack
ServerCmdMissionStartPhase2Ack
ServerCmdMissionStartPhase3Ack
This module handles the server-side activities in the mission download process (see Figure 7.1).
Team LRN
250 Chapter 7 ■ Common Scripts
Figure 7.1 Mission download phases.
This module contains the mission download methods for each client's GameConnection object.
The download process for the client object starts when its LoadMission method in this module is called at the end of the server's LoadMissionStage2 function in the server's MissionLoad module described in the previous section. It then embarks on a phased series of activities coordinated between the client server (see Figure 7.2). The messaging system for this process is the CommandToServer and CommandToClient pair of direct messaging functions.
The server invokes the client MissionStartPhasen (where n is 1, 2, or 3) function to request permission to start each phase. This is done using our old friend CommandToServer. When a client is ready for a phase, it responds with a
MissionStartPhasenAck message, for which there is a handler on the server contained in this module.
The handler GameConnection::onDataBlocksDone is invoked when phase one has finished. This handler then initiates phase two by sending the MissionStartPhase2 message to the client.
The GameConnection::onGhostAlwaysObjects Received handler is invoked when phase two is completed. At the end of this phase, the client has all of the data needed to replicate the server's version of any dynamic objects in the game that are ghosted to the clients. This handler then sends the MissionStartPhase3 message to the client.
When the server receives the MissionStartPhase3Ack message, it then starts the mission for each client, inserting the client into the game.
|
|
The ClientConnection Module |
||
|
|
The ClientConnection module is where |
||
|
|
|||
|
|
most of the server-side code for dealing with |
||
|
|
clients |
is located. Here are the contents |
|
|
|
of the |
common/server/clientconnection.cs |
|
Figure 7.2 Mission download process. |
||||
module. |
Team LRN
Selected Common Server Modules 251
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.com
// Portions Copyright (c) 2001 by Sierra Online, Inc. //-----------------------------------------------------------------------------
function GameConnection::OnConnectRequest( %client, %netAddress, %name )
{
Echo("Connect request from: " @ %netAddress); if($Server::PlayerCount >= $pref::Server::MaxPlayers)
return "CR_SERVERFULL"; return "";
}
function GameConnection::OnConnect( %client, %name )
{
MessageClient(%client,'MsgConnectionError',"",$Pref::Server::ConnectionError);
SendLoadInfoToClient( %client );
if (%client.getAddress() $= "local") { %client.isAdmin = true; %client.isSuperAdmin = true;
}
%client.guid = 0; AddToServerGuidList( %client.guid );
//Set admin status %client.isAdmin = false; %client.isSuperAdmin = false;
//Save client preferences on the Connection object for later use. %client.gender = "Male";
%client.armor = "Light"; %client.race = "Human";
%client.skin = AddTaggedString( "base" ); %client.SetPlayerName(%name); %client.score = 0;
$instantGroup = MissionCleanup;
Team LRN
252 Chapter 7 ■ Common Scripts
Echo("CADD: " @ %client @ " " @ %client.GetAddress());
// Inform the client of all the other clients %count = ClientGroup.GetCount();
for (%cl = 0; %cl < %count; %cl++) { %other = ClientGroup.GetObject(%cl); if ((%other != %client)) {
MessageClient(%client, 'MsgClientJoin', "",
%other.name,
%other,
%other.sendGuid,
%other.score,
%other.IsAIControlled(),
%other.isAdmin,
%other.isSuperAdmin);
}
}
//Inform the client we've joined up MessageClient(%client,
'MsgClientJoin', '\c2Welcome to the Torque demo app %1.', %client.name,
%client,
%client.sendGuid,
%client.score,
%client.IsAiControlled(),
%client.isAdmin,
%client.isSuperAdmin);
//Inform all the other clients of the new guy MessageAllExcept(%client, -1, 'MsgClientJoin', '\c1%1 joined the game.',
%client.name,
%client,
%client.sendGuid,
%client.score,
%client.IsAiControlled(),
%client.isAdmin,
%client.isSuperAdmin);
//If the mission is running, go ahead and download it to the client
if ($missionRunning)
Team LRN
Selected Common Server Modules 253
%client.LoadMission(); $Server::PlayerCount++;
}
function GameConnection::SetPlayerName(%client,%name)
{
%client.SendGuid = 0;
// Minimum length requirements
%name = StripTrailingSpaces( 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.name = 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.name ) ), "\cp\co\c6\c7\c8\c9" );
if ( Strcmp( %name, %rawName ) == 0 ) return false;
}
return true;
}
Team LRN
254Chapter 7 ■ Common Scripts
function GameConnection::OnDrop(%client, %reason)
{
%client.OnClientLeaveGame();
RemoveFromServerGuidList( %client.guid );
MessageAllExcept(%client, -1, 'MsgClientDrop', '\c1%1 has left the game.', %client.name, %client);
RemoveTaggedString(%client.name);
Echo("CDROP: " @ %client @ " " @ %client.GetAddress()); $Server::PlayerCount--;
if( $Server::PlayerCount == 0 && $Server::Dedicated) Schedule(0, 0, "ResetServerDefaults");
}
function GameConnection::StartMission(%this)
{
CommandToClient(%this, 'MissionStart', $missionSequence);
}
function GameConnection::EndMission(%this)
{
CommandToClient(%this, 'MissionEnd', $missionSequence);
}
function GameConnection::SyncClock(%client, %time)
{
CommandToClient(%client, 'syncClock', %time);
}
function GameConnection::IncScore(%this,%delta)
{
%this.score += %delta;
MessageAll('MsgClientScoreChanged', "", %this.score, %this);
}
The following functions and GameConnection methods are defined in the ClientConnection module:
GameConnection::OnConnectRequest
GameConnection::OnConnect
GameConnection::SetPlayerName
Team LRN
Selected Common Server Modules 255
IsNameUnique
GameConnection::OnDrop
GameConnection::StartMission
GameConnection::EndMission
GameConnection::SyncClock
GameConnection::IncScore
The method GameConnection::OnConnectRequest is the server-side destination of the clientside GameConnection::Connect method. We use this method to vet the request—for example, examine the IP address to compare to a ban list, or make sure that the server is not full, and stuff like that. We have to make sure that if we want to allow the request, we must return a null string ( "" ).
The next method, GameConnection::OnConnect, is called after the server has approved the connection request. We get a client handle and a name string passed in as parameters. The first thing we do is ship down to the client a tagged string to indicate that a connection error has happened. We do not tell the client to use this string. It's just a form of preloading the client.
Then we send the load information to the client. This is the mission information that the client can display to the user while the mission loading process takes place. After that, if the client also happens to be the host (entirely possible), we set the client to be a superAdmin.
Then we add the client to the user ID list that the server maintains. After that there are a slew of game play client settings we can initialize.
Next, we start a series of notifications. First, we tell all clients that the player has joined the server. Then we tell the joining player that he is indeed welcome here, despite possible rumors to the contrary. Finally, we tell all the client-players that there is a new kid on the block, so go kill him. Or some such—whatever you feel like!
After all the glad-handing is done, we start downloading the mission data to the client starting the chain of events depicted back there in Figure 7.2.
GameConnection::SetPlayerName does some interesting name manipulation. First, it tidies up any messy names that have leading or trailing spaces. We don't like names that are too short (trying to hide something?), so we don't allow those names. Then we make sure that the name is not already in use. If it is, then an instance number is added to the end of the name. The name is converted to a tagged string so that the full name only gets transmitted once to each client; then the tag number is used after that, if necessary.
The function IsNameUnique searches through the server's name list looking for a match. If it finds the name, then it isn't unique; otherwise it is.
Team LRN
256Chapter 7 ■ Common Scripts
The method GameConnection::OnDrop is called when the decision is made to drop a client. First, the method makes a call to the client so that it knows how to act during the drop. Then it removes the client from its internal list. All clients (except the one dropped) are sent a server text message notifying them of the drop, which they can display. After the last player leaves the game, this method restarts the server. For a persistent game, this statement should probably be removed.
The next method, GameConnection::StartMission, simply notifies clients whenever the server receives a command to start another server session in order to give the clients time to prepare for the near-future availability of the server. The $missionSequence is used to manage mission ordering, if needed.
Next, GameConnection::EndMission is used to notify clients that a mission is ended, and hey! Stop playing already!
The method GameConnection::SyncClock is used to make sure that all clients' timers are synchronized with the server. You can call this function for a client anytime after the mission is loaded, but before the client's player has spawned.
Finally, the method GameConnection::IncScore is called whenever you want to reward a player for doing well. By default, this method is called when a player gets a kill on another player. When the player's score is incremented, all other players are notified, via their clients, of the score.
The Game Module
The server-side Game module is the logical place to put server-specific game play features. Here are the contents of the common/server/game.cs module.
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.com
// Portions Copyright (c) 2001 by Sierra Online, Inc. //-----------------------------------------------------------------------------
function OnServerCreated()
{
$Server::GameType = "Test App"; $Server::MissionType = "Deathmatch";
}
function OnServerDestroyed()
{
DestroyGame();
}
Team LRN
Selected Common Server Modules 257
function OnMissionLoaded()
{
StartGame();
}
function OnMissionEnded()
{
EndGame();
}
function OnMissionReset()
{
// stub
}
function GameConnection::OnClientEnterGame(%this)
{
//stub
}
function GameConnection::OnClientLeaveGame(%this)
{
//stub
}
//-----------------------------------------------------------------------------
// Functions that implement game-play //-----------------------------------------------------------------------------
function StartGame()
{
//stub
}
function EndGame()
{
//stub
}
The following functions and GameConnection methods are defined in the Game module:
OnServerCreated
OnServerDestroyed
OnMissionLoaded
Team LRN