diff --git a/AMBuilder b/AMBuilder
index d48208dc..6a3edacb 100644
--- a/AMBuilder
+++ b/AMBuilder
@@ -36,7 +36,6 @@ for sdk_target in MMSPlugin.sdk_targets:
'src/adminsystem.cpp',
'src/commands.cpp',
'src/addresses.cpp',
- 'src/detours.cpp',
'src/entities.cpp',
'src/events.cpp',
'src/utils/entity.cpp',
@@ -51,6 +50,7 @@ for sdk_target in MMSPlugin.sdk_targets:
'src/gameconfig.cpp',
'src/gamesystem.cpp',
'src/votemanager.cpp',
+ 'src/hookmanager.cpp',
'src/httpmanager.cpp',
'src/discord.cpp',
'src/map_votes.cpp',
diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj
index e564762d..36fc5ad4 100644
--- a/CS2Fixes.vcxproj
+++ b/CS2Fixes.vcxproj
@@ -193,13 +193,13 @@
-
+
@@ -264,13 +264,13 @@
-
+
diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters
index 2696c312..60943f02 100644
--- a/CS2Fixes.vcxproj.filters
+++ b/CS2Fixes.vcxproj.filters
@@ -65,9 +65,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -86,6 +83,9 @@
Source Files\sdk
+
+ Source Files
+
Source Files
@@ -256,9 +256,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -274,6 +271,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/src/adminsystem.cpp b/src/adminsystem.cpp
index 1cdc6726..69e575ad 100644
--- a/src/adminsystem.cpp
+++ b/src/adminsystem.cpp
@@ -21,7 +21,6 @@
#include "KeyValues.h"
#include "commands.h"
#include "ctimer.h"
-#include "detours.h"
#include "discord.h"
#include "entity/cbaseentity.h"
#include "entity/cgamerules.h"
@@ -29,6 +28,7 @@
#include "entwatch.h"
#include "filesystem.h"
#include "gamesystem.h"
+#include "hookmanager.h"
#include "hud_manager.h"
#include "icvar.h"
#include "interfaces/interfaces.h"
diff --git a/src/buttonwatch.cpp b/src/buttonwatch.cpp
index b8e0c00a..cb7df77e 100644
--- a/src/buttonwatch.cpp
+++ b/src/buttonwatch.cpp
@@ -22,7 +22,6 @@
#include "commands.h"
#include "cs2fixes.h"
#include "ctimer.h"
-#include "detours.h"
#include "entity.h"
#include "entity/cbaseplayercontroller.h"
#include "entity/ccsplayercontroller.h"
diff --git a/src/buttonwatch.h b/src/buttonwatch.h
index d7e9a825..792aeae4 100644
--- a/src/buttonwatch.h
+++ b/src/buttonwatch.h
@@ -18,7 +18,7 @@
*/
#pragma once
-#include "detours.h"
+#include "cs2_sdk/entityio.h"
extern CConVar g_cvarEnableButtonWatch;
diff --git a/src/commands.cpp b/src/commands.cpp
index 51642931..dab1855d 100644
--- a/src/commands.cpp
+++ b/src/commands.cpp
@@ -21,7 +21,6 @@
#include "adminsystem.h"
#include "common.h"
#include "ctimer.h"
-#include "detours.h"
#include "discord.h"
#include "engine/igameeventsystem.h"
#include "entity/cbaseentity.h"
@@ -31,6 +30,7 @@
#include "entity/ccsweaponbase.h"
#include "entity/cparticlesystem.h"
#include "entity/lights.h"
+#include "hookmanager.h"
#include "httpmanager.h"
#include "leader.h"
#include "networksystem/inetworkmessages.h"
diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h
index e502d01d..93b96729 100644
--- a/src/cs2_sdk/entity/cbaseentity.h
+++ b/src/cs2_sdk/entity/cbaseentity.h
@@ -19,8 +19,8 @@
#pragma once
+#include "../../addresses.h"
#include "../../gameconfig.h"
-#include "../detours.h"
#include "ccollisionproperty.h"
#include "ctakedamageinfo.h"
#include "ehandle.h"
diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp
index d8a6c785..6c29e66d 100644
--- a/src/cs2fixes.cpp
+++ b/src/cs2fixes.cpp
@@ -26,7 +26,6 @@
#include "common.h"
#include "cs_gameevents.pb.h"
#include "ctimer.h"
-#include "detours.h"
#include "discord.h"
#include "entities.h"
#include "entity/ccsplayercontroller.h"
@@ -38,6 +37,7 @@
#include "gameconfig.h"
#include "gameevents.pb.h"
#include "gamesystem.h"
+#include "hookmanager.h"
#include "httpmanager.h"
#include "hud_manager.h"
#include "icvar.h"
@@ -64,37 +64,6 @@
#include "tier0/memdbgon.h"
-class GameSessionConfiguration_t
-{};
-
-KHook::Virtual gameFrameHook(&IServerGameDLL::GameFrame, &g_CS2Fixes, nullptr, &CS2Fixes::Hook_GameFrame_Post);
-KHook::Virtual gameServerSteamAPIActivatedHook(&IServerGameDLL::GameServerSteamAPIActivated, &g_CS2Fixes, &CS2Fixes::Hook_GameServerSteamAPIActivated, nullptr);
-KHook::Virtual applyGameSettingsHook(&IServerGameDLL::ApplyGameSettings, &g_CS2Fixes, &CS2Fixes::Hook_ApplyGameSettings, nullptr);
-KHook::Virtual clientActiveHook(&IServerGameClients::ClientActive, &g_CS2Fixes, nullptr, &CS2Fixes::Hook_ClientActive_Post);
-KHook::Virtual clientDisconnectHook(&IServerGameClients::ClientDisconnect, &g_CS2Fixes, nullptr, &CS2Fixes::Hook_ClientDisconnect_Post);
-KHook::Virtual clientPutInServerHook(&IServerGameClients::ClientPutInServer, &g_CS2Fixes, nullptr, &CS2Fixes::Hook_ClientPutInServer_Post);
-KHook::Virtual clientSettingsChangedHook(&IServerGameClients::ClientSettingsChanged, &g_CS2Fixes, &CS2Fixes::Hook_ClientSettingsChanged, nullptr);
-KHook::Virtual onClientConnectedHook(&IServerGameClients::OnClientConnected, &g_CS2Fixes, &CS2Fixes::Hook_OnClientConnected, nullptr);
-KHook::Virtual clientConnectHook(&IServerGameClients::ClientConnect, &g_CS2Fixes, &CS2Fixes::Hook_ClientConnect, nullptr);
-KHook::Virtual clientCommandHook(&IServerGameClients::ClientCommand, &g_CS2Fixes, &CS2Fixes::Hook_ClientCommand, nullptr);
-KHook::Virtual postEventAbstractHook(&IGameEventSystem::PostEventAbstract, &g_CS2Fixes, &CS2Fixes::Hook_PostEventAbstract, nullptr);
-KHook::Virtual startupServerHook(&INetworkServerService::StartupServer, &g_CS2Fixes, nullptr, &CS2Fixes::Hook_StartupServer_Post);
-KHook::Virtual checkTransmitHook(&ISource2GameEntities::CheckTransmit, &g_CS2Fixes, nullptr, &CS2Fixes::Hook_CheckTransmit_Post);
-KHook::Virtual dispatchConCommandHook(&ICvar::DispatchConCommand, &g_CS2Fixes, &CS2Fixes::Hook_DispatchConCommand, nullptr);
-KHook::Virtual loadEventsFromFileHook(&IGameEventManager2::LoadEventsFromFile, &g_CS2Fixes, &CS2Fixes::Hook_LoadEventsFromFile, nullptr);
-KHook::Virtual spawnHook(&CEntitySystem::Spawn, &g_CS2Fixes, nullptr, &CS2Fixes::Hook_Spawn_Post);
-KHook::Virtual setGameSpawnGroupMgrHook(&INetworkGameServer::SetGameSpawnGroupMgr, &g_CS2Fixes, &CS2Fixes::Hook_SetGameSpawnGroupMgr, nullptr);
-KHook::Virtual createWorkshopMapGroupHook(&g_CS2Fixes, &CS2Fixes::Hook_CreateWorkshopMapGroup, nullptr);
-KHook::Virtual getTouchingListHook(&g_CS2Fixes, nullptr, &CS2Fixes::Hook_GetTouchingList_Post);
-KHook::Virtual checkMovingGroundHook(&g_CS2Fixes, &CS2Fixes::Hook_CheckMovingGround, nullptr);
-KHook::Virtual dropWeaponHook(&g_CS2Fixes, nullptr, &CS2Fixes::Hook_DropWeapon_Post);
-KHook::Virtual playerEquipUseHook(&g_CS2Fixes, &CS2Fixes::Hook_PlayerEquipUse, nullptr);
-KHook::Virtual playerEquipPrecacheHook(&g_CS2Fixes, nullptr, &CS2Fixes::Hook_PlayerEquipPrecache_Post);
-KHook::Virtual triggerGravityPrecacheHook(&g_CS2Fixes, nullptr, &CS2Fixes::Hook_TriggerGravityPrecache_Post);
-KHook::Virtual triggerGravityEndTouchHook(&g_CS2Fixes, nullptr, &CS2Fixes::Hook_TriggerGravityEndTouch_Post);
-KHook::Virtual onTakeDamageAliveHook(&g_CS2Fixes, &CS2Fixes::Hook_OnTakeDamage_Alive, nullptr);
-KHook::Virtual playerPawnTeleportHook(&g_CS2Fixes, &CS2Fixes::Hook_CCSPlayerPawn_Teleport, nullptr);
-
CS2Fixes g_CS2Fixes;
IGameEventSystem* g_gameEventSystem = nullptr;
IGameEventManager2* g_gameEventManager = nullptr;
@@ -103,18 +72,6 @@ IVEngineServer2* g_pEngineServer2 = nullptr;
CCSGameRules* g_pGameRules = nullptr; // Will be null between map end & new map startup, null check if necessary!
CSpawnGroupMgrGameSystem* g_pSpawnGroupMgr = nullptr; // Will be null between map end & new map startup, null check if necessary!
-IGameEventManager2* g_pCGameEventManagerVTable = nullptr;
-CEntitySystem* g_pCEntitySystemVTable = nullptr;
-CVPhys2World* g_pCVPhys2WorldVTable = nullptr;
-CCSPlayer_MovementServices* g_pCCSPlayer_MovementServicesVTable = nullptr;
-CCSPlayer_WeaponServices* g_pCCSPlayer_WeaponServicesVTable = nullptr;
-CGamePlayerEquip* g_pCGamePlayerEquipVTable = nullptr;
-CTriggerGravity* g_pTriggerGravityVTable = nullptr;
-CCSPlayerPawn* g_pCCSPlayerPawnVTable = nullptr;
-
-double g_flUniversalTime = 0.0;
-float g_flLastTickedTime = 0.0f;
-bool g_bHasTicked = false;
bool g_bRequiredInitLoaded = true;
CGameEntitySystem* GameEntitySystem()
@@ -170,185 +127,17 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
return false;
}
- gameFrameHook.Add(g_pSource2Server);
- gameServerSteamAPIActivatedHook.Add(g_pSource2Server);
- applyGameSettingsHook.Add(g_pSource2Server);
- clientActiveHook.Add(g_pSource2GameClients);
- clientDisconnectHook.Add(g_pSource2GameClients);
- clientPutInServerHook.Add(g_pSource2GameClients);
- clientSettingsChangedHook.Add(g_pSource2GameClients);
- onClientConnectedHook.Add(g_pSource2GameClients);
- clientConnectHook.Add(g_pSource2GameClients);
- clientCommandHook.Add(g_pSource2GameClients);
- postEventAbstractHook.Add(g_gameEventSystem);
- startupServerHook.Add(g_pNetworkServerService);
- checkTransmitHook.Add(g_pSource2GameEntities);
- dispatchConCommandHook.Add(g_pCVar);
-
if (!addresses::Initialize(g_GameConfig))
g_bRequiredInitLoaded = false;
if (!InitPatches(g_GameConfig))
g_bRequiredInitLoaded = false;
- InitDetours(g_GameConfig);
+ g_pHookManager = new CHookManager(g_GameConfig);
if (!InitGameSystems())
g_bRequiredInitLoaded = false;
- g_pCGameEventManagerVTable = (IGameEventManager2*)modules::server->FindVirtualTable("CGameEventManager");
- if (!g_pCGameEventManagerVTable)
- {
- Panic("Failed to find CGameEventManager vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- loadEventsFromFileHook.AddGlobal((IGameEventManager2*)&g_pCGameEventManagerVTable);
-
- g_pCEntitySystemVTable = (CEntitySystem*)modules::server->FindVirtualTable("CGameEntitySystem");
- if (!g_pCEntitySystemVTable)
- {
- Panic("Failed to find CGameEntitySystem vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- spawnHook.AddGlobal((CEntitySystem*)&g_pCEntitySystemVTable);
-
- int offset = g_GameConfig->GetOffset("IGameTypes_CreateWorkshopMapGroup");
- if (offset == -1)
- {
- Panic("Failed to find IGameTypes_CreateWorkshopMapGroup\n");
- g_bRequiredInitLoaded = false;
- }
-
- createWorkshopMapGroupHook.Configure(offset);
- createWorkshopMapGroupHook.Add(g_pGameTypes);
-
- g_pCVPhys2WorldVTable = (CVPhys2World*)modules::vphysics2->FindVirtualTable("CVPhys2World");
- if (!g_pCVPhys2WorldVTable)
- {
- Panic("Failed to find CVPhys2World vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- offset = g_GameConfig->GetOffset("CVPhys2World::GetTouchingList");
- if (offset == -1)
- {
- Panic("Failed to find offset for CVPhys2World::GetTouchingList\n");
- g_bRequiredInitLoaded = false;
- }
-
- getTouchingListHook.Configure(offset);
- getTouchingListHook.AddGlobal((CVPhys2World*)&g_pCVPhys2WorldVTable);
-
- g_pCCSPlayer_MovementServicesVTable = (CCSPlayer_MovementServices*)modules::server->FindVirtualTable("CCSPlayer_MovementServices");
- if (!g_pCCSPlayer_MovementServicesVTable)
- {
- Panic("Failed to find CCSPlayer_MovementServices vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- offset = g_GameConfig->GetOffset("CCSPlayer_MovementServices::CheckMovingGround");
- if (offset == -1)
- {
- Panic("Failed to find offset for CCSPlayer_MovementServices::CheckMovingGround\n");
- g_bRequiredInitLoaded = false;
- }
-
- checkMovingGroundHook.Configure(offset);
- checkMovingGroundHook.AddGlobal((CCSPlayer_MovementServices*)&g_pCCSPlayer_MovementServicesVTable);
-
- g_pCCSPlayer_WeaponServicesVTable = (CCSPlayer_WeaponServices*)modules::server->FindVirtualTable("CCSPlayer_WeaponServices");
- if (!g_pCCSPlayer_WeaponServicesVTable)
- {
- Panic("Failed to find CCSPlayer_WeaponServices vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- offset = g_GameConfig->GetOffset("CCSPlayer_WeaponServices::DropWeapon");
- if (offset == -1)
- {
- Panic("Failed to find offset for CCSPlayer_WeaponServices::DropWeapon\n");
- g_bRequiredInitLoaded = false;
- }
-
- dropWeaponHook.Configure(offset);
- dropWeaponHook.AddGlobal((CCSPlayer_WeaponServices*)&g_pCCSPlayer_WeaponServicesVTable);
-
- g_pCGamePlayerEquipVTable = (CGamePlayerEquip*)modules::server->FindVirtualTable("CGamePlayerEquip");
- if (!g_pCGamePlayerEquipVTable)
- {
- Panic("Failed to find CGamePlayerEquip vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- offset = g_GameConfig->GetOffset("CBaseEntity::Use");
- if (offset == -1)
- {
- Panic("Failed to find offset for CBaseEntity::Use\n");
- g_bRequiredInitLoaded = false;
- }
-
- playerEquipUseHook.Configure(offset);
- playerEquipUseHook.AddGlobal((CGamePlayerEquip*)&g_pCGamePlayerEquipVTable);
-
- offset = g_GameConfig->GetOffset("CBaseEntity::Precache");
- if (offset == -1)
- {
- Panic("Failed to find offset for CBaseEntity::Precache\n");
- g_bRequiredInitLoaded = false;
- }
-
- playerEquipPrecacheHook.Configure(offset);
- playerEquipPrecacheHook.AddGlobal((CGamePlayerEquip*)&g_pCGamePlayerEquipVTable);
-
- g_pTriggerGravityVTable = (CTriggerGravity*)modules::server->FindVirtualTable("CTriggerGravity");
- if (!g_pTriggerGravityVTable)
- {
- Panic("Failed to find CTriggerGravity vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- triggerGravityPrecacheHook.Configure(offset);
- triggerGravityPrecacheHook.AddGlobal((CTriggerGravity*)&g_pTriggerGravityVTable);
-
- offset = g_GameConfig->GetOffset("CBaseEntity::EndTouch");
- if (offset == -1)
- {
- Panic("Failed to find offset for CBaseEntity::EndTouch\n");
- g_bRequiredInitLoaded = false;
- }
-
- triggerGravityEndTouchHook.Configure(offset);
- triggerGravityEndTouchHook.AddGlobal((CTriggerGravity*)&g_pTriggerGravityVTable);
-
- g_pCCSPlayerPawnVTable = (CCSPlayerPawn*)modules::server->FindVirtualTable("CCSPlayerPawn");
- if (!g_pCCSPlayerPawnVTable)
- {
- Panic("Failed to find CCSPlayerPawn vtable\n");
- g_bRequiredInitLoaded = false;
- }
-
- offset = g_GameConfig->GetOffset("CCSPlayerPawn::OnTakeDamage_Alive");
- if (offset == -1)
- {
- Panic("Failed to find offset for CCSPlayerPawn::OnTakeDamage_Alive\n");
- g_bRequiredInitLoaded = false;
- }
-
- onTakeDamageAliveHook.Configure(offset);
- onTakeDamageAliveHook.AddGlobal((CCSPlayerPawn*)&g_pCCSPlayerPawnVTable);
-
- offset = g_GameConfig->GetOffset("Teleport");
- if (offset == -1)
- {
- Panic("Failed to find offset for Teleport\n");
- g_bRequiredInitLoaded = false;
- }
-
- playerPawnTeleportHook.Configure(offset);
- playerPawnTeleportHook.AddGlobal((CCSPlayerPawn*)&g_pCCSPlayerPawnVTable);
-
if (!g_bRequiredInitLoaded)
{
snprintf(error, maxlen, "One or more address lookups, patches or detours failed, please refer to startup logs for more information");
@@ -429,33 +218,6 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
bool CS2Fixes::Unload(char* error, size_t maxlen)
{
- gameFrameHook.Remove(g_pSource2Server);
- gameServerSteamAPIActivatedHook.Remove(g_pSource2Server);
- applyGameSettingsHook.Remove(g_pSource2Server);
- clientActiveHook.Remove(g_pSource2GameClients);
- clientDisconnectHook.Remove(g_pSource2GameClients);
- clientPutInServerHook.Remove(g_pSource2GameClients);
- clientSettingsChangedHook.Remove(g_pSource2GameClients);
- onClientConnectedHook.Remove(g_pSource2GameClients);
- clientConnectHook.Remove(g_pSource2GameClients);
- clientCommandHook.Remove(g_pSource2GameClients);
- postEventAbstractHook.Remove(g_gameEventSystem);
- startupServerHook.Remove(g_pNetworkServerService);
- checkTransmitHook.Remove(g_pSource2GameEntities);
- dispatchConCommandHook.Remove(g_pCVar);
- loadEventsFromFileHook.RemoveGlobal((IGameEventManager2*)&g_pCGameEventManagerVTable);
- spawnHook.RemoveGlobal((CEntitySystem*)&g_pCEntitySystemVTable);
- setGameSpawnGroupMgrHook.Remove(GetNetworkGameServer());
- createWorkshopMapGroupHook.Remove(g_pGameTypes);
- getTouchingListHook.RemoveGlobal((CVPhys2World*)&g_pCVPhys2WorldVTable);
- checkMovingGroundHook.RemoveGlobal((CCSPlayer_MovementServices*)&g_pCCSPlayer_MovementServicesVTable);
- dropWeaponHook.RemoveGlobal((CCSPlayer_WeaponServices*)&g_pCCSPlayer_WeaponServicesVTable);
- playerEquipUseHook.RemoveGlobal((CGamePlayerEquip*)&g_pCGamePlayerEquipVTable);
- playerEquipPrecacheHook.RemoveGlobal((CGamePlayerEquip*)&g_pCGamePlayerEquipVTable);
- triggerGravityPrecacheHook.RemoveGlobal((CTriggerGravity*)&g_pTriggerGravityVTable);
- triggerGravityEndTouchHook.RemoveGlobal((CTriggerGravity*)&g_pTriggerGravityVTable);
- onTakeDamageAliveHook.RemoveGlobal((CCSPlayerPawn*)&g_pCCSPlayerPawnVTable);
- playerPawnTeleportHook.RemoveGlobal((CCSPlayerPawn*)&g_pCCSPlayerPawnVTable);
ConVar_Unregister();
@@ -468,6 +230,9 @@ bool CS2Fixes::Unload(char* error, size_t maxlen)
if (g_GameConfig)
delete g_GameConfig;
+ if (g_pHookManager)
+ delete g_pHookManager;
+
if (g_pAdminSystem)
delete g_pAdminSystem;
@@ -519,306 +284,6 @@ bool CS2Fixes::Unload(char* error, size_t maxlen)
return true;
}
-KHook::Return CS2Fixes::Hook_DispatchConCommand(ICvar* pThis, ConCommandRef cmdHandle, const CCommandContext& ctx, const CCommand& args)
-{
- VPROF_BUDGET("CS2Fixes::Hook_DispatchConCommand", "ConCommands");
-
- if (!g_pEntitySystem)
- return {KHook::Action::Ignore};
-
- auto iCommandPlayerSlot = ctx.GetPlayerSlot();
-
- if (!g_cvarEnableCommands.Get())
- return {KHook::Action::Ignore};
-
- bool bSay = !V_strcmp(args.Arg(0), "say");
- bool bTeamSay = !V_strcmp(args.Arg(0), "say_team");
-
- if (iCommandPlayerSlot != -1 && (bSay || bTeamSay))
- {
- auto pController = CCSPlayerController::FromSlot(iCommandPlayerSlot);
- bool bGagged = pController && pController->GetZEPlayer()->IsGagged();
- bool bFlooding = pController && pController->GetZEPlayer()->IsFlooding();
- bool bIsAdmin = pController && pController->GetZEPlayer()->IsAdminFlagSet(ADMFLAG_GENERIC);
- bool bAdminChat = bTeamSay && *args[1] == '@';
- bool bSilent = *args[1] == '/' || bAdminChat;
- bool bCommand = *args[1] == '!' || *args[1] == '/';
-
- // Chat messages should generate events regardless
- if (pController)
- {
- IGameEvent* pEvent = g_gameEventManager->CreateEvent("player_chat");
-
- if (pEvent)
- {
- pEvent->SetBool("teamonly", bTeamSay);
- pEvent->SetInt("userid", pController->GetPlayerSlot());
- pEvent->SetString("text", args[1]);
-
- g_gameEventManager->FireEvent(pEvent, true);
- }
- }
-
- if (!bGagged && !bSilent && !bFlooding)
- {
- dispatchConCommandHook.CallOriginal(pThis, cmdHandle, ctx, args);
- }
- else if (bFlooding)
- {
- if (pController)
- ClientPrint(pController, HUD_PRINTTALK, CHAT_PREFIX "You are flooding the server!");
- }
- else if (bAdminChat && GetGlobals()) // Admin chat can be sent by anyone but only seen by admins, use flood protection here too
- {
- // HACK: At this point, we can safely modify the arg buffer as it won't be passed anywhere else
- // The string here is originally ("@foo bar"), trim it to be (foo bar)
- char* pszMessage = (char*)(args.ArgS() + 2);
- pszMessage[V_strlen(pszMessage) - 1] = 0;
-
- for (int i = 0; i < GetGlobals()->maxClients; i++)
- {
- ZEPlayer* pPlayer = g_playerManager->GetPlayer(i);
-
- if (!pPlayer)
- continue;
-
- if (i == iCommandPlayerSlot.Get() || pPlayer->IsAdminFlagSet(ADMFLAG_GENERIC))
- ClientPrint(CCSPlayerController::FromSlot(i), HUD_PRINTTALK, " \4(%sADMINS) %s:\6 %s", bIsAdmin ? "" : "TO ", pController->GetPlayerName().c_str(), pszMessage);
- }
- }
-
- // Finally, run the chat command if it is one, so anything will print after the player's message
- if (bCommand)
- {
- char* pszMessage = (char*)(args.ArgS() + 1);
-
- if (pszMessage[0] == '"' || pszMessage[0] == '!' || pszMessage[0] == '/')
- pszMessage += 1;
-
- // Host_Say at some point removes the trailing " for whatever reason, so we only remove if it was never called
- if ((bGagged || bSilent || bFlooding) && pszMessage[V_strlen(pszMessage) - 1] == '"')
- pszMessage[V_strlen(pszMessage) - 1] = '\0';
-
- ParseChatCommand(pszMessage, pController);
- }
-
- return {KHook::Action::Supersede};
- }
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarMotdUrl("cs2f_motd_url", FCVAR_NONE, "Server MOTD URL, shows up as a \"Server Website\" button in scoreboard", "");
-
-KHook::Return CS2Fixes::Hook_StartupServer_Post(INetworkServerService* pThis, const GameSessionConfiguration_t& config, ISource2WorldSession* pSession, const char* pszMapName)
-{
- g_pEntitySystem = GameEntitySystem();
- g_pEntitySystem->AddListenerEntity(g_pEntityListener);
-
- if (GetNetworkGameServer())
- setGameSpawnGroupMgrHook.Add(GetNetworkGameServer());
-
- Message("Hook_StartupServer: %s\n", pszMapName);
-
- RegisterEventListeners();
-
- if (g_bHasTicked)
- RemoveTimers(TIMERFLAG_MAP);
-
- g_bHasTicked = false;
-
- g_pPanoramaVoteHandler->Reset();
- g_pVoteManager->VoteManager_Init();
- g_pIdleSystem->Reset();
-
- INetworkStringTable* pInfoPanelTable = g_pNetworkStringTableServer->FindTable("InfoPanel");
-
- if (pInfoPanelTable && V_strcmp(g_cvarMotdUrl.Get(), ""))
- {
- SetStringUserDataRequest_t pUserData;
- pUserData.m_pRawData = (void*)g_cvarMotdUrl.Get().Get();
- pUserData.m_cbDataSize = g_cvarMotdUrl.Get().Length() + 1;
-
- pInfoPanelTable->AddString(true, "motd", &pUserData);
- }
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_PlayerEquipUse(CGamePlayerEquip* pThis, InputData_t* pInput)
-{
- CGamePlayerEquipHandler::Use(pThis, pInput);
-
- return {KHook::Action::Ignore};
-}
-KHook::Return CS2Fixes::Hook_PlayerEquipPrecache_Post(CGamePlayerEquip* pThis, CEntityPrecacheContext* param)
-{
- const auto kv = param->m_pKeyValues;
- CGamePlayerEquipHandler::OnPrecache(pThis, kv);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_TriggerGravityPrecache_Post(CTriggerGravity* pThis, CEntityPrecacheContext* param)
-{
- const auto kv = param->m_pKeyValues;
- CTriggerGravityHandler::OnPrecache(pThis, kv);
-
- return {KHook::Action::Ignore};
-}
-KHook::Return CS2Fixes::Hook_TriggerGravityEndTouch_Post(CTriggerGravity* pThis, CBaseEntity* pOther)
-{
- CTriggerGravityHandler::OnEndTouch(pThis, pOther);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_GameServerSteamAPIActivated(IServerGameDLL* pThis)
-{
- g_playerManager->OnSteamAPIActivated();
-
- if (g_cvarVoteManagerEnable.Get() && !g_pMapVoteSystem->IsMapListLoaded())
- g_pMapVoteSystem->LoadMapList();
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarBlockParticleMsgs("cs2f_block_particle_msgs", FCVAR_NONE, "Whether to block CUserMsg_ParticleManager messages to fix lag/crashes, experimental", false);
-
-KHook::Return CS2Fixes::Hook_PostEventAbstract(IGameEventSystem* pThis, CSplitScreenSlot nSlot, bool bLocalOnly, int nClientCount, const uint64* clients,
- INetworkMessageInternal* pEvent, const CNetMessage* pData, unsigned long nSize, NetChannelBufType_t bufType)
-{
- // Message( "Hook_PostEvent(%d, %d, %d, %lli)\n", nSlot, bLocalOnly, nClientCount, clients );
- NetMessageInfo_t* info = pEvent->GetNetMessageInfo();
-
- if (g_cvarEnableStopSound.Get() && info->m_MessageId == GE_FireBulletsId)
- {
- if (g_playerManager->GetSilenceSoundMask())
- {
- // Post the silenced sound to those who use silencesound
- // Creating a new event object requires us to include the protobuf c files which I didn't feel like doing yet
- // So instead just edit the event in place and reset later
- auto msg = const_cast(pData)->ToPB();
-
- int32_t weapon_id = msg->weapon_id();
- int32_t sound_type = msg->sound_type();
- int32_t item_def_index = msg->item_def_index();
-
- // original weapon_id will override new settings if not removed
- msg->set_weapon_id(0);
- msg->set_sound_type(9);
- msg->set_item_def_index(61); // weapon_usp_silencer
-
- uint64 clientMask = *(uint64*)clients & g_playerManager->GetSilenceSoundMask();
-
- postEventAbstractHook.CallOriginal(pThis, nSlot, bLocalOnly, nClientCount, &clientMask, pEvent, msg, nSize, bufType);
-
- msg->set_weapon_id(weapon_id);
- msg->set_sound_type(sound_type);
- msg->set_item_def_index(item_def_index);
- }
-
- // Filter out people using stop/silence sound from the original event
- *(uint64*)clients &= ~g_playerManager->GetStopSoundMask();
- *(uint64*)clients &= ~g_playerManager->GetSilenceSoundMask();
- }
- else if (info->m_MessageId == GE_PlaceDecalEvent)
- {
- *(uint64*)clients &= ~g_playerManager->GetStopDecalsMask();
- }
- else if (info->m_MessageId == GE_Source1LegacyGameEvent)
- {
- if (g_cvarEnableLeader.Get())
- Leader_PostEventAbstract_Source1LegacyGameEvent(clients, pData);
- }
- else if (info->m_MessageId == UM_Shake)
- {
- auto pPBData = const_cast(pData)->ToPB();
- if (g_cvarMaxShakeAmp.Get() >= 0 && pPBData->amplitude() > g_cvarMaxShakeAmp.Get())
- pPBData->set_amplitude(g_cvarMaxShakeAmp.Get());
-
- // remove client with noshake from the event
- if (g_cvarEnableNoShake.Get())
- *(uint64*)clients &= ~g_playerManager->GetNoShakeMask();
- }
- else if (info->m_MessageId == GE_SosStartSoundEvent)
- {
- auto msg = const_cast(pData)->ToPB();
-
- if (g_cvarEnableZR.Get())
- ZR_PostEventAbstract_SosStartSoundEvent(clients, msg);
-
- if (g_cvarEnableStopSound.Get())
- {
- static std::set soundEventHashes;
-
- ExecuteOnce(
- soundEventHashes.insert(GetSoundEventHash("Weapon_sg556.ZoomIn"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_sg556.ZoomOut"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_AUG.ZoomIn"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_AUG.ZoomOut"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_SSG08.Zoom"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_SSG08.ZoomOut"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_SCAR20.Zoom"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_SCAR20.ZoomOut"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_G3SG1.Zoom"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_G3SG1.ZoomOut"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_AWP.Zoom"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_AWP.ZoomOut"));
- soundEventHashes.insert(GetSoundEventHash("Weapon_Revolver.Prepare"));
- soundEventHashes.insert(GetSoundEventHash("Weapon.AutoSemiAutoSwitch")););
-
- if (!soundEventHashes.contains(msg->soundevent_hash()))
- return {KHook::Action::Ignore};
-
- uint64 stopSoundMask = g_playerManager->GetStopSoundMask();
- uint64 silenceSoundMask = g_playerManager->GetSilenceSoundMask();
-
- if (!msg->has_source_entity_index())
- return {KHook::Action::Ignore};
-
- CBaseEntity* pSourceEntity = (CBaseEntity*)g_pEntitySystem->GetEntityInstance(CEntityIndex(msg->source_entity_index()));
- int playerSlot = -1;
-
- if (!pSourceEntity)
- return {KHook::Action::Ignore};
-
- if (pSourceEntity->IsPawn() && ((CCSPlayerPawn*)pSourceEntity)->GetController())
- {
- playerSlot = ((CCSPlayerPawn*)pSourceEntity)->GetController()->GetPlayerSlot();
- }
- else if (!V_strncasecmp(pSourceEntity->GetClassname(), "weapon_", 7))
- {
- CCSPlayerPawn* pPawn = (CCSPlayerPawn*)pSourceEntity->m_hOwnerEntity().Get();
-
- if (pPawn && pPawn->IsPawn() && pPawn->GetController())
- playerSlot = pPawn->GetController()->GetPlayerSlot();
- }
-
- // Remove player who triggered this sound from masks
- // Because some of these sounds never get played locally (Zoom's, Knife Hit/Stab)
- if (playerSlot != -1 && g_playerManager->IsPlayerUsingStopSound(playerSlot))
- stopSoundMask &= ~((uint64)1 << playerSlot);
-
- if (playerSlot != -1 && g_playerManager->IsPlayerUsingSilenceSound(playerSlot))
- silenceSoundMask &= ~((uint64)1 << playerSlot);
-
- // Filter out people using stop/silence sound from hearing this sound from other players
- *(uint64*)clients &= ~stopSoundMask;
- *(uint64*)clients &= ~silenceSoundMask;
- }
- }
- else if (info->m_MessageId == UM_ParticleManager)
- {
- // These messages were previously unused, but recently started being used for weapon particles in the AG2 update
- // Unfortunately, this new system seems extremely unoptimized for 64 players, and was causing severe performance issues & vector overflow client crashes
- if (g_cvarBlockParticleMsgs.Get())
- *(uint64*)clients = 0;
- }
-
- return {KHook::Action::Ignore};
-}
-
void CS2Fixes::AllPluginsLoaded()
{
/* This is where we'd do stuff that relies on the mod or other plugins
@@ -828,425 +293,6 @@ void CS2Fixes::AllPluginsLoaded()
Message("AllPluginsLoaded\n");
}
-KHook::Return CS2Fixes::Hook_ClientActive_Post(IServerGameClients* pThis, CPlayerSlot slot, bool bLoadGame, const char* pszName, uint64 xuid)
-{
- Message("Hook_ClientActive(%d, %d, \"%s\", %lli)\n", slot, bLoadGame, pszName, xuid);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_ClientCommand(IServerGameClients* pThis, CPlayerSlot slot, const CCommand& args)
-{
-#ifdef _DEBUG
- Message("Hook_ClientCommand(%d, \"%s\")\n", slot, args.GetCommandString());
-#endif
-
- if (g_cvarIdleKickTime.Get() > 0.0f)
- {
- ZEPlayer* pPlayer = g_playerManager->GetPlayer(slot);
-
- if (pPlayer)
- pPlayer->UpdateLastInputTime();
- }
-
- if (g_cvarVoteManagerEnable.Get() && V_stricmp(args[0], "endmatch_votenextmap") == 0 && args.ArgC() == 2)
- {
- if (g_pMapVoteSystem->RegisterPlayerVote(slot, atoi(args[1])))
- return {KHook::Action::Ignore};
- else
- return {KHook::Action::Supersede};
- }
-
- if (g_cvarEnableZR.Get() && slot != -1 && !V_strncmp(args.Arg(0), "jointeam", 8))
- {
- ZR_Hook_ClientCommand_JoinTeam(slot, args);
- return {KHook::Action::Supersede};
- }
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_ClientSettingsChanged(IServerGameClients* pThis, CPlayerSlot slot)
-{
-#ifdef _DEBUG
- Message("Hook_ClientSettingsChanged(%d)\n", slot);
-#endif
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_OnClientConnected(IServerGameClients* pThis, CPlayerSlot slot, const char* pszName, uint64 xuid, const char* pszNetworkID, const char* pszAddress, bool bFakePlayer)
-{
- Message("Hook_OnClientConnected(%d, \"%s\", %lli, \"%s\", \"%s\", %d)\n", slot, pszName, xuid, pszNetworkID, pszAddress, bFakePlayer);
-
- static ConVarRefAbstract tv_name("tv_name");
- const char* pszTvName = tv_name.GetString().Get();
-
- // Ideally we would use CServerSideClient::IsHLTV().. but it doesn't work :(
- if (bFakePlayer && V_strcmp(pszName, pszTvName))
- g_playerManager->OnBotConnected(slot);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_ClientConnect(IServerGameClients* pThis, CPlayerSlot slot, const char* pszName, uint64 xuid, const char* pszNetworkID, bool unk1, CBufferString* pRejectReason)
-{
- Message("Hook_ClientConnect(%d, \"%s\", %lli, \"%s\", %d, \"%s\")\n", slot, pszName, xuid, pszNetworkID, unk1, pRejectReason->Get());
-
- // Player is banned
- if (!g_playerManager->OnClientConnected(slot, xuid, pszNetworkID))
- return {KHook::Action::Supersede, false};
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_ClientPutInServer_Post(IServerGameClients* pThis, CPlayerSlot slot, char const* pszName, int type, uint64 xuid)
-{
- Message("Hook_ClientPutInServer(%d, \"%s\", %d, %d, %lli)\n", slot, pszName, type, xuid);
-
- if (!g_playerManager->GetPlayer(slot))
- return {KHook::Action::Ignore};
-
- g_playerManager->OnClientPutInServer(slot);
-
- if (g_cvarEnableZR.Get())
- ZR_Hook_ClientPutInServer(slot, pszName, type, xuid);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_ClientDisconnect_Post(IServerGameClients* pThis, CPlayerSlot slot, ENetworkDisconnectionReason reason, const char* pszName, uint64 xuid, const char* pszNetworkID)
-{
- Message("Hook_ClientDisconnect(%d, %d, \"%s\", %lli)\n", slot, reason, pszName, xuid);
-
- CCSPlayerController* player = CCSPlayerController::FromSlot(slot);
-
- if (g_cvarEnableZR.Get())
- {
- // Controller team num is not valid post-disconnect, so just check both teams
- if (!ZR_CheckTeamWinConditions(CS_TEAM_T))
- ZR_CheckTeamWinConditions(CS_TEAM_CT);
- }
-
- ZEPlayer* pPlayer = g_playerManager->GetPlayer(slot);
-
- if (!pPlayer)
- return {KHook::Action::Ignore};
-
- // Dont add to c_listdc clients that are downloading MultiAddonManager stuff or were present during a map change
- if (reason != NETWORK_DISCONNECT_LOOPSHUTDOWN && reason != NETWORK_DISCONNECT_SHUTDOWN)
- g_pAdminSystem->AddDisconnectedPlayer(pszName, xuid, pPlayer ? pPlayer->GetIpAddress() : "");
-
- g_playerManager->OnClientDisconnect(slot);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_GameFrame_Post(IServerGameDLL* pThis, bool simulating, bool bFirstTick, bool bLastTick)
-{
- /**
- * simulating:
- * ***********
- * true | game is ticking
- * false | game is not ticking
- */
-
- VPROF_BUDGET("CS2Fixes::Hook_GameFramePost", "CS2FixesPerFrame");
-
- if (!GetGlobals())
- return {KHook::Action::Ignore};
-
- if (simulating && g_bHasTicked)
- g_flUniversalTime += GetGlobals()->curtime - g_flLastTickedTime;
-
- g_flLastTickedTime = GetGlobals()->curtime;
- g_bHasTicked = true;
-
- RunTimers();
- EntityHandler_OnGameFramePost(simulating, GetGlobals()->tickcount);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_CheckTransmit_Post(ISource2GameEntities* pThis, CCheckTransmitInfo** ppInfoList, int infoCount, CBitVec<16384>& unionTransmitEdicts,
- CBitVec<16384>&, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntities)
-{
- if (!g_pEntitySystem || !GetGlobals())
- return {KHook::Action::Ignore};
-
- VPROF("CS2Fixes::Hook_CheckTransmit");
-
- for (int i = 0; i < infoCount; i++)
- {
- auto& pInfo = ppInfoList[i];
-
- // the offset happens to have a player index here,
- // though this is probably part of the client class that contains the CCheckTransmitInfo
- static int offset = g_GameConfig->GetOffset("CheckTransmitPlayerSlot");
- int iPlayerSlot = (int)*((uint8*)pInfo + offset);
-
- CCSPlayerController* pSelfController = CCSPlayerController::FromSlot(iPlayerSlot);
-
- if (!pSelfController || !pSelfController->IsConnected())
- continue;
-
- auto pSelfZEPlayer = g_playerManager->GetPlayer(iPlayerSlot);
-
- if (!pSelfZEPlayer)
- continue;
-
- for (int j = 0; j < GetGlobals()->maxClients; j++)
- {
- CCSPlayerController* pController = CCSPlayerController::FromSlot(j);
- // Always transmit to themselves
- if (!pController || pController->m_bIsHLTV || j == iPlayerSlot)
- continue;
-
- // Don't transmit other players' flashlights
- CBarnLight* pFlashLight = pController->IsConnected() ? g_playerManager->GetPlayer(j)->GetFlashLight() : nullptr;
-
- if (!g_cvarFlashLightTransmitOthers.Get() && pFlashLight)
- pInfo->m_pTransmitEntity->Clear(pFlashLight->entindex());
-
- if (g_cvarEnableEntWatch.Get() && g_pEWHandler->IsConfigLoaded())
- {
- // Don't transmit other players' entwatch hud
- CPointWorldText* pHud = pController->IsConnected() ? g_playerManager->GetPlayer(j)->GetEntwatchHud() : nullptr;
- if (pHud)
- pInfo->m_pTransmitEntity->Clear(pHud->entindex());
- }
-
- // Always transmit other players if spectating
- if (!g_cvarEnableHide.Get() || pSelfController->GetPawnState() == STATE_OBSERVER_MODE)
- continue;
-
- // Get the actual pawn as the player could be currently spectating
- CCSPlayerPawn* pPawn = pController->GetPlayerPawn();
-
- if (!pPawn)
- continue;
-
- // Do not hide leaders or item holders to other players
- ZEPlayer* pOtherZEPlayer = g_playerManager->GetPlayer(j);
- if (pSelfZEPlayer->ShouldBlockTransmit(j) && pOtherZEPlayer && !pOtherZEPlayer->IsLeader() && g_pEWHandler->FindItemInstanceByOwner(j, false, 0) == -1)
- {
- pInfo->m_pTransmitEntity->Clear(pPawn->entindex());
-
- if (g_cvarHideWeapons.Get())
- {
- auto pVecWeapons = pPawn->m_pWeaponServices->m_hMyWeapons();
-
- FOR_EACH_VEC(*pVecWeapons, i)
- {
- auto pWeapon = (*pVecWeapons)[i].Get();
-
- if (pWeapon)
- pInfo->m_pTransmitEntity->Clear(pWeapon->entindex());
- }
- }
- }
- }
-
- // Don't transmit glow model to it's owner
- CBaseModelEntity* pGlowModel = pSelfZEPlayer->GetGlowModel();
-
- if (pGlowModel)
- pInfo->m_pTransmitEntity->Clear(pGlowModel->entindex());
- }
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_ApplyGameSettings(IServerGameDLL* pThis, KeyValues* pKV)
-{
- g_pMapVoteSystem->ApplyGameSettings(pKV);
- g_pMapMigrations->ApplyGameSettings(pKV);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_CreateWorkshopMapGroup(IGameTypes* pThis, const char* name, const CUtlStringList& mapList)
-{
- if (g_cvarVoteManagerEnable.Get() && g_pMapVoteSystem->IsMapListLoaded())
- return KHook::Recall(nullptr, {KHook::Action::Ignore}, pThis, name, g_pMapVoteSystem->CreateWorkshopMapGroup());
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarDropMapWeapons("cs2f_drop_map_weapons", FCVAR_NONE, "Whether to force drop map-spawned weapons on death", false);
-
-KHook::Return CS2Fixes::Hook_OnTakeDamage_Alive(CCSPlayerPawn* pPawn, CTakeDamageResult* pDamageResult)
-{
- if (g_cvarEnableZR.Get() && ZR_Hook_OnTakeDamage_Alive(pDamageResult->m_pOriginatingInfo, pPawn))
- {
- pDamageResult->m_bWasDamageSuppressed = true;
- pDamageResult->m_flDamageDealt = 0.0f;
- return {KHook::Action::Supersede, false};
- }
-
- // This is a shit place to be doing this, but player_death event is too late and there is no pre-hook alternative
- // Check if this is going to kill the player
- if (g_cvarDropMapWeapons.Get() && pPawn && pPawn->m_iHealth() <= 0)
- {
- if (g_cvarEnableEntWatch.Get())
- {
- CCSPlayerController* pController = pPawn->GetOriginalController();
- if (pController)
- EW_PlayerDeathPre(pController);
- }
-
- pPawn->DropMapWeapons();
- }
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarFixPhysicsPlayerShuffle("cs2f_shuffle_player_physics_sim", FCVAR_NONE, "Whether to enable shuffle player list in physics simulate", false);
-
-struct TouchLinked_t
-{
- uint32_t TouchFlags;
-
-private:
- uint8_t padding_0[20];
-
-public:
- CBaseHandle SourceHandle;
- CBaseHandle TargetHandle;
-
-private:
- uint8_t padding_1[224];
-
-public:
- [[nodiscard]] bool IsUnTouching() const
- {
- return !!(TouchFlags & 0x10);
- }
-
- [[nodiscard]] bool IsTouching() const
- {
- return (!!(TouchFlags & 4)) || (!!(TouchFlags & 8));
- }
-};
-static_assert(sizeof(TouchLinked_t) == 256, "Touch_t size mismatch");
-KHook::Return CS2Fixes::Hook_GetTouchingList_Post(CVPhys2World* pThis, CUtlVector* pList, bool unknown)
-{
- if (!g_cvarFixPhysicsPlayerShuffle.Get() || pList->Count() <= 1)
- return {KHook::Action::Ignore};
-
- // [Kxnrl]
- // seems it sorted by flags?
-
- if (GetGlobals())
- std::srand(GetGlobals()->tickcount);
-
- // Fisher-Yates shuffle
-
- std::vector touchingLinks;
- std::vector unTouchLinks;
-
- FOR_EACH_VEC(*pList, i)
- {
- const auto& link = pList->Element(i);
- if (link.IsUnTouching())
- unTouchLinks.push_back(link);
- else
- touchingLinks.push_back(link);
- }
-
- if (touchingLinks.size() <= 1)
- return {KHook::Action::Ignore};
-
- for (size_t i = touchingLinks.size() - 1; i > 0; --i)
- {
- const auto j = std::rand() % (i + 1);
- std::swap(touchingLinks[i], touchingLinks[j]);
- }
-
- pList->Purge();
-
- for (const auto& link : touchingLinks)
- pList->AddToTail(link);
- for (const auto& link : unTouchLinks)
- pList->AddToTail(link);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_CheckMovingGround(CCSPlayer_MovementServices* pThis, double frametime)
-{
- CCSPlayerPawn* pPawn = pThis->GetPawn();
-
- if (!pPawn || !GetGlobals())
- return {KHook::Action::Ignore};
-
- CCSPlayerController* pController = pPawn->GetOriginalController();
-
- if (!pController)
- return {KHook::Action::Ignore};
-
- int iSlot = pController->GetPlayerSlot();
-
- static int aPlayerTicks[MAXPLAYERS] = {0};
-
- // The point of doing this is to avoid running the function (and applying/resetting basevelocity) multiple times per tick
- // This can happen when the client or server lags
- if (aPlayerTicks[iSlot] == GetGlobals()->tickcount)
- return {KHook::Action::Supersede};
-
- aPlayerTicks[iSlot] = GetGlobals()->tickcount;
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_DropWeapon_Post(CCSPlayer_WeaponServices* pThis, CBasePlayerWeapon* pWeapon, Vector* pVecTarget, Vector* pVelocity)
-{
- if (g_cvarEnableEntWatch.Get())
- EW_DropWeapon(pThis, pWeapon);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_LoadEventsFromFile(IGameEventManager2* pThis, const char* filename, bool bSearchAll)
-{
- ExecuteOnce(g_gameEventManager = pThis);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_SetGameSpawnGroupMgr(INetworkGameServer* pThis, IGameSpawnGroupMgr* pSpawnGroupMgr)
-{
- // This also resets our stored pointer on deletion, since null gets passed into this function, nice!
- g_pSpawnGroupMgr = (CSpawnGroupMgrGameSystem*)pSpawnGroupMgr;
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_Spawn_Post(CEntitySystem* pThis, int nCount, const EntitySpawnInfo_t* pInfo)
-{
- for (int i = 0; i < nCount; i++)
- g_pMapMigrations->OnEntitySpawned(pInfo[i].m_pEntity->m_pInstance, pInfo[i].m_pKeyValues);
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return CS2Fixes::Hook_CCSPlayerPawn_Teleport(CCSPlayerPawn* pPawn, const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity)
-{
- if (!pAngles)
- return {KHook::Action::Ignore};
-
- QAngle* pCastAngles = const_cast(pAngles);
-
- // Post-AG2, changing x or z angles on a playermodel will bug out, and never did anything pre-AG2 anyways
- if (pCastAngles->x != 0.0f)
- pCastAngles->x = 0.0f;
-
- if (pCastAngles->z != 0.0f)
- pCastAngles->z = 0.0f;
-
- return {KHook::Action::Ignore};
-}
-
void* CS2Fixes::OnMetamodQuery(const char* iface, int* ret)
{
if (V_strcmp(iface, CS2FIXES_INTERFACE))
diff --git a/src/cs2fixes.h b/src/cs2fixes.h
index 643abc2c..68de77d8 100644
--- a/src/cs2fixes.h
+++ b/src/cs2fixes.h
@@ -37,18 +37,6 @@
#include "version_gen_placeholder.h"
#endif
-class CCSPlayer_MovementServices;
-class CServerSideClient;
-struct TouchLinked_t;
-class CCSPlayer_WeaponServices;
-class CBasePlayerWeapon;
-class IGameEventSystem;
-class CGamePlayerEquip;
-class CCSGameRules;
-class CCSPlayerPawn;
-class CVPhys2World;
-class CTriggerGravity;
-
extern IGameEventSystem* g_gameEventSystem;
extern IGameEventManager2* g_gameEventManager;
extern CGameEntitySystem* g_pEntitySystem;
@@ -77,37 +65,6 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener, public ICS2Fixes
bool background);
void OnLevelShutdown();
-public: // hooks
- KHook::Return Hook_GameFrame_Post(IServerGameDLL* pThis, bool simulating, bool bFirstTick, bool bLastTick);
- KHook::Return Hook_GameServerSteamAPIActivated(IServerGameDLL* pThis);
- KHook::Return Hook_ApplyGameSettings(IServerGameDLL* pThis, KeyValues* pKV);
- KHook::Return Hook_ClientActive_Post(IServerGameClients* pThis, CPlayerSlot slot, bool bLoadGame, const char* pszName, uint64 xuid);
- KHook::Return Hook_ClientDisconnect_Post(IServerGameClients* pThis, CPlayerSlot slot, ENetworkDisconnectionReason reason, const char* pszName, uint64 xuid, const char* pszNetworkID);
- KHook::Return Hook_ClientPutInServer_Post(IServerGameClients* pThis, CPlayerSlot slot, char const* pszName, int type, uint64 xuid);
- KHook::Return Hook_ClientSettingsChanged(IServerGameClients* pThis, CPlayerSlot slot);
- KHook::Return Hook_OnClientConnected(IServerGameClients* pThis, CPlayerSlot slot, const char* pszName, uint64 xuid, const char* pszNetworkID, const char* pszAddress, bool bFakePlayer);
- KHook::Return Hook_ClientConnect(IServerGameClients* pThis, CPlayerSlot slot, const char* pszName, uint64 xuid, const char* pszNetworkID, bool unk1, CBufferString* pRejectReason);
- KHook::Return Hook_ClientCommand(IServerGameClients* pThis, CPlayerSlot nSlot, const CCommand& _cmd);
- KHook::Return Hook_PostEventAbstract(IGameEventSystem* pThis, CSplitScreenSlot nSlot, bool bLocalOnly, int nClientCount, const uint64* clients,
- INetworkMessageInternal* pEvent, const CNetMessage* pData, unsigned long nSize, NetChannelBufType_t bufType);
- KHook::Return Hook_StartupServer_Post(INetworkServerService* pThis, const GameSessionConfiguration_t& config, ISource2WorldSession*, const char*);
- KHook::Return Hook_CheckTransmit_Post(ISource2GameEntities* pThis, CCheckTransmitInfo** ppInfoList, int infoCount, CBitVec<16384>& unionTransmitEdicts,
- CBitVec<16384>&, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntities);
- KHook::Return Hook_DispatchConCommand(ICvar* pThis, ConCommandRef cmd, const CCommandContext& ctx, const CCommand& args);
- KHook::Return Hook_LoadEventsFromFile(IGameEventManager2* pThis, const char* filename, bool bSearchAll);
- KHook::Return Hook_Spawn_Post(CEntitySystem* pThis, int nCount, const EntitySpawnInfo_t* pInfo);
- KHook::Return Hook_SetGameSpawnGroupMgr(INetworkGameServer* pThis, IGameSpawnGroupMgr* pSpawnGroupMgr);
- KHook::Return Hook_CreateWorkshopMapGroup(IGameTypes* pThis, const char* name, const CUtlStringList& mapList);
- KHook::Return Hook_GetTouchingList_Post(CVPhys2World* pThis, CUtlVector* pList, bool unknown);
- KHook::Return Hook_CheckMovingGround(CCSPlayer_MovementServices* pThis, double frametime);
- KHook::Return Hook_DropWeapon_Post(CCSPlayer_WeaponServices* pThis, CBasePlayerWeapon* pWeapon, Vector* pVecTarget, Vector* pVelocity);
- KHook::Return Hook_PlayerEquipUse(CGamePlayerEquip* pThis, class InputData_t*);
- KHook::Return Hook_PlayerEquipPrecache_Post(CGamePlayerEquip* pThis, CEntityPrecacheContext*);
- KHook::Return Hook_TriggerGravityPrecache_Post(CTriggerGravity* pThis, CEntityPrecacheContext* param);
- KHook::Return Hook_TriggerGravityEndTouch_Post(CTriggerGravity* pThis, CBaseEntity* pOther);
- KHook::Return Hook_OnTakeDamage_Alive(CCSPlayerPawn* pPawn, CTakeDamageResult* pDamageResult);
- KHook::Return Hook_CCSPlayerPawn_Teleport(CCSPlayerPawn* pPawn, const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity);
-
public: // MetaMod API
void* OnMetamodQuery(const char* iface, int* ret);
std::uint64_t GetAdminFlags(std::uint64_t iSteam64ID) const override;
diff --git a/src/detours.cpp b/src/detours.cpp
deleted file mode 100644
index a93beac3..00000000
--- a/src/detours.cpp
+++ /dev/null
@@ -1,923 +0,0 @@
-/**
- * =============================================================================
- * CS2Fixes
- * Copyright (C) 2023-2026 Source2ZE
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- */
-
-#include "cs_usercmd.pb.h"
-#include "networkbasetypes.pb.h"
-#include "usercmd.pb.h"
-
-#include "addresses.h"
-#include "buttonwatch.h"
-#include "commands.h"
-#include "common.h"
-#include "ctimer.h"
-#include "customio.h"
-#include "detours.h"
-#include "entities.h"
-#include "entity/cbasemodelentity.h"
-#include "entity/ccsplayercontroller.h"
-#include "entity/ccsplayerpawn.h"
-#include "entity/ccsweaponbase.h"
-#include "entity/cenvhudhint.h"
-#include "entity/cgamerules.h"
-#include "entity/cpointviewcontrol.h"
-#include "entity/ctakedamageinfo.h"
-#include "entity/ctriggerpush.h"
-#include "entity/services.h"
-#include "entwatch.h"
-#include "gameconfig.h"
-#include "igameevents.h"
-#include "irecipientfilter.h"
-#include "map_votes.h"
-#include "mapmigrations.h"
-#include "module.h"
-#include "networksystem/inetworkserializer.h"
-#include "playermanager.h"
-#include "serversideclient.h"
-#include "tier0/vprof.h"
-#include "votemanager.h"
-#include "zombiereborn.h"
-
-#include "tier0/memdbgon.h"
-
-KHook::Member takeDamageOldHook(Detour_CBaseEntity_TakeDamageOld, Detour_CBaseEntity_TakeDamageOld_Post);
-KHook::Member triggerPushTouchHook(Detour_TriggerPush_Touch, nullptr);
-KHook::Function isHearingClientHook(Detour_IsHearingClient, nullptr);
-KHook::Function sayTextFilterHook(Detour_UTIL_SayTextFilter, nullptr);
-KHook::Function sayText2FilterHook(Detour_UTIL_SayText2Filter, nullptr);
-KHook::Member canUseHook(Detour_CCSPlayer_WeaponServices_CanUse, nullptr);
-KHook::Member equipWeaponHook(Detour_CCSPlayer_WeaponServices_EquipWeapon, nullptr);
-KHook::Member acceptInputHook(Detour_CEntityIdentity_AcceptInput, nullptr);
-KHook::Member getNearestNavAreaHook(Detour_CNavMesh_GetNearestNavArea, nullptr);
-KHook::Member processMovementHook(Detour_ProcessMovement, Detour_ProcessMovement_Post);
-KHook::Member processUsercmdsHook(Detour_ProcessUsercmds, nullptr);
-KHook::Member inputTriggerForAllPlayersHook(Detour_CGamePlayerEquip_InputTriggerForAllPlayers, nullptr);
-KHook::Member inputTriggerForActivatedPlayerHook(Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer, nullptr);
-KHook::Member gravityTouchHook(Detour_CTriggerGravity_GravityTouch, nullptr);
-KHook::Function getFreeClientHook(Detour_GetFreeClient, nullptr);
-KHook::Member getMaxSpeedHook(Detour_CCSPlayerPawn_GetMaxSpeed, nullptr);
-KHook::Member findUseEntityHook(Detour_FindUseEntity, Detour_FindUseEntity_Post);
-KHook::Function traceFuncHook(Detour_TraceFunc, nullptr);
-KHook::Function traceShapeHook(Detour_TraceShape, nullptr);
-KHook::Member fireOutputInternalHook(Detour_CEntityIOOutput_FireOutputInternal, nullptr);
-#ifdef PLATFORM_WINDOWS
-KHook::Member getEyePositionHook(Detour_CBasePlayerPawn_GetEyePosition, nullptr);
-KHook::Member getEyeAnglesHook(Detour_CBasePlayerPawn_GetEyeAngles, nullptr);
-#else
-KHook::Member getEyePositionHook(Detour_CBasePlayerPawn_GetEyePosition, nullptr);
-KHook::Member getEyeAnglesHook(Detour_CBasePlayerPawn_GetEyeAngles, nullptr);
-#endif
-KHook::Member inputTestActivatorHook(Detour_CBaseFilter_InputTestActivator, nullptr);
-KHook::Function checkSteamBanHook(nullptr, Detour_GameSystem_Think_CheckSteamBan_Post);
-KHook::Member canAcquireHook(Detour_CCSPlayer_ItemServices_CanAcquire, nullptr);
-KHook::Function scriptSetModelHook(Detour_CS_Script_SetModel, Detour_CS_Script_SetModel_Post);
-KHook::Member setModelHook(Detour_CBaseModelEntity_SetModel, nullptr);
-KHook::Member goToIntermissionHook(Detour_CCSGameRules_GoToIntermission, nullptr);
-
-template
-void SetupDetour(CGameConfig* gameConfig, KHook::Function& hook, const char* name)
-{
- auto pfnFunc = reinterpret_cast(gameConfig->ResolveSignature(name));
-
- if (!pfnFunc)
- {
- g_bRequiredInitLoaded = false;
- return;
- }
-
- hook.Configure(pfnFunc);
- Message("Detoured %s at 0x%p\n", name, pfnFunc);
-}
-
-template
-void SetupDetour(CGameConfig* gameConfig, KHook::Member& hook, const char* name)
-{
- void* pfnFunc = gameConfig->ResolveSignature(name);
-
- if (!pfnFunc)
- {
- g_bRequiredInitLoaded = false;
- return;
- }
-
- hook.Configure(pfnFunc);
- Message("Detoured %s at 0x%p\n", name, pfnFunc);
-}
-
-void InitDetours(CGameConfig* gameConfig)
-{
- SetupDetour(gameConfig, takeDamageOldHook, "CBaseEntity_TakeDamageOld");
- SetupDetour(gameConfig, triggerPushTouchHook, "TriggerPush_Touch");
- SetupDetour(gameConfig, isHearingClientHook, "IsHearingClient");
- SetupDetour(gameConfig, sayTextFilterHook, "UTIL_SayTextFilter");
- SetupDetour(gameConfig, sayText2FilterHook, "UTIL_SayText2Filter");
- SetupDetour(gameConfig, canUseHook, "CCSPlayer_WeaponServices_CanUse");
- SetupDetour(gameConfig, equipWeaponHook, "CCSPlayer_WeaponServices_EquipWeapon");
- SetupDetour(gameConfig, acceptInputHook, "CEntityIdentity_AcceptInput");
- SetupDetour(gameConfig, getNearestNavAreaHook, "CNavMesh_GetNearestNavArea");
- SetupDetour(gameConfig, processMovementHook, "ProcessMovement");
- SetupDetour(gameConfig, processUsercmdsHook, "ProcessUsercmds");
- SetupDetour(gameConfig, inputTriggerForAllPlayersHook, "CGamePlayerEquip_InputTriggerForAllPlayers");
- SetupDetour(gameConfig, inputTriggerForActivatedPlayerHook, "CGamePlayerEquip_InputTriggerForActivatedPlayer");
- SetupDetour(gameConfig, gravityTouchHook, "CTriggerGravity_GravityTouch");
- SetupDetour(gameConfig, getFreeClientHook, "GetFreeClient");
-#ifdef __linux__
- // Inlined by MSVC as of 2025-07-28 CS2 update
- // TODO: Find some alternative that supports Windows
- SetupDetour(gameConfig, getMaxSpeedHook, "CCSPlayerPawn_GetMaxSpeed");
-#endif
- SetupDetour(gameConfig, findUseEntityHook, "FindUseEntity");
- SetupDetour(gameConfig, traceFuncHook, "TraceFunc");
- SetupDetour(gameConfig, traceShapeHook, "TraceShape");
- SetupDetour(gameConfig, fireOutputInternalHook, "CEntityIOOutput_FireOutputInternal");
- SetupDetour(gameConfig, getEyePositionHook, "CBasePlayerPawn_GetEyePosition");
- SetupDetour(gameConfig, getEyeAnglesHook, "CBasePlayerPawn_GetEyeAngles");
- SetupDetour(gameConfig, inputTestActivatorHook, "CBaseFilter_InputTestActivator");
- SetupDetour(gameConfig, checkSteamBanHook, "GameSystem_Think_CheckSteamBan");
- SetupDetour(gameConfig, canAcquireHook, "CCSPlayer_ItemServices_CanAcquire");
- SetupDetour(gameConfig, scriptSetModelHook, "CS_Script_SetModel");
- SetupDetour(gameConfig, setModelHook, "CBaseModelEntity_SetModel");
- SetupDetour(gameConfig, goToIntermissionHook, "CCSGameRules_GoToIntermission");
-}
-
-CConVar g_cvarBlockMolotovSelfDmg("cs2f_block_molotov_self_dmg", FCVAR_NONE, "Whether to block self-damage from molotovs", false);
-CConVar g_cvarBlockAllDamage("cs2f_block_all_dmg", FCVAR_NONE, "Whether to block all damage to players", false);
-CConVar g_cvarFixBlockDamage("cs2f_fix_block_dmg", FCVAR_NONE, "Whether to fix block-damage on players", false);
-
-KHook::Return Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult)
-{
- // NOTE valve always return 1 here, since 2025/10/15 update.
-
-#ifdef _DEBUG
- Message("\n--------------------------------\n"
- "TakeDamage on %s\n"
- "Attacker: %s\n"
- "Inflictor: %s\n"
- "Ability: %s\n"
- "Damage: %.2f\n"
- "Damage Type: %i\n"
- "--------------------------------\n",
- pThis->GetClassname(),
- pInfo->m_hAttacker.Get() ? pInfo->m_hAttacker.Get()->GetClassname() : "NULL",
- pInfo->m_hInflictor.Get() ? pInfo->m_hInflictor.Get()->GetClassname() : "NULL",
- pInfo->m_hAbility.Get() ? pInfo->m_hAbility.Get()->GetClassname() : "NULL",
- pInfo->m_flDamage,
- pInfo->m_bitsDamageType);
-#endif
-
- // Block all player damage if desired
- if (g_cvarBlockAllDamage.Get() && pThis->IsPawn())
- return {KHook::Action::Supersede, 1};
-
- CEntityInstance* pInflictor = pInfo->m_hInflictor.Get();
- const char* pszInflictorClass = pInflictor ? pInflictor->GetClassname() : "";
-
- // After Armory update, activator became attacker on block damage, which broke it..
- if (g_cvarFixBlockDamage.Get() && pInfo->m_AttackerInfo.m_bIsPawn && pInfo->m_bitsDamageType ^ DMG_BULLET && pInfo->m_hAttacker != pThis->GetHandle())
- {
- if (V_strcasecmp(pszInflictorClass, "func_movelinear") == 0
- || V_strcasecmp(pszInflictorClass, "func_mover") == 0
- || V_strcasecmp(pszInflictorClass, "func_door") == 0
- || V_strcasecmp(pszInflictorClass, "func_door_rotating") == 0
- || V_strcasecmp(pszInflictorClass, "func_rotating") == 0
- || V_strcasecmp(pszInflictorClass, "point_hurt") == 0)
- {
- pInfo->m_AttackerInfo.m_bIsPawn = false;
- pInfo->m_AttackerInfo.m_bIsWorld = true;
- pInfo->m_hAttacker = pInfo->m_hInflictor;
-
- pInfo->m_AttackerInfo.m_hAttackerPawn = CHandle(~0u);
- pInfo->m_AttackerInfo.m_nAttackerPlayerSlot = ~0;
- }
- }
-
- // Prevent molly on self
- if (g_cvarBlockMolotovSelfDmg.Get() && pInfo->m_hAttacker == pThis && !V_strncmp(pszInflictorClass, "inferno", 7))
- return {KHook::Action::Supersede, 1};
-
- // Fix disconnected players grenades being able to damage teammates
- if (!V_strcasecmp(pszInflictorClass, "hegrenade_projectile") && pInfo->m_AttackerInfo.m_bIsPawn && pInfo->m_AttackerInfo.m_nTeam == 0)
- return {KHook::Action::Supersede, 1};
-
- // maybe call in flow
- CTakeDamageResult damageResult(0);
-
- if (pResult == nullptr)
- {
- damageResult.CopyFrom(pInfo);
- return KHook::Recall(nullptr, {KHook::Action::Ignore}, pThis, pInfo, &damageResult);
- }
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_CBaseEntity_TakeDamageOld_Post(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult)
-{
- if (pResult->m_flDamageDealt > 0.0f && !pResult->m_bWasDamageSuppressed && g_cvarEnableZR.Get() && pThis->IsPawn())
- ZR_OnPlayerTakeDamage(reinterpret_cast(pThis), pInfo, pResult->m_flDamageDealt);
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarUseOldPush("cs2f_use_old_push", FCVAR_NONE, "Whether to use the old CSGO trigger_push behavior", false);
-CConVar g_cvarLogPushes("cs2f_log_pushes", FCVAR_NONE, "Whether to log pushes (cs2f_use_old_push must be enabled)", false);
-
-KHook::Return Detour_TriggerPush_Touch(CTriggerPush* pPush, CBaseEntity* pOther)
-{
- // This trigger pushes only once (and kills itself) or pushes only on StartTouch, both of which are fine already
- if (!g_cvarUseOldPush.Get() || pPush->m_spawnflags() & SF_TRIG_PUSH_ONCE || pPush->m_bTriggerOnStartTouch())
- return {KHook::Action::Ignore};
-
- MoveType_t movetype = pOther->m_nActualMoveType();
-
- // VPhysics handling doesn't need any changes
- if (movetype == MOVETYPE_VPHYSICS)
- return {KHook::Action::Ignore};
-
- if (movetype == MOVETYPE_NONE || movetype == MOVETYPE_PUSH || movetype == MOVETYPE_NOCLIP)
- return {KHook::Action::Supersede};
-
- CCollisionProperty* collisionProp = pOther->m_pCollision();
- if (!IsSolid(collisionProp->m_nSolidType(), collisionProp->m_usSolidFlags()))
- return {KHook::Action::Supersede};
-
- if (!pPush->PassesTriggerFilters(pOther))
- return {KHook::Action::Supersede};
-
- if (pOther->m_CBodyComponent()->m_pSceneNode()->m_pParent())
- return {KHook::Action::Supersede};
-
- Vector vecAbsDir;
- matrix3x4_t matTransform = pPush->m_CBodyComponent()->m_pSceneNode()->EntityToWorldTransform();
-
- Vector vecPushDir = pPush->m_vecPushDirEntitySpace();
- VectorRotate(vecPushDir, matTransform, vecAbsDir);
-
- Vector vecPush = vecAbsDir * pPush->m_flSpeed();
-
- uint32 flags = pOther->m_fFlags();
-
- if (flags & (1 << 23)) // TODO: is FL_BASEVELOCITY really gone?
- vecPush = vecPush + pOther->m_vecBaseVelocity();
-
- if (vecPush.z > 0 && (flags & FL_ONGROUND))
- {
- pOther->SetGroundEntity(nullptr);
- Vector origin = pOther->GetAbsOrigin();
- origin.z += 1.0f;
-
- pOther->Teleport(&origin, nullptr, nullptr);
- }
-
- if (g_cvarLogPushes.Get() && GetGlobals())
- {
- Vector vecEntBaseVelocity = pOther->m_vecBaseVelocity;
- Vector vecOrigPush = vecAbsDir * pPush->m_flSpeed();
-
- Message("Pushing entity %i | frame = %i | tick = %i | entity basevelocity %s = %.2f %.2f %.2f | original push velocity = %.2f %.2f %.2f | final push velocity = %.2f %.2f %.2f\n",
- pOther->GetEntityIndex(),
- GetGlobals()->framecount,
- GetGlobals()->tickcount,
- (flags & (1 << 23)) ? "WITH FLAG" : "",
- vecEntBaseVelocity.x, vecEntBaseVelocity.y, vecEntBaseVelocity.z,
- vecOrigPush.x, vecOrigPush.y, vecOrigPush.z,
- vecPush.x, vecPush.y, vecPush.z);
- }
-
- pOther->m_vecBaseVelocity(vecPush);
-
- flags |= (1 << 23); // TODO: is FL_BASEVELOCITY really gone?
- pOther->m_fFlags(flags);
-
- return {KHook::Action::Supersede};
-}
-
-KHook::Return Detour_IsHearingClient(void* serverClient, int index)
-{
- ZEPlayer* player = g_playerManager->GetPlayer(index);
- if (player && player->IsMuted())
- return {KHook::Action::Supersede, false};
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return SayChatMessageWithTimer(IRecipientFilter& filter, const char* pText, CCSPlayerController* pPlayer, uint64 eMessageType)
-{
- VPROF("SayChatMessageWithTimer");
-
- char buf[256];
-
- // Filter console message - remove non-alphanumeric chars and convert to lowercase
- uint32 uiTextLength = strlen(pText);
- uint32 uiFilteredTextLength = 0;
- char filteredText[256];
-
- for (uint32 i = 0; i < uiTextLength; i++)
- {
- if (pText[i] >= 'A' && pText[i] <= 'Z')
- filteredText[uiFilteredTextLength++] = pText[i] + 32;
- if (pText[i] == ' ' || (pText[i] >= '0' && pText[i] <= '9') || (pText[i] >= 'a' && pText[i] <= 'z'))
- filteredText[uiFilteredTextLength++] = pText[i];
- }
- filteredText[uiFilteredTextLength] = '\0';
-
- // Split console message into words seperated by the space character
- CSplitString words(filteredText, " ");
-
- // Word count includes the first word "Console:" at index 0, first relevant word is at index 1
- int iWordCount = words.Count();
- uint32 uiTriggerTimerLength = 0;
-
- if (iWordCount == 2)
- uiTriggerTimerLength = V_StringToUint32(words.Element(1), 0, NULL, NULL, PARSING_FLAG_SKIP_WARNING);
-
- for (int i = 1; i < iWordCount && uiTriggerTimerLength == 0; i++)
- {
- uint32 uiCurrentValue = V_StringToUint32(words.Element(i), 0, NULL, NULL, PARSING_FLAG_SKIP_WARNING);
- uint32 uiNextWordLength = 0;
- char* pNextWord = NULL;
-
- if (i + 1 < iWordCount)
- {
- pNextWord = words.Element(i + 1);
- uiNextWordLength = strlen(pNextWord);
- }
-
- // Case: ... X sec(onds) ... or ... X s ... or ... X min(utes) ...
- if (pNextWord != NULL && uiCurrentValue > 0)
- {
- if (uiNextWordLength == 1)
- {
- if (pNextWord[0] == 's')
- uiTriggerTimerLength = uiCurrentValue;
- }
- else if (uiNextWordLength > 2)
- {
- if (pNextWord[0] == 's' && pNextWord[1] == 'e' && pNextWord[2] == 'c')
- uiTriggerTimerLength = uiCurrentValue;
- if (pNextWord[0] == 'm' && pNextWord[1] == 'i' && pNextWord[2] == 'n')
- uiTriggerTimerLength = uiCurrentValue * 60;
- }
- }
-
- // Case: ... Xs - only support up to 3 digit numbers (in seconds) for this timer parse method
- if (uiCurrentValue == 0)
- {
- char* pCurrentWord = words.Element(i);
- uint32 uiCurrentScanLength = MIN(strlen(pCurrentWord), 4);
-
- for (uint32 j = 0; j < uiCurrentScanLength; j++)
- {
- if (pCurrentWord[j] >= '0' && pCurrentWord[j] <= '9')
- continue;
-
- if (pCurrentWord[j] == 's')
- {
- pCurrentWord[j] = '\0';
- uiTriggerTimerLength = V_StringToUint32(pCurrentWord, 0, NULL, NULL, PARSING_FLAG_SKIP_WARNING);
- }
- break;
- }
- }
- }
-
- float fCurrentRoundClock = g_pGameRules->m_iRoundTime - (GetGlobals()->curtime - g_pGameRules->m_fRoundStartTime.Get().GetTime());
-
- // Only display trigger time if the timer is greater than 4 seconds, and time expires within the round
- if ((uiTriggerTimerLength > 4) && (fCurrentRoundClock > uiTriggerTimerLength))
- {
- int iTriggerTime = fCurrentRoundClock - uiTriggerTimerLength;
-
- // Round timer to nearest whole second
- if ((int)(fCurrentRoundClock - 0.5f) == (int)fCurrentRoundClock)
- iTriggerTime++;
-
- int mins = iTriggerTime / 60;
- int secs = iTriggerTime % 60;
-
- V_snprintf(buf, sizeof(buf), "%s %s %s %2d:%02d", " \7CONSOLE:\4", pText + sizeof("Console:"), "\x10- @", mins, secs);
- }
- else
- V_snprintf(buf, sizeof(buf), "%s %s", " \7CONSOLE:\4", pText + sizeof("Console:"));
-
- return KHook::Recall(nullptr, {KHook::Action::Ignore}, filter, buf, pPlayer, eMessageType);
-}
-
-CConVar g_cvarEnableTriggerTimer("cs2f_trigger_timer_enable", FCVAR_NONE, "Whether to process countdown messages said by Console (e.g. Hold for 10 seconds) and append the round time where the countdown resolves", false);
-
-KHook::Return Detour_UTIL_SayTextFilter(IRecipientFilter& filter, const char* pText, CCSPlayerController* pPlayer, uint64 eMessageType)
-{
- if (pPlayer)
- return {KHook::Action::Ignore};
-
- if (g_cvarEnableTriggerTimer.Get() && GetGlobals() && g_pGameRules)
- return SayChatMessageWithTimer(filter, pText, pPlayer, eMessageType);
-
- char buf[256];
- V_snprintf(buf, sizeof(buf), "%s %s", " \7CONSOLE:\4", pText + sizeof("Console:"));
-
- return KHook::Recall(nullptr, {KHook::Action::Ignore}, filter, buf, pPlayer, eMessageType);
-}
-
-KHook::Return Detour_UTIL_SayText2Filter(
- IRecipientFilter& filter,
- CCSPlayerController* pEntity,
- uint64 eMessageType,
- const char* msg_name,
- const char* param1,
- const char* param2,
- const char* param3,
- const char* param4)
-{
-#ifdef _DEBUG
- CPlayerSlot slot = filter.GetRecipientIndex(0);
- CCSPlayerController* target = CCSPlayerController::FromSlot(slot);
-
- if (target)
- Message("Chat from %s to %s: %s\n", param1, target->GetPlayerName().c_str(), param2);
-#endif
-
- return KHook::Recall(nullptr, {KHook::Action::Ignore}, filter, pEntity, eMessageType, msg_name, pEntity->GetPlayerName().c_str(), param2, param3, param4);
-}
-
-KHook::Return Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon)
-{
- if (g_cvarEnableEntWatch.Get() && !EW_Detour_CCSPlayer_WeaponServices_CanUse(pWeaponServices, pPlayerWeapon))
- return {KHook::Action::Supersede, false};
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon)
-{
- if (g_cvarEnableEntWatch.Get())
- EW_Detour_CCSPlayer_WeaponServices_EquipWeapon(pWeaponServices, pPlayerWeapon);
-
- g_pMapMigrations->OnEquipWeapon(pPlayerWeapon);
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarDisableSetModel("cs2f_disable_setmodel", FCVAR_NONE, "Whether to disable SetModel usage from maps (custom input, cs_script function)", false);
-
-bool PrepareMapSetModel(CBaseModelEntity* pModel)
-{
- if (!pModel->IsPawn())
- return true;
-
- if (g_cvarDisableSetModel.Get() || g_pMapMigrations->Migrations20260420Enabled())
- return false;
-
- // Player color may have been changed by zclass/server customization, so reset it first
- // This also means if maps want to change player color, it needs to be done after the SetModel call
- int originalAlpha = pModel->m_clrRender().a();
- pModel->m_clrRender = Color(255, 255, 255, originalAlpha);
-
- return true;
-}
-
-KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID, void* a7, void* a8)
-{
- VPROF_SCOPE_BEGIN("Detour_CEntityIdentity_AcceptInput");
-
- if (g_cvarEnableZR.Get())
- {
- bool result = ZR_Detour_CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, nOutputID);
-
- if (!result)
- return {KHook::Action::Supersede, result};
- }
-
- // Handle KeyValue(s)
- if (!V_strnicmp(pInputName->String(), "KeyValue", 8))
- {
- if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString)
- {
- // always const char*, even if it's FIELD_STRING (that is bug string from lua 'EntFire')
- return {KHook::Action::Supersede, CustomIO_HandleInput(pThis->m_pInstance, value->m_pszString, pActivator, pCaller)};
- }
- Message("Invalid value type for input %s\n", pInputName->String());
- return {KHook::Action::Supersede, false};
- }
-
- if (!V_strnicmp(pInputName->String(), "IgniteL", 7)) // Override IgniteLifetime
- {
- float flDuration = 0.f;
-
- if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString)
- flDuration = V_StringToFloat32(value->m_pszString, 0.f);
- else
- flDuration = value->m_float32;
-
- CCSPlayerPawn* pPawn = reinterpret_cast(pThis->m_pInstance);
-
- if (pPawn->IsPawn() && IgnitePawn(pPawn, flDuration, pPawn, pPawn))
- return {KHook::Action::Supersede, true};
- }
- else if (!V_strnicmp(pInputName->String(), "AddScore", 8))
- {
- int iScore = 0;
-
- if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString)
- iScore = V_StringToInt32(value->m_pszString, 0);
- else
- iScore = value->m_int32;
-
- CCSPlayerPawn* pPawn = reinterpret_cast(pThis->m_pInstance);
-
- if (pPawn->IsPawn() && pPawn->GetOriginalController())
- {
- pPawn->GetOriginalController()->AddScore(iScore);
- return {KHook::Action::Supersede, true};
- }
- }
- else if (!V_strcasecmp(pInputName->String(), "SetMessage"))
- {
- if (const auto pHudHint = reinterpret_cast(pThis->m_pInstance)->AsHudHint())
- {
- if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString)
- pHudHint->m_iszMessage(GameEntitySystem()->AllocPooledString(value->m_pszString));
- return {KHook::Action::Supersede, true};
- }
- }
- else if (!V_strcasecmp(pInputName->String(), "SetModel"))
- {
- if (const auto pModelEntity = reinterpret_cast(pThis->m_pInstance)->AsBaseModelEntity())
- {
- if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString && PrepareMapSetModel(pModelEntity))
- pModelEntity->SetModel(value->m_pszString);
-
- return {KHook::Action::Supersede, true};
- }
- }
- else if (const auto pGameUI = reinterpret_cast(pThis->m_pInstance)->AsGameUI())
- {
- if (!V_strcasecmp(pInputName->String(), "Activate"))
- return {KHook::Action::Supersede, CGameUIHandler::OnActivate(pGameUI, reinterpret_cast(pActivator))};
- if (!V_strcasecmp(pInputName->String(), "Deactivate"))
- return {KHook::Action::Supersede, CGameUIHandler::OnDeactivate(pGameUI, reinterpret_cast(pActivator))};
- }
- else if (const auto pViewControl = reinterpret_cast(pThis->m_pInstance)->AsPointViewControl())
- {
- if (!V_strcasecmp(pInputName->String(), "EnableCamera"))
- return {KHook::Action::Supersede, CPointViewControlHandler::OnEnable(pViewControl, reinterpret_cast(pActivator))};
- if (!V_strcasecmp(pInputName->String(), "DisableCamera"))
- return {KHook::Action::Supersede, CPointViewControlHandler::OnDisable(pViewControl, reinterpret_cast(pActivator))};
- if (!V_strcasecmp(pInputName->String(), "EnableCameraAll"))
- return {KHook::Action::Supersede, CPointViewControlHandler::OnEnableAll(pViewControl)};
- if (!V_strcasecmp(pInputName->String(), "DisableCameraAll"))
- return {KHook::Action::Supersede, CPointViewControlHandler::OnDisableAll(pViewControl)};
- }
-
- VPROF_SCOPE_END();
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarBlockNavLookup("cs2f_block_nav_lookup", FCVAR_NONE, "Whether to block navigation mesh lookup, improves server performance but breaks bot navigation", false);
-
-KHook::Return Detour_CNavMesh_GetNearestNavArea(CNavMesh* pNavMesh, float* unk2, unsigned int* unk3, unsigned int unk4, int64_t unk5, float unk6, int64_t unk7)
-{
- if (g_cvarBlockNavLookup.Get())
- return {KHook::Action::Supersede, nullptr};
-
- return {KHook::Action::Ignore};
-}
-
-float g_flStoreFrametime = 0.0f;
-
-KHook::Return Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove)
-{
- CCSPlayerPawn* pPawn = pThis->GetPawn();
-
- if (!pPawn->IsAlive() || !GetGlobals())
- return {KHook::Action::Ignore};
-
- CCSPlayerController* pController = pPawn->GetOriginalController();
-
- if (!pController || !pController->IsConnected())
- return {KHook::Action::Ignore};
-
- float flSpeedMod = pController->GetZEPlayer()->GetSpeedMod();
-
- if (flSpeedMod == 1.f)
- return {KHook::Action::Ignore};
-
- // Yes, this is what source1 does to scale player speed
- // Scale frametime during the entire movement processing step and revert right after
- g_flStoreFrametime = GetGlobals()->frametime;
- GetGlobals()->frametime *= flSpeedMod;
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_ProcessMovement_Post(CCSPlayer_MovementServices* pThis, void* pMove)
-{
- GetGlobals()->frametime = g_flStoreFrametime;
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarDisableSubtickMovement("cs2f_disable_subtick_move", FCVAR_NONE, "Whether to disable subtick movement", false);
-CConVar g_cvarDisableSubtickShooting("cs2f_disable_subtick_shooting", FCVAR_NONE, "Whether to disable subtick shooting, experimental (WARNING: add \"log_flags Shooting + DoNotEcho\" to your cfg to prevent console spam on every shot fired)", false);
-
-class CUserCmd
-{
-public:
- [[maybe_unused]] char pad0[0x10];
- CSGOUserCmdPB cmd;
- [[maybe_unused]] char pad1[0x38];
-#ifdef PLATFORM_WINDOWS
- [[maybe_unused]] char pad2[0x8];
-#endif
-};
-
-KHook::Return Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd* cmds, int numcmds, bool paused, float margin)
-{
- VPROF_SCOPE_BEGIN("Detour_ProcessUsercmds");
-
- for (int i = 0; i < numcmds; i++)
- {
- // Push fix only works properly if subtick movement is also disabled
- if (g_cvarDisableSubtickMovement.Get() || g_cvarUseOldPush.Get())
- {
- auto subtickMoves = cmds[i].cmd.mutable_base()->mutable_subtick_moves();
- auto iterator = subtickMoves->begin();
-
- while (iterator != subtickMoves->end())
- {
- uint64 button = iterator->button();
-
- // Remove normal subtick movement inputs by button
- // Unfortunately, we also need to ignore IN_JUMP, because de-subticking jumps somehow conflicts with other subtick inputs pressed at the same time
- if (button >= IN_DUCK && button <= IN_MOVERIGHT && button != IN_USE)
- {
- subtickMoves->erase(iterator);
- }
- else
- {
- // Remove subtick movement viewangles by pitch/yaw
- if (iterator->pitch_delta() != 0.0f)
- iterator->set_pitch_delta(0.0f);
-
- if (iterator->yaw_delta() != 0.0f)
- iterator->set_yaw_delta(0.0f);
-
- iterator++;
- }
- }
- }
-
- if (g_cvarDisableSubtickShooting.Get())
- {
- cmds[i].cmd.set_attack1_start_history_index(-1);
- cmds[i].cmd.set_attack2_start_history_index(-1);
- cmds[i].cmd.mutable_input_history()->Clear();
- }
- }
-
- VPROF_SCOPE_END();
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_CGamePlayerEquip_InputTriggerForAllPlayers(CGamePlayerEquip* pEntity, InputData_t* pInput)
-{
- CGamePlayerEquipHandler::TriggerForAllPlayers(pEntity, pInput);
- return {KHook::Action::Ignore};
-}
-KHook::Return Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer(CGamePlayerEquip* pEntity, InputData_t* pInput)
-{
- if (CGamePlayerEquipHandler::TriggerForActivatedPlayer(pEntity, pInput))
- return {KHook::Action::Ignore};
-
- return {KHook::Action::Supersede};
-}
-
-KHook::Return Detour_CTriggerGravity_GravityTouch(CTriggerGravity* pEntity, CBaseEntity* pOther)
-{
- // no need to call original function here
- // because original function calls CBaseEntity::SetGravityScale internal
- // but passes the wrong gravity scale value
- if (CTriggerGravityHandler::GravityTouching(pEntity, pOther))
- return {KHook::Action::Supersede};
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_GetFreeClient(int64_t unk1, const __m128i* unk2, unsigned int unk3, int64_t unk4, char unk5, void* unk6)
-{
- // Not sure if this function can even be called in this state, but if it is, we can't do shit anyways
- if (!GetClientList() || !GetGlobals())
- return {KHook::Action::Supersede, nullptr};
-
- // Check if there is still unused slots, this should never break so just fall back to original behaviour for ease (we don't have a CServerSideClient constructor)
- if (GetGlobals()->maxClients != GetClientList()->Count())
- return {KHook::Action::Ignore};
-
- // Phantom client fix
- for (int i = 0; i < GetClientList()->Count(); i++)
- {
- CServerSideClient* pClient = (*GetClientList())[i];
-
- if (pClient && pClient->GetSignonState() < SIGNONSTATE_CONNECTED)
- return {KHook::Action::Supersede, pClient};
- }
-
- // Server is actually full for real
- return {KHook::Action::Supersede, nullptr};
-}
-
-KHook::Return Detour_CCSPlayerPawn_GetMaxSpeed(CCSPlayerPawn* pPawn)
-{
- auto flMaxSpeed = getMaxSpeedHook.CallOriginal(pPawn);
-
- const auto pController = reinterpret_cast(pPawn->GetController());
- if (const auto pPlayer = pController != nullptr ? pController->GetZEPlayer() : nullptr)
- flMaxSpeed *= pPlayer->GetMaxSpeed();
-
- return {KHook::Action::Supersede, flMaxSpeed};
-}
-
-CConVar g_cvarPreventUsingPlayers("cs2f_prevent_using_players", FCVAR_NONE, "Whether to prevent +use from hitting players (0=can use players, 1=cannot use players)", false);
-bool g_bFindingUseEntity = false;
-
-KHook::Return Detour_FindUseEntity(CCSPlayer_UseServices* pThis, float a2)
-{
- g_bFindingUseEntity = true;
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_FindUseEntity_Post(CCSPlayer_UseServices* pThis, float a2)
-{
- g_bFindingUseEntity = false;
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_TraceFunc(int64* a1, int* a2, float* a3, uint64 traceMask)
-{
- if (g_cvarPreventUsingPlayers.Get() && g_bFindingUseEntity)
- {
- uint64 newMask = traceMask & (~(CONTENTS_PLAYER & CONTENTS_NPC));
- KHook::Recall(nullptr, {KHook::Action::Ignore}, a1, a2, a3, newMask);
- }
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, CTraceFilter* filter, int64 a6)
-{
- if (g_cvarPreventUsingPlayers.Get() && g_bFindingUseEntity)
- {
- filter->DisableInteractsWithLayer(LAYER_INDEX_CONTENTS_PLAYER);
- filter->DisableInteractsWithLayer(LAYER_INDEX_CONTENTS_NPC);
- }
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_CEntityIOOutput_FireOutputInternal(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay, void* a6, void* a7)
-{
- if (g_cvarEnableButtonWatch.Get())
- ButtonWatch(pThis, pActivator, pCaller, value, flDelay);
-
- if (g_cvarEnableEntWatch.Get())
- EW_FireOutput(pThis, pActivator, pCaller, value, flDelay);
-
- return {KHook::Action::Ignore};
-}
-
-#ifdef PLATFORM_WINDOWS
-KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn, Vector* pRet)
-{
- if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn)))
- {
- const auto& origin = pPawn->GetEyePosition();
- pRet->Init(origin.x, origin.y, origin.z);
- return {KHook::Action::Supersede, pRet};
- }
-
- return {KHook::Action::Ignore};
-}
-KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn, QAngle* pRet)
-{
- if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn)))
- {
- const auto& angles = pPawn->v_angle();
- pRet->Init(angles.x, angles.y, angles.z);
- return {KHook::Action::Supersede, pRet};
- }
-
- return {KHook::Action::Ignore};
-}
-#else
-KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn)
-{
- if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn)))
- {
- const auto& origin = pPawn->GetEyePosition();
- return {KHook::Action::Supersede, origin};
- }
-
- return {KHook::Action::Ignore};
-}
-KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn)
-{
- if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn)))
- {
- const auto& angles = pPawn->v_angle();
- return {KHook::Action::Supersede, angles};
- }
-
- return {KHook::Action::Ignore};
-}
-#endif
-
-KHook::Return Detour_CBaseFilter_InputTestActivator(CBaseFilter* pThis, InputData_t& inputdata)
-{
- // If null activator (player disconnected & pawn removed), block the real function from executing and crashing the server
- if (!inputdata.pActivator)
- return {KHook::Action::Supersede};
-
- return {KHook::Action::Ignore};
-}
-
-CConVar g_cvarFixGameBans("cs2f_fix_game_bans", FCVAR_NONE, "Whether to fix CS2 game bans spreading to all new joining players", false);
-
-KHook::Return Detour_GameSystem_Think_CheckSteamBan_Post()
-{
- // Implementation shared by @aiolos1045
- if (!g_cvarFixGameBans.Get())
- return {KHook::Action::Ignore};
-
- auto pMap = addresses::sm_mapGcBanInformation;
-
- // After player has been kicked, remove any ban entries, to prevent spreading to all new joining players
- if (pMap->Count() > 0)
- pMap->RemoveAll();
-
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_CCSPlayer_ItemServices_CanAcquire(CCSPlayer_ItemServices* pItemServices, CEconItemView* pEconItem, AcquireMethod iAcquireMethod, uint64_t unk4)
-{
- if (g_cvarEnableZR.Get())
- {
- AcquireResult zrResult = ZR_Detour_CCSPlayer_ItemServices_CanAcquire(pItemServices, pEconItem);
-
- if (zrResult != AcquireResult::Allowed)
- return {KHook::Action::Supersede, zrResult};
- }
-
- return {KHook::Action::Ignore};
-}
-
-bool g_bInScriptSetModel = false;
-
-KHook::Return Detour_CS_Script_SetModel(uint64_t unk1)
-{
- g_bInScriptSetModel = true;
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_CS_Script_SetModel_Post(uint64_t unk1)
-{
- g_bInScriptSetModel = false;
- return {KHook::Action::Ignore};
-}
-
-KHook::Return Detour_CBaseModelEntity_SetModel(CBaseModelEntity* pModel, const char* pszModel)
-{
- if (!g_bInScriptSetModel)
- return {KHook::Action::Ignore};
-
- if (PrepareMapSetModel(pModel))
- return {KHook::Action::Ignore};
-
- return {KHook::Action::Supersede};
-}
-
-KHook::Return Detour_CCSGameRules_GoToIntermission(CCSGameRules* pThis, bool bAbortedMatch)
-{
- if (!g_pMapVoteSystem->IsIntermissionAllowed(false) && g_cvarVoteManagerEnable.Get())
- return {KHook::Action::Supersede};
-
- if (g_cvarVoteManagerEnable.Get())
- g_pVoteManager->OnIntermission();
-
- return {KHook::Action::Ignore};
-}
diff --git a/src/detours.h b/src/detours.h
deleted file mode 100644
index a21ecbd6..00000000
--- a/src/detours.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * =============================================================================
- * CS2Fixes
- * Copyright (C) 2023-2026 Source2ZE
- * =============================================================================
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, version 3.0, as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- */
-
-#pragma once
-#include "cs2_sdk/entityio.h"
-#include "khook.hpp"
-#include
-
-class CCheckTransmitInfo;
-class IRecipientFilter;
-class ISoundEmitterSystemBase;
-class CBaseEntity;
-class CBaseFilter;
-class CCSPlayerController;
-class CEntityIndex;
-class CCommand;
-class CTriggerPush;
-class CTriggerGravity;
-class CGameConfig;
-class CGameRules;
-class CTakeDamageInfo;
-class CCSPlayer_WeaponServices;
-class CCSPlayer_MovementServices;
-class CCSPlayer_ItemServices;
-class CBasePlayerWeapon;
-class INetworkMessageInternal;
-class IEngineServiceMgr;
-class CServerSideClient;
-class INetChannel;
-class CBasePlayerPawn;
-class CUserCmd;
-class CGamePlayerEquip;
-class InputData_t;
-class CCSPlayerPawn;
-class CCSPlayer_UseServices;
-class CTraceFilter;
-class CNavMesh;
-class CBaseModelEntity;
-class Vector;
-class QAngle;
-class CEconItemView;
-class CCSGameRules;
-struct CTakeDamageResult;
-
-enum class AcquireMethod
-{
- PickUp,
- Buy,
-};
-
-enum class AcquireResult
-{
- Allowed,
- InvalidItem,
- AlreadyOwned,
- AlreadyPurchased,
- ReachedGrenadeTypeLimit,
- ReachedGrenadeTotalLimit,
- NotAllowedByTeam,
- NotAllowedByMap,
- NotAllowedByMode,
- NotAllowedForPurchase,
- NotAllowedByProhibition,
-};
-
-void InitDetours(CGameConfig* gameConfig);
-
-KHook::Return Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult);
-KHook::Return Detour_CBaseEntity_TakeDamageOld_Post(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult);
-KHook::Return Detour_TriggerPush_Touch(CTriggerPush* pPush, CBaseEntity* pOther);
-KHook::Return Detour_IsHearingClient(void*, int);
-KHook::Return Detour_UTIL_SayTextFilter(IRecipientFilter&, const char*, CCSPlayerController*, uint64);
-KHook::Return Detour_UTIL_SayText2Filter(IRecipientFilter&, CCSPlayerController*, uint64, const char*, const char*, const char*, const char*, const char*);
-KHook::Return Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices*, CBasePlayerWeapon*);
-KHook::Return Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices*, CBasePlayerWeapon*);
-KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, int nOutputID, void*, void*);
-KHook::Return Detour_CNavMesh_GetNearestNavArea(CNavMesh* pNavMesh, float* unk2, unsigned int* unk3, unsigned int unk4, int64_t unk5, float unk6, int64_t unk7);
-KHook::Return Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove);
-KHook::Return Detour_ProcessMovement_Post(CCSPlayer_MovementServices* pThis, void* pMove);
-KHook::Return Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd* cmds, int numcmds, bool paused, float margin);
-KHook::Return Detour_CGamePlayerEquip_InputTriggerForAllPlayers(CGamePlayerEquip*, InputData_t*);
-KHook::Return Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer(CGamePlayerEquip*, InputData_t*);
-KHook::Return Detour_CTriggerGravity_GravityTouch(CTriggerGravity* pEntity, CBaseEntity* pOther);
-KHook::Return Detour_GetFreeClient(int64_t unk1, const __m128i* unk2, unsigned int unk3, int64_t unk4, char unk5, void* unk6);
-KHook::Return Detour_CCSPlayerPawn_GetMaxSpeed(CCSPlayerPawn*);
-KHook::Return Detour_FindUseEntity(CCSPlayer_UseServices* pThis, float a2);
-KHook::Return Detour_FindUseEntity_Post(CCSPlayer_UseServices* pThis, float a2);
-KHook::Return Detour_TraceFunc(int64*, int*, float*, uint64);
-KHook::Return Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64);
-KHook::Return Detour_CEntityIOOutput_FireOutputInternal(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay, void*, void*);
-#ifdef PLATFORM_WINDOWS
-KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*, Vector*);
-KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*, QAngle*);
-#else
-KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*);
-KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*);
-#endif
-KHook::Return Detour_CBaseFilter_InputTestActivator(CBaseFilter* pThis, InputData_t& inputdata);
-KHook::Return Detour_GameSystem_Think_CheckSteamBan_Post();
-KHook::Return Detour_CCSPlayer_ItemServices_CanAcquire(CCSPlayer_ItemServices* pItemServices, CEconItemView* pEconItem, AcquireMethod iAcquireMethod, uint64_t unk4);
-KHook::Return Detour_CS_Script_SetModel(uint64_t unk1);
-KHook::Return Detour_CS_Script_SetModel_Post(uint64_t unk1);
-KHook::Return Detour_CBaseModelEntity_SetModel(CBaseModelEntity* pModel, const char* pszModel);
-KHook::Return Detour_CCSGameRules_GoToIntermission(CCSGameRules* pThis, bool bAbortedMatch);
diff --git a/src/entwatch.cpp b/src/entwatch.cpp
index cc199c27..0e47fafc 100644
--- a/src/entwatch.cpp
+++ b/src/entwatch.cpp
@@ -22,7 +22,6 @@
#include "cs2fixes.h"
#include "ctimer.h"
#include "customio.h"
-#include "detours.h"
#include "engine/igameeventsystem.h"
#include "entity/cbasebutton.h"
#include "entity/ccsplayercontroller.h"
@@ -33,6 +32,7 @@
#include "entity/cteam.h"
#include "entity/services.h"
#include "eventlistener.h"
+#include "hookmanager.h"
#include "gameevents.pb.h"
#include "leader.h"
#include "map_votes.h"
diff --git a/src/entwatch.h b/src/entwatch.h
index 0132c240..25ed8c8a 100644
--- a/src/entwatch.h
+++ b/src/entwatch.h
@@ -23,6 +23,7 @@
#include "ctimer.h"
#include "eventlistener.h"
#include "gamesystem.h"
+#include "cs2_sdk/entityio.h"
#include "khook.hpp"
#include "vendor/nlohmann/json_fwd.hpp"
@@ -218,20 +219,20 @@ class CEWHandler
public:
CEWHandler() :
m_bConfigLoaded(false),
- m_hBaseButtonUse(this, &CEWHandler::Hook_Use, nullptr),
- m_hPhysBoxUse(this, &CEWHandler::Hook_Use, nullptr),
- m_hRotButtonUse(this, &CEWHandler::Hook_Use, nullptr),
- m_hMomentaryRotButtonUse(this, &CEWHandler::Hook_Use, nullptr),
- m_hPhysicalButtonUse(this, &CEWHandler::Hook_Use, nullptr),
- m_hTriggerTeleportStartTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerOnceStartTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerMultipleStartTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerTeleportTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerOnceTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerMultipleTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerTeleportEndTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerOnceEndTouch(this, &CEWHandler::Hook_Touch, nullptr),
- m_hTriggerMultipleEndTouch(this, &CEWHandler::Hook_Touch, nullptr)
+ m_hBaseButtonUse(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Use, nullptr)),
+ m_hPhysBoxUse(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Use, nullptr)),
+ m_hRotButtonUse(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Use, nullptr)),
+ m_hMomentaryRotButtonUse(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Use, nullptr)),
+ m_hPhysicalButtonUse(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Use, nullptr)),
+ m_hTriggerTeleportStartTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerOnceStartTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerMultipleStartTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerTeleportTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerOnceTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerMultipleTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerTeleportEndTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerOnceEndTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr)),
+ m_hTriggerMultipleEndTouch(new KHook::Virtual(nullptr, this, &CEWHandler::Hook_Touch, nullptr))
{
CreateHooks();
}
@@ -260,7 +261,6 @@ class CEWHandler
void RegisterHandler(CBaseEntity* pEnt);
bool RegisterTrigger(CBaseEntity* pEnt);
- KHook::Return Hook_Touch(CBaseEntity* pThis, CBaseEntity* pOther);
bool RemoveTrigger(CBaseEntity* pEnt);
void RemoveHandler(CBaseEntity* pEnt);
void ResetAllClantags();
@@ -272,7 +272,6 @@ class CEWHandler
void Transfer(CCSPlayerController* pCaller, int iItemInstance, CHandle hReceiver);
void RemoveUseEntity(CBaseEntity* pEnt);
- KHook::Return Hook_Use(CBaseEntity* pThis, InputData_t* pInput);
std::map> mapItemConfig; /* items defined in the config */
std::vector> vecItems; /* all items found spawned */
@@ -285,21 +284,25 @@ class CEWHandler
std::map> mapTransfers; // Any etransfers that target multiple items
std::vector> vecActiveTransfers; // Active transfers where only the receiver can pickup the weapon
+public:
+ KHook::Return Hook_Touch(CBaseEntity* pThis, CBaseEntity* pOther);
+ KHook::Return Hook_Use(CBaseEntity* pThis, InputData_t* pInput);
- KHook::Virtual m_hBaseButtonUse;
- KHook::Virtual m_hPhysBoxUse;
- KHook::Virtual m_hRotButtonUse;
- KHook::Virtual m_hMomentaryRotButtonUse;
- KHook::Virtual m_hPhysicalButtonUse;
- KHook::Virtual m_hTriggerTeleportStartTouch;
- KHook::Virtual m_hTriggerOnceStartTouch;
- KHook::Virtual m_hTriggerMultipleStartTouch;
- KHook::Virtual m_hTriggerTeleportTouch;
- KHook::Virtual m_hTriggerOnceTouch;
- KHook::Virtual m_hTriggerMultipleTouch;
- KHook::Virtual m_hTriggerTeleportEndTouch;
- KHook::Virtual m_hTriggerOnceEndTouch;
- KHook::Virtual m_hTriggerMultipleEndTouch;
+protected:
+ KHook::Virtual* m_hBaseButtonUse;
+ KHook::Virtual* m_hPhysBoxUse;
+ KHook::Virtual* m_hRotButtonUse;
+ KHook::Virtual* m_hMomentaryRotButtonUse;
+ KHook::Virtual* m_hPhysicalButtonUse;
+ KHook::Virtual* m_hTriggerTeleportStartTouch;
+ KHook::Virtual* m_hTriggerOnceStartTouch;
+ KHook::Virtual* m_hTriggerMultipleStartTouch;
+ KHook::Virtual* m_hTriggerTeleportTouch;
+ KHook::Virtual* m_hTriggerOnceTouch;
+ KHook::Virtual* m_hTriggerMultipleTouch;
+ KHook::Virtual* m_hTriggerTeleportEndTouch;
+ KHook::Virtual* m_hTriggerOnceEndTouch;
+ KHook::Virtual* m_hTriggerMultipleEndTouch;
};
extern CEWHandler* g_pEWHandler;
diff --git a/src/hookmanager.cpp b/src/hookmanager.cpp
new file mode 100644
index 00000000..f850ec57
--- /dev/null
+++ b/src/hookmanager.cpp
@@ -0,0 +1,1839 @@
+/**
+ * =============================================================================
+ * CS2Fixes
+ * Copyright (C) 2023-2026 Source2ZE
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "cs_usercmd.pb.h"
+#include "networkbasetypes.pb.h"
+#include "usercmd.pb.h"
+
+#include "addresses.h"
+#include "adminsystem.h"
+#include "buttonwatch.h"
+#include "commands.h"
+#include "common.h"
+#include "cs2fixes.h"
+#include "cs_gameevents.pb.h"
+#include "ctimer.h"
+#include "customio.h"
+#include "entities.h"
+#include "entity/cbasemodelentity.h"
+#include "entity/ccsplayercontroller.h"
+#include "entity/ccsplayerpawn.h"
+#include "entity/ccsweaponbase.h"
+#include "entity/cenvhudhint.h"
+#include "entity/cgamerules.h"
+#include "entity/cpointviewcontrol.h"
+#include "entity/ctakedamageinfo.h"
+#include "entity/ctriggerpush.h"
+#include "entity/services.h"
+#include "entitylistener.h"
+#include "entwatch.h"
+#include "eventlistener.h"
+#include "gameconfig.h"
+#include "gameevents.pb.h"
+#include "hookmanager.h"
+#include "idlemanager.h"
+#include "igameevents.h"
+#include "irecipientfilter.h"
+#include "leader.h"
+#include "map_votes.h"
+#include "mapmigrations.h"
+#include "module.h"
+#include "networksystem/inetworkserializer.h"
+#include "networkstringtabledefs.h"
+#include "panoramavote.h"
+#include "patches.h"
+#include "playermanager.h"
+#include "serversideclient.h"
+#include "te.pb.h"
+#include "tier0/vprof.h"
+#include "usermessages.pb.h"
+#include "votemanager.h"
+#include "zombiereborn.h"
+#include
+
+#include "tier0/memdbgon.h"
+
+CHookManager* g_pHookManager = nullptr;
+
+double g_flUniversalTime = 0.0;
+float g_flLastTickedTime = 0.0f;
+bool g_bHasTicked = false;
+
+CConVar g_cvarMotdUrl("cs2f_motd_url", FCVAR_NONE, "Server MOTD URL, shows up as a \"Server Website\" button in scoreboard", "");
+CConVar g_cvarBlockParticleMsgs("cs2f_block_particle_msgs", FCVAR_NONE, "Whether to block CUserMsg_ParticleManager messages to fix lag/crashes, experimental", false);
+CConVar g_cvarDropMapWeapons("cs2f_drop_map_weapons", FCVAR_NONE, "Whether to force drop map-spawned weapons on death", false);
+CConVar g_cvarFixPhysicsPlayerShuffle("cs2f_shuffle_player_physics_sim", FCVAR_NONE, "Whether to enable shuffle player list in physics simulate", false);
+
+class CUserCmd
+{
+public:
+ [[maybe_unused]] char pad0[0x10];
+ CSGOUserCmdPB cmd;
+ [[maybe_unused]] char pad1[0x38];
+#ifdef PLATFORM_WINDOWS
+ [[maybe_unused]] char pad2[0x8];
+#endif
+};
+
+CConVar g_cvarBlockMolotovSelfDmg("cs2f_block_molotov_self_dmg", FCVAR_NONE, "Whether to block self-damage from molotovs", false);
+CConVar