1155 lines
33 KiB
C++
1155 lines
33 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
#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"
|
|
#include "sv_steamauth.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->m_ClientState.UpdateInstanceBaseline( stringNumber );
|
|
pHLTV->LinkInstanceBaselines();
|
|
}
|
|
|
|
extern CUtlLinkedList< CRecvDecoder *, unsigned short > g_RecvDecoders;
|
|
|
|
extern ConVar tv_autorecord;
|
|
static ConVar tv_autoretry( "tv_autoretry", "1", FCVAR_RELEASE, "Relay proxies retry connection after network timeout" );
|
|
static ConVar tv_timeout( "tv_timeout", "30", FCVAR_RELEASE, "GOTV connection timeout in seconds." );
|
|
ConVar tv_snapshotrate("tv_snapshotrate", "32", FCVAR_RELEASE | FCVAR_REPLICATED, "Snapshots broadcasted per second" ); // for the best quality of replay, use 64
|
|
ConVar tv_snapshotrate1( "tv_snapshotrate1", "32", FCVAR_RELEASE, "Snapshots broadcasted per second, GOTV[1]" ); // set this to 128 to record 128-tick server demo
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
CHLTVClientState::CHLTVClientState(CHLTVServer *pHltvServer) : m_pHLTV( pHltvServer )
|
|
{
|
|
m_pNewClientFrame = NULL;
|
|
m_pCurrentClientFrame = NULL;
|
|
m_bSaveMemory = false;
|
|
|
|
eventid_hltv_status = -1;
|
|
eventid_hltv_title = -1;
|
|
}
|
|
|
|
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.
|
|
int nFromTick = 0; // MOTODO get tick when baseline last changed
|
|
|
|
SerializedEntityHandle_t oldbaseline = SERIALIZED_ENTITY_HANDLE_INVALID;
|
|
|
|
PackedEntity *baseline = u.m_bAsDelta ? GetEntityBaseline( u.m_nBaseline, ent ) : NULL;
|
|
|
|
if ( baseline && baseline->m_pClientClass == pClientClass )
|
|
{
|
|
oldbaseline = baseline->GetPackedData();
|
|
}
|
|
else
|
|
{
|
|
// Every entity must have a static or an instance baseline when we get here.
|
|
ErrorIfNot(
|
|
GetClassBaseline( iClass, &oldbaseline ),
|
|
("HLTV_CopyNewEntity: GetDynamicBaseline(%d) failed.", iClass)
|
|
);
|
|
}
|
|
|
|
// create new ChangeFrameList containing all properties set as changed
|
|
int nFlatProps = SendTable_GetNumFlatProps( pServerClass->m_pTable );
|
|
CChangeFrameList *pChangeFrame = NULL;
|
|
|
|
if ( !m_bSaveMemory )
|
|
{
|
|
pChangeFrame = new CChangeFrameList( 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.
|
|
SerializedEntityHandle_t newbaseline = g_pSerializedEntities->AllocateSerializedEntity(__FILE__, __LINE__);
|
|
|
|
CUtlVector< int > changedProps;
|
|
|
|
RecvTable_ReadFieldList( pClientClass->m_pRecvTable, *u.m_pBuf, u.m_DecodeEntity, -1, false );
|
|
|
|
// decode basline, is compressed against zero values
|
|
RecvTable_MergeDeltas( pClientClass->m_pRecvTable, oldbaseline, u.m_DecodeEntity, newbaseline, -1, &changedProps );
|
|
|
|
// update change tick in ChangeFrameList
|
|
if ( pChangeFrame )
|
|
{
|
|
pChangeFrame->SetChangeTick( changedProps.Base(), changedProps.Count(), pSnapshot->m_nTickCount );
|
|
}
|
|
|
|
if ( u.m_bUpdateBaselines )
|
|
{
|
|
SetEntityBaseline( (u.m_nBaseline==0)?1:0, pClientClass, u.m_nNewEntity, newbaseline );
|
|
}
|
|
|
|
pPackedEntity->CopyPackedData( newbaseline );
|
|
|
|
// 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); // GetBaseLocalClient().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, const CNETMsg_SignonState *msg )
|
|
{
|
|
// ConDMsg ("CL_SignonReply: %i\n", GetBaseLocalClient().signon);
|
|
|
|
if ( !CBaseClientState::SetSignonState( state, count, msg ) )
|
|
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)
|
|
CNETMsg_SetConVar_t convars;
|
|
Host_BuildUserInfoUpdateMessage( 0, convars.mutable_convars(), false );
|
|
|
|
// also set all the userinfo vars that we will be modifying for accurate tracking
|
|
SetLocalInfoConvarsForUpstreamConnection( *convars.mutable_convars(), true );
|
|
|
|
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() )
|
|
{
|
|
m_pHLTV->m_DemoRecorder.StartAutoRecording();
|
|
m_NetChannel->SetDemoRecorder( m_pHLTV->m_DemoRecorder.GetDemoRecorder() );
|
|
}
|
|
break;
|
|
|
|
case SIGNONSTATE_CHANGELEVEL: m_pHLTV->Changelevel( true );
|
|
m_NetChannel->SetTimeout( SIGNON_TIME_OUT ); // allow 5 minutes timeout
|
|
break;
|
|
}
|
|
|
|
if ( m_nSignonState >= SIGNONSTATE_CONNECTED )
|
|
{
|
|
// tell server that we entered now that state
|
|
CNETMsg_SignonState_t signonState( m_nSignonState, count );
|
|
m_NetChannel->SendNetMsg( signonState );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CHLTVClientState::SendClientInfo( void )
|
|
{
|
|
CCLCMsg_ClientInfo_t info;
|
|
|
|
info.set_send_table_crc( SendTable_GetCRC() );
|
|
info.set_server_count( m_nServerCount );
|
|
info.set_is_hltv( true );
|
|
#if defined( REPLAY_ENABLED )
|
|
info.set_is_replay( false );
|
|
#endif
|
|
info.set_friends_id( 0 );
|
|
// info.set_friends_name( "" );
|
|
|
|
// CheckOwnCustomFiles(); // load & verfiy custom player files
|
|
|
|
// for ( int i=0; i< MAX_CUSTOM_FILES; i++ )
|
|
// info.add_custom_files( "" );
|
|
|
|
m_NetChannel->SendNetMsg( info );
|
|
}
|
|
|
|
|
|
void CHLTVClientState::SendPacket()
|
|
{
|
|
if ( !IsConnected() )
|
|
return;
|
|
|
|
if ( ( net_time < m_flNextCmdTime ) || !m_NetChannel->CanPacket() )
|
|
return;
|
|
|
|
if ( IsActive() )
|
|
{
|
|
CNETMsg_Tick_t tick( m_nDeltaTick, host_frameendtime_computationduration, host_frametime_stddeviation, host_framestarttime_stddeviation );
|
|
m_NetChannel->SendNetMsg( tick );
|
|
}
|
|
|
|
m_NetChannel->SendDatagram( NULL );
|
|
|
|
if ( IsActive() )
|
|
{
|
|
// use full update rate when active
|
|
float commandInterval = (2.0f/3.0f) / m_pHLTV->GetSnapshotRate();
|
|
float maxDelta = MIN( host_state.interval_per_tick, commandInterval );
|
|
float delta = clamp( 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::NETMsg_StringCmd(const CNETMsg_StringCmd& msg)
|
|
{
|
|
CNETMsg_StringCmd_t stringcmd( msg.command().c_str() );
|
|
stringcmd.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( stringcmd ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::NETMsg_SetConVar(const CNETMsg_SetConVar& msg)
|
|
{
|
|
if ( !CBaseClientState::NETMsg_SetConVar( msg ) )
|
|
return false;
|
|
|
|
CNETMsg_SetConVar_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
if ( sendmsg.convars().cvars_size() )
|
|
{ // Make sure convars are expanded using dictionary
|
|
for ( int iCV = 0; iCV < sendmsg.convars().cvars_size(); ++iCV )
|
|
{
|
|
CMsg_CVars::CVar *convar = sendmsg.mutable_convars()->mutable_cvars( iCV );
|
|
NetMsgExpandCVarUsingDictionary( convar );
|
|
}
|
|
}
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::NETMsg_PlayerAvatarData( const CNETMsg_PlayerAvatarData& msg )
|
|
{
|
|
// Don't chain to the base client implementation
|
|
return m_pHLTV->NETMsg_PlayerAvatarData( msg ); // relay to server
|
|
}
|
|
|
|
void CHLTVClientState::Clear()
|
|
{
|
|
CBaseClientState::Clear();
|
|
|
|
m_pNewClientFrame = NULL;
|
|
m_pCurrentClientFrame = NULL;
|
|
|
|
eventid_hltv_status = -1;
|
|
eventid_hltv_title = -1;
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_ServerInfo( const CSVCMsg_ServerInfo& msg )
|
|
{
|
|
// Reset client state
|
|
Clear();
|
|
|
|
// is server a HLTV proxy or demo file ?
|
|
if ( !m_pHLTV->IsPlayingBack() )
|
|
{
|
|
if ( !msg.is_hltv() )
|
|
{
|
|
ConMsg ( "Server (%s) is not a GOTV proxy.\n", m_NetChannel->GetAddress() );
|
|
Disconnect();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// tell HLTV relay to clear everything
|
|
m_pHLTV->StartRelay();
|
|
|
|
// Process the message
|
|
if ( !CBaseClientState::SVCMsg_ServerInfo( msg ) )
|
|
{
|
|
Disconnect();
|
|
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
|
|
m_pHLTV->worldmapCRC = msg.map_crc();
|
|
m_pHLTV->clientDllCRC = msg.client_crc();
|
|
m_pHLTV->stringTableCRC = CRC32_ConvertFromUnsignedLong( msg.string_table_crc() );
|
|
m_pHLTV->m_flTickInterval = msg.tick_interval();
|
|
|
|
host_state.interval_per_tick = msg.tick_interval();
|
|
|
|
Q_strncpy( m_pHLTV->m_szMapname, msg.map_name().c_str(), sizeof(m_pHLTV->m_szMapname) );
|
|
Q_strncpy( m_pHLTV->m_szSkyname, msg.sky_name().c_str(), sizeof(m_pHLTV->m_szSkyname) );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_ClassInfo( const CSVCMsg_ClassInfo& msg )
|
|
{
|
|
if ( !msg.create_on_client() )
|
|
{
|
|
ConMsg("HLTV SendTable CRC differs from server.\n");
|
|
Disconnect();
|
|
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
|
|
|
|
#if defined(DEDICATED)
|
|
bool bAllowMismatches = false;
|
|
#else
|
|
bool bAllowMismatches = ( g_pClientDemoPlayer && g_pClientDemoPlayer->IsPlayingBack() );
|
|
#endif
|
|
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::SVCMsg_SetView( const CSVCMsg_SetView& msg )
|
|
{
|
|
if ( !CBaseClientState::SVCMsg_SetView( msg ) )
|
|
return false;
|
|
|
|
CSVCMsg_SetView_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg );
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_VoiceInit( const CSVCMsg_VoiceInit& msg )
|
|
{
|
|
CSVCMsg_VoiceInit_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_VoiceData( const CSVCMsg_VoiceData &msg )
|
|
{
|
|
CSVCMsg_VoiceData_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_EncryptedData( const CSVCMsg_EncryptedData& msg )
|
|
{
|
|
CSVCMsg_EncryptedData_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_Sounds(const CSVCMsg_Sounds& msg)
|
|
{
|
|
CSVCMsg_Sounds_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_Prefetch( const CSVCMsg_Prefetch& msg )
|
|
{
|
|
CSVCMsg_Prefetch_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_FixAngle( const CSVCMsg_FixAngle& msg )
|
|
{
|
|
CSVCMsg_FixAngle_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_CrosshairAngle( const CSVCMsg_CrosshairAngle& msg )
|
|
{
|
|
CSVCMsg_CrosshairAngle_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_BSPDecal( const CSVCMsg_BSPDecal& msg )
|
|
{
|
|
CSVCMsg_BSPDecal_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_GameEvent( const CSVCMsg_GameEvent& msg )
|
|
{
|
|
const char *pszName = msg.event_name().c_str();
|
|
|
|
bool bDontForward = false;
|
|
|
|
if ( msg.eventid() == eventid_hltv_status || Q_strcmp( pszName, "hltv_status" ) == 0 )
|
|
{
|
|
IGameEvent *event = g_GameEventManager.UnserializeEvent( msg );
|
|
m_pHLTV->m_nGlobalSlots = event->GetInt("slots");
|
|
m_pHLTV->m_nGlobalProxies = event->GetInt("proxies");
|
|
m_pHLTV->m_nGlobalClients = event->GetInt("clients");
|
|
m_pHLTV->m_nExternalTotalViewers = event->GetInt( "externaltotal" );
|
|
m_pHLTV->m_nExternalLinkedViewers = event->GetInt( "externallinked" );
|
|
|
|
char const *szMasterAddress = event->GetString("master");
|
|
if ( szMasterAddress && *szMasterAddress )
|
|
m_pHLTV->m_RootServer.SetFromString( szMasterAddress );
|
|
else if ( m_pHLTV->m_RootServer.IsValid() )
|
|
m_pHLTV->m_RootServer.Clear();
|
|
|
|
g_GameEventManager.FreeEvent( event );
|
|
bDontForward = true;
|
|
|
|
// make sure we update GC information now that we updated HLTV status
|
|
if ( serverGameDLL && Steam3Server().GetGSSteamID().IsValid() )
|
|
serverGameDLL->UpdateGCInformation();
|
|
}
|
|
else if ( msg.eventid() == eventid_hltv_title || Q_strcmp( pszName, "hltv_title" ) == 0 )
|
|
{
|
|
// ignore title messages
|
|
bDontForward = true;
|
|
}
|
|
|
|
if ( bDontForward )
|
|
return true;
|
|
|
|
// forward event
|
|
CSVCMsg_GameEvent_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_GameEventList( const CSVCMsg_GameEventList& msg )
|
|
{
|
|
if ( !CBaseClientState::SVCMsg_GameEventList( msg ) )
|
|
return false;
|
|
|
|
// cache off the eventids for hltv_status and hltv_title
|
|
CGameEventDescriptor *pDescriptor_hltv_status = g_GameEventManager.GetEventDescriptor( "hltv_status" );
|
|
CGameEventDescriptor *pDescriptor_hltv_title = g_GameEventManager.GetEventDescriptor( "hltv_title" );
|
|
|
|
if ( pDescriptor_hltv_status )
|
|
{
|
|
eventid_hltv_status = pDescriptor_hltv_status->eventid;
|
|
}
|
|
if ( pDescriptor_hltv_title )
|
|
{
|
|
eventid_hltv_title = pDescriptor_hltv_title->eventid;
|
|
}
|
|
|
|
CSVCMsg_GameEventList_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_UserMessage( const CSVCMsg_UserMessage& msg )
|
|
{
|
|
CSVCMsg_UserMessage_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_EntityMsg( const CSVCMsg_EntityMsg& msg )
|
|
{
|
|
CSVCMsg_EntityMsg_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_Menu( const CSVCMsg_Menu& msg )
|
|
{
|
|
CSVCMsg_Menu_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_PacketEntities( const CSVCMsg_PacketEntities &msg )
|
|
{
|
|
CClientFrame *oldFrame = NULL;
|
|
|
|
#ifdef _HLTVTEST
|
|
if ( g_RecvDecoders.Count() == 0 )
|
|
return false;
|
|
#endif
|
|
|
|
if ( msg.is_delta() )
|
|
{
|
|
if ( GetServerTickCount() == msg.delta_from() )
|
|
{
|
|
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( msg.delta_from() );
|
|
}
|
|
|
|
// create new empty snapshot
|
|
CFrameSnapshot* pSnapshot = framesnapshotmanager->CreateEmptySnapshot(
|
|
#ifdef DEBUG_SNAPSHOT_REFERENCES
|
|
"CHLTVClientState::SVCMsg_PacketEntities",
|
|
#endif
|
|
GetServerTickCount(), msg.max_entries() );
|
|
|
|
Assert( m_pNewClientFrame == NULL );
|
|
|
|
m_pNewClientFrame = new CClientFrame( pSnapshot );
|
|
|
|
Assert( msg.baseline() >= 0 && msg.baseline() < 2 );
|
|
|
|
if ( msg.update_baseline() )
|
|
{
|
|
// server requested to use this snapshot as baseline update
|
|
int nUpdateBaseline = (msg.baseline() == 0) ? 1 : 0;
|
|
CopyEntityBaseline( msg.baseline(), nUpdateBaseline );
|
|
|
|
// send new baseline acknowledgement(as reliable)
|
|
CCLCMsg_BaselineAck_t baseline;
|
|
baseline.set_baseline_tick( GetServerTickCount() );
|
|
baseline.set_baseline_nr( msg.baseline() );
|
|
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;
|
|
bf_read entityBuf( &msg.entity_data()[0], msg.entity_data().size() );
|
|
u.m_pBuf = &entityBuf;
|
|
u.m_pFrom = oldFrame;
|
|
u.m_pTo = m_pNewClientFrame;
|
|
u.m_bAsDelta = msg.is_delta();
|
|
u.m_nHeaderCount = msg.updated_entries();
|
|
u.m_nBaseline = msg.baseline();
|
|
u.m_bUpdateBaselines = msg.update_baseline();
|
|
|
|
ReadPacketEntities( u );
|
|
|
|
// adjust reference count to be 1
|
|
pSnapshot->ReleaseReference();
|
|
|
|
return CBaseClientState::SVCMsg_PacketEntities( msg );
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_TempEntities( const CSVCMsg_TempEntities &msg )
|
|
{
|
|
CSVCMsg_TempEntities_t copy;
|
|
copy.CopyFrom( msg );
|
|
copy.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( copy ); // relay to server
|
|
}
|
|
|
|
bool CHLTVClientState::SVCMsg_PaintmapData( const CSVCMsg_PaintmapData& msg )
|
|
{
|
|
CSVCMsg_PaintmapData_t sendmsg;
|
|
sendmsg.CopyFrom( msg );
|
|
sendmsg.SetReliable( m_NetChannel->WasLastMessageReliable() );
|
|
return m_pHLTV->SendNetMsg( sendmsg ); // 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
|
|
CChangeFrameList* pChangeFrame = pFromPackedEntity->GetChangeFrameList()->Copy();
|
|
pToPackedEntity->SetChangeFrameList( pChangeFrame );
|
|
|
|
// Make space for the baseline data.
|
|
SerializedEntityHandle_t fromEntity = pFromPackedEntity->GetPackedData();
|
|
|
|
SerializedEntityHandle_t outEntity = g_pSerializedEntities->AllocateSerializedEntity(__FILE__, __LINE__);
|
|
|
|
CUtlVector< int > changedProps;
|
|
|
|
RecvTable_ReadFieldList( pToPackedEntity->m_pClientClass->m_pRecvTable, *u.m_pBuf, u.m_DecodeEntity, -1, false );
|
|
|
|
// decode baseline, is compressed against zero values
|
|
RecvTable_MergeDeltas( pToPackedEntity->m_pClientClass->m_pRecvTable,
|
|
fromEntity, u.m_DecodeEntity, outEntity, -1, &changedProps );
|
|
|
|
// update change tick in ChangeFrameList
|
|
if ( pChangeFrame )
|
|
{
|
|
pChangeFrame->SetChangeTick( changedProps.Base(), changedProps.Count(), pSnapshot->m_nTickCount );
|
|
}
|
|
|
|
// store as normal
|
|
pToPackedEntity->SetPackedData( outEntity );
|
|
|
|
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
|
|
if ( u.m_nNewEntity < 0 || u.m_nNewEntity >= MAX_EDICTS )
|
|
{
|
|
Host_Error ("CL_ReadPreserveEnt: u.m_nNewEntity == MAX_EDICTS");
|
|
}
|
|
|
|
HLTV_CopyExitingEnt( u );
|
|
|
|
u.NextOldEntity();
|
|
}
|
|
|
|
void CHLTVClientState::ReadDeletions( CEntityReadInfo &u )
|
|
{
|
|
int nBase = -1;
|
|
int nCount = u.m_pBuf->ReadUBitVar();
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
int nDelta = u.m_pBuf->ReadUBitVar();
|
|
int nSlot = nBase + nDelta;
|
|
|
|
Assert( !u.m_pTo->transmit_entity.Get( nSlot ) );
|
|
|
|
CFrameSnapshot *pSnapshot = u.m_pTo->GetSnapshot();
|
|
CFrameSnapshotEntry *pEntry = &pSnapshot->m_pEntities[nSlot];
|
|
|
|
// clear entity references
|
|
pEntry->m_nSerialNumber = -1;
|
|
pEntry->m_pClass = NULL;
|
|
Assert( pEntry->m_pPackedData == INVALID_PACKED_ENTITY_HANDLE );
|
|
|
|
nBase = nSlot;
|
|
}
|
|
}
|
|
|
|
// Returns false if you should stop reading entities.
|
|
inline static bool CL_DetermineUpdateType( CEntityReadInfo &u )
|
|
{
|
|
if ( !u.m_bIsEntity || ( u.m_nNewEntity > u.m_nOldEntity ) )
|
|
{
|
|
// If we're at the last entity, preserve whatever entities followed it in the old packet.
|
|
// If newnum > oldnum, then the server skipped sending entities that it wants to leave the state alone for.
|
|
if ( !u.m_pFrom || ( u.m_nOldEntity > u.m_pFrom->last_entity ) )
|
|
{
|
|
Assert( !u.m_bIsEntity );
|
|
u.m_UpdateType = Finished;
|
|
return false;
|
|
}
|
|
|
|
// Preserve entities until we reach newnum (ie: the server didn't send certain entities because
|
|
// they haven't changed).
|
|
u.m_UpdateType = PreserveEnt;
|
|
}
|
|
else
|
|
{
|
|
if( u.m_UpdateFlags & FHDR_ENTERPVS )
|
|
{
|
|
u.m_UpdateType = EnterPVS;
|
|
}
|
|
else if( u.m_UpdateFlags & FHDR_LEAVEPVS )
|
|
{
|
|
u.m_UpdateType = LeavePVS;
|
|
}
|
|
else
|
|
{
|
|
u.m_UpdateType = DeltaEnt;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline void CL_ParseDeltaHeader( CEntityReadInfo &u )
|
|
{
|
|
u.m_UpdateFlags = FHDR_ZERO;
|
|
|
|
#ifdef DEBUG_NETWORKING
|
|
int startbit = u.m_pBuf->GetNumBitsRead();
|
|
#endif
|
|
u.m_nNewEntity = u.m_nHeaderBase + 1 + u.m_pBuf->ReadUBitVar();
|
|
|
|
|
|
u.m_nHeaderBase = u.m_nNewEntity;
|
|
|
|
// leave pvs flag
|
|
if ( u.m_pBuf->ReadOneBit() == 0 )
|
|
{
|
|
// enter pvs flag
|
|
if ( u.m_pBuf->ReadOneBit() != 0 )
|
|
{
|
|
u.m_UpdateFlags |= FHDR_ENTERPVS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u.m_UpdateFlags |= FHDR_LEAVEPVS;
|
|
|
|
// Force delete flag
|
|
if ( u.m_pBuf->ReadOneBit() != 0 )
|
|
{
|
|
u.m_UpdateFlags |= FHDR_DELETE;
|
|
}
|
|
}
|
|
// Output the bitstream...
|
|
#ifdef DEBUG_NETWORKING
|
|
int lastbit = u.m_pBuf->GetNumBitsRead();
|
|
{
|
|
void SpewBitStream( unsigned char* pMem, int bit, int lastbit );
|
|
SpewBitStream( (byte *)u.m_pBuf->m_pData, startbit, lastbit );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void CHLTVClientState::ReadPacketEntities( CEntityReadInfo &u )
|
|
{
|
|
// Loop until there are no more entities to read
|
|
|
|
u.NextOldEntity();
|
|
|
|
while ( u.m_UpdateType < Finished )
|
|
{
|
|
u.m_nHeaderCount--;
|
|
|
|
u.m_bIsEntity = ( u.m_nHeaderCount >= 0 ) ? true : false;
|
|
|
|
if ( u.m_bIsEntity )
|
|
{
|
|
CL_ParseDeltaHeader( u );
|
|
}
|
|
|
|
u.m_UpdateType = PreserveEnt;
|
|
|
|
while( u.m_UpdateType == PreserveEnt )
|
|
{
|
|
// Figure out what kind of an update this is.
|
|
if( CL_DetermineUpdateType( u ) )
|
|
{
|
|
switch( u.m_UpdateType )
|
|
{
|
|
case EnterPVS: ReadEnterPVS( u );
|
|
break;
|
|
|
|
case LeavePVS: ReadLeavePVS( u );
|
|
break;
|
|
|
|
case DeltaEnt: ReadDeltaEnt( u );
|
|
break;
|
|
|
|
case PreserveEnt: ReadPreserveEnt( u );
|
|
break;
|
|
|
|
default: DevMsg(1, "ReadPacketEntities: unknown updatetype %i\n", u.m_UpdateType );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now process explicit deletes
|
|
if ( u.m_bAsDelta && u.m_UpdateType == Finished )
|
|
{
|
|
ReadDeletions( u );
|
|
}
|
|
|
|
// Something didn't parse...
|
|
if ( u.m_pBuf->IsOverflowed() )
|
|
{
|
|
Host_Error ( "CL_ParsePacketEntities: buffer read overflow\n" );
|
|
}
|
|
|
|
// If we get an uncompressed packet, then the server is waiting for us to ack the validsequence
|
|
// that we got the uncompressed packet on. So we stop reading packets here and force ourselves to
|
|
// send the clc_move on the next frame.
|
|
|
|
if ( !u.m_bAsDelta )
|
|
{
|
|
m_flNextCmdTime = 0.0; // answer ASAP to confirm full update tick
|
|
}
|
|
}
|
|
|
|
int CHLTVClientState::GetConnectionRetryNumber() const
|
|
{
|
|
if ( tv_autoretry.GetBool() )
|
|
{
|
|
// in autoretry mode try extra long
|
|
return CL_CONNECTION_RETRIES * 4;
|
|
}
|
|
|
|
return CBaseClientState::GetConnectionRetryNumber();
|
|
}
|
|
|
|
void CHLTVClientState::ConnectionCrashed(const char *reason)
|
|
{
|
|
CBaseClientState::ConnectionCrashed( reason );
|
|
|
|
if ( tv_autoretry.GetBool() && m_Remote.Count() > 0 )
|
|
{
|
|
Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "tv_relay %s\n", m_Remote.Get( 0 ).m_szRetryAddress.String() ) );
|
|
}
|
|
}
|
|
|
|
void CHLTVClientState::ConnectionClosing( const char *reason )
|
|
{
|
|
CBaseClientState::ConnectionClosing( reason );
|
|
|
|
if ( tv_autoretry.GetBool() && m_Remote.Count() > 0 )
|
|
{
|
|
Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "tv_relay %s\n", m_Remote.Get( 0 ).m_szRetryAddress.String() ) );
|
|
}
|
|
}
|
|
|
|
void CHLTVClientState::Disconnect( bool bShowMainMenu /* = true */ )
|
|
{
|
|
CBaseClientState::Disconnect( bShowMainMenu );
|
|
|
|
// Update hibernation state on the main server which controls hibernation and shutdown criteria
|
|
sv.UpdateHibernationState();
|
|
|
|
// Shutdown if we are an official relay
|
|
if ( serverGameDLL && serverGameDLL->IsValveDS() && !sv.IsActive() )
|
|
{
|
|
Msg( "Official relay disconnected and shutting down...\n" );
|
|
Cbuf_AddText( CBUF_SERVER, "quit;\n" );
|
|
}
|
|
}
|
|
|
|
void CHLTVClientState::RunFrame()
|
|
{
|
|
CBaseClientState::RunFrame();
|
|
|
|
if ( m_NetChannel && m_NetChannel->IsTimedOut() && IsConnected() )
|
|
{
|
|
ConMsg ("\nGOTV connection timed out.\n");
|
|
Disconnect();
|
|
return;
|
|
}
|
|
|
|
UpdateStats();
|
|
}
|
|
|
|
void CHLTVClientState::SetLocalInfoConvarsForUpstreamConnection( CMsg_CVars &cvars, bool bMaxSlots )
|
|
{
|
|
int proxies, slots, clients;
|
|
|
|
m_pHLTV->GetRelayStats( proxies, slots, clients );
|
|
|
|
extern ConVar tv_maxclients_relayreserved;
|
|
int numSlots = m_pHLTV->GetMaxClients();
|
|
if ( !numSlots && bMaxSlots )
|
|
{
|
|
extern ConVar tv_maxclients;
|
|
numSlots = tv_maxclients.GetInt();
|
|
}
|
|
if ( tv_maxclients_relayreserved.GetInt() > 0 )
|
|
numSlots -= tv_maxclients_relayreserved.GetInt();
|
|
numSlots = MAX( 0, numSlots );
|
|
|
|
int numClients = m_pHLTV->GetNumClients();
|
|
if ( numClients > numSlots )
|
|
numSlots = numClients;
|
|
|
|
proxies += 1; // add self to number of proxies
|
|
slots += numSlots; // add own slots
|
|
clients += numClients; // add own clients
|
|
|
|
// let server know that we are a proxy server and all our stats
|
|
NetMsgSetCVarUsingDictionary( cvars.add_cvars(), "tv_relay", "1" );
|
|
NetMsgSetCVarUsingDictionary( cvars.add_cvars(), "hltv_proxies", va( "%d", proxies ) );
|
|
NetMsgSetCVarUsingDictionary( cvars.add_cvars(), "hltv_clients", va( "%d", clients ) );
|
|
NetMsgSetCVarUsingDictionary( cvars.add_cvars(), "hltv_slots", va( "%d", slots ) );
|
|
|
|
static ConVarRef ipname_relay( "ip_relay" ); // Override relay IP for NAT hosts
|
|
netadr_t netAdrHltvRelay( net_local_adr );
|
|
if ( ipname_relay.IsValid() && ipname_relay.GetString()[0] )
|
|
{
|
|
netadr_t netAdrIpRelay;
|
|
NET_StringToAdr( ipname_relay.GetString(), &netAdrIpRelay );
|
|
if ( !netAdrIpRelay.IsLoopback() && !netAdrIpRelay.IsLocalhost() &&
|
|
netAdrIpRelay.IsBaseAdrValid() )
|
|
{
|
|
// Good address specified in ipname_relay
|
|
netAdrHltvRelay = netAdrIpRelay;
|
|
}
|
|
}
|
|
NetMsgSetCVarUsingDictionary( cvars.add_cvars(), "hltv_addr", va( "%s:%u", netAdrHltvRelay.ToString( true ), m_pHLTV->GetUDPPort() ) );
|
|
|
|
static ConVarRef sv_steamdatagramtransport_port( "sv_steamdatagramtransport_port" );
|
|
if ( serverGameDLL->IsValveDS() && sv_steamdatagramtransport_port.GetInt() > 0 )
|
|
{
|
|
ns_address nsadrsdr;
|
|
nsadrsdr.SetAddrType( NSAT_PROXIED_GAMESERVER );
|
|
nsadrsdr.m_steamID.SetFromSteamID( Steam3Server().GetGSSteamID(), sv_steamdatagramtransport_port.GetInt() );
|
|
NetMsgSetCVarUsingDictionary( cvars.add_cvars(), "hltv_sdr", ns_address_render( nsadrsdr ).String() );
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
CNETMsg_SetConVar_t conVars;
|
|
SetLocalInfoConvarsForUpstreamConnection( *conVars.mutable_convars() );
|
|
m_NetChannel->SendNetMsg( conVars );
|
|
}
|
|
|