Fixed few stuff & now server bullet impacts are events

- Fixed some game events not being sent to clients
- Use event instead of descriptor for sending it to client
- Moved server bullets to events
- Re-trained zstd data
- Corrected some stuff on clock correction
This commit is contained in:
Kamay Xutax 2024-07-26 05:32:47 +02:00
parent 542b2c2711
commit 69312da01b
21 changed files with 124 additions and 107 deletions

View file

@ -10,6 +10,7 @@
//////////////////////////////////////////////////////////////////////
#include "GameEventManager.h"
#include "KeyValues.h"
#include "filesystem_engine.h"
#include "server.h"
#include "client.h"
@ -113,6 +114,11 @@ bool CGameEvent::IsReliable() const
return m_pDescriptor->reliable;
}
KeyValues* CGameEvent::GetDataKeys()
{
return m_pDataKeys;
}
CGameEventManager::CGameEventManager()
{
Reset();
@ -129,6 +135,16 @@ bool CGameEventManager::Init()
LoadEventsFromFile( "resource/serverevents.res" );
// TODO_ENHANCED: write this into a res file.
static auto bullet_impact_kv = new KeyValues("bullet_impact");
RegisterEvent(bullet_impact_kv);
static auto bullet_hit_player_kv = new KeyValues("bullet_hit_player");
RegisterEvent(bullet_hit_player_kv);
static auto bullet_player_hitboxes_kv = new KeyValues("bullet_player_hitboxes");
RegisterEvent(bullet_player_hitboxes_kv);
static auto player_lag_hitboxes_kv = new KeyValues("player_lag_hitboxes");
RegisterEvent(player_lag_hitboxes_kv);
return true;
}
@ -369,19 +385,18 @@ void CGameEventManager::ConPrintEvent( IGameEvent *event)
if ( !descriptor )
return;
KeyValues *key = descriptor->keys->GetFirstSubKey();
KeyValues *key = event->GetDataKeys()->GetFirstSubKey();
while ( key )
{
const char * keyName = key->GetName();
int type = key->GetInt();
int type = key->GetDataType();
switch ( type )
{
case TYPE_LOCAL : ConMsg( "- \"%s\" = \"%s\" (local)\n", keyName, event->GetString(keyName) ); break;
case TYPE_STRING : ConMsg( "- \"%s\" = \"%s\"\n", keyName, event->GetString(keyName) ); break;
case TYPE_FLOAT : ConMsg( "- \"%s\" = \"%.2f\"\n", keyName, event->GetFloat(keyName) ); break;
case KeyValues::types_t::TYPE_STRING : ConMsg( "- \"%s\" = \"%s\"\n", keyName, event->GetString(keyName) ); break;
case KeyValues::types_t::TYPE_FLOAT : ConMsg( "- \"%s\" = \"%.2f\"\n", keyName, event->GetFloat(keyName) ); break;
default: ConMsg( "- \"%s\" = \"%i\"\n", keyName, event->GetInt(keyName) ); break;
}
key = key->GetNextKey();
@ -482,28 +497,43 @@ bool CGameEventManager::SerializeEvent( IGameEvent *event, bf_write* buf )
CGameEventDescriptor *descriptor = GetEventDescriptor( event );
Assert( descriptor );
buf->WriteUBitLong( descriptor->eventid, MAX_EVENT_BITS );
// now iterate trough all fields described in gameevents.res and put them in the buffer
// TODO_ENHANCED: this now gets directly from event keys,
// because they can be different (bullet_impact), or not enumerated at all. (bullet_hit_player)
KeyValues * key = descriptor->keys->GetFirstSubKey();
KeyValues * key = event->GetDataKeys()->GetFirstSubKey();
if ( net_showevents.GetInt() > 2 )
{
DevMsg("Serializing event '%s' (%i):\n", descriptor->name, descriptor->eventid );
}
uint32 keyCount = 0;
while ( key )
{
keyCount++;
key = key->GetNextKey();
}
buf->WriteVarInt32(keyCount);
key = event->GetDataKeys()->GetFirstSubKey();
while ( key )
{
const char * keyName = key->GetName();
int type = key->GetInt();
int type = key->GetDataType();
if ( net_showevents.GetInt() > 2 )
{
DevMsg(" - %s (%i)\n", keyName, type );
}
}
buf->WriteString(keyName);
buf->WriteByte(type);
//Make sure every key is used in the event
// Assert( event->FindKey(keyName) && "GameEvent field not found in passed KeyValues" );
@ -511,14 +541,10 @@ bool CGameEventManager::SerializeEvent( IGameEvent *event, bf_write* buf )
// see s_GameEnventTypeMap for index
switch ( type )
{
case TYPE_LOCAL : break; // don't network this guy
case TYPE_STRING: buf->WriteString( event->GetString( keyName, "") ); break;
case TYPE_FLOAT : buf->WriteFloat( event->GetFloat( keyName, 0.0f) ); break;
case TYPE_LONG : buf->WriteLong( event->GetInt( keyName, 0) ); break;
case TYPE_SHORT : buf->WriteShort( event->GetInt( keyName, 0) ); break;
case TYPE_BYTE : buf->WriteByte( event->GetInt( keyName, 0) ); break;
case TYPE_BOOL : buf->WriteOneBit( event->GetInt( keyName, 0) ); break;
default: DevMsg(1, "CGameEventManager: unkown type %i for key '%s'.\n", type, key->GetName() ); break;
case KeyValues::types_t::TYPE_STRING : buf->WriteString( event->GetString( keyName, "") ); break;
case KeyValues::types_t::TYPE_FLOAT : buf->WriteFloat( event->GetFloat( keyName, 0.0f) ); break;
case KeyValues::types_t::TYPE_INT : buf->WriteLong( event->GetInt( keyName, 0) ); break;
default: DevMsg(1, "CGameEventManager::SerializeEvent: can't serialize type %i yet for key '%s'.\n", type, keyName ); break;
}
key = key->GetNextKey();
@ -553,29 +579,30 @@ IGameEvent *CGameEventManager::UnserializeEvent( bf_read *buf)
return NULL;
}
KeyValues * key = descriptor->keys->GetFirstSubKey();
KeyValues * key = event->GetDataKeys()->GetFirstSubKey();
uint32 keyCount = buf->ReadVarInt32();
while ( key )
{
const char * keyName = key->GetName();
while ( keyCount > 0 )
{
char keyName[256];
buf->ReadString(keyName, sizeof(keyName));
int type = key->GetInt();
int type = buf->ReadByte();
switch ( type )
{
case TYPE_LOCAL : break; // ignore
case TYPE_STRING : if ( buf->ReadString( databuf, sizeof(databuf) ) )
event->SetString( keyName, databuf );
break;
case TYPE_FLOAT : event->SetFloat( keyName, buf->ReadFloat() ); break;
case TYPE_LONG : event->SetInt( keyName, buf->ReadLong() ); break;
case TYPE_SHORT : event->SetInt( keyName, buf->ReadShort() ); break;
case TYPE_BYTE : event->SetInt( keyName, buf->ReadByte() ); break;
case TYPE_BOOL : event->SetInt( keyName, buf->ReadOneBit() ); break;
default: DevMsg(1, "CGameEventManager: unknown type %i for key '%s'.\n", type, key->GetName() ); break;
}
case KeyValues::types_t::TYPE_STRING:
if (buf->ReadString(databuf, sizeof(databuf)))
{
event->SetString(keyName, databuf);
}
break;
case KeyValues::types_t::TYPE_FLOAT : event->SetFloat( keyName, buf->ReadFloat() ); break;
case KeyValues::types_t::TYPE_INT : event->SetInt( keyName, buf->ReadLong() ); break;
default: DevMsg(1, "CGameEventManager::UnserializeEvent: can't unserialize type %i yet for key '%s'.\n", type, keyName ); break;
}
key = key->GetNextKey();
keyCount--;
}
return event;

