123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- 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.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Game_local.h"
- #include "..\framework\Common_local.h"
- static const int SNAP_GAMESTATE = 0;
- static const int SNAP_SHADERPARMS = 1;
- static const int SNAP_PORTALS = 2;
- static const int SNAP_PLAYERSTATE = SNAP_PORTALS + 1;
- static const int SNAP_PLAYERSTATE_END = SNAP_PLAYERSTATE + MAX_PLAYERS;
- static const int SNAP_ENTITIES = SNAP_PLAYERSTATE_END;
- static const int SNAP_ENTITIES_END = SNAP_ENTITIES + MAX_GENTITIES;
- static const int SNAP_LAST_CLIENT_FRAME = SNAP_ENTITIES_END;
- static const int SNAP_LAST_CLIENT_FRAME_END = SNAP_LAST_CLIENT_FRAME + MAX_PLAYERS;
- /*
- ===============================================================================
- Client running game code:
- - entity events don't work and should not be issued
- - entities should never be spawned outside idGameLocal::ClientReadSnapshot
- ===============================================================================
- */
- idCVar net_clientSmoothing( "net_clientSmoothing", "0.8", CVAR_GAME | CVAR_FLOAT, "smooth other clients angles and position.", 0.0f, 0.95f );
- idCVar net_clientSelfSmoothing( "net_clientSelfSmoothing", "0.6", CVAR_GAME | CVAR_FLOAT, "smooth self position if network causes prediction error.", 0.0f, 0.95f );
- extern idCVar net_clientMaxPrediction;
- idCVar cg_predictedSpawn_debug( "cg_predictedSpawn_debug", "0", CVAR_BOOL, "Debug predictive spawning of presentables" );
- idCVar g_clientFire_checkLineOfSightDebug( "g_clientFire_checkLineOfSightDebug", "0", CVAR_BOOL, "" );
- /*
- ================
- idGameLocal::InitAsyncNetwork
- ================
- */
- void idGameLocal::InitAsyncNetwork() {
- eventQueue.Init();
- savedEventQueue.Init();
- entityDefBits = -( idMath::BitsForInteger( declManager->GetNumDecls( DECL_ENTITYDEF ) ) + 1 );
- realClientTime = 0;
- fast.Set( 0, 0, 0 );
- slow.Set( 0, 0, 0 );
- isNewFrame = true;
- clientSmoothing = net_clientSmoothing.GetFloat();
- lastCmdRunTimeOnClient.Zero();
- lastCmdRunTimeOnServer.Zero();
- usercmdLastClientMilliseconds.Zero();
- }
- /*
- ================
- idGameLocal::ShutdownAsyncNetwork
- ================
- */
- void idGameLocal::ShutdownAsyncNetwork() {
- eventQueue.Shutdown();
- savedEventQueue.Shutdown();
- }
- /*
- ================
- idGameLocal::ServerRemapDecl
- ================
- */
- int idGameLocal::ServerRemapDecl( int clientNum, declType_t type, int index ) {
- return index;
- }
- /*
- ================
- idGameLocal::ClientRemapDecl
- ================
- */
- int idGameLocal::ClientRemapDecl( declType_t type, int index ) {
- return index;
- }
- /*
- ================
- idGameLocal::SyncPlayersWithLobbyUsers
- ================
- */
- void idGameLocal::SyncPlayersWithLobbyUsers( bool initial ) {
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- if ( !lobby.IsHost() ) {
- return;
- }
- idStaticList< lobbyUserID_t, MAX_CLIENTS > newLobbyUsers;
- // First, loop over lobby users, and see if we find a lobby user that we haven't registered
- for ( int i = 0; i < lobby.GetNumLobbyUsers(); i++ ) {
- lobbyUserID_t lobbyUserID1 = lobby.GetLobbyUserIdByOrdinal( i );
- if ( !lobbyUserID1.IsValid() ) {
- continue;
- }
- if ( !initial && !lobby.IsLobbyUserLoaded( lobbyUserID1 ) ) {
- continue;
- }
- // Now, see if we find this lobby user in our list
- bool found = false;
- for ( int j = 0; j < MAX_PLAYERS; j++ ) {
- idPlayer * player = static_cast<idPlayer *>( entities[ j ] );
- if ( player == NULL ) {
- continue;
- }
-
- lobbyUserID_t lobbyUserID2 = lobbyUserIDs[j];
- if ( lobbyUserID1 == lobbyUserID2 ) {
- found = true;
- break;
- }
- }
- if ( !found ) {
- // If we didn't find it, we need to create a player and assign it to this new lobby user
- newLobbyUsers.Append( lobbyUserID1 );
- }
- }
- // Validate connected players
- for ( int i = 0; i < MAX_PLAYERS; i++ ) {
- idPlayer * player = static_cast<idPlayer *>( entities[ i ] );
- if ( player == NULL ) {
- continue;
- }
-
- lobbyUserID_t lobbyUserID = lobbyUserIDs[i];
-
- if ( !lobby.IsLobbyUserValid( lobbyUserID ) ) {
- delete entities[ i ];
- mpGame.DisconnectClient( i );
- lobbyUserIDs[i] = lobbyUserID_t();
- continue;
- }
- lobby.EnableSnapshotsForLobbyUser( lobbyUserID );
- }
- while ( newLobbyUsers.Num() > 0 ) {
- // Find a free player data slot to use for this new player
- int freePlayerDataIndex = -1;
- for ( int i = 0; i < MAX_PLAYERS; ++i ) {
- idPlayer * player = static_cast<idPlayer *>( entities[ i ] );
- if ( player == NULL ) {
- freePlayerDataIndex = i;
- break;
- }
- }
- if ( freePlayerDataIndex == -1 ) {
- break; // No player data slots (this shouldn't happen)
- }
- lobbyUserID_t lobbyUserID = newLobbyUsers[0];
- newLobbyUsers.RemoveIndex( 0 );
- mpGame.ServerClientConnect( freePlayerDataIndex );
- Printf( "client %d connected.\n", freePlayerDataIndex );
- lobbyUserIDs[ freePlayerDataIndex ] = lobbyUserID;
- // Clear this player's old usercmds.
- common->ResetPlayerInput( freePlayerDataIndex );
- common->UpdateLevelLoadPacifier();
- // spawn the player
- SpawnPlayer( freePlayerDataIndex );
- common->UpdateLevelLoadPacifier();
- ServerWriteInitialReliableMessages( freePlayerDataIndex, lobbyUserID );
- }
- }
- /*
- ================
- idGameLocal::ServerSendNetworkSyncCvars
- ================
- */
- void idGameLocal::ServerSendNetworkSyncCvars() {
- if ( ( cvarSystem->GetModifiedFlags() & CVAR_NETWORKSYNC ) == 0 ) {
- return;
- }
- cvarSystem->ClearModifiedFlags( CVAR_NETWORKSYNC );
- idBitMsg outMsg;
- byte msgBuf[MAX_GAME_MESSAGE_SIZE];
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.BeginWriting();
- idDict syncedCvars;
- cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC, syncedCvars, true );
- outMsg.WriteDeltaDict( syncedCvars, NULL );
- lobby.SendReliable( GAME_RELIABLE_MESSAGE_SYNCEDCVARS, outMsg, false );
- idLib::Printf( "Sending networkSync cvars:\n" );
- syncedCvars.Print();
- }
- /*
- ================
- idGameLocal::ServerWriteInitialReliableMessages
- Send reliable messages to initialize the client game up to a certain initial state.
- ================
- */
- void idGameLocal::ServerWriteInitialReliableMessages( int clientNum, lobbyUserID_t lobbyUserID ) {
- if ( clientNum == GetLocalClientNum() ) {
- // We don't need to send messages to ourself
- return;
- }
- idBitMsg outMsg;
- byte msgBuf[MAX_GAME_MESSAGE_SIZE];
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.BeginWriting();
- idDict syncedCvars;
- cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC, syncedCvars, true );
- outMsg.WriteDeltaDict( syncedCvars, NULL );
- lobby.SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_SYNCEDCVARS, outMsg );
- idLib::Printf( "Sending initial networkSync cvars:\n" );
- syncedCvars.Print();
- // send all saved events
- for ( entityNetEvent_t * event = savedEventQueue.Start(); event; event = event->next ) {
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.BeginWriting();
- outMsg.WriteBits( event->spawnId, 32 );
- outMsg.WriteByte( event->event );
- outMsg.WriteLong( event->time );
- outMsg.WriteBits( event->paramsSize, idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
- if ( event->paramsSize ) {
- outMsg.WriteData( event->paramsBuf, event->paramsSize );
- }
- lobby.SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_EVENT, outMsg );
- }
- mpGame.ServerWriteInitialReliableMessages( clientNum, lobbyUserID );
- }
- /*
- ================
- idGameLocal::SaveEntityNetworkEvent
- ================
- */
- void idGameLocal::SaveEntityNetworkEvent( const idEntity *ent, int eventId, const idBitMsg *msg ) {
- entityNetEvent_t * event = savedEventQueue.Alloc();
- event->spawnId = GetSpawnId( ent );
- event->event = eventId;
- event->time = time;
- if ( msg ) {
- event->paramsSize = msg->GetSize();
- memcpy( event->paramsBuf, msg->GetReadData(), msg->GetSize() );
- } else {
- event->paramsSize = 0;
- }
- savedEventQueue.Enqueue( event, idEventQueue::OUTOFORDER_IGNORE );
- }
- /*
- ================
- idGameLocal::ServerWriteSnapshot
- Write a snapshot of the current game state
- ================
- */
- void idGameLocal::ServerWriteSnapshot( idSnapShot & ss ) {
- ss.SetTime( fast.time );
- byte buffer[ MAX_ENTITY_STATE_SIZE ];
- idBitMsg msg;
- // First write the generic game state to the snapshot
- msg.InitWrite( buffer, sizeof( buffer ) );
- mpGame.WriteToSnapshot( msg );
- ss.S_AddObject( SNAP_GAMESTATE, ~0U, msg, "Game State" );
- // Update global shader parameters
- msg.InitWrite( buffer, sizeof( buffer ) );
- for ( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
- msg.WriteFloat( globalShaderParms[i] );
- }
- ss.S_AddObject( SNAP_SHADERPARMS, ~0U, msg, "Shader Parms" );
- // update portals for opened doors
- msg.InitWrite( buffer, sizeof( buffer ) );
- int numPortals = gameRenderWorld->NumPortals();
- msg.WriteLong( numPortals );
- for ( int i = 0; i < numPortals; i++ ) {
- msg.WriteBits( gameRenderWorld->GetPortalState( (qhandle_t) (i+1) ) , NUM_RENDER_PORTAL_BITS );
- }
- ss.S_AddObject( SNAP_PORTALS, ~0U, msg, "Portal State" );
- idEntity * skyEnt = portalSkyEnt.GetEntity();
- pvsHandle_t portalSkyPVS;
- portalSkyPVS.i = -1;
- if ( skyEnt != NULL ) {
- portalSkyPVS = pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() );
- }
- // Build PVS data for each player and write their player state to the snapshot as well
- pvsHandle_t pvsHandles[ MAX_PLAYERS ];
- for ( int i = 0; i < MAX_PLAYERS; i++ ) {
- idPlayer * player = static_cast<idPlayer *>( entities[ i ] );
- if ( player == NULL ) {
- pvsHandles[i].i = -1;
- continue;
- }
- idPlayer * spectated = player;
- if ( player->spectating && player->spectator != i && entities[ player->spectator ] ) {
- spectated = static_cast< idPlayer * >( entities[ player->spectator ] );
- }
- msg.InitWrite( buffer, sizeof( buffer ) );
- spectated->WritePlayerStateToSnapshot( msg );
- ss.S_AddObject( SNAP_PLAYERSTATE + i, ~0U, msg, "Player State" );
- int sourceAreas[ idEntity::MAX_PVS_AREAS ];
- int numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS );
- pvsHandles[i] = pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL );
- if ( portalSkyPVS.i >= 0 ) {
- pvsHandle_t tempPVS = pvs.MergeCurrentPVS( pvsHandles[i], portalSkyPVS );
- pvs.FreeCurrentPVS( pvsHandles[i] );
- pvsHandles[i] = tempPVS;
- }
- // Write the last usercmd processed by the server so that clients know
- // when to stop predicting.
- msg.BeginWriting();
- msg.WriteLong( usercmdLastClientMilliseconds[i] );
- ss.S_AddObject( SNAP_LAST_CLIENT_FRAME + i, ~0U, msg, "Last client frame" );
- }
- if ( portalSkyPVS.i >= 0 ) {
- pvs.FreeCurrentPVS( portalSkyPVS );
- }
- // Add all entities to the snapshot
- for ( idEntity * ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- if ( ent->GetSkipReplication() ) {
- continue;
- }
- msg.InitWrite( buffer, sizeof( buffer ) );
- msg.WriteBits( spawnIds[ ent->entityNumber ], 32 - GENTITYNUM_BITS );
- msg.WriteBits( ent->GetType()->typeNum, idClass::GetTypeNumBits() );
- msg.WriteBits( ServerRemapDecl( -1, DECL_ENTITYDEF, ent->entityDefNumber ), entityDefBits );
-
- msg.WriteBits( ent->GetPredictedKey(), 32 );
- if ( ent->fl.networkSync ) {
- // write the class specific data to the snapshot
- ent->WriteToSnapshot( msg );
- }
- ss.S_AddObject( SNAP_ENTITIES + ent->entityNumber, ~0U, msg, ent->GetName() );
- }
- // Free PVS handles for all the players
- for ( int i = 0; i < MAX_PLAYERS; i++ ) {
- if ( pvsHandles[i].i < 0 ) {
- continue;
- }
- pvs.FreeCurrentPVS( pvsHandles[i] );
- }
- }
- /*
- ================
- idGameLocal::NetworkEventWarning
- ================
- */
- void idGameLocal::NetworkEventWarning( const entityNetEvent_t *event, const char *fmt, ... ) {
- char buf[1024];
- int length = 0;
- va_list argptr;
- int entityNum = event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 );
- int id = event->spawnId >> GENTITYNUM_BITS;
- length += idStr::snPrintf( buf+length, sizeof(buf)-1-length, "event %d for entity %d %d: ", event->event, entityNum, id );
- va_start( argptr, fmt );
- length = idStr::vsnPrintf( buf+length, sizeof(buf)-1-length, fmt, argptr );
- va_end( argptr );
- idStr::Append( buf, sizeof(buf), "\n" );
- common->DWarning( buf );
- }
- /*
- ================
- idGameLocal::ServerProcessEntityNetworkEventQueue
- ================
- */
- void idGameLocal::ServerProcessEntityNetworkEventQueue() {
- while ( eventQueue.Start() ) {
- entityNetEvent_t * event = eventQueue.Start();
- if ( event->time > time ) {
- break;
- }
- idEntityPtr< idEntity > entPtr;
-
- if( !entPtr.SetSpawnId( event->spawnId ) ) {
- NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
- } else {
- idEntity * ent = entPtr.GetEntity();
- assert( ent );
- idBitMsg eventMsg;
- eventMsg.InitRead( event->paramsBuf, sizeof( event->paramsBuf ) );
- eventMsg.SetSize( event->paramsSize );
- eventMsg.BeginReading();
- if ( !ent->ServerReceiveEvent( event->event, event->time, eventMsg ) ) {
- NetworkEventWarning( event, "unknown event" );
- }
- }
- entityNetEvent_t* freedEvent = eventQueue.Dequeue();
- verify( freedEvent == event );
- eventQueue.Free( event );
- }
- }
- /*
- ================
- idGameLocal::ProcessReliableMessage
- ================
- */
- void idGameLocal::ProcessReliableMessage( int clientNum, int type, const idBitMsg &msg ) {
- if ( session->GetActingGameStateLobbyBase().IsPeer() ) {
- ClientProcessReliableMessage( type, msg );
- } else {
- ServerProcessReliableMessage( clientNum, type, msg );
- }
- }
- /*
- ================
- idGameLocal::ServerProcessReliableMessage
- ================
- */
- void idGameLocal::ServerProcessReliableMessage( int clientNum, int type, const idBitMsg &msg ) {
- if ( clientNum < 0 ) {
- return;
- }
- switch( type ) {
- case GAME_RELIABLE_MESSAGE_CHAT:
- case GAME_RELIABLE_MESSAGE_TCHAT: {
- char name[128];
- char text[128];
- msg.ReadString( name, sizeof( name ) );
- msg.ReadString( text, sizeof( text ) );
- mpGame.ProcessChatMessage( clientNum, type == GAME_RELIABLE_MESSAGE_TCHAT, name, text, NULL );
- break;
- }
- case GAME_RELIABLE_MESSAGE_VCHAT: {
- int index = msg.ReadLong();
- bool team = msg.ReadBits( 1 ) != 0;
- mpGame.ProcessVoiceChat( clientNum, team, index );
- break;
- }
- case GAME_RELIABLE_MESSAGE_DROPWEAPON: {
- mpGame.DropWeapon( clientNum );
- break;
- }
- case GAME_RELIABLE_MESSAGE_EVENT: {
- // allocate new event
- entityNetEvent_t * event = eventQueue.Alloc();
- eventQueue.Enqueue( event, idEventQueue::OUTOFORDER_DROP );
- event->spawnId = msg.ReadBits( 32 );
- event->event = msg.ReadByte();
- event->time = msg.ReadLong();
- event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
- if ( event->paramsSize ) {
- if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
- NetworkEventWarning( event, "invalid param size" );
- return;
- }
- msg.ReadByteAlign();
- msg.ReadData( event->paramsBuf, event->paramsSize );
- }
- break;
- }
- case GAME_RELIABLE_MESSAGE_SPECTATE: {
- bool spec = msg.ReadBool();
- idPlayer * player = GetClientByNum( clientNum );
- if ( serverInfo.GetBool( "si_spectators" ) ) {
- // never let spectators go back to game while sudden death is on
- if ( mpGame.GetGameState() == idMultiplayerGame::SUDDENDEATH && !spec && player->wantSpectate ) {
- // Don't allow the change
- } else {
- if ( player->wantSpectate && !spec ) {
- player->forceRespawn = true;
- }
- player->wantSpectate = spec;
- }
- } else {
- // If the server turned off si_spectators while a player is spectating, then any spectate message forces the player out of spectate mode
- if ( player->wantSpectate ) {
- player->forceRespawn = true;
- }
- player->wantSpectate = false;
- }
- break;
- }
- case GAME_RELIABLE_MESSAGE_CLIENT_HITSCAN_HIT: {
- const int attackerNum = msg.ReadShort();
- const int victimNum = msg.ReadShort();
- idVec3 dir;
- msg.ReadVectorFloat( dir );
- const int damageDefIndex = msg.ReadLong();
- const float damageScale = msg.ReadFloat();
- const int location = msg.ReadLong();
- if ( gameLocal.entities[victimNum] == NULL ) {
- break;
- }
- if ( gameLocal.entities[attackerNum] == NULL ) {
- break;
- }
- idPlayer & victim = static_cast< idPlayer & >( *gameLocal.entities[victimNum] );
- idPlayer & attacker = static_cast< idPlayer & >( *gameLocal.entities[attackerNum] );
-
- if ( victim.GetPhysics() == NULL ) {
- break;
- }
- if ( attacker.weapon.GetEntity() == NULL ) {
- break;
- }
- if ( location == INVALID_JOINT ) {
- break;
- }
- // Line of sight check. As a basic precaution against cheating,
- // the server performs a ray intersection from the client's position
- // to the joint he hit on the target.
- idVec3 muzzleOrigin;
- idMat3 muzzleAxis;
- attacker.weapon.GetEntity()->GetProjectileLaunchOriginAndAxis( muzzleOrigin, muzzleAxis );
- idVec3 targetLocation = victim.GetRenderEntity()->origin + victim.GetRenderEntity()->joints[location].ToVec3() * victim.GetRenderEntity()->axis;
- trace_t tr;
- gameLocal.clip.Translation( tr, muzzleOrigin, targetLocation, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, &attacker );
-
- idEntity * hitEnt = gameLocal.entities[ tr.c.entityNum ];
- if ( hitEnt != &victim ) {
- break;
- }
- const idDeclEntityDef *damageDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, damageDefIndex, false ) );
-
- if ( damageDef != NULL ) {
- victim.Damage( NULL, gameLocal.entities[attackerNum], dir, damageDef->GetName(), damageScale, location );
- }
- break;
- }
- default: {
- Warning( "Unknown reliable message (%d) from client %d", type, clientNum );
- break;
- }
- }
- }
- /*
- ================
- idGameLocal::ClientReadSnapshot
- ================
- */
- void idGameLocal::ClientReadSnapshot( const idSnapShot & ss ) {
- if ( GetLocalClientNum() < 0 ) {
- return;
- }
- // if prediction is off, enable local client smoothing
- //localPlayer->SetSelfSmooth( dupeUsercmds > 2 );
- // clear any debug lines from a previous frame
- gameRenderWorld->DebugClearLines( time );
- // clear any debug polygons from a previous frame
- gameRenderWorld->DebugClearPolygons( time );
- SelectTimeGroup( false );
- // so that StartSound/StopSound doesn't risk skipping
- isNewFrame = true;
- // clear the snapshot entity list
- snapshotEntities.Clear();
- // read all entities from the snapshot
- for ( int o = 0; o < ss.NumObjects(); o++ ) {
- idBitMsg msg;
- int snapObjectNum = ss.GetObjectMsgByIndex( o, msg );
- if ( snapObjectNum < 0 ) {
- assert( false );
- continue;
- }
- if ( snapObjectNum == SNAP_GAMESTATE ) {
- mpGame.ReadFromSnapshot( msg );
- continue;
- }
- if ( snapObjectNum == SNAP_SHADERPARMS ) {
- for ( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
- globalShaderParms[i] = msg.ReadFloat();
- }
- continue;
- }
- if ( snapObjectNum == SNAP_PORTALS ) {
- // update portals for opened doors
- int numPortals = msg.ReadLong();
- assert( numPortals == gameRenderWorld->NumPortals() );
- for ( int i = 0; i < numPortals; i++ ) {
- gameRenderWorld->SetPortalState( (qhandle_t) (i+1), msg.ReadBits( NUM_RENDER_PORTAL_BITS ) );
- }
- continue;
- }
- if ( snapObjectNum >= SNAP_PLAYERSTATE && snapObjectNum < SNAP_PLAYERSTATE_END ) {
- int playerNumber = snapObjectNum - SNAP_PLAYERSTATE;
- idPlayer * otherPlayer = static_cast< idPlayer * >( entities[ playerNumber ] );
- // Don't process Player Snapshots that are disconnected.
- const int lobbyIndex = session->GetActingGameStateLobbyBase().GetLobbyUserIndexFromLobbyUserID( lobbyUserIDs[ playerNumber ] );
- if( lobbyIndex < 0 || session->GetActingGameStateLobbyBase().IsLobbyUserConnected( lobbyIndex ) == false ) {
- continue;
- }
- if ( otherPlayer != NULL ) {
- otherPlayer->ReadPlayerStateFromSnapshot( msg );
- if ( otherPlayer != entities[ GetLocalClientNum() ] ) { // This happens when we spectate another player
- idWeapon * weap = otherPlayer->weapon.GetEntity();
- if ( weap && ( weap->GetRenderEntity()->bounds[0] == weap->GetRenderEntity()->bounds[1] ) ) {
- // update the weapon's viewmodel bounds so that the model doesn't flicker in the spectator's view
- weap->GetAnimator()->GetBounds( gameLocal.time, weap->GetRenderEntity()->bounds );
- weap->UpdateVisuals();
- }
- }
- }
- continue;
- }
- if ( snapObjectNum >= SNAP_LAST_CLIENT_FRAME && snapObjectNum < SNAP_LAST_CLIENT_FRAME_END ) {
- int playerNumber = snapObjectNum - SNAP_LAST_CLIENT_FRAME;
- // Don't process Player Snapshots that are disconnected.
- const int lobbyIndex = session->GetActingGameStateLobbyBase().GetLobbyUserIndexFromLobbyUserID( lobbyUserIDs[ playerNumber ] );
- if( lobbyIndex < 0 || session->GetActingGameStateLobbyBase().IsLobbyUserConnected( lobbyIndex ) == false ) {
- continue;
- }
- usercmdLastClientMilliseconds[playerNumber] = msg.ReadLong();
- continue;
- }
- if ( snapObjectNum < SNAP_ENTITIES || snapObjectNum >= SNAP_ENTITIES_END ) {
- continue;
- }
- int entityNumber = snapObjectNum - SNAP_ENTITIES;
- if ( msg.GetSize() == 0 ) {
- delete entities[entityNumber];
- continue;
- }
- bool debug = false;
- int spawnId = msg.ReadBits( 32 - GENTITYNUM_BITS );
- int typeNum = msg.ReadBits( idClass::GetTypeNumBits() );
- int entityDefNumber = ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( entityDefBits ) );
- const int predictedKey = msg.ReadBits( 32 );
- idTypeInfo * typeInfo = idClass::GetType( typeNum );
- if ( !typeInfo ) {
- idLib::Error( "Unknown type number %d for entity %d with class number %d", typeNum, entityNumber, entityDefNumber );
- }
- // If there is no entity on this client, but the server's entity matches a predictionKey, move the client's
- // predicted entity to the normal, replicated area in the entity list.
- if ( entities[entityNumber] == NULL ) {
- if ( predictedKey != idEntity::INVALID_PREDICTION_KEY ) {
- idLib::PrintfIf( debug, "Looking for predicted key %d.\n", predictedKey );
- idEntity * predictedEntity = FindPredictedEntity( predictedKey, typeInfo );
- if ( predictedEntity != NULL ) {
- // This presentable better be in the proper place in the list or bad things will happen if we move this presentable around
- assert( predictedEntity->GetEntityNumber() >= ENTITYNUM_FIRST_NON_REPLICATED );
- continue;
- #if 0
- idProjectile * predictedProjectile = idProjectile::CastTo( predictedEntity );
- if ( predictedProjectile != NULL ) {
- for ( int i = 0; i < MAX_PLAYERS; i++ ) {
- if ( entities[i] == NULL ) {
- continue;
- }
- idPlayer * player = idPlayer::CastTo( entities[i] );
- if ( player != NULL ) {
- if ( player->GetUniqueProjectile() == predictedProjectile ) {
- // Set new spawn id
- player->TrackUniqueProjectile( predictedProjectile );
- }
- }
- }
- }
- idLib::PrintfIf( debug, "Found predicted EntNum old:%i new:%i spawnID:%i\n", predictedEntity->GetEntityNumber(), entityNumber, spawnId >> GENTITYNUM_BITS );
- // move the entity
- RemoveEntityFromHash( predictedEntity->name.c_str(), predictedEntity );
- UnregisterEntity( predictedEntity );
- assert( entities[predictedEntity->GetEntityNumber()] == NULL );
- predictedEntity->spawnArgs.SetInt( "spawn_entnum", entityNumber );
- RegisterEntity( predictedEntity, spawnId, predictedEntity->spawnArgs );
- predictedEntity->SetName( "" );
- // now mark us as no longer predicted
- predictedEntity->BecomeReplicated();
- #endif
- }
- //TODO make this work with non-client preditced entities
- /* else {
- idLib::Warning( "Could not find predicted entity - key: %d. EntityIndex: %d", predictedKey, entityNum );
- } */
- }
- }
- idEntity * ent = entities[entityNumber];
- // if there is no entity or an entity of the wrong type
- if ( !ent || ent->GetType()->typeNum != typeNum || ent->entityDefNumber != entityDefNumber || spawnId != spawnIds[ entityNumber ] ) {
- delete ent;
- spawnCount = spawnId;
- if ( entityNumber < MAX_CLIENTS ) {
- commonLocal.GetUCmdMgr().ResetPlayer( entityNumber );
- SpawnPlayer( entityNumber );
- ent = entities[ entityNumber ];
- ent->FreeModelDef();
- } else {
- idDict args;
- args.SetInt( "spawn_entnum", entityNumber );
- args.Set( "name", va( "entity%d", entityNumber ) );
- if ( entityDefNumber >= 0 ) {
- if ( entityDefNumber >= declManager->GetNumDecls( DECL_ENTITYDEF ) ) {
- Error( "server has %d entityDefs instead of %d", entityDefNumber, declManager->GetNumDecls( DECL_ENTITYDEF ) );
- }
- const char * classname = declManager->DeclByIndex( DECL_ENTITYDEF, entityDefNumber, false )->GetName();
- args.Set( "classname", classname );
- if ( !SpawnEntityDef( args, &ent ) || !entities[entityNumber] || entities[entityNumber]->GetType()->typeNum != typeNum ) {
- Error( "Failed to spawn entity with classname '%s' of type '%s'", classname, typeInfo->classname );
- }
- } else {
- ent = SpawnEntityType( *typeInfo, &args, true );
- if ( !entities[entityNumber] || entities[entityNumber]->GetType()->typeNum != typeNum ) {
- Error( "Failed to spawn entity of type '%s'", typeInfo->classname );
- }
- }
- if ( ent != NULL ) {
- // Fixme: for now, force all think flags on. We'll need to figure out how we want dormancy to work on clients
- // (but for now since clientThink is so light weight, this is ok)
- ent->BecomeActive( TH_ANIMATE );
- ent->BecomeActive( TH_THINK );
- ent->BecomeActive( TH_PHYSICS );
- }
- if ( entityNumber < MAX_CLIENTS && entityNumber >= numClients ) {
- numClients = entityNumber + 1;
- }
- }
- }
- if ( ss.ObjectIsStaleByIndex( o ) ) {
- if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < mapSpawnCount && !ent->spawnArgs.GetBool("net_dynamic", "0")) { //_D3XP
- // server says it's not in PVS
- // if that happens on map entities, most likely something is wrong
- // I can see that moving pieces along several PVS could be a legit situation though
- // this is a band aid, which means something is not done right elsewhere
- common->DWarning( "map entity 0x%x (%s) is stale", ent->entityNumber, ent->name.c_str() );
- } else {
- ent->snapshotStale = true;
- ent->FreeModelDef();
- // possible fix for left over lights on CTF flag
- ent->FreeLightDef();
- ent->UpdateVisuals();
- ent->GetPhysics()->UnlinkClip();
- }
- } else {
- // add the entity to the snapshot list
- ent->snapshotNode.AddToEnd( snapshotEntities );
- int snapshotChanged = ss.ObjectChangedCountByIndex( o );
- msg.SetHasChanged( ent->snapshotChanged != snapshotChanged );
- ent->snapshotChanged = snapshotChanged;
- ent->FlagNewSnapshot();
- // read the class specific data from the snapshot
- if ( msg.GetRemainingReadBits() > 0 ) {
- ent->ReadFromSnapshot_Ex( msg );
- ent->snapshotBits = msg.GetSize();
- }
- // Set after ReadFromSnapshot so we can detect coming unstale
- ent->snapshotStale = false;
- }
- }
- // process entity events
- ClientProcessEntityNetworkEventQueue();
- }
- /*
- ================
- idGameLocal::ClientProcessEntityNetworkEventQueue
- ================
- */
- void idGameLocal::ClientProcessEntityNetworkEventQueue() {
- while( eventQueue.Start() ) {
- entityNetEvent_t * event = eventQueue.Start();
- // only process forward, in order
- if ( event->time > this->serverTime ) {
- break;
- }
- idEntityPtr< idEntity > entPtr;
-
- if( !entPtr.SetSpawnId( event->spawnId ) ) {
- if( !gameLocal.entities[ event->spawnId & ( ( 1 << GENTITYNUM_BITS ) - 1 ) ] ) {
- // if new entity exists in this position, silently ignore
- NetworkEventWarning( event, "Entity does not exist any longer, or has not been spawned yet." );
- }
- } else {
- idEntity * ent = entPtr.GetEntity();
- assert( ent );
- idBitMsg eventMsg;
- eventMsg.InitRead( event->paramsBuf, sizeof( event->paramsBuf ) );
- eventMsg.SetSize( event->paramsSize );
- eventMsg.BeginReading();
- if ( !ent->ClientReceiveEvent( event->event, event->time, eventMsg ) ) {
- NetworkEventWarning( event, "unknown event" );
- }
- }
- verify( eventQueue.Dequeue() == event );
- eventQueue.Free( event );
- }
- }
- /*
- ================
- idGameLocal::ClientProcessReliableMessage
- ================
- */
- void idGameLocal::ClientProcessReliableMessage( int type, const idBitMsg &msg ) {
- switch( type ) {
- case GAME_RELIABLE_MESSAGE_SYNCEDCVARS: {
- idDict syncedCvars;
- msg.ReadDeltaDict( syncedCvars, NULL );
- idLib::Printf( "Got networkSync cvars:\n" );
- syncedCvars.Print();
- cvarSystem->ResetFlaggedVariables( CVAR_NETWORKSYNC );
- cvarSystem->SetCVarsFromDict( syncedCvars );
- break;
- }
- case GAME_RELIABLE_MESSAGE_CHAT:
- case GAME_RELIABLE_MESSAGE_TCHAT: { // (client should never get a TCHAT though)
- char name[128];
- char text[128];
- msg.ReadString( name, sizeof( name ) );
- msg.ReadString( text, sizeof( text ) );
- mpGame.AddChatLine( "%s^0: %s\n", name, text );
- break;
- }
- case GAME_RELIABLE_MESSAGE_SOUND_EVENT: {
- snd_evt_t snd_evt = (snd_evt_t)msg.ReadByte();
- mpGame.PlayGlobalSound( -1, snd_evt );
- break;
- }
- case GAME_RELIABLE_MESSAGE_SOUND_INDEX: {
- int index = gameLocal.ClientRemapDecl( DECL_SOUND, msg.ReadLong() );
- if ( index >= 0 && index < declManager->GetNumDecls( DECL_SOUND ) ) {
- const idSoundShader *shader = declManager->SoundByIndex( index );
- mpGame.PlayGlobalSound( -1, SND_COUNT, shader->GetName() );
- }
- break;
- }
- case GAME_RELIABLE_MESSAGE_DB: {
- idMultiplayerGame::msg_evt_t msg_evt = (idMultiplayerGame::msg_evt_t)msg.ReadByte();
- int parm1, parm2;
- parm1 = msg.ReadByte( );
- parm2 = msg.ReadByte( );
- mpGame.PrintMessageEvent( msg_evt, parm1, parm2 );
- break;
- }
- case GAME_RELIABLE_MESSAGE_EVENT: {
- // allocate new event
- entityNetEvent_t * event = eventQueue.Alloc();
- eventQueue.Enqueue( event, idEventQueue::OUTOFORDER_IGNORE );
- event->spawnId = msg.ReadBits( 32 );
- event->event = msg.ReadByte();
- event->time = msg.ReadLong();
- event->paramsSize = msg.ReadBits( idMath::BitsForInteger( MAX_EVENT_PARAM_SIZE ) );
- if ( event->paramsSize ) {
- if ( event->paramsSize > MAX_EVENT_PARAM_SIZE ) {
- NetworkEventWarning( event, "invalid param size" );
- return;
- }
- msg.ReadByteAlign();
- msg.ReadData( event->paramsBuf, event->paramsSize );
- }
- break;
- }
- case GAME_RELIABLE_MESSAGE_RESTART: {
- MapRestart();
- break;
- }
- case GAME_RELIABLE_MESSAGE_TOURNEYLINE: {
- int line = msg.ReadByte( );
- idPlayer * p = static_cast< idPlayer * >( entities[ GetLocalClientNum() ] );
- if ( !p ) {
- break;
- }
- p->tourneyLine = line;
- break;
- }
- case GAME_RELIABLE_MESSAGE_STARTSTATE: {
- mpGame.ClientReadStartState( msg );
- break;
- }
- case GAME_RELIABLE_MESSAGE_WARMUPTIME: {
- mpGame.ClientReadWarmupTime( msg );
- break;
- }
- case GAME_RELIABLE_MESSAGE_LOBBY_COUNTDOWN: {
- int timeRemaining = msg.ReadLong();
- Shell_UpdateClientCountdown( timeRemaining );
- break;
- }
- case GAME_RELIABLE_MESSAGE_RESPAWN_AVAILABLE: {
- idPlayer * p = static_cast< idPlayer * >( entities[ GetLocalClientNum() ] );
- if ( p ) {
- p->ShowRespawnHudMessage();
- }
- break;
- }
- case GAME_RELIABLE_MESSAGE_MATCH_STARTED_TIME: {
- mpGame.ClientReadMatchStartedTime( msg );
- break;
- }
- case GAME_RELIABLE_MESSAGE_ACHIEVEMENT_UNLOCK: {
- mpGame.ClientReadAchievementUnlock( msg );
- break;
- }
- default: {
- Error( "Unknown reliable message (%d) from host", type );
- break;
- }
- }
- }
- /*
- ================
- idGameLocal::ClientRunFrame
- ================
- */
- void idGameLocal::ClientRunFrame( idUserCmdMgr & cmdMgr, bool lastPredictFrame, gameReturn_t & ret ) {
- idEntity *ent;
- // update the game time
- previousTime = FRAME_TO_MSEC( framenum );
- framenum++;
- time = FRAME_TO_MSEC( framenum );
- idPlayer * player = static_cast<idPlayer *>( entities[GetLocalClientNum()] );
- if ( !player ) {
- // service any pending events
- idEvent::ServiceEvents();
- return;
- }
- // check for local client lag
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- if ( lobby.GetPeerTimeSinceLastPacket( lobby.PeerIndexForHost() ) >= net_clientMaxPrediction.GetInteger() ) {
- player->isLagged = true;
- } else {
- player->isLagged = false;
- }
- // update the real client time and the new frame flag
- if ( time > realClientTime ) {
- realClientTime = time;
- isNewFrame = true;
- } else {
- isNewFrame = false;
- }
- slow.Set( time, previousTime, realClientTime );
- fast.Set( time, previousTime, realClientTime );
- // run prediction on all active entities
- for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
- ent->thinkFlags |= TH_PHYSICS;
- if ( ent->entityNumber != GetLocalClientNum() ) {
- ent->ClientThink( netInterpolationInfo.serverGameMs, netInterpolationInfo.pct, true );
- } else {
- RunAllUserCmdsForPlayer( cmdMgr, ent->entityNumber );
- }
- }
- // service any pending events
- idEvent::ServiceEvents();
- // show any debug info for this frame
- if ( isNewFrame ) {
- RunDebugInfo();
- D_DrawDebugLines();
- }
- BuildReturnValue( ret );
- }
- /*
- ===============
- idGameLocal::Tokenize
- ===============
- */
- void idGameLocal::Tokenize( idStrList &out, const char *in ) {
- char buf[ MAX_STRING_CHARS ];
- char *token, *next;
-
- idStr::Copynz( buf, in, MAX_STRING_CHARS );
- token = buf;
- next = strchr( token, ';' );
- while ( token ) {
- if ( next ) {
- *next = '\0';
- }
- idStr::ToLower( token );
- out.Append( token );
- if ( next ) {
- token = next + 1;
- next = strchr( token, ';' );
- } else {
- token = NULL;
- }
- }
- }
- /*
- ========================
- idGameLocal::FindPredictedEntity
- ========================
- */
- idEntity * idGameLocal::FindPredictedEntity( uint32 predictedKey, idTypeInfo * type ) {
- for ( idEntity * predictedEntity = activeEntities.Next(); predictedEntity != NULL; predictedEntity = predictedEntity->activeNode.Next() ) {
- if ( !verify( predictedEntity != NULL ) ) {
- continue;
- }
- if ( !predictedEntity->IsReplicated() && predictedEntity->GetPredictedKey() == predictedKey ) {
- if ( predictedEntity->GetType() != type ) {
- idLib::Warning("Mismatched presentable type. Predicted: %s Actual: %s", predictedEntity->GetType()->classname, type->classname );
- }
- return predictedEntity;
- }
- }
- return NULL;
- }
- /*
- ========================
- idGameLocal::GeneratePredictionKey
- ========================
- */
- uint32 idGameLocal::GeneratePredictionKey( idWeapon * weapon, idPlayer * playerAttacker, int overrideKey ) {
- if ( overrideKey != -1 ) {
- uint32 predictedKey = overrideKey;
- int peerIndex = -1;
- if ( common->IsServer() ) {
- peerIndex = session->GetActingGameStateLobbyBase().PeerIndexFromLobbyUser( lobbyUserIDs[ playerAttacker->entityNumber ] );
- } else {
- peerIndex = session->GetActingGameStateLobbyBase().PeerIndexOnHost();
- }
- predictedKey |= ( peerIndex << 28 );
-
- return predictedKey;
- }
- uint32 predictedKey = idEntity::INVALID_PREDICTION_KEY;
- int peerIndex = -1;
-
- // Get key - fireCount or throwCount
- //if ( weapon != NULL ) {
- if ( common->IsClient() ) {
- predictedKey = playerAttacker->GetClientFireCount();
- } else {
- predictedKey = playerAttacker->usercmd.fireCount;
- }
- //} else {
- // predictedKey = ( playerAttacker->GetThrowCount() );
- //}
- // Get peer index
- if ( common->IsServer() ) {
- peerIndex = session->GetActingGameStateLobbyBase().PeerIndexFromLobbyUser( lobbyUserIDs[ playerAttacker->entityNumber ] );
- } else {
- peerIndex = session->GetActingGameStateLobbyBase().PeerIndexOnHost();
- }
- if ( cg_predictedSpawn_debug.GetBool() ) {
- idLib::Printf("GeneratePredictionKey. predictedKey: %d peedIndex: %d\n", predictedKey, peerIndex );
- }
- predictedKey |= ( peerIndex << 28 );
- return predictedKey;
- }
- /*
- ===============
- idEventQueue::Alloc
- ===============
- */
- entityNetEvent_t* idEventQueue::Alloc() {
- entityNetEvent_t* event = eventAllocator.Alloc();
- event->prev = NULL;
- event->next = NULL;
- return event;
- }
- /*
- ===============
- idEventQueue::Free
- ===============
- */
- void idEventQueue::Free( entityNetEvent_t *event ) {
- // should only be called on an unlinked event!
- assert( !event->next && !event->prev );
- eventAllocator.Free( event );
- }
- /*
- ===============
- idEventQueue::Shutdown
- ===============
- */
- void idEventQueue::Shutdown() {
- eventAllocator.Shutdown();
- this->Init();
- }
- /*
- ===============
- idEventQueue::Init
- ===============
- */
- void idEventQueue::Init() {
- start = NULL;
- end = NULL;
- }
- /*
- ===============
- idEventQueue::Dequeue
- ===============
- */
- entityNetEvent_t* idEventQueue::Dequeue() {
- entityNetEvent_t* event = start;
- if ( !event ) {
- return NULL;
- }
- start = start->next;
- if ( !start ) {
- end = NULL;
- } else {
- start->prev = NULL;
- }
- event->next = NULL;
- event->prev = NULL;
- return event;
- }
- /*
- ===============
- idEventQueue::RemoveLast
- ===============
- */
- entityNetEvent_t* idEventQueue::RemoveLast() {
- entityNetEvent_t *event = end;
- if ( !event ) {
- return NULL;
- }
- end = event->prev;
- if ( !end ) {
- start = NULL;
- } else {
- end->next = NULL;
- }
- event->next = NULL;
- event->prev = NULL;
- return event;
- }
- /*
- ===============
- idEventQueue::Enqueue
- ===============
- */
- void idEventQueue::Enqueue( entityNetEvent_t *event, outOfOrderBehaviour_t behaviour ) {
- if ( behaviour == OUTOFORDER_DROP ) {
- // go backwards through the queue and determine if there are
- // any out-of-order events
- while ( end && end->time > event->time ) {
- entityNetEvent_t *outOfOrder = RemoveLast();
- common->DPrintf( "WARNING: new event with id %d ( time %d ) caused removal of event with id %d ( time %d ), game time = %d.\n", event->event, event->time, outOfOrder->event, outOfOrder->time, gameLocal.time );
- Free( outOfOrder );
- }
- } else if ( behaviour == OUTOFORDER_SORT && end ) {
- // NOT TESTED -- sorting out of order packets hasn't been
- // tested yet... wasn't strictly necessary for
- // the patch fix.
- entityNetEvent_t *cur = end;
- // iterate until we find a time < the new event's
- while ( cur && cur->time > event->time ) {
- cur = cur->prev;
- }
- if ( !cur ) {
- // add to start
- event->next = start;
- event->prev = NULL;
- start = event;
- } else {
- // insert
- event->prev = cur;
- event->next = cur->next;
- cur->next = event;
- }
- return;
- }
- // add the new event
- event->next = NULL;
- event->prev = NULL;
- if ( end ) {
- end->next = event;
- event->prev = end;
- } else {
- start = event;
- }
- end = event;
- }
|