summaryrefslogtreecommitdiff
path: root/engine/hltvclientstate.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/hltvclientstate.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'engine/hltvclientstate.cpp')
-rw-r--r--engine/hltvclientstate.cpp911
1 files changed, 911 insertions, 0 deletions
diff --git a/engine/hltvclientstate.cpp b/engine/hltvclientstate.cpp
new file mode 100644
index 0000000..79590a8
--- /dev/null
+++ b/engine/hltvclientstate.cpp
@@ -0,0 +1,911 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <proto_version.h>
+#include <netmessages.h>
+#include "hltvclientstate.h"
+#include "hltvserver.h"
+#include "quakedef.h"
+#include "cl_main.h"
+#include "host.h"
+#include "dt_recv_eng.h"
+#include "dt_common_eng.h"
+#include "framesnapshot.h"
+#include "clientframe.h"
+#include "ents_shared.h"
+#include "server.h"
+#include "eiface.h"
+#include "server_class.h"
+#include "cdll_engine_int.h"
+#include "sv_main.h"
+#include "changeframelist.h"
+#include "GameEventManager.h"
+#include "dt_recv_decoder.h"
+#include "utllinkedlist.h"
+#include "cl_demo.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// copy message data from in to out buffer
+#define CopyDataInToOut(msg) \
+ int size = PAD_NUMBER( Bits2Bytes(msg->m_nLength), 4); \
+ byte *buffer = (byte*) stackalloc( size ); \
+ msg->m_DataIn.ReadBits( buffer, msg->m_nLength ); \
+ msg->m_DataOut.StartWriting( buffer, size, msg->m_nLength );\
+
+static void HLTV_Callback_InstanceBaseline( void *object, INetworkStringTable *stringTable, int stringNumber, char const *newString, void const *newData )
+{
+ // relink server classes to instance baselines
+ CHLTVServer *pHLTV = (CHLTVServer*)object;
+ pHLTV->LinkInstanceBaselines();
+}
+
+extern CUtlLinkedList< CRecvDecoder *, unsigned short > g_RecvDecoders;
+
+extern ConVar tv_autorecord;
+static ConVar tv_autoretry( "tv_autoretry", "1", 0, "Relay proxies retry connection after network timeout" );
+static ConVar tv_timeout( "tv_timeout", "30", 0, "SourceTV connection timeout in seconds." );
+ ConVar tv_snapshotrate("tv_snapshotrate", "16", 0, "Snapshots broadcasted per second" );
+
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+
+CHLTVClientState::CHLTVClientState()
+{
+ m_pNewClientFrame = NULL;
+ m_pCurrentClientFrame = NULL;
+ m_bSaveMemory = false;
+}
+
+CHLTVClientState::~CHLTVClientState()
+{
+
+}
+
+void CHLTVClientState::CopyNewEntity(
+ CEntityReadInfo &u,
+ int iClass,
+ int iSerialNum
+ )
+{
+ ServerClass *pServerClass = SV_FindServerClass( iClass );
+ Assert( pServerClass );
+
+ ClientClass *pClientClass = GetClientClass( iClass );
+ Assert( pClientClass );
+
+ const int ent = u.m_nNewEntity;
+
+ // copy class & serial
+ CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot();
+ pSnapshot->m_pEntities[ent].m_nSerialNumber = iSerialNum;
+ pSnapshot->m_pEntities[ent].m_pClass = pServerClass;
+
+ // Get either the static or instance baseline.
+ const void *pFromData = NULL;
+ int nFromBits = 0;
+ int nFromTick = 0; // MOTODO get tick when baseline last changed
+
+ PackedEntity *baseline = u.m_bAsDelta ? GetEntityBaseline( u.m_nBaseline, ent ) : NULL;
+
+ if ( baseline && baseline->m_pClientClass == pClientClass )
+ {
+ 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(
+ GetClassBaseline( iClass, &pFromData, &nFromBits ),
+ ("HLTV_CopyNewEntity: GetDynamicBaseline(%d) failed.", iClass)
+ );
+ nFromBits *= 8; // convert to bits
+ }
+
+ // create new ChangeFrameList containing all properties set as changed
+ int nFlatProps = SendTable_GetNumFlatProps( pServerClass->m_pTable );
+ IChangeFrameList *pChangeFrame = NULL;
+
+ if ( !m_bSaveMemory )
+ {
+ pChangeFrame = AllocChangeFrameList( nFlatProps, nFromTick );
+ }
+
+ // Now make a PackedEntity and store the new packed data in there.
+ PackedEntity *pPackedEntity = framesnapshotmanager->CreatePackedEntity( pSnapshot, ent );
+ pPackedEntity->SetChangeFrameList( pChangeFrame );
+ pPackedEntity->SetServerAndClientClass( pServerClass, pClientClass );
+
+ // Make space for the baseline data.
+ ALIGN4 char packedData[MAX_PACKEDENTITY_DATA] ALIGN4_POST;
+ bf_read fromBuf( "HLTV_ReadEnterPVS1", pFromData, Bits2Bytes( nFromBits ), nFromBits );
+ bf_write writeBuf( "HLTV_ReadEnterPVS2", packedData, sizeof( packedData ) );
+
+ int changedProps[MAX_DATATABLE_PROPS];
+
+ // decode basline, is compressed against zero values
+ int nChangedProps = RecvTable_MergeDeltas( pClientClass->m_pRecvTable, &fromBuf,
+ u.m_pBuf, &writeBuf, -1, changedProps );
+
+ // update change tick in ChangeFrameList
+ if ( pChangeFrame )
+ {
+ pChangeFrame->SetChangeTick( changedProps, nChangedProps, pSnapshot->m_nTickCount );
+ }
+
+ if ( u.m_bUpdateBaselines )
+ {
+ SetEntityBaseline( (u.m_nBaseline==0)?1:0, pClientClass, u.m_nNewEntity, packedData, writeBuf.GetNumBytesWritten() );
+ }
+
+ pPackedEntity->AllocAndCopyPadded( packedData, writeBuf.GetNumBytesWritten() );
+
+ // If ent doesn't think it's in PVS, signal that it is
+ Assert( u.m_pTo->last_entity <= ent );
+ u.m_pTo->last_entity = ent;
+ u.m_pTo->transmit_entity.Set( ent );
+}
+
+static inline void HLTV_CopyExitingEnt( CEntityReadInfo &u )
+{
+ if ( !u.m_bAsDelta ) // Should never happen on a full update.
+ {
+ Assert(0); // cl.validsequence = 0;
+ ConMsg( "WARNING: CopyExitingEnt on full update.\n" );
+ u.m_UpdateType = Failed; // break out
+ return;
+ }
+
+ CFrameSnapshot *pFromSnapshot = u.m_pFrom->GetSnapshot(); // get from snapshot
+
+ const int ent = u.m_nOldEntity;
+
+ CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot(); // get to snapshot
+
+ // copy ent handle, serial numbers & class info
+ Assert( ent < pFromSnapshot->m_nNumEntities );
+ pSnapshot->m_pEntities[ent] = pFromSnapshot->m_pEntities[ent];
+
+ Assert( pSnapshot->m_pEntities[ent].m_pPackedData != INVALID_PACKED_ENTITY_HANDLE );
+
+ // increase PackedEntity reference counter
+ PackedEntity *pEntity = framesnapshotmanager->GetPackedEntity( pSnapshot, ent );
+ Assert( pEntity );
+ pEntity->m_ReferenceCount++;
+
+
+ Assert( u.m_pTo->last_entity <= ent );
+
+ // mark flags as received
+ u.m_pTo->last_entity = ent;
+ u.m_pTo->transmit_entity.Set( ent );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: A svc_signonnum has been received, perform a client side setup
+// Output : void CL_SignonReply
+//-----------------------------------------------------------------------------
+bool CHLTVClientState::SetSignonState ( int state, int count )
+{
+ // ConDMsg ("CL_SignonReply: %i\n", cl.signon);
+
+ if ( !CBaseClientState::SetSignonState( state, count ) )
+ return false;
+
+ Assert ( m_nSignonState == state );
+
+ switch ( m_nSignonState )
+ {
+ case SIGNONSTATE_CHALLENGE : break;
+ case SIGNONSTATE_CONNECTED : {
+ // allow longer timeout
+ m_NetChannel->SetTimeout( SIGNON_TIME_OUT );
+
+ m_NetChannel->Clear();
+ // set user settings (rate etc)
+ NET_SetConVar convars;
+ Host_BuildConVarUpdateMessage( &convars, FCVAR_USERINFO, false );
+
+ // let server know that we are a proxy server:
+ NET_SetConVar::cvar_t acvar;
+ V_strcpy_safe( acvar.name, "tv_relay" );
+ V_strcpy_safe( acvar.value, "1" );
+ convars.m_ConVars.AddToTail( acvar );
+
+ m_NetChannel->SendNetMsg( convars );
+ }
+ break;
+
+ case SIGNONSTATE_NEW : SendClientInfo();
+ break;
+
+ case SIGNONSTATE_PRESPAWN : break;
+
+ case SIGNONSTATE_SPAWN : m_pHLTV->SignonComplete();
+ break;
+
+ case SIGNONSTATE_FULL : m_NetChannel->SetTimeout( tv_timeout.GetFloat() );
+ // start new recording if autorecord is enabled
+ if ( tv_autorecord.GetBool() )
+ {
+ hltv->m_DemoRecorder.StartAutoRecording();
+ m_NetChannel->SetDemoRecorder( &hltv->m_DemoRecorder );
+ }
+ break;
+
+ case SIGNONSTATE_CHANGELEVEL: m_pHLTV->Changelevel();
+ m_NetChannel->SetTimeout( SIGNON_TIME_OUT ); // allow 5 minutes timeout
+ break;
+ }
+
+ if ( m_nSignonState >= SIGNONSTATE_CONNECTED )
+ {
+ // tell server that we entered now that state
+ NET_SignonState signonState( m_nSignonState, count );
+ m_NetChannel->SendNetMsg( signonState );
+ }
+
+ return true;
+}
+
+void CHLTVClientState::SendClientInfo( void )
+{
+ CLC_ClientInfo info;
+
+ info.m_nSendTableCRC = SendTable_GetCRC();
+ info.m_nServerCount = m_nServerCount;
+ info.m_bIsHLTV = true;
+#if defined( REPLAY_ENABLED )
+ info.m_bIsReplay = false;
+#endif
+ info.m_nFriendsID = 0;
+ info.m_FriendsName[0] = 0;
+
+ // CheckOwnCustomFiles(); // load & verfiy custom player files
+
+ for ( int i=0; i< MAX_CUSTOM_FILES; i++ )
+ info.m_nCustomFiles[i] = 0;
+
+ m_NetChannel->SendNetMsg( info );
+}
+
+
+void CHLTVClientState::SendPacket()
+{
+ if ( !IsConnected() )
+ return;
+
+ if ( ( net_time < m_flNextCmdTime ) || !m_NetChannel->CanPacket() )
+ return;
+
+ if ( IsActive() )
+ {
+ NET_Tick tick( m_nDeltaTick, host_frametime_unbounded, host_frametime_stddeviation );
+ m_NetChannel->SendNetMsg( tick );
+ }
+
+ m_NetChannel->SendDatagram( NULL );
+
+ if ( IsActive() )
+ {
+ // use full update rate when active
+ float commandInterval = (2.0f/3.0f) / tv_snapshotrate.GetInt();
+ float maxDelta = min ( host_state.interval_per_tick, commandInterval );
+ float delta = clamp( (float)(net_time - m_flNextCmdTime), 0.0f, maxDelta );
+ m_flNextCmdTime = net_time + commandInterval - delta;
+ }
+ else
+ {
+ // during signon process send only 5 packets/second
+ m_flNextCmdTime = net_time + ( 1.0f / 5.0f );
+ }
+}
+
+bool CHLTVClientState::ProcessStringCmd(NET_StringCmd *msg)
+{
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessSetConVar(NET_SetConVar *msg)
+{
+ if ( !CBaseClientState::ProcessSetConVar( msg ) )
+ return false;
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+void CHLTVClientState::Clear()
+{
+ CBaseClientState::Clear();
+
+ m_pNewClientFrame = NULL;
+ m_pCurrentClientFrame = NULL;
+}
+
+bool CHLTVClientState::ProcessServerInfo(SVC_ServerInfo *msg )
+{
+ // Reset client state
+ Clear();
+
+ // is server a HLTV proxy or demo file ?
+ if ( !m_pHLTV->IsPlayingBack() )
+ {
+ if ( !msg->m_bIsHLTV )
+ {
+ ConMsg ( "Server (%s) is not a SourceTV proxy.\n", m_NetChannel->GetAddress() );
+ Disconnect( "Server is not a SourceTV proxy", true );
+ return false;
+ }
+ }
+
+ // tell HLTV relay to clear everything
+ m_pHLTV->StartRelay();
+
+ // Process the message
+ if ( !CBaseClientState::ProcessServerInfo( msg ) )
+ {
+ Disconnect( "CBaseClientState::ProcessServerInfo failed", true );
+ return false;
+ }
+
+ m_StringTableContainer = m_pHLTV->m_StringTables;
+
+ Assert( m_StringTableContainer->GetNumTables() == 0); // must be empty
+
+#ifndef SHARED_NET_STRING_TABLES
+ // relay uses normal string tables without a change history
+ m_StringTableContainer->EnableRollback( false );
+#endif
+
+ // copy setting from HLTV client to HLTV server
+ m_pHLTV->m_nGameServerMaxClients = m_nMaxClients;
+ m_pHLTV->serverclasses = m_nServerClasses;
+ m_pHLTV->serverclassbits = m_nServerClassBits;
+ m_pHLTV->m_nPlayerSlot = m_nPlayerSlot;
+
+ // copy other settings to HLTV server
+ V_memcpy( m_pHLTV->worldmapMD5.bits, msg->m_nMapMD5.bits, MD5_DIGEST_LENGTH );
+ m_pHLTV->m_flTickInterval = msg->m_fTickInterval;
+
+ host_state.interval_per_tick = msg->m_fTickInterval;
+
+ Q_strncpy( m_pHLTV->m_szMapname, msg->m_szMapName, sizeof(m_pHLTV->m_szMapname) );
+ Q_strncpy( m_pHLTV->m_szSkyname, msg->m_szSkyName, sizeof(m_pHLTV->m_szSkyname) );
+
+ return true;
+}
+
+bool CHLTVClientState::ProcessClassInfo( SVC_ClassInfo *msg )
+{
+ if ( !msg->m_bCreateOnClient )
+ {
+ ConMsg("HLTV SendTable CRC differs from server.\n");
+ Disconnect( "HLTV SendTable CRC differs from server.", true );
+ return false;
+ }
+
+#ifdef _HLTVTEST
+ RecvTable_Term( false );
+#endif
+
+ // Create all of the send tables locally
+ DataTable_CreateClientTablesFromServerTables();
+
+ // Now create all of the server classes locally, too
+ DataTable_CreateClientClassInfosFromServerClasses( this );
+
+ LinkClasses(); // link server and client classes
+
+#ifdef DEDICATED
+ bool bAllowMismatches = false;
+#else
+ bool bAllowMismatches = ( demoplayer && demoplayer->IsPlayingBack() );
+#endif // DEDICATED
+
+ if ( !RecvTable_CreateDecoders( serverGameDLL->GetStandardSendProxies(), bAllowMismatches ) ) // create receive table decoders
+ {
+ Host_EndGame( true, "CL_ParseClassInfo_EndClasses: CreateDecoders failed.\n" );
+ return false;
+ }
+
+ return true;
+}
+
+void CHLTVClientState::PacketEnd( void )
+{
+ // did we get a snapshot with this packet ?
+ if ( m_pNewClientFrame )
+ {
+ // if so, add a new frame to HLTV
+ m_pCurrentClientFrame = m_pHLTV->AddNewFrame( m_pNewClientFrame );
+ delete m_pNewClientFrame; // release own refernces
+ m_pNewClientFrame = NULL;
+ }
+}
+
+bool CHLTVClientState::HookClientStringTable( char const *tableName )
+{
+ INetworkStringTable *table = GetStringTable( tableName );
+ if ( !table )
+ return false;
+
+ // Hook instance baseline table
+ if ( !Q_strcasecmp( tableName, INSTANCE_BASELINE_TABLENAME ) )
+ {
+ table->SetStringChangedCallback( m_pHLTV, HLTV_Callback_InstanceBaseline );
+ return true;
+ }
+
+ return false;
+}
+
+void CHLTVClientState::InstallStringTableCallback( char const *tableName )
+{
+ INetworkStringTable *table = GetStringTable( tableName );
+
+ if ( !table )
+ return;
+
+ // Hook instance baseline table
+ if ( !Q_strcasecmp( tableName, INSTANCE_BASELINE_TABLENAME ) )
+ {
+ table->SetStringChangedCallback( m_pHLTV, HLTV_Callback_InstanceBaseline );
+ return;
+ }
+}
+
+bool CHLTVClientState::ProcessSetView( SVC_SetView *msg )
+{
+ if ( !CBaseClientState::ProcessSetView( msg ) )
+ return false;
+
+ return m_pHLTV->SendNetMsg( *msg );
+}
+
+bool CHLTVClientState::ProcessVoiceInit( SVC_VoiceInit *msg )
+{
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessVoiceData( SVC_VoiceData *msg )
+{
+ int size = PAD_NUMBER( Bits2Bytes(msg->m_nLength), 4);
+ byte *buffer = (byte*) stackalloc( size );
+ msg->m_DataIn.ReadBits( buffer, msg->m_nLength );
+ msg->m_DataOut = buffer;
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessSounds(SVC_Sounds *msg)
+{
+ CopyDataInToOut( msg );
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessPrefetch( SVC_Prefetch *msg )
+{
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessFixAngle( SVC_FixAngle *msg )
+{
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessCrosshairAngle( SVC_CrosshairAngle *msg )
+{
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessBSPDecal( SVC_BSPDecal *msg )
+{
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessGameEvent( SVC_GameEvent *msg )
+{
+ bf_read tmpBuf = msg->m_DataIn;
+
+ IGameEvent *event = g_GameEventManager.UnserializeEvent( &tmpBuf );
+
+ if ( event )
+ {
+ const char *pszName = event->GetName();
+
+ bool bDontForward = false;
+
+ if ( Q_strcmp( pszName, "hltv_status" ) == 0 )
+ {
+ m_pHLTV->m_nGlobalSlots = event->GetInt("slots");;
+ m_pHLTV->m_nGlobalProxies = event->GetInt("proxies");
+ m_pHLTV->m_nGlobalClients = event->GetInt("clients");
+ m_pHLTV->m_RootServer.SetFromString( event->GetString("master") );
+ bDontForward = true;
+ }
+ else if ( Q_strcmp( pszName, "hltv_title" ) == 0 )
+ {
+ // ignore title messages
+ bDontForward = true;
+ }
+
+ // free event resources
+ g_GameEventManager.FreeEvent( event );
+
+ if ( bDontForward )
+ return true;
+ }
+
+ // forward event
+ CopyDataInToOut( msg );
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessGameEventList( SVC_GameEventList *msg )
+{
+ // copy message before processing
+ SVC_GameEventList tmpMsg = *msg;
+ CBaseClientState::ProcessGameEventList( &tmpMsg );
+
+ CopyDataInToOut( msg );
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessUserMessage( SVC_UserMessage *msg )
+{
+ CopyDataInToOut( msg );
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessEntityMessage( SVC_EntityMessage *msg )
+{
+ CopyDataInToOut( msg );
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessMenu( SVC_Menu *msg )
+{
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+bool CHLTVClientState::ProcessPacketEntities( SVC_PacketEntities *entmsg )
+{
+ CClientFrame *oldFrame = NULL;
+
+#ifdef _HLTVTEST
+ if ( g_RecvDecoders.Count() == 0 )
+ return false;
+#endif
+
+ if ( entmsg->m_bIsDelta )
+ {
+ if ( 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 = m_pHLTV->GetClientFrame( entmsg->m_nDeltaFrom );
+ }
+
+ // create new empty snapshot
+ CFrameSnapshot* pSnapshot = framesnapshotmanager->CreateEmptySnapshot( GetServerTickCount(), entmsg->m_nMaxEntries );
+
+ Assert( m_pNewClientFrame == NULL );
+
+ m_pNewClientFrame = new CClientFrame( pSnapshot );
+
+ 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;
+ CopyEntityBaseline( entmsg->m_nBaseline, nUpdateBaseline );
+
+ // send new baseline acknowledgement(as reliable)
+ CLC_BaselineAck baseline( GetServerTickCount(), entmsg->m_nBaseline );
+ m_NetChannel->SendNetMsg( baseline, true );
+ }
+
+ // copy classes and serial numbers from current frame
+ if ( m_pCurrentClientFrame )
+ {
+ CFrameSnapshot* pLastSnapshot = m_pCurrentClientFrame->GetSnapshot();
+ CFrameSnapshotEntry *pEntry = pSnapshot->m_pEntities;
+ CFrameSnapshotEntry *pLastEntry = pLastSnapshot->m_pEntities;
+
+ Assert( pLastSnapshot->m_nNumEntities <= pSnapshot->m_nNumEntities );
+
+ for ( int i = 0; i<pLastSnapshot->m_nNumEntities; i++ )
+ {
+ pEntry->m_nSerialNumber = pLastEntry->m_nSerialNumber;
+ pEntry->m_pClass = pLastEntry->m_pClass;
+
+ pEntry++;
+ pLastEntry++;
+ }
+ }
+
+ CEntityReadInfo u;
+ u.m_pBuf = &entmsg->m_DataIn;
+ u.m_pFrom = oldFrame;
+ u.m_pTo = m_pNewClientFrame;
+ u.m_bAsDelta = entmsg->m_bIsDelta;
+ u.m_nHeaderCount = entmsg->m_nUpdatedEntries;
+ u.m_nBaseline = entmsg->m_nBaseline;
+ u.m_bUpdateBaselines = entmsg->m_bUpdateBaseline;
+
+ ReadPacketEntities( u );
+
+ // adjust reference count to be 1
+ pSnapshot->ReleaseReference();
+
+ return CBaseClientState::ProcessPacketEntities( entmsg );
+}
+
+bool CHLTVClientState::ProcessTempEntities( SVC_TempEntities *msg )
+{
+ CopyDataInToOut( msg );
+
+ return m_pHLTV->SendNetMsg( *msg ); // relay to server
+}
+
+void CHLTVClientState::ReadEnterPVS( CEntityReadInfo &u )
+{
+ int iClass = u.m_pBuf->ReadUBitLong( m_nServerClassBits );
+
+ int iSerialNum = u.m_pBuf->ReadUBitLong( NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS );
+
+ CopyNewEntity( u, iClass, iSerialNum );
+
+ if ( u.m_nNewEntity == u.m_nOldEntity ) // that was a recreate
+ u.NextOldEntity();
+}
+
+void CHLTVClientState::ReadLeavePVS( CEntityReadInfo &u )
+{
+ // do nothing, this entity was removed
+ Assert( !u.m_pTo->transmit_entity.Get(u.m_nOldEntity) );
+
+ if ( u.m_UpdateFlags & FHDR_DELETE )
+ {
+ CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot();
+ CFrameSnapshotEntry *pEntry = &pSnapshot->m_pEntities[u.m_nOldEntity];
+
+ // clear entity references
+ pEntry->m_nSerialNumber = -1;
+ pEntry->m_pClass = NULL;
+ Assert( pEntry->m_pPackedData == INVALID_PACKED_ENTITY_HANDLE );
+ }
+
+ u.NextOldEntity();
+
+}
+
+void CHLTVClientState::ReadDeltaEnt( CEntityReadInfo &u )
+{
+ const int i = u.m_nNewEntity;
+ CFrameSnapshot *pFromSnapshot = u.m_pFrom->GetSnapshot();
+
+ CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot();
+
+ Assert( i < pFromSnapshot->m_nNumEntities );
+ pSnapshot->m_pEntities[i] = pFromSnapshot->m_pEntities[i];
+
+ PackedEntity *pToPackedEntity = framesnapshotmanager->CreatePackedEntity( pSnapshot, i );
+
+ // WARNING! get pFromPackedEntity after new pPackedEntity has been created, otherwise pointer may be wrong
+ PackedEntity *pFromPackedEntity = framesnapshotmanager->GetPackedEntity( pFromSnapshot, i );
+
+ pToPackedEntity->SetServerAndClientClass( pFromPackedEntity->m_pServerClass, pFromPackedEntity->m_pClientClass );
+
+ // create a copy of the pFromSnapshot ChangeFrameList
+ IChangeFrameList* pChangeFrame = NULL;
+
+ if ( !m_bSaveMemory )
+ {
+ pChangeFrame = pFromPackedEntity->GetChangeFrameList()->Copy();
+ pToPackedEntity->SetChangeFrameList( pChangeFrame );
+ }
+
+ // Make space for the baseline data.
+ ALIGN4 char packedData[MAX_PACKEDENTITY_DATA] ALIGN4_POST;
+ const void *pFromData;
+ int nFromBits;
+
+ if ( pFromPackedEntity->IsCompressed() )
+ {
+ pFromData = m_pHLTV->UncompressPackedEntity( pFromPackedEntity, nFromBits );
+ }
+ else
+ {
+ pFromData = pFromPackedEntity->GetData();
+ nFromBits = pFromPackedEntity->GetNumBits();
+ }
+
+ bf_read fromBuf( "HLTV_ReadEnterPVS1", pFromData, Bits2Bytes( nFromBits ), nFromBits );
+ bf_write writeBuf( "HLTV_ReadEnterPVS2", packedData, sizeof( packedData ) );
+
+ int changedProps[MAX_DATATABLE_PROPS];
+
+ // decode baseline, is compressed against zero values
+ int nChangedProps = RecvTable_MergeDeltas( pToPackedEntity->m_pClientClass->m_pRecvTable,
+ &fromBuf, u.m_pBuf, &writeBuf, -1, changedProps, false );
+
+ // update change tick in ChangeFrameList
+ if ( pChangeFrame )
+ {
+ pChangeFrame->SetChangeTick( changedProps, nChangedProps, pSnapshot->m_nTickCount );
+ }
+
+ if ( m_bSaveMemory )
+ {
+ int bits = writeBuf.GetNumBitsWritten();
+
+ const char *compressedData = m_pHLTV->CompressPackedEntity(
+ pToPackedEntity->m_pServerClass,
+ (char*)writeBuf.GetData(),
+ bits );
+
+ // store as compressed data and don't use mem pools
+ pToPackedEntity->AllocAndCopyPadded( compressedData, Bits2Bytes(bits) );
+ pToPackedEntity->SetCompressed();
+ }
+ else
+ {
+ // store as normal
+ pToPackedEntity->AllocAndCopyPadded( packedData, writeBuf.GetNumBytesWritten() );
+ }
+
+ u.m_pTo->last_entity = u.m_nNewEntity;
+ u.m_pTo->transmit_entity.Set( u.m_nNewEntity );
+
+ u.NextOldEntity();
+}
+
+void CHLTVClientState::ReadPreserveEnt( CEntityReadInfo &u )
+{
+ // copy one of the old entities over to the new packet unchanged
+
+ // XXX(JohnS): This was historically checking for NewEntity overflow, though this path does not care (and new entity
+ // may be -1). The old entity bounds check here seems like what was intended, but since nNewEntity
+ // should not be overflowed either, I've left that check in case it was guarding against a case I am
+ // overlooking.
+ if ( u.m_nOldEntity >= MAX_EDICTS || u.m_nOldEntity < 0 || u.m_nNewEntity >= MAX_EDICTS )
+ {
+ Host_Error( "CL_ReadPreserveEnt: Entity out of bounds. Old: %i, New: %i",
+ u.m_nOldEntity, u.m_nNewEntity );
+ }
+
+ HLTV_CopyExitingEnt( u );
+
+ u.NextOldEntity();
+}
+
+void CHLTVClientState::ReadDeletions( CEntityReadInfo &u )
+{
+ while ( u.m_pBuf->ReadOneBit()!=0 )
+ {
+ int idx = u.m_pBuf->ReadUBitLong( MAX_EDICT_BITS );
+
+ Assert( !u.m_pTo->transmit_entity.Get( idx ) );
+
+ CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot();
+ CFrameSnapshotEntry *pEntry = &pSnapshot->m_pEntities[idx];
+
+ // clear entity references
+ pEntry->m_nSerialNumber = -1;
+ pEntry->m_pClass = NULL;
+ Assert( pEntry->m_pPackedData == INVALID_PACKED_ENTITY_HANDLE );
+ }
+}
+
+int CHLTVClientState::GetConnectionRetryNumber() const
+{
+ if ( tv_autoretry.GetBool() )
+ {
+ // in autoretry mode try extra long
+ return CL_CONNECTION_RETRIES * 4;
+ }
+ else
+ {
+ return CL_CONNECTION_RETRIES;
+ }
+}
+
+void CHLTVClientState::ConnectionCrashed(const char *reason)
+{
+ CBaseClientState::ConnectionCrashed( reason );
+
+ if ( tv_autoretry.GetBool() && m_szRetryAddress[0] )
+ {
+ Cbuf_AddText( va( "tv_relay %s\n", m_szRetryAddress ) );
+ }
+}
+
+void CHLTVClientState::ConnectionClosing( const char *reason )
+{
+ CBaseClientState::ConnectionClosing( reason );
+
+ if ( tv_autoretry.GetBool() && m_szRetryAddress[0] )
+ {
+ Cbuf_AddText( va( "tv_relay %s\n", m_szRetryAddress ) );
+ }
+}
+
+void CHLTVClientState::RunFrame()
+{
+ CBaseClientState::RunFrame();
+
+ if ( m_NetChannel && m_NetChannel->IsTimedOut() && IsConnected() )
+ {
+ ConMsg ("\nSourceTV connection timed out.\n");
+ Disconnect( "nSourceTV connection timed out", true );
+ return;
+ }
+
+ UpdateStats();
+}
+
+void CHLTVClientState::UpdateStats()
+{
+ if ( m_nSignonState < SIGNONSTATE_FULL )
+ {
+ m_fNextSendUpdateTime = 0.0f;
+ return;
+ }
+
+ if ( m_fNextSendUpdateTime > net_time )
+ return;
+
+ m_fNextSendUpdateTime = net_time + 8.0f;
+
+ int proxies, slots, clients;
+
+ m_pHLTV->GetRelayStats( proxies, slots, clients );
+
+ proxies += 1; // add self to number of proxies
+ slots += m_pHLTV->GetMaxClients(); // add own slots
+ clients += m_pHLTV->GetNumClients(); // add own clients
+
+ NET_SetConVar conVars;
+ NET_SetConVar::cvar_t acvar;
+
+ Q_strncpy( acvar.name, "hltv_proxies", sizeof(acvar.name) );
+ Q_snprintf( acvar.value, sizeof(acvar.value), "%d", proxies );
+ conVars.m_ConVars.AddToTail( acvar );
+
+ Q_strncpy( acvar.name, "hltv_clients", sizeof(acvar.name) );
+ Q_snprintf( acvar.value, sizeof(acvar.value), "%d", clients );
+ conVars.m_ConVars.AddToTail( acvar );
+
+ Q_strncpy( acvar.name, "hltv_slots", sizeof(acvar.name) );
+ Q_snprintf( acvar.value, sizeof(acvar.value), "%d", slots );
+ conVars.m_ConVars.AddToTail( acvar );
+
+ Q_strncpy( acvar.name, "hltv_addr", sizeof(acvar.name) );
+ Q_snprintf( acvar.value, sizeof(acvar.value), "%s:%u", net_local_adr.ToString(true), m_pHLTV->GetUDPPort() );
+ conVars.m_ConVars.AddToTail( acvar );
+
+ m_NetChannel->SendNetMsg( conVars );
+}
+