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