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 }