diff options
Diffstat (limited to 'engine/cl_ents_parse.cpp')
| -rw-r--r-- | engine/cl_ents_parse.cpp | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/engine/cl_ents_parse.cpp b/engine/cl_ents_parse.cpp new file mode 100644 index 0000000..9d96df8 --- /dev/null +++ b/engine/cl_ents_parse.cpp @@ -0,0 +1,754 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Parsing of entity network packets. +// +// $NoKeywords: $ +//=============================================================================// + + +#include "client_pch.h" +#include "con_nprint.h" +#include "iprediction.h" +#include "cl_entityreport.h" +#include "dt_recv_eng.h" +#include "net_synctags.h" +#include "ispatialpartitioninternal.h" +#include "LocalNetworkBackdoor.h" +#include "basehandle.h" +#include "dt_localtransfer.h" +#include "iprediction.h" +#include "netmessages.h" +#include "ents_shared.h" +#include "cl_ents_parse.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static ConVar cl_flushentitypacket("cl_flushentitypacket", "0", FCVAR_CHEAT, "For debugging. Force the engine to flush an entity packet."); + +// Prints important entity creation/deletion events to console +#if defined( _DEBUG ) +static ConVar cl_deltatrace( "cl_deltatrace", "0", 0, "For debugging, print entity creation/deletion info to console." ); +#define TRACE_DELTA( text ) if ( cl_deltatrace.GetInt() ) { ConMsg( "%s", text ); }; +#else +#define TRACE_DELTA( funcs ) +#endif + + + +//----------------------------------------------------------------------------- +// Debug networking stuff. +//----------------------------------------------------------------------------- + + +// #define DEBUG_NETWORKING 1 + +#if defined( DEBUG_NETWORKING ) +void SpewToFile( char const* pFmt, ... ); +static ConVar cl_packettrace( "cl_packettrace", "1", 0, "For debugging, massive spew to file." ); +#define TRACE_PACKET( text ) if ( cl_packettrace.GetInt() ) { SpewToFile text ; }; +#else +#define TRACE_PACKET( text ) +#endif + + +#if defined( DEBUG_NETWORKING ) + +//----------------------------------------------------------------------------- +// Opens the recording file +//----------------------------------------------------------------------------- + +static FileHandle_t OpenRecordingFile() +{ + FileHandle_t fp = 0; + static bool s_CantOpenFile = false; + static bool s_NeverOpened = true; + if (!s_CantOpenFile) + { + fp = g_pFileSystem->Open( "cltrace.txt", s_NeverOpened ? "wt" : "at" ); + if (!fp) + { + s_CantOpenFile = true; + } + s_NeverOpened = false; + } + return fp; +} + +//----------------------------------------------------------------------------- +// Records an argument for a command, flushes when the command is done +//----------------------------------------------------------------------------- + +void SpewToFile( char const* pFmt, ... ) +{ + static CUtlVector<unsigned char> s_RecordingBuffer; + + char temp[2048]; + va_list args; + + va_start( args, pFmt ); + int len = Q_vsnprintf( temp, sizeof( temp ), pFmt, args ); + va_end( args ); + assert( len < 2048 ); + + int idx = s_RecordingBuffer.AddMultipleToTail( len ); + memcpy( &s_RecordingBuffer[idx], temp, len ); + if ( 1 ) //s_RecordingBuffer.Size() > 8192) + { + FileHandle_t fp = OpenRecordingFile(); + g_pFileSystem->Write( s_RecordingBuffer.Base(), s_RecordingBuffer.Size(), fp ); + g_pFileSystem->Close( fp ); + + s_RecordingBuffer.RemoveAll(); + } +} + +#endif // DEBUG_NETWORKING + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Purpose: Frees the client DLL's binding to the object. +// Input : iEnt - +//----------------------------------------------------------------------------- +void CL_DeleteDLLEntity( int iEnt, const char *reason, bool bOnRecreatingAllEntities ) +{ + IClientNetworkable *pNet = entitylist->GetClientNetworkable( iEnt ); + + if ( pNet ) + { + ClientClass *pClientClass = pNet->GetClientClass(); + TRACE_DELTA( va( "Trace %i (%s): delete (%s)\n", iEnt, pClientClass ? pClientClass->m_pNetworkName : "unknown", reason ) ); +#ifndef _XBOX + CL_RecordDeleteEntity( iEnt, pClientClass ); +#endif + if ( bOnRecreatingAllEntities ) + { + pNet->SetDestroyedOnRecreateEntities(); + } + + pNet->Release(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Has the client DLL allocate its data for the object. +// Input : iEnt - +// iClass - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +IClientNetworkable* CL_CreateDLLEntity( int iEnt, int iClass, int iSerialNum ) +{ +#if defined( _DEBUG ) + IClientNetworkable *pOldNetworkable = entitylist->GetClientNetworkable( iEnt ); + Assert( !pOldNetworkable ); +#endif + + ClientClass *pClientClass; + if ( ( pClientClass = cl.m_pServerClasses[iClass].m_pClientClass ) != NULL ) + { + TRACE_DELTA( va( "Trace %i (%s): create\n", iEnt, pClientClass->m_pNetworkName ) ); +#ifndef _XBOX + CL_RecordAddEntity( iEnt ); +#endif + + if ( !cl.IsActive() ) + { + COM_TimestampedLog( "cl: create '%s'", pClientClass->m_pNetworkName ); + } + + // Create the entity. + return pClientClass->m_pCreateFn( iEnt, iSerialNum ); + } + + Assert(false); + return NULL; +} + +void SpewBitStream( unsigned char* pMem, int bit, int lastbit ) +{ + int val = 0; + char buf[1024]; + char* pTemp = buf; + int bitcount = 0; + int charIdx = 1; + while( bit < lastbit ) + { + int byte = bit >> 3; + int bytebit = bit & 0x7; + + val |= ((pMem[byte] & bytebit) != 0) << bitcount; + + ++bit; + ++bitcount; + + if (bitcount == 4) + { + if ((val >= 0) && (val <= 9)) + pTemp[charIdx] = '0' + val; + else + pTemp[charIdx] = 'A' + val - 0xA; + if (charIdx == 1) + charIdx = 0; + else + { + charIdx = 1; + pTemp += 2; + } + bitcount = 0; + val = 0; + } + } + if ((bitcount != 0) || (charIdx != 0)) + { + if (bitcount > 0) + { + if ((val >= 0) && (val <= 9)) + pTemp[charIdx] = '0' + val; + else + pTemp[charIdx] = 'A' + val - 0xA; + } + if (charIdx == 1) + { + pTemp[0] = '0'; + } + pTemp += 2; + } + pTemp[0] = '\0'; + + TRACE_PACKET(( " CL Bitstream %s\n", buf )); +} + + +inline static void CL_AddPostDataUpdateCall( CEntityReadInfo &u, int iEnt, DataUpdateType_t updateType ) +{ + ErrorIfNot( u.m_nPostDataUpdateCalls < MAX_EDICTS, + ("CL_AddPostDataUpdateCall: overflowed u.m_PostDataUpdateCalls") ); + + u.m_PostDataUpdateCalls[u.m_nPostDataUpdateCalls].m_iEnt = iEnt; + u.m_PostDataUpdateCalls[u.m_nPostDataUpdateCalls].m_UpdateType = updateType; + ++u.m_nPostDataUpdateCalls; +} + + + + +//----------------------------------------------------------------------------- +// Purpose: Get the receive table for the specified entity +// Input : *pEnt - +// Output : RecvTable* +//----------------------------------------------------------------------------- +static inline RecvTable* GetEntRecvTable( int entnum ) +{ + IClientNetworkable *pNet = entitylist->GetClientNetworkable( entnum ); + if ( pNet ) + return pNet->GetClientClass()->m_pRecvTable; + else + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the entity index corresponds to a player slot +// Input : index - +// Output : bool +//----------------------------------------------------------------------------- +static inline bool CL_IsPlayerIndex( int index ) +{ + return ( index >= 1 && index <= cl.m_nMaxClients ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Bad data was received, just flushes incoming delta data. +//----------------------------------------------------------------------------- +void CL_FlushEntityPacket( CClientFrame *packet, char const *errorString, ... ) +{ + con_nprint_t np; + char str[2048]; + va_list marker; + + // Spit out an error. + va_start(marker, errorString); + Q_vsnprintf(str, sizeof(str), errorString, marker); + va_end(marker); + + ConMsg("%s", str); + + np.fixed_width_font = false; + np.time_to_live = 1.0; + np.index = 0; + np.color[ 0 ] = 1.0; + np.color[ 1 ] = 0.2; + np.color[ 2 ] = 0.0; + Con_NXPrintf( &np, "WARNING: CL_FlushEntityPacket, %s", str ); + + // Free packet memory. + delete packet; +} + + +// ----------------------------------------------------------------------------- // +// Regular handles for ReadPacketEntities. +// ----------------------------------------------------------------------------- // + +void CL_CopyNewEntity( + CEntityReadInfo &u, + int iClass, + int iSerialNum + ) +{ + if ( u.m_nNewEntity < 0 || u.m_nNewEntity >= MAX_EDICTS ) + { + Host_Error ("CL_CopyNewEntity: u.m_nNewEntity < 0 || m_nNewEntity >= MAX_EDICTS"); + return; + } + + // If it's new, make sure we have a slot for it. + IClientNetworkable *ent = entitylist->GetClientNetworkable( u.m_nNewEntity ); + + if( iClass >= cl.m_nServerClasses ) + { + Host_Error("CL_CopyNewEntity: invalid class index (%d).\n", iClass); + return; + } + + // Delete the entity. + ClientClass *pClass = cl.m_pServerClasses[iClass].m_pClientClass; + bool bNew = false; + if ( ent ) + { + // if serial number is different, destory old entity + if ( ent->GetIClientUnknown()->GetRefEHandle().GetSerialNumber() != iSerialNum ) + { + CL_DeleteDLLEntity( u.m_nNewEntity, "CopyNewEntity" ); + ent = NULL; // force a recreate + } + } + + if ( !ent ) + { + // Ok, it doesn't exist yet, therefore this is not an "entered PVS" message. + ent = CL_CreateDLLEntity( u.m_nNewEntity, iClass, iSerialNum ); + if( !ent ) + { + const char *pNetworkName = cl.m_pServerClasses[iClass].m_pClientClass ? cl.m_pServerClasses[iClass].m_pClientClass->m_pNetworkName : ""; + Host_Error( "CL_ParsePacketEntities: Error creating entity %s(%i)\n", pNetworkName, u.m_nNewEntity ); + return; + } + + bNew = true; + } + + int start_bit = u.m_pBuf->GetNumBitsRead(); + + DataUpdateType_t updateType = bNew ? DATA_UPDATE_CREATED : DATA_UPDATE_DATATABLE_CHANGED; + ent->PreDataUpdate( updateType ); + + // Get either the static or instance baseline. + const void *pFromData; + int nFromBits; + + PackedEntity *baseline = u.m_bAsDelta ? cl.GetEntityBaseline( u.m_nBaseline, u.m_nNewEntity ) : NULL; + if ( baseline && baseline->m_pClientClass == pClass ) + { + Assert( !baseline->IsCompressed() ); + pFromData = baseline->GetData(); + nFromBits = baseline->GetNumBits(); + } + else + { + // Every entity must have a static or an instance baseline when we get here. + ErrorIfNot( + cl.GetClassBaseline( iClass, &pFromData, &nFromBits ), + ("CL_CopyNewEntity: GetClassBaseline(%d) failed.", iClass) + ); + + nFromBits *= 8; // convert to bits + } + + // Delta from baseline and merge to new baseline + bf_read fromBuf( "CL_CopyNewEntity->fromBuf", pFromData, Bits2Bytes(nFromBits), nFromBits ); + + RecvTable *pRecvTable = GetEntRecvTable( u.m_nNewEntity ); + + if( !pRecvTable ) + Host_Error( "CL_ParseDelta: invalid recv table for ent %d.\n", u.m_nNewEntity ); + + if ( u.m_bUpdateBaselines ) + { + // store this baseline in u.m_pUpdateBaselines + ALIGN4 char packedData[MAX_PACKEDENTITY_DATA] ALIGN4_POST; + bf_write writeBuf( "CL_CopyNewEntity->newBuf", packedData, sizeof(packedData) ); + + RecvTable_MergeDeltas( pRecvTable, &fromBuf, u.m_pBuf, &writeBuf, -1, NULL, true ); + + // set the other baseline + cl.SetEntityBaseline( (u.m_nBaseline==0)?1:0, pClass, u.m_nNewEntity, packedData, writeBuf.GetNumBytesWritten() ); + + fromBuf.StartReading( packedData, writeBuf.GetNumBytesWritten() ); + + RecvTable_Decode( pRecvTable, ent->GetDataTableBasePtr(), &fromBuf, u.m_nNewEntity, false ); + + } + else + { + // write data from baseline into entity + RecvTable_Decode( pRecvTable, ent->GetDataTableBasePtr(), &fromBuf, u.m_nNewEntity, false ); + + // Now parse in the contents of the network stream. + RecvTable_Decode( pRecvTable, ent->GetDataTableBasePtr(), u.m_pBuf, u.m_nNewEntity, true ); + } + + CL_AddPostDataUpdateCall( u, u.m_nNewEntity, updateType ); + + // If ent doesn't think it's in PVS, signal that it is + Assert( u.m_pTo->last_entity <= u.m_nNewEntity ); + u.m_pTo->last_entity = u.m_nNewEntity; + Assert( !u.m_pTo->transmit_entity.Get(u.m_nNewEntity) ); + u.m_pTo->transmit_entity.Set( u.m_nNewEntity ); + + // + // Net stats.. + // + int bit_count = u.m_pBuf->GetNumBitsRead() - start_bit; +#ifndef _XBOX + if ( cl_entityreport.GetBool() ) + CL_RecordEntityBits( u.m_nNewEntity, bit_count ); +#endif + if ( CL_IsPlayerIndex( u.m_nNewEntity ) ) + { + if ( u.m_nNewEntity == cl.m_nPlayerSlot + 1 ) + { + u.m_nLocalPlayerBits += bit_count; + } + else + { + u.m_nOtherPlayerBits += bit_count; + } + } +} + +void CL_PreserveExistingEntity( int nOldEntity ) +{ + IClientNetworkable *pEnt = entitylist->GetClientNetworkable( nOldEntity ); + if ( !pEnt ) + { + // If you hit this, this is because there's a networked client entity that got released + // by some method other than a server update. This can happen if client code calls + // release on a networked entity. + +#if defined( STAGING_ONLY ) + // Try to use the cl_removeentity_backtrace_capture code in cliententitylist.cpp... + Msg( "%s: missing client entity %d.\n", __FUNCTION__, nOldEntity ); + Cbuf_AddText( CFmtStr( "cl_removeentity_backtrace_dump %d\n", nOldEntity ) ); + Cbuf_Execute(); +#endif // STAGING_ONLY + + Host_Error( "CL_PreserveExistingEntity: missing client entity %d.\n", nOldEntity ); + return; + } + + pEnt->OnDataUnchangedInPVS(); +} + +void CL_CopyExistingEntity( CEntityReadInfo &u ) +{ + int start_bit = u.m_pBuf->GetNumBitsRead(); + + IClientNetworkable *pEnt = entitylist->GetClientNetworkable( u.m_nNewEntity ); + if ( !pEnt ) + { + Host_Error( "CL_CopyExistingEntity: missing client entity %d.\n", u.m_nNewEntity ); + return; + } + + Assert( u.m_pFrom->transmit_entity.Get(u.m_nNewEntity) ); + + // Read raw data from the network stream + pEnt->PreDataUpdate( DATA_UPDATE_DATATABLE_CHANGED ); + + RecvTable *pRecvTable = GetEntRecvTable( u.m_nNewEntity ); + + if( !pRecvTable ) + { + Host_Error( "CL_ParseDelta: invalid recv table for ent %d.\n", u.m_nNewEntity ); + return; + } + + RecvTable_Decode( pRecvTable, pEnt->GetDataTableBasePtr(), u.m_pBuf, u.m_nNewEntity ); + + CL_AddPostDataUpdateCall( u, u.m_nNewEntity, DATA_UPDATE_DATATABLE_CHANGED ); + + u.m_pTo->last_entity = u.m_nNewEntity; + Assert( !u.m_pTo->transmit_entity.Get(u.m_nNewEntity) ); + u.m_pTo->transmit_entity.Set( u.m_nNewEntity ); + + int bit_count = u.m_pBuf->GetNumBitsRead() - start_bit; +#ifndef _XBOX + if ( cl_entityreport.GetBool() ) + CL_RecordEntityBits( u.m_nNewEntity, bit_count ); +#endif + if ( CL_IsPlayerIndex( u.m_nNewEntity ) ) + { + if ( u.m_nNewEntity == cl.m_nPlayerSlot + 1 ) + { + u.m_nLocalPlayerBits += bit_count; + } + else + { + u.m_nOtherPlayerBits += bit_count; + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CL_MarkEntitiesOutOfPVS( CBitVec<MAX_EDICTS> *pvs_flags ) +{ + int highest_index = entitylist->GetHighestEntityIndex(); + // Note that we go up to and including the highest_index + for ( int i = 0; i <= highest_index; i++ ) + { + IClientNetworkable *ent = entitylist->GetClientNetworkable( i ); + if ( !ent ) + continue; + + // FIXME: We can remove IClientEntity here if we keep track of the + // last frame's entity_in_pvs + bool curstate = !ent->IsDormant(); + bool newstate = pvs_flags->Get( i ) ? true : false; + + if ( !curstate && newstate ) + { + // Inform the client entity list that the entity entered the PVS + ent->NotifyShouldTransmit( SHOULDTRANSMIT_START ); + } + else if ( curstate && !newstate ) + { + // Inform the client entity list that the entity left the PVS + ent->NotifyShouldTransmit( SHOULDTRANSMIT_END ); +#ifndef _XBOX + CL_RecordLeavePVS( i ); +#endif + } + } +} + +static void CL_CallPostDataUpdates( CEntityReadInfo &u ) +{ + for ( int i=0; i < u.m_nPostDataUpdateCalls; i++ ) + { + MDLCACHE_CRITICAL_SECTION_(g_pMDLCache); + CPostDataUpdateCall *pCall = &u.m_PostDataUpdateCalls[i]; + + IClientNetworkable *pEnt = entitylist->GetClientNetworkable( pCall->m_iEnt ); + ErrorIfNot( pEnt, + ("CL_CallPostDataUpdates: missing ent %d", pCall->m_iEnt) ); + + pEnt->PostDataUpdate( pCall->m_UpdateType ); + } +} + +static float g_flLastPerfRequest = 0.0f; + +static ConVar cl_debug_player_perf( "cl_debug_player_perf", "0", 0 ); + +//----------------------------------------------------------------------------- +// Purpose: An svc_packetentities has just been parsed, deal with the +// rest of the data stream. This can be a delta from the baseline or from a previous +// client frame for this client. +// Input : delta - +// *playerbits - +// Output : void CL_ParsePacketEntities +//----------------------------------------------------------------------------- +bool CL_ProcessPacketEntities ( SVC_PacketEntities *entmsg ) +{ + VPROF( "_CL_ParsePacketEntities" ); + + // Packed entities for that frame + // Allocate space for new packet info. + CClientFrame *newFrame = cl.AllocateFrame(); + newFrame->Init( cl.GetServerTickCount() ); + CClientFrame *oldFrame = NULL; + + // if cl_flushentitypacket is set to N, the next N entity updates will be flushed + if ( cl_flushentitypacket.GetInt() ) + { + // we can't use this, it is too old + CL_FlushEntityPacket( newFrame, "Forced by cvar\n" ); + cl_flushentitypacket.SetValue( cl_flushentitypacket.GetInt() - 1 ); // Reduce the cvar. + return false; + } + + if ( entmsg->m_bIsDelta ) + { + int nDeltaTicks = cl.GetServerTickCount() - entmsg->m_nDeltaFrom; + float flDeltaSeconds = TICKS_TO_TIME( nDeltaTicks ); + + // If we have cl_debug_player_perf set and we see a huge delta between what we've ack'd to the server and where it's at + // ask it for an instantaneous perf snapshot + if ( cl_debug_player_perf.GetBool() && + ( flDeltaSeconds > 0.5f ) && // delta is pretty out of date + ( ( realtime - g_flLastPerfRequest ) > 5.0f ) ) // haven't requested in a while + { + g_flLastPerfRequest = realtime; + Warning( "Gap in server data, requesting connection perf data\n" ); + cl.SendStringCmd( "playerperf\n" ); + } + + if ( cl.GetServerTickCount() == entmsg->m_nDeltaFrom ) + { + Host_Error( "Update self-referencing, connection dropped.\n" ); + return false; + } + + // Otherwise, mark where we are valid to and point to the packet entities we'll be updating from. + oldFrame = cl.GetClientFrame( entmsg->m_nDeltaFrom ); + + if ( !oldFrame ) + { + CL_FlushEntityPacket( newFrame, "Update delta not found.\n" ); + return false; + } + } + else + { + if ( cl_debug_player_perf.GetBool() ) + { + Warning( "Received uncompressed server update\n" ); + } + + // Clear out the client's entity states.. + for ( int i=0; i <= entitylist->GetHighestEntityIndex(); i++ ) + { + CL_DeleteDLLEntity( i, "ProcessPacketEntities", true ); + } + } + + // signal client DLL that we have started updating entities + ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_START ); + + g_nPropsDecoded = 0; + + Assert( entmsg->m_nBaseline >= 0 && entmsg->m_nBaseline < 2 ); + + if ( entmsg->m_bUpdateBaseline ) + { + // server requested to use this snapshot as baseline update + int nUpdateBaseline = (entmsg->m_nBaseline == 0) ? 1 : 0; + cl.CopyEntityBaseline( entmsg->m_nBaseline, nUpdateBaseline ); + + // send new baseline acknowledgement(as reliable) + cl.m_NetChannel->SendNetMsg( CLC_BaselineAck( cl.GetServerTickCount(), entmsg->m_nBaseline ), true ); + + } + + CEntityReadInfo u; + u.m_pBuf = &entmsg->m_DataIn; + u.m_pFrom = oldFrame; + u.m_pTo = newFrame; + u.m_bAsDelta = entmsg->m_bIsDelta; + u.m_nHeaderCount = entmsg->m_nUpdatedEntries; + u.m_nBaseline = entmsg->m_nBaseline; + u.m_bUpdateBaselines = entmsg->m_bUpdateBaseline; + + // update the entities + cl.ReadPacketEntities( u ); + + ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_POSTDATAUPDATE_START ); + + // call PostDataUpdate() for each entity + CL_CallPostDataUpdates( u ); + + ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_POSTDATAUPDATE_END ); + + // call NotifyShouldTransmit() for entities that entered or left the PVS + CL_MarkEntitiesOutOfPVS( &newFrame->transmit_entity ); + + // adjust net channel stats + + cl.m_NetChannel->UpdateMessageStats( INetChannelInfo::LOCALPLAYER, u.m_nLocalPlayerBits ); + cl.m_NetChannel->UpdateMessageStats( INetChannelInfo::OTHERPLAYERS, u.m_nOtherPlayerBits ); + cl.m_NetChannel->UpdateMessageStats( INetChannelInfo::ENTITIES, -(u.m_nLocalPlayerBits+u.m_nOtherPlayerBits) ); + + cl.DeleteClientFrames( entmsg->m_nDeltaFrom ); + + // If the client has more than 64 frames, the host will start to eat too much memory. + // TODO: We should enforce this somehow. + if ( MAX_CLIENT_FRAMES < cl.AddClientFrame( newFrame ) ) + { + DevMsg( 1, "CL_ProcessPacketEntities: frame window too big (>%i)\n", MAX_CLIENT_FRAMES ); + } + + // all update activities are finished + ClientDLL_FrameStageNotify( FRAME_NET_UPDATE_END ); + + return true; +} + +/* +================== +CL_PreprocessEntities + +Server information pertaining to this client only +================== +*/ +namespace CDebugOverlay +{ + extern void PurgeServerOverlays( void ); +} + +void CL_PreprocessEntities( void ) +{ + // Zero latency!!! (single player or listen server?) + bool bIsUsingMultiplayerNetworking = NET_IsMultiplayer(); + bool bLastOutgoingCommandEqualsLastAcknowledgedCommand = cl.lastoutgoingcommand == cl.command_ack; + + // We always want to re-run prediction when using the multiplayer networking, or if we're the listen server and we get a packet + // before any frames have run + if ( bIsUsingMultiplayerNetworking || + bLastOutgoingCommandEqualsLastAcknowledgedCommand ) + { + //Msg( "%i/%i CL_ParseClientdata: no latency server ack %i\n", + // host_framecount, cl.tickcount, + // command_ack ); + CL_RunPrediction( PREDICTION_SIMULATION_RESULTS_ARRIVING_ON_SEND_FRAME ); + } + + // Copy some results from prediction back into right spot + // Anything not sent over the network from server to client must be specified here. + //if ( cl.last_command_ack ) + { + int number_of_commands_executed = ( cl.command_ack - cl.last_command_ack ); + +#if 0 + COM_Log( "cl.log", "Receiving frame acknowledging %i commands\n", + number_of_commands_executed ); + + COM_Log( "cl.log", " last command number executed %i\n", + cl.command_ack ); + + COM_Log( "cl.log", " previous last command number executed %i\n", + cl.last_command_ack ); + + COM_Log( "cl.log", " current world frame %i\n", + cl.m_nCurrentSequence ); +#endif + + // Copy last set of changes right into current frame. + g_pClientSidePrediction->PreEntityPacketReceived( number_of_commands_executed, cl.m_nCurrentSequence ); + } + + CDebugOverlay::PurgeServerOverlays(); +} + + + + + |