Ray-Trace/src/schema/serversideclient.h
2026-01-11 19:41:16 +04:00

445 lines
15 KiB
C++

//
// Created by Michal Přikryl on 26.06.2025.
// Copyright (c) 2025 slynxcz. All rights reserved.
//
#pragma once
#include "playerslot.h"
#include "steam/steamclientpublic.h"
#include "utlstring.h"
#include "inetchannel.h"
#include "networkbasetypes.pb.h"
#include <inetchannel.h>
#include <playerslot.h>
#include "circularbuffer.h"
#include "networksystem/inetworksystem.h"
#include "threadtools.h"
#include "tier1/netadr.h"
#include <entity2/entityidentity.h>
#include <networksystem/inetworksystem.h>
#include <steam/steamclientpublic.h>
#include <tier1/utlstring.h>
#include <network_connection.pb.h>
#include <netmessages.pb.h>
namespace
RayTracePlugin
{
class CHLTVServer;
class INetMessage;
class CNetworkGameServerBase;
class CNetworkGameServer;
class CFrameSnapshot;
class CMsg_CVars;
class CNETMsg_StringCmd_t;
class CNETMsg_Tick_t;
class CNETMsg_SpawnGroup_LoadCompleted_t;
class CCLCMsg_ClientInfo_t;
class CCLCMsg_BaselineAck_t;
class CCLCMsg_LoadingProgress_t;
class CCLCMsg_SplitPlayerConnect_t;
class CCLCMsg_SplitPlayerDisconnect_t;
class CCLCMsg_CmdKeyValues_t;
class CCLCMsg_Move_t;
class CCLCMsg_VoiceData_t;
class CCLCMsg_FileCRCCheck_t;
class CCLCMsg_RespondCvarValue_t;
class NetMessagePacketStart_t;
class NetMessagePacketEnd_t;
class NetMessageConnectionClosed_t;
class NetMessageConnectionCrashed_t;
class NetMessageSplitscreenUserChanged_t;
struct HltvReplayStats_t
{
enum FailEnum_t
{
FAILURE_ALREADY_IN_REPLAY,
FAILURE_TOO_FREQUENT,
FAILURE_NO_FRAME,
FAILURE_NO_FRAME2,
FAILURE_CANNOT_MATCH_DELAY,
FAILURE_FRAME_NOT_READY,
NUM_FAILURES
};
uint nClients;
uint nStartRequests;
uint nSuccessfulStarts;
uint nStopRequests;
uint nAbortStopRequests;
uint nUserCancels;
uint nFullReplays;
uint nNetAbortReplays;
uint nFailedReplays[NUM_FAILURES];
}; // sizeof 56
COMPILE_TIME_ASSERT(sizeof(HltvReplayStats_t) == 56);
struct Spike_t
{
public:
CUtlString m_szDesc;
int m_nBits;
};
COMPILE_TIME_ASSERT(sizeof(Spike_t) == 16);
class CNetworkStatTrace
{
public:
CUtlVector<Spike_t> m_Records;
int m_nMinWarningBytes;
int m_nStartBit;
int m_nCurBit;
};
COMPILE_TIME_ASSERT(sizeof(CNetworkStatTrace) == 40);
enum CopiedLockState_t : int32
{
CLS_NOCOPY = 0,
CLS_UNLOCKED = 1,
CLS_LOCKED_BY_COPYING_THREAD = 2,
};
template <class MUTEX, CopiedLockState_t L = CLS_UNLOCKED>
class CCopyableLock : public MUTEX
{
typedef MUTEX BaseClass;
public:
// ...
};
class CUtlSignaller_Base
{
public:
using Delegate_t = CUtlDelegate<void(CUtlSlot*)>;
CUtlSignaller_Base(const Delegate_t& other) :
m_SlotDeletionDelegate(other)
{
}
CUtlSignaller_Base(Delegate_t&& other) :
m_SlotDeletionDelegate(Move(other))
{
}
private:
Delegate_t m_SlotDeletionDelegate;
};
class CUtlSlot
{
public:
using MTElement_t = CUtlSignaller_Base*;
CUtlSlot() :
m_ConnectedSignallers(0, 1)
{
}
private:
CCopyableLock<CThreadFastMutex> m_Mutex;
CUtlVector<MTElement_t> m_ConnectedSignallers;
};
class CServerSideClientBase : public CUtlSlot, public INetworkChannelNotify,
public INetworkMessageProcessingPreFilter
{
public:
virtual ~CServerSideClientBase() = 0;
public:
CPlayerSlot GetPlayerSlot() const { return m_nClientSlot; }
CPlayerUserId GetUserID() const { return m_UserID; }
CEntityIndex GetEntityIndex() const { return m_nEntityIndex; }
CSteamID GetClientSteamID() const { return m_SteamID; }
const char* GetClientName() const { return m_Name; }
INetChannel* GetNetChannel() const { return m_NetChannel; }
const netadr_t* GetRemoteAddress() const { return &m_nAddr.GetAddress(); }
CNetworkGameServerBase* GetServer() const { return m_Server; }
virtual void Connect(int socket, const char* pszName, int nUserID, INetChannel* pNetChannel,
uint8 nConnectionTypeFlags,
uint32 uChallengeNumber) = 0;
// bool bFakePlayer = !nConnectionTypeFlags || (nConnectionTypeFlags & 8) != 0;
virtual void Inactivate(const char* pszAddons) = 0;
virtual void Reactivate(CPlayerSlot nSlot) = 0;
virtual void SetServer(CNetworkGameServer* pNetServer) = 0;
virtual void Reconnect() = 0;
virtual void Disconnect(ENetworkDisconnectionReason reason, const char* pszInternalReason) = 0;
virtual bool CheckConnect() = 0;
virtual void Create(CPlayerSlot& nSlot, CSteamID nSteamID, const char* pszName) = 0;
virtual void SetRate(int nRate) = 0;
virtual void SetUpdateRate(float fUpdateRate) = 0;
virtual int GetRate() = 0;
virtual void Clear() = 0;
virtual bool ExecuteStringCommand(const CNETMsg_StringCmd_t& msg) = 0;
// "false" trigger an anti spam counter to kick a client.
virtual bool SendNetMessage(const CNetMessage* pData, NetChannelBufType_t bufType = BUF_DEFAULT) = 0;
// "Client %d(%s) tried to send a RebroadcastSourceId msg.\n"
virtual bool FilterMessage(const CNetMessage* pData, INetChannel* pChannel) = 0;
// On Windows, this function is in a separate virtual table
public:
virtual void ClientPrintf(PRINTF_FORMAT_STRING const char*, ...) = 0;
bool IsConnected() const { return m_nSignonState >= SIGNONSTATE_CONNECTED; }
bool IsInGame() const { return m_nSignonState == SIGNONSTATE_FULL; }
bool IsSpawned() const { return m_nSignonState >= SIGNONSTATE_NEW; }
bool IsActive() const { return m_nSignonState == SIGNONSTATE_FULL; }
virtual bool IsFakeClient() const { return m_bFakePlayer; }
bool IsHLTV() const { return m_bIsHLTV; }
virtual bool IsHumanPlayer() const { return false; }
// Is an actual human player or splitscreen player (not a bot and not a HLTV slot)
virtual bool IsHearingClient(CPlayerSlot nSlot) const { return false; }
virtual bool IsProximityHearingClient() const = 0;
virtual bool IsLowViolenceClient() const { return m_bLowViolence; }
virtual bool IsSplitScreenUser() const { return m_bSplitScreenUser; }
public: // Message Handlers
virtual bool ProcessTick(const CNETMsg_Tick_t& msg) = 0;
virtual bool ProcessStringCmd(const CNETMsg_StringCmd_t& msg) = 0;
public:
virtual bool ApplyConVars(const CMsg_CVars& list) = 0;
private:
virtual bool unk_28() = 0;
public:
virtual bool ProcessSpawnGroup_LoadCompleted(const CNETMsg_SpawnGroup_LoadCompleted_t& msg) = 0;
virtual bool ProcessClientInfo(const CCLCMsg_ClientInfo_t& msg) = 0;
virtual bool ProcessBaselineAck(const CCLCMsg_BaselineAck_t& msg) = 0;
virtual bool ProcessLoadingProgress(const CCLCMsg_LoadingProgress_t& msg) = 0;
virtual bool ProcessSplitPlayerConnect(const CCLCMsg_SplitPlayerConnect_t& msg) = 0;
virtual bool ProcessSplitPlayerDisconnect(const CCLCMsg_SplitPlayerDisconnect_t& msg) = 0;
virtual bool ProcessCmdKeyValues(const CCLCMsg_CmdKeyValues_t& msg) = 0;
private:
virtual bool unk_36() = 0;
virtual bool unk_37() = 0;
public:
virtual bool ProcessMove(const CCLCMsg_Move_t& msg) = 0;
virtual bool ProcessVoiceData(const CCLCMsg_VoiceData_t& msg) = 0;
virtual bool ProcessRespondCvarValue(const CCLCMsg_RespondCvarValue_t& msg) = 0;
virtual bool ProcessPacketStart(const NetMessagePacketStart_t& msg) = 0;
virtual bool ProcessPacketEnd(const NetMessagePacketEnd_t& msg) = 0;
virtual bool ProcessConnectionClosed(const NetMessageConnectionClosed_t& msg) = 0;
virtual bool ProcessConnectionCrashed(const NetMessageConnectionCrashed_t& msg) = 0;
public:
virtual bool ProcessChangeSplitscreenUser(const NetMessageSplitscreenUserChanged_t& msg) = 0;
private:
virtual bool unk_47() = 0;
virtual bool unk_48() = 0;
virtual bool unk_49() = 0;
public:
virtual void ConnectionStart(INetChannel* pNetChannel) = 0;
private: // SpawnGroup something.
virtual void unk_51() = 0;
virtual void unk_52() = 0;
public:
virtual void ExecuteDelayedCall(void*) = 0;
virtual bool UpdateAcknowledgedFramecount(int tick) = 0;
void ForceFullUpdate()
{
// For some reason, it doesn't work.
// UpdateAcknowledgedFramecount(-1);
m_nDeltaTick = -1;
}
virtual bool ShouldSendMessages() = 0;
virtual void UpdateSendState() = 0;
virtual const CMsgPlayerInfo& GetPlayerInfo() const { return m_playerInfo; }
virtual void UpdateUserSettings() = 0;
virtual void ResetUserSettings() = 0;
private:
virtual void unk_60() = 0;
public:
virtual void SendSignonData() = 0;
virtual void SpawnPlayer() = 0;
virtual void ActivatePlayer() = 0;
virtual void SetName(const char* name) = 0;
virtual void SetUserCVar(const char* cvar, const char* value) = 0;
SignonState_t GetSignonState() const { return m_nSignonState; }
virtual void FreeBaselines() = 0;
bool IsFullyAuthenticated(void) { return m_bFullyAuthenticated; }
void SetFullyAuthenticated(void) { m_bFullyAuthenticated = true; }
virtual CServerSideClientBase* GetSplitScreenOwner() { return m_pAttachedTo; }
virtual int GetNumPlayers() = 0;
virtual void ShouldReceiveStringTableUserData() = 0;
private:
virtual void unk_70(CPlayerSlot nSlot) = 0;
virtual void unk_71() = 0;
virtual void unk_72() = 0;
public:
virtual int GetHltvLastSendTick() = 0;
private:
virtual void unk_74() = 0;
virtual void unk_75() = 0;
virtual void unk_76() = 0;
public:
virtual void Await() = 0;
virtual void MarkToKick() = 0;
virtual void UnmarkToKick() = 0;
virtual bool ProcessSignonStateMsg(int state) = 0;
virtual void PerformDisconnection(ENetworkDisconnectionReason reason) = 0;
public:
CUtlString m_UserIDString;
CUtlString m_Name;
CPlayerSlot m_nClientSlot;
CEntityIndex m_nEntityIndex;
CNetworkGameServerBase* m_Server;
INetChannel* m_NetChannel;
// CServerSideClientBase::Connect( name='%s', userid=%d, fake=%d, connectiontypeflags=%d, chan->addr=%s )
uint8 m_nConnectionTypeFlags;
uint8 m_nAsyncDisconnectFlags; // check in Disconnect function, 1 add to queue, 2 disconnect now
bool m_bMarkedToKick;
SignonState_t m_nSignonState;
bool m_bSplitScreenUser;
bool m_bSplitAllowFastDisconnect;
int m_nSplitScreenPlayerSlot;
CServerSideClientBase* m_SplitScreenUsers[4];
CServerSideClientBase* m_pAttachedTo;
bool m_bSplitPlayerDisconnecting;
int m_nDisconnectionTypeFlags;
bool m_bFakePlayer;
bool m_bSendingSnapshot;
private:
[[maybe_unused]] char pad162[0x6];
public:
CPlayerUserId m_UserID = -1;
bool m_bReceivedPacket; // true, if client received a packet after the last send packet
CSteamID m_SteamID;
CSteamID m_DisconnectedSteamID;
CSteamID m_AuthTicketSteamID; // Auth ticket
CSteamID m_nFriendsID;
ns_address m_nAddr;
ns_address m_nAddr2;
KeyValues* m_ConVars;
bool m_bUnk0;
private:
[[maybe_unused]] char pad281[0x28];
public:
bool m_bConVarsChanged;
bool m_bIsHLTV;
private:
[[maybe_unused]] char pad323[0xD];
public:
uint32 m_nSendtableCRC;
uint32 m_uChallengeNumber;
int m_nSignonTick;
int m_nDeltaTick;
int m_UnkVariable3;
int m_nStringTableAckTick;
int m_UnkVariable4;
CFrameSnapshot* m_pLastSnapshot; // last send snapshot
CUtlVector<void*> m_vecLoadedSpawnGroups;
CMsgPlayerInfo m_playerInfo;
CFrameSnapshot* m_pBaseline;
int m_nBaselineUpdateTick;
CBitVec<MAX_EDICTS> m_BaselinesSent;
int m_nBaselineUsed; // 0/1 toggling flag, singaling client what baseline to use
int m_nLoadingProgress; // 0..100 progress, only valid during loading
// This is used when we send out a nodelta packet to put the client in a state where we wait
// until we get an ack from them on this packet.
// This is for 3 reasons:
// 1. A client requesting a nodelta packet means they're screwed so no point in deluging them with data.
// Better to send the uncompressed data at a slow rate until we hear back from them (if at all).
// 2. Since the nodelta packet deletes all client entities, we can't ever delta from a packet previous to it.
// 3. It can eat up a lot of CPU on the server to keep building nodelta packets while waiting for
// a client to get back on its feet.
int m_nForceWaitForTick = -1;
CCircularBuffer m_UnkBuffer = {1024};
bool m_bLowViolence = false; // true if client is in low-violence mode (L4D server needs to know)
bool m_bSomethingWithAddressType = true;
bool m_bFullyAuthenticated = false;
bool m_bUnk1 = false;
int m_nUnk;
// The datagram is written to after every frame, but only cleared
// when it is sent out to the client. overflow is tolerated.
// Time when we should send next world state update ( datagram )
float m_fNextMessageTime = 0.0f;
float m_fAuthenticatedTime = -1.0f;
// Default time to wait for next message
float m_fSnapshotInterval = 0.0f;
private:
[[maybe_unused]] char pad2572[0x8];
[[maybe_unused]] char m_packetmsg[0x15C]; // CSVCMsg_PacketEntities_t
#ifdef __linux__
[[maybe_unused]] char pad2928[0x8];
#endif
public:
CNetworkStatTrace m_Trace;
private:
[[maybe_unused]] char pad2976[0x8];
public:
// SV: Player %s kicked for too many failed console commands
int m_spamCommandsCount = 0; // if the value is greater than 16, the player will be kicked with reason 39
int m_unknown = 0;
double m_lastExecutedCommand = 0.0; // if command executed more than once per second, ++m_spamCommandCount
private:
[[maybe_unused]] char pad3000[0x8];
public:
CCommand* m_pCommand;
};
#ifdef __linux__
COMPILE_TIME_ASSERT(sizeof(CServerSideClientBase) == 3016);
#endif
class CServerSideClient : public CServerSideClientBase
{
};
}