View file

@ -71,6 +71,7 @@ public:
void SetInt( const char *keyName, int value );
void SetFloat( const char *keyName, float value );
void SetString( const char *keyName, const char *value );
KeyValues* GetDataKeys();
CGameEventDescriptor *m_pDescriptor;
KeyValues *m_pDataKeys;

View file

@ -793,9 +793,12 @@ void CBaseClient::ConnectionStart(INetChannel *chan)
bool CBaseClient::ProcessTick( NET_Tick *msg )
{
m_NetChannel->SetRemoteFramerate( msg->m_flHostFrameTime, msg->m_flHostFrameTimeStdDeviation );
m_nClientTick = msg->m_nLagTick;
return UpdateAcknowledgedFramecount( msg->m_nTick );
m_NetChannel->SetRemoteFramerate(msg->m_flHostFrameTime, msg->m_flHostFrameTimeStdDeviation);
if (msg->m_nLagTick != -1)
{
m_nClientTick = msg->m_nLagTick;
}
return UpdateAcknowledgedFramecount( msg->m_nTick );
}
bool CBaseClient::ProcessStringCmd( NET_StringCmd *msg )

View file

@ -1768,6 +1768,15 @@ bool CBaseClientState::GetClassBaseline( int iClass, void const **pData, int *pD
return *pData != NULL;
}
bool CBaseClientState::ProcessGameEvent( SVC_GameEvent *msg )
{
VPROF( "ProcessGameEvent" );
const auto deserializedEvent = g_GameEventManager.UnserializeEvent(&msg->m_DataIn);
return g_GameEventManager.FireEvent(deserializedEvent);
}
bool CBaseClientState::ProcessGameEventList( SVC_GameEventList *msg )
{
VPROF( "ProcessGameEventList" );
@ -1775,7 +1784,6 @@ bool CBaseClientState::ProcessGameEventList( SVC_GameEventList *msg )
return g_GameEventManager.ParseEventList( msg );
}
bool CBaseClientState::ProcessGetCvarValue( SVC_GetCvarValue *msg )
{
VPROF( "ProcessGetCvarValue" );

View file

@ -105,6 +105,7 @@ public: // IServerMessageHandlers
PROCESS_SVC_MESSAGE( SetView );
PROCESS_SVC_MESSAGE( PacketEntities );
PROCESS_SVC_MESSAGE( Menu );
PROCESS_SVC_MESSAGE( GameEvent );
PROCESS_SVC_MESSAGE( GameEventList );
PROCESS_SVC_MESSAGE( GetCvarValue );
PROCESS_SVC_MESSAGE( CmdKeyValues );

View file

@ -7,6 +7,7 @@
// $NoKeywords: $
//===========================================================================//
#include "client.h"
#include "client_pch.h"
#include "sound.h"
#include <inetchannel.h>
@ -572,7 +573,7 @@ void CL_ReadPackets ( bool bFinalTick )
}
else
{
cl.m_ClockDriftMgr.ApplyClockCorrection(bFinalTick);
cl.m_ClockDriftMgr.IncrementCachedTickCount(bFinalTick);
}
g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
@ -2242,7 +2243,7 @@ void CL_Move(float accumulated_extra_samples, bool bFinalTick )
// multiple commands are ran from client because it had low fps.
// We could probably store in CUserCmd structure the current tick number to account for that ?
// This works anyway.
NET_Tick mymsg( cl.m_nDeltaTick, cl.m_ClockDriftMgr.m_nCachedRealClientTick + cl.m_ClockDriftMgr.m_nCurrentTick, host_frametime_unbounded, host_frametime_stddeviation );
NET_Tick mymsg( cl.m_nDeltaTick, cl.m_ClockDriftMgr.m_nCachedRealClientTick + g_ClientGlobalVariables.currenttick, host_frametime_unbounded, host_frametime_stddeviation );
cl.m_NetChannel->SendNetMsg( mymsg );
}

