3894 lines
109 KiB
C++
3894 lines
109 KiB
C++
|
// 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();
|
||
|
}
|