2011-11-22 21:28:15 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 GPL Source Code
2011-12-06 18:20:15 +00:00
Copyright ( C ) 1999 - 2011 id Software LLC , a ZeniMax Media company .
2011-11-22 21:28:15 +00:00
2011-12-06 16:14:59 +00:00
This file is part of the Doom 3 GPL Source Code ( " Doom 3 Source Code " ) .
2011-11-22 21:28:15 +00:00
Doom 3 Source Code is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Doom 3 Source Code is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code . If not , see < http : //www.gnu.org/licenses/>.
In addition , the Doom 3 Source Code is also subject to certain additional terms . You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code . If not , please request a copy in writing from id Software at the address below .
If you have questions concerning this license or the applicable additional terms , you may contact in writing id Software LLC , c / o ZeniMax Media Inc . , Suite 120 , Rockville , Maryland 20850 USA .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "../idlib/precompiled.h"
# pragma hdrstop
# include "Game_local.h"
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Client running game code :
- entity events don ' t work and should not be issued
- entities should never be spawned outside idGameLocal : : ClientReadSnapshot
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
// adds tags to the network protocol to detect when things go bad ( internal consistency )
// NOTE: this changes the network protocol
# ifndef ASYNC_WRITE_TAGS
# define ASYNC_WRITE_TAGS 0
# endif
idCVar net_clientShowSnapshot ( " net_clientShowSnapshot " , " 0 " , CVAR_GAME | CVAR_INTEGER , " " , 0 , 3 , idCmdSystem : : ArgCompletion_Integer < 0 , 3 > ) ;
idCVar net_clientShowSnapshotRadius ( " net_clientShowSnapshotRadius " , " 128 " , CVAR_GAME | CVAR_FLOAT , " " ) ;
idCVar net_clientSmoothing ( " net_clientSmoothing " , " 0.8 " , CVAR_GAME | CVAR_FLOAT , " smooth other clients angles and position. " , 0.0f , 0.95f ) ;
idCVar net_clientSelfSmoothing ( " net_clientSelfSmoothing " , " 0.6 " , CVAR_GAME | CVAR_FLOAT , " smooth self position if network causes prediction error. " , 0.0f , 0.95f ) ;
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 " , " 1 " , CVAR_GAME | CVAR_BOOL | CVAR_NOCHEAT | CVAR_ARCHIVE , " draw prediction graph " ) ;
/*
= = = = = = = = = = = = = = = =
idGameLocal : : InitAsyncNetwork
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : InitAsyncNetwork ( void ) {
int i , type ;
for ( i = 0 ; i < MAX_CLIENTS ; i + + ) {
for ( type = 0 ; type < declManager - > GetNumDeclTypes ( ) ; type + + ) {
clientDeclRemap [ i ] [ type ] . Clear ( ) ;
}
}
memset ( clientEntityStates , 0 , sizeof ( clientEntityStates ) ) ;
memset ( clientPVS , 0 , sizeof ( clientPVS ) ) ;
memset ( clientSnapshots , 0 , sizeof ( clientSnapshots ) ) ;
eventQueue . Init ( ) ;
savedEventQueue . Init ( ) ;
entityDefBits = - ( idMath : : BitsForInteger ( declManager - > GetNumDecls ( DECL_ENTITYDEF ) ) + 1 ) ;
localClientNum = 0 ; // on a listen server SetLocalUser will set this right
realClientTime = 0 ;
isNewFrame = true ;
clientSmoothing = net_clientSmoothing . GetFloat ( ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ShutdownAsyncNetwork
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ShutdownAsyncNetwork ( void ) {
entityStateAllocator . Shutdown ( ) ;
snapshotAllocator . Shutdown ( ) ;
eventQueue . Shutdown ( ) ;
savedEventQueue . Shutdown ( ) ;
memset ( clientEntityStates , 0 , sizeof ( clientEntityStates ) ) ;
memset ( clientPVS , 0 , sizeof ( clientPVS ) ) ;
memset ( clientSnapshots , 0 , sizeof ( clientSnapshots ) ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : InitLocalClient
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : InitLocalClient ( int clientNum ) {
isServer = false ;
isClient = true ;
localClientNum = clientNum ;
clientSmoothing = net_clientSmoothing . GetFloat ( ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : InitClientDeclRemap
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : InitClientDeclRemap ( int clientNum ) {
int type , i , num ;
for ( type = 0 ; type < declManager - > GetNumDeclTypes ( ) ; type + + ) {
// only implicit materials and sound shaders decls are used
if ( type ! = DECL_MATERIAL & & type ! = DECL_SOUND ) {
continue ;
}
num = declManager - > GetNumDecls ( ( declType_t ) type ) ;
clientDeclRemap [ clientNum ] [ type ] . Clear ( ) ;
clientDeclRemap [ clientNum ] [ type ] . AssureSize ( num , - 1 ) ;
// pre-initialize the remap with non-implicit decls, all non-implicit decls are always going
// to be in order and in sync between server and client because of the decl manager checksum
for ( i = 0 ; i < num ; i + + ) {
const idDecl * decl = declManager - > DeclByIndex ( ( declType_t ) type , i , false ) ;
if ( decl - > IsImplicit ( ) ) {
// once the first implicit decl is found all remaining decls are considered implicit as well
break ;
}
clientDeclRemap [ clientNum ] [ type ] [ i ] = i ;
}
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerSendDeclRemapToClient
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerSendDeclRemapToClient ( int clientNum , declType_t type , int index ) {
idBitMsg outMsg ;
byte msgBuf [ MAX_GAME_MESSAGE_SIZE ] ;
// if no client connected for this spot
if ( entities [ clientNum ] = = NULL ) {
return ;
}
// increase size of list if required
if ( index > = clientDeclRemap [ clientNum ] [ type ] . Num ( ) ) {
clientDeclRemap [ clientNum ] [ ( int ) type ] . AssureSize ( index + 1 , - 1 ) ;
}
// if already remapped
if ( clientDeclRemap [ clientNum ] [ ( int ) type ] [ index ] ! = - 1 ) {
return ;
}
const idDecl * decl = declManager - > DeclByIndex ( type , index , false ) ;
if ( decl = = NULL ) {
gameLocal . Error ( " server tried to remap bad %s decl index %d " , declManager - > GetDeclNameFromType ( type ) , index ) ;
return ;
}
// set the index at the server
clientDeclRemap [ clientNum ] [ ( int ) type ] [ index ] = index ;
// write update to client
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_REMAP_DECL ) ;
outMsg . WriteByte ( type ) ;
outMsg . WriteLong ( index ) ;
outMsg . WriteString ( decl - > GetName ( ) ) ;
networkSystem - > ServerSendReliableMessage ( clientNum , outMsg ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerRemapDecl
= = = = = = = = = = = = = = = =
*/
int idGameLocal : : ServerRemapDecl ( int clientNum , declType_t type , int index ) {
// only implicit materials and sound shaders decls are used
if ( type ! = DECL_MATERIAL & & type ! = DECL_SOUND ) {
return index ;
}
if ( clientNum = = - 1 ) {
for ( int i = 0 ; i < MAX_CLIENTS ; i + + ) {
ServerSendDeclRemapToClient ( i , type , index ) ;
}
} else {
ServerSendDeclRemapToClient ( clientNum , type , index ) ;
}
return index ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ClientRemapDecl
= = = = = = = = = = = = = = = =
*/
int idGameLocal : : ClientRemapDecl ( declType_t type , int index ) {
// only implicit materials and sound shaders decls are used
if ( type ! = DECL_MATERIAL & & type ! = DECL_SOUND ) {
return index ;
}
// negative indexes are sometimes used for NULL decls
if ( index < 0 ) {
return index ;
}
// make sure the index is valid
if ( clientDeclRemap [ localClientNum ] [ ( int ) type ] . Num ( ) = = 0 ) {
gameLocal . Error ( " client received decl index %d before %s decl remap was initialized " , index , declManager - > GetDeclNameFromType ( type ) ) ;
return - 1 ;
}
if ( index > = clientDeclRemap [ localClientNum ] [ ( int ) type ] . Num ( ) ) {
gameLocal . Error ( " client received unmapped %s decl index %d from server " , declManager - > GetDeclNameFromType ( type ) , index ) ;
return - 1 ;
}
if ( clientDeclRemap [ localClientNum ] [ ( int ) type ] [ index ] = = - 1 ) {
gameLocal . Error ( " client received unmapped %s decl index %d from server " , declManager - > GetDeclNameFromType ( type ) , index ) ;
return - 1 ;
}
return clientDeclRemap [ localClientNum ] [ type ] [ index ] ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerAllowClient
= = = = = = = = = = = = = = = =
*/
allowReply_t idGameLocal : : ServerAllowClient ( int numClients , const char * IP , const char * guid , const char * password , char reason [ MAX_STRING_CHARS ] ) {
reason [ 0 ] = ' \0 ' ;
if ( serverInfo . GetInt ( " si_pure " ) & & ! mpGame . IsPureReady ( ) ) {
idStr : : snPrintf ( reason , MAX_STRING_CHARS , " #str_07139 " ) ;
return ALLOW_NOTYET ;
}
if ( ! serverInfo . GetInt ( " si_maxPlayers " ) ) {
idStr : : snPrintf ( reason , MAX_STRING_CHARS , " #str_07140 " ) ;
return ALLOW_NOTYET ;
}
if ( numClients > = serverInfo . GetInt ( " si_maxPlayers " ) ) {
idStr : : snPrintf ( reason , MAX_STRING_CHARS , " #str_07141 " ) ;
return ALLOW_NOTYET ;
}
if ( ! cvarSystem - > GetCVarBool ( " si_usepass " ) ) {
return ALLOW_YES ;
}
const char * pass = cvarSystem - > GetCVarString ( " g_password " ) ;
if ( pass [ 0 ] = = ' \0 ' ) {
common - > Warning ( " si_usepass is set but g_password is empty " ) ;
cmdSystem - > BufferCommandText ( CMD_EXEC_NOW , " say si_usepass is set but g_password is empty " ) ;
// avoids silent misconfigured state
idStr : : snPrintf ( reason , MAX_STRING_CHARS , " #str_07142 " ) ;
return ALLOW_NOTYET ;
}
if ( ! idStr : : Cmp ( pass , password ) ) {
return ALLOW_YES ;
}
idStr : : snPrintf ( reason , MAX_STRING_CHARS , " #str_07143 " ) ;
Printf ( " Rejecting client %s from IP %s: invalid password \n " , guid , IP ) ;
return ALLOW_BADPASS ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerClientConnect
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerClientConnect ( int clientNum , const char * guid ) {
// make sure no parasite entity is left
if ( entities [ clientNum ] ) {
common - > DPrintf ( " ServerClientConnect: remove old player entity \n " ) ;
delete entities [ clientNum ] ;
}
userInfo [ clientNum ] . Clear ( ) ;
mpGame . ServerClientConnect ( clientNum ) ;
Printf ( " client %d connected. \n " , clientNum ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerClientBegin
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerClientBegin ( int clientNum ) {
idBitMsg outMsg ;
byte msgBuf [ MAX_GAME_MESSAGE_SIZE ] ;
// initialize the decl remap
InitClientDeclRemap ( clientNum ) ;
// send message to initialize decl remap at the client (this is always the very first reliable game message)
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_INIT_DECL_REMAP ) ;
networkSystem - > ServerSendReliableMessage ( clientNum , outMsg ) ;
// spawn the player
SpawnPlayer ( clientNum ) ;
if ( clientNum = = localClientNum ) {
mpGame . EnterGame ( clientNum ) ;
}
// send message to spawn the player at the clients
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_SPAWN_PLAYER ) ;
outMsg . WriteByte ( clientNum ) ;
outMsg . WriteLong ( spawnIds [ clientNum ] ) ;
networkSystem - > ServerSendReliableMessage ( - 1 , outMsg ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerClientDisconnect
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerClientDisconnect ( int clientNum ) {
int i ;
idBitMsg outMsg ;
byte msgBuf [ MAX_GAME_MESSAGE_SIZE ] ;
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_DELETE_ENT ) ;
outMsg . WriteBits ( ( spawnIds [ clientNum ] < < GENTITYNUM_BITS ) | clientNum , 32 ) ; // see GetSpawnId
networkSystem - > ServerSendReliableMessage ( - 1 , outMsg ) ;
// free snapshots stored for this client
FreeSnapshotsOlderThanSequence ( clientNum , 0x7FFFFFFF ) ;
// free entity states stored for this client
for ( i = 0 ; i < MAX_GENTITIES ; i + + ) {
if ( clientEntityStates [ clientNum ] [ i ] ) {
entityStateAllocator . Free ( clientEntityStates [ clientNum ] [ i ] ) ;
clientEntityStates [ clientNum ] [ i ] = NULL ;
}
}
// clear the client PVS
memset ( clientPVS [ clientNum ] , 0 , sizeof ( clientPVS [ clientNum ] ) ) ;
// delete the player entity
delete entities [ clientNum ] ;
mpGame . DisconnectClient ( clientNum ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerWriteInitialReliableMessages
Send reliable messages to initialize the client game up to a certain initial state .
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerWriteInitialReliableMessages ( int clientNum ) {
int i ;
idBitMsg outMsg ;
byte msgBuf [ MAX_GAME_MESSAGE_SIZE ] ;
entityNetEvent_t * event ;
// spawn players
for ( i = 0 ; i < MAX_CLIENTS ; i + + ) {
if ( entities [ i ] = = NULL | | i = = clientNum ) {
continue ;
}
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_SPAWN_PLAYER ) ;
outMsg . WriteByte ( i ) ;
outMsg . WriteLong ( spawnIds [ i ] ) ;
networkSystem - > ServerSendReliableMessage ( clientNum , outMsg ) ;
}
// send all saved events
for ( event = savedEventQueue . Start ( ) ; event ; event = event - > next ) {
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_EVENT ) ;
outMsg . WriteBits ( event - > spawnId , 32 ) ;
outMsg . WriteByte ( event - > event ) ;
outMsg . WriteLong ( event - > time ) ;
outMsg . WriteBits ( event - > paramsSize , idMath : : BitsForInteger ( MAX_EVENT_PARAM_SIZE ) ) ;
if ( event - > paramsSize ) {
outMsg . WriteData ( event - > paramsBuf , event - > paramsSize ) ;
}
networkSystem - > ServerSendReliableMessage ( clientNum , outMsg ) ;
}
// update portals for opened doors
int numPortals = gameRenderWorld - > NumPortals ( ) ;
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_PORTALSTATES ) ;
outMsg . WriteLong ( numPortals ) ;
for ( i = 0 ; i < numPortals ; i + + ) {
outMsg . WriteBits ( gameRenderWorld - > GetPortalState ( ( qhandle_t ) ( i + 1 ) ) , NUM_RENDER_PORTAL_BITS ) ;
}
networkSystem - > ServerSendReliableMessage ( clientNum , outMsg ) ;
mpGame . ServerWriteInitialReliableMessages ( clientNum ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : SaveEntityNetworkEvent
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : SaveEntityNetworkEvent ( const idEntity * ent , int eventId , const idBitMsg * msg ) {
entityNetEvent_t * event ;
event = savedEventQueue . Alloc ( ) ;
event - > spawnId = GetSpawnId ( ent ) ;
event - > event = eventId ;
event - > time = time ;
if ( msg ) {
event - > paramsSize = msg - > GetSize ( ) ;
memcpy ( event - > paramsBuf , msg - > GetData ( ) , msg - > GetSize ( ) ) ;
} else {
event - > paramsSize = 0 ;
}
savedEventQueue . Enqueue ( event , idEventQueue : : OUTOFORDER_IGNORE ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : FreeSnapshotsOlderThanSequence
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : FreeSnapshotsOlderThanSequence ( int clientNum , int sequence ) {
snapshot_t * snapshot , * lastSnapshot , * nextSnapshot ;
entityState_t * state ;
for ( lastSnapshot = NULL , snapshot = clientSnapshots [ clientNum ] ; snapshot ; snapshot = nextSnapshot ) {
nextSnapshot = snapshot - > next ;
if ( snapshot - > sequence < sequence ) {
for ( state = snapshot - > firstEntityState ; state ; state = snapshot - > firstEntityState ) {
snapshot - > firstEntityState = snapshot - > firstEntityState - > next ;
entityStateAllocator . Free ( state ) ;
}
if ( lastSnapshot ) {
lastSnapshot - > next = snapshot - > next ;
} else {
clientSnapshots [ clientNum ] = snapshot - > next ;
}
snapshotAllocator . Free ( snapshot ) ;
} else {
lastSnapshot = snapshot ;
}
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ApplySnapshot
= = = = = = = = = = = = = = = =
*/
bool idGameLocal : : ApplySnapshot ( int clientNum , int sequence ) {
snapshot_t * snapshot , * lastSnapshot , * nextSnapshot ;
entityState_t * state ;
FreeSnapshotsOlderThanSequence ( clientNum , sequence ) ;
for ( lastSnapshot = NULL , snapshot = clientSnapshots [ clientNum ] ; snapshot ; snapshot = nextSnapshot ) {
nextSnapshot = snapshot - > next ;
if ( snapshot - > sequence = = sequence ) {
for ( state = snapshot - > firstEntityState ; state ; state = state - > next ) {
if ( clientEntityStates [ clientNum ] [ state - > entityNumber ] ) {
entityStateAllocator . Free ( clientEntityStates [ clientNum ] [ state - > entityNumber ] ) ;
}
clientEntityStates [ clientNum ] [ state - > entityNumber ] = state ;
}
memcpy ( clientPVS [ clientNum ] , snapshot - > pvs , sizeof ( snapshot - > pvs ) ) ;
if ( lastSnapshot ) {
lastSnapshot - > next = nextSnapshot ;
} else {
clientSnapshots [ clientNum ] = nextSnapshot ;
}
snapshotAllocator . Free ( snapshot ) ;
return true ;
} else {
lastSnapshot = snapshot ;
}
}
return false ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : WriteGameStateToSnapshot
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : WriteGameStateToSnapshot ( idBitMsgDelta & msg ) const {
int i ;
for ( i = 0 ; i < MAX_GLOBAL_SHADER_PARMS ; i + + ) {
msg . WriteFloat ( globalShaderParms [ i ] ) ;
}
mpGame . WriteToSnapshot ( msg ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ReadGameStateFromSnapshot
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ReadGameStateFromSnapshot ( const idBitMsgDelta & msg ) {
int i ;
for ( i = 0 ; i < MAX_GLOBAL_SHADER_PARMS ; i + + ) {
globalShaderParms [ i ] = msg . ReadFloat ( ) ;
}
mpGame . ReadFromSnapshot ( msg ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerWriteSnapshot
Write a snapshot of the current game state for the given client .
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerWriteSnapshot ( int clientNum , int sequence , idBitMsg & msg , byte * clientInPVS , int numPVSClients ) {
int i , msgSize , msgWriteBit ;
idPlayer * player , * spectated = NULL ;
idEntity * ent ;
pvsHandle_t pvsHandle ;
idBitMsgDelta deltaMsg ;
snapshot_t * snapshot ;
entityState_t * base , * newBase ;
int numSourceAreas , sourceAreas [ idEntity : : MAX_PVS_AREAS ] ;
player = static_cast < idPlayer * > ( entities [ clientNum ] ) ;
if ( ! player ) {
return ;
}
if ( player - > spectating & & player - > spectator ! = clientNum & & entities [ player - > spectator ] ) {
spectated = static_cast < idPlayer * > ( entities [ player - > spectator ] ) ;
} else {
spectated = player ;
}
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
// free too old snapshots
FreeSnapshotsOlderThanSequence ( clientNum , sequence - 64 ) ;
// allocate new snapshot
snapshot = snapshotAllocator . Alloc ( ) ;
snapshot - > sequence = sequence ;
snapshot - > firstEntityState = NULL ;
snapshot - > next = clientSnapshots [ clientNum ] ;
clientSnapshots [ clientNum ] = snapshot ;
memset ( snapshot - > pvs , 0 , sizeof ( snapshot - > pvs ) ) ;
// get PVS for this player
// don't use PVSAreas for networking - PVSAreas depends on animations (and md5 bounds), which are not synchronized
numSourceAreas = gameRenderWorld - > BoundsInAreas ( spectated - > GetPlayerPhysics ( ) - > GetAbsBounds ( ) , sourceAreas , idEntity : : MAX_PVS_AREAS ) ;
pvsHandle = gameLocal . pvs . SetupCurrentPVS ( sourceAreas , numSourceAreas , PVS_NORMAL ) ;
# ifdef _D3XP
// Add portalSky areas to PVS
if ( portalSkyEnt . GetEntity ( ) ) {
pvsHandle_t otherPVS , newPVS ;
idEntity * skyEnt = portalSkyEnt . GetEntity ( ) ;
otherPVS = gameLocal . pvs . SetupCurrentPVS ( skyEnt - > GetPVSAreas ( ) , skyEnt - > GetNumPVSAreas ( ) ) ;
newPVS = gameLocal . pvs . MergeCurrentPVS ( pvsHandle , otherPVS ) ;
pvs . FreeCurrentPVS ( pvsHandle ) ;
pvs . FreeCurrentPVS ( otherPVS ) ;
pvsHandle = newPVS ;
}
# endif
# if ASYNC_WRITE_TAGS
idRandom tagRandom ;
tagRandom . SetSeed ( random . RandomInt ( ) ) ;
msg . WriteLong ( tagRandom . GetSeed ( ) ) ;
# endif
// create the snapshot
for ( ent = spawnedEntities . Next ( ) ; ent ! = NULL ; ent = ent - > spawnNode . Next ( ) ) {
// if the entity is not in the player PVS
if ( ! ent - > PhysicsTeamInPVS ( pvsHandle ) & & ent - > entityNumber ! = clientNum ) {
continue ;
}
// add the entity to the snapshot pvs
snapshot - > pvs [ ent - > entityNumber > > 5 ] | = 1 < < ( ent - > entityNumber & 31 ) ;
// if that entity is not marked for network synchronization
if ( ! ent - > fl . networkSync ) {
continue ;
}
// save the write state to which we can revert when the entity didn't change at all
msg . SaveWriteState ( msgSize , msgWriteBit ) ;
// write the entity to the snapshot
msg . WriteBits ( ent - > entityNumber , GENTITYNUM_BITS ) ;
base = clientEntityStates [ clientNum ] [ ent - > entityNumber ] ;
if ( base ) {
base - > state . BeginReading ( ) ;
}
newBase = entityStateAllocator . Alloc ( ) ;
newBase - > entityNumber = ent - > entityNumber ;
newBase - > state . Init ( newBase - > stateBuf , sizeof ( newBase - > stateBuf ) ) ;
newBase - > state . BeginWriting ( ) ;
deltaMsg . Init ( base ? & base - > state : NULL , & newBase - > state , & msg ) ;
deltaMsg . WriteBits ( spawnIds [ ent - > entityNumber ] , 32 - GENTITYNUM_BITS ) ;
deltaMsg . WriteBits ( ent - > GetType ( ) - > typeNum , idClass : : GetTypeNumBits ( ) ) ;
deltaMsg . WriteBits ( ServerRemapDecl ( - 1 , DECL_ENTITYDEF , ent - > entityDefNumber ) , entityDefBits ) ;
// write the class specific data to the snapshot
ent - > WriteToSnapshot ( deltaMsg ) ;
if ( ! deltaMsg . HasChanged ( ) ) {
msg . RestoreWriteState ( msgSize , msgWriteBit ) ;
entityStateAllocator . Free ( newBase ) ;
} else {
newBase - > next = snapshot - > firstEntityState ;
snapshot - > firstEntityState = newBase ;
# if ASYNC_WRITE_TAGS
msg . WriteLong ( tagRandom . RandomInt ( ) ) ;
# endif
}
}
msg . WriteBits ( ENTITYNUM_NONE , GENTITYNUM_BITS ) ;
// write the PVS to the snapshot
# if ASYNC_WRITE_PVS
for ( i = 0 ; i < idEntity : : MAX_PVS_AREAS ; i + + ) {
if ( i < numSourceAreas ) {
msg . WriteLong ( sourceAreas [ i ] ) ;
} else {
msg . WriteLong ( 0 ) ;
}
}
gameLocal . pvs . WritePVS ( pvsHandle , msg ) ;
# endif
for ( i = 0 ; i < ENTITY_PVS_SIZE ; i + + ) {
msg . WriteDeltaLong ( clientPVS [ clientNum ] [ i ] , snapshot - > pvs [ i ] ) ;
}
// free the PVS
pvs . FreeCurrentPVS ( pvsHandle ) ;
// write the game and player state to the snapshot
base = clientEntityStates [ clientNum ] [ ENTITYNUM_NONE ] ; // ENTITYNUM_NONE is used for the game and player state
if ( base ) {
base - > state . BeginReading ( ) ;
}
newBase = entityStateAllocator . Alloc ( ) ;
newBase - > entityNumber = ENTITYNUM_NONE ;
newBase - > next = snapshot - > firstEntityState ;
snapshot - > firstEntityState = newBase ;
newBase - > state . Init ( newBase - > stateBuf , sizeof ( newBase - > stateBuf ) ) ;
newBase - > state . BeginWriting ( ) ;
deltaMsg . Init ( base ? & base - > state : NULL , & newBase - > state , & msg ) ;
if ( player - > spectating & & player - > spectator ! = player - > entityNumber & & gameLocal . entities [ player - > spectator ] & & gameLocal . entities [ player - > spectator ] - > IsType ( idPlayer : : Type ) ) {
static_cast < idPlayer * > ( gameLocal . entities [ player - > spectator ] ) - > WritePlayerStateToSnapshot ( deltaMsg ) ;
} else {
player - > WritePlayerStateToSnapshot ( deltaMsg ) ;
}
WriteGameStateToSnapshot ( deltaMsg ) ;
// copy the client PVS string
memcpy ( clientInPVS , snapshot - > pvs , ( numPVSClients + 7 ) > > 3 ) ;
LittleRevBytes ( clientInPVS , sizeof ( int ) , sizeof ( clientInPVS ) / sizeof ( int ) ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerApplySnapshot
= = = = = = = = = = = = = = = =
*/
bool idGameLocal : : ServerApplySnapshot ( int clientNum , int sequence ) {
return ApplySnapshot ( clientNum , sequence ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : NetworkEventWarning
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : NetworkEventWarning ( const entityNetEvent_t * event , const char * fmt , . . . ) {
char buf [ 1024 ] ;
int length = 0 ;
va_list argptr ;
int entityNum = event - > spawnId & ( ( 1 < < GENTITYNUM_BITS ) - 1 ) ;
int id = event - > spawnId > > GENTITYNUM_BITS ;
length + = idStr : : snPrintf ( buf + length , sizeof ( buf ) - 1 - length , " event %d for entity %d %d: " , event - > event , entityNum , id ) ;
va_start ( argptr , fmt ) ;
length = idStr : : vsnPrintf ( buf + length , sizeof ( buf ) - 1 - length , fmt , argptr ) ;
va_end ( argptr ) ;
idStr : : Append ( buf , sizeof ( buf ) , " \n " ) ;
common - > DWarning ( buf ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerProcessEntityNetworkEventQueue
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerProcessEntityNetworkEventQueue ( void ) {
idEntity * ent ;
entityNetEvent_t * event ;
idBitMsg eventMsg ;
while ( eventQueue . Start ( ) ) {
event = eventQueue . Start ( ) ;
if ( event - > time > time ) {
break ;
}
idEntityPtr < idEntity > entPtr ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( ! entPtr . SetSpawnId ( event - > spawnId ) ) {
NetworkEventWarning ( event , " Entity does not exist any longer, or has not been spawned yet. " ) ;
} else {
ent = entPtr . GetEntity ( ) ;
assert ( ent ) ;
eventMsg . Init ( event - > paramsBuf , sizeof ( event - > paramsBuf ) ) ;
eventMsg . SetSize ( event - > paramsSize ) ;
eventMsg . BeginReading ( ) ;
if ( ! ent - > ServerReceiveEvent ( event - > event , event - > time , eventMsg ) ) {
NetworkEventWarning ( event , " unknown event " ) ;
}
}
entityNetEvent_t * freedEvent = eventQueue . Dequeue ( ) ;
assert ( freedEvent = = event ) ;
eventQueue . Free ( event ) ;
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerSendChatMessage
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerSendChatMessage ( int to , const char * name , const char * text ) {
idBitMsg outMsg ;
byte msgBuf [ MAX_GAME_MESSAGE_SIZE ] ;
outMsg . Init ( msgBuf , sizeof ( msgBuf ) ) ;
outMsg . BeginWriting ( ) ;
outMsg . WriteByte ( GAME_RELIABLE_MESSAGE_CHAT ) ;
outMsg . WriteString ( name ) ;
outMsg . WriteString ( text , - 1 , false ) ;
networkSystem - > ServerSendReliableMessage ( to , outMsg ) ;
if ( to = = - 1 | | to = = localClientNum ) {
mpGame . AddChatLine ( " %s^0: %s \n " , name , text ) ;
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ServerProcessReliableMessage
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ServerProcessReliableMessage ( int clientNum , const idBitMsg & msg ) {
int id ;
id = msg . ReadByte ( ) ;
switch ( id ) {
case GAME_RELIABLE_MESSAGE_CHAT :
case GAME_RELIABLE_MESSAGE_TCHAT : {
char name [ 128 ] ;
char text [ 128 ] ;
msg . ReadString ( name , sizeof ( name ) ) ;
msg . ReadString ( text , sizeof ( text ) ) ;
mpGame . ProcessChatMessage ( clientNum , id = = GAME_RELIABLE_MESSAGE_TCHAT , name , text , NULL ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_VCHAT : {
int index = msg . ReadLong ( ) ;
bool team = msg . ReadBits ( 1 ) ! = 0 ;
mpGame . ProcessVoiceChat ( clientNum , team , index ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_KILL : {
mpGame . WantKilled ( clientNum ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_DROPWEAPON : {
mpGame . DropWeapon ( clientNum ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_CALLVOTE : {
mpGame . ServerCallVote ( clientNum , msg ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_CASTVOTE : {
bool vote = ( msg . ReadByte ( ) ! = 0 ) ;
mpGame . CastVote ( clientNum , vote ) ;
break ;
}
#if 0
// uncomment this if you want to track when players are in a menu
case GAME_RELIABLE_MESSAGE_MENU : {
bool menuUp = ( msg . ReadBits ( 1 ) ! = 0 ) ;
break ;
}
# endif
case GAME_RELIABLE_MESSAGE_EVENT : {
entityNetEvent_t * event ;
// allocate new event
event = eventQueue . Alloc ( ) ;
eventQueue . Enqueue ( event , idEventQueue : : OUTOFORDER_DROP ) ;
event - > spawnId = msg . ReadBits ( 32 ) ;
event - > event = msg . ReadByte ( ) ;
event - > time = msg . ReadLong ( ) ;
event - > paramsSize = msg . ReadBits ( idMath : : BitsForInteger ( MAX_EVENT_PARAM_SIZE ) ) ;
if ( event - > paramsSize ) {
if ( event - > paramsSize > MAX_EVENT_PARAM_SIZE ) {
NetworkEventWarning ( event , " invalid param size " ) ;
return ;
}
msg . ReadByteAlign ( ) ;
msg . ReadData ( event - > paramsBuf , event - > paramsSize ) ;
}
break ;
}
default : {
Warning ( " Unknown client->server reliable message: %d " , id ) ;
break ;
}
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ClientShowSnapshot
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ClientShowSnapshot ( int clientNum ) const {
int baseBits ;
idEntity * ent ;
idPlayer * player ;
idMat3 viewAxis ;
idBounds viewBounds ;
entityState_t * base ;
if ( ! net_clientShowSnapshot . GetInteger ( ) ) {
return ;
}
player = static_cast < idPlayer * > ( entities [ clientNum ] ) ;
if ( ! player ) {
return ;
}
viewAxis = player - > viewAngles . ToMat3 ( ) ;
viewBounds = player - > GetPhysics ( ) - > GetAbsBounds ( ) . Expand ( net_clientShowSnapshotRadius . GetFloat ( ) ) ;
for ( ent = snapshotEntities . Next ( ) ; ent ! = NULL ; ent = ent - > snapshotNode . Next ( ) ) {
if ( net_clientShowSnapshot . GetInteger ( ) = = 1 & & ent - > snapshotBits = = 0 ) {
continue ;
}
const idBounds & entBounds = ent - > GetPhysics ( ) - > GetAbsBounds ( ) ;
if ( ! entBounds . IntersectsBounds ( viewBounds ) ) {
continue ;
}
base = clientEntityStates [ clientNum ] [ ent - > entityNumber ] ;
if ( base ) {
baseBits = base - > state . GetNumBitsWritten ( ) ;
} else {
baseBits = 0 ;
}
if ( net_clientShowSnapshot . GetInteger ( ) = = 2 & & baseBits = = 0 ) {
continue ;
}
gameRenderWorld - > DebugBounds ( colorGreen , entBounds ) ;
gameRenderWorld - > DrawText ( va ( " %d: %s (%d,%d bytes of %d,%d) \n " , ent - > entityNumber ,
ent - > name . c_str ( ) , ent - > snapshotBits > > 3 , ent - > snapshotBits & 7 , baseBits > > 3 , baseBits & 7 ) ,
entBounds . GetCenter ( ) , 0.1f , colorWhite , viewAxis , 1 ) ;
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : UpdateLagometer
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : UpdateLagometer ( int aheadOfServer , int dupeUsercmds ) {
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 / 16.0f ) ;
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 , dupeUsercmds ) ; i < LAGO_HEIGHT ; i + + ) {
lagometer [ i ] [ j ] [ 0 ] = 255 ;
if ( dupeUsercmds < = 2 ) {
lagometer [ i ] [ j ] [ 1 ] = 255 ;
}
lagometer [ i ] [ j ] [ 3 ] = 255 ;
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ClientReadSnapshot
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ClientReadSnapshot ( int clientNum , int sequence , const int gameFrame , const int gameTime , const int dupeUsercmds , const int aheadOfServer , const idBitMsg & msg ) {
int i , typeNum , entityDefNumber , numBitsRead ;
idTypeInfo * typeInfo ;
idEntity * ent ;
idPlayer * player , * spectated ;
pvsHandle_t pvsHandle ;
idDict args ;
const char * classname ;
idBitMsgDelta deltaMsg ;
snapshot_t * snapshot ;
entityState_t * base , * newBase ;
int spawnId ;
int numSourceAreas , sourceAreas [ idEntity : : MAX_PVS_AREAS ] ;
idWeapon * weap ;
if ( net_clientLagOMeter . GetBool ( ) & & renderSystem ) {
UpdateLagometer ( aheadOfServer , dupeUsercmds ) ;
if ( ! renderSystem - > UploadImage ( LAGO_IMAGE , ( byte * ) lagometer , LAGO_IMG_WIDTH , LAGO_IMG_HEIGHT ) ) {
common - > Printf ( " lagometer: UploadImage failed. turning off net_clientLagOMeter \n " ) ;
net_clientLagOMeter . SetBool ( false ) ;
}
}
InitLocalClient ( clientNum ) ;
// clear any debug lines from a previous frame
gameRenderWorld - > DebugClearLines ( time ) ;
// clear any debug polygons from a previous frame
gameRenderWorld - > DebugClearPolygons ( time ) ;
// update the game time
framenum = gameFrame ;
time = gameTime ;
previousTime = time - msec ;
// so that StartSound/StopSound doesn't risk skipping
isNewFrame = true ;
// clear the snapshot entity list
snapshotEntities . Clear ( ) ;
// allocate new snapshot
snapshot = snapshotAllocator . Alloc ( ) ;
snapshot - > sequence = sequence ;
snapshot - > firstEntityState = NULL ;
snapshot - > next = clientSnapshots [ clientNum ] ;
clientSnapshots [ clientNum ] = snapshot ;
# if ASYNC_WRITE_TAGS
idRandom tagRandom ;
tagRandom . SetSeed ( msg . ReadLong ( ) ) ;
# endif
// read all entities from the snapshot
for ( i = msg . ReadBits ( GENTITYNUM_BITS ) ; i ! = ENTITYNUM_NONE ; i = msg . ReadBits ( GENTITYNUM_BITS ) ) {
base = clientEntityStates [ clientNum ] [ i ] ;
if ( base ) {
base - > state . BeginReading ( ) ;
}
newBase = entityStateAllocator . Alloc ( ) ;
newBase - > entityNumber = i ;
newBase - > next = snapshot - > firstEntityState ;
snapshot - > firstEntityState = newBase ;
newBase - > state . Init ( newBase - > stateBuf , sizeof ( newBase - > stateBuf ) ) ;
newBase - > state . BeginWriting ( ) ;
numBitsRead = msg . GetNumBitsRead ( ) ;
deltaMsg . Init ( base ? & base - > state : NULL , & newBase - > state , & msg ) ;
spawnId = deltaMsg . ReadBits ( 32 - GENTITYNUM_BITS ) ;
typeNum = deltaMsg . ReadBits ( idClass : : GetTypeNumBits ( ) ) ;
entityDefNumber = ClientRemapDecl ( DECL_ENTITYDEF , deltaMsg . ReadBits ( entityDefBits ) ) ;
typeInfo = idClass : : GetType ( typeNum ) ;
if ( ! typeInfo ) {
Error ( " Unknown type number %d for entity %d with class number %d " , typeNum , i , entityDefNumber ) ;
}
ent = entities [ i ] ;
// if there is no entity or an entity of the wrong type
if ( ! ent | | ent - > GetType ( ) - > typeNum ! = typeNum | | ent - > entityDefNumber ! = entityDefNumber | | spawnId ! = spawnIds [ i ] ) {
if ( i < MAX_CLIENTS & & ent ) {
// SPAWN_PLAYER should be taking care of spawning the entity with the right spawnId
common - > Warning ( " ClientReadSnapshot: recycling client entity %d \n " , i ) ;
}
delete ent ;
spawnCount = spawnId ;
args . Clear ( ) ;
args . SetInt ( " spawn_entnum " , i ) ;
args . Set ( " name " , va ( " entity%d " , i ) ) ;
if ( entityDefNumber > = 0 ) {
if ( entityDefNumber > = declManager - > GetNumDecls ( DECL_ENTITYDEF ) ) {
Error ( " server has %d entityDefs instead of %d " , entityDefNumber , declManager - > GetNumDecls ( DECL_ENTITYDEF ) ) ;
}
classname = declManager - > DeclByIndex ( DECL_ENTITYDEF , entityDefNumber , false ) - > GetName ( ) ;
args . Set ( " classname " , classname ) ;
if ( ! SpawnEntityDef ( args , & ent ) | | ! entities [ i ] | | entities [ i ] - > GetType ( ) - > typeNum ! = typeNum ) {
Error ( " Failed to spawn entity with classname '%s' of type '%s' " , classname , typeInfo - > classname ) ;
}
} else {
ent = SpawnEntityType ( * typeInfo , & args , true ) ;
if ( ! entities [ i ] | | entities [ i ] - > GetType ( ) - > typeNum ! = typeNum ) {
Error ( " Failed to spawn entity of type '%s' " , typeInfo - > classname ) ;
}
}
if ( i < MAX_CLIENTS & & i > = numClients ) {
numClients = i + 1 ;
}
}
// add the entity to the snapshot list
ent - > snapshotNode . AddToEnd ( snapshotEntities ) ;
ent - > snapshotSequence = sequence ;
// read the class specific data from the snapshot
ent - > ReadFromSnapshot ( deltaMsg ) ;
ent - > snapshotBits = msg . GetNumBitsRead ( ) - numBitsRead ;
# if ASYNC_WRITE_TAGS
if ( msg . ReadLong ( ) ! = tagRandom . RandomInt ( ) ) {
cmdSystem - > BufferCommandText ( CMD_EXEC_NOW , " writeGameState " ) ;
if ( entityDefNumber > = 0 & & entityDefNumber < declManager - > GetNumDecls ( DECL_ENTITYDEF ) ) {
classname = declManager - > DeclByIndex ( DECL_ENTITYDEF , entityDefNumber , false ) - > GetName ( ) ;
Error ( " write to and read from snapshot out of sync for classname '%s' of type '%s' " , classname , typeInfo - > classname ) ;
} else {
Error ( " write to and read from snapshot out of sync for type '%s' " , typeInfo - > classname ) ;
}
}
# endif
}
player = static_cast < idPlayer * > ( entities [ clientNum ] ) ;
if ( ! player ) {
return ;
}
// if prediction is off, enable local client smoothing
player - > SetSelfSmooth ( dupeUsercmds > 2 ) ;
if ( player - > spectating & & player - > spectator ! = clientNum & & entities [ player - > spectator ] ) {
spectated = static_cast < idPlayer * > ( entities [ player - > spectator ] ) ;
} else {
spectated = player ;
}
// get PVS for this player
// don't use PVSAreas for networking - PVSAreas depends on animations (and md5 bounds), which are not synchronized
numSourceAreas = gameRenderWorld - > BoundsInAreas ( spectated - > GetPlayerPhysics ( ) - > GetAbsBounds ( ) , sourceAreas , idEntity : : MAX_PVS_AREAS ) ;
pvsHandle = gameLocal . pvs . SetupCurrentPVS ( sourceAreas , numSourceAreas , PVS_NORMAL ) ;
# ifdef _D3XP
// Add portalSky areas to PVS
if ( portalSkyEnt . GetEntity ( ) ) {
pvsHandle_t otherPVS , newPVS ;
idEntity * skyEnt = portalSkyEnt . GetEntity ( ) ;
otherPVS = gameLocal . pvs . SetupCurrentPVS ( skyEnt - > GetPVSAreas ( ) , skyEnt - > GetNumPVSAreas ( ) ) ;
newPVS = gameLocal . pvs . MergeCurrentPVS ( pvsHandle , otherPVS ) ;
pvs . FreeCurrentPVS ( pvsHandle ) ;
pvs . FreeCurrentPVS ( otherPVS ) ;
pvsHandle = newPVS ;
}
# endif
// read the PVS from the snapshot
# if ASYNC_WRITE_PVS
int serverPVS [ idEntity : : MAX_PVS_AREAS ] ;
i = numSourceAreas ;
while ( i < idEntity : : MAX_PVS_AREAS ) {
sourceAreas [ i + + ] = 0 ;
}
for ( i = 0 ; i < idEntity : : MAX_PVS_AREAS ; i + + ) {
serverPVS [ i ] = msg . ReadLong ( ) ;
}
if ( memcmp ( sourceAreas , serverPVS , idEntity : : MAX_PVS_AREAS * sizeof ( int ) ) ) {
common - > Warning ( " client PVS areas != server PVS areas, sequence 0x%x " , sequence ) ;
for ( i = 0 ; i < idEntity : : MAX_PVS_AREAS ; i + + ) {
common - > DPrintf ( " %3d " , sourceAreas [ i ] ) ;
}
common - > DPrintf ( " \n " ) ;
for ( i = 0 ; i < idEntity : : MAX_PVS_AREAS ; i + + ) {
common - > DPrintf ( " %3d " , serverPVS [ i ] ) ;
}
common - > DPrintf ( " \n " ) ;
}
gameLocal . pvs . ReadPVS ( pvsHandle , msg ) ;
# endif
for ( i = 0 ; i < ENTITY_PVS_SIZE ; i + + ) {
snapshot - > pvs [ i ] = msg . ReadDeltaLong ( clientPVS [ clientNum ] [ i ] ) ;
}
// add entities in the PVS that haven't changed since the last applied snapshot
for ( ent = spawnedEntities . Next ( ) ; ent ! = NULL ; ent = ent - > spawnNode . Next ( ) ) {
// if the entity is already in the snapshot
if ( ent - > snapshotSequence = = sequence ) {
continue ;
}
// if the entity is not in the snapshot PVS
if ( ! ( snapshot - > pvs [ ent - > entityNumber > > 5 ] & ( 1 < < ( ent - > entityNumber & 31 ) ) ) ) {
if ( ent - > PhysicsTeamInPVS ( pvsHandle ) ) {
if ( ent - > entityNumber > = MAX_CLIENTS & & ent - > entityNumber < mapSpawnCount & & ! ent - > spawnArgs . GetBool ( " net_dynamic " , " 0 " ) ) { //_D3XP
// server says it's not in PVS, client says it's in PVS
// if that happens on map entities, most likely something is wrong
// I can see that moving pieces along several PVS could be a legit situation though
// this is a band aid, which means something is not done right elsewhere
common - > DWarning ( " client thinks map entity 0x%x (%s) is stale, sequence 0x%x " , ent - > entityNumber , ent - > name . c_str ( ) , sequence ) ;
} else {
ent - > FreeModelDef ( ) ;
# ifdef CTF
// possible fix for left over lights on CTF flag
ent - > FreeLightDef ( ) ;
# endif
ent - > UpdateVisuals ( ) ;
ent - > GetPhysics ( ) - > UnlinkClip ( ) ;
}
}
continue ;
}
// add the entity to the snapshot list
ent - > snapshotNode . AddToEnd ( snapshotEntities ) ;
ent - > snapshotSequence = sequence ;
ent - > snapshotBits = 0 ;
base = clientEntityStates [ clientNum ] [ ent - > entityNumber ] ;
if ( ! base ) {
// entity has probably fl.networkSync set to false
continue ;
}
base - > state . BeginReading ( ) ;
deltaMsg . Init ( & base - > state , NULL , ( const idBitMsg * ) NULL ) ;
spawnId = deltaMsg . ReadBits ( 32 - GENTITYNUM_BITS ) ;
typeNum = deltaMsg . ReadBits ( idClass : : GetTypeNumBits ( ) ) ;
entityDefNumber = deltaMsg . ReadBits ( entityDefBits ) ;
typeInfo = idClass : : GetType ( typeNum ) ;
// if the entity is not the right type
if ( ! typeInfo | | ent - > GetType ( ) - > typeNum ! = typeNum | | ent - > entityDefNumber ! = entityDefNumber ) {
// should never happen - it does though. with != entityDefNumber only?
common - > DWarning ( " entity '%s' is not the right type %p 0x%d 0x%x 0x%x 0x%x " , ent - > GetName ( ) , typeInfo , ent - > GetType ( ) - > typeNum , typeNum , ent - > entityDefNumber , entityDefNumber ) ;
continue ;
}
// read the class specific data from the base state
ent - > ReadFromSnapshot ( deltaMsg ) ;
}
// free the PVS
pvs . FreeCurrentPVS ( pvsHandle ) ;
// read the game and player state from the snapshot
base = clientEntityStates [ clientNum ] [ ENTITYNUM_NONE ] ; // ENTITYNUM_NONE is used for the game and player state
if ( base ) {
base - > state . BeginReading ( ) ;
}
newBase = entityStateAllocator . Alloc ( ) ;
newBase - > entityNumber = ENTITYNUM_NONE ;
newBase - > next = snapshot - > firstEntityState ;
snapshot - > firstEntityState = newBase ;
newBase - > state . Init ( newBase - > stateBuf , sizeof ( newBase - > stateBuf ) ) ;
newBase - > state . BeginWriting ( ) ;
deltaMsg . Init ( base ? & base - > state : NULL , & newBase - > state , & msg ) ;
if ( player - > spectating & & player - > spectator ! = player - > entityNumber & & gameLocal . entities [ player - > spectator ] & & gameLocal . entities [ player - > spectator ] - > IsType ( idPlayer : : Type ) ) {
static_cast < idPlayer * > ( gameLocal . entities [ player - > spectator ] ) - > ReadPlayerStateFromSnapshot ( deltaMsg ) ;
weap = static_cast < idPlayer * > ( gameLocal . entities [ player - > spectator ] ) - > weapon . GetEntity ( ) ;
if ( weap & & ( weap - > GetRenderEntity ( ) - > bounds [ 0 ] = = weap - > GetRenderEntity ( ) - > bounds [ 1 ] ) ) {
// update the weapon's viewmodel bounds so that the model doesn't flicker in the spectator's view
weap - > GetAnimator ( ) - > GetBounds ( gameLocal . time , weap - > GetRenderEntity ( ) - > bounds ) ;
weap - > UpdateVisuals ( ) ;
}
} else {
player - > ReadPlayerStateFromSnapshot ( deltaMsg ) ;
}
ReadGameStateFromSnapshot ( deltaMsg ) ;
// visualize the snapshot
ClientShowSnapshot ( clientNum ) ;
// process entity events
ClientProcessEntityNetworkEventQueue ( ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ClientApplySnapshot
= = = = = = = = = = = = = = = =
*/
bool idGameLocal : : ClientApplySnapshot ( int clientNum , int sequence ) {
return ApplySnapshot ( clientNum , sequence ) ;
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ClientProcessEntityNetworkEventQueue
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ClientProcessEntityNetworkEventQueue ( void ) {
idEntity * ent ;
entityNetEvent_t * event ;
idBitMsg eventMsg ;
while ( eventQueue . Start ( ) ) {
event = eventQueue . Start ( ) ;
// only process forward, in order
if ( event - > time > time ) {
break ;
}
idEntityPtr < idEntity > entPtr ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( ! entPtr . SetSpawnId ( event - > spawnId ) ) {
if ( ! gameLocal . entities [ event - > spawnId & ( ( 1 < < GENTITYNUM_BITS ) - 1 ) ] ) {
// if new entity exists in this position, silently ignore
NetworkEventWarning ( event , " Entity does not exist any longer, or has not been spawned yet. " ) ;
}
} else {
ent = entPtr . GetEntity ( ) ;
assert ( ent ) ;
eventMsg . Init ( event - > paramsBuf , sizeof ( event - > paramsBuf ) ) ;
eventMsg . SetSize ( event - > paramsSize ) ;
eventMsg . BeginReading ( ) ;
if ( ! ent - > ClientReceiveEvent ( event - > event , event - > time , eventMsg ) ) {
NetworkEventWarning ( event , " unknown event " ) ;
}
}
entityNetEvent_t * freedEvent = eventQueue . Dequeue ( ) ;
assert ( freedEvent = = event ) ;
eventQueue . Free ( event ) ;
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ClientProcessReliableMessage
= = = = = = = = = = = = = = = =
*/
void idGameLocal : : ClientProcessReliableMessage ( int clientNum , const idBitMsg & msg ) {
int id , line ;
idPlayer * p ;
idDict backupSI ;
InitLocalClient ( clientNum ) ;
id = msg . ReadByte ( ) ;
switch ( id ) {
case GAME_RELIABLE_MESSAGE_INIT_DECL_REMAP : {
InitClientDeclRemap ( clientNum ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_REMAP_DECL : {
int type , index ;
char name [ MAX_STRING_CHARS ] ;
type = msg . ReadByte ( ) ;
index = msg . ReadLong ( ) ;
msg . ReadString ( name , sizeof ( name ) ) ;
const idDecl * decl = declManager - > FindType ( ( declType_t ) type , name , false ) ;
if ( decl ! = NULL ) {
if ( index > = clientDeclRemap [ clientNum ] [ type ] . Num ( ) ) {
clientDeclRemap [ clientNum ] [ type ] . AssureSize ( index + 1 , - 1 ) ;
}
clientDeclRemap [ clientNum ] [ type ] [ index ] = decl - > Index ( ) ;
}
break ;
}
case GAME_RELIABLE_MESSAGE_SPAWN_PLAYER : {
int client = msg . ReadByte ( ) ;
int spawnId = msg . ReadLong ( ) ;
if ( ! entities [ client ] ) {
SpawnPlayer ( client ) ;
entities [ client ] - > FreeModelDef ( ) ;
}
// fix up the spawnId to match what the server says
// otherwise there is going to be a bogus delete/new of the client entity in the first ClientReadFromSnapshot
spawnIds [ client ] = spawnId ;
break ;
}
case GAME_RELIABLE_MESSAGE_DELETE_ENT : {
int spawnId = msg . ReadBits ( 32 ) ;
idEntityPtr < idEntity > entPtr ;
if ( ! entPtr . SetSpawnId ( spawnId ) ) {
break ;
}
delete entPtr . GetEntity ( ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_CHAT :
case GAME_RELIABLE_MESSAGE_TCHAT : { // (client should never get a TCHAT though)
char name [ 128 ] ;
char text [ 128 ] ;
msg . ReadString ( name , sizeof ( name ) ) ;
msg . ReadString ( text , sizeof ( text ) ) ;
mpGame . AddChatLine ( " %s^0: %s \n " , name , text ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_SOUND_EVENT : {
snd_evt_t snd_evt = ( snd_evt_t ) msg . ReadByte ( ) ;
mpGame . PlayGlobalSound ( - 1 , snd_evt ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_SOUND_INDEX : {
int index = gameLocal . ClientRemapDecl ( DECL_SOUND , msg . ReadLong ( ) ) ;
if ( index > = 0 & & index < declManager - > GetNumDecls ( DECL_SOUND ) ) {
const idSoundShader * shader = declManager - > SoundByIndex ( index ) ;
mpGame . PlayGlobalSound ( - 1 , SND_COUNT , shader - > GetName ( ) ) ;
}
break ;
}
case GAME_RELIABLE_MESSAGE_DB : {
idMultiplayerGame : : msg_evt_t msg_evt = ( idMultiplayerGame : : msg_evt_t ) msg . ReadByte ( ) ;
int parm1 , parm2 ;
parm1 = msg . ReadByte ( ) ;
parm2 = msg . ReadByte ( ) ;
mpGame . PrintMessageEvent ( - 1 , msg_evt , parm1 , parm2 ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_EVENT : {
entityNetEvent_t * event ;
// allocate new event
event = eventQueue . Alloc ( ) ;
eventQueue . Enqueue ( event , idEventQueue : : OUTOFORDER_IGNORE ) ;
event - > spawnId = msg . ReadBits ( 32 ) ;
event - > event = msg . ReadByte ( ) ;
event - > time = msg . ReadLong ( ) ;
event - > paramsSize = msg . ReadBits ( idMath : : BitsForInteger ( MAX_EVENT_PARAM_SIZE ) ) ;
if ( event - > paramsSize ) {
if ( event - > paramsSize > MAX_EVENT_PARAM_SIZE ) {
NetworkEventWarning ( event , " invalid param size " ) ;
return ;
}
msg . ReadByteAlign ( ) ;
msg . ReadData ( event - > paramsBuf , event - > paramsSize ) ;
}
break ;
}
case GAME_RELIABLE_MESSAGE_SERVERINFO : {
idDict info ;
msg . ReadDeltaDict ( info , NULL ) ;
gameLocal . SetServerInfo ( info ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_RESTART : {
# ifdef _D3XP
int newServerInfo = msg . ReadBits ( 1 ) ;
if ( newServerInfo ) {
idDict info ;
msg . ReadDeltaDict ( info , NULL ) ;
gameLocal . SetServerInfo ( info ) ;
}
# endif
MapRestart ( ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_TOURNEYLINE : {
line = msg . ReadByte ( ) ;
p = static_cast < idPlayer * > ( entities [ clientNum ] ) ;
if ( ! p ) {
break ;
}
p - > tourneyLine = line ;
break ;
}
case GAME_RELIABLE_MESSAGE_STARTVOTE : {
char voteString [ MAX_STRING_CHARS ] ;
int clientNum = msg . ReadByte ( ) ;
msg . ReadString ( voteString , sizeof ( voteString ) ) ;
mpGame . ClientStartVote ( clientNum , voteString ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_UPDATEVOTE : {
int result = msg . ReadByte ( ) ;
int yesCount = msg . ReadByte ( ) ;
int noCount = msg . ReadByte ( ) ;
mpGame . ClientUpdateVote ( ( idMultiplayerGame : : vote_result_t ) result , yesCount , noCount ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_PORTALSTATES : {
int numPortals = msg . ReadLong ( ) ;
assert ( numPortals = = gameRenderWorld - > NumPortals ( ) ) ;
for ( int i = 0 ; i < numPortals ; i + + ) {
gameRenderWorld - > SetPortalState ( ( qhandle_t ) ( i + 1 ) , msg . ReadBits ( NUM_RENDER_PORTAL_BITS ) ) ;
}
break ;
}
case GAME_RELIABLE_MESSAGE_PORTAL : {
qhandle_t portal = msg . ReadLong ( ) ;
int blockingBits = msg . ReadBits ( NUM_RENDER_PORTAL_BITS ) ;
assert ( portal > 0 & & portal < = gameRenderWorld - > NumPortals ( ) ) ;
gameRenderWorld - > SetPortalState ( portal , blockingBits ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_STARTSTATE : {
mpGame . ClientReadStartState ( msg ) ;
break ;
}
case GAME_RELIABLE_MESSAGE_WARMUPTIME : {
mpGame . ClientReadWarmupTime ( msg ) ;
break ;
}
default : {
Error ( " Unknown server->client reliable message: %d " , id ) ;
break ;
}
}
}
/*
= = = = = = = = = = = = = = = =
idGameLocal : : ClientPrediction
= = = = = = = = = = = = = = = =
*/
gameReturn_t idGameLocal : : ClientPrediction ( int clientNum , const usercmd_t * clientCmds , bool lastPredictFrame ) {
idEntity * ent ;
idPlayer * player ;
gameReturn_t ret ;
ret . sessionCommand [ 0 ] = ' \0 ' ;
player = static_cast < idPlayer * > ( entities [ clientNum ] ) ;
if ( ! player ) {
return ret ;
}
// check for local client lag
if ( networkSystem - > ClientGetTimeSinceLastPacket ( ) > = net_clientMaxPrediction . GetInteger ( ) ) {
player - > isLagged = true ;
} else {
player - > isLagged = false ;
}
InitLocalClient ( clientNum ) ;
// update the game time
framenum + + ;
previousTime = time ;
time + = msec ;
// update the real client time and the new frame flag
if ( time > realClientTime ) {
realClientTime = time ;
isNewFrame = true ;
} else {
isNewFrame = false ;
}
# ifdef _D3XP
slow . Set ( time , previousTime , msec , framenum , realClientTime ) ;
fast . Set ( time , previousTime , msec , framenum , realClientTime ) ;
# endif
// set the user commands for this frame
memcpy ( usercmds , clientCmds , numClients * sizeof ( usercmds [ 0 ] ) ) ;
// run prediction on all entities from the last snapshot
for ( ent = snapshotEntities . Next ( ) ; ent ! = NULL ; ent = ent - > snapshotNode . Next ( ) ) {
ent - > thinkFlags | = TH_PHYSICS ;
ent - > ClientPredictionThink ( ) ;
}
// service any pending events
idEvent : : ServiceEvents ( ) ;
// show any debug info for this frame
if ( isNewFrame ) {
RunDebugInfo ( ) ;
D_DrawDebugLines ( ) ;
}
if ( sessionCommand . Length ( ) ) {
strncpy ( ret . sessionCommand , sessionCommand , sizeof ( ret . sessionCommand ) ) ;
}
return ret ;
}
/*
= = = = = = = = = = = = = = =
idGameLocal : : Tokenize
= = = = = = = = = = = = = = =
*/
void idGameLocal : : Tokenize ( idStrList & out , const char * in ) {
char buf [ MAX_STRING_CHARS ] ;
char * token , * next ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
idStr : : Copynz ( buf , in , MAX_STRING_CHARS ) ;
token = buf ;
next = strchr ( token , ' ; ' ) ;
while ( token ) {
if ( next ) {
* next = ' \0 ' ;
}
idStr : : ToLower ( token ) ;
out . Append ( token ) ;
if ( next ) {
token = next + 1 ;
next = strchr ( token , ' ; ' ) ;
} else {
token = NULL ;
2011-12-06 18:20:15 +00:00
}
2011-11-22 21:28:15 +00:00
}
}
/*
= = = = = = = = = = = = = = =
idGameLocal : : DownloadRequest
= = = = = = = = = = = = = = =
*/
bool idGameLocal : : DownloadRequest ( const char * IP , const char * guid , const char * paks , char urls [ MAX_STRING_CHARS ] ) {
if ( ! cvarSystem - > GetCVarInteger ( " net_serverDownload " ) ) {
return false ;
}
if ( cvarSystem - > GetCVarInteger ( " net_serverDownload " ) = = 1 ) {
// 1: single URL redirect
if ( ! strlen ( cvarSystem - > GetCVarString ( " si_serverURL " ) ) ) {
common - > Warning ( " si_serverURL not set " ) ;
return false ;
}
idStr : : snPrintf ( urls , MAX_STRING_CHARS , " 1;%s " , cvarSystem - > GetCVarString ( " si_serverURL " ) ) ;
return true ;
} else {
// 2: table of pak URLs
// first token is the game pak if request, empty if not requested by the client
// there may be empty tokens for paks the server couldn't pinpoint - the order matters
idStr reply = " 2; " ;
idStrList dlTable , pakList ;
int i , j ;
Tokenize ( dlTable , cvarSystem - > GetCVarString ( " net_serverDlTable " ) ) ;
Tokenize ( pakList , paks ) ;
for ( i = 0 ; i < pakList . Num ( ) ; i + + ) {
if ( i > 0 ) {
reply + = " ; " ;
}
if ( pakList [ i ] [ 0 ] = = ' \0 ' ) {
if ( i = = 0 ) {
// pak 0 will always miss when client doesn't ask for game bin
common - > DPrintf ( " no game pak request \n " ) ;
} else {
common - > DPrintf ( " no pak %d \n " , i ) ;
}
continue ;
}
for ( j = 0 ; j < dlTable . Num ( ) ; j + + ) {
if ( ! fileSystem - > FilenameCompare ( pakList [ i ] , dlTable [ j ] ) ) {
break ;
}
}
if ( j = = dlTable . Num ( ) ) {
common - > Printf ( " download for %s: pak not matched: %s \n " , IP , pakList [ i ] . c_str ( ) ) ;
} else {
idStr url = cvarSystem - > GetCVarString ( " net_serverDlBaseURL " ) ;
url . AppendPath ( dlTable [ j ] ) ;
reply + = url ;
common - > DPrintf ( " download for %s: %s \n " , IP , url . c_str ( ) ) ;
}
}
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
idStr : : Copynz ( urls , reply , MAX_STRING_CHARS ) ;
return true ;
}
return false ;
}
/*
= = = = = = = = = = = = = = =
idEventQueue : : Alloc
= = = = = = = = = = = = = = =
*/
entityNetEvent_t * idEventQueue : : Alloc ( ) {
entityNetEvent_t * event = eventAllocator . Alloc ( ) ;
event - > prev = NULL ;
event - > next = NULL ;
return event ;
}
/*
= = = = = = = = = = = = = = =
idEventQueue : : Free
= = = = = = = = = = = = = = =
*/
void idEventQueue : : Free ( entityNetEvent_t * event ) {
// should only be called on an unlinked event!
assert ( ! event - > next & & ! event - > prev ) ;
eventAllocator . Free ( event ) ;
}
/*
= = = = = = = = = = = = = = =
idEventQueue : : Shutdown
= = = = = = = = = = = = = = =
*/
void idEventQueue : : Shutdown ( ) {
eventAllocator . Shutdown ( ) ;
this - > Init ( ) ;
}
/*
= = = = = = = = = = = = = = =
idEventQueue : : Init
= = = = = = = = = = = = = = =
*/
void idEventQueue : : Init ( void ) {
start = NULL ;
end = NULL ;
}
/*
= = = = = = = = = = = = = = =
idEventQueue : : Dequeue
= = = = = = = = = = = = = = =
*/
entityNetEvent_t * idEventQueue : : Dequeue ( void ) {
entityNetEvent_t * event = start ;
if ( ! event ) {
return NULL ;
}
start = start - > next ;
if ( ! start ) {
end = NULL ;
} else {
start - > prev = NULL ;
}
event - > next = NULL ;
event - > prev = NULL ;
return event ;
}
/*
= = = = = = = = = = = = = = =
idEventQueue : : RemoveLast
= = = = = = = = = = = = = = =
*/
entityNetEvent_t * idEventQueue : : RemoveLast ( void ) {
entityNetEvent_t * event = end ;
if ( ! event ) {
return NULL ;
}
end = event - > prev ;
if ( ! end ) {
start = NULL ;
} else {
2011-12-06 18:20:15 +00:00
end - > next = NULL ;
2011-11-22 21:28:15 +00:00
}
event - > next = NULL ;
event - > prev = NULL ;
return event ;
}
/*
= = = = = = = = = = = = = = =
idEventQueue : : Enqueue
= = = = = = = = = = = = = = =
*/
void idEventQueue : : Enqueue ( entityNetEvent_t * event , outOfOrderBehaviour_t behaviour ) {
if ( behaviour = = OUTOFORDER_DROP ) {
// go backwards through the queue and determine if there are
// any out-of-order events
while ( end & & end - > time > event - > time ) {
entityNetEvent_t * outOfOrder = RemoveLast ( ) ;
common - > DPrintf ( " WARNING: new event with id %d ( time %d ) caused removal of event with id %d ( time %d ), game time = %d. \n " , event - > event , event - > time , outOfOrder - > event , outOfOrder - > time , gameLocal . time ) ;
Free ( outOfOrder ) ;
}
} else if ( behaviour = = OUTOFORDER_SORT & & end ) {
// NOT TESTED -- sorting out of order packets hasn't been
// tested yet... wasn't strictly necessary for
// the patch fix.
entityNetEvent_t * cur = end ;
// iterate until we find a time < the new event's
while ( cur & & cur - > time > event - > time ) {
cur = cur - > prev ;
}
if ( ! cur ) {
// add to start
event - > next = start ;
event - > prev = NULL ;
start = event ;
} else {
// insert
event - > prev = cur ;
event - > next = cur - > next ;
cur - > next = event ;
}
return ;
2011-12-06 18:20:15 +00:00
}
2011-11-22 21:28:15 +00:00
// add the new event
event - > next = NULL ;
event - > prev = NULL ;
if ( end ) {
end - > next = event ;
event - > prev = end ;
} else {
start = event ;
}
end = event ;
}