View file

@ -95,14 +95,14 @@ void CClockDriftMgr::SetServerTick( int nTick, int nLaggedTick, float flServerHo
#endif // SWDS
}
void CClockDriftMgr::ApplyClockCorrection(bool bFinalTick)
void CClockDriftMgr::IncrementCachedTickCount(bool bFinalTick)
{
if (bFinalTick)
{
cl.m_ClockDriftMgr.m_nCachedRealClientTick += cl.m_ClockDriftMgr.m_nNumberOfTicks;
m_nCachedRealClientTick += m_nNumberOfTicks;
}
m_nClientTick = m_nCachedRealClientTick + m_nLagDiff;
m_nClientTick = m_nCachedRealClientTick + m_nLagDiff;
}
void CClockDriftMgr::ShowDebugInfo()

View file

@ -27,7 +27,7 @@ public:
// This is called each time a server packet comes in. It is used to correlate
// where the server is in time compared to us.
void SetServerTick( int iServerTick, int nLaggedTick, float flServerHostFrametime, float flServerHostFrametimeStdDeviation);
void ApplyClockCorrection(bool bFinalTick);
void IncrementCachedTickCount(bool bFinalTick);
private:
@ -40,7 +40,6 @@ public:
int m_nServerTick; // Last-received tick from the server.
int m_nClientTick;
int m_nNumberOfTicks;
int m_nCurrentTick;
int m_nCachedRealClientTick; // The client's own tick counter (specifically, for interpolation during rendering).
// The server may be on a slightly different tick and the client will drift towards it.
int m_nLaggedClientTick;

