diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h index d444140..962a975 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -378,6 +378,8 @@ extern cvar_t sv_rehlds_attachedentities_playeranimationspeed_fix; extern cvar_t sv_rehlds_local_gametime; extern cvar_t sv_rehlds_send_mapcycle; extern cvar_t sv_usercmd_custom_random_seed; +extern cvar_t sv_maxusrcmdprocessticks; +extern cvar_t sv_maxusrcmdprocessticks_warning; #endif extern int sv_playermodel; diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 74a285f..2c663f7 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -210,6 +210,8 @@ cvar_t sv_rehlds_send_mapcycle = { "sv_rehlds_send_mapcycle", "0", 0, 0.0f, null cvar_t sv_rehlds_maxclients_from_single_ip = { "sv_rehlds_maxclients_from_single_ip", "5", 0, 5.0f, nullptr }; cvar_t sv_use_entity_file = { "sv_use_entity_file", "0", 0, 0.0f, nullptr }; cvar_t sv_usercmd_custom_random_seed = { "sv_usercmd_custom_random_seed", "0", 0, 0.0f, nullptr }; +cvar_t sv_maxusrcmdprocessticks = { "sv_maxusrcmdprocessticks", "16", 0, 0.0f, nullptr }; +cvar_t sv_maxusrcmdprocessticks_warning = { "sv_maxusrcmdprocessticks_warning", "-1", 0, 0.0f, nullptr }; #endif delta_t *SV_LookupDelta(char *name) @@ -8025,6 +8027,8 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_rollangle); Cvar_RegisterVariable(&sv_use_entity_file); Cvar_RegisterVariable(&sv_usercmd_custom_random_seed); + Cvar_RegisterVariable(&sv_maxusrcmdprocessticks); + Cvar_RegisterVariable(&sv_maxusrcmdprocessticks_warning); #endif for (int i = 0; i < MAX_MODELS; i++) diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 5e2c001..118250e 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -790,6 +790,54 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) } #endif +#ifdef REHLDS_FIXES + #define TICK_INTERVAL (1.0f / sys_ticrate.value) + + CGameClient* gameClient = g_GameClients[host_client - g_psvs.clients]; + // bool bRunNullCmd = false; + if (int numUsrCmdProcessTicksMax = (int)sv_maxusrcmdprocessticks.value) + { + // Grant the client some time buffer to execute user commands + gameClient->m_flMovementTimeForUserCmdProcessingRemaining += TICK_INTERVAL; + + // but never accumulate more than N ticks + if (gameClient->GetRemainingMovementTimeForUserCmdProcessing() > numUsrCmdProcessTicksMax * TICK_INTERVAL) + { + gameClient->m_flMovementTimeForUserCmdProcessingRemaining = numUsrCmdProcessTicksMax * TICK_INTERVAL; + // bRunNullCmd = true; + } + } + /*else + { + // Otherwise we don't care to track time + m_flMovementTimeForUserCmdProcessingRemaining = FLT_MAX; + } + */ + + float playerFrameTime = TICK_INTERVAL; + float flTimeAllowedForProcessing = gameClient->ConsumeMovementTimeForUserCmdProcessing(playerFrameTime); + bool isBot = host_client->fakeclient; + + if (!isBot && (flTimeAllowedForProcessing < playerFrameTime)) + { + // Make sure that the activity in command is erased because player cheated or dropped too many packets + double dblWarningFrequencyThrottle = sv_maxusrcmdprocessticks_warning.value; + if (dblWarningFrequencyThrottle >= 0.0) + { + static double s_dblLastWarningTime = 0.0; + double dblTimeNow = Sys_FloatTime(); + if (!s_dblLastWarningTime || (dblTimeNow - s_dblLastWarningTime >= dblWarningFrequencyThrottle)) + { + s_dblLastWarningTime = dblTimeNow; + Con_Printf("sv_maxusrcmdprocessticks_warning at server tick %u: Ignored client %s usrcmd (%.6f < %.6f)!\n", + /* System::GetTick() */ 123, host_client->name, flTimeAllowedForProcessing, playerFrameTime); + } + } + + return; // Don't process this command + } +#endif // REHLDS_FIXES + gEntityInterface.pfnCmdStart(sv_player, ucmd, random_seed); frametime = float(ucmd->msec * 0.001); host_client->svtimebase = frametime + host_client->svtimebase; diff --git a/rehlds/rehlds/rehlds_interfaces_impl.h b/rehlds/rehlds/rehlds_interfaces_impl.h index 5cf4dba..cba5421 100644 --- a/rehlds/rehlds/rehlds_interfaces_impl.h +++ b/rehlds/rehlds/rehlds_interfaces_impl.h @@ -251,6 +251,30 @@ public: void SetupLocalGameTime() { m_localGameTimeBase = g_psv.time; } double GetLocalGameTime() const { return g_psv.time - m_localGameTimeBase; } double GetLocalGameTimeBase() const { return m_localGameTimeBase; } + + // How much of a movement time buffer can we process from this user? + float m_flMovementTimeForUserCmdProcessingRemaining; + + float GetRemainingMovementTimeForUserCmdProcessing() const { return m_flMovementTimeForUserCmdProcessingRemaining; } + float ConsumeMovementTimeForUserCmdProcessing( float flTimeNeeded ) + { + float MOVE_EPSILON = 0.03125f; + if ( m_flMovementTimeForUserCmdProcessingRemaining <= 0.0f ) + return 0.0f; + else if ( flTimeNeeded > m_flMovementTimeForUserCmdProcessingRemaining + MOVE_EPSILON ) + { + float flResult = m_flMovementTimeForUserCmdProcessingRemaining; + m_flMovementTimeForUserCmdProcessingRemaining = 0.0f; + return flResult; + } + else + { + m_flMovementTimeForUserCmdProcessingRemaining -= flTimeNeeded; + if ( m_flMovementTimeForUserCmdProcessingRemaining < 0.0f ) + m_flMovementTimeForUserCmdProcessingRemaining = 0.0f; + return flTimeNeeded; + } + } #endif };