csgo-2018-source/engine/baseclientstate.h
2021-07-24 21:11:47 -07:00

599 lines
22 KiB
C++

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef BASECLIENTSTATE_H
#define BASECLIENTSTATE_H
#ifdef _WIN32
#pragma once
#endif
#include "inetmsghandler.h"
#include "protocol.h"
#include "client_class.h"
#include "cdll_int.h"
#include "tier1/netadr.h"
#include "common.h"
#include "clockdriftmgr.h"
#include "convar.h"
#include "cl_bounded_cvars.h"
#include "tier1/utlstring.h"
#include "netmessages.h"
#include "utlmap.h"
#include "matchmaking/imatchasync.h"
#include "netmessages.pb.h"
// Only send this many requests before timing out.
#define CL_CONNECTION_RETRIES 4
// Mininum time gap (in seconds) before a subsequent connection request is sent.
#define CL_MIN_RESEND_TIME 1.5f
// Max time. The cvar cl_resend is bounded by these.
#define CL_MAX_RESEND_TIME 20.0f
// In release, send commands at least this many times per second
#define MIN_CMD_RATE 10.0f
#define MAX_CMD_RATE 128.0f
typedef intp SerializedEntityHandle_t;
extern ConVar cl_name;
abstract_class CBaseClientState;
// This represents a server's
class C_ServerClassInfo
{
public:
C_ServerClassInfo();
~C_ServerClassInfo();
public:
ClientClass *m_pClientClass;
char *m_ClassName;
char *m_DatatableName;
// This is an index into the network string table (GetBaseLocalClient().GetInstanceBaselineTable()).
int m_InstanceBaselineIndex; // INVALID_STRING_INDEX if not initialized yet.
};
#define EndGameAssertMsg( assertion, msg ) \
if ( !(assertion) )\
Host_EndGame msg
class CNetworkStringTableContainer;
class PackedEntity;
class INetworkStringTable;
class CEntityReadInfo;
struct DeferredConnection_t
{
DeferredConnection_t()
{
m_bActive = false;
m_nChallenge = 0;
m_nAuthprotocol = 0;
m_unGSSteamID = 0ull;;
m_unLobbyID = 0ull;
m_bGSSecure = false;
m_bRequiresPassword = false;
m_bDCFriendsReqd = false;
m_bOfficialValveServer = false;
m_nEncryptionKey = 0;
m_nEncryptedSize = 0;
memset( m_chLobbyType, 0, sizeof( m_chLobbyType ) );
}
bool m_bActive;
bool m_bGSSecure;
bool m_bRequiresPassword;
bool m_bDCFriendsReqd;
bool m_bOfficialValveServer;
char m_chLobbyType[16];
int m_nChallenge;
int m_nAuthprotocol;
uint64 m_unGSSteamID;
uint64 m_unLobbyID;
ns_address m_adrServerAddress;
int m_nEncryptionKey;
int m_nEncryptedSize;
};
// 0 == public, 1 == private, 2 == double NAT'd private (for direct connection)
struct Remote_t
{
bool Resolve();
CUtlString m_szAlias; // debugging: "public", "private", "direct"
CUtlString m_szRetryAddress;
// Address for actual packets (may differ from m_szRetryAddress)
ns_address m_adrRemote;
};
class CAddressList
{
public:
CAddressList() {}
void RemoveAll();
bool IsRemoteInList( char const *pchAdrCheck ) const;
bool IsAddressInList( const ns_address &adr ) const;
// Only adds if not in list already
void AddRemote( char const *pchAddress, char const *pchAlias );
void Describe( CUtlString &str );
int Count() const;
Remote_t &Get( int index );
const Remote_t &Get( int index ) const;
private:
CUtlVector< Remote_t > m_List;
};
// Classes to help with sending messages to server, with retries and timeouts
// Start by sending the message immediately. Wait for timeout before
// sending message again.
// If maxAttempts exceeded with no response set state to AOS_FAILED and call callback.
// If a response is received set state to AOS_SUCCEEDED and call callback.
// Result is only valid if state == AOS_SUCCEEDED
class CServerMsg : public IMatchAsyncOperation
{
public:
// Methods of IMatchAsyncOperation
bool IsFinished() { return m_eState > AOS_ABORTING; }
AsyncOperationState_t GetState() { return m_eState; }
uint64 GetResult() { return m_result; }
void Abort() { m_eState = AOS_ABORTED; }
// Timeout in seconds
explicit CServerMsg( CBaseClientState *pParent, IMatchAsyncOperationCallback *pCallback,
const ns_address& serverAdr, int socket, uint32 maxAttempts, double timeout );
// Per frame update, check for timeout etc if state == AOS_RUNNING
void Update( void );
// Call this function to check if it is ok to process a msg
bool IsValidResponse( const ns_address &from, uint32 token );
// Called by derived class when response received. Sets state to AOS_SUCCEEDED
// and calls callback
void ResponseReceived( uint64 result );
// Methods derived class must implement. Token is a random number that can be
// used to match up a message-response pair. A new token is generated for each
// SendMsg call; the last token generated can be queried using GetLastToken()
virtual void SendMsg( const ns_address& serverAdr, int socket, uint32 token ) = 0;
// See comment for SendMsg()
uint32 GetLastToken() { return m_lastToken; }
const ns_address& GetServerAddr() { return m_serverAdr; }
// base client state parent
CBaseClientState *m_pParent;
// Data members
AsyncOperationState_t m_eState;
IMatchAsyncOperationCallback *m_pCallback;
ns_address m_serverAdr;
int m_socket;
double m_lastMsgSendTime;
double m_timeOut;
uint32 m_maxAttempts;
uint32 m_numAttempts;
uint64 m_result;
uint32 m_lastToken; // See comment for SendMsg()
};
class CServerMsg_CheckReservation : public CServerMsg
{
public:
explicit CServerMsg_CheckReservation( CBaseClientState *pParent, IMatchAsyncOperationCallback *pCallback,
const ns_address &serverAdr, int socket, uint64 reservationCookie, uint32 uiReservationStage );
void Release();
void SendMsg( const ns_address& serverAdr, int socket, uint32 token );
void ResponseReceived( const ns_address& from, bf_read &msg, int32 hostVersion, uint32 token );
uint64 m_reservationCookie;
uint32 m_uiReservationStage;
};
class CServerMsg_Ping : public CServerMsg
{
public:
explicit CServerMsg_Ping( CBaseClientState *pParent, IMatchAsyncOperationCallback *pCallback,
const ns_address &serverAdr, int socket );
void Release();
void SendMsg( const ns_address& serverAdrserverAdr, int socket, uint32 token );
void ResponseReceived( const ns_address& from, bf_read &msg, int32 hostVersion, uint32 token );
double m_timeLastMsgSent;
};
// CBaseClientState
abstract_class CBaseClientState :
public INetChannelHandler,
public IConnectionlessPacketHandler
{
public:
CBaseClientState();
virtual ~CBaseClientState();
public: // IConnectionlessPacketHandler interface:
virtual bool ProcessConnectionlessPacket(struct netpacket_s *packet);
public: // INetMsgHandler interface:
virtual void ConnectionStart(INetChannel *chan) OVERRIDE;
virtual void ConnectionStop( ) OVERRIDE;
virtual void ConnectionClosing( const char *reason );
virtual void ConnectionCrashed(const char *reason);
virtual void PacketStart(int incoming_sequence, int outgoing_acknowledged) {};
virtual void PacketEnd( void ) {};
virtual void FileReceived( const char *fileName, unsigned int transferID, bool isReplayDemoFile );
virtual void FileRequested(const char *fileName, unsigned int transferID, bool isReplayDemoFile );
virtual void FileDenied(const char *fileName, unsigned int transferID, bool isReplayDemoFile );
virtual void FileSent(const char *fileName, unsigned int transferID, bool isReplayDemoFile );
virtual bool ChangeSplitscreenUser( int nSplitScreenUserSlot ); // interleaved networking used by SS system is changing the SS player slot that the subsequent messages pertain to
public: // IServerMessageHandlers
virtual bool NETMsg_Tick( const CNETMsg_Tick& msg );
static bool NETMsg_Tick_Delegate( CBaseClientState *pThis, const CNETMsg_Tick& msg ) { return pThis->NETMsg_Tick( msg ); }
virtual bool NETMsg_StringCmd( const CNETMsg_StringCmd& msg );
bool NETMsg_SignonState( const CNETMsg_SignonState& msg );
virtual bool NETMsg_PlayerAvatarData( const CNETMsg_PlayerAvatarData& msg );
virtual bool NETMsg_SetConVar( const CNETMsg_SetConVar& msg );
bool SVCMsg_CmdKeyValues( const CSVCMsg_CmdKeyValues& msg);
virtual bool SVCMsg_EncryptedData( const CSVCMsg_EncryptedData& msg );
bool SVCMsg_SendTable( const CSVCMsg_SendTable& msg );
bool SVCMsg_Print( const CSVCMsg_Print& msg );
virtual bool SVCMsg_ServerInfo( const CSVCMsg_ServerInfo& msg );
virtual bool SVCMsg_ClassInfo( const CSVCMsg_ClassInfo& msg );
virtual bool SVCMsg_SetPause( const CSVCMsg_SetPause& msg );
virtual bool SVCMsg_SetView( const CSVCMsg_SetView& msg );
virtual bool SVCMsg_CreateStringTable( const CSVCMsg_CreateStringTable& msg );
virtual bool SVCMsg_UpdateStringTable( const CSVCMsg_UpdateStringTable& msg );
virtual bool SVCMsg_VoiceInit( const CSVCMsg_VoiceInit& msg ) = 0;
virtual bool SVCMsg_VoiceData( const CSVCMsg_VoiceData& msg ) = 0;
virtual bool SVCMsg_FixAngle( const CSVCMsg_FixAngle& msg ) = 0;
virtual bool SVCMsg_Prefetch( const CSVCMsg_Prefetch& msg ) = 0;
virtual bool SVCMsg_CrosshairAngle( const CSVCMsg_CrosshairAngle& msg ) = 0;
virtual bool SVCMsg_BSPDecal( const CSVCMsg_BSPDecal& msg ) = 0;
virtual bool SVCMsg_SplitScreen( const CSVCMsg_SplitScreen& msg );
virtual bool SVCMsg_GetCvarValue( const CSVCMsg_GetCvarValue& msg );
virtual bool SVCMsg_Menu( const CSVCMsg_Menu& msg );
virtual bool SVCMsg_UserMessage( const CSVCMsg_UserMessage& msg ) = 0;
virtual bool SVCMsg_PaintmapData( const CSVCMsg_PaintmapData& msg ) = 0;
virtual bool SVCMsg_GameEvent( const CSVCMsg_GameEvent& msg ) = 0;
virtual bool SVCMsg_GameEventList( const CSVCMsg_GameEventList &msg );
virtual bool SVCMsg_TempEntities( const CSVCMsg_TempEntities& msg ) = 0;
virtual bool SVCMsg_PacketEntities( const CSVCMsg_PacketEntities& msg );
virtual bool SVCMsg_Sounds( const CSVCMsg_Sounds& msg ) = 0;
virtual bool SVCMsg_EntityMsg( const CSVCMsg_EntityMsg& msg ) = 0;
CNetMessageBinder m_NETMsgTick;
CNetMessageBinder m_NETMsgStringCmd;
CNetMessageBinder m_NETMsgSignonState;
CNetMessageBinder m_NETMsgSetConVar;
CNetMessageBinder m_NETMsgPlayerAvatarData;
CNetMessageBinder m_SVCMsgServerInfo;
CNetMessageBinder m_SVCMsgCmdKeyValues;
CNetMessageBinder m_SVCMsg_EncryptedData;
CNetMessageBinder m_SVCMsgClassInfo;
CNetMessageBinder m_SVCMsgSendTable;
CNetMessageBinder m_SVCMsgPrint;
CNetMessageBinder m_SVCMsgSetPause;
CNetMessageBinder m_SVCMsgSetView;
CNetMessageBinder m_SVCMsgCreateStringTable;
CNetMessageBinder m_SVCMsgUpdateStringTable;
CNetMessageBinder m_SVCMsgVoiceInit;
CNetMessageBinder m_SVCMsgVoiceData;
CNetMessageBinder m_SVCMsgFixAngle;
CNetMessageBinder m_SVCMsgPrefetch;
CNetMessageBinder m_SVCMsgCrosshairAngle;
CNetMessageBinder m_SVCMsgBSPDecal;
CNetMessageBinder m_SVCMsgSplitScreen;
CNetMessageBinder m_SVCMsgGetCvarValue;
CNetMessageBinder m_SVCMsgMenu;
CNetMessageBinder m_SVCMsgUserMessage;
CNetMessageBinder m_SVCMsgPaintmapData;
CNetMessageBinder m_SVCMsgGameEvent;
CNetMessageBinder m_SVCMsgGameEventList;
CNetMessageBinder m_SVCMsgTempEntities;
CNetMessageBinder m_SVCMsgPacketEntities;
CNetMessageBinder m_SVCMsgSounds;
CNetMessageBinder m_SVCMsgEntityMsg;
public: // IMatchEventsSink
virtual void OnEvent( KeyValues *pEvent );
public:
inline bool IsActive( void ) const { return m_nSignonState == SIGNONSTATE_FULL; };
inline bool IsConnected( void ) const { return m_nSignonState >= SIGNONSTATE_CONNECTED; };
inline bool IsConnecting( void ) const { return m_nSignonState >= SIGNONSTATE_NONE; }
virtual void Clear( void );
virtual void FullConnect( const ns_address &adr, int nEncryptionKey ); // a connection was established
virtual void Connect( const char *pchPublicAddress, char const *pchPrivateAddress, const char* szJoinType ); // start a connection challenge
virtual void ConnectSplitScreen( const char *pchPublicAddress, char const *pchPrivateAddress, int numPlayers, const char* szJoinType ); // start a connection challenge
virtual bool SetSignonState ( int state, int count, const CNETMsg_SignonState *msg );
virtual void Disconnect( bool bShowMainMenu = true );
virtual void SendConnectPacket ( const ns_address &netAdrRemote, int challengeNr, int authProtocol, uint64 unGSSteamID, bool bGSSecure );
virtual const char *GetCDKeyHash() { return "123"; }
virtual void RunFrame ( void );
virtual void CheckForResend ( bool bForceResendNow = false );
virtual void CheckForReservationResend( void );
virtual void ResendGameDetailsRequest( const ns_address &adr );
virtual void InstallStringTableCallback( char const *tableName ) { }
virtual bool HookClientStringTable( char const *tableName ) { return false; }
virtual bool LinkClasses( void );
virtual int GetConnectionRetryNumber() const { return m_nRetryMax; }
virtual const char *GetClientName() { return cl_name.GetString(); }
virtual void ReserveServer( const ns_address &netAdrPublic, const ns_address &netAdrPrivate, uint64 nServerReservationCookie,
KeyValues *pKVGameSettings, IMatchAsyncOperationCallback *pCallback, IMatchAsyncOperation **ppAsyncOperation );
bool CheckServerReservation( const ns_address &netAdrPublic, uint64 nServerReservationCookie, uint32 uiReservationStage, IMatchAsyncOperationCallback *pCallback, IMatchAsyncOperation **ppAsyncOperation );
bool ServerPing( const ns_address &netAdrPublic, IMatchAsyncOperationCallback *pCallback, IMatchAsyncOperation **ppAsyncOperation );
struct ReservationResponseReply_t
{
ReservationResponseReply_t() { m_uiResponse = 0; m_bValveDS = false; m_numGameSlots = 0; }
ns_address m_adrFrom;
uint8 m_uiResponse;
bool m_bValveDS;
uint32 m_numGameSlots;
};
virtual void HandleReservationResponse( const ReservationResponseReply_t &reply );
virtual void HandleReserveServerChallengeResponse( int nChallengeNr );
virtual void SetServerReservationCookie( uint64 nReservationCookie ) { m_nServerReservationCookie = nReservationCookie; }
static ClientClass* FindClientClass(const char *pClassName);
CClockDriftMgr& GetClockDriftMgr();
int GetClientTickCount() const; // Get the client tick count.
void SetClientTickCount( int tick );
int GetServerTickCount() const;
void SetServerTickCount( int tick );
void SetClientAndServerTickCount( int tick );
INetworkStringTable *GetStringTable( const char * name ) const;
PackedEntity *GetEntityBaseline( int iBaseline, int nEntityIndex );
void SetEntityBaseline(int iBaseline, ClientClass *pClientClass, int index, SerializedEntityHandle_t handle);
void CopyEntityBaseline( int iFrom, int iTo );
void FreeEntityBaselines();
bool GetClassBaseline( int iClass, SerializedEntityHandle_t *pHandle );
void UpdateInstanceBaseline( int nStringNumber );
ClientClass *GetClientClass( int i );
void ForceFullUpdate( char const *pchReason );
void SendStringCmd(const char * command);
virtual void ReadPacketEntities( CEntityReadInfo &u ) = 0;
int GetViewEntity();
void HandleDeferredConnection();
void SetConnectionPassword( char const *pchCurrentPW );
void ResetConnectionRetries();
virtual bool IsClientStateTv() const { return false; }
protected:
bool InternalProcessStringCmd( const CNETMsg_StringCmd& msg );
private:
bool PrepareSteamConnectResponse( uint64 unGSSteamID, bool bGSSecure, const ns_address &adr, bf_write &msg );
void SendReserveServerMsg();
void SendReserveServerChallenge();
void BuildReserveServerPayload( bf_write &msg, int nChallengeNr );
void SendReservationCheckMsg( const ns_address &netAdrPublic, uint64 nServerReservationCookie );
int FindSplitPlayerSlot( int nPlayerIndex );
void ConnectInternal( const char *pchPublicAddress, char const *pchPrivateAddress, int numPlayers, const char* szJoinType );
void RememberIPAddressForLobby( uint64 unLobbyID, const ns_address &adrRemote );
bool IsRemoteInList( char const *pchAdrCheck ) const;
bool ShouldUseDirectConnectAddress( const CAddressList &list ) const;
public:
// Connection to server.
int m_Socket; // network socket
INetChannel *m_NetChannel; // Our sequenced channel to the remote server.
bool m_bSplitScreenUser;
unsigned int m_nChallengeNr; // connection challenge number
double m_flConnectTime; // If gap of connect_time to net_time > 3000, then resend connect packet
int m_nRetryNumber; // number of retry connection attempts
int m_nRetryMax; // max # of retry attempts allowed
// Address for actual packets (may differ from m_szRetryAddress)
CAddressList m_Remote;
struct DirectConnectLobby_t
{
DirectConnectLobby_t() : m_flEndTime( -1 ), m_unLobbyID( 0ull ) {}
float m_flEndTime;
ns_address m_adrRemote;
uint64 m_unLobbyID;
};
DirectConnectLobby_t m_DirectConnectLobby;
uint64 m_ListenServerSteamID;
int m_nSignonState; // see SIGNONSTATE_* definitions
double m_flNextCmdTime; // When can we send the next command packet?
int m_nServerCount; // server identification for prespawns, must match the svs.spawncount which
// is incremented on server spawning. This supercedes svs.spawn_issued, in that
// we can now spend a fair amount of time sitting connected to the server
// but downloading models, sounds, etc. So much time that it is possible that the
// server might change levels again and, if so, we need to know that.
int m_nCurrentSequence; // this is the sequence number of the current incoming packet
uint64 m_ulGameServerSteamID; // Steam ID of the game server we are trying to connect to, or are connected to. Zero if unknown
CClockDriftMgr m_ClockDriftMgr;
int m_nDeltaTick; // last valid received snapshot (server) tick
bool m_bPaused; // send over by server
int m_nViewEntity; // player point of view override
int m_nPlayerSlot; // own player entity index-1. skips world. Add 1 to get cl_entitites index;
int m_nSplitScreenSlot;
char m_szLevelName[ MAX_PATH ]; // for display on solo scoreboard
char m_szLevelNameShort[ 40 ]; // removes maps/ and .bsp extension
char m_szMapGroupName[ 40 ]; //the name of the map group we are currently playing in
char m_szLastLevelNameShort[ 40 ]; // stores the previous value of m_szLevelNameShort when that gets cleared
PublishedFileId_t m_unUGCMapFileID; // If a community map, this is the published file id
int m_nMaxClients; // max clients on server
int m_nNumPlayersToConnect; // number of clients to connect to server.
class CAsyncOperation_ReserveServer : public IMatchAsyncOperation
{
public:
explicit CAsyncOperation_ReserveServer( CBaseClientState *pParent ) : m_eState( AOS_RUNNING ), m_pParent( pParent )
{
m_numGameSlotsForReservation = 0;
}
virtual bool IsFinished() { return m_eState > AOS_ABORTING; }
virtual AsyncOperationState_t GetState() { return m_eState; }
virtual uint64 GetResult();
virtual uint64 GetResultExtraInfo() { return m_numGameSlotsForReservation; }
virtual void Abort() {}
virtual void Release()
{
if ( m_pParent && m_pParent->m_pServerReservationOperation == this )
{
m_pParent->m_pServerReservationOperation = NULL;
m_pParent->m_pServerReservationCallback = NULL;
}
delete this;
}
public:
AsyncOperationState_t m_eState;
ns_address m_adr;
CBaseClientState *m_pParent;
uint32 m_numGameSlotsForReservation;
};
CAsyncOperation_ReserveServer *m_pServerReservationOperation; // server reservation operation
IMatchAsyncOperationCallback *m_pServerReservationCallback; // callback for pending reservation request
CUtlVector< CServerMsg_CheckReservation * > m_arrSvReservationCheck;
CUtlVector< CServerMsg_Ping * > m_arrSvPing;
uint64 m_nServerReservationCookie; // cookie to set during reservation and provide upon connection
KeyValues *m_pKVGameSettings; // game settings to request on reserved server
double m_flReservationMsgSendTime; // time we last sent reservation msg
int m_nReservationMsgRetryNumber; // # of times we've retried sending reservation msg
CAddressList m_netadrReserveServer; // netadr of server we're trying to reserve
bool m_bEnteredPassword;
bool m_bWaitingForPassword;
#if ENGINE_CONNECT_VIA_MMS
bool m_bWaitingForServerGameDetails;
#endif
DeferredConnection_t m_DeferredConnection;
CUtlMap< int32, byte*, int32, CDefLess< int32 > > m_mapGeneratedEncryptionKeys;
PackedEntity *m_pEntityBaselines[2][MAX_EDICTS]; // storing entity baselines
// This stuff manages the receiving of data tables and instantiating of client versions
// of server-side classes.
C_ServerClassInfo *m_pServerClasses;
int m_nServerClasses;
int m_nServerClassBits;
char m_szEncryptionKey[STEAM_KEYSIZE];
unsigned int m_iEncryptionKeySize;
CNetworkStringTableContainer *m_StringTableContainer;
CUtlMap< int, SerializedEntityHandle_t > m_BaselineHandles;
typedef CUtlMap< uint32, CNETMsg_PlayerAvatarData_t *, int, CDefLess< uint32 > > PlayerAvatarDataMap_t;
PlayerAvatarDataMap_t m_mapPlayerAvatarData;
CNETMsg_PlayerAvatarData_t * AllocOwnPlayerAvatarData() const;
bool m_bRestrictServerCommands; // If true, then the server is only allowed to execute commands marked with FCVAR_SERVER_CAN_EXECUTE on the client.
bool m_bRestrictClientCommands; // If true, then IVEngineClient::ClientCmd is only allowed to execute commands marked with FCVAR_CLIENTCMD_CAN_EXECUTE on the client.
// tracks valid reception of server info
bool m_bServerInfoProcessed;
int m_nServerProtocolVersion;
int m_nServerInfoMsgProtocol;
bool m_bServerConnectionRedirect;
};
inline CClockDriftMgr& CBaseClientState::GetClockDriftMgr()
{
return m_ClockDriftMgr;
}
inline void CBaseClientState::SetClientTickCount( int tick )
{
m_ClockDriftMgr.m_nClientTick = tick;
}
inline int CBaseClientState::GetClientTickCount() const
{
return m_ClockDriftMgr.m_nClientTick;
}
inline int CBaseClientState::GetServerTickCount() const
{
return m_ClockDriftMgr.m_nServerTick;
}
inline void CBaseClientState::SetServerTickCount( int tick )
{
m_ClockDriftMgr.m_nServerTick = tick;
}
inline void CBaseClientState::SetClientAndServerTickCount( int tick )
{
m_ClockDriftMgr.m_nServerTick = m_ClockDriftMgr.m_nClientTick = tick;
}
#endif // BASECLIENTSTATE_H