View file

@ -1304,7 +1304,7 @@ unsigned int COM_GetIdealDestinationCompressionBufferSize_ZSTD(
return 4 + ZSTD_compressBound(uncompressedSize);
}
static constexpr int ZSTD_COMPRESSION_LEVEL = 22; // ZSTD_btultra2
static constexpr int ZSTD_COMPRESSION_LEVEL = 0; // ZSTD_btultra2
static auto g_pZSTDCCtx = ZSTD_createCCtx();
template<typename T>

View file

@ -249,6 +249,14 @@ void DrawGridOverlay();
void ClearAllOverlays();
void ClearDeadOverlays();
//-----------------------------------------------------------------------------
// Purpose: Allow all debug overlays to be cleared at once
//-----------------------------------------------------------------------------
CON_COMMAND( clear_debug_overlays, "clears debug overlays" )
{
ClearAllOverlays();
}
//-----------------------------------------------------------------------------
// Init static member variables

View file

@ -3250,7 +3250,11 @@ void _Host_RunFrame (float time)
g_ServerGlobalVariables.simTicksThisFrame = 1;
cl.SetFrameTime(host_frametime);
for ( int tick = 0; tick < numticks; tick++ )
{
{
g_ServerGlobalVariables.currenttick = tick;
#ifndef SWDS
g_ClientGlobalVariables.currenttick = tick;
#endif
// Emit an ETW event every simulation frame.
ETWSimFrameMark( sv.IsDedicated() );
@ -3318,7 +3322,6 @@ void _Host_RunFrame (float time)
#ifndef SWDS
if ( !sv.IsDedicated() )
{
cl.m_ClockDriftMgr.m_nCurrentTick = tick;
_Host_RunFrame_Client(bFinalTick);
}
@ -3361,7 +3364,6 @@ void _Host_RunFrame (float time)
// as quickly as we can.
if ( numticks == 0 && ( demoplayer->IsPlayingTimeDemo() || demoplayer->IsSkipping() ) )
{
cl.m_ClockDriftMgr.m_nCurrentTick = 0;
_Host_RunFrame_Client(true);
}
@ -3436,7 +3438,9 @@ void _Host_RunFrame (float time)
// THREADED: Run Client
// -------------------
for ( int tick = 0; tick < clientticks; tick++ )
{
{
g_ServerGlobalVariables.currenttick = tick;
g_ClientGlobalVariables.currenttick = tick;
// process any asynchronous network traffic (TCP), set net_time
NET_RunFrame( Plat_FloatTime() );
@ -3459,7 +3463,6 @@ void _Host_RunFrame (float time)
//-------------------
if ( !sv.IsDedicated() )
{
cl.m_ClockDriftMgr.m_nCurrentTick = tick;
_Host_RunFrame_Client(bFinalTick);
}
toolframework->Think( bFinalTick );
@ -3468,7 +3471,9 @@ void _Host_RunFrame (float time)
// Also when demoplayer is skipping packets to a certain tick we should process the queue
// as quickly as we can.
if ( clientticks == 0 && ( demoplayer->IsPlayingTimeDemo() || demoplayer->IsSkipping() ) )
{
{
g_ServerGlobalVariables.currenttick = 0;
g_ClientGlobalVariables.currenttick = 0;
_Host_RunFrame_Client( true );
}
@ -3493,7 +3498,9 @@ void _Host_RunFrame (float time)
int saveTick = g_ClientGlobalVariables.tickcount;
for ( int tick = 0; tick < serverticks; tick++ )
{
{
g_ServerGlobalVariables.currenttick = tick;
g_ClientGlobalVariables.currenttick = tick;
// NOTE: Do we want do this at start or end of this loop?
++host_tickcount;
++host_currentframetick;

View file

@ -72,7 +72,7 @@
#define NETMSG_TYPE_BITS 6 // must be 2^NETMSG_TYPE_BITS > SVC_LASTMSG
#define NETMSG_LENGTH_BITS 11 // 256 bytes
#define NETMSG_LENGTH_BITS 20 // 256 bytes
// This is the payload plus any header info (excluding UDP header)

View file

@ -43,7 +43,7 @@ static ConVar net_drawslider( "net_drawslider", "0", 0, "Draw completion slider
static ConVar net_chokeloopback( "net_chokeloop", "0", 0, "Apply bandwidth choke to loopback packets" );
static ConVar net_maxfilesize( "net_maxfilesize", "16", 0, "Maximum allowed file size for uploading in MB", true, 0, true, 64 );
static ConVar net_compresspackets( "net_compresspackets", "1", 0, "Use compression on game packets." );
static ConVar net_compresspackets_minsize( "net_compresspackets_minsize", "1024", 0, "Don't bother compressing packets below this size." );
static ConVar net_compresspackets_minsize( "net_compresspackets_minsize", "0", 0, "Don't bother compressing packets below this size." );
static ConVar net_maxcleartime( "net_maxcleartime", "4.0", 0, "Max # of seconds we can wait for next packets to be sent based on rate setting (0 == no limit)." );
static ConVar net_maxpacketdrop( "net_maxpacketdrop", "5000", 0, "Ignore any packets with the sequence number more than this ahead (0 == no limit)" );

View file

@ -74,8 +74,8 @@ void cc_cl_interp_all_changed( IConVar *pConVar, const char *pOldString, float f
}
}
static ConVar cl_extrapolate( "cl_extrapolate", "1", FCVAR_CHEAT, "Enable/disable extrapolation if interpolation history runs out." );
// TODO_ENHANCED: Never extrapolate, this breaks clock correction.
static ConVar cl_extrapolate( "cl_extrapolate", "0", FCVAR_CHEAT, "Enable/disable extrapolation if interpolation history runs out." );
static ConVar cl_interp_npcs( "cl_interp_npcs", "0.0", FCVAR_USERINFO, "Interpolate NPC positions starting this many seconds in past (or cl_interp, if greater)" );
static ConVar cl_interp_all( "cl_interp_all", "0", 0, "Disable interpolation list optimizations.", 0, 0, 0, 0, cc_cl_interp_all_changed );
ConVar r_drawmodeldecals( "r_drawmodeldecals", "1" );

View file

@ -790,21 +790,6 @@ C_CSPlayer::C_CSPlayer() :
m_bPlayingFreezeCamSound = false;
// HACK_ENHANCED:
static bool bDoOnce = false;
if (!bDoOnce)
{
static auto bullet_impact_kv = new KeyValues("bullet_impact");
gameeventmanager->RegisterEvent(bullet_impact_kv);
static auto bullet_hit_player_kv = new KeyValues("bullet_hit_player");
gameeventmanager->RegisterEvent(bullet_hit_player_kv);
static auto bullet_player_hitboxes_kv = new KeyValues("bullet_player_hitboxes");
gameeventmanager->RegisterEvent(bullet_player_hitboxes_kv);
static auto player_lag_hitboxes_kv = new KeyValues("player_lag_hitboxes");
gameeventmanager->RegisterEvent(player_lag_hitboxes_kv);
bDoOnce = true;
}
ListenForGameEvent( "bullet_impact" );
ListenForGameEvent( "bullet_hit_player" );
ListenForGameEvent( "bullet_player_hitboxes" );
@ -2209,7 +2194,7 @@ void C_CSPlayer::FireGameEvent(IGameEvent* event)
};
if ( FStrEq( event->GetName(), "bullet_impact" ) && shouldShowImpacts )
{
{
const int index = event->GetInt( "userid" );
if ( index == GetUserID() && IsLocalPlayer() )
{
@ -2354,7 +2339,7 @@ void C_CSPlayer::Simulate( void )
if ((cl_showhitboxes.GetInt() == 1 || cl_showhitboxes.GetInt() == 2) && !IsLocalPlayer())
{
DrawClientHitboxes(-1.0f, true);
DrawClientHitboxes(gpGlobals->absoluteframetime, true);
}
}

View file

@ -1629,7 +1629,7 @@ void CCSPlayer::PostThink()
event->SetFloat(buffer, angles[indexes[i]].z);
}
gameeventmanager->FireEventClientSide(event);
gameeventmanager->FireEvent(event);
}
}

View file

@ -242,28 +242,3 @@ void DebugDrawLine( const Vector& vecAbsStart, const Vector& vecAbsEnd, int r, i
{
NDebugOverlay::Line( vecAbsStart + Vector( 0,0,0.1), vecAbsEnd + Vector( 0,0,0.1), r,g,b, test, duration );
}
//-----------------------------------------------------------------------------
// Purpose: Allow all debug overlays to be cleared at once
//-----------------------------------------------------------------------------
CON_COMMAND( clear_debug_overlays, "clears debug overlays" )
{
if ( !UTIL_IsCommandIssuedByServerAdmin() )
return;
CBaseEntity *pEntity = gEntList.FirstEnt();
// Clear all entities of their debug overlays
while ( pEntity )
{
pEntity->m_debugOverlays = 0;
// UNDONE: Clear out / expire timed overlays?
pEntity = gEntList.NextEnt( pEntity );
}
// Clear all engine overlays
if ( debugoverlay )
{
debugoverlay->ClearAllOverlays();
}
}

View file

@ -518,7 +518,7 @@ void CCSPlayer::FireBullet(
shouldDebugHitboxesOnHit = shouldDebugHitboxesOnHit && (cl_showimpacts.GetInt() == 1 || cl_showimpacts.GetInt() == 2);
shouldDebugHitboxesOnFire = shouldDebugHitboxesOnFire && (cl_showfirebullethitboxes.GetInt() == 1 || cl_showfirebullethitboxes.GetInt() == 2);
#endif
if ( shouldDebugHitboxesOnFire && !shouldDebugHitboxesOnHit )
if ( shouldDebugHitboxesOnFire )
{
for (int i = 1; i <= gpGlobals->maxClients; i++)
{
@ -563,7 +563,7 @@ void CCSPlayer::FireBullet(
event->SetFloat(buffer, angles[indexes[i]].z);
}
gameeventmanager->FireEventClientSide(event);
gameeventmanager->FireEvent(event);
}
#endif
}
@ -641,7 +641,7 @@ void CCSPlayer::FireBullet(
event->SetFloat("dst_y", tr.endpos.y);
event->SetFloat("dst_z", tr.endpos.z);
event->SetFloat("radius", flBulletRadius);
gameeventmanager->FireEventClientSide(event);
gameeventmanager->FireEvent(event);
}
#endif
if (tr.m_pEnt && tr.m_pEnt->IsPlayer() && !shouldDebugHitboxesOnFire)
@ -685,7 +685,7 @@ void CCSPlayer::FireBullet(
eventHit->SetFloat(buffer, angles[indexes[i]].z);
}
gameeventmanager->FireEventClientSide(eventHit);
gameeventmanager->FireEvent(eventHit);
}
#endif
}

View file

@ -62,6 +62,7 @@ public:
// Simulation ticks
int tickcount;
int currenttick;
// Simulation tick interval
float interval_per_tick;

View file

@ -57,7 +57,7 @@ data field should not be broadcasted to clients, use the type "local".
#define MAX_EVENT_NAME_LENGTH 32 // max game event name length
#define MAX_EVENT_BITS 9 // max bits needed for an event index
#define MAX_EVENT_NUMBER (1<<MAX_EVENT_BITS) // max number of events allowed
#define MAX_EVENT_BYTES 1024 // max size in bytes for a serialized event
#define MAX_EVENT_BYTES (1<<12) // max size in bytes for a serialized event
class KeyValues;
class CGameEvent;
@ -82,6 +82,7 @@ public:
virtual void SetInt( const char *keyName, int value ) = 0;
virtual void SetFloat( const char *keyName, float value ) = 0;
virtual void SetString( const char *keyName, const char *value ) = 0;
virtual KeyValues *GetDataKeys() = 0;
};

Binary file not shown.