2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 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.
24 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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
35 ===============================================================================
37 Client running game code:
38 - entity events don't work and should not be issued
39 - entities should never be spawned outside idGameLocal::ClientReadSnapshot
41 ===============================================================================
44 // adds tags to the network protocol to detect when things go bad ( internal consistency )
45 // NOTE: this changes the network protocol
46 #ifndef ASYNC_WRITE_TAGS
47 #define ASYNC_WRITE_TAGS 0
50 idCVar net_clientShowSnapshot( "net_clientShowSnapshot", "0", CVAR_GAME | CVAR_INTEGER, "", 0, 3, idCmdSystem::ArgCompletion_Integer<0,3> );
51 idCVar net_clientShowSnapshotRadius( "net_clientShowSnapshotRadius", "128", CVAR_GAME | CVAR_FLOAT, "" );
52 idCVar net_clientSmoothing( "net_clientSmoothing", "0.8", CVAR_GAME | CVAR_FLOAT, "smooth other clients angles and position.", 0.0f, 0.95f );
53 idCVar net_clientSelfSmoothing( "net_clientSelfSmoothing", "0.6", CVAR_GAME | CVAR_FLOAT, "smooth self position if network causes prediction error.", 0.0f, 0.95f );
54 idCVar net_clientMaxPrediction( "net_clientMaxPrediction", "1000", CVAR_SYSTEM | CVAR_INTEGER | CVAR_NOCHEAT, "maximum number of milliseconds a client can predict ahead of server." );
55 idCVar net_clientLagOMeter( "net_clientLagOMeter", "1", CVAR_GAME | CVAR_BOOL | CVAR_NOCHEAT | CVAR_ARCHIVE, "draw prediction graph" );
59 idGameLocal::InitAsyncNetwork
62 void idGameLocal::InitAsyncNetwork( void ) {
65 for ( i = 0; i < MAX_CLIENTS; i++ ) {
66 for ( type = 0; type < declManager->GetNumDeclTypes(); type++ ) {
67 clientDeclRemap[i][type].Clear();
71 memset( clientEntityStates, 0, sizeof( clientEntityStates ) );
72 memset( clientPVS, 0, sizeof( clientPVS ) );
73 memset( clientSnapshots, 0, sizeof( clientSnapshots ) );
76 savedEventQueue.Init();
78 entityDefBits = -( idMath::BitsForInteger( declManager->GetNumDecls( DECL_ENTITYDEF ) ) + 1 );
79 localClientNum = 0; // on a listen server SetLocalUser will set this right
82 clientSmoothing = net_clientSmoothing.GetFloat();
87 idGameLocal::ShutdownAsyncNetwork
90 void idGameLocal::ShutdownAsyncNetwork( void ) {
91 entityStateAllocator.Shutdown();
92 snapshotAllocator.Shutdown();
93 eventQueue.Shutdown();
94 savedEventQueue.Shutdown();
95 memset( clientEntityStates, 0, sizeof( clientEntityStates ) );
96 memset( clientPVS, 0, sizeof( clientPVS ) );
97 memset( clientSnapshots, 0, sizeof( clientSnapshots ) );
102 idGameLocal::InitLocalClient
105 void idGameLocal::InitLocalClient( int clientNum ) {
108 localClientNum = clientNum;
109 clientSmoothing = net_clientSmoothing.GetFloat();
114 idGameLocal::InitClientDeclRemap
117 void idGameLocal::InitClientDeclRemap( int clientNum ) {
120 for ( type = 0; type < declManager->GetNumDeclTypes(); type++ ) {
122 // only implicit materials and sound shaders decls are used
123 if ( type != DECL_MATERIAL && type != DECL_SOUND ) {
127 num = declManager->GetNumDecls( (declType_t) type );
128 clientDeclRemap[clientNum][type].Clear();
129 clientDeclRemap[clientNum][type].AssureSize( num, -1 );
131 // pre-initialize the remap with non-implicit decls, all non-implicit decls are always going
132 // to be in order and in sync between server and client because of the decl manager checksum
133 for ( i = 0; i < num; i++ ) {
134 const idDecl *decl = declManager->DeclByIndex( (declType_t) type, i, false );
135 if ( decl->IsImplicit() ) {
136 // once the first implicit decl is found all remaining decls are considered implicit as well
139 clientDeclRemap[clientNum][type][i] = i;
146 idGameLocal::ServerSendDeclRemapToClient
149 void idGameLocal::ServerSendDeclRemapToClient( int clientNum, declType_t type, int index ) {
151 byte msgBuf[MAX_GAME_MESSAGE_SIZE];
153 // if no client connected for this spot
154 if ( entities[clientNum] == NULL ) {
157 // increase size of list if required
158 if ( index >= clientDeclRemap[clientNum][type].Num() ) {
159 clientDeclRemap[clientNum][(int)type].AssureSize( index + 1, -1 );
161 // if already remapped
162 if ( clientDeclRemap[clientNum][(int)type][index] != -1 ) {
166 const idDecl *decl = declManager->DeclByIndex( type, index, false );
167 if ( decl == NULL ) {
168 gameLocal.Error( "server tried to remap bad %s decl index %d", declManager->GetDeclNameFromType( type ), index );
172 // set the index at the server
173 clientDeclRemap[clientNum][(int)type][index] = index;
175 // write update to client
176 outMsg.Init( msgBuf, sizeof( msgBuf ) );
177 outMsg.BeginWriting();
178 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_REMAP_DECL );
179 outMsg.WriteByte( type );
180 outMsg.WriteLong( index );
181 outMsg.WriteString( decl->GetName() );
182 networkSystem->ServerSendReliableMessage( clientNum, outMsg );
187 idGameLocal::ServerRemapDecl
190 int idGameLocal::ServerRemapDecl( int clientNum, declType_t type, int index ) {
192 // only implicit materials and sound shaders decls are used
193 if ( type != DECL_MATERIAL && type != DECL_SOUND ) {
197 if ( clientNum == -1 ) {
198 for ( int i = 0; i < MAX_CLIENTS; i++ ) {
199 ServerSendDeclRemapToClient( i, type, index );
202 ServerSendDeclRemapToClient( clientNum, type, index );
209 idGameLocal::ClientRemapDecl
212 int idGameLocal::ClientRemapDecl( declType_t type, int index ) {
214 // only implicit materials and sound shaders decls are used
215 if ( type != DECL_MATERIAL && type != DECL_SOUND ) {
219 // negative indexes are sometimes used for NULL decls
224 // make sure the index is valid
225 if ( clientDeclRemap[localClientNum][(int)type].Num() == 0 ) {
226 gameLocal.Error( "client received decl index %d before %s decl remap was initialized", index, declManager->GetDeclNameFromType( type ) );
229 if ( index >= clientDeclRemap[localClientNum][(int)type].Num() ) {
230 gameLocal.Error( "client received unmapped %s decl index %d from server", declManager->GetDeclNameFromType( type ), index );
233 if ( clientDeclRemap[localClientNum][(int)type][index] == -1 ) {
234 gameLocal.Error( "client received unmapped %s decl index %d from server", declManager->GetDeclNameFromType( type ), index );
237 return clientDeclRemap[localClientNum][type][index];
242 idGameLocal::ServerAllowClient
245 allowReply_t idGameLocal::ServerAllowClient( int numClients, const char *IP, const char *guid, const char *password, char reason[ MAX_STRING_CHARS ] ) {
248 if ( serverInfo.GetInt( "si_pure" ) && !mpGame.IsPureReady() ) {
249 idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07139" );
253 if ( !serverInfo.GetInt( "si_maxPlayers" ) ) {
254 idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07140" );
258 if ( numClients >= serverInfo.GetInt( "si_maxPlayers" ) ) {
259 idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07141" );
263 if ( !cvarSystem->GetCVarBool( "si_usepass" ) ) {
267 const char *pass = cvarSystem->GetCVarString( "g_password" );
268 if ( pass[ 0 ] == '\0' ) {
269 common->Warning( "si_usepass is set but g_password is empty" );
270 cmdSystem->BufferCommandText( CMD_EXEC_NOW, "say si_usepass is set but g_password is empty" );
271 // avoids silent misconfigured state
272 idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07142" );
276 if ( !idStr::Cmp( pass, password ) ) {
280 idStr::snPrintf( reason, MAX_STRING_CHARS, "#str_07143" );
281 Printf( "Rejecting client %s from IP %s: invalid password\n", guid, IP );
282 return ALLOW_BADPASS;
287 idGameLocal::ServerClientConnect
290 void idGameLocal::ServerClientConnect( int clientNum, const char *guid ) {
291 // make sure no parasite entity is left
292 if ( entities[ clientNum ] ) {
293 common->DPrintf( "ServerClientConnect: remove old player entity\n" );
294 delete entities[ clientNum ];
296 userInfo[ clientNum ].Clear();
297 mpGame.ServerClientConnect( clientNum );
298 Printf( "client %d connected.\n", clientNum );
303 idGameLocal::ServerClientBegin
306 void idGameLocal::ServerClientBegin( int clientNum ) {
308 byte msgBuf[MAX_GAME_MESSAGE_SIZE];
310 // initialize the decl remap
311 InitClientDeclRemap( clientNum );
313 // send message to initialize decl remap at the client (this is always the very first reliable game message)
314 outMsg.Init( msgBuf, sizeof( msgBuf ) );
315 outMsg.BeginWriting();
316 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_INIT_DECL_REMAP );
317 networkSystem->ServerSendReliableMessage( clientNum, outMsg );
320 SpawnPlayer( clientNum );
321 if ( clientNum == localClientNum ) {
322 mpGame.EnterGame( clientNum );
325 // send message to spawn the player at the clients
326 outMsg.Init( msgBuf, sizeof( msgBuf ) );
327 outMsg.BeginWriting();
328 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SPAWN_PLAYER );
329 outMsg.WriteByte( clientNum );
330 outMsg.WriteLong( spawnIds[ clientNum ] );
331 networkSystem->ServerSendReliableMessage( -1, outMsg );
336 idGameLocal::ServerClientDisconnect
339 void idGameLocal::ServerClientDisconnect( int clientNum ) {
342 byte msgBuf[MAX_GAME_MESSAGE_SIZE];
344 outMsg.Init( msgBuf, sizeof( msgBuf ) );
345 outMsg.BeginWriting();
346 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DELETE_ENT );
347 outMsg.WriteBits( ( spawnIds[ clientNum ] << GENTITYNUM_BITS ) | clientNum, 32 ); // see GetSpawnId
348 networkSystem->ServerSendReliableMessage( -1, outMsg );
350 // free snapshots stored for this client
351 FreeSnapshotsOlderThanSequence( clientNum, 0x7FFFFFFF );
353 // free entity states stored for this client
354 for ( i = 0; i < MAX_GENTITIES; i++ ) {
355 if ( clientEntityStates[ clientNum ][ i ] ) {
356 entityStateAllocator.Free( clientEntityStates[ clientNum ][ i ] );
357 clientEntityStates[ clientNum ][ i ] = NULL;
361 // clear the client PVS
362 memset( clientPVS[ clientNum ], 0, sizeof( clientPVS[ clientNum ] ) );
364 // delete the player entity
365 delete entities[ clientNum ];
367 mpGame.DisconnectClient( clientNum );
373 idGameLocal::ServerWriteInitialReliableMessages
375 Send reliable messages to initialize the client game up to a certain initial state.
378 void idGameLocal::ServerWriteInitialReliableMessages( int clientNum ) {
381 byte msgBuf[MAX_GAME_MESSAGE_SIZE];
382 entityNetEvent_t *event;
385 for ( i = 0; i < MAX_CLIENTS; i++ ) {
386 if ( entities[i] == NULL || i == clientNum ) {
389 outMsg.Init( msgBuf, sizeof( msgBuf ) );
390 outMsg.BeginWriting( );
391 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SPAWN_PLAYER );
392 outMsg.WriteByte( i );
393 outMsg.WriteLong( spawnIds[ i ] );
394 networkSystem->ServerSendReliableMessage( clientNum, outMsg );
397 // send all saved events
398 for ( event = savedEventQueue.Start(); event; event = event->next ) {
399 outMsg.Init( msgBuf, sizeof( msgBuf ) );
400 outMsg.BeginWriting();
401 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_EVENT );
402 outMsg.WriteBits( event->spawnId, 32 );
403 outMsg.WriteByte( event->event );
404 outMsg.WriteLong( event->time );
405 outMsg.WriteBits( event->paramsSize, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
406 if ( event->paramsSize ) {
407 outMsg.WriteData( event->paramsBuf, event->paramsSize );
410 networkSystem->ServerSendReliableMessage( clientNum, outMsg );
413 // update portals for opened doors
414 int numPortals = gameRenderWorld->NumPortals();
415 outMsg.Init( msgBuf, sizeof( msgBuf ) );
416 outMsg.BeginWriting();
417 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_PORTALSTATES );
418 outMsg.WriteLong( numPortals );
419 for ( i = 0; i < numPortals; i++ ) {
420 outMsg.WriteBits( gameRenderWorld->GetPortalState( (qhandle_t) (i+1) ) , NUM_RENDER_PORTAL_BITS );
422 networkSystem->ServerSendReliableMessage( clientNum, outMsg );
424 mpGame.ServerWriteInitialReliableMessages( clientNum );
429 idGameLocal::SaveEntityNetworkEvent
432 void idGameLocal::SaveEntityNetworkEvent( const idEntity *ent, int eventId, const idBitMsg *msg ) {
433 entityNetEvent_t *event;
435 event = savedEventQueue.Alloc();
436 event->spawnId = GetSpawnId( ent );
437 event->event = eventId;
440 event->paramsSize = msg->GetSize();
441 memcpy( event->paramsBuf, msg->GetData(), msg->GetSize() );
443 event->paramsSize = 0;
446 savedEventQueue.Enqueue( event, idEventQueue::OUTOFORDER_IGNORE );
451 idGameLocal::FreeSnapshotsOlderThanSequence
454 void idGameLocal::FreeSnapshotsOlderThanSequence( int clientNum, int sequence ) {
455 snapshot_t *snapshot, *lastSnapshot, *nextSnapshot;
456 entityState_t *state;
458 for ( lastSnapshot = NULL, snapshot = clientSnapshots[clientNum]; snapshot; snapshot = nextSnapshot ) {
459 nextSnapshot = snapshot->next;
460 if ( snapshot->sequence < sequence ) {
461 for ( state = snapshot->firstEntityState; state; state = snapshot->firstEntityState ) {
462 snapshot->firstEntityState = snapshot->firstEntityState->next;
463 entityStateAllocator.Free( state );
465 if ( lastSnapshot ) {
466 lastSnapshot->next = snapshot->next;
468 clientSnapshots[clientNum] = snapshot->next;
470 snapshotAllocator.Free( snapshot );
472 lastSnapshot = snapshot;
479 idGameLocal::ApplySnapshot
482 bool idGameLocal::ApplySnapshot( int clientNum, int sequence ) {
483 snapshot_t *snapshot, *lastSnapshot, *nextSnapshot;
484 entityState_t *state;
486 FreeSnapshotsOlderThanSequence( clientNum, sequence );
488 for ( lastSnapshot = NULL, snapshot = clientSnapshots[clientNum]; snapshot; snapshot = nextSnapshot ) {
489 nextSnapshot = snapshot->next;
490 if ( snapshot->sequence == sequence ) {
491 for ( state = snapshot->firstEntityState; state; state = state->next ) {
492 if ( clientEntityStates[clientNum][state->entityNumber] ) {
493 entityStateAllocator.Free( clientEntityStates[clientNum][state->entityNumber] );
495 clientEntityStates[clientNum][state->entityNumber] = state;
497 memcpy( clientPVS[clientNum], snapshot->pvs, sizeof( snapshot->pvs ) );
498 if ( lastSnapshot ) {
499 lastSnapshot->next = nextSnapshot;
501 clientSnapshots[clientNum] = nextSnapshot;
503 snapshotAllocator.Free( snapshot );
506 lastSnapshot = snapshot;
515 idGameLocal::WriteGameStateToSnapshot
518 void idGameLocal::WriteGameStateToSnapshot( idBitMsgDelta &msg ) const {
521 for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
522 msg.WriteFloat( globalShaderParms[i] );
525 mpGame.WriteToSnapshot( msg );
530 idGameLocal::ReadGameStateFromSnapshot
533 void idGameLocal::ReadGameStateFromSnapshot( const idBitMsgDelta &msg ) {
536 for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
537 globalShaderParms[i] = msg.ReadFloat();
540 mpGame.ReadFromSnapshot( msg );
545 idGameLocal::ServerWriteSnapshot
547 Write a snapshot of the current game state for the given client.
550 void idGameLocal::ServerWriteSnapshot( int clientNum, int sequence, idBitMsg &msg, byte *clientInPVS, int numPVSClients ) {
551 int i, msgSize, msgWriteBit;
552 idPlayer *player, *spectated = NULL;
554 pvsHandle_t pvsHandle;
555 idBitMsgDelta deltaMsg;
556 snapshot_t *snapshot;
557 entityState_t *base, *newBase;
558 int numSourceAreas, sourceAreas[ idEntity::MAX_PVS_AREAS ];
560 player = static_cast<idPlayer *>( entities[ clientNum ] );
564 if ( player->spectating && player->spectator != clientNum && entities[ player->spectator ] ) {
565 spectated = static_cast< idPlayer * >( entities[ player->spectator ] );
570 // free too old snapshots
571 FreeSnapshotsOlderThanSequence( clientNum, sequence - 64 );
573 // allocate new snapshot
574 snapshot = snapshotAllocator.Alloc();
575 snapshot->sequence = sequence;
576 snapshot->firstEntityState = NULL;
577 snapshot->next = clientSnapshots[clientNum];
578 clientSnapshots[clientNum] = snapshot;
579 memset( snapshot->pvs, 0, sizeof( snapshot->pvs ) );
581 // get PVS for this player
582 // don't use PVSAreas for networking - PVSAreas depends on animations (and md5 bounds), which are not synchronized
583 numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS );
584 pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL );
588 tagRandom.SetSeed( random.RandomInt() );
589 msg.WriteLong( tagRandom.GetSeed() );
592 // create the snapshot
593 for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
595 // if the entity is not in the player PVS
596 if ( !ent->PhysicsTeamInPVS( pvsHandle ) && ent->entityNumber != clientNum ) {
600 // add the entity to the snapshot pvs
601 snapshot->pvs[ ent->entityNumber >> 5 ] |= 1 << ( ent->entityNumber & 31 );
603 // if that entity is not marked for network synchronization
604 if ( !ent->fl.networkSync ) {
608 // save the write state to which we can revert when the entity didn't change at all
609 msg.SaveWriteState( msgSize, msgWriteBit );
611 // write the entity to the snapshot
612 msg.WriteBits( ent->entityNumber, GENTITYNUM_BITS );
614 base = clientEntityStates[clientNum][ent->entityNumber];
616 base->state.BeginReading();
618 newBase = entityStateAllocator.Alloc();
619 newBase->entityNumber = ent->entityNumber;
620 newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
621 newBase->state.BeginWriting();
623 deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
625 deltaMsg.WriteBits( spawnIds[ ent->entityNumber ], 32 - GENTITYNUM_BITS );
626 deltaMsg.WriteBits( ent->GetType()->typeNum, idClass::GetTypeNumBits() );
627 deltaMsg.WriteBits( ServerRemapDecl( -1, DECL_ENTITYDEF, ent->entityDefNumber ), entityDefBits );
629 // write the class specific data to the snapshot
630 ent->WriteToSnapshot( deltaMsg );
632 if ( !deltaMsg.HasChanged() ) {
633 msg.RestoreWriteState( msgSize, msgWriteBit );
634 entityStateAllocator.Free( newBase );
636 newBase->next = snapshot->firstEntityState;
637 snapshot->firstEntityState = newBase;
640 msg.WriteLong( tagRandom.RandomInt() );
645 msg.WriteBits( ENTITYNUM_NONE, GENTITYNUM_BITS );
647 // write the PVS to the snapshot
649 for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
650 if ( i < numSourceAreas ) {
651 msg.WriteLong( sourceAreas[ i ] );
656 gameLocal.pvs.WritePVS( pvsHandle, msg );
658 for ( i = 0; i < ENTITY_PVS_SIZE; i++ ) {
659 msg.WriteDeltaLong( clientPVS[clientNum][i], snapshot->pvs[i] );
663 pvs.FreeCurrentPVS( pvsHandle );
665 // write the game and player state to the snapshot
666 base = clientEntityStates[clientNum][ENTITYNUM_NONE]; // ENTITYNUM_NONE is used for the game and player state
668 base->state.BeginReading();
670 newBase = entityStateAllocator.Alloc();
671 newBase->entityNumber = ENTITYNUM_NONE;
672 newBase->next = snapshot->firstEntityState;
673 snapshot->firstEntityState = newBase;
674 newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
675 newBase->state.BeginWriting();
676 deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
677 if ( player->spectating && player->spectator != player->entityNumber && gameLocal.entities[ player->spectator ] && gameLocal.entities[ player->spectator ]->IsType( idPlayer::Type ) ) {
678 static_cast< idPlayer * >( gameLocal.entities[ player->spectator ] )->WritePlayerStateToSnapshot( deltaMsg );
680 player->WritePlayerStateToSnapshot( deltaMsg );
682 WriteGameStateToSnapshot( deltaMsg );
684 // copy the client PVS string
685 memcpy( clientInPVS, snapshot->pvs, ( numPVSClients + 7 ) >> 3 );
686 LittleRevBytes( clientInPVS, sizeof( int ), sizeof( clientInPVS ) / sizeof ( int ) );
691 idGameLocal::ServerApplySnapshot
694 bool idGameLocal::ServerApplySnapshot( int clientNum, int sequence ) {
695 return ApplySnapshot( clientNum, sequence );
700 idGameLocal::NetworkEventWarning
703 void idGameLocal::NetworkEventWarning( const entityNetEvent_t *event, const char *fmt, ... ) {
708 int entityNum = event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 );
709 int id = event->spawnId >> GENTITYNUM_BITS;
711 length += idStr::snPrintf( buf+length, sizeof(buf)-1-length, "event %d for entity %d %d: ", event->event, entityNum, id );
712 va_start( argptr, fmt );
713 length = idStr::vsnPrintf( buf+length, sizeof(buf)-1-length, fmt, argptr );
715 idStr::Append( buf, sizeof(buf), "\n" );
717 common->DWarning( buf );
722 idGameLocal::ServerProcessEntityNetworkEventQueue
725 void idGameLocal::ServerProcessEntityNetworkEventQueue( void ) {
727 entityNetEvent_t *event;
730 while ( eventQueue.Start() ) {
731 event = eventQueue.Start();
733 if ( event->time > time ) {
737 idEntityPtr< idEntity > entPtr;
739 if( !entPtr.SetSpawnId( event->spawnId ) ) {
740 NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
742 ent = entPtr.GetEntity();
745 eventMsg.Init( event->paramsBuf, sizeof( event->paramsBuf ) );
746 eventMsg.SetSize( event->paramsSize );
747 eventMsg.BeginReading();
748 if ( !ent->ServerReceiveEvent( event->event, event->time, eventMsg ) ) {
749 NetworkEventWarning( event, "unknown event" );
753 entityNetEvent_t* freedEvent = eventQueue.Dequeue();
754 assert( freedEvent == event );
755 eventQueue.Free( event );
761 idGameLocal::ServerSendChatMessage
764 void idGameLocal::ServerSendChatMessage( int to, const char *name, const char *text ) {
766 byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
768 outMsg.Init( msgBuf, sizeof( msgBuf ) );
769 outMsg.BeginWriting();
770 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CHAT );
771 outMsg.WriteString( name );
772 outMsg.WriteString( text, -1, false );
773 networkSystem->ServerSendReliableMessage( to, outMsg );
775 if ( to == -1 || to == localClientNum ) {
776 mpGame.AddChatLine( "%s^0: %s\n", name, text );
782 idGameLocal::ServerProcessReliableMessage
785 void idGameLocal::ServerProcessReliableMessage( int clientNum, const idBitMsg &msg ) {
790 case GAME_RELIABLE_MESSAGE_CHAT:
791 case GAME_RELIABLE_MESSAGE_TCHAT: {
795 msg.ReadString( name, sizeof( name ) );
796 msg.ReadString( text, sizeof( text ) );
798 mpGame.ProcessChatMessage( clientNum, id == GAME_RELIABLE_MESSAGE_TCHAT, name, text, NULL );
802 case GAME_RELIABLE_MESSAGE_VCHAT: {
803 int index = msg.ReadLong();
804 bool team = msg.ReadBits( 1 ) != 0;
805 mpGame.ProcessVoiceChat( clientNum, team, index );
808 case GAME_RELIABLE_MESSAGE_KILL: {
809 mpGame.WantKilled( clientNum );
812 case GAME_RELIABLE_MESSAGE_DROPWEAPON: {
813 mpGame.DropWeapon( clientNum );
816 case GAME_RELIABLE_MESSAGE_CALLVOTE: {
817 mpGame.ServerCallVote( clientNum, msg );
820 case GAME_RELIABLE_MESSAGE_CASTVOTE: {
821 bool vote = ( msg.ReadByte() != 0 );
822 mpGame.CastVote( clientNum, vote );
826 // uncomment this if you want to track when players are in a menu
827 case GAME_RELIABLE_MESSAGE_MENU: {
828 bool menuUp = ( msg.ReadBits( 1 ) != 0 );
832 case GAME_RELIABLE_MESSAGE_EVENT: {
833 entityNetEvent_t *event;
835 // allocate new event
836 event = eventQueue.Alloc();
837 eventQueue.Enqueue( event, idEventQueue::OUTOFORDER_DROP );
839 event->spawnId = msg.ReadBits( 32 );
840 event->event = msg.ReadByte();
841 event->time = msg.ReadLong();
843 event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
844 if ( event->paramsSize ) {
845 if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
846 NetworkEventWarning( event, "invalid param size" );
850 msg.ReadData( event->paramsBuf, event->paramsSize );
855 Warning( "Unknown client->server reliable message: %d", id );
863 idGameLocal::ClientShowSnapshot
866 void idGameLocal::ClientShowSnapshot( int clientNum ) const {
874 if ( !net_clientShowSnapshot.GetInteger() ) {
878 player = static_cast<idPlayer *>( entities[clientNum] );
883 viewAxis = player->viewAngles.ToMat3();
884 viewBounds = player->GetPhysics()->GetAbsBounds().Expand( net_clientShowSnapshotRadius.GetFloat() );
886 for( ent = snapshotEntities.Next(); ent != NULL; ent = ent->snapshotNode.Next() ) {
888 if ( net_clientShowSnapshot.GetInteger() == 1 && ent->snapshotBits == 0 ) {
892 const idBounds &entBounds = ent->GetPhysics()->GetAbsBounds();
894 if ( !entBounds.IntersectsBounds( viewBounds ) ) {
898 base = clientEntityStates[clientNum][ent->entityNumber];
900 baseBits = base->state.GetNumBitsWritten();
905 if ( net_clientShowSnapshot.GetInteger() == 2 && baseBits == 0 ) {
909 gameRenderWorld->DebugBounds( colorGreen, entBounds );
910 gameRenderWorld->DrawText( va( "%d: %s (%d,%d bytes of %d,%d)\n", ent->entityNumber,
911 ent->name.c_str(), ent->snapshotBits >> 3, ent->snapshotBits & 7, baseBits >> 3, baseBits & 7 ),
912 entBounds.GetCenter(), 0.1f, colorWhite, viewAxis, 1 );
918 idGameLocal::UpdateLagometer
921 void idGameLocal::UpdateLagometer( int aheadOfServer, int dupeUsercmds ) {
923 for ( i = 0; i < LAGO_HEIGHT; i++ ) {
924 memmove( (byte *)lagometer + LAGO_WIDTH * 4 * i, (byte *)lagometer + LAGO_WIDTH * 4 * i + 4, ( LAGO_WIDTH - 1 ) * 4 );
927 for ( i = 0; i < LAGO_HEIGHT; i++ ) {
928 lagometer[i][j][0] = lagometer[i][j][1] = lagometer[i][j][2] = lagometer[i][j][3] = 0;
930 ahead = idMath::Rint( (float)aheadOfServer / 16.0f );
932 for ( i = 2 * Max( 0, 5 - ahead ); i < 2 * 5; i++ ) {
933 lagometer[i][j][1] = 255;
934 lagometer[i][j][3] = 255;
937 for ( i = 2 * 5; i < 2 * ( 5 + Min( 10, -ahead ) ); i++ ) {
938 lagometer[i][j][0] = 255;
939 lagometer[i][j][1] = 255;
940 lagometer[i][j][3] = 255;
943 for ( i = LAGO_HEIGHT - 2 * Min( 6, dupeUsercmds ); i < LAGO_HEIGHT; i++ ) {
944 lagometer[i][j][0] = 255;
945 if ( dupeUsercmds <= 2 ) {
946 lagometer[i][j][1] = 255;
948 lagometer[i][j][3] = 255;
954 idGameLocal::ClientReadSnapshot
957 void idGameLocal::ClientReadSnapshot( int clientNum, int sequence, const int gameFrame, const int gameTime, const int dupeUsercmds, const int aheadOfServer, const idBitMsg &msg ) {
958 int i, typeNum, entityDefNumber, numBitsRead;
959 idTypeInfo *typeInfo;
961 idPlayer *player, *spectated;
962 pvsHandle_t pvsHandle;
964 const char *classname;
965 idBitMsgDelta deltaMsg;
966 snapshot_t *snapshot;
967 entityState_t *base, *newBase;
969 int numSourceAreas, sourceAreas[ idEntity::MAX_PVS_AREAS ];
972 if ( net_clientLagOMeter.GetBool() && renderSystem ) {
973 UpdateLagometer( aheadOfServer, dupeUsercmds );
974 if ( !renderSystem->UploadImage( LAGO_IMAGE, (byte *)lagometer, LAGO_IMG_WIDTH, LAGO_IMG_HEIGHT ) ) {
975 common->Printf( "lagometer: UploadImage failed. turning off net_clientLagOMeter\n" );
976 net_clientLagOMeter.SetBool( false );
980 InitLocalClient( clientNum );
982 // clear any debug lines from a previous frame
983 gameRenderWorld->DebugClearLines( time );
985 // clear any debug polygons from a previous frame
986 gameRenderWorld->DebugClearPolygons( time );
988 // update the game time
989 framenum = gameFrame;
991 previousTime = time - msec;
993 // so that StartSound/StopSound doesn't risk skipping
996 // clear the snapshot entity list
997 snapshotEntities.Clear();
999 // allocate new snapshot
1000 snapshot = snapshotAllocator.Alloc();
1001 snapshot->sequence = sequence;
1002 snapshot->firstEntityState = NULL;
1003 snapshot->next = clientSnapshots[clientNum];
1004 clientSnapshots[clientNum] = snapshot;
1006 #if ASYNC_WRITE_TAGS
1008 tagRandom.SetSeed( msg.ReadLong() );
1011 // read all entities from the snapshot
1012 for ( i = msg.ReadBits( GENTITYNUM_BITS ); i != ENTITYNUM_NONE; i = msg.ReadBits( GENTITYNUM_BITS ) ) {
1014 base = clientEntityStates[clientNum][i];
1016 base->state.BeginReading();
1018 newBase = entityStateAllocator.Alloc();
1019 newBase->entityNumber = i;
1020 newBase->next = snapshot->firstEntityState;
1021 snapshot->firstEntityState = newBase;
1022 newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
1023 newBase->state.BeginWriting();
1025 numBitsRead = msg.GetNumBitsRead();
1027 deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
1029 spawnId = deltaMsg.ReadBits( 32 - GENTITYNUM_BITS );
1030 typeNum = deltaMsg.ReadBits( idClass::GetTypeNumBits() );
1031 entityDefNumber = ClientRemapDecl( DECL_ENTITYDEF, deltaMsg.ReadBits( entityDefBits ) );
1033 typeInfo = idClass::GetType( typeNum );
1035 Error( "Unknown type number %d for entity %d with class number %d", typeNum, i, entityDefNumber );
1040 // if there is no entity or an entity of the wrong type
1041 if ( !ent || ent->GetType()->typeNum != typeNum || ent->entityDefNumber != entityDefNumber || spawnId != spawnIds[ i ] ) {
1043 if ( i < MAX_CLIENTS && ent ) {
1044 // SPAWN_PLAYER should be taking care of spawning the entity with the right spawnId
1045 common->Warning( "ClientReadSnapshot: recycling client entity %d\n", i );
1050 spawnCount = spawnId;
1053 args.SetInt( "spawn_entnum", i );
1054 args.Set( "name", va( "entity%d", i ) );
1056 if ( entityDefNumber >= 0 ) {
1057 if ( entityDefNumber >= declManager->GetNumDecls( DECL_ENTITYDEF ) ) {
1058 Error( "server has %d entityDefs instead of %d", entityDefNumber, declManager->GetNumDecls( DECL_ENTITYDEF ) );
1060 classname = declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
1061 args.Set( "classname", classname );
1062 if ( !SpawnEntityDef( args, &ent ) || !entities[i] || entities[i]->GetType()->typeNum != typeNum ) {
1063 Error( "Failed to spawn entity with classname '%s' of type '%s'", classname, typeInfo->classname );
1066 ent = SpawnEntityType( *typeInfo, &args, true );
1067 if ( !entities[i] || entities[i]->GetType()->typeNum != typeNum ) {
1068 Error( "Failed to spawn entity of type '%s'", typeInfo->classname );
1071 if ( i < MAX_CLIENTS && i >= numClients ) {
1076 // add the entity to the snapshot list
1077 ent->snapshotNode.AddToEnd( snapshotEntities );
1078 ent->snapshotSequence = sequence;
1080 // read the class specific data from the snapshot
1081 ent->ReadFromSnapshot( deltaMsg );
1083 ent->snapshotBits = msg.GetNumBitsRead() - numBitsRead;
1085 #if ASYNC_WRITE_TAGS
1086 if ( msg.ReadLong() != tagRandom.RandomInt() ) {
1087 cmdSystem->BufferCommandText( CMD_EXEC_NOW, "writeGameState" );
1088 if ( entityDefNumber >= 0 && entityDefNumber < declManager->GetNumDecls( DECL_ENTITYDEF ) ) {
1089 classname = declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
1090 Error( "write to and read from snapshot out of sync for classname '%s' of type '%s'", classname, typeInfo->classname );
1092 Error( "write to and read from snapshot out of sync for type '%s'", typeInfo->classname );
1098 player = static_cast<idPlayer *>( entities[clientNum] );
1103 // if prediction is off, enable local client smoothing
1104 player->SetSelfSmooth( dupeUsercmds > 2 );
1106 if ( player->spectating && player->spectator != clientNum && entities[ player->spectator ] ) {
1107 spectated = static_cast< idPlayer * >( entities[ player->spectator ] );
1112 // get PVS for this player
1113 // don't use PVSAreas for networking - PVSAreas depends on animations (and md5 bounds), which are not synchronized
1114 numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS );
1115 pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL );
1117 // read the PVS from the snapshot
1119 int serverPVS[idEntity::MAX_PVS_AREAS];
1121 while ( i < idEntity::MAX_PVS_AREAS ) {
1122 sourceAreas[ i++ ] = 0;
1124 for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
1125 serverPVS[ i ] = msg.ReadLong();
1127 if ( memcmp( sourceAreas, serverPVS, idEntity::MAX_PVS_AREAS * sizeof( int ) ) ) {
1128 common->Warning( "client PVS areas != server PVS areas, sequence 0x%x", sequence );
1129 for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
1130 common->DPrintf( "%3d ", sourceAreas[ i ] );
1132 common->DPrintf( "\n" );
1133 for ( i = 0; i < idEntity::MAX_PVS_AREAS; i++ ) {
1134 common->DPrintf( "%3d ", serverPVS[ i ] );
1136 common->DPrintf( "\n" );
1138 gameLocal.pvs.ReadPVS( pvsHandle, msg );
1140 for ( i = 0; i < ENTITY_PVS_SIZE; i++ ) {
1141 snapshot->pvs[i] = msg.ReadDeltaLong( clientPVS[clientNum][i] );
1144 // add entities in the PVS that haven't changed since the last applied snapshot
1145 for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
1147 // if the entity is already in the snapshot
1148 if ( ent->snapshotSequence == sequence ) {
1152 // if the entity is not in the snapshot PVS
1153 if ( !( snapshot->pvs[ent->entityNumber >> 5] & ( 1 << ( ent->entityNumber & 31 ) ) ) ) {
1154 if ( ent->PhysicsTeamInPVS( pvsHandle ) ) {
1155 if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < mapSpawnCount ) {
1156 // server says it's not in PVS, client says it's in PVS
1157 // if that happens on map entities, most likely something is wrong
1158 // I can see that moving pieces along several PVS could be a legit situation though
1159 // this is a band aid, which means something is not done right elsewhere
1160 common->DWarning( "client thinks map entity 0x%x (%s) is stale, sequence 0x%x", ent->entityNumber, ent->name.c_str(), sequence );
1162 ent->FreeModelDef();
1163 ent->UpdateVisuals();
1164 ent->GetPhysics()->UnlinkClip();
1170 // add the entity to the snapshot list
1171 ent->snapshotNode.AddToEnd( snapshotEntities );
1172 ent->snapshotSequence = sequence;
1173 ent->snapshotBits = 0;
1175 base = clientEntityStates[clientNum][ent->entityNumber];
1177 // entity has probably fl.networkSync set to false
1181 base->state.BeginReading();
1183 deltaMsg.Init( &base->state, NULL, (const idBitMsg *)NULL );
1185 spawnId = deltaMsg.ReadBits( 32 - GENTITYNUM_BITS );
1186 typeNum = deltaMsg.ReadBits( idClass::GetTypeNumBits() );
1187 entityDefNumber = deltaMsg.ReadBits( entityDefBits );
1189 typeInfo = idClass::GetType( typeNum );
1191 // if the entity is not the right type
1192 if ( !typeInfo || ent->GetType()->typeNum != typeNum || ent->entityDefNumber != entityDefNumber ) {
1193 // should never happen - it does though. with != entityDefNumber only?
1194 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 );
1198 // read the class specific data from the base state
1199 ent->ReadFromSnapshot( deltaMsg );
1203 pvs.FreeCurrentPVS( pvsHandle );
1205 // read the game and player state from the snapshot
1206 base = clientEntityStates[clientNum][ENTITYNUM_NONE]; // ENTITYNUM_NONE is used for the game and player state
1208 base->state.BeginReading();
1210 newBase = entityStateAllocator.Alloc();
1211 newBase->entityNumber = ENTITYNUM_NONE;
1212 newBase->next = snapshot->firstEntityState;
1213 snapshot->firstEntityState = newBase;
1214 newBase->state.Init( newBase->stateBuf, sizeof( newBase->stateBuf ) );
1215 newBase->state.BeginWriting();
1216 deltaMsg.Init( base ? &base->state : NULL, &newBase->state, &msg );
1217 if ( player->spectating && player->spectator != player->entityNumber && gameLocal.entities[ player->spectator ] && gameLocal.entities[ player->spectator ]->IsType( idPlayer::Type ) ) {
1218 static_cast< idPlayer * >( gameLocal.entities[ player->spectator ] )->ReadPlayerStateFromSnapshot( deltaMsg );
1219 weap = static_cast< idPlayer * >( gameLocal.entities[ player->spectator ] )->weapon.GetEntity();
1220 if ( weap && ( weap->GetRenderEntity()->bounds[0] == weap->GetRenderEntity()->bounds[1] ) ) {
1221 // update the weapon's viewmodel bounds so that the model doesn't flicker in the spectator's view
1222 weap->GetAnimator()->GetBounds( gameLocal.time, weap->GetRenderEntity()->bounds );
1223 weap->UpdateVisuals();
1226 player->ReadPlayerStateFromSnapshot( deltaMsg );
1228 ReadGameStateFromSnapshot( deltaMsg );
1230 // visualize the snapshot
1231 ClientShowSnapshot( clientNum );
1233 // process entity events
1234 ClientProcessEntityNetworkEventQueue();
1239 idGameLocal::ClientApplySnapshot
1242 bool idGameLocal::ClientApplySnapshot( int clientNum, int sequence ) {
1243 return ApplySnapshot( clientNum, sequence );
1248 idGameLocal::ClientProcessEntityNetworkEventQueue
1251 void idGameLocal::ClientProcessEntityNetworkEventQueue( void ) {
1253 entityNetEvent_t *event;
1256 while( eventQueue.Start() ) {
1257 event = eventQueue.Start();
1259 // only process forward, in order
1260 if ( event->time > time ) {
1264 idEntityPtr< idEntity > entPtr;
1266 if( !entPtr.SetSpawnId( event->spawnId ) ) {
1267 if( !gameLocal.entities[ event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 ) ] ) {
1268 // if new entity exists in this position, silently ignore
1269 NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
1272 ent = entPtr.GetEntity();
1275 eventMsg.Init( event->paramsBuf, sizeof( event->paramsBuf ) );
1276 eventMsg.SetSize( event->paramsSize );
1277 eventMsg.BeginReading();
1278 if ( !ent->ClientReceiveEvent( event->event, event->time, eventMsg ) ) {
1279 NetworkEventWarning( event, "unknown event" );
1283 entityNetEvent_t* freedEvent = eventQueue.Dequeue();
1284 assert( freedEvent == event );
1285 eventQueue.Free( event );
1291 idGameLocal::ClientProcessReliableMessage
1294 void idGameLocal::ClientProcessReliableMessage( int clientNum, const idBitMsg &msg ) {
1299 InitLocalClient( clientNum );
1301 id = msg.ReadByte();
1303 case GAME_RELIABLE_MESSAGE_INIT_DECL_REMAP: {
1304 InitClientDeclRemap( clientNum );
1307 case GAME_RELIABLE_MESSAGE_REMAP_DECL: {
1309 char name[MAX_STRING_CHARS];
1311 type = msg.ReadByte();
1312 index = msg.ReadLong();
1313 msg.ReadString( name, sizeof( name ) );
1315 const idDecl *decl = declManager->FindType( (declType_t)type, name, false );
1316 if ( decl != NULL ) {
1317 if ( index >= clientDeclRemap[clientNum][type].Num() ) {
1318 clientDeclRemap[clientNum][type].AssureSize( index + 1, -1 );
1320 clientDeclRemap[clientNum][type][index] = decl->Index();
1324 case GAME_RELIABLE_MESSAGE_SPAWN_PLAYER: {
1325 int client = msg.ReadByte();
1326 int spawnId = msg.ReadLong();
1327 if ( !entities[ client ] ) {
1328 SpawnPlayer( client );
1329 entities[ client ]->FreeModelDef();
1331 // fix up the spawnId to match what the server says
1332 // otherwise there is going to be a bogus delete/new of the client entity in the first ClientReadFromSnapshot
1333 spawnIds[ client ] = spawnId;
1336 case GAME_RELIABLE_MESSAGE_DELETE_ENT: {
1337 int spawnId = msg.ReadBits( 32 );
1338 idEntityPtr< idEntity > entPtr;
1339 if( !entPtr.SetSpawnId( spawnId ) ) {
1342 delete entPtr.GetEntity();
1345 case GAME_RELIABLE_MESSAGE_CHAT:
1346 case GAME_RELIABLE_MESSAGE_TCHAT: { // (client should never get a TCHAT though)
1349 msg.ReadString( name, sizeof( name ) );
1350 msg.ReadString( text, sizeof( text ) );
1351 mpGame.AddChatLine( "%s^0: %s\n", name, text );
1354 case GAME_RELIABLE_MESSAGE_SOUND_EVENT: {
1355 snd_evt_t snd_evt = (snd_evt_t)msg.ReadByte();
1356 mpGame.PlayGlobalSound( -1, snd_evt );
1359 case GAME_RELIABLE_MESSAGE_SOUND_INDEX: {
1360 int index = gameLocal.ClientRemapDecl( DECL_SOUND, msg.ReadLong() );
1361 if ( index >= 0 && index < declManager->GetNumDecls( DECL_SOUND ) ) {
1362 const idSoundShader *shader = declManager->SoundByIndex( index );
1363 mpGame.PlayGlobalSound( -1, SND_COUNT, shader->GetName() );
1367 case GAME_RELIABLE_MESSAGE_DB: {
1368 idMultiplayerGame::msg_evt_t msg_evt = (idMultiplayerGame::msg_evt_t)msg.ReadByte();
1370 parm1 = msg.ReadByte( );
1371 parm2 = msg.ReadByte( );
1372 mpGame.PrintMessageEvent( -1, msg_evt, parm1, parm2 );
1375 case GAME_RELIABLE_MESSAGE_EVENT: {
1376 entityNetEvent_t *event;
1378 // allocate new event
1379 event = eventQueue.Alloc();
1380 eventQueue.Enqueue( event, idEventQueue::OUTOFORDER_IGNORE );
1382 event->spawnId = msg.ReadBits( 32 );
1383 event->event = msg.ReadByte();
1384 event->time = msg.ReadLong();
1386 event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
1387 if ( event->paramsSize ) {
1388 if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
1389 NetworkEventWarning( event, "invalid param size" );
1392 msg.ReadByteAlign();
1393 msg.ReadData( event->paramsBuf, event->paramsSize );
1397 case GAME_RELIABLE_MESSAGE_SERVERINFO: {
1399 msg.ReadDeltaDict( info, NULL );
1400 gameLocal.SetServerInfo( info );
1403 case GAME_RELIABLE_MESSAGE_RESTART: {
1407 case GAME_RELIABLE_MESSAGE_TOURNEYLINE: {
1408 line = msg.ReadByte( );
1409 p = static_cast< idPlayer * >( entities[ clientNum ] );
1413 p->tourneyLine = line;
1416 case GAME_RELIABLE_MESSAGE_STARTVOTE: {
1417 char voteString[ MAX_STRING_CHARS ];
1418 int clientNum = msg.ReadByte( );
1419 msg.ReadString( voteString, sizeof( voteString ) );
1420 mpGame.ClientStartVote( clientNum, voteString );
1423 case GAME_RELIABLE_MESSAGE_UPDATEVOTE: {
1424 int result = msg.ReadByte( );
1425 int yesCount = msg.ReadByte( );
1426 int noCount = msg.ReadByte( );
1427 mpGame.ClientUpdateVote( (idMultiplayerGame::vote_result_t)result, yesCount, noCount );
1430 case GAME_RELIABLE_MESSAGE_PORTALSTATES: {
1431 int numPortals = msg.ReadLong();
1432 assert( numPortals == gameRenderWorld->NumPortals() );
1433 for ( int i = 0; i < numPortals; i++ ) {
1434 gameRenderWorld->SetPortalState( (qhandle_t) (i+1), msg.ReadBits( NUM_RENDER_PORTAL_BITS ) );
1438 case GAME_RELIABLE_MESSAGE_PORTAL: {
1439 qhandle_t portal = msg.ReadLong();
1440 int blockingBits = msg.ReadBits( NUM_RENDER_PORTAL_BITS );
1441 assert( portal > 0 && portal <= gameRenderWorld->NumPortals() );
1442 gameRenderWorld->SetPortalState( portal, blockingBits );
1445 case GAME_RELIABLE_MESSAGE_STARTSTATE: {
1446 mpGame.ClientReadStartState( msg );
1449 case GAME_RELIABLE_MESSAGE_WARMUPTIME: {
1450 mpGame.ClientReadWarmupTime( msg );
1454 Error( "Unknown server->client reliable message: %d", id );
1462 idGameLocal::ClientPrediction
1465 gameReturn_t idGameLocal::ClientPrediction( int clientNum, const usercmd_t *clientCmds, bool lastPredictFrame ) {
1470 ret.sessionCommand[ 0 ] = '\0';
1472 player = static_cast<idPlayer *>( entities[clientNum] );
1477 // check for local client lag
1478 if ( networkSystem->ClientGetTimeSinceLastPacket() >= net_clientMaxPrediction.GetInteger() ) {
1479 player->isLagged = true;
1481 player->isLagged = false;
1484 InitLocalClient( clientNum );
1486 // update the game time
1488 previousTime = time;
1491 // update the real client time and the new frame flag
1492 if ( time > realClientTime ) {
1493 realClientTime = time;
1499 // set the user commands for this frame
1500 memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
1502 // run prediction on all entities from the last snapshot
1503 for( ent = snapshotEntities.Next(); ent != NULL; ent = ent->snapshotNode.Next() ) {
1504 ent->thinkFlags |= TH_PHYSICS;
1505 ent->ClientPredictionThink();
1508 // service any pending events
1509 idEvent::ServiceEvents();
1511 // show any debug info for this frame
1517 if ( sessionCommand.Length() ) {
1518 strncpy( ret.sessionCommand, sessionCommand, sizeof( ret.sessionCommand ) );
1525 idGameLocal::Tokenize
1528 void idGameLocal::Tokenize( idStrList &out, const char *in ) {
1529 char buf[ MAX_STRING_CHARS ];
1532 idStr::Copynz( buf, in, MAX_STRING_CHARS );
1534 next = strchr( token, ';' );
1539 idStr::ToLower( token );
1540 out.Append( token );
1543 next = strchr( token, ';' );
1552 idGameLocal::DownloadRequest
1555 bool idGameLocal::DownloadRequest( const char *IP, const char *guid, const char *paks, char urls[ MAX_STRING_CHARS ] ) {
1556 if ( !cvarSystem->GetCVarInteger( "net_serverDownload" ) ) {
1559 if ( cvarSystem->GetCVarInteger( "net_serverDownload" ) == 1 ) {
1560 // 1: single URL redirect
1561 if ( !strlen( cvarSystem->GetCVarString( "si_serverURL" ) ) ) {
1562 common->Warning( "si_serverURL not set" );
1565 idStr::snPrintf( urls, MAX_STRING_CHARS, "1;%s", cvarSystem->GetCVarString( "si_serverURL" ) );
1568 // 2: table of pak URLs
1569 // first token is the game pak if request, empty if not requested by the client
1570 // there may be empty tokens for paks the server couldn't pinpoint - the order matters
1572 idStrList dlTable, pakList;
1575 Tokenize( dlTable, cvarSystem->GetCVarString( "net_serverDlTable" ) );
1576 Tokenize( pakList, paks );
1578 for ( i = 0; i < pakList.Num(); i++ ) {
1582 if ( pakList[ i ][ 0 ] == '\0' ) {
1584 // pak 0 will always miss when client doesn't ask for game bin
1585 common->DPrintf( "no game pak request\n" );
1587 common->DPrintf( "no pak %d\n", i );
1591 for ( j = 0; j < dlTable.Num(); j++ ) {
1592 if ( !fileSystem->FilenameCompare( pakList[ i ], dlTable[ j ] ) ) {
1596 if ( j == dlTable.Num() ) {
1597 common->Printf( "download for %s: pak not matched: %s\n", IP, pakList[ i ].c_str() );
1599 idStr url = cvarSystem->GetCVarString( "net_serverDlBaseURL" );
1600 url.AppendPath( dlTable[ j ] );
1602 common->DPrintf( "download for %s: %s\n", IP, url.c_str() );
1606 idStr::Copynz( urls, reply, MAX_STRING_CHARS );
1617 entityNetEvent_t* idEventQueue::Alloc() {
1618 entityNetEvent_t* event = eventAllocator.Alloc();
1629 void idEventQueue::Free( entityNetEvent_t *event ) {
1630 // should only be called on an unlinked event!
1631 assert( !event->next && !event->prev );
1632 eventAllocator.Free( event );
1637 idEventQueue::Shutdown
1640 void idEventQueue::Shutdown() {
1641 eventAllocator.Shutdown();
1650 void idEventQueue::Init( void ) {
1657 idEventQueue::Dequeue
1660 entityNetEvent_t* idEventQueue::Dequeue( void ) {
1661 entityNetEvent_t* event = start;
1666 start = start->next;
1682 idEventQueue::RemoveLast
1685 entityNetEvent_t* idEventQueue::RemoveLast( void ) {
1686 entityNetEvent_t *event = end;
1707 idEventQueue::Enqueue
1710 void idEventQueue::Enqueue( entityNetEvent_t *event, outOfOrderBehaviour_t behaviour ) {
1711 if ( behaviour == OUTOFORDER_DROP ) {
1712 // go backwards through the queue and determine if there are
1713 // any out-of-order events
1714 while ( end && end->time > event->time ) {
1715 entityNetEvent_t *outOfOrder = RemoveLast();
1716 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 );
1719 } else if ( behaviour == OUTOFORDER_SORT && end ) {
1720 // NOT TESTED -- sorting out of order packets hasn't been
1721 // tested yet... wasn't strictly necessary for
1723 entityNetEvent_t *cur = end;
1724 // iterate until we find a time < the new event's
1725 while ( cur && cur->time > event->time ) {
1730 event->next = start;
1736 event->next = cur->next;
1742 // add the new event