summaryrefslogtreecommitdiff
path: root/engine/cl_ents_parse.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/cl_ents_parse.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'engine/cl_ents_parse.cpp')
-rw-r--r--engine/cl_ents_parse.cpp754
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();
+}
+
+
+
+
+