DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Game_network.cpp (39978B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition 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.
     13 
     14 Doom 3 BFG Edition 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.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     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.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 #include "Game_local.h"
     33 #include "..\framework\Common_local.h"
     34 
     35 static const int SNAP_GAMESTATE = 0;
     36 static const int SNAP_SHADERPARMS = 1;
     37 static const int SNAP_PORTALS = 2;
     38 static const int SNAP_PLAYERSTATE = SNAP_PORTALS + 1;
     39 static const int SNAP_PLAYERSTATE_END = SNAP_PLAYERSTATE + MAX_PLAYERS;
     40 static const int SNAP_ENTITIES = SNAP_PLAYERSTATE_END;
     41 static const int SNAP_ENTITIES_END = SNAP_ENTITIES + MAX_GENTITIES;
     42 static const int SNAP_LAST_CLIENT_FRAME = SNAP_ENTITIES_END;
     43 static const int SNAP_LAST_CLIENT_FRAME_END = SNAP_LAST_CLIENT_FRAME + MAX_PLAYERS;
     44 
     45 /*
     46 ===============================================================================
     47 
     48 	Client running game code:
     49 	- entity events don't work and should not be issued
     50 	- entities should never be spawned outside idGameLocal::ClientReadSnapshot
     51 
     52 ===============================================================================
     53 */
     54 
     55 idCVar net_clientSmoothing( "net_clientSmoothing", "0.8", CVAR_GAME | CVAR_FLOAT, "smooth other clients angles and position.", 0.0f, 0.95f );
     56 idCVar net_clientSelfSmoothing( "net_clientSelfSmoothing", "0.6", CVAR_GAME | CVAR_FLOAT, "smooth self position if network causes prediction error.", 0.0f, 0.95f );
     57 extern idCVar net_clientMaxPrediction;
     58 
     59 idCVar cg_predictedSpawn_debug( "cg_predictedSpawn_debug", "0", CVAR_BOOL, "Debug predictive spawning of presentables" );
     60 idCVar g_clientFire_checkLineOfSightDebug( "g_clientFire_checkLineOfSightDebug", "0", CVAR_BOOL, "" );
     61 
     62 
     63 /*
     64 ================
     65 idGameLocal::InitAsyncNetwork
     66 ================
     67 */
     68 void idGameLocal::InitAsyncNetwork() {
     69 	eventQueue.Init();
     70 	savedEventQueue.Init();
     71 
     72 	entityDefBits = -( idMath::BitsForInteger( declManager->GetNumDecls( DECL_ENTITYDEF ) ) + 1 );
     73 	realClientTime = 0;
     74 	fast.Set( 0, 0, 0 );
     75 	slow.Set( 0, 0, 0 );
     76 	isNewFrame = true;
     77 	clientSmoothing = net_clientSmoothing.GetFloat();
     78 
     79 	lastCmdRunTimeOnClient.Zero();
     80 	lastCmdRunTimeOnServer.Zero();
     81 	usercmdLastClientMilliseconds.Zero();
     82 }
     83 
     84 /*
     85 ================
     86 idGameLocal::ShutdownAsyncNetwork
     87 ================
     88 */
     89 void idGameLocal::ShutdownAsyncNetwork() {
     90 	eventQueue.Shutdown();
     91 	savedEventQueue.Shutdown();
     92 }
     93 
     94 /*
     95 ================
     96 idGameLocal::ServerRemapDecl
     97 ================
     98 */
     99 int idGameLocal::ServerRemapDecl( int clientNum, declType_t type, int index ) {
    100 	return index;
    101 }
    102 
    103 /*
    104 ================
    105 idGameLocal::ClientRemapDecl
    106 ================
    107 */
    108 int idGameLocal::ClientRemapDecl( declType_t type, int index ) {
    109 	return index;
    110 }
    111 
    112 /*
    113 ================
    114 idGameLocal::SyncPlayersWithLobbyUsers
    115 ================
    116 */
    117 void idGameLocal::SyncPlayersWithLobbyUsers( bool initial ) {
    118 	idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
    119 	if ( !lobby.IsHost() ) {
    120 		return;
    121 	}
    122 
    123 	idStaticList< lobbyUserID_t, MAX_CLIENTS > newLobbyUsers;
    124 
    125 	// First, loop over lobby users, and see if we find a lobby user that we haven't registered
    126 	for ( int i = 0; i < lobby.GetNumLobbyUsers(); i++ ) {	
    127 		lobbyUserID_t lobbyUserID1 = lobby.GetLobbyUserIdByOrdinal( i );
    128 
    129 		if ( !lobbyUserID1.IsValid() ) {
    130 			continue;
    131 		}
    132 
    133 		if ( !initial && !lobby.IsLobbyUserLoaded( lobbyUserID1 ) ) {
    134 			continue;
    135 		}
    136 
    137 		// Now, see if we find this lobby user in our list
    138 		bool found = false;
    139 
    140 		for ( int j = 0; j < MAX_PLAYERS; j++ ) {
    141 			idPlayer * player = static_cast<idPlayer *>( entities[ j ] );
    142 			if ( player == NULL ) {
    143 				continue;
    144 			}
    145 			
    146 			lobbyUserID_t lobbyUserID2 = lobbyUserIDs[j];
    147 
    148 			if ( lobbyUserID1 == lobbyUserID2 ) {
    149 				found = true;
    150 				break;
    151 			}
    152 		}
    153 
    154 		if ( !found ) {
    155 			// If we didn't find it, we need to create a player and assign it to this new lobby user
    156 			newLobbyUsers.Append( lobbyUserID1 );
    157 		}
    158 	}
    159 
    160 	// Validate connected players
    161 	for ( int i = 0; i < MAX_PLAYERS; i++ ) {
    162 		idPlayer * player = static_cast<idPlayer *>( entities[ i ] );
    163 		if ( player == NULL ) {
    164 			continue;
    165 		}
    166 		
    167 		lobbyUserID_t lobbyUserID = lobbyUserIDs[i];
    168 		
    169 		if ( !lobby.IsLobbyUserValid( lobbyUserID ) ) {
    170 			delete entities[ i ];
    171 			mpGame.DisconnectClient( i );
    172 			lobbyUserIDs[i] = lobbyUserID_t();
    173 			continue;
    174 		}
    175 
    176 		lobby.EnableSnapshotsForLobbyUser( lobbyUserID );
    177 	}
    178 
    179 	while ( newLobbyUsers.Num() > 0 ) {
    180 		// Find a free player data slot to use for this new player
    181 		int freePlayerDataIndex = -1;
    182 
    183 		for ( int i = 0; i < MAX_PLAYERS; ++i ) {
    184 			idPlayer * player = static_cast<idPlayer *>( entities[ i ] );
    185 			if ( player == NULL ) {
    186 				freePlayerDataIndex = i;
    187 				break;
    188 			}
    189 		}
    190 		if ( freePlayerDataIndex == -1 ) {
    191 			break;			// No player data slots (this shouldn't happen)
    192 		}
    193 		lobbyUserID_t lobbyUserID = newLobbyUsers[0];
    194 		newLobbyUsers.RemoveIndex( 0 );
    195 
    196 		mpGame.ServerClientConnect( freePlayerDataIndex );
    197 		Printf( "client %d connected.\n", freePlayerDataIndex );
    198 
    199 		lobbyUserIDs[ freePlayerDataIndex ] = lobbyUserID;
    200 
    201 		// Clear this player's old usercmds.
    202 		common->ResetPlayerInput( freePlayerDataIndex );
    203 
    204 		common->UpdateLevelLoadPacifier();
    205 
    206 
    207 		// spawn the player
    208 		SpawnPlayer( freePlayerDataIndex );
    209 
    210 		common->UpdateLevelLoadPacifier();
    211 
    212 		ServerWriteInitialReliableMessages( freePlayerDataIndex, lobbyUserID );
    213 	}
    214 }
    215 
    216 /*
    217 ================
    218 idGameLocal::ServerSendNetworkSyncCvars
    219 ================
    220 */
    221 void idGameLocal::ServerSendNetworkSyncCvars() {
    222 	if ( ( cvarSystem->GetModifiedFlags() & CVAR_NETWORKSYNC ) == 0 ) {
    223 		return;
    224 	}
    225 	cvarSystem->ClearModifiedFlags( CVAR_NETWORKSYNC );
    226 
    227 	idBitMsg	outMsg;
    228 	byte		msgBuf[MAX_GAME_MESSAGE_SIZE];
    229 
    230 	idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
    231 
    232 	outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
    233 	outMsg.BeginWriting();
    234 	idDict syncedCvars;
    235 	cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC, syncedCvars, true );
    236 	outMsg.WriteDeltaDict( syncedCvars, NULL );
    237 	lobby.SendReliable( GAME_RELIABLE_MESSAGE_SYNCEDCVARS, outMsg, false );
    238 
    239 	idLib::Printf( "Sending networkSync cvars:\n" );
    240 	syncedCvars.Print();
    241 }
    242 
    243 /*
    244 ================
    245 idGameLocal::ServerWriteInitialReliableMessages
    246 
    247   Send reliable messages to initialize the client game up to a certain initial state.
    248 ================
    249 */
    250 void idGameLocal::ServerWriteInitialReliableMessages( int clientNum, lobbyUserID_t lobbyUserID ) {
    251 	if ( clientNum == GetLocalClientNum() ) {
    252 		// We don't need to send messages to ourself
    253 		return;
    254 	}
    255 
    256 	idBitMsg	outMsg;
    257 	byte		msgBuf[MAX_GAME_MESSAGE_SIZE];
    258 
    259 	idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
    260 
    261 	outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
    262 	outMsg.BeginWriting();
    263 	idDict syncedCvars;
    264 	cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC, syncedCvars, true );
    265 	outMsg.WriteDeltaDict( syncedCvars, NULL );
    266 	lobby.SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_SYNCEDCVARS, outMsg );
    267 
    268 	idLib::Printf( "Sending initial networkSync cvars:\n" );
    269 	syncedCvars.Print();
    270 
    271 	// send all saved events
    272 	for ( entityNetEvent_t * event = savedEventQueue.Start(); event; event = event->next ) {
    273 		outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
    274 		outMsg.BeginWriting();
    275 		outMsg.WriteBits( event->spawnId, 32 );
    276 		outMsg.WriteByte( event->event );
    277 		outMsg.WriteLong( event->time );
    278 		outMsg.WriteBits( event->paramsSize, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
    279 		if ( event->paramsSize ) {
    280 			outMsg.WriteData( event->paramsBuf, event->paramsSize );
    281 		}
    282 		lobby.SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_EVENT, outMsg );
    283 	}
    284 
    285 	mpGame.ServerWriteInitialReliableMessages( clientNum, lobbyUserID );
    286 }
    287 
    288 /*
    289 ================
    290 idGameLocal::SaveEntityNetworkEvent
    291 ================
    292 */
    293 void idGameLocal::SaveEntityNetworkEvent( const idEntity *ent, int eventId, const idBitMsg *msg ) {
    294 	entityNetEvent_t * event = savedEventQueue.Alloc();
    295 	event->spawnId = GetSpawnId( ent );
    296 	event->event = eventId;
    297 	event->time = time;
    298 	if ( msg ) {
    299 		event->paramsSize = msg->GetSize();
    300 		memcpy( event->paramsBuf, msg->GetReadData(), msg->GetSize() );
    301 	} else {
    302 		event->paramsSize = 0;
    303 	}
    304 
    305 	savedEventQueue.Enqueue( event, idEventQueue::OUTOFORDER_IGNORE );
    306 }
    307 
    308 /*
    309 ================
    310 idGameLocal::ServerWriteSnapshot
    311 
    312   Write a snapshot of the current game state
    313 ================
    314 */
    315 void idGameLocal::ServerWriteSnapshot( idSnapShot & ss ) {
    316 
    317 	ss.SetTime( fast.time );
    318 
    319 	byte buffer[ MAX_ENTITY_STATE_SIZE ];
    320 	idBitMsg msg;
    321 
    322 	// First write the generic game state to the snapshot
    323 	msg.InitWrite( buffer, sizeof( buffer ) );
    324 	mpGame.WriteToSnapshot( msg );
    325 	ss.S_AddObject( SNAP_GAMESTATE, ~0U, msg, "Game State" );
    326 
    327 	// Update global shader parameters
    328 	msg.InitWrite( buffer, sizeof( buffer ) );
    329 	for ( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
    330 		msg.WriteFloat( globalShaderParms[i] );
    331 	}
    332 	ss.S_AddObject( SNAP_SHADERPARMS, ~0U, msg, "Shader Parms" );
    333 
    334 	// update portals for opened doors
    335 	msg.InitWrite( buffer, sizeof( buffer ) );
    336 	int numPortals = gameRenderWorld->NumPortals();
    337 	msg.WriteLong( numPortals );
    338 	for ( int i = 0; i < numPortals; i++ ) {
    339 		msg.WriteBits( gameRenderWorld->GetPortalState( (qhandle_t) (i+1) ) , NUM_RENDER_PORTAL_BITS );
    340 	}
    341 	ss.S_AddObject( SNAP_PORTALS, ~0U, msg, "Portal State" );
    342 
    343 	idEntity * skyEnt = portalSkyEnt.GetEntity();
    344 	pvsHandle_t	portalSkyPVS;
    345 	portalSkyPVS.i = -1;
    346 	if ( skyEnt != NULL ) {
    347 		portalSkyPVS = pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() );
    348 	}
    349 
    350 	// Build PVS data for each player and write their player state to the snapshot as well
    351 	pvsHandle_t pvsHandles[ MAX_PLAYERS ];
    352 	for ( int i = 0; i < MAX_PLAYERS; i++ ) {
    353 		idPlayer * player = static_cast<idPlayer *>( entities[ i ] );
    354 		if ( player == NULL ) {
    355 			pvsHandles[i].i = -1;
    356 			continue;
    357 		}
    358 		idPlayer * spectated = player;
    359 		if ( player->spectating && player->spectator != i && entities[ player->spectator ] ) {
    360 			spectated = static_cast< idPlayer * >( entities[ player->spectator ] );
    361 		}
    362 
    363 		msg.InitWrite( buffer, sizeof( buffer ) );
    364 		spectated->WritePlayerStateToSnapshot( msg );
    365 		ss.S_AddObject( SNAP_PLAYERSTATE + i, ~0U, msg, "Player State" );
    366 
    367 		int sourceAreas[ idEntity::MAX_PVS_AREAS ];
    368 		int numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS );
    369 		pvsHandles[i] = pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL );
    370 		if ( portalSkyPVS.i >= 0 ) {
    371 			pvsHandle_t	tempPVS = pvs.MergeCurrentPVS( pvsHandles[i], portalSkyPVS );
    372 			pvs.FreeCurrentPVS( pvsHandles[i] );
    373 			pvsHandles[i] = tempPVS;
    374 		}
    375 
    376 		// Write the last usercmd processed by the server so that clients know
    377 		// when to stop predicting.
    378 		msg.BeginWriting();
    379 		msg.WriteLong( usercmdLastClientMilliseconds[i] );
    380 		ss.S_AddObject( SNAP_LAST_CLIENT_FRAME + i, ~0U, msg, "Last client frame" );
    381 	}
    382 
    383 	if ( portalSkyPVS.i >= 0 ) {
    384 		pvs.FreeCurrentPVS( portalSkyPVS );
    385 	}
    386 
    387 	// Add all entities to the snapshot
    388 	for ( idEntity * ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
    389 		if ( ent->GetSkipReplication() ) {
    390 			continue;
    391 		}
    392 
    393 		msg.InitWrite( buffer, sizeof( buffer ) );
    394 		msg.WriteBits( spawnIds[ ent->entityNumber ], 32 - GENTITYNUM_BITS );
    395 		msg.WriteBits( ent->GetType()->typeNum, idClass::GetTypeNumBits() );
    396 		msg.WriteBits( ServerRemapDecl( -1, DECL_ENTITYDEF, ent->entityDefNumber ), entityDefBits );
    397 		
    398 		msg.WriteBits( ent->GetPredictedKey(), 32 );
    399 
    400 		if ( ent->fl.networkSync ) {
    401 			// write the class specific data to the snapshot
    402 			ent->WriteToSnapshot( msg );
    403 		}
    404 
    405 		ss.S_AddObject( SNAP_ENTITIES + ent->entityNumber, ~0U, msg, ent->GetName() );
    406 	}
    407 
    408 	// Free PVS handles for all the players
    409 	for ( int i = 0; i < MAX_PLAYERS; i++ ) {
    410 		if ( pvsHandles[i].i < 0 ) {
    411 			continue;
    412 		}
    413 		pvs.FreeCurrentPVS( pvsHandles[i] );
    414 	}
    415 }
    416 
    417 /*
    418 ================
    419 idGameLocal::NetworkEventWarning
    420 ================
    421 */
    422 void idGameLocal::NetworkEventWarning( const entityNetEvent_t *event, const char *fmt, ... ) {
    423 	char buf[1024];
    424 	int length = 0;
    425 	va_list argptr;
    426 
    427 	int entityNum	= event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 );
    428 	int id			= event->spawnId >> GENTITYNUM_BITS;
    429 
    430 	length += idStr::snPrintf( buf+length, sizeof(buf)-1-length, "event %d for entity %d %d: ", event->event, entityNum, id );
    431 	va_start( argptr, fmt );
    432 	length = idStr::vsnPrintf( buf+length, sizeof(buf)-1-length, fmt, argptr );
    433 	va_end( argptr );
    434 	idStr::Append( buf, sizeof(buf), "\n" );
    435 
    436 	common->DWarning( buf );
    437 }
    438 
    439 /*
    440 ================
    441 idGameLocal::ServerProcessEntityNetworkEventQueue
    442 ================
    443 */
    444 void idGameLocal::ServerProcessEntityNetworkEventQueue() {
    445 	while ( eventQueue.Start() ) {
    446 		entityNetEvent_t * event = eventQueue.Start();
    447 
    448 		if ( event->time > time ) {
    449 			break;
    450 		}
    451 
    452 		idEntityPtr< idEntity > entPtr;
    453 			
    454 		if( !entPtr.SetSpawnId( event->spawnId ) ) {
    455 			NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
    456 		} else {
    457 			idEntity * ent = entPtr.GetEntity();
    458 			assert( ent );
    459 
    460 			idBitMsg eventMsg;
    461 			eventMsg.InitRead( event->paramsBuf, sizeof( event->paramsBuf ) );
    462 			eventMsg.SetSize( event->paramsSize );
    463 			eventMsg.BeginReading();
    464 			if ( !ent->ServerReceiveEvent( event->event, event->time, eventMsg ) ) {
    465 				NetworkEventWarning( event, "unknown event" );
    466 			}
    467 		}
    468 
    469 		entityNetEvent_t* freedEvent = eventQueue.Dequeue();
    470 		verify( freedEvent == event );
    471 		eventQueue.Free( event );
    472 	}
    473 }
    474 
    475 /*
    476 ================
    477 idGameLocal::ProcessReliableMessage
    478 ================
    479 */
    480 void idGameLocal::ProcessReliableMessage( int clientNum, int type, const idBitMsg &msg ) {
    481 	if ( session->GetActingGameStateLobbyBase().IsPeer() ) {
    482 		ClientProcessReliableMessage( type, msg );
    483 	} else {
    484 		ServerProcessReliableMessage( clientNum, type, msg );
    485 	}
    486 }
    487 
    488 /*
    489 ================
    490 idGameLocal::ServerProcessReliableMessage
    491 ================
    492 */
    493 void idGameLocal::ServerProcessReliableMessage( int clientNum, int type, const idBitMsg &msg ) {
    494 	if ( clientNum < 0 ) {
    495 		return;
    496 	}
    497 	switch( type ) {
    498 		case GAME_RELIABLE_MESSAGE_CHAT:
    499 		case GAME_RELIABLE_MESSAGE_TCHAT: {
    500 			char name[128];
    501 			char text[128];
    502 
    503 			msg.ReadString( name, sizeof( name ) );
    504 			msg.ReadString( text, sizeof( text ) );
    505 
    506 			mpGame.ProcessChatMessage( clientNum, type == GAME_RELIABLE_MESSAGE_TCHAT, name, text, NULL );
    507 			break;
    508 		}
    509 		case GAME_RELIABLE_MESSAGE_VCHAT: {
    510 			int index = msg.ReadLong();
    511 			bool team = msg.ReadBits( 1 ) != 0;
    512 			mpGame.ProcessVoiceChat( clientNum, team, index );
    513 			break;
    514 		}
    515 		case GAME_RELIABLE_MESSAGE_DROPWEAPON: {
    516 			mpGame.DropWeapon( clientNum );
    517 			break;
    518 		}
    519 		case GAME_RELIABLE_MESSAGE_EVENT: {
    520 			// allocate new event
    521 			entityNetEvent_t * event = eventQueue.Alloc();
    522 			eventQueue.Enqueue( event, idEventQueue::OUTOFORDER_DROP );
    523 
    524 			event->spawnId = msg.ReadBits( 32 );
    525 			event->event = msg.ReadByte();
    526 			event->time = msg.ReadLong();
    527 
    528 			event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
    529 			if ( event->paramsSize ) {
    530 				if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
    531 					NetworkEventWarning( event, "invalid param size" );
    532 					return;
    533 				}
    534 				msg.ReadByteAlign();
    535 				msg.ReadData( event->paramsBuf, event->paramsSize );
    536 			}
    537 			break;
    538 		}
    539 		case GAME_RELIABLE_MESSAGE_SPECTATE: {
    540 			bool spec = msg.ReadBool();
    541 			idPlayer * player = GetClientByNum( clientNum );
    542 			if ( serverInfo.GetBool( "si_spectators" ) ) {
    543 				// never let spectators go back to game while sudden death is on
    544 				if ( mpGame.GetGameState() == idMultiplayerGame::SUDDENDEATH && !spec && player->wantSpectate ) {
    545 					// Don't allow the change
    546 				} else {
    547 					if ( player->wantSpectate && !spec ) {
    548 						player->forceRespawn = true;
    549 					}
    550 					player->wantSpectate = spec;
    551 				}
    552 			} else {
    553 				// If the server turned off si_spectators while a player is spectating, then any spectate message forces the player out of spectate mode
    554 				if ( player->wantSpectate ) {
    555 					player->forceRespawn = true;
    556 				}
    557 				player->wantSpectate = false;
    558 			}
    559 			break;
    560 		}
    561 		case GAME_RELIABLE_MESSAGE_CLIENT_HITSCAN_HIT: {
    562 			const int attackerNum = msg.ReadShort();
    563 			const int victimNum = msg.ReadShort();
    564 			idVec3 dir;
    565 			msg.ReadVectorFloat( dir );
    566 			const int damageDefIndex = msg.ReadLong();
    567 			const float damageScale = msg.ReadFloat();
    568 			const int location = msg.ReadLong();
    569 
    570 			if ( gameLocal.entities[victimNum] == NULL ) {
    571 				break;
    572 			}
    573 
    574 			if ( gameLocal.entities[attackerNum] == NULL ) {
    575 				break;
    576 			}
    577 
    578 			idPlayer & victim = static_cast< idPlayer & >( *gameLocal.entities[victimNum] );
    579 			idPlayer & attacker = static_cast< idPlayer & >( *gameLocal.entities[attackerNum] );
    580 			
    581 			if ( victim.GetPhysics() == NULL ) {
    582 				break;
    583 			}
    584 
    585 			if ( attacker.weapon.GetEntity() == NULL ) {
    586 				break;
    587 			}
    588 
    589 			if ( location == INVALID_JOINT ) {
    590 				break;
    591 			}
    592 
    593 			// Line of sight check. As a basic precaution against cheating,
    594 			// the server performs a ray intersection from the client's position
    595 			// to the joint he hit on the target.
    596 			idVec3 muzzleOrigin;
    597 			idMat3 muzzleAxis;
    598 
    599 			attacker.weapon.GetEntity()->GetProjectileLaunchOriginAndAxis( muzzleOrigin, muzzleAxis );
    600 
    601 			idVec3 targetLocation = victim.GetRenderEntity()->origin + victim.GetRenderEntity()->joints[location].ToVec3() * victim.GetRenderEntity()->axis;
    602 
    603 			trace_t tr;
    604 			gameLocal.clip.Translation( tr, muzzleOrigin, targetLocation, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, &attacker );
    605 			
    606 			idEntity * hitEnt = gameLocal.entities[ tr.c.entityNum ];
    607 			if ( hitEnt != &victim ) {
    608 				break;
    609 			}
    610 			const idDeclEntityDef *damageDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, damageDefIndex, false ) );
    611 			
    612 			if ( damageDef != NULL ) {
    613 				victim.Damage( NULL, gameLocal.entities[attackerNum], dir, damageDef->GetName(), damageScale, location );
    614 			}
    615 			break;
    616 		}
    617 		default: {
    618 			Warning( "Unknown reliable message (%d) from client %d", type, clientNum );
    619 			break;
    620 		}
    621 	}
    622 }
    623 
    624 /*
    625 ================
    626 idGameLocal::ClientReadSnapshot
    627 ================
    628 */
    629 void idGameLocal::ClientReadSnapshot( const idSnapShot & ss ) {
    630 	if ( GetLocalClientNum() < 0 ) {
    631 		return;
    632 	}
    633 
    634 	// if prediction is off, enable local client smoothing
    635 	//localPlayer->SetSelfSmooth( dupeUsercmds > 2 );
    636 
    637 	// clear any debug lines from a previous frame
    638 	gameRenderWorld->DebugClearLines( time );
    639 
    640 	// clear any debug polygons from a previous frame
    641 	gameRenderWorld->DebugClearPolygons( time );
    642 
    643 	SelectTimeGroup( false );
    644 
    645 	// so that StartSound/StopSound doesn't risk skipping
    646 	isNewFrame = true;
    647 
    648 	// clear the snapshot entity list
    649 	snapshotEntities.Clear();
    650 
    651 	// read all entities from the snapshot
    652 	for ( int o = 0; o < ss.NumObjects(); o++ ) {
    653 		idBitMsg msg;
    654 		int snapObjectNum = ss.GetObjectMsgByIndex( o, msg );
    655 		if ( snapObjectNum < 0 ) {
    656 			assert( false );
    657 			continue;
    658 		}
    659 		if ( snapObjectNum == SNAP_GAMESTATE ) {
    660 			mpGame.ReadFromSnapshot( msg );
    661 			continue;
    662 		}
    663 		if ( snapObjectNum == SNAP_SHADERPARMS ) {
    664 			for ( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
    665 				globalShaderParms[i] = msg.ReadFloat();
    666 			}
    667 			continue;
    668 		}
    669 		if ( snapObjectNum == SNAP_PORTALS ) {
    670 			// update portals for opened doors
    671 			int numPortals = msg.ReadLong();
    672 			assert( numPortals == gameRenderWorld->NumPortals() );
    673 			for ( int i = 0; i < numPortals; i++ ) {
    674 				gameRenderWorld->SetPortalState( (qhandle_t) (i+1), msg.ReadBits( NUM_RENDER_PORTAL_BITS ) );
    675 			}
    676 			continue;
    677 		}
    678 		if ( snapObjectNum >= SNAP_PLAYERSTATE && snapObjectNum < SNAP_PLAYERSTATE_END ) {
    679 			int playerNumber = snapObjectNum - SNAP_PLAYERSTATE;
    680 			idPlayer * otherPlayer = static_cast< idPlayer * >( entities[ playerNumber ] );
    681 
    682 			// Don't process Player Snapshots that are disconnected.
    683 			const int lobbyIndex = session->GetActingGameStateLobbyBase().GetLobbyUserIndexFromLobbyUserID( lobbyUserIDs[ playerNumber ] );
    684 			if( lobbyIndex < 0 || session->GetActingGameStateLobbyBase().IsLobbyUserConnected( lobbyIndex ) == false ) {
    685 				continue;
    686 			}
    687 
    688 			if ( otherPlayer != NULL ) {
    689 				otherPlayer->ReadPlayerStateFromSnapshot( msg );
    690 				if ( otherPlayer != entities[ GetLocalClientNum() ] ) { // This happens when we spectate another player
    691 					idWeapon * weap = otherPlayer->weapon.GetEntity();
    692 					if ( weap && ( weap->GetRenderEntity()->bounds[0] == weap->GetRenderEntity()->bounds[1] ) ) {
    693 						// update the weapon's viewmodel bounds so that the model doesn't flicker in the spectator's view
    694 						weap->GetAnimator()->GetBounds( gameLocal.time, weap->GetRenderEntity()->bounds );
    695 						weap->UpdateVisuals();
    696 					}
    697 				}
    698 			}
    699 			continue;
    700 		}
    701 		if ( snapObjectNum >= SNAP_LAST_CLIENT_FRAME && snapObjectNum < SNAP_LAST_CLIENT_FRAME_END ) {
    702 			int playerNumber = snapObjectNum - SNAP_LAST_CLIENT_FRAME;
    703 
    704 			// Don't process Player Snapshots that are disconnected.
    705 			const int lobbyIndex = session->GetActingGameStateLobbyBase().GetLobbyUserIndexFromLobbyUserID( lobbyUserIDs[ playerNumber ] );
    706 			if( lobbyIndex < 0 || session->GetActingGameStateLobbyBase().IsLobbyUserConnected( lobbyIndex ) == false ) {
    707 				continue;
    708 			}
    709 
    710 			usercmdLastClientMilliseconds[playerNumber] = msg.ReadLong();
    711 			continue;
    712 		}
    713 		if ( snapObjectNum < SNAP_ENTITIES || snapObjectNum >= SNAP_ENTITIES_END ) {
    714 			continue;
    715 		}
    716 
    717 		int entityNumber = snapObjectNum - SNAP_ENTITIES;
    718 
    719 		if ( msg.GetSize() == 0 ) {
    720 			delete entities[entityNumber];
    721 			continue;
    722 		}
    723 
    724 		bool debug = false;
    725 
    726 		int spawnId = msg.ReadBits( 32 - GENTITYNUM_BITS );
    727 		int typeNum = msg.ReadBits( idClass::GetTypeNumBits() );
    728 		int entityDefNumber = ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( entityDefBits ) );
    729 		const int predictedKey = msg.ReadBits( 32 );
    730 
    731 		idTypeInfo * typeInfo = idClass::GetType( typeNum );
    732 		if ( !typeInfo ) {
    733 			idLib::Error( "Unknown type number %d for entity %d with class number %d", typeNum, entityNumber, entityDefNumber );
    734 		}
    735 
    736 		// If there is no entity on this client, but the server's entity matches a predictionKey, move the client's
    737 		// predicted entity to the normal, replicated area in the entity list.
    738 		if ( entities[entityNumber] == NULL ) {
    739 			if ( predictedKey != idEntity::INVALID_PREDICTION_KEY ) {
    740 				idLib::PrintfIf( debug, "Looking for predicted key %d.\n", predictedKey );
    741 				idEntity * predictedEntity = FindPredictedEntity( predictedKey, typeInfo );
    742 
    743 				if ( predictedEntity != NULL ) {
    744 					// This presentable better be in the proper place in the list or bad things will happen if we move this presentable around
    745 					assert( predictedEntity->GetEntityNumber() >= ENTITYNUM_FIRST_NON_REPLICATED );
    746 					continue;
    747 #if 0
    748 					idProjectile * predictedProjectile = idProjectile::CastTo( predictedEntity );
    749 					if ( predictedProjectile != NULL ) {
    750 						for ( int i = 0; i < MAX_PLAYERS; i++ ) {
    751 							if ( entities[i] == NULL ) {
    752 								continue;
    753 							}
    754 							idPlayer * player = idPlayer::CastTo( entities[i] );
    755 							if ( player != NULL ) {
    756 								if ( player->GetUniqueProjectile() == predictedProjectile ) {
    757 									// Set new spawn id
    758 									player->TrackUniqueProjectile( predictedProjectile );
    759 								}
    760 							}
    761 						}
    762 					}
    763 
    764 					idLib::PrintfIf( debug, "Found predicted EntNum old:%i new:%i spawnID:%i\n", predictedEntity->GetEntityNumber(), entityNumber, spawnId >> GENTITYNUM_BITS );
    765 
    766 					// move the entity
    767 					RemoveEntityFromHash( predictedEntity->name.c_str(), predictedEntity );
    768 					UnregisterEntity( predictedEntity );
    769 					assert( entities[predictedEntity->GetEntityNumber()] == NULL );
    770 					predictedEntity->spawnArgs.SetInt( "spawn_entnum", entityNumber );
    771 					RegisterEntity( predictedEntity, spawnId, predictedEntity->spawnArgs );
    772 					predictedEntity->SetName( "" );
    773 
    774 					// now mark us as no longer predicted
    775 					predictedEntity->BecomeReplicated();
    776 #endif
    777 				} 
    778 				//TODO make this work with non-client preditced entities
    779 				/* else {
    780 					idLib::Warning( "Could not find predicted entity - key: %d. EntityIndex: %d", predictedKey, entityNum );
    781 				} */
    782 			}
    783 		}
    784 
    785 		idEntity * ent = entities[entityNumber];
    786 
    787 		// if there is no entity or an entity of the wrong type
    788 		if ( !ent || ent->GetType()->typeNum != typeNum || ent->entityDefNumber != entityDefNumber || spawnId != spawnIds[ entityNumber ] ) {
    789 			delete ent;
    790 
    791 			spawnCount = spawnId;
    792 
    793 			if ( entityNumber < MAX_CLIENTS ) {
    794 				commonLocal.GetUCmdMgr().ResetPlayer( entityNumber );
    795 				SpawnPlayer( entityNumber );
    796 				ent = entities[ entityNumber ];
    797 				ent->FreeModelDef();
    798 			} else {
    799 				idDict args;
    800 				args.SetInt( "spawn_entnum", entityNumber );
    801 				args.Set( "name", va( "entity%d", entityNumber ) );
    802 
    803 				if ( entityDefNumber >= 0 ) {
    804 					if ( entityDefNumber >= declManager->GetNumDecls( DECL_ENTITYDEF ) ) {
    805 						Error( "server has %d entityDefs instead of %d", entityDefNumber, declManager->GetNumDecls( DECL_ENTITYDEF ) );
    806 					}
    807 					const char * classname = declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
    808 					args.Set( "classname", classname );
    809 					if ( !SpawnEntityDef( args, &ent ) || !entities[entityNumber] || entities[entityNumber]->GetType()->typeNum != typeNum ) {
    810 						Error( "Failed to spawn entity with classname '%s' of type '%s'", classname, typeInfo->classname );
    811 					}
    812 				} else {
    813 					ent = SpawnEntityType( *typeInfo, &args, true );
    814 					if ( !entities[entityNumber] || entities[entityNumber]->GetType()->typeNum != typeNum ) {
    815 						Error( "Failed to spawn entity of type '%s'", typeInfo->classname );
    816 					}
    817 				}
    818 				if ( ent != NULL ) {
    819 					// Fixme: for now, force all think flags on. We'll need to figure out how we want dormancy to work on clients
    820 					// (but for now since clientThink is so light weight, this is ok)
    821 					ent->BecomeActive( TH_ANIMATE );
    822 					ent->BecomeActive( TH_THINK );
    823 					ent->BecomeActive( TH_PHYSICS );
    824 				}
    825 				if ( entityNumber < MAX_CLIENTS && entityNumber >= numClients ) {
    826 					numClients = entityNumber + 1;
    827 				}
    828 			}
    829 		}
    830 
    831 		if ( ss.ObjectIsStaleByIndex( o ) ) {
    832 			if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < mapSpawnCount && !ent->spawnArgs.GetBool("net_dynamic", "0")) { //_D3XP
    833 				// server says it's not in PVS
    834 				// if that happens on map entities, most likely something is wrong
    835 				// I can see that moving pieces along several PVS could be a legit situation though
    836 				// this is a band aid, which means something is not done right elsewhere
    837 				common->DWarning( "map entity 0x%x (%s) is stale", ent->entityNumber, ent->name.c_str() );
    838 			} else {
    839 				ent->snapshotStale = true;
    840 
    841 				ent->FreeModelDef();
    842 				// possible fix for left over lights on CTF flag
    843 				ent->FreeLightDef();
    844 				ent->UpdateVisuals();
    845 				ent->GetPhysics()->UnlinkClip();
    846 			}
    847 		} else {
    848 			// add the entity to the snapshot list
    849 			ent->snapshotNode.AddToEnd( snapshotEntities );
    850 			int snapshotChanged = ss.ObjectChangedCountByIndex( o );
    851 			msg.SetHasChanged( ent->snapshotChanged != snapshotChanged );
    852 			ent->snapshotChanged = snapshotChanged;
    853 
    854 			ent->FlagNewSnapshot();
    855 
    856 			// read the class specific data from the snapshot
    857 			if ( msg.GetRemainingReadBits() > 0 ) {
    858 				ent->ReadFromSnapshot_Ex( msg );
    859 				ent->snapshotBits = msg.GetSize();
    860 			}
    861 
    862 			// Set after ReadFromSnapshot so we can detect coming unstale
    863 			ent->snapshotStale = false;
    864 		}
    865 	}
    866 
    867 	// process entity events
    868 	ClientProcessEntityNetworkEventQueue();
    869 }
    870 
    871 /*
    872 ================
    873 idGameLocal::ClientProcessEntityNetworkEventQueue
    874 ================
    875 */
    876 void idGameLocal::ClientProcessEntityNetworkEventQueue() {
    877 	while( eventQueue.Start() ) {
    878 		entityNetEvent_t * event = eventQueue.Start();
    879 
    880 		// only process forward, in order
    881 		if ( event->time >  this->serverTime ) {
    882 			break;
    883 		}
    884 
    885 		idEntityPtr< idEntity > entPtr;
    886 			
    887 		if( !entPtr.SetSpawnId( event->spawnId ) ) {
    888 			if( !gameLocal.entities[ event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 ) ] ) {
    889 				// if new entity exists in this position, silently ignore
    890 				NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
    891 			}
    892 		} else {
    893 			idEntity * ent = entPtr.GetEntity();
    894 			assert( ent );
    895 
    896 			idBitMsg eventMsg;
    897 			eventMsg.InitRead( event->paramsBuf, sizeof( event->paramsBuf ) );
    898 			eventMsg.SetSize( event->paramsSize );
    899 			eventMsg.BeginReading();
    900 			if ( !ent->ClientReceiveEvent( event->event, event->time, eventMsg ) ) {
    901 				NetworkEventWarning( event, "unknown event" );
    902 			}
    903 		}
    904 
    905 		verify( eventQueue.Dequeue() == event );
    906 		eventQueue.Free( event );
    907 	}
    908 }
    909 
    910 /*
    911 ================
    912 idGameLocal::ClientProcessReliableMessage
    913 ================
    914 */
    915 void idGameLocal::ClientProcessReliableMessage( int type, const idBitMsg &msg ) {
    916 	switch( type ) {
    917 		case GAME_RELIABLE_MESSAGE_SYNCEDCVARS: {
    918 			idDict syncedCvars;
    919 			msg.ReadDeltaDict( syncedCvars, NULL );
    920 
    921 			idLib::Printf( "Got networkSync cvars:\n" );
    922 			syncedCvars.Print();
    923 
    924 			cvarSystem->ResetFlaggedVariables( CVAR_NETWORKSYNC );
    925 			cvarSystem->SetCVarsFromDict( syncedCvars );
    926 			break;
    927 		}
    928 		case GAME_RELIABLE_MESSAGE_CHAT:
    929 		case GAME_RELIABLE_MESSAGE_TCHAT: { // (client should never get a TCHAT though)
    930 			char name[128];
    931 			char text[128];
    932 			msg.ReadString( name, sizeof( name ) );
    933 			msg.ReadString( text, sizeof( text ) );
    934 			mpGame.AddChatLine( "%s^0: %s\n", name, text );
    935 			break;
    936 		}
    937 		case GAME_RELIABLE_MESSAGE_SOUND_EVENT: {
    938 			snd_evt_t snd_evt = (snd_evt_t)msg.ReadByte();
    939 			mpGame.PlayGlobalSound( -1, snd_evt );
    940 			break;
    941 		}
    942 		case GAME_RELIABLE_MESSAGE_SOUND_INDEX: {
    943 			int index = gameLocal.ClientRemapDecl( DECL_SOUND, msg.ReadLong() );
    944 			if ( index >= 0 && index < declManager->GetNumDecls( DECL_SOUND ) ) {
    945 				const idSoundShader *shader = declManager->SoundByIndex( index );
    946 				mpGame.PlayGlobalSound( -1, SND_COUNT, shader->GetName() );
    947 			}
    948 			break;
    949 		}
    950 		case GAME_RELIABLE_MESSAGE_DB: {
    951 			idMultiplayerGame::msg_evt_t msg_evt = (idMultiplayerGame::msg_evt_t)msg.ReadByte();
    952 			int parm1, parm2;
    953 			parm1 = msg.ReadByte( );
    954 			parm2 = msg.ReadByte( );
    955 			mpGame.PrintMessageEvent( msg_evt, parm1, parm2 );
    956 			break;
    957 		}
    958 		case GAME_RELIABLE_MESSAGE_EVENT: {
    959 			// allocate new event
    960 			entityNetEvent_t * event = eventQueue.Alloc();
    961 			eventQueue.Enqueue( event, idEventQueue::OUTOFORDER_IGNORE );
    962 
    963 			event->spawnId = msg.ReadBits( 32 );
    964 			event->event = msg.ReadByte();
    965 			event->time = msg.ReadLong();
    966 
    967 			event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
    968 			if ( event->paramsSize ) {
    969 				if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
    970 					NetworkEventWarning( event, "invalid param size" );
    971 					return;
    972 				}
    973 				msg.ReadByteAlign();
    974 				msg.ReadData( event->paramsBuf, event->paramsSize );
    975 			}
    976 			break;
    977 		}
    978 		case GAME_RELIABLE_MESSAGE_RESTART: {
    979 			MapRestart();
    980 			break;
    981 		}
    982 		case GAME_RELIABLE_MESSAGE_TOURNEYLINE: {
    983 			int line = msg.ReadByte( );
    984 			idPlayer * p = static_cast< idPlayer * >( entities[ GetLocalClientNum() ] );
    985 			if ( !p ) {
    986 				break;
    987 			}
    988 			p->tourneyLine = line;
    989 			break;
    990 		}
    991 		case GAME_RELIABLE_MESSAGE_STARTSTATE: {
    992 			mpGame.ClientReadStartState( msg );
    993 			break;
    994 		}
    995 		case GAME_RELIABLE_MESSAGE_WARMUPTIME: {
    996 			mpGame.ClientReadWarmupTime( msg );
    997 			break;
    998 		}
    999 		case GAME_RELIABLE_MESSAGE_LOBBY_COUNTDOWN: {
   1000 			int timeRemaining = msg.ReadLong();
   1001 			Shell_UpdateClientCountdown( timeRemaining );
   1002 			break;
   1003 		}
   1004 		case GAME_RELIABLE_MESSAGE_RESPAWN_AVAILABLE: {
   1005 			idPlayer * p = static_cast< idPlayer * >( entities[ GetLocalClientNum() ] );
   1006 			if ( p ) {
   1007 				p->ShowRespawnHudMessage();
   1008 			}
   1009 			break;
   1010 		}
   1011 		case GAME_RELIABLE_MESSAGE_MATCH_STARTED_TIME: {
   1012 			mpGame.ClientReadMatchStartedTime( msg );
   1013 			break;
   1014 		}
   1015 		case GAME_RELIABLE_MESSAGE_ACHIEVEMENT_UNLOCK: {
   1016 			mpGame.ClientReadAchievementUnlock( msg );
   1017 			break;
   1018 		}
   1019 		default: {
   1020 			Error( "Unknown reliable message (%d) from host", type );
   1021 			break;
   1022 		}
   1023 	}
   1024 }
   1025 
   1026 /*
   1027 ================
   1028 idGameLocal::ClientRunFrame
   1029 ================
   1030 */
   1031 void idGameLocal::ClientRunFrame( idUserCmdMgr & cmdMgr, bool lastPredictFrame, gameReturn_t & ret ) {
   1032 	idEntity *ent;
   1033 
   1034 	// update the game time
   1035 	previousTime = FRAME_TO_MSEC( framenum );
   1036 	framenum++;
   1037 	time = FRAME_TO_MSEC( framenum );
   1038 
   1039 	idPlayer * player = static_cast<idPlayer *>( entities[GetLocalClientNum()] );
   1040 	if ( !player ) {
   1041 
   1042 		// service any pending events
   1043 		idEvent::ServiceEvents();
   1044 
   1045 		return;
   1046 	}
   1047 
   1048 	// check for local client lag
   1049 	idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
   1050 	if ( lobby.GetPeerTimeSinceLastPacket( lobby.PeerIndexForHost() ) >= net_clientMaxPrediction.GetInteger() ) {
   1051 		player->isLagged = true;
   1052 	} else {
   1053 		player->isLagged = false;
   1054 	}
   1055 
   1056 	// update the real client time and the new frame flag
   1057 	if ( time > realClientTime ) {
   1058 		realClientTime = time;
   1059 		isNewFrame = true;
   1060 	} else {
   1061 		isNewFrame = false;
   1062 	}
   1063 
   1064 	slow.Set( time, previousTime, realClientTime );
   1065 	fast.Set( time, previousTime, realClientTime );
   1066 
   1067 	// run prediction on all active entities
   1068 	for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
   1069 		ent->thinkFlags |= TH_PHYSICS;
   1070 
   1071 		if ( ent->entityNumber != GetLocalClientNum() ) {
   1072 			ent->ClientThink( netInterpolationInfo.serverGameMs, netInterpolationInfo.pct, true );
   1073 		} else {
   1074 			RunAllUserCmdsForPlayer( cmdMgr, ent->entityNumber );
   1075 		}
   1076 	}
   1077 
   1078 	// service any pending events
   1079 	idEvent::ServiceEvents();
   1080 
   1081 	// show any debug info for this frame
   1082 	if ( isNewFrame ) {
   1083 		RunDebugInfo();
   1084 		D_DrawDebugLines();
   1085 	}
   1086 
   1087 	BuildReturnValue( ret );
   1088 }
   1089 
   1090 /*
   1091 ===============
   1092 idGameLocal::Tokenize
   1093 ===============
   1094 */
   1095 void idGameLocal::Tokenize( idStrList &out, const char *in ) {
   1096 	char buf[ MAX_STRING_CHARS ];
   1097 	char *token, *next;
   1098 	
   1099 	idStr::Copynz( buf, in, MAX_STRING_CHARS );
   1100 	token = buf;
   1101 	next = strchr( token, ';' );
   1102 	while ( token ) {
   1103 		if ( next ) {
   1104 			*next = '\0';
   1105 		}
   1106 		idStr::ToLower( token );
   1107 		out.Append( token );
   1108 		if ( next ) {
   1109 			token = next + 1;
   1110 			next = strchr( token, ';' );
   1111 		} else {
   1112 			token = NULL;
   1113 		}		
   1114 	}
   1115 }
   1116 
   1117 /*
   1118 ========================
   1119 idGameLocal::FindPredictedEntity
   1120 ========================
   1121 */
   1122 idEntity *  idGameLocal::FindPredictedEntity( uint32 predictedKey, idTypeInfo * type ) {
   1123 	for ( idEntity * predictedEntity = activeEntities.Next(); predictedEntity != NULL; predictedEntity = predictedEntity->activeNode.Next() ) {
   1124 		if ( !verify( predictedEntity != NULL ) ) {
   1125 			continue;
   1126 		}
   1127 		if ( !predictedEntity->IsReplicated() && predictedEntity->GetPredictedKey() == predictedKey ) {
   1128 			if ( predictedEntity->GetType() != type ) {
   1129 				idLib::Warning("Mismatched presentable type. Predicted: %s Actual: %s", predictedEntity->GetType()->classname, type->classname );
   1130 			}
   1131 			return predictedEntity;
   1132 		}
   1133 	}
   1134 	return NULL;
   1135 }
   1136 
   1137 /*
   1138 ========================
   1139 idGameLocal::GeneratePredictionKey
   1140 ========================
   1141 */
   1142 uint32  idGameLocal::GeneratePredictionKey( idWeapon * weapon, idPlayer * playerAttacker, int overrideKey ) {
   1143 	if ( overrideKey != -1 ) {
   1144 		uint32 predictedKey = overrideKey;
   1145 		int peerIndex		= -1;
   1146 
   1147 		if ( common->IsServer() ) {
   1148 			peerIndex = session->GetActingGameStateLobbyBase().PeerIndexFromLobbyUser( lobbyUserIDs[ playerAttacker->entityNumber ] );
   1149 		} else {
   1150 			peerIndex = session->GetActingGameStateLobbyBase().PeerIndexOnHost();
   1151 		}
   1152 
   1153 		predictedKey |= ( peerIndex << 28 );
   1154 		
   1155 		return predictedKey;
   1156 	}
   1157 
   1158 	uint32 predictedKey = idEntity::INVALID_PREDICTION_KEY;
   1159 	int peerIndex		= -1;
   1160 	
   1161 	// Get key - fireCount or throwCount
   1162 	//if ( weapon != NULL ) {
   1163 	if ( common->IsClient() ) {
   1164 		predictedKey = playerAttacker->GetClientFireCount();
   1165 	} else {
   1166 		predictedKey = playerAttacker->usercmd.fireCount;
   1167 	}
   1168 	//} else {
   1169 	//	predictedKey = ( playerAttacker->GetThrowCount() );
   1170 	//}
   1171 
   1172 	// Get peer index
   1173 	if ( common->IsServer() ) {		
   1174 		peerIndex = session->GetActingGameStateLobbyBase().PeerIndexFromLobbyUser( lobbyUserIDs[ playerAttacker->entityNumber ] );
   1175 	} else {
   1176 		peerIndex = session->GetActingGameStateLobbyBase().PeerIndexOnHost();
   1177 	}
   1178 
   1179 	if ( cg_predictedSpawn_debug.GetBool() ) {
   1180 		idLib::Printf("GeneratePredictionKey. predictedKey: %d peedIndex: %d\n", predictedKey, peerIndex ); 
   1181 	}
   1182 
   1183 	predictedKey |= ( peerIndex << 28 );
   1184 	return predictedKey;
   1185 }
   1186 
   1187 /*
   1188 ===============
   1189 idEventQueue::Alloc
   1190 ===============
   1191 */
   1192 entityNetEvent_t* idEventQueue::Alloc() {
   1193 	entityNetEvent_t* event = eventAllocator.Alloc();
   1194 	event->prev = NULL;
   1195 	event->next = NULL;
   1196 	return event;
   1197 }
   1198 
   1199 /*
   1200 ===============
   1201 idEventQueue::Free
   1202 ===============
   1203 */
   1204 void idEventQueue::Free( entityNetEvent_t *event ) {
   1205 	// should only be called on an unlinked event!
   1206 	assert( !event->next && !event->prev );
   1207 	eventAllocator.Free( event );
   1208 }
   1209 
   1210 /*
   1211 ===============
   1212 idEventQueue::Shutdown
   1213 ===============
   1214 */
   1215 void idEventQueue::Shutdown() {
   1216 	eventAllocator.Shutdown();
   1217 	this->Init();
   1218 }
   1219 
   1220 /*
   1221 ===============
   1222 idEventQueue::Init
   1223 ===============
   1224 */
   1225 void idEventQueue::Init() {
   1226 	start = NULL;
   1227 	end = NULL;
   1228 }
   1229 
   1230 /*
   1231 ===============
   1232 idEventQueue::Dequeue
   1233 ===============
   1234 */
   1235 entityNetEvent_t* idEventQueue::Dequeue() {
   1236 	entityNetEvent_t* event = start;
   1237 	if ( !event ) {
   1238 		return NULL;
   1239 	}
   1240 
   1241 	start = start->next;
   1242 
   1243 	if ( !start ) {
   1244 		end = NULL;
   1245 	} else {
   1246 		start->prev = NULL;
   1247 	}
   1248 
   1249 	event->next = NULL;
   1250 	event->prev = NULL;
   1251 
   1252 	return event;
   1253 }
   1254 
   1255 /*
   1256 ===============
   1257 idEventQueue::RemoveLast
   1258 ===============
   1259 */
   1260 entityNetEvent_t* idEventQueue::RemoveLast() {
   1261 	entityNetEvent_t *event = end;
   1262 	if ( !event ) {
   1263 		return NULL;
   1264 	}
   1265 
   1266 	end = event->prev;
   1267 
   1268 	if ( !end ) {
   1269 		start = NULL;
   1270 	} else {
   1271 		end->next = NULL;		
   1272 	}
   1273 
   1274 	event->next = NULL;
   1275 	event->prev = NULL;
   1276 
   1277 	return event;
   1278 }
   1279 
   1280 /*
   1281 ===============
   1282 idEventQueue::Enqueue
   1283 ===============
   1284 */
   1285 void idEventQueue::Enqueue( entityNetEvent_t *event, outOfOrderBehaviour_t behaviour ) {
   1286 	if ( behaviour == OUTOFORDER_DROP ) {
   1287 		// go backwards through the queue and determine if there are
   1288 		// any out-of-order events
   1289 		while ( end && end->time > event->time ) {
   1290 			entityNetEvent_t *outOfOrder = RemoveLast();
   1291 			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 );
   1292 			Free( outOfOrder );
   1293 		}
   1294 	} else if ( behaviour == OUTOFORDER_SORT && end ) {
   1295 		// NOT TESTED -- sorting out of order packets hasn't been
   1296 		//				 tested yet... wasn't strictly necessary for
   1297 		//				 the patch fix.
   1298 		entityNetEvent_t *cur = end;
   1299 		// iterate until we find a time < the new event's
   1300 		while ( cur && cur->time > event->time ) {
   1301 			cur = cur->prev;
   1302 		}
   1303 		if ( !cur ) {
   1304 			// add to start
   1305 			event->next = start;
   1306 			event->prev = NULL;
   1307 			start = event;
   1308 		} else {
   1309 			// insert
   1310 			event->prev = cur;
   1311 			event->next = cur->next;
   1312 			cur->next = event;
   1313 		}
   1314 		return;
   1315 	} 
   1316 
   1317 	// add the new event
   1318 	event->next = NULL;
   1319 	event->prev = NULL;
   1320 
   1321 	if ( end ) {
   1322 		end->next = event;
   1323 		event->prev = end;
   1324 	} else {
   1325 		start = event;
   1326 	}
   1327 	end = event;
   1328 }