diff options
Diffstat (limited to 'NorthstarDLL/limits.cpp')
-rw-r--r-- | NorthstarDLL/limits.cpp | 126 |
1 files changed, 119 insertions, 7 deletions
diff --git a/NorthstarDLL/limits.cpp b/NorthstarDLL/limits.cpp index f1dc23bd..fd635136 100644 --- a/NorthstarDLL/limits.cpp +++ b/NorthstarDLL/limits.cpp @@ -3,7 +3,10 @@ #include "hoststate.h" #include "r2client.h" #include "r2engine.h" +#include "r2server.h" +#include "maxplayers.h" #include "tier0.h" +#include "vector.h" #include "serverauthentication.h" AUTOHOOK_INIT() @@ -12,12 +15,41 @@ ServerLimitsManager* g_pServerLimits; ConVar* Cvar_net_datablock_enabled; +// todo: make this work on higher timescales, also possibly disable when sv_cheats is set +void ServerLimitsManager::RunFrame(double flCurrentTime, float flFrameTime) +{ + if (Cvar_sv_antispeedhack_enable->GetBool()) + { + // for each player, set their usercmd processing budget for the frame to the last frametime for the server + for (int i = 0; i < R2::GetMaxPlayers(); i++) + { + R2::CBaseClient* player = &R2::g_pClientArray[i]; + + if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end()) + { + PlayerLimitData* pLimitData = &g_pServerLimits->m_PlayerLimitData[player]; + if (pLimitData->flFrameUserCmdBudget < 0.016666667 * Cvar_sv_antispeedhack_maxtickbudget->GetFloat()) + pLimitData->flFrameUserCmdBudget += + fmax(flFrameTime, 0.016666667) * g_pServerLimits->Cvar_sv_antispeedhack_budgetincreasemultiplier->GetFloat(); + } + } + } +} + void ServerLimitsManager::AddPlayer(R2::CBaseClient* player) { PlayerLimitData limitData; + limitData.flFrameUserCmdBudget = 0.016666667 * Cvar_sv_antispeedhack_maxtickbudget->GetFloat(); + m_PlayerLimitData.insert(std::make_pair(player, limitData)); } +void ServerLimitsManager::RemovePlayer(R2::CBaseClient* player) +{ + if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end()) + m_PlayerLimitData.erase(player); +} + bool ServerLimitsManager::CheckStringCommandLimits(R2::CBaseClient* player) { if (CVar_sv_quota_stringcmdspersecond->GetInt() != -1) @@ -31,8 +63,7 @@ bool ServerLimitsManager::CheckStringCommandLimits(R2::CBaseClient* player) } m_PlayerLimitData[player].numClientCommandsInQuota++; - if (m_PlayerLimitData[player].numClientCommandsInQuota > - CVar_sv_quota_stringcmdspersecond->GetInt()) + if (m_PlayerLimitData[player].numClientCommandsInQuota > CVar_sv_quota_stringcmdspersecond->GetInt()) { // too many stringcmds, dc player return false; @@ -57,8 +88,10 @@ bool ServerLimitsManager::CheckChatLimits(R2::CBaseClient* player) return true; } +// clang-format off AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0, char, __fastcall, (void* self, void* buf)) +// clang-format on { enum eNetChanLimitMode { @@ -87,8 +120,7 @@ char, __fastcall, (void* self, void* buf)) g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart = startTime; g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime = 0.0; } - g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime += - (Tier0::Plat_FloatTime() * 1000) - (startTime * 1000); + g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime += (Tier0::Plat_FloatTime() * 1000) - (startTime * 1000); if (g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime >= g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt()) @@ -100,8 +132,7 @@ char, __fastcall, (void* self, void* buf)) g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt()); // never kick local player - if (g_pServerLimits->Cvar_net_chan_limit_mode->GetInt() != NETCHANLIMIT_WARN && - strcmp(R2::g_pLocalPlayerUserID, sender->m_UID)) + if (g_pServerLimits->Cvar_net_chan_limit_mode->GetInt() != NETCHANLIMIT_WARN && strcmp(R2::g_pLocalPlayerUserID, sender->m_UID)) { R2::CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit"); return false; @@ -112,8 +143,10 @@ char, __fastcall, (void* self, void* buf)) return ret; } +// clang-format off AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, bool, , (void* a1, R2::netpacket_t* packet)) +// clang-format on { if (packet->adr.type == R2::NA_IP && (!(packet->data[4] == 'N' && Cvar_net_datablock_enabled->GetBool()) || !Cvar_net_datablock_enabled->GetBool())) @@ -162,9 +195,71 @@ bool, , (void* a1, R2::netpacket_t* packet)) return ProcessConnectionlessPacket(a1, packet); } +// this is weird and i'm not sure if it's correct, so not using for now +/*AUTOHOOK(CBasePlayer__PhysicsSimulate, server.dll + 0x5A6E50, bool, __fastcall, (void* self, int a2, char a3)) +{ + spdlog::info("CBasePlayer::PhysicsSimulate"); + return CBasePlayer__PhysicsSimulate(self, a2, a3); +}*/ + +struct alignas(4) SV_CUserCmd +{ + DWORD command_number; + DWORD tick_count; + float command_time; + Vector3 worldViewAngles; + BYTE gap18[4]; + Vector3 localViewAngles; + Vector3 attackangles; + Vector3 move; + DWORD buttons; + BYTE impulse; + short weaponselect; + DWORD meleetarget; + BYTE gap4C[24]; + char headoffset; + BYTE gap65[11]; + Vector3 cameraPos; + Vector3 cameraAngles; + BYTE gap88[4]; + int tickSomething; + DWORD dword90; + DWORD predictedServerEventAck; + DWORD dword98; + float frameTime; +}; + +// clang-format off +AUTOHOOK(CPlayerMove__RunCommand, server.dll + 0x5B8100, +void, __fastcall, (void* self, R2::CBasePlayer* player, SV_CUserCmd* pUserCmd, uint64_t a4)) +// clang-format on +{ + if (g_pServerLimits->Cvar_sv_antispeedhack_enable->GetBool()) + { + R2::CBaseClient* pClient = &R2::g_pClientArray[player->m_nPlayerIndex - 1]; + + if (g_pServerLimits->m_PlayerLimitData.find(pClient) != g_pServerLimits->m_PlayerLimitData.end()) + { + PlayerLimitData* pLimitData = &g_pServerLimits->m_PlayerLimitData[pClient]; + + pLimitData->flFrameUserCmdBudget = fmax(0.0, pLimitData->flFrameUserCmdBudget - pUserCmd->frameTime); + + if (pLimitData->flFrameUserCmdBudget <= 0.0) + { + spdlog::warn("player {} went over usercmd budget ({})", pClient->m_Name, pLimitData->flFrameUserCmdBudget); + return; + } + // else + // spdlog::info("{}: {}", pClient->m_Name, pLimitData->flFrameUserCmdBudget); + } + } + + CPlayerMove__RunCommand(self, player, pUserCmd, a4); +} + ON_DLL_LOAD_RELIESON("engine.dll", ServerLimits, ConVar, (CModule module)) { - AUTOHOOK_DISPATCH() + AUTOHOOK_DISPATCH_MODULE(engine.dll) g_pServerLimits = new ServerLimitsManager; @@ -182,6 +277,23 @@ ON_DLL_LOAD_RELIESON("engine.dll", ServerLimits, ConVar, (CModule module)) "Netchannel processing is limited to so many milliseconds, abort connection if exceeding budget"); g_pServerLimits->Cvar_sv_querylimit_per_sec = new ConVar("sv_querylimit_per_sec", "15", FCVAR_GAMEDLL, ""); g_pServerLimits->Cvar_sv_max_chat_messages_per_sec = new ConVar("sv_max_chat_messages_per_sec", "5", FCVAR_GAMEDLL, ""); + g_pServerLimits->Cvar_sv_antispeedhack_enable = + new ConVar("sv_antispeedhack_enable", "0", FCVAR_NONE, "whether to enable antispeedhack protections"); + g_pServerLimits->Cvar_sv_antispeedhack_maxtickbudget = new ConVar( + "sv_antispeedhack_maxtickbudget", + "64", + FCVAR_GAMEDLL, + "Maximum number of client-issued usercmd ticks that can be replayed in packet loss conditions, 0 to allow no restrictions"); + g_pServerLimits->Cvar_sv_antispeedhack_budgetincreasemultiplier = new ConVar( + "sv_antispeedhack_budgetincreasemultiplier", + "1.2", + FCVAR_GAMEDLL, + "Increase usercmd processing budget by tickinterval * value per tick"); Cvar_net_datablock_enabled = R2::g_pCVar->FindVar("net_datablock_enabled"); } + +ON_DLL_LOAD("server.dll", ServerLimitsServer, (CModule module)) +{ + AUTOHOOK_DISPATCH_MODULE(server.dll) +} |