Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

3D Game Programming All In One (2004)

.pdf
Скачиваний:
139
Добавлен:
17.08.2013
Размер:
17.91 Mб
Скачать

248 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