etqw-sdk/source/game/Game_network.cpp

3894 lines
109 KiB
C++
Raw Permalink Normal View History

2008-05-29 00:00:00 +00:00
// Copyright (C) 2007 Id Software, Inc.
//
#include "precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "gamesys/SysCmds.h"
#include "rules/GameRules.h"
#include "rules/AdminSystem.h"
#include "Entity.h"
#include "Player.h"
#include "structures/TeamManager.h"
#include "client/ClientEntity.h"
#include "client/ClientEffect.h"
#include "structures/DeployRequest.h"
#include "roles/FireTeams.h"
#include "roles/ObjectiveManager.h"
#include "roles/Tasks.h"
#include "demos/DemoManager.h"
#include "script/Script_Helper.h"
#include "script/Script_ScriptObject.h"
#include "effects/Effects.h"
#include "effects/Wakes.h"
#include "effects/TireTread.h"
#include "effects/FootPrints.h"
#include "rules/VoteManager.h"
#include "roles/WayPointManager.h"
#include "vehicles/Transport.h"
#include "proficiency/StatsTracker.h"
#include "guis/UserInterfaceManager.h"
#include "../bse/BSEInterface.h"
#include "../bse/BSE_Envelope.h"
#include "../bse/BSE_SpawnDomains.h"
#include "../bse/BSE_Particle.h"
#include "../bse/BSE.h"
#include "botai/BotThreadData.h"
#include "botai/Bot.h"
#include "misc/ProfileHelper.h"
#include "AntiLag.h"
extern rvBSEManager* bse;
/*
===============================================================================
Client running game code:
===============================================================================
*/
idCVar net_clientShowSnapshot( "net_clientShowSnapshot", "0", CVAR_GAME | CVAR_INTEGER, "", 0, 4, idCmdSystem::ArgCompletion_Integer<0,4> );
idCVar net_clientShowSnapshotRadius( "net_clientShowSnapshotRadius", "128", CVAR_GAME | CVAR_FLOAT, "" );
idCVar net_clientShowAOR( "net_clientShowAOR", "0", CVAR_GAME | CVAR_INTEGER, "", 0, 3, idCmdSystem::ArgCompletion_Integer< 0, 3 > );
idCVar net_clientAORFilter( "net_clientAORFilter", "0", CVAR_GAME, "", 0, 0, idClass::ArgCompletion_ClassName );
idCVar net_clientMaxPrediction( "net_clientMaxPrediction", "1000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "maximum number of milliseconds a client can predict ahead of server." );
idCVar net_clientLagOMeter( "net_clientLagOMeter", "0", CVAR_GAME | CVAR_BOOL | CVAR_NOCHEAT | CVAR_ARCHIVE | CVAR_PROFILE, "draw prediction graph" );
idCVar net_clientSelfSmoothing( "net_clientSelfSmoothing", "1", CVAR_GAME | CVAR_BOOL, "smooth local client position" );
idCVar net_serverMaxReservedClientSlots( "net_serverMaxReservedClientSlots", "2", CVAR_GAME | CVAR_INTEGER, "maximum number of player slots reserved for session invites", 0, MAX_CLIENTS, idCmdSystem::ArgCompletion_Integer< 0, MAX_CLIENTS > );
const int RESERVEDCLIENTSLOT_TIMEOUT = 45000;
const int CLIENTBITS = idMath::BitsForInteger( MAX_CLIENTS );
/*
===============================================================================
sdReliableServerMessage
===============================================================================
*/
void sdReliableServerMessage::Send( const sdReliableMessageClientInfoBase& info ) const {
#ifdef SD_SUPPORT_REPEATER
if ( info.SendToRepeaterClients() ) {
networkSystem->RepeaterSendReliableMessage( info.GetClientNum(), *this, info.DontSendToRelays() );
}
#endif // SD_SUPPORT_REPEATER
if ( info.SendToClients() ) {
networkSystem->ServerSendReliableMessage( info.GetClientNum(), *this );
if ( info.SendToAll() && g_debugNetworkWrite.GetBool() ) {
gameLocal.LogNetwork( va( "Reliable Broadcast: %d Size: %d bits\n", type, GetNumBitsWritten() ) );
}
}
}
/*
===============================================================================
sdEntityNetEvent
===============================================================================
*/
/*
================
sdEntityNetEvent::sdEntityNetEvent
================
*/
sdEntityNetEvent::sdEntityNetEvent( void ) {
node.SetOwner( this );
}
/*
================
sdEntityNetEvent::OutputParms
================
*/
void sdEntityNetEvent::OutputParms( idBitMsg& msg ) const {
msg.InitRead( paramsBuf, sizeof( paramsBuf ) );
msg.SetSize( paramsSize );
msg.BeginReading();
}
/*
================
sdEntityNetEvent::Read
================
*/
void sdEntityNetEvent::Read( const idBitMsg& msg ) {
spawnId = msg.ReadLong();
event = msg.ReadByte();
time = msg.ReadLong();
saveEvent = msg.ReadBool();
paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
if ( paramsSize ) {
if ( paramsSize > MAX_EVENT_PARAM_SIZE ) {
gameLocal.NetworkEventWarning( *this, "invalid param size" );
return;
}
msg.ReadData( paramsBuf, paramsSize );
}
}
/*
================
sdEntityNetEvent::GetTotalSize
================
*/
int sdEntityNetEvent::GetTotalSize( void ) const {
return 4 + 1 + 4 + ( ( ( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) + 1 ) + 7 ) >> 3 ) + paramsSize;
}
/*
================
sdEntityNetEvent::Write
================
*/
void sdEntityNetEvent::Write( idBitMsg& msg ) const {
msg.WriteLong( spawnId );
msg.WriteByte( event );
msg.WriteLong( time );
msg.WriteBool( saveEvent );
msg.WriteBits( paramsSize, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
if ( paramsSize ) {
msg.WriteData( paramsBuf, paramsSize );
}
}
/*
================
sdEntityNetEvent::Read
================
*/
void sdEntityNetEvent::Read( idFile* file ) {
file->ReadInt( spawnId );
file->ReadInt( event );
file->ReadInt( time );
file->ReadInt( paramsSize );
file->ReadBool( saveEvent );
if ( paramsSize ) {
file->Read( paramsBuf, paramsSize );
}
}
/*
================
sdEntityNetEvent::Write
================
*/
void sdEntityNetEvent::Write( idFile* file ) const {
file->WriteInt( spawnId );
file->WriteInt( event );
file->WriteInt( time );
file->WriteBool( saveEvent );
file->WriteInt( paramsSize );
if ( paramsSize ) {
file->Write( paramsBuf, paramsSize );
}
}
/*
================
sdEntityNetEvent::Create
================
*/
void sdEntityNetEvent::Create( const idEntity* _entity, int _event, bool _saveEvent, const idBitMsg* _msg ) {
spawnId = gameLocal.GetSpawnId( _entity );
event = _event;
time = gameLocal.time;
saveEvent = _saveEvent;
if ( _msg ) {
paramsSize = _msg->GetSize();
memcpy( paramsBuf, _msg->GetData(), _msg->GetSize() );
} else {
paramsSize = 0;
}
}
/*
================
sdEntityNetEvent::Create
================
*/
void sdEntityNetEvent::Create( const sdEntityNetEvent& other ) {
spawnId = other.spawnId;
event = other.event;
time = other.time;
saveEvent = other.saveEvent;
paramsSize = other.paramsSize;
memcpy( paramsBuf, other.paramsBuf, other.paramsSize );
}
/*
===============================================================================
sdUnreliableEntityNetEvent
===============================================================================
*/
/*
================
sdUnreliableEntityNetEvent::sdUnreliableEntityNetEvent
================
*/
sdUnreliableEntityNetEvent::sdUnreliableEntityNetEvent( void ) {
unreliableNode.SetOwner( this );
#ifdef SD_SUPPORT_REPEATER
numRepeaterClients = 0;
#endif // SD_SUPPORT_REPEATER
}
/*
================
sdUnreliableEntityNetEvent::ClearSent
================
*/
void sdUnreliableEntityNetEvent::ClearSent( void ) {
// mark client slots that aren't occupied as already having been sent
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* client = gameLocal.GetClient( i );
if ( client != NULL && client != gameLocal.GetLocalPlayer() && gameLocal.isServer ) {
sentClients.Clear( i );
} else {
sentClients.Set( i );
}
}
if ( sdDemoManager::GetInstance().GetState() == sdDemoManagerLocal::DS_RECORDING && gameLocal.localClientNum == ASYNC_DEMO_CLIENT_INDEX ) {
sentClients.Clear( MAX_CLIENTS );
} else {
sentClients.Set( MAX_CLIENTS );
}
#ifdef SD_SUPPORT_REPEATER
numRepeaterClients = gameLocal.GetNumRepeaterClients();
sentRepeaterClients.Init( numRepeaterClients );
sentRepeaterClients.SetAll();
for ( int i = 0; i < numRepeaterClients; i++ ) {
if ( gameLocal.IsRepeaterClientConnected( i ) ) {
sentRepeaterClients.Clear( i );
}
}
#endif // SD_SUPPORT_REPEATER
}
/*
================
sdUnreliableEntityNetEvent::SetSent
================
*/
void sdUnreliableEntityNetEvent::SetSent( int clientTo ) {
assert( clientTo >= 0 && clientTo < ( MAX_CLIENTS + 1 ) );
sentClients.Set( clientTo );
}
/*
================
sdUnreliableEntityNetEvent::HasExpired
================
*/
bool sdUnreliableEntityNetEvent::HasExpired( void ) const {
bool allSent = true;
for ( int i = 0; i < MAX_CLIENTS + 1; i++ ) {
if ( sentClients.Get( i ) != 0 ) {
allSent = false;
break;
}
}
#ifdef SD_SUPPORT_REPEATER
for ( int i = 0; i < numRepeaterClients; i++ ) {
if ( sentRepeaterClients.Get( i ) != 0 ) {
allSent = false;
break;
}
}
#endif // SD_SUPPORT_REPEATER
if ( allSent ) {
return true;
}
// expire 2 second old messages
if ( gameLocal.time - GetTime() > 2000 ) {
return true;
}
return false;
}
/*
================
sdUnreliableEntityNetEvent::GetSent
================
*/
bool sdUnreliableEntityNetEvent::GetSent( int clientTo ) const {
return sentClients.Get( clientTo ) != 0;
}
#ifdef SD_SUPPORT_REPEATER
/*
================
sdUnreliableEntityNetEvent::SetRepeaterSent
================
*/
void sdUnreliableEntityNetEvent::SetRepeaterSent( int clientTo ) {
if ( clientTo >= numRepeaterClients ) {
assert( false );
return;
}
sentRepeaterClients.Set( clientTo );
}
/*
================
sdUnreliableEntityNetEvent::GetRepeaterSent
================
*/
bool sdUnreliableEntityNetEvent::GetRepeaterSent( int clientTo ) const {
if ( clientTo >= numRepeaterClients ) {
return true;
}
return sentRepeaterClients.Get( clientTo ) != 0;
}
#endif // SD_SUPPORT_REPEATER
/*
===============================================================================
idGameLocal
===============================================================================
*/
/*
================
idGameLocal::InitAsyncNetwork
================
*/
void idGameLocal::InitAsyncNetwork( void ) {
entityNetEventQueue.Clear();
savedEntityNetEventQueue.Clear();
numEntityDefBits = idMath::BitsForInteger( declEntityDefType.Num() + 1 );
numSkinDeclBits = idMath::BitsForInteger( declSkinType.Num() + 1 );
numDamageDeclBits = idMath::BitsForInteger( declDamageType.Num() + 1 );
numInvItemBits = idMath::BitsForInteger( declInvItemType.Num() + 1 );
numDeployObjectBits = idMath::BitsForInteger( declDeployableObjectType.Num() + 1 );
numPlayerClassBits = idMath::BitsForInteger( declPlayerClassType.Num() + 1 );
numClientIndexBits = idMath::BitsForInteger( MAX_CLIENTS + 1 );
realClientTime = 0;
targetTimers.Clear();
targetTimerLookup.Clear();
numServerTimers = 0;
if ( isClient ) {
SetupGameStateBase( GetNetworkInfo( localClientNum ) );
SetupEntityStateBase( GetNetworkInfo( localClientNum ) );
}
SetupGameStateBase( GetNetworkInfo( ASYNC_DEMO_CLIENT_INDEX ) );
SetupEntityStateBase( GetNetworkInfo( ASYNC_DEMO_CLIENT_INDEX ) );
}
/*
================
idGameLocal::ShutdownAsyncNetwork
================
*/
void idGameLocal::ShutdownAsyncNetwork( void ) {
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
ShutdownClientNetworkState( i );
}
ShutdownClientNetworkState( ASYNC_DEMO_CLIENT_INDEX );
#ifdef SD_SUPPORT_REPEATER
ShutdownClientNetworkState( REPEATER_CLIENT_INDEX );
ShutdownRepeatersNetworkStates();
#endif // SD_SUPPORT_REPEATER
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
sdNetworkStateObject& object = GetGameStateObject( ( extNetworkStateMode_t )i );
object.Clear();
}
snapshotAllocator.Shutdown();
entityNetEventAllocator.Shutdown();
unreliableEntityNetEventAllocator.Shutdown();
}
/*
================
idGameLocal::InitLocalClient
================
*/
void idGameLocal::InitLocalClient( int clientNum, bool server ) {
isServer = server;
isClient = !server;
localClientNum = clientNum;
#ifdef SD_SUPPORT_REPEATER
serverIsRepeater = clientNum == REPEATER_CLIENT_INDEX;
#else
serverIsRepeater = false;
#endif // SD_SUPPORT_REPEATER
if ( isClient ) {
SetupGameStateBase( GetNetworkInfo( clientNum ) );
SetupEntityStateBase( GetNetworkInfo( clientNum ) );
}
sdFireTeamManager::GetInstance().Clear();
}
/*
================
idGameLocal::OnServerShutdown
================
*/
void idGameLocal::OnServerShutdown() {
#ifndef _XENON
sdnet.StopGameSession( true );
if ( networkService->GetDedicatedServerState() == sdNetService::DS_ONLINE ) {
#if !defined( SD_DEMO_BUILD )
sdnet.FlushStats( true );
#endif /* !SD_DEMO_BUILD */
sdnet.SignOutDedicated();
}
#endif
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
ServerClientDisconnect( i );
}
reservedClientSlots.Clear();
isServer = false;
}
/*
================
idGameLocal::ReserveClientSlot
================
*/
void idGameLocal::ReserveClientSlot( const sdNetClientId& netClientId ) {
CheckForExpiredReservedSlots();
if ( reservedClientSlots.Num() == MAX_CLIENTS ) {
// find the oldest one and remove it
int oldestIndex = 0;
int oldestTime = reservedClientSlots[0].time;
for ( int i = 1; i < reservedClientSlots.Num(); i++ ) {
if ( reservedClientSlots[i].time < oldestTime ) {
oldestIndex = i;
oldestTime = reservedClientSlots[i].time;
}
}
reservedClientSlots.RemoveIndexFast( oldestIndex );
}
reservedSlot_t* reservedSlot = reservedClientSlots.Alloc();
reservedSlot->netClientId = netClientId;
reservedSlot->time = sys->Milliseconds();
}
/*
================
idGameLocal::CheckForExpiredReservedSlots
================
*/
void idGameLocal::CheckForExpiredReservedSlots( void ) {
int currentTime = sys->Milliseconds();
int i = 0;
while ( i < reservedClientSlots.Num() ) {
if ( reservedClientSlots[i].time < currentTime - RESERVEDCLIENTSLOT_TIMEOUT ) {
reservedClientSlots.RemoveIndexFast( i );
continue;
}
i++;
}
}
/*
================
idGameLocal::GetMaxPrivateClients
================
*/
int idGameLocal::GetMaxPrivateClients( void ) {
const int MAX_RANKED_PRIVATE_SLOTS = 4;
int max = si_privateClients.GetInteger();
if ( networkSystem->IsRankedServer() ) {
if ( max > MAX_RANKED_PRIVATE_SLOTS ) {
max = MAX_RANKED_PRIVATE_SLOTS;
si_privateClients.SetInteger( MAX_RANKED_PRIVATE_SLOTS );
}
}
return max;
}
/*
================
idGameLocal::ServerAllowClient
================
*/
allowReply_t idGameLocal::ServerAllowClient( int numClients, int numBots, const clientNetworkAddress_t& address, const sdNetClientId& netClientId, const char *guid, const char *password, allowFailureReason_t& reason ) {
clientGUIDLookup_t lookup;
lookup.ip = ( *( int* )address.ip );
lookup.pbid = 0;
lookup.clientId = netClientId;
sdGUIDFile::banState_t banState = guidFile.CheckForBan( lookup );
switch ( banState ) {
case sdGUIDFile::BS_PERM_BAN:
reason = ALLOWFAIL_PERMBAN;
return ALLOW_NO;
case sdGUIDFile::BS_TEMP_BAN:
reason = ALLOWFAIL_TEMPBAN;
return ALLOW_NO;
case sdGUIDFile::BS_NOT_BANNED:
break;
}
if ( serverInfo.GetInt( "si_pure" ) && !rules->IsPureReady() ) {
reason = ALLOWFAIL_SERVERSPAWNING;
return ALLOW_NOTYET;
}
int maxPlayers = si_maxPlayers.GetInteger();
if ( maxPlayers == 0 ) {
reason = ALLOWFAIL_SERVERSPAWNING;
return ALLOW_NOTYET;
}
int maxPrivatePlayers = GetMaxPrivateClients();
if ( maxPrivatePlayers > maxPlayers ) {
maxPrivatePlayers = maxPlayers;
}
int maxRegularPlayers = maxPlayers - maxPrivatePlayers;
// check if there is a reserved slot for this client, expire old slots first
CheckForExpiredReservedSlots();
int numReservedSlots = 0;
if ( !networkSystem->IsRankedServer() ) {
numReservedSlots = Min( reservedClientSlots.Num(), net_serverMaxReservedClientSlots.GetInteger() );
}
int numAvailableSlots = maxRegularPlayers - numReservedSlots;
bool isPrivateClient = false;
const char* privatePass = g_privatePassword.GetString();
if ( *privatePass != '\0' && idStr::Cmp( privatePass, password ) == 0 ) {
isPrivateClient = true;
numAvailableSlots = maxPlayers; // with private password, any slot is available
} else if ( numReservedSlots > 0 ) {
for ( int i = 0; i < reservedClientSlots.Num(); i++ ) {
if ( reservedClientSlots[ i ].netClientId == netClientId ) {
reservedClientSlots.RemoveIndexFast( i );
numAvailableSlots = maxRegularPlayers; // with reserved id, any slot is available, bar the private slots
break;
}
}
}
if ( numClients >= numAvailableSlots ) {
if ( numClients >= maxPrivatePlayers ) {
reason = ALLOWFAIL_SERVERFULL;
return ALLOW_NOTYET;
}
reason = ALLOWFAIL_INVALIDPASSWORD;
return ALLOW_BADPASS;
}
if ( !isPrivateClient ) {
if ( si_needPass.GetBool() ) {
const char* pass = g_password.GetString();
if ( *pass == '\0' ) {
// Gordon: FIXME: This is rubbish
gameLocal.Warning( "si_needPass is set but g_password is empty" );
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "say si_needPass is set but g_password is empty" );
// avoids silent misconfigured state
reason = ALLOWFAIL_SERVERMISCONFIGURED;
return ALLOW_NOTYET;
}
if ( idStr::Cmp( pass, password ) != 0 ) {
reason = ALLOWFAIL_INVALIDPASSWORD;
Printf( "Rejecting client %s from IP '%i.%i.%i.%i': invalid password\n", guid, address.ip[ 0 ], address.ip[ 1 ], address.ip[ 2 ], address.ip[ 3 ] );
return ALLOW_BADPASS;
}
}
}
if ( ( numClients + numBots ) >= numAvailableSlots ) {
// We need to kick a bot to let the player in
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* player = GetClient( i );
if ( player == NULL ) {
continue;
}
if ( player->IsType( idBot::Type ) ) {
networkSystem->ServerKickClient( i, "", false );
break;
}
}
}
return ALLOW_YES;
}
/*
================
idGameLocal::ServerClientConnect
================
*/
void idGameLocal::ServerClientConnect( int clientNum ) {
Printf( "client %d connected.\n", clientNum );
if ( !clientConnected[ clientNum ] ) {
clientConnected[ clientNum ] = true;
clientRanks[ clientNum ].calculated = false;
clientRanks[ clientNum ].rank = -1;
clientStatsRequestsPending = true;
#if !defined( SD_DEMO_BUILD )
clientStatsList[ clientNum ].SetNum( 0, false );
clientStatsHash[ clientNum ].Clear();
#endif /* !SD_DEMO_BUILD */
clientCompaintCount[ clientNum ] = 0;
clientUniqueComplaints[ clientNum ].SetNum( 0, false );
clientLastBanIndexReceived[ clientNum ] = -1;
clientGUIDLookup_t lookup;
clientNetworkAddress_t netAddr;
networkSystem->ServerGetClientNetworkInfo( clientNum, netAddr );
lookup.ip = ( *( int* )netAddr.ip );
lookup.pbid = 0;
networkSystem->ServerGetClientNetId( clientNum, lookup.clientId );
gameLocal.guidFile.AuthUser( clientNum, lookup );
#ifndef _XENON
if ( sdnet.HasGameSession() ) {
sdnet.ServerClientConnect( clientNum );
sdnet.UpdateGameSession( false, false );
}
#endif
}
sdInstanceCollector< sdTransport > vehicles( true );
for ( int i = 0; i < vehicles.Num(); i++ ) {
vehicles[ i ]->GetPositionManager().ResetBan( clientNum );
}
}
/*
================
idGameLocal::ServerClientBegin
================
*/
void idGameLocal::ServerClientBegin( int clientNum, bool isBot ) {
if ( isServer ) { // client needs to get this straight away
sdReliableServerMessage ruleMsg( GAME_RELIABLE_SMESSAGE_RULES_DATA );
ruleMsg.WriteLong( sdGameRules::EVENT_CREATE );
ruleMsg.WriteLong( rules->GetType()->typeNum );
ruleMsg.Send( sdReliableMessageClientInfo( clientNum ) );
rules->WriteInitialReliableMessages( sdReliableMessageClientInfo( clientNum ) );
}
idPlayer* player = GetClient( clientNum );
if ( player == NULL ) {
// spawn the player
SpawnPlayer( clientNum, isBot );
player = GetClient( clientNum );
}
if ( player != NULL ) {
sdProficiencyManager::GetInstance().RestoreProficiency( player );
}
sdGlobalStatsTracker::GetInstance().Restore( clientNum );
SetupFixedClientRank( clientNum );
if ( clientNum == localClientNum ) {
rules->EnterGame( GetLocalPlayer() );
}
}
/*
================
idGameLocal::SetClientNum
================
*/
void idGameLocal::SetClientNum( int clientNum, bool server ) {
InitLocalClient( clientNum, server );
}
/*
================
idGameLocal::ShutdownClientNetworkState
================
*/
void idGameLocal::ShutdownClientNetworkState( int clientNum ) {
GetNetworkInfo( clientNum ).Reset();
}
/*
================
idGameLocal::ServerClientDisconnect
================
*/
void idGameLocal::ServerClientDisconnect( int clientNum ) {
idPlayer* disconnectClient = GetClient( clientNum );
if ( disconnectClient != NULL ) {
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( i == clientNum ) {
continue;
}
idPlayer* player = GetClient( i );
if ( player == NULL ) {
continue;
}
if ( player->GetSpectateClient() != disconnectClient ) {
continue;
}
player->SpectateCycle( true );
if ( player->GetSpectateClient() != disconnectClient ) {
continue;
}
player->SetSpectateClient( player );
}
if ( !disconnectClient->userInfo.isBot ) {
sdProficiencyManager::GetInstance().CacheProficiency( disconnectClient );
}
disconnectClient->PostEventMS( &EV_Remove, 0 );
}
if ( !clientConnected[ clientNum ] ) {
return;
}
Printf( "client %d disconnected.\n", clientNum );
clientConnected[ clientNum ] = false;
#if !defined( SD_DEMO_BUILD )
clientStatsList[ clientNum ].SetNum( 0, false );
clientStatsHash[ clientNum ].Clear();
#endif // SD_DEMO_BUILD
clientRanks[ clientNum ].calculated = false;
clientRanks[ clientNum ].rank = -1;
clientLastBanIndexReceived[ clientNum ] = -1;
clientMuteMask[ clientNum ].Clear();
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
clientMuteMask[ i ].Clear( clientNum );
}
for ( int i = 0; i < endGameStats.Num(); i++ ) {
endGameStats[ i ].data[ clientNum ] = -1.f;
}
if ( botThreadData.GetGameWorldState() != NULL ) {
botThreadData.InitClientInfo( clientNum, true, true );
}
ShutdownClientNetworkState( clientNum );
for ( int i = 0; i < declVehicleScriptDefType.Num(); i++ ) {
const sdDeclVehicleScript* script = declVehicleScriptDefType.LocalFindByIndex( i, false );
script->ResetCameraMode( clientNum );
}
rules->DisconnectClient( clientNum );
#ifndef _XENON
if ( sdnet.HasGameSession() ) {
sdnet.ServerClientDisconnect( clientNum );
sdnet.UpdateGameSession( false, false );
}
#endif
}
/*
================
idGameLocal::ServerWriteInitialReliableMessages
Send reliable messages to initialize the client game up to a certain initial state.
================
*/
void idGameLocal::ServerWriteInitialReliableMessages( int clientNum ) {
WriteInitialReliableMessages( sdReliableMessageClientInfo( clientNum ) );
}
/*
================
idGameLocal::WriteInitialReliableMessages
================
*/
void idGameLocal::WriteInitialReliableMessages( const sdReliableMessageClientInfoBase& target ) {
assert( !target.SendToAll() );
if ( target.SendToClients() ) {
idPlayer* player = GetClient( target.GetClientNum() );
if ( player == NULL ) {
Error( "idGameLocal::ServerWriteInitialReliableMessages Client '%d' was NULL", target.GetClientNum() );
}
}
SendPauseInfo( target );
for ( int i = 0; i < targetTimers.Num(); i++ ) {
sdReliableServerMessage outMsg( GAME_RELIABLE_SMESSAGE_CREATEPLAYERTARGETTIMER );
outMsg.WriteString( targetTimers[ i ].name );
outMsg.WriteShort( i );
outMsg.Send( target );
}
int count = 0;
int totalCount = 0;
int totalBits = 0;
int batchCount = 0;
count = networkedEntities.Num();
totalCount = count;
batchCount = 0;
idEntity* ent = networkedEntities.Next();
totalBits = 0;
while ( count > 0 ) {
const int MAX_COMBINED_CREATE = 128;
int use = Min( count, MAX_COMBINED_CREATE );
batchCount++;
sdReliableServerMessage entMsg( GAME_RELIABLE_SMESSAGE_MULTI_CREATE_ENT );
entMsg.WriteLong( use );
for ( int j = 0; j < use; j++ ) {
ent->WriteCreateEvent( &entMsg, target );
ent = ent->networkNode.Next();
}
entMsg.Send( target );
totalBits += entMsg.GetNumBitsWritten();
count -= use;
}
// gameLocal.Printf( "idGameLocal::ServerWriteInitialReliableMessages Writing %d Entities Using %d bits in %d batches\n", totalCount, totalBits, batchCount );
// send all saved events
totalCount = 0;
batchCount = 0;
sdEntityNetEvent* start = savedEntityNetEventQueue.Next();
while ( start != NULL ) {
count = 0;
const int maxEventSizeTotal = MAX_GAME_MESSAGE_SIZE - 16;
sdEntityNetEvent* last = start;
int totalSize = 0;
while ( last != NULL ) {
totalSize += last->GetTotalSize();
if ( totalSize >= maxEventSizeTotal ) {
break;
}
count++;
totalCount++;
last = last->GetNode().Next();
}
assert( count > 0 );
batchCount++;
sdReliableServerMessage evtMsg( GAME_RELIABLE_SMESSAGE_MULTI_ENTITY_EVENT );
evtMsg.WriteLong( count );
while ( start != last ) {
idEntity* temp = gameLocal.EntityForSpawnId( start->GetSpawnId() );
if ( temp == NULL ) {
gameLocal.Warning( "idGameLocal::ServerWriteInitialReliableMessages NULL entity" );
}
start->Write( evtMsg );
start = start->GetNode().Next();
}
evtMsg.Send( target );
totalBits += evtMsg.GetNumBitsWritten();
}
// gameLocal.Printf( "idGameLocal::ServerWriteInitialReliableMessages Writing %d Saved Events Using %d bits in %d batches\n", totalCount, totalBits, batchCount );
for ( idEntity* ent = networkedEntities.Next(); ent; ent = ent->networkNode.Next() ) {
ent->WriteInitialReliableMessages( target );
}
for ( int i = 0; i < NUM_DEPLOY_REQUESTS; i++ ) {
if ( !deployRequests[ i ] ) {
continue;
}
deployRequests[ i ]->WriteCreateEvent( i, target );
}
sdTaskManager::GetInstance().WriteInitialReliableMessages( target );
sdFireTeamManager::GetInstance().WriteInitialReliableMessages( target );
sdObjectiveManager::GetInstance().WriteInitialReliableMessages( target );
sdUserGroupManager::GetInstance().WriteNetworkData( target );
SendEndGameStats( target );
}
/*
================
idGameLocal::ClientWriteGameState
================
*/
void idGameLocal::ClientWriteGameState( idFile* file ) {
file->WriteBool( isPaused );
file->WriteInt( time );
file->WriteInt( timeOffset );
assert( rules != NULL );
file->WriteInt( rules->GetType()->typeNum );
file->WriteInt( targetTimers.Num() );
for ( int i = 0; i < targetTimers.Num(); i++ ) {
file->WriteString( targetTimers[ i ].name );
file->WriteInt( targetTimers[ i ].serverHandle );
if ( localClientNum != ASYNC_DEMO_CLIENT_INDEX ) {
file->WriteInt( targetTimers[ i ].endTimes[ localClientNum ] );
}
}
file->WriteInt( networkedEntities.Num() );
for ( idEntity* ent = networkedEntities.Next(); ent; ent = ent->networkNode.Next() ) {
ent->WriteCreationData( file );
ASYNC_SECURITY_WRITE_FILE( file )
}
// Gordon: now seperate so the client can create all entities before reading this data
for ( idEntity* ent = networkedEntities.Next(); ent; ent = ent->networkNode.Next() ) {
ent->WriteDemoBaseData( file );
ASYNC_SECURITY_WRITE_FILE( file )
}
file->WriteInt( entityNetEventQueue.Num() );
for ( sdEntityNetEvent* event = entityNetEventQueue.Next(); event; event = event->GetNode().Next() ) {
event->Write( file );
ASYNC_SECURITY_WRITE_FILE( file )
}
for ( int i = 0; i < NUM_DEPLOY_REQUESTS; i++ ) {
if ( !deployRequests[ i ] ) {
file->WriteBool( false );
continue;
}
file->WriteBool( true );
deployRequests[ i ]->Write( file );
}
sdTaskManager::GetInstance().Write( file );
sdFireTeamManager::GetInstance().Write( file );
sdUserGroupManager::GetInstance().Write( file );
}
/*
================
idGameLocal::ClientReadGameState
================
*/
void idGameLocal::ClientReadGameState( idFile* file ) {
file->ReadBool( isPaused );
file->ReadInt( time );
file->ReadInt( timeOffset );
int rulesTypeNum;
file->ReadInt( rulesTypeNum );
SetRules( idClass::GetType( rulesTypeNum ) );
ClearTargetTimers();
int count;
file->ReadInt( count );
targetTimers.SetNum( count );
numServerTimers = 0;
for ( int i = 0; i < count; i++ ) {
int serverHandle;
file->ReadString( targetTimers[ i ].name );
file->ReadInt( serverHandle );
targetTimers[ i ].serverHandle = serverHandle;
if ( localClientNum != ASYNC_DEMO_CLIENT_INDEX ) {
file->ReadInt( targetTimers[ i ].endTimes[ localClientNum ] );
}
if ( serverHandle != -1 ) {
numServerTimers = Max( serverHandle + 1, numServerTimers );
if ( numServerTimers > targetTimerLookup.Num() ) {
targetTimerLookup.AssureSize( numServerTimers, -1 );
}
targetTimerLookup[ serverHandle ] = i;
}
}
idList< idEntity* > creationEnts;
file->ReadInt( count );
creationEnts.SetNum( count );
for ( int i = 0; i < count; i++ ) {
creationEnts[ i ] = idEntity::FromCreationData( file );
if ( !creationEnts[ i ] ) {
gameLocal.Error( "idGameLocal::ClientReadGameState Failed to spawn entity from demo" );
}
ASYNC_SECURITY_READ_FILE( file )
}
// Gordon: moved this out of the above loop so all entities are spawned before they read their creation data
// as they may want to link to an existing entity further down the chain
for ( int i = 0; i < count; i++ ) {
creationEnts[ i ]->LoadDemoBaseData( file );
ASYNC_SECURITY_READ_FILE( file )
}
file->ReadInt( count );
for ( int i = 0; i < count; i++ ) {
sdEntityNetEvent* event = entityNetEventAllocator.Alloc();
event->GetNode().AddToEnd( entityNetEventQueue );
event->Read( file );
ASYNC_SECURITY_READ_FILE( file )
}
ClearDeployRequests();
for ( int i = 0; i < NUM_DEPLOY_REQUESTS; i++ ) {
bool temp;
file->ReadBool( temp );
if ( !temp ) {
continue;
}
deployRequests[ i ] = new sdDeployRequest( file );
}
sdTaskManager::GetInstance().Read( file );
sdFireTeamManager::GetInstance().Read( file );
sdUserGroupManager::GetInstance().Read( file );
}
/*
================
idGameLocal::FreeEntityNetworkEvents
================
*/
void idGameLocal::FreeEntityNetworkEvents( const idEntity *ent, int eventId ) {
sdEntityNetEvent* next;
for ( sdEntityNetEvent* event = savedEntityNetEventQueue.Next(); event; event = next ) {
next = event->GetNode().Next();
if ( eventId == -1 || event->GetEvent() == eventId ) {
idEntity* eventEnt = gameLocal.EntityForSpawnId( event->GetSpawnId() );
if ( eventEnt == ent ) {
event->GetNode().Remove();
entityNetEventAllocator.Free( event );
}
}
}
}
/*
================
idGameLocal::SaveEntityNetworkEvent
================
*/
void idGameLocal::SaveEntityNetworkEvent( const sdEntityNetEvent& oldEvent ) {
sdEntityNetEvent* event;
event = entityNetEventAllocator.Alloc();
event->Create( oldEvent );
event->GetNode().AddToEnd( savedEntityNetEventQueue );
}
/*
================
idGameLocal::SendUnreliableEntityNetworkEvent
================
*/
void idGameLocal::SendUnreliableEntityNetworkEvent( const idEntity *ent, int eventId, const idBitMsg *msg ) {
sdUnreliableEntityNetEvent* event;
event = unreliableEntityNetEventAllocator.Alloc();
event->Create( ent, eventId, false, msg );
event->ClearSent();
event->GetUnreliableNode().AddToEnd( unreliableEntityNetEventQueue );
}
/*
================
idGameLocal::SendUnreliableEntityNetworkEvent
================
*/
void idGameLocal::SendUnreliableEntityNetworkEvent( const sdUnreliableEntityNetEvent& oldEvent ) {
sdUnreliableEntityNetEvent* event;
event = unreliableEntityNetEventAllocator.Alloc();
event->Create( oldEvent );
event->ClearSent();
event->GetUnreliableNode().AddToEnd( unreliableEntityNetEventQueue );
}
/*
================
idGameLocal::WriteUnreliableEntityEvents
================
*/
void idGameLocal::WriteUnreliableEntityNetEvents( int clientNum, bool repeaterClient, idBitMsg &msg ) {
// write unreliable entity network events to the snapshot
// first count them up
int numEvents = 0;
sdUnreliableEntityNetEvent* event;
sdUnreliableEntityNetEvent* nextEvent;
for ( event = unreliableEntityNetEventQueue.Next(); event != NULL; event = nextEvent ) {
nextEvent = event->GetUnreliableNode().Next();
#ifdef SD_SUPPORT_REPEATER
if ( repeaterClient ) {
if ( !event->GetRepeaterSent( clientNum ) ) {
numEvents++;
}
} else
#endif // SD_SUPPORT_REPEATER
{
if ( !event->GetSent( clientNum ) ) {
numEvents++;
}
}
}
// now write them
msg.WriteLong( numEvents );
for ( event = unreliableEntityNetEventQueue.Next(); event != NULL; event = nextEvent ) {
nextEvent = event->GetUnreliableNode().Next();
#ifdef SD_SUPPORT_REPEATER
if ( repeaterClient ) {
if ( !event->GetRepeaterSent( clientNum ) ) {
event->Write( msg );
event->SetRepeaterSent( clientNum );
}
} else
#endif // SD_SUPPORT_REPEATER
{
if ( !event->GetSent( clientNum ) ) {
event->Write( msg );
event->SetSent( clientNum );
}
}
// check if the event has expired
if ( event->HasExpired() ) {
event->GetUnreliableNode().Remove();
unreliableEntityNetEventAllocator.Free( event );
}
}
}
/*
================
idGameLocal::FreeSnapshotsOlderThanSequence
================
*/
void idGameLocal::FreeSnapshotsOlderThanSequence( clientNetworkInfo_t& nwInfo, int sequence ) {
snapshot_t* nextSnapshot;
snapshot_t* lastSnapshot = NULL;
for ( snapshot_t* snapshot = nwInfo.snapshots; snapshot; snapshot = nextSnapshot ) {
nextSnapshot = snapshot->next;
if ( snapshot->sequence < sequence ) {
if ( lastSnapshot ) {
lastSnapshot->next = snapshot->next;
} else {
nwInfo.snapshots = snapshot->next;
}
for ( int i = 0; i < NSM_NUM_MODES; i++ ) {
sdEntityState* nextState;
for ( sdEntityState* state = snapshot->firstEntityState[ i ]; state; state = nextState ) {
nextState = state->next;
idEntity* ent = EntityForSpawnId( state->GetSpawnId() );
if ( ent ) {
state->next = ent->freeStates[ i ];
ent->freeStates[ i ] = state;
} else {
FreeNetworkState( state );
}
}
}
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
if ( snapshot->gameStates[ i ] == NULL ) {
continue;
}
sdNetworkStateObject& obj = GetGameStateObject( ( extNetworkStateMode_t )i );
snapshot->gameStates[ i ]->next = obj.freeStates;
obj.freeStates = snapshot->gameStates[ i ];
}
snapshotAllocator.Free( snapshot );
} else {
lastSnapshot = snapshot;
}
}
}
/*
================
idGameLocal::ApplySnapshot
================
*/
bool idGameLocal::ApplySnapshot( clientNetworkInfo_t& nwInfo, int sequence ) {
snapshot_t *snapshot, *lastSnapshot, *nextSnapshot;
FreeSnapshotsOlderThanSequence( nwInfo, sequence );
for ( lastSnapshot = NULL, snapshot = nwInfo.snapshots; snapshot; snapshot = nextSnapshot ) {
nextSnapshot = snapshot->next;
if ( snapshot->sequence == sequence ) {
if ( lastSnapshot ) {
lastSnapshot->next = nextSnapshot;
} else {
nwInfo.snapshots = nextSnapshot;
}
for ( int i = 0; i < NSM_NUM_MODES; i++ ) {
sdEntityState* next;
for ( sdEntityState* state = snapshot->firstEntityState[ i ]; state; state = next ) {
next = state->next;
idEntity* ent = EntityForSpawnId( state->GetSpawnId() );
if ( ent ) {
sdEntityState* oldState = nwInfo.states[ ent->entityNumber ][ i ];
oldState->next = ent->freeStates[ i ];
ent->freeStates[ i ] = oldState;
nwInfo.states[ ent->entityNumber ][ i ] = state;
state->next = NULL;
} else {
FreeNetworkState( state );
}
}
}
for ( int i = 0; i < snapshot->visibleEntities.Num(); i++ ) {
nwInfo.lastMarker[ snapshot->visibleEntities[ i ] ] = snapshot->time;
}
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( snapshot->clientUserCommands.Get( i ) ) {
if ( nwInfo.lastUserCommand[ i ] != -1 ) {
nwInfo.lastUserCommandDelay[ i ] = snapshot->time - nwInfo.lastUserCommand[ i ];
} else {
nwInfo.lastUserCommandDelay[ i ] = 0;
}
nwInfo.lastUserCommand[ i ] = snapshot->time;
}
}
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
if ( !snapshot->gameStates[ i ] ) {
continue;
}
sdNetworkStateObject& object = GetGameStateObject( ( extNetworkStateMode_t )i );
sdGameState* oldState = nwInfo.gameStates[ i ];
oldState->next = object.freeStates;
object.freeStates = oldState;
nwInfo.gameStates[ i ] = snapshot->gameStates[ i ];
}
snapshotAllocator.Free( snapshot );
return true;
} else {
lastSnapshot = snapshot;
}
}
return false;
}
/*
================
idGameLocal::ServerWriteSnapshot
================
*/
snapshot_t* idGameLocal::AllocateSnapshot( int sequence, clientNetworkInfo_t& nwInfo ) {
// allocate new snapshot
snapshot_t* snapshot = snapshotAllocator.Alloc();
snapshot->Init( sequence, time );
snapshot->next = nwInfo.snapshots;
nwInfo.snapshots = snapshot;
activeSnapshot = snapshot;
snapshot->visibleEntities.SetNum( 0, false );
snapshot->clientUserCommands.Clear();
return snapshot;
}
/*
================
idGameLocal::WriteSnapshotGameStates
================
*/
void idGameLocal::WriteSnapshotGameStates( snapshot_t* snapshot, clientNetworkInfo_t& nwInfo, idBitMsg& msg ) {
// ASYNC_SECURITY_WRITE( msg )
WriteGameState( ENSM_TEAM, snapshot, nwInfo, msg );
// ASYNC_SECURITY_WRITE( msg )
// ASYNC_SECURITY_WRITE( msg )
WriteGameState( ENSM_RULES, snapshot, nwInfo, msg );
// ASYNC_SECURITY_WRITE( msg )
}
/*
================
idGameLocal::WriteSnapshotEntityStates
================
*/
void idGameLocal::WriteSnapshotEntityStates( snapshot_t* snapshot, clientNetworkInfo_t& nwInfo, int clientNum, idBitMsg& msg, bool useAOR ) {
idStaticList< idEntity*, MAX_GENTITIES > visibleEntities;
idStaticList< idEntity*, MAX_GENTITIES > broadcastEntities;
idEntity* ent = NULL;
if ( useAOR ) {
for ( ent = networkedEntities.Next(); ent != NULL; ent = ent->networkNode.Next() ) {
ent->snapshotPVSFlags = PVS_VISIBLE;
bool update = true;
aorManager.UpdateEntityAORFlags( ent, ent->GetPhysics()->GetOrigin(), update );
sdEntityState* baseState;
if ( ent != snapShotPlayer && ent->entityNumber != clientNum && !update ) {
ent->snapshotPVSFlags &= ~( PVS_VISIBLE );
} else {
baseState = nwInfo.states[ ent->entityNumber ][ NSM_VISIBLE ];
if ( ent->CheckNetworkStateChanges( NSM_VISIBLE, *baseState->data ) ) {
*visibleEntities.Alloc() = ent;
} else {
ent->snapshotPVSFlags &= ~( PVS_VISIBLE );
}
snapshot->visibleEntities.Alloc() = ent->entityNumber;
}
baseState = nwInfo.states[ ent->entityNumber ][ NSM_BROADCAST ];
if ( ent->CheckNetworkStateChanges( NSM_BROADCAST, *baseState->data ) ) {
*broadcastEntities.Alloc() = ent;
}
}
} else {
for( ent = networkedEntities.Next(); ent != NULL; ent = ent->networkNode.Next() ) {
ent->snapshotPVSFlags = PVS_VISIBLE;
snapshot->visibleEntities.Alloc() = ent->entityNumber;
sdEntityState* baseState;
baseState = nwInfo.states[ ent->entityNumber ][ NSM_VISIBLE ];
if ( ent->CheckNetworkStateChanges( NSM_VISIBLE, *baseState->data ) ) {
*visibleEntities.Alloc() = ent;
} else {
ent->snapshotPVSFlags &= ~( PVS_VISIBLE );
}
baseState = nwInfo.states[ ent->entityNumber ][ NSM_BROADCAST ];
if ( ent->CheckNetworkStateChanges( NSM_BROADCAST, *baseState->data ) ) {
*broadcastEntities.Alloc() = ent;
}
}
}
for ( int i = 0; i < visibleEntities.Num(); i++ ) {
idEntity* ent = visibleEntities[ i ];
int oldBits = msg.GetNumBitsWritten();
msg.WriteBits( ent->entityNumber, GENTITYNUM_BITS );
sdEntityState* baseState = nwInfo.states[ ent->entityNumber ][ NSM_VISIBLE ];
sdEntityState* newState = AllocEntityState( NSM_VISIBLE, ent );
// ASYNC_SECURITY_WRITE( msg )
ent->WriteNetworkState( NSM_VISIBLE, *baseState->data, *newState->data, msg );
// ASYNC_SECURITY_WRITE( msg )
if ( g_debugNetworkWrite.GetBool() ) {
int bitsWritten = msg.GetNumBitsWritten() - oldBits;
gameLocal.LogNetwork( va( "Writing Visible: '%s' '%s', %d bits\n", ent->GetType()->classname, ent->name.c_str(), bitsWritten ) );
}
newState->next = snapshot->firstEntityState[ NSM_VISIBLE ];
snapshot->firstEntityState[ NSM_VISIBLE ] = newState;
}
msg.WriteBits( ENTITYNUM_NONE, GENTITYNUM_BITS );
for ( int i = 0; i < broadcastEntities.Num(); i++ ) {
idEntity* ent = broadcastEntities[ i ];
int oldBits = msg.GetNumBitsWritten();
msg.WriteBits( ent->entityNumber, GENTITYNUM_BITS );
sdEntityState* baseState = nwInfo.states[ ent->entityNumber ][ NSM_BROADCAST ];
sdEntityState* newState = AllocEntityState( NSM_BROADCAST, ent );
// ASYNC_SECURITY_WRITE( msg )
ent->WriteNetworkState( NSM_BROADCAST, *baseState->data, *newState->data, msg );
// ASYNC_SECURITY_WRITE( msg )
if ( g_debugNetworkWrite.GetBool() ) {
int bitsWritten = msg.GetNumBitsWritten() - oldBits;
gameLocal.LogNetwork( va( "Writing Broadcast: '%s' '%s', %d bits\n", ent->GetType()->classname, ent->name.c_str(), bitsWritten ) );
}
newState->next = snapshot->firstEntityState[ NSM_BROADCAST ];
snapshot->firstEntityState[ NSM_BROADCAST ] = newState;
}
msg.WriteBits( ENTITYNUM_NONE, GENTITYNUM_BITS );
}
/*
================
idGameLocal::WriteSnapshotUserCmds
================
*/
void idGameLocal::WriteSnapshotUserCmds( snapshot_t* snapshot, idBitMsg& msg, int ignoreClientNum, bool useAOR ) {
// write the latest user commands from the other clients in the PVS to the snapshot
// written to a seperate idBitMsg, which uses a different compression strategy
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( i == ignoreClientNum ) {
continue;
}
idPlayer* otherPlayer = GetClient( i );
if ( !otherPlayer ) {
continue;
}
if ( useAOR && otherPlayer != snapShotPlayer ) {
if ( otherPlayer->GetProxyEntity() == NULL ) {
if ( otherPlayer->aorFlags & AOR_INHIBIT_USERCMDS ) {
continue;
}
} else {
if ( otherPlayer->GetProxyEntity()->aorFlags & AOR_INHIBIT_USERCMDS ) {
continue;
}
}
}
msg.WriteBits( i, CLIENTBITS );
networkSystem->WriteClientUserCmds( i, msg );
// record that this client had its user command's sent with this snapshot
snapshot->clientUserCommands.Set( i );
if ( !IsPaused() ) {
sdAntiLagManager::GetInstance().CreateUserCommandBranch( otherPlayer );
}
}
msg.WriteBits( MAX_CLIENTS, CLIENTBITS );
}
/*
================
idGameLocal::ServerWriteSnapshot
Write a snapshot of the current game state for the given client.
================
*/
void idGameLocal::ServerWriteSnapshot( int clientNum, int sequence, idBitMsg &msg, idBitMsg &ucmdmsg ) {
int oldBitsMain = msg.GetNumBitsWritten();
if ( g_debugNetworkWrite.GetBool() ) {
gameLocal.LogNetwork( va( "Writing Network State for client %d\n\n", clientNum ) );
}
bool useAOR = net_useAOR.GetBool();
snapShotClientIsRepeater = false; // Gordon: Main server will never have repeaters connected to it
SetSnapShotClient( NULL );
SetSnapShotPlayer( NULL );
if ( clientNum != ASYNC_DEMO_CLIENT_INDEX ) {
idPlayer* player = GetClient( clientNum );
if ( !player ) {
return;
}
SetSnapShotClient( player );
SetSnapShotPlayer( player->GetSpectateClient() );
aorManager.SetClient( snapShotPlayer );
} else {
useAOR = false;
}
clientNetworkInfo_t& nwInfo = GetNetworkInfo( clientNum );
// free too old snapshots
FreeSnapshotsOlderThanSequence( nwInfo, sequence - 64 );
snapshot_t* snapshot = AllocateSnapshot( sequence, nwInfo );
WriteSnapshotGameStates( snapshot, nwInfo, msg );
WriteSnapshotEntityStates( snapshot, nwInfo, clientNum, msg, useAOR );
WriteUnreliableEntityNetEvents( clientNum, false, msg );
WriteSnapshotUserCmds( snapshot, ucmdmsg, clientNum, useAOR );
snapShotClientIsRepeater = false;
SetSnapShotClient( NULL );
SetSnapShotPlayer( NULL );
if ( g_debugNetworkWrite.GetBool() ) {
int bitsWrittenMain = msg.GetNumBitsWritten() - oldBitsMain;
gameLocal.LogNetwork( va( "\nTotal Bits Written: %d\nFinished Writing Network State for client %d\n\n", bitsWrittenMain, clientNum ) );
}
}
/*
================
idGameLocal::ServerApplySnapshot
================
*/
bool idGameLocal::ServerApplySnapshot( int clientNum, int sequence ) {
return ApplySnapshot( GetNetworkInfo( clientNum ), sequence );
}
/*
================
idGameLocal::NetworkEventWarning
================
*/
void idGameLocal::NetworkEventWarning( const sdEntityNetEvent& event, const char *fmt, ... ) {
char buf[1024];
int length = 0;
va_list argptr;
length += idStr::snPrintf( buf+length, sizeof(buf)-1-length, "event %d for entity %d %d: ", event.GetEvent(), event.GetEntityNumber(), event.GetId() );
va_start( argptr, fmt );
length = idStr::vsnPrintf( buf+length, sizeof(buf)-1-length, fmt, argptr );
va_end( argptr );
// idStr::Append( buf, sizeof(buf), "\n" ); - warning puts a \n already
gameLocal.DWarning( buf );
}
/*
================
idGameLocal::UnreliableNetworkEventWarning
================
*/
void idGameLocal::UnreliableNetworkEventWarning( const sdUnreliableEntityNetEvent& event, const char *fmt, ... ) {
char buf[1024];
int length = 0;
va_list argptr;
length += idStr::snPrintf( buf+length, sizeof(buf)-1-length, "unreliable event %d for entity %d %d: ", event.GetEvent(), event.GetEntityNumber(), event.GetId() );
va_start( argptr, fmt );
length = idStr::vsnPrintf( buf+length, sizeof(buf)-1-length, fmt, argptr );
va_end( argptr );
// idStr::Append( buf, sizeof(buf), "\n" ); - warning puts a \n already
gameLocal.DWarning( buf );
}
/*
================
idGameLocal::ServerSendQuickChatMessage
================
*/
void idGameLocal::ServerSendQuickChatMessage( idPlayer* player, const sdDeclQuickChat* quickChat, idPlayer* recipient, idEntity* target ) {
if ( !quickChat ) {
return;
}
if ( !isClient ) {
if ( player != NULL ) {
//mal: update this players chat time and what the chat was, so the bots can see it, and respond if needed.
if ( !botThreadData.GetGameWorldState()->clientInfo[ player->entityNumber ].isBot ) {
if ( quickChat->GetType() != NULL_CHAT ) {
botThreadData.UpdateChats( player->entityNumber, quickChat );
}
}
const char* callback = quickChat->GetCallback();
if ( *callback ) {
sdScriptHelper helper;
if ( target != NULL ) {
helper.Push( target->GetScriptObject() );
}
player->CallNonBlockingScriptEvent( player->scriptObject->GetFunction( callback ), helper );
}
}
if ( player != NULL && recipient == NULL ) {
if ( gameLocal.rules->MuteStatus( player ) & MF_CHAT ) {
const sdUserGroup& userGroup = sdUserGroupManager::GetInstance().GetGroup( player->GetUserGroup() );
if ( !userGroup.HasPermission( PF_NO_MUTE ) ) {
player->SendLocalisedMessage( declHolder.declLocStrType[ "rules/messages/muted" ], idWStrList() );
return;
}
}
if ( !quickChat->IsTeam() ) {
if ( si_disableGlobalChat.GetBool() ) {
const sdUserGroup& userGroup = sdUserGroupManager::GetInstance().GetGroup( player->GetUserGroup() );
if ( !userGroup.HasPermission( PF_NO_MUTE ) ) {
player->SendLocalisedMessage( declHolder.declLocStrType[ "rules/messages/globalchatdisabled" ], idWStrList() );
return;
}
}
}
}
}
int sendingClientNum = player == NULL ? -1 : player->entityNumber;
idVec3 location = player == NULL ? vec3_origin : player->GetPhysics()->GetOrigin();
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* other = GetClient( i );
if ( !other ) {
continue;
}
if ( recipient && other != recipient ) {
continue;
}
if ( !quickChat->Check( other, player ) ) {
continue;
} else if ( quickChat->IsTeam() && player != NULL ) {
if ( player->GetEntityAllegiance( other ) != TA_FRIEND ) {
continue;
}
}
other->PlayQuickChat( location, sendingClientNum, quickChat, target );
}
}
/*
================
idGameLocal::ServerProcessReliableMessage
================
*/
void idGameLocal::ServerProcessReliableMessage( int clientNum, const idBitMsg &msg ) {
gameReliableClientMessage_t id = static_cast< gameReliableClientMessage_t >( msg.ReadBits( idMath::BitsForInteger( GAME_RELIABLE_CMESSAGE_NUM_MESSAGES ) ) );
idPlayer* client = GetClient( clientNum );
if ( !client ) {
return;
}
switch( id ) {
case GAME_RELIABLE_CMESSAGE_CHAT:
case GAME_RELIABLE_CMESSAGE_TEAM_CHAT: {
case GAME_RELIABLE_CMESSAGE_FIRETEAM_CHAT:
wchar_t text[ 128 ];
msg.ReadString( text, sizeof( text ) / sizeof( wchar_t ) );
rules->ProcessChatMessage( client, id, text );
break;
}
case GAME_RELIABLE_CMESSAGE_QUICK_CHAT: {
const sdDeclQuickChat* quickChat = declQuickChatType.SafeIndex( msg.ReadLong() );
int spawnId = msg.ReadLong();
client->RequestQuickChat( quickChat, spawnId );
break;
}
case GAME_RELIABLE_CMESSAGE_KILL: {
rules->WantKilled( client );
break;
}
case GAME_RELIABLE_CMESSAGE_GOD: {
ToggleGodMode( client );
break;
}
case GAME_RELIABLE_CMESSAGE_NOCLIP: {
ToggleNoclipMode( client );
break;
}
case GAME_RELIABLE_CMESSAGE_NETWORKSPAWN: {
char classname[128];
msg.ReadString( classname, sizeof( classname ) );
NetworkSpawn( classname, client );
break;
}
case GAME_RELIABLE_CMESSAGE_IMPULSE: {
client->PerformImpulse( msg.ReadBits( 6 ) );
break;
}
case GAME_RELIABLE_CMESSAGE_GUISCRIPT: {
idEntity* entity = EntityForSpawnId( msg.ReadBits( 32 ) );
if ( !entity ) {
break;
}
const int MAX_GUISCRIPT_STRING = 1024;
char buffer[ MAX_GUISCRIPT_STRING ];
msg.ReadString( buffer, MAX_GUISCRIPT_STRING );
HandleGuiScriptMessage( client, entity, buffer );
break;
}
case GAME_RELIABLE_CMESSAGE_NETWORKCOMMAND: {
idEntity* entity = EntityForSpawnId( msg.ReadBits( 32 ) );
if ( !entity ) {
break;
}
const int MAX_GUISCRIPT_STRING = 1024;
char buffer[ MAX_GUISCRIPT_STRING ];
msg.ReadString( buffer, MAX_GUISCRIPT_STRING );
HandleNetworkMessage( client, entity, buffer );
break;
}
case GAME_RELIABLE_CMESSAGE_TEAMSWITCH: {
int index = msg.ReadLong( );
char buffer[ 256 ];
char buffer2[ 256 ];
msg.ReadString( buffer, sizeof( buffer ) );
bool useBuffer2 = false;
if ( index == sdTeamManager::GetInstance().GetNumTeams() + 1 ) {
msg.ReadString( buffer2, sizeof( buffer2 ) );
// auto join team with the least number of players
int lowest = -1;
int clientTeamIndex = client->GetTeam() != NULL ? client->GetTeam()->GetIndex() : -1;
for ( int i = 0; i < sdTeamManager::GetInstance().GetNumTeams(); i++ ) {
int num = sdTeamManager::GetInstance().GetTeamByIndex( i ).GetNumPlayers();
if ( clientTeamIndex == i ) {
num--;
}
if ( num < lowest || lowest == -1 ) {
lowest = num;
index = i + 1;
}
}
if ( index == 2 ) {
// use password for team 2
useBuffer2 = true;
}
}
rules->SetClientTeam( client, index, false, useBuffer2 == false ? buffer : buffer2 );
break;
}
case GAME_RELIABLE_CMESSAGE_CLASSSWITCH: {
int index = msg.ReadLong();
int classOption = msg.ReadLong();
const sdDeclPlayerClass* pc = declPlayerClassType.SafeIndex( index );
if ( pc != NULL ) {
client->ChangeClass( pc, classOption );
}
break;
}
case GAME_RELIABLE_CMESSAGE_CHANGEWEAPON: {
int id = msg.ReadLong();
if ( client->GetInventory().CanEquip( id, true ) ) {
client->GetInventory().SetIdealWeapon( id );
}
break;
}
case GAME_RELIABLE_CMESSAGE_DEFAULTSPAWN: {
client->SetSpawnPoint( NULL );
break;
}
case GAME_RELIABLE_CMESSAGE_SETSPAWNPOINT: {
idEntity* ent = gameLocal.EntityForSpawnId( msg.ReadLong() );
client->SetSpawnPoint( ent );
break;
}
case GAME_RELIABLE_CMESSAGE_GIVECLASS: {
char buffer[ 128 ];
msg.ReadString( buffer, sizeof( buffer ) );
const sdDeclPlayerClass* pc = declPlayerClassType[ buffer ];
if ( CheatsOk( false ) && pc ) {
client->GetInventory().GiveClass( pc, true );
}
break;
}
case GAME_RELIABLE_CMESSAGE_RESPAWN: {
if ( !client->IsSpectating() && client->IsDead() ) {
client->ServerForceRespawn( false );
}
break;
}
case GAME_RELIABLE_CMESSAGE_ADMIN: {
idCmdArgs args;
args.AppendArg( "admin" );
char buffer[ 256 ];
int numArgs = msg.ReadLong();
if ( numArgs > 8 ) { // Gordon: prevent evil people making us attempt to read a silly number of args
numArgs = 8;
}
for ( int i = 0; i < numArgs; i++ ) {
msg.ReadString( buffer, sizeof( buffer ) );
args.AppendArg( buffer );
}
sdAdminSystem::GetInstance().PerformCommand( args, client );
break;
}
case GAME_RELIABLE_CMESSAGE_FIRETEAM: {
idCmdArgs args;
args.AppendArg( "fireteam" );
char buffer[ 256 ];
int numArgs = msg.ReadLong();
if ( numArgs > 8 ) { // Gordon: prevent evil people making us attempt to read a silly number of args
numArgs = 8;
}
for ( int i = 0; i < numArgs; i++ ) {
msg.ReadString( buffer, sizeof( buffer ) );
args.AppendArg( buffer );
}
sdFireTeamManager::GetInstance().PerformCommand( args, client );
break;
}
case GAME_RELIABLE_CMESSAGE_TASK: {
qhandle_t handle = msg.ReadBits( sdPlayerTask::TASK_BITS + 1 ) - 1;
sdTaskManager::GetInstance().SelectTask( client, handle );
break;
}
case GAME_RELIABLE_CMESSAGE_SELECTWEAPON: {
char buffer[ 256 ];
msg.ReadString( buffer, sizeof( buffer ) );
client->SelectWeaponByName( buffer );
break;
}
case GAME_RELIABLE_CMESSAGE_VOTE: {
sdVoteManager::GetInstance().ServerReadNetworkMessage( client, msg );
break;
}
case GAME_RELIABLE_CMESSAGE_CALLVOTE: {
idCmdArgs args;
args.AppendArg( "callvote" );
char buffer[ 256 ];
int numArgs = msg.ReadLong();
if ( numArgs > 8 ) { // Gordon: prevent evil people making us attempt to read a silly number of args
numArgs = 8;
}
for ( int i = 0; i < numArgs; i++ ) {
msg.ReadString( buffer, sizeof( buffer ) );
args.AppendArg( buffer );
}
sdVoteManager::GetInstance().PerformCommand( args, client );
break;
}
case GAME_RELIABLE_CMESSAGE_RESERVECLIENTSLOT: {
sdNetClientId netClientId;
netClientId.id[0] = msg.ReadLong();
netClientId.id[1] = msg.ReadLong();
ReserveClientSlot( netClientId );
break;
}
case GAME_RELIABLE_CMESSAGE_REQUESTSTATS: {
sdGlobalStatsTracker::GetInstance().AddStatsRequest( client->entityNumber );
break;
}
case GAME_RELIABLE_CMESSAGE_ACKNOWLEDGESTATS: {
sdGlobalStatsTracker::GetInstance().AcknowledgeStatsReponse( client->entityNumber );
break;
}
case GAME_RELIABLE_CMESSAGE_SETMUTESTATUS: {
bool muteState = msg.ReadBool();
int clientIndex = msg.ReadByte();
if ( muteState ) {
MutePlayerLocal( client, clientIndex );
} else {
UnMutePlayerLocal( client, clientIndex );
}
break;
}
case GAME_RELIABLE_CMESSAGE_SETSPECTATECLIENT: {
int entityNum = msg.ReadByte();
if ( entityNum >= 0 && entityNum < MAX_CLIENTS ) {
client->OnSetClientSpectatee( gameLocal.GetClient( entityNum ) );
}
break;
}
case GAME_RELIABLE_CMESSAGE_ANTILAGDEBUG: {
sdAntiLagManager::GetInstance().OnNetworkEvent( clientNum, msg );
break;
}
case GAME_RELIABLE_CMESSAGE_ACKNOWLEDGEBANLIST: {
if ( clientLastBanIndexReceived[ clientNum ] != -1 ) {
SendBanList( client );
}
break;
}
case GAME_RELIABLE_CMESSAGE_REPEATERSTATUS: {
sdReliableServerMessage msg( GAME_RELIABLE_SMESSAGE_REPEATERSTATUS );
msg.WriteBool( gameLocal.isRepeater );
msg.Send( sdReliableMessageClientInfo( clientNum ) );
break;
}
case GAME_RELIABLE_CMESSAGE_SPECTATECOMMAND: {
int command = msg.ReadBits( idMath::BitsForInteger( idPlayer::SPECTATE_MAX ) );
idVec3 origin = vec3_origin;
idAngles angles = ang_zero;
if ( command == idPlayer::SPECTATE_POSITION ) {
origin = msg.ReadVector();
angles.pitch = msg.ReadFloat();
angles.yaw = msg.ReadFloat();
angles.roll = msg.ReadFloat();
}
if ( client->IsSpectator() ) {
client->SpectateCommand( ( idPlayer::spectateCommand_t )command, origin, angles );
}
break;
}
default: {
Warning( "Unknown client->server reliable message: %d", id );
break;
}
}
}
/*
================
idGameLocal::ClientShowAOR
================
*/
void idGameLocal::ClientShowAOR( int clientNum ) const {
if ( !net_clientShowAOR.GetInteger() ) {
return;
}
if ( net_clientShowAOR.GetInteger() & 1 ) {
gameLocal.aorManager.DebugDraw( clientNum );
}
if ( net_clientShowAOR.GetInteger() & 2 ) {
gameLocal.aorManager.DebugDrawEntities( clientNum );
}
}
/*
================
idGameLocal::ClientShowSnapshot
================
*/
void idGameLocal::ClientShowSnapshot( int clientNum ) const {
}
// Gordon: FIXME: This is a maintenance nightmare
/*
================
idGameLocal::ResetGameState
================
*/
void idGameLocal::ResetGameState( extNetworkStateMode_t mode ) {
sdNetworkStateObject& object = GetGameStateObject( mode );
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
FreeGameState( clientNetworkInfo[ i ].gameStates[ mode ] );
}
FreeGameState( demoClientNetworkInfo.gameStates[ mode ] );
#ifdef SD_SUPPORT_REPEATER
FreeGameState( repeaterClientNetworkInfo.gameStates[ mode ] );
for ( int i = 0; i < repeaterNetworkInfo.Num(); i++ ) {
if ( repeaterNetworkInfo[ i ] == NULL ) {
continue;
}
FreeGameState( repeaterNetworkInfo[ i ]->gameStates[ mode ] );
}
#endif // SD_SUPPORT_REPEATER
while ( object.freeStates ) {
sdGameState* nextState = object.freeStates->next;
FreeGameState( object.freeStates );
object.freeStates = nextState;
}
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
sdGameState* newState = AllocGameState( object );
if ( newState->data ) {
newState->data->MakeDefault();
}
clientNetworkInfo[ i ].gameStates[ mode ] = newState;
}
sdGameState* newState = AllocGameState( object );
if ( newState->data ) {
newState->data->MakeDefault();
}
demoClientNetworkInfo.gameStates[ mode ] = newState;
#ifdef SD_SUPPORT_REPEATER
newState = AllocGameState( object );
if ( newState->data ) {
newState->data->MakeDefault();
}
repeaterClientNetworkInfo.gameStates[ mode ] = newState;
for ( int i = 0; i < repeaterNetworkInfo.Num(); i++ ) {
if ( repeaterNetworkInfo[ i ] == NULL ) {
continue;
}
sdGameState* newState = AllocGameState( object );
if ( newState->data ) {
newState->data->MakeDefault();
}
( *repeaterNetworkInfo[ i ] ).gameStates[ mode ] = newState;
}
#endif // SD_SUPPORT_REPEATER
}
/*
================
idGameLocal::GetGameStateObject
================
*/
sdNetworkStateObject& idGameLocal::GetGameStateObject( extNetworkStateMode_t mode ) {
switch ( mode ) {
case ENSM_TEAM:
return sdTeamManager::GetInstance().GetStateObject();
case ENSM_RULES:
return rulesStateObject;
}
Error( "idGameLocal::GetGameStateObject Invalid Mode" );
return *( sdNetworkStateObject* )( NULL );
}
/*
================
idGameLocal::WriteGameState
================
*/
void idGameLocal::WriteGameState( extNetworkStateMode_t mode, snapshot_t* snapshot, clientNetworkInfo_t& nwInfo, idBitMsg& msg ) {
sdGameState* baseState = nwInfo.gameStates[ mode ];
const sdNetworkStateObject& object = GetGameStateObject( mode );
if ( !object.CheckNetworkStateChanges( *baseState->data ) ) {
msg.WriteBool( false );
return;
}
msg.WriteBool( true );
snapshot->gameStates[ mode ] = AllocGameState( object );
object.WriteNetworkState( *baseState->data, *snapshot->gameStates[ mode ]->data, msg );
}
/*
================
idGameLocal::ReadGameState
================
*/
void idGameLocal::ReadGameState( extNetworkStateMode_t mode, snapshot_t* snapshot, const idBitMsg& msg ) {
if ( !msg.ReadBool() ) {
return;
}
sdGameState* baseState = GetNetworkInfo( localClientNum ).gameStates[ mode ];
sdNetworkStateObject& object = GetGameStateObject( mode );
snapshot->gameStates[ mode ] = AllocGameState( object );
object.ReadNetworkState( *baseState->data, *snapshot->gameStates[ mode ]->data, msg );
object.ApplyNetworkState( *snapshot->gameStates[ mode ]->data );
}
/*
================
idGameLocal::EnsureAlloced
================
*/
void idGameLocal::EnsureAlloced( int entityNum, idEntity* ent, const char* oldState, const char* currentState ) {
if ( entityNum < 0 || entityNum >= MAX_GENTITIES ) {
Error( "Entitynum (%i) out of range", entityNum );
}
if ( !entities[ entityNum ] ) {
Printf( "Current Entity (%s)\n", currentState );
Printf( "=======================================\n" );
entityDebugInfo[ entityNum ].PrintStatus( entityNum );
if ( ent ) {
Printf( "Previous Entity To Blame (%s)\n", oldState );
Printf( "=======================================\n" );
entityDebugInfo[ ent->entityNumber ].PrintStatus( ent->entityNumber );
} else {
Printf( "No Previous Entity To Blame\n" );
}
Error( "idGameLocal::EnsureAlloced State Received For Entity With No Header" );
}
}
/*
================
idGameLocal::UpdateLagometer
================
*/
void idGameLocal::UpdateLagometer( int aheadOfServer, int numDuplicatedUsercmds ) {
int i, j, ahead;
for ( i = 0; i < LAGO_HEIGHT; i++ ) {
memmove( (byte *)lagometer + LAGO_WIDTH * 4 * i, (byte *)lagometer + LAGO_WIDTH * 4 * i + 4, ( LAGO_WIDTH - 1 ) * 4 );
}
j = LAGO_WIDTH - 1;
for ( i = 0; i < LAGO_HEIGHT; i++ ) {
lagometer[i][j][0] = lagometer[i][j][1] = lagometer[i][j][2] = lagometer[i][j][3] = 0;
}
ahead = idMath::Rint( (float)aheadOfServer / (float)USERCMD_MSEC );
if ( ahead >= 0 ) {
for ( i = 2 * Max( 0, 5 - ahead ); i < 2 * 5; i++ ) {
lagometer[i][j][1] = 255;
lagometer[i][j][3] = 255;
}
} else {
for ( i = 2 * 5; i < 2 * ( 5 + Min( 10, -ahead ) ); i++ ) {
lagometer[i][j][0] = 255;
lagometer[i][j][1] = 255;
lagometer[i][j][3] = 255;
}
}
for ( i = LAGO_HEIGHT - 2 * Min( 6, numDuplicatedUsercmds ); i < LAGO_HEIGHT; i++ ) {
lagometer[i][j][0] = 255;
if ( numDuplicatedUsercmds <= 2 ) {
lagometer[i][j][1] = 255;
}
lagometer[i][j][3] = 255;
}
if ( renderSystem && !renderSystem->UploadImage( LAGO_IMAGE, (byte *)lagometer, LAGO_IMG_WIDTH, LAGO_IMG_HEIGHT, false, false ) ) {
common->Printf( "lagometer: UploadImage failed. turning off net_clientLagOMeter\n" );
net_clientLagOMeter.SetBool( false );
}
}
/*
================
idGameLocal::ClientReadSnapshot
================
*/
bool idGameLocal::ClientReadSnapshot( int sequence, const int gameFrame, const int gameTime, const int numDuplicatedUsercmds, const int aheadOfServer, const idBitMsg &msg, const idBitMsg &ucmdmsg ) {
snapshot_t* snapshot;
clientNetworkInfo_t& nwInfo = GetNetworkInfo( localClientNum );
g_trainingMode.SetBool( false );
sdPhysicsEvent* physicsEvent;
sdPhysicsEvent* nextPhysicsEvent;
for ( physicsEvent = physicsEvents.Next(); physicsEvent; physicsEvent = nextPhysicsEvent ) {
nextPhysicsEvent = physicsEvent->GetNode().Next();
if ( gameTime > physicsEvent->GetCreationTime() ) {
delete physicsEvent;
}
}
if ( net_clientLagOMeter.GetBool() ) {
UpdateLagometer( aheadOfServer, numDuplicatedUsercmds );
}
// clear any debug lines from a previous frame
gameRenderWorld->DebugClearLines( time );
// clear any debug polygons from a previous frame
gameRenderWorld->DebugClearPolygons( time );
idEntity* ent = NULL;
for( ent = networkedEntities.Next(); ent != NULL; ent = ent->networkNode.Next() ) {
ent->snapshotPVSFlags = PVS_DEFERRED_VISIBLE;
sdEntityState* state = nwInfo.states[ ent->entityNumber ][ NSM_VISIBLE ];
if ( state == NULL ) {
gameLocal.Warning( "idGameLocal::ClientReadSnapshot Entity '%s' has no Visible State, Likely a Client Spawned Entity", ent->name.c_str() );
continue;
}
if ( state->data == NULL ) {
continue;
}
// reset back to the old visible state
ent->ResetNetworkState( NSM_VISIBLE, *state->data );
}
for( ent = networkedEntities.Next(); ent != NULL; ent = ent->networkNode.Next() ) {
ent->snapshotPVSFlags = PVS_DEFERRED_VISIBLE;
sdEntityState* state = nwInfo.states[ ent->entityNumber ][ NSM_BROADCAST ];
if ( state == NULL ) {
gameLocal.Warning( "idGameLocal::ClientReadSnapshot Entity '%s' has no Broadcast State, Likely a Client Spawned Entity", ent->name.c_str() );
continue;
}
if ( state->data == NULL ) {
continue;
}
// reset back to the old broadcast state
ent->ResetNetworkState( NSM_BROADCAST, *state->data );
}
// update the game time
framenum = gameFrame;
if ( !IsPaused() ) {
time = gameTime - timeOffset;
previousTime = time - networkSystem->ClientGetFrameTime();
} else {
timeOffset = gameTime - time;
previousTime = time;
}
isNewFrame = true;
predictionUpdateRequired = true;
// clear the snapshot entity list
snapshotEntities.Clear();
snapshotVisbileEntities.Clear();
// allocate new snapshot
snapshot = snapshotAllocator.Alloc();
snapshot->Init( sequence, gameTime );
snapshot->next = nwInfo.snapshots;
nwInfo.snapshots = snapshot;
activeSnapshot = snapshot;
int followPlayer = 0;
if ( serverIsRepeater ) {
followPlayer = msg.ReadByte() - 1;
}
// ASYNC_SECURITY_READ( msg )
ReadGameState( ENSM_TEAM, snapshot, msg );
// ASYNC_SECURITY_READ( msg )
// ASYNC_SECURITY_READ( msg )
ReadGameState( ENSM_RULES, snapshot, msg );
// ASYNC_SECURITY_READ( msg )
ent = NULL;
const char* oldState = NULL;
// process entity events
ClientProcessEntityNetworkEventQueue();
if ( serverIsRepeater ) {
SetSnapShotClient( NULL );
SetSnapShotPlayer( followPlayer == -1 ? NULL : GetClient( followPlayer ) );
} else {
idPlayer* localPlayer = GetLocalPlayer();
if ( localPlayer ) {
SetSnapShotClient( localPlayer );
SetSnapShotPlayer( localPlayer->GetSpectateClient() );
} else {
SetSnapShotClient( NULL );
SetSnapShotPlayer( NULL );
}
}
// read visible states
for ( int i = msg.ReadBits( GENTITYNUM_BITS ); i != ENTITYNUM_NONE; i = msg.ReadBits( GENTITYNUM_BITS ) ) {
EnsureAlloced( i, ent, oldState, "Visible" );
ent = entities[ i ];
ent->snapshotPVSFlags &= ~( PVS_DEFERRED_VISIBLE );
ent->snapshotPVSFlags |= PVS_VISIBLE;
sdEntityState* newState = AllocEntityState( NSM_VISIBLE, ent );
sdEntityState* baseState = nwInfo.states[ i ][ NSM_VISIBLE ];
// ASYNC_SECURITY_READ( msg )
ent->ReadNetworkState( NSM_VISIBLE, *baseState->data, *newState->data, msg );
// ASYNC_SECURITY_READ( msg )
ent->ApplyNetworkState( NSM_VISIBLE, *newState->data );
newState->next = snapshot->firstEntityState[ NSM_VISIBLE ];
snapshot->firstEntityState[ NSM_VISIBLE ] = newState;
oldState = "Visible";
}
// read broadcast states
for ( int i = msg.ReadBits( GENTITYNUM_BITS ); i != ENTITYNUM_NONE; i = msg.ReadBits( GENTITYNUM_BITS ) ) {
EnsureAlloced( i, ent, oldState, "Broadcast" );
ent = entities[ i ];
ent->snapshotPVSFlags |= PVS_BROADCAST;
sdEntityState* newState = AllocEntityState( NSM_BROADCAST, ent );
sdEntityState* baseState = nwInfo.states[ i ][ NSM_BROADCAST ];
// ASYNC_SECURITY_READ( msg )
ent->ReadNetworkState( NSM_BROADCAST, *baseState->data, *newState->data, msg );
// ASYNC_SECURITY_READ( msg )
ent->ApplyNetworkState( NSM_BROADCAST, *newState->data );
newState->next = snapshot->firstEntityState[ NSM_BROADCAST ];
snapshot->firstEntityState[ NSM_BROADCAST ] = newState;
oldState = "Broadcast";
}
if ( snapShotPlayer ) {
// show AOR
ClientShowAOR( localClientNum );
// visualize the snapshot
ClientShowSnapshot( localClientNum );
}
if ( SetupClientAoR() ) {
for ( ent = networkedEntities.Next(); ent != NULL; ent = ent->networkNode.Next() ) {
// Gordon: Use evaluate position here, which for some fully predictable physics classes
// will return a newly evaluated positon, and not the currently last updated one
idPhysics* physics = ent->GetPhysics();
bool update = false;
aorManager.UpdateEntityAORFlags( ent, physics->EvaluatePosition(), update );
if ( !physics->AllowInhibit() ) {
ent->aorFlags &= ~AOR_INHIBIT_PHYSICS;
}
}
} else {
for ( ent = networkedEntities.Next(); ent != NULL; ent = ent->networkNode.Next() ) {
ent->aorFlags = 0;
}
}
// read the unreliable entity network events
int numUnreliableEvents = msg.ReadLong();
for ( int i = 0; i < numUnreliableEvents; i++ ) {
sdUnreliableEntityNetEvent* event = unreliableEntityNetEventAllocator.Alloc();
event->GetUnreliableNode().AddToEnd( unreliableEntityNetEventQueue );
event->Read( msg );
if ( gameLocal.isRepeater ) {
gameLocal.SendUnreliableEntityNetworkEvent( *event );
}
}
// read user commands
while ( true ) {
int clientNum = ucmdmsg.ReadBits( CLIENTBITS );
if ( clientNum == MAX_CLIENTS ) {
break;
}
networkSystem->ReadClientUserCmds( clientNum, ucmdmsg );
}
return true;
}
/*
================
idGameLocal::SetupClientAoR
================
*/
bool idGameLocal::SetupClientAoR( void ) {
if ( !net_useAOR.GetBool() ) {
return false;
}
renderView_t view;
if ( sdDemoManager::GetInstance().CalculateRenderView( &view ) ) {
aorManager.SetPosition( view.vieworg );
return true;
}
idPlayer* viewer = GetActiveViewer();
if ( viewer != NULL ) {
aorManager.SetClient( viewer );
return true;
}
if ( serverIsRepeater ) {
playerView.CalculateRepeaterView( view );
aorManager.SetPosition( view.vieworg );
return true;
}
return false;
}
/*
================
idGameLocal::ClientApplySnapshot
================
*/
bool idGameLocal::ClientApplySnapshot( int sequence ) {
return ApplySnapshot( GetNetworkInfo( localClientNum ), sequence );
}
/*
================
idGameLocal::WriteClientNetworkInfo
================
*/
void idGameLocal::WriteClientNetworkInfo( idFile* file ) {
ClientWriteGameState( file );
clientNetworkInfo_t& nwInfo = GetNetworkInfo( localClientNum );
SetSnapShotClient( GetLocalPlayer() );
SetSnapShotPlayer( GetLocalPlayer() );
for ( idEntity* ent = networkedEntities.Next(); ent; ent = ent->networkNode.Next() ) {
file->WriteInt( ent->entityNumber );
int num = ent->entityNumber;
for ( int j = 0; j < NSM_NUM_MODES; j++ ) {
if ( nwInfo.states[ num ][ j ] == NULL || nwInfo.states[ num ][ j ]->data == NULL ) {
file->WriteBool( false );
continue;
}
file->WriteBool( true );
nwInfo.states[ num ][ j ]->data->Write( file );
ASYNC_SECURITY_WRITE_FILE( file )
}
ASYNC_SECURITY_WRITE_FILE( file )
for ( int j = 0; j < NSM_NUM_MODES; j++ ) {
networkStateMode_t mode = ( networkStateMode_t )j;
sdEntityState* state = AllocEntityState( mode, ent );
if ( state->data != NULL ) {
file->WriteBool( true );
idBitMsg temp;
byte buffer[ 2048 ];
temp.InitWrite( buffer, sizeof( buffer ) );
sdEntityState* defaultState = AllocEntityState( mode, ent );
defaultState->data->MakeDefault();
ent->WriteNetworkState( mode, *defaultState->data, *state->data, temp );
FreeNetworkState( defaultState );
state->data->Write( file );
} else {
file->WriteBool( false );
}
FreeNetworkState( state );
}
ASYNC_SECURITY_WRITE_FILE( file )
}
file->WriteInt( MAX_GENTITIES );
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
if ( nwInfo.gameStates[ i ] == NULL || nwInfo.gameStates[ i ]->data == NULL ) {
file->WriteBool( false );
continue;
}
file->WriteBool( true );
nwInfo.gameStates[ i ]->data->Write( file );
}
ASYNC_SECURITY_WRITE_FILE( file )
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
extNetworkStateMode_t mode = ( extNetworkStateMode_t )i;
sdNetworkStateObject& obj = GetGameStateObject( mode );
sdGameState* state = AllocGameState( obj );
if ( state->data != NULL ) {
file->WriteBool( true );
idBitMsg temp;
byte buffer[ 2048 ];
temp.InitWrite( buffer, sizeof( buffer ) );
sdGameState* defaultState = AllocGameState( obj );
defaultState->data->MakeDefault();
obj.WriteNetworkState( *defaultState->data, *state->data, temp );
FreeGameState( defaultState );
state->data->Write( file );
} else {
file->WriteBool( false );
}
FreeGameState( state );
}
ASYNC_SECURITY_WRITE_FILE( file )
// write number of snapshots so on readback we know how many to allocate
int snapCount = 0;
for ( snapshot_t* snapshot = nwInfo.snapshots; snapshot; snapshot = snapshot->next ) {
snapCount++;
}
file->WriteInt( snapCount );
for ( snapshot_t* snapshot = nwInfo.snapshots; snapshot; snapshot = snapshot->next ) {
file->WriteInt( snapshot->sequence );
for ( int i = 0; i < NSM_NUM_MODES; i++ ) {
int stateCount = 0;
for ( sdEntityState* state = snapshot->firstEntityState[ i ]; state; state = state->next ) {
stateCount++;
}
file->WriteInt( stateCount );
for ( sdEntityState* state = snapshot->firstEntityState[ i ]; state; state = state->next ) {
file->WriteInt( state->spawnId );
state->data->Write( file );
}
}
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
if ( !snapshot->gameStates[ i ] ) {
file->WriteBool( false );
continue;
}
file->WriteBool( true );
snapshot->gameStates[ i ]->data->Write( file );
}
}
SetSnapShotClient( NULL );
SetSnapShotPlayer( NULL );
}
/*
================
idGameLocal::ReadClientNetworkInfo
================
*/
void idGameLocal::ReadClientNetworkInfo( idFile* file ) {
ClientReadGameState( file );
// force new frame
realClientTime = 0;
isNewFrame = true;
SetSnapShotClient( GetLocalPlayer() );
SetSnapShotPlayer( GetLocalPlayer() );
clientNetworkInfo_t& nwInfo = GetNetworkInfo( localClientNum );
while ( true ) {
int num;
file->ReadInt( num );
if ( num == MAX_GENTITIES ) {
break;
}
if ( num < 0 || num >= MAX_GENTITIES ) {
Error( "idGameLocal::ReadClientNetworkInfo Tried to read out of bounds entity" );
}
idEntity* ent = entities[ num ];
if ( ent == NULL ) {
Error( "idGameLocal::ReadClientNetworkInfo Tried to Read State For Missing Entity" );
}
for ( int j = 0; j < NSM_NUM_MODES; j++ ) {
bool temp;
file->ReadBool( temp );
if ( !temp ) {
continue;
}
FreeNetworkState( nwInfo.states[ num ][ j ] );
networkStateMode_t mode = ( networkStateMode_t )j;
sdEntityState& newState = *AllocEntityState( mode, ent );
if ( newState.data == NULL ) {
Error( "idGameLocal::ReadClientNetworkInfo Failed to Alloc Network State" );
}
newState.data->Read( file );
ASYNC_SECURITY_READ_FILE( file )
nwInfo.states[ num ][ j ] = &newState;
}
ASYNC_SECURITY_READ_FILE( file )
for ( int j = 0; j < NSM_NUM_MODES; j++ ) {
bool temp;
file->ReadBool( temp );
if ( !temp ) {
continue;
}
networkStateMode_t mode = ( networkStateMode_t )j;
sdEntityState* state = AllocEntityState( mode, ent );
assert( state->data != NULL );
state->data->Read( file );
ent->ApplyNetworkState( mode, *state->data );
FreeNetworkState( state );
}
ASYNC_SECURITY_READ_FILE( file )
}
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
FreeGameState( nwInfo.gameStates[ i ] );
extNetworkStateMode_t mode = ( extNetworkStateMode_t )i;
sdNetworkStateObject& stateObject = GetGameStateObject( mode );
bool temp;
file->ReadBool( temp );
if ( !temp ) {
continue;
}
nwInfo.gameStates[ i ] = AllocGameState( stateObject );
nwInfo.gameStates[ i ]->data->Read( file );
}
ASYNC_SECURITY_READ_FILE( file )
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
extNetworkStateMode_t mode = ( extNetworkStateMode_t )i;
sdNetworkStateObject& stateObject = GetGameStateObject( mode );
bool temp;
file->ReadBool( temp );
if ( !temp ) {
continue;
}
sdGameState* state = AllocGameState( stateObject );
assert( state->data != NULL );
state->data->Read( file );
stateObject.ApplyNetworkState( *state->data );
FreeGameState( state );
}
ASYNC_SECURITY_READ_FILE( file )
FreeSnapshotsOlderThanSequence( nwInfo, 0x7FFFFFFF );
int count;
file->ReadInt( count );
snapshot_t** lastSnapshot = &nwInfo.snapshots;
for ( int i = 0; i < count; i++ ) {
snapshot_t* snapshot = snapshotAllocator.Alloc();
int sequence;
file->ReadInt( sequence );
snapshot->Init( sequence, -1 /* FIXME: Gordon: Clients don't use this atm, and i don't want to break demos right now */ );
for ( int j = 0; j < NSM_NUM_MODES; j++ ) {
int numStates;
file->ReadInt( numStates );
for ( int k = 0; k < numStates; k++ ) {
int spawnId;
file->ReadInt( spawnId );
idEntity* ent = EntityForSpawnId( spawnId );
if ( !ent ) {
Error( "idGameLocal::ReadClientNetworkInfo Missing Entity for Snapshot State" );
}
sdEntityState* state = AllocEntityState( ( networkStateMode_t )j, ent );
state->next = snapshot->firstEntityState[ j ];
snapshot->firstEntityState[ j ] = state;
if ( state->data ) {
state->data->Read( file );
}
}
}
for ( int j = 0; j < ENSM_NUM_MODES; j++ ) {
bool temp;
file->ReadBool( temp );
if ( !temp ) {
continue;
}
snapshot->gameStates[ j ] = AllocGameState( GetGameStateObject( ( extNetworkStateMode_t )j ) );
snapshot->gameStates[ j ]->data->Read( file );
}
*lastSnapshot = snapshot;
lastSnapshot = &snapshot->next;
}
SetSnapShotClient( NULL );
SetSnapShotPlayer( NULL );
}
/*
================
userInfo_t::ToDict
================
*/
void userInfo_t::ToDict( idDict& info ) const {
info.Set( "ui_name", baseName.c_str() );
info.Set( "ui_realname", rawName.c_str() );
info.SetBool( "ui_showGun", showGun );
info.SetBool( "ui_ignoreExplosiveWeapons", ignoreExplosiveWeapons );
info.SetBool( "ui_autoSwitchEmptyWeapons", autoSwitchEmptyWeapons );
info.SetBool( "ui_postArmFindBestWeapon", postArmFindBestWeapon );
info.SetBool( "ui_advancedFlightControls", advancedFlightControls );
info.SetBool( "ui_rememberCameraMode", rememberCameraMode );
info.SetBool( "ui_drivingCameraFreelook", drivingCameraFreelook );
info.SetBool( "ui_bot", isBot );
info.SetBool( "ui_swapFlightYawAndRoll", swapFlightYawAndRoll );
}
/*
================
userInfo_t::FromDict
================
*/
void userInfo_t::FromDict( const idDict& info ) {
baseName = info.GetString( "ui_name" ); // doesn't include clan tag
rawName = info.GetString( "ui_realname" );
name = rawName + "^0";
cleanName = rawName;
idGameLocal::CleanName( cleanName );
wideName = va( L"%hs", name.c_str() );
showGun = info.GetBool( "ui_showGun" );
ignoreExplosiveWeapons = info.GetBool( "ui_ignoreExplosiveWeapons" );
autoSwitchEmptyWeapons = info.GetBool( "ui_autoSwitchEmptyWeapons" );
postArmFindBestWeapon = info.GetBool( "ui_postArmFindBestWeapon" );
advancedFlightControls = info.GetBool( "ui_advancedFlightControls" );
rememberCameraMode = info.GetBool( "ui_rememberCameraMode" );
drivingCameraFreelook = info.GetBool( "ui_drivingCameraFreelook" );
isBot = info.GetBool( "ui_bot" );
voipReceiveGlobal = info.GetBool( "ui_voipReceiveGlobal" );
voipReceiveTeam = info.GetBool( "ui_voipReceiveTeam" );
voipReceiveFireTeam = info.GetBool( "ui_voipReceiveFireTeam" );
showComplaints = info.GetBool( "ui_showComplaints" );
swapFlightYawAndRoll = info.GetBool( "ui_swapFlightYawAndRoll" );
}
/*
================
idGameLocal::WriteUserInfo
================
*/
void idGameLocal::WriteUserInfo( idBitMsg& msg, const idDict& info ) {
userInfo_t userInfo;
userInfo.FromDict( info );
msg.WriteString( userInfo.baseName.c_str() );
msg.WriteString( userInfo.rawName.c_str() );
msg.WriteBool( userInfo.showGun );
msg.WriteBool( userInfo.ignoreExplosiveWeapons );
msg.WriteBool( userInfo.autoSwitchEmptyWeapons );
msg.WriteBool( userInfo.postArmFindBestWeapon );
msg.WriteBool( userInfo.advancedFlightControls );
msg.WriteBool( userInfo.rememberCameraMode );
msg.WriteBool( userInfo.drivingCameraFreelook );
msg.WriteBool( userInfo.isBot );
msg.WriteBool( userInfo.swapFlightYawAndRoll );
}
/*
================
idGameLocal::ReadUserInfo
================
*/
void idGameLocal::ReadUserInfo( const idBitMsg& msg, idDict& info ) {
char buffer[ 256 ];
userInfo_t userInfo;
msg.ReadString( buffer, sizeof( buffer ) );
userInfo.baseName = buffer;
msg.ReadString( buffer, sizeof( buffer ) );
userInfo.rawName = buffer;
userInfo.showGun = msg.ReadBool();
userInfo.ignoreExplosiveWeapons = msg.ReadBool();
userInfo.autoSwitchEmptyWeapons = msg.ReadBool();
userInfo.postArmFindBestWeapon = msg.ReadBool();
userInfo.advancedFlightControls = msg.ReadBool();
userInfo.rememberCameraMode = msg.ReadBool();
userInfo.drivingCameraFreelook = msg.ReadBool();
userInfo.isBot = msg.ReadBool();
userInfo.swapFlightYawAndRoll = msg.ReadBool();
userInfo.ToDict( info );
}
/*
================
idGameLocal::ClientProcessEntityNetworkEventQueue
================
*/
void idGameLocal::ClientProcessEntityNetworkEventQueue( void ) {
sdEntityNetEvent* next;
for ( sdEntityNetEvent* event = entityNetEventQueue.Next(); event; event = next ) {
next = event->GetNode().Next();
// only process forward, in order
if ( event->GetTime() > time ) {
break;
}
idEntity* entity = gameLocal.EntityForSpawnId( event->GetSpawnId() );
if( !entity ) {
NetworkEventWarning( *event, "unknown entity" );
} else {
if ( gameLocal.isRepeater ) { // Gordon: Save any off that we need to send to clients which connect later
gameLocal.FreeEntityNetworkEvents( entity, event->GetEvent() );
if ( event->ShouldSaveEvent() ) {
gameLocal.SaveEntityNetworkEvent( *event );
}
}
idBitMsg eventMsg;
event->OutputParms( eventMsg );
if ( !entity->ClientReceiveEvent( event->GetEvent(), event->GetTime(), eventMsg ) ) {
NetworkEventWarning( *event, "unknown event" );
}
}
event->GetNode().Remove();
entityNetEventAllocator.Free( event );
}
// process unreliable events
sdUnreliableEntityNetEvent* unreliableNext;
for ( sdUnreliableEntityNetEvent* event = unreliableEntityNetEventQueue.Next(); event; event = unreliableNext ) {
unreliableNext = event->GetUnreliableNode().Next();
// only process forward, in order
if ( event->GetTime() > time ) {
break;
}
idEntity* entity = gameLocal.EntityForSpawnId( event->GetSpawnId() );
if( !entity ) {
UnreliableNetworkEventWarning( *event, "unknown entity" );
} else {
idBitMsg eventMsg;
event->OutputParms( eventMsg );
if ( !entity->ClientReceiveUnreliableEvent( event->GetEvent(), event->GetTime(), eventMsg ) ) {
UnreliableNetworkEventWarning( *event, "unknown event" );
}
}
event->GetUnreliableNode().Remove();
unreliableEntityNetEventAllocator.Free( event );
}
}
/*
================
idGameLocal::ClientReceiveEvent
================
*/
bool idGameLocal::ClientReceiveEvent( const idVec3& origin, int event, int time, const idBitMsg &msg ) {
return true;
}
/*
================
idGameLocal::FreeNetworkState
================
*/
void idGameLocal::FreeNetworkState( sdEntityState* state ) {
delete state;
}
/*
================
idGameLocal::FreeGameState
================
*/
void idGameLocal::FreeGameState( sdGameState* state ) {
delete state;
}
/*
================
idGameLocal::CreateNetworkState
================
*/
void idGameLocal::CreateNetworkState( clientNetworkInfo_t& networkInfo, int entityNum ) {
idEntity* entity = entities[ entityNum ];
for ( int i = 0; i < NSM_NUM_MODES; i++ ) {
if ( !networkInfo.states[ entityNum ][ i ] ) {
networkInfo.states[ entityNum ][ i ] = AllocEntityState( ( networkStateMode_t )i, entity );
if ( networkInfo.states[ entityNum ][ i ]->data ) {
networkInfo.states[ entityNum ][ i ]->data->MakeDefault();
}
}
}
}
/*
================
idGameLocal::CreateNetworkState
================
*/
void idGameLocal::CreateNetworkState( int entityNum ) {
if ( isClient ) {
CreateNetworkState( GetNetworkInfo( localClientNum ), entityNum );
} else {
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
if ( !entities[ i ] ) {
continue;
}
CreateNetworkState( GetNetworkInfo( i ), entityNum );
}
CreateNetworkState( demoClientNetworkInfo, entityNum );
}
#ifdef SD_SUPPORT_REPEATER
for ( int i = 0; i < repeaterNetworkInfo.Num(); i++ ) {
if ( repeaterNetworkInfo[ i ] == NULL ) {
continue;
}
CreateNetworkState( *repeaterNetworkInfo[ i ], entityNum );
}
#endif // SD_SUPPORT_REPEATER
}
/*
================
idGameLocal::FreeNetworkState
================
*/
void idGameLocal::FreeNetworkState( clientNetworkInfo_t& networkInfo, int entityNum ) {
for ( int i = 0; i < NSM_NUM_MODES; i++ ) {
FreeNetworkState( networkInfo.states[ entityNum ][ i ] );
networkInfo.states[ entityNum ][ i ] = NULL;
}
}
/*
================
idGameLocal::FreeNetworkState
================
*/
void idGameLocal::FreeNetworkState( int entityNum ) {
if ( isClient ) {
FreeNetworkState( GetNetworkInfo( localClientNum ), entityNum );
// Gordon: Clear any pending events that we may have just caught this frame
sdEntityNetEvent* next = NULL;
for ( sdEntityNetEvent* evt = entityNetEventQueue.Next(); evt != NULL; evt = next ) {
next = evt->GetNode().Next();
if ( evt->GetEntityNumber() == entityNum ) {
evt->GetNode().Remove();
entityNetEventAllocator.Free( evt );
}
}
} else {
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
FreeNetworkState( GetNetworkInfo( i ), entityNum );
}
FreeNetworkState( demoClientNetworkInfo, entityNum );
}
#ifdef SD_SUPPORT_REPEATER
for ( int i = 0; i < repeaterNetworkInfo.Num(); i++ ) {
if ( repeaterNetworkInfo[ i ] == NULL ) {
continue;
}
FreeNetworkState( *repeaterNetworkInfo[ i ], entityNum );
}
#endif // SD_SUPPORT_REPEATER
}
/*
================
idGameLocal::ClientSpawn
================
*/
void idGameLocal::ClientSpawn( int entityNum, int spawnId, int typeNum, int entityDefNumber, int mapSpawnId ) {
idTypeInfo* typeInfo = NULL;
// if there is no entity or an entity of the wrong type
if ( entities[ entityNum ] ) {
// Warning( "idGameLocal::ClientSpawn: New Entity Spawned In Already Occupied Position" );
entities[ entityNum ]->ProcessEvent( &EV_Remove );
assert( !entities[ entityNum ] );
}
spawnCount = spawnId; // Makes the spawn func set up the spawnId properly
idDict args;
args.SetInt( "spawn_entnum", entityNum );
args.Set( "name", va( "entity%d", entityNum ) );
idEntity* ent = NULL;
if ( mapSpawnId != -1 ) {
if ( mapSpawnId > mapFile->GetNumEntities() ) {
Error( "Tried to spawn out of bounds entity number '%i' from map", mapSpawnId );
}
idMapEntity* mapEnt = mapFile->GetEntity( mapSpawnId );
args.Copy( mapEnt->epairs );
// precache any media specified in the map entity
CacheDictionaryMedia( args );
SpawnEntityDef( args, true, &ent, -1, mapSpawnId );
assert( ent );
} else if ( entityDefNumber >= 0 ) {
if ( entityDefNumber >= declHolder.declEntityDefType.Num() ) {
Error( "server has %d entityDefs instead of %d", entityDefNumber, declHolder.declEntityDefType.Num() );
}
const char* classname = declHolder.declEntityDefType.LocalFindByIndex( entityDefNumber, false )->GetName();
args.Set( "classname", classname );
if ( !SpawnEntityDef( args, true, &ent ) ) {
Error( "Failed to spawn entity with classname '%s'", classname );
}
} else {
idTypeInfo* t = idClass::GetType( typeNum );
if ( !t ) {
Error( "Unknown type number %d for entity %d with class number %d", typeNum, entityNum, entityDefNumber );
}
ent = CreateEntityType( *t );
CallSpawnFuncs( ent, &args );
if ( !entities[ entityNum ] ) {
Error( "Failed to spawn entity of type '%s'", t->classname );
}
ent->PostMapSpawn();
}
}
/*
================
idGameLocal::SetupGameStateBase
================
*/
void idGameLocal::SetupGameStateBase( clientNetworkInfo_t& networkInfo ) {
for ( int i = 0; i < ENSM_NUM_MODES; i++ ) {
FreeGameState( networkInfo.gameStates[ i ] );
sdGameState* state = AllocGameState( GetGameStateObject( ( extNetworkStateMode_t )i ) );
if ( state->data ) {
state->data->MakeDefault();
}
networkInfo.gameStates[ i ] = state;
}
}
/*
================
idGameLocal::SetupEntityStateBase
================
*/
void idGameLocal::SetupEntityStateBase( clientNetworkInfo_t& networkInfo ) {
idEntity* ent;
for ( ent = networkedEntities.Next(); ent; ent = ent->networkNode.Next() ) {
for ( int i = 0; i < NSM_NUM_MODES; i++ ) {
FreeNetworkState( networkInfo.states[ ent->entityNumber ][ i ] );
sdEntityState* state = AllocEntityState( ( networkStateMode_t )i, ent );
if ( state->data ) {
state->data->MakeDefault();
}
networkInfo.states[ ent->entityNumber ][ i ] = state;
}
}
}
/*
================
idGameLocal::OnEntityCreateMessage
================
*/
void idGameLocal::OnEntityCreateMessage( const idBitMsg& msg ) {
int temp = msg.ReadBits( 32 );
int entityNum = EntityNumForSpawnId( temp );
int spawnId = SpawnNumForSpawnId( temp );
int createType = msg.ReadBits( 2 );
switch ( createType ) {
case 0: {
int mapSpawnId = msg.ReadBits( idMath::BitsForInteger( GetNumMapEntities() ) );
ClientSpawn( entityNum, spawnId, -1, -1, mapSpawnId );
break;
}
case 1: {
int entityDefNumber = msg.ReadBits( GetNumEntityDefBits() ) - 1;
ClientSpawn( entityNum, spawnId, -1, entityDefNumber, -1 );
break;
}
case 2: {
int typeNum = msg.ReadBits( idClass::GetTypeNumBits() );
ClientSpawn( entityNum, spawnId, typeNum, -1, -1 );
break;
}
default:
Error( "idGameLocal::OnEntityCreateMessage Unknown Create Type: '%d'", createType );
break;
}
if ( entities[ entityNum ] == NULL ) {
Error( "idGameLocal::OnEntityCreateMessage Failed To Create Entity" );
return;
}
entities[ entityNum ]->networkNode.AddToEnd( networkedEntities );
CreateNetworkState( entityNum );
}
/*
================
idGameLocal::HandleNewEntityEvent
================
*/
void idGameLocal::HandleNewEntityEvent( const idBitMsg &msg ) {
sdEntityNetEvent* event = entityNetEventAllocator.Alloc();
event->GetNode().AddToEnd( entityNetEventQueue );
event->Read( msg );
}
/*
================
idGameLocal::ClientProcessReliableMessage
================
*/
void idGameLocal::ClientProcessReliableMessage( const idBitMsg &msg ) {
gameLocal.isNewFrame = true;
idPlayer* localPlayer = GetLocalPlayer();
gameReliableServerMessage_t id = static_cast< gameReliableServerMessage_t >( msg.ReadBits( idMath::BitsForInteger( GAME_RELIABLE_SMESSAGE_NUM_MESSAGES ) ) );
switch( id ) {
case GAME_RELIABLE_SMESSAGE_DELETE_ENT: {
int spawnId = msg.ReadBits( 32 );
idEntity* ent = EntityForSpawnId( spawnId );
if ( ent ) {
ent->ProcessEvent( &EV_Remove );
}
break;
}
case GAME_RELIABLE_SMESSAGE_MULTI_CREATE_ENT: {
int count = msg.ReadLong();
for ( int i = 0; i < count; i++ ) {
OnEntityCreateMessage( msg );
}
break;
}
case GAME_RELIABLE_SMESSAGE_CREATE_ENT: {
OnEntityCreateMessage( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_CHAT:
case GAME_RELIABLE_SMESSAGE_FIRETEAM_CHAT:
case GAME_RELIABLE_SMESSAGE_TEAM_CHAT: {
sdGameRules::chatMode_t chatMode;
switch ( id ) {
case GAME_RELIABLE_SMESSAGE_CHAT:
chatMode = sdGameRules::CHAT_MODE_SAY;
break;
case GAME_RELIABLE_SMESSAGE_FIRETEAM_CHAT:
chatMode = sdGameRules::CHAT_MODE_SAY_FIRETEAM;
break;
case GAME_RELIABLE_SMESSAGE_TEAM_CHAT:
chatMode = sdGameRules::CHAT_MODE_SAY_TEAM;
break;
}
wchar_t text[ 128 ];
idVec3 location = msg.ReadVector();
int clientNum = msg.ReadChar();
msg.ReadString( text, sizeof( text ) / sizeof( wchar_t ) );
rules->AddChatLine( location, chatMode, clientNum, text );
break;
}
case GAME_RELIABLE_SMESSAGE_QUICK_CHAT: {
idVec3 location = msg.ReadVector();
int clientNum = msg.ReadChar();
int index = msg.ReadLong();
int spawnID = msg.ReadLong();
idEntity* target = NULL;
if ( spawnID != -1 ) {
target = gameLocal.EntityForSpawnId( spawnID );
}
localPlayer->PlayQuickChat( location, clientNum, declQuickChatType.SafeIndex( index ), target );
break;
}
case GAME_RELIABLE_SMESSAGE_MULTI_ENTITY_EVENT: {
int count = msg.ReadLong();
for ( int i = 0; i < count; i++ ) {
HandleNewEntityEvent( msg );
}
break;
}
case GAME_RELIABLE_SMESSAGE_ENTITY_EVENT: {
HandleNewEntityEvent( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_TOOLTIP: {
const sdDeclToolTip* toolTip = declToolTipType.SafeIndex( msg.ReadLong() );
if ( toolTip == NULL ) {
return;
}
int numParms = msg.ReadLong();
sdToolTipParms* parms = new sdToolTipParms();
wchar_t buffer[ 128 ];
for ( int i = 0; i < numParms; i++ ) {
msg.ReadString( buffer, sizeof( buffer ) );
parms->Add( buffer );
}
localPlayer->SpawnToolTip( toolTip, parms );
break;
}
case GAME_RELIABLE_SMESSAGE_TASK: {
sdPlayerTask::HandleMessage( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_FIRETEAM: {
sdFireTeam::HandleMessage( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_CREATEPLAYERTARGETTIMER: {
if ( isServer ) {
return;
}
char buffer[ 256 ];
msg.ReadString( buffer, sizeof( buffer ) );
qhandle_t serverHandle = msg.ReadShort();
qhandle_t handle = AllocTargetTimer( buffer );
if ( targetTimers[ handle ].serverHandle == -1 ) {
targetTimers[ handle ].serverHandle = serverHandle;
if ( serverHandle >= targetTimerLookup.Num() ) {
targetTimerLookup.AssureSize( serverHandle + 1, -1 );
}
targetTimerLookup[ serverHandle ] = handle;
numServerTimers++;
// assert( targetTimerLookup.Num() == numServerTimers );
} else {
assert( targetTimers[ handle ].serverHandle == serverHandle );
}
break;
}
case GAME_RELIABLE_SMESSAGE_NETWORKEVENT: {
int entityId = msg.ReadLong();
char buffer[ 512 ];
msg.ReadString( buffer, sizeof( buffer ) );
if ( entityId == NETWORKEVENT_RULES_ID ) {
rules->OnNetworkEvent( buffer );
} else if ( entityId == NETWORKEVENT_OBJECTIVE_ID ) {
sdObjectiveManager::GetInstance().OnNetworkEvent( buffer );
} else {
HandleNetworkEvent( EntityForSpawnId( entityId ), buffer );
}
break;
}
case GAME_RELIABLE_SMESSAGE_CREATEDEPLOYREQUEST: {
int index = msg.ReadLong();
float rotation = msg.ReadFloat();
idPlayer* owner = EntityForSpawnId( msg.ReadLong() )->Cast< idPlayer >();
const sdDeclDeployableObject* object = declDeployableObjectType[ msg.ReadBits( GetNumDeployObjectBits() ) - 1 ];
idVec3 position = msg.ReadVector();
sdTeamInfo* team = sdTeamManager::GetInstance().ReadTeamFromStream( msg );
if ( deployRequests[ index ] ) {
assert( false );
delete deployRequests[ index ];
}
deployRequests[ index ] = new sdDeployRequest( object, owner, position, rotation, team, 0 );
break;
}
case GAME_RELIABLE_SMESSAGE_DELETEDEPLOYREQUEST: {
int index = msg.ReadLong();
assert( deployRequests[ index ] );
delete deployRequests[ index ];
deployRequests[ index ] = NULL;
break;
}
case GAME_RELIABLE_SMESSAGE_USERGROUPS: {
sdUserGroupManager::GetInstance().ReadNetworkData( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_OBITUARY: {
int clientIndex = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
int attackerIndex = msg.ReadLong();
int damageDeclIndex = msg.ReadBits( gameLocal.GetNumDamageDeclBits() ) - 1;
idPlayer* player = GetClient( clientIndex );
idEntity* killer = EntityForSpawnId( attackerIndex );
const sdDeclDamage* damageDecl = declDamageType[ damageDeclIndex ];
if ( player != NULL ) {
player->Obituary( killer, damageDecl );
}
break;
}
case GAME_RELIABLE_SMESSAGE_VOTE: {
sdVoteManager::GetInstance().ClientReadNetworkMessage( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_SETNEXTOBJECTIVE: {
sdTeamInfo* team = sdTeamManager::GetInstance().ReadTeamFromStream( msg );
int index = msg.ReadLong();
sdObjectiveManager::GetInstance().SetNextObjective( team, index );
break;
}
case GAME_RELIABLE_SMESSAGE_RECORD_DEMO: {
StartRecordingDemo();
break;
}
case GAME_RELIABLE_SMESSAGE_RULES_DATA: {
sdGameRules::ParseNetworkMessage( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_SETSPAWNPOINT: {
int spawnId = msg.ReadLong();
localPlayer->SetSpawnPoint( gameLocal.EntityForSpawnId( spawnId ) );
break;
}
case GAME_RELIABLE_SMESSAGE_FIRETEAM_DISBAND: {
localPlayer->OnFireTeamDisbanded();
break;
}
case GAME_RELIABLE_SMESSAGE_SESSION_ID: {
sdnet.ProcessSessionId( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_STATSMESSAGE: {
sdGlobalStatsTracker::GetInstance().OnServerStatsRequestMessage( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_STATSFINIHED: {
bool completed = msg.ReadBool();
if ( completed ) {
sdGlobalStatsTracker::GetInstance().OnServerStatsRequestCompleted();
} else {
sdGlobalStatsTracker::GetInstance().OnServerStatsRequestCancelled();
}
break;
}
case GAME_RELIABLE_SMESSAGE_LOCALISEDMESSAGE: {
idVec3 location = msg.ReadVector();
const sdDeclLocStr* locStr = declHolder.declLocStrType.SafeIndex( msg.ReadLong() );
wchar_t buffer[ 256 ];
int parmCount = msg.ReadLong();
idWStrList strings;
strings.SetNum( parmCount );
for ( int i = 0; i < parmCount; i++ ) {
msg.ReadString( buffer, sizeof( buffer ) );
strings[ i ] = buffer;
}
localPlayer->SendLocalisedMessage( locStr, strings );
break;
}
case GAME_RELIABLE_SMESSAGE_ENDGAMESTATS: {
int count = msg.ReadLong();
endGameStats.SetNum( count, false );
for ( int i = 0; i < count; i++ ) {
int rankIndex = msg.ReadLong();
endGameStats[ i ].rank = rankIndex == -1 ? NULL : gameLocal.declRankType.SafeIndex( rankIndex );
char buffer[ 256 ];
msg.ReadString( buffer, sizeof( buffer ) );
endGameStats[ i ].name = buffer;
endGameStats[ i ].value = msg.ReadFloat();
float localValue = msg.ReadFloat();
if ( localClientNum >= 0 && localClientNum < MAX_CLIENTS ) {
endGameStats[ i ].data[ localClientNum ] = localValue;
}
}
OnEndGameStatsReceived();
break;
}
case GAME_RELIABLE_SMESSAGE_ADMINFEEDBACK: {
bool success = msg.ReadBool();
sdAdminSystem::GetInstance().SetCommandState( success ? sdAdminSystemLocal::CS_SUCCESS : sdAdminSystemLocal::CS_FAILED );
break;
}
case GAME_RELIABLE_SMESSAGE_SETCRITICALCLASS: {
playerTeamTypes_t playerTeam = static_cast< playerTeamTypes_t >( msg.ReadChar() );
playerClassTypes_t criticalClass = static_cast< playerClassTypes_t >( msg.ReadChar() );
sdObjectiveManager::GetInstance().SetCriticalClass( playerTeam, criticalClass );
break;
}
case GAME_RELIABLE_SMESSAGE_MAP_RESTART: {
OnLocalMapRestart();
break;
}
case GAME_RELIABLE_SMESSAGE_PAUSEINFO: {
bool paused = msg.ReadBool();
int newTime = msg.ReadLong();
int newTimeOffset = msg.ReadLong();
isPaused = paused;
time = newTime;
timeOffset = newTimeOffset;
break;
}
#ifdef SD_SUPPORT_REPEATER
case GAME_RELIABLE_SMESSAGE_REPEATER_CHAT: {
char playerName[ 128 ];
msg.ReadString( playerName, sizeof( playerName ) );
int clientIndex = msg.ReadLong();
wchar_t textBuffer[ 128 ];
msg.ReadString( textBuffer, sizeof( textBuffer ) / sizeof( textBuffer[ 0 ] ) );
rules->AddRepeaterChatLine( playerName, clientIndex, textBuffer );
break;
}
#endif // SD_SUPPORT_REPEATER
case GAME_RELIABLE_SMESSAGE_BANLISTMESSAGE: {
sdReliableClientMessage outMsg( GAME_RELIABLE_CMESSAGE_ACKNOWLEDGEBANLIST );
outMsg.Send();
guidFile.ListBans( msg );
break;
}
case GAME_RELIABLE_SMESSAGE_BANLISTFINISHED: {
break;
}
case GAME_RELIABLE_SMESSAGE_REPEATERSTATUS: {
bool repeaterActive = msg.ReadBool();
if ( repeaterActive ) {
gameLocal.Printf( "Repeater is running on this server\n" );
} else {
gameLocal.Printf( "Repeater is not running on this server\n" );
}
break;
}
default: {
Warning( "idGameLocal::ClientProcessReliableMessage: Unknown reliable message: %d", id );
break;
}
}
}
ID_INLINE bool ThinkNow( idEntity* ent, bool newFrame ) {
return !( ent->snapshotPVSFlags & PVS_DEFERRED_VISIBLE ) || newFrame;
}
/*
================
idGameLocal::OnSnapshotHitch
================
*/
void idGameLocal::OnSnapshotHitch( int snapshotTime ) {
if ( realClientTime != 0 ) {
uiManager->OnSnapshotHitch( realClientTime - snapshotTime );
}
realClientTime = snapshotTime;
}
/*
================
idGameLocal::OnClientDisconnected
================
*/
void idGameLocal::OnClientDisconnected( void ) {
isClient = false;
realClientTime = 0;
time = 0;
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
GetProficiencyTable( i ).Clear( true );
}
}
/*
================
idGameLocal::ClientPrediction
================
*/
void idGameLocal::ClientPrediction( const usercmd_t* clientCmds, const usercmd_t* demoCmd ) {
idPlayer* localPlayer = GetLocalPlayer();
if ( localPlayer != NULL ) {
unlock.canUnlockFrames = localPlayer->IsFPSUnlock();
} else {
unlock.canUnlockFrames = false;
}
unlock.unlockedDraw = false;
interpolateEntities.Clear();
idEntity* ent;
idPlayer* player = GetLocalPlayer();
sdDemoManagerLocal& demoManager = sdDemoManager::GetInstance();
if ( demoManager.GetState() == sdDemoManagerLocal::DS_PAUSED ) {
gameRenderWorld->DebugClearLines( 0 );
}
if ( serverIsRepeater && demoCmd != NULL ) {
playerView.SetRepeaterUserCmd( *demoCmd );
}
framenum++;
int frameTime = networkSystem->ClientGetFrameTime();
if ( IsPaused() ) {
msec = 0;
previousTime = time;
time += 0;
timeOffset += frameTime;
} else {
msec = frameTime;
previousTime = time;
time += frameTime;
timeOffset += 0;
}
int now = gameLocal.ToGuiTime( time );
// update the real client time and the new frame flag
if ( now > realClientTime ) {
realClientTime = now;
isNewFrame = true;
if ( predictionUpdateRequired ) {
for ( idEntity* ent = networkedEntities.Next(); ent; ent = ent->networkNode.Next() ) {
ent->UpdatePredictionErrorDecay();
}
predictionUpdateRequired = false;
}
} else {
isNewFrame = false;
}
UpdateDeploymentRequests();
// check for local client lag
if ( player ) {
player->SetLagged( networkSystem->ClientGetTimeSinceLastPacket() >= net_clientMaxPrediction.GetInteger() );
}
gameRenderWorld->DebugClearLines( time );
gameRenderWorld->DebugClearPolygons( time );
#ifdef CLIP_DEBUG_EXTREME
clip.UpdateTraceLogging();
#endif
sdProfileHelperManager::GetInstance().Update();
if ( isNewFrame ) {
bse->StartFrame();
}
// set the user commands for this frame
memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
// update demo state
demoManager.RunDemoFrame( demoCmd );
if ( !isRepeater ) {
sdPhysicsEvent* physicsEvent;
for ( physicsEvent = physicsEvents.Next(); physicsEvent; physicsEvent = physicsEvent->GetNode().Next() ) {
if ( physicsEvent->GetCreationTime() != time ) {
continue;
}
physicsEvent->Apply();
}
for ( int i = 0; i < changedEntities.Num(); i++ ) {
idEntity* ent = changedEntities[ i ];
if ( !ent ) {
continue;
}
if ( !ent->thinkFlags || ent->NoThink() ) {
ent->activeNode.Remove();
ent->activeNetworkNode.Remove();
} else {
if ( ent->IsNetSynced() ) {
if ( !ent->activeNetworkNode.InList() ) {
ent->activeNetworkNode.AddToEnd( gameLocal.activeNetworkEntities );
}
}
if ( !ent->activeNode.InList() ) {
ent->activeNode.AddToEnd( gameLocal.activeEntities );
}
}
}
changedEntities.Clear();
idEntity::ClearEntityCaches();
if ( !isNewFrame ) {
idEntity* nextEnt;
for( ent = activeNetworkEntities.Next(); ent != NULL; ent = nextEnt ) {
nextEnt = ent->activeNetworkNode.Next();
if ( ( ent->snapshotPVSFlags & PVS_DEFERRED_VISIBLE ) == 0 ) {
sdProfileHelper_ScopeTimer( "EntityThink", ent->spawnArgs.GetString( "classname" ), g_profileEntityThink.GetBool() );
ent->Think();
} else {
ent->UpdateVisuals();
}
}
} else {
// sort the active entity list
SortActiveEntityList();
idEntity* nextEnt;
for( ent = activeEntities.Next(); ent != NULL; ent = nextEnt ) {
sdProfileHelper_ScopeTimer( "EntityThink", ent->spawnArgs.GetString( "classname" ), g_profileEntityThink.GetBool() );
nextEnt = ent->activeNode.Next();
ent->Think();
}
}
idPlayer* viewPlayer = GetLocalViewPlayer();
if ( viewPlayer != gameLocal.localPlayerProperties.GetActivePlayer() ) {
gameLocal.OnLocalViewPlayerChanged();
}
playerView.UpdateProxyView( viewPlayer, false );
if ( isNewFrame || demoManager.GetState() == sdDemoManagerLocal::DS_PAUSED ) {
for( ent = postThinkEntities.Next(); ent; ent = ent->GetPostThinkNode()->Next() ) {
ent->PostThink();
}
}
}
{
if ( isNewFrame ) {
rvClientEntity* cent;
for( cent = clientSpawnedEntities.Next(); cent != NULL; cent = cent->spawnNode.Next() ) {
cent->Think();
}
localPlayerProperties.UpdateHudModules();
// Some will remove themselves
sdEffect::UpdateDeadEffects();
// service any pending events
idEvent::ServiceEvents();
sdTeamManager::GetInstance().Think();
sdTaskManager::GetInstance().Think();
sdObjectiveManager::GetInstance().Think();
sdWayPointManager::GetInstance().Think();
sdWakeManager::GetInstance().Think();
sdAntiLagManager::GetInstance().Think();
tireTreadManager->Think();
footPrintManager->Think();
sdGlobalStatsTracker::GetInstance().UpdateStatsRequests();
if ( gameLocal.serverIsRepeater ) {
playerView.UpdateRepeaterView();
}
idPlayer* viewPlayer = GetLocalViewPlayer();
if ( viewPlayer != NULL ) {
playerView.CalculateShake( viewPlayer );
// fps unlock: record view position
int index = framenum & 1;
unlock.originlog[ index ] = viewPlayer->GetRenderView()->vieworg;
}
bse->EndFrame();
UpdateLoggedDecals();
// Run multiplayer game rules
if ( rules ) {
rules->Run();
}
// decrease deleted clip model reference counts
clip.ThreadDeleteClipModels();
// delete clip models without references for real
clip.ActuallyDeleteClipModels();
}
demoManager.EndDemoFrame();
if ( isNewFrame || demoManager.GetState() == sdDemoManagerLocal::DS_PAUSED ) {
// show any debug info for this frame
RunDebugInfo();
D_DrawDebugLines();
}
}
#ifndef _XENON
UpdateGameSession();
#endif // _XENON
}
/*
================
idGameLocal::ApplyRulesData
================
*/
void idGameLocal::ApplyRulesData( const sdEntityStateNetworkData& newState ) {
rules->ApplyNetworkState( newState );
}
/*
================
idGameLocal::ReadRulesData
================
*/
void idGameLocal::ReadRulesData( const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
rules->ReadNetworkState( baseState, newState, msg );
}
/*
================
idGameLocal::WriteRulesData
================
*/
void idGameLocal::WriteRulesData( const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
rules->WriteNetworkState( baseState, newState, msg );
}
/*
================
idGameLocal::CheckRulesData
================
*/
bool idGameLocal::CheckRulesData( const sdEntityStateNetworkData& baseState ) const {
return rules->CheckNetworkStateChanges( baseState );
}
/*
================
idGameLocal::CreateRulesData
================
*/
sdEntityStateNetworkData* idGameLocal::CreateRulesData( void ) const {
if ( !rules ) {
return NULL;
}
return rules->CreateNetworkStructure();
}