diff --git a/.gitmodules b/.gitmodules
index bb58a87d1..3f55c7c2f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,7 @@
[submodule "sdk"]
path = sdk
url = https://github.com/alliedmodders/hl2sdk.git
+ branch = cs2
[submodule "hl2sdk-manifests"]
path = hl2sdk-manifests
url = https://github.com/alliedmodders/hl2sdk-manifests
diff --git a/AMBuildScript b/AMBuildScript
index 7e67b9ba4..eb9bcac73 100644
--- a/AMBuildScript
+++ b/AMBuildScript
@@ -438,7 +438,7 @@ const char *PLUGIN_FULL_VERSION = \"{version}\";
cxx.cxxincludes += [
os.path.join(context.currentSourcePath),
os.path.join(mms_core_path),
- os.path.join(mms_core_path, 'sourcehook'),
+ os.path.join(self.mms_root, 'third_party', 'khook', 'include'),
]
for other_sdk in self.sdk_manifests:
diff --git a/AMBuilder b/AMBuilder
index 30237a945..31cb31af0 100644
--- a/AMBuilder
+++ b/AMBuilder
@@ -13,23 +13,16 @@ for sdk_target in MMSPlugin.sdk_targets:
os.path.join(builder.sourcePath, 'src', 'utils'),
os.path.join(builder.sourcePath, 'public'),
os.path.join(builder.sourcePath, 'sdk', 'thirdparty', 'protobuf-3.21.8', 'src'),
- os.path.join(builder.sourcePath, 'vendor', 'funchook', 'include'),
]
- target_folder = 'Debug' if builder.options.debug else 'Release'
-
if binary.compiler.target.platform == 'linux':
binary.compiler.postlink += [
- os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', target_folder, 'libfunchook.a'),
- os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', target_folder, 'libdistorm.a'),
os.path.join(builder.sourcePath, 'sdk', 'lib', 'linux64', 'release', 'libprotobuf.a'),
]
binary.sources += ['src/utils/plat_unix.cpp']
elif binary.compiler.target.platform == 'windows':
binary.compiler.postlink += [
os.path.join('psapi.lib'),
- os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', target_folder, 'funchook.lib'),
- os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', target_folder, 'distorm.lib'),
os.path.join(builder.sourcePath, 'sdk', 'lib' ,'public', 'win64', '2015', 'libprotobuf.lib'),
os.path.join(builder.sourcePath, 'sdk', 'lib', 'public', 'win64', 'steam_api64.lib')
]
@@ -44,6 +37,7 @@ for sdk_target in MMSPlugin.sdk_targets:
'src/commands.cpp',
'src/addresses.cpp',
'src/detours.cpp',
+ 'src/virtual_hooks.cpp',
'src/entities.cpp',
'src/events.cpp',
'src/utils/entity.cpp',
@@ -52,6 +46,7 @@ for sdk_target in MMSPlugin.sdk_targets:
'src/utils/utils.cpp',
'src/utils/vscript_function.cpp',
'src/cs2_sdk/entity/services.cpp',
+ 'src/cs2_sdk/entity/customhudlayout.cpp',
'src/cs2_sdk/entity/ccsplayerpawn.cpp',
'src/cs2_sdk/entity/cbasemodelentity.cpp',
'src/cs2_sdk/schema.cpp',
diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj
index a13151251..a97138e7b 100644
--- a/CS2Fixes.vcxproj
+++ b/CS2Fixes.vcxproj
@@ -201,6 +201,7 @@
+
@@ -232,6 +233,7 @@
+
@@ -265,6 +267,7 @@
+
@@ -274,10 +277,10 @@
-
+
@@ -314,6 +317,7 @@
+
diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters
index ebaa3c84a..fd62f6b59 100644
--- a/CS2Fixes.vcxproj.filters
+++ b/CS2Fixes.vcxproj.filters
@@ -68,6 +68,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -200,6 +203,9 @@
Source Files\cs2_sdk\entity
+
+ Source Files\cs2_sdk\entity
+
Source Files
@@ -268,15 +274,15 @@
Header Files
-
- Header Files
-
Header Files
Header Files
+
+ Header Files
+
Header Files
@@ -445,6 +451,9 @@
Header Files\utils
+
+ Header Files\utils
+
Header Files\cs2_sdk\entity
@@ -505,5 +514,8 @@
Header Files
+
+ Header Files\cs2_sdk\entity
+
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index a68009bc3..e080113ac 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@ FROM ghcr.io/source2ze/build-containers:steamrt3
WORKDIR /app
-RUN git clone https://github.com/alliedmodders/metamod-source
+RUN git clone --recurse-submodules https://github.com/alliedmodders/metamod-source
RUN git config --global --add safe.directory /app
COPY ./docker-entrypoint.sh ./
diff --git a/cfg/cs2fixes/cs2fixes.cfg b/cfg/cs2fixes/cs2fixes.cfg
index a0b226ac2..645c4807c 100644
--- a/cfg/cs2fixes/cs2fixes.cfg
+++ b/cfg/cs2fixes/cs2fixes.cfg
@@ -155,10 +155,10 @@ cs2f_idle_kick_min_players 0 // Minimum amount of connected clients to kick id
cs2f_idle_kick_admins 1 // Whether to kick idle players with ADMFLAG_GENERIC
// Button watch
-cs2f_enable_button_watch 1 // INCOMPATIBLE WITH CS#. Whether to enable button watch or not.
+cs2f_enable_button_watch 0 // Whether to enable button watch or not.
// EntWatch Settings
-entwatch_enable 0 // INCOMPATIBLE WITH CS#. Whether to enable EntWatch features
+entwatch_enable 0 // Whether to enable EntWatch features
entwatch_auto_filter 1 // Whether to automatically block non-item holders from triggering uses
entwatch_clantag 1 // Whether to set item holder's clantag and set 9999 score
entwatch_clantag_colour_mode 1 // Whether to set item clantag colours (0=No colour, 1=Use item colour, 2=Use entwatch_clantag_colour value)
diff --git a/gamedata/cs2fixes.jsonc b/gamedata/cs2fixes.jsonc
index 5f4784bda..bb983481a 100644
--- a/gamedata/cs2fixes.jsonc
+++ b/gamedata/cs2fixes.jsonc
@@ -5,14 +5,14 @@
"UTIL_SayTextFilter":
{
"library": "server",
- "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 49 8B F8",
- "linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 4C 8D 75 ? 41 55 41 89 CD 41 54 49 89 D4"
+ "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 49 8B F0 48 89 4D",
+ "linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 4C 8D 75 ? 41 55 49 89 D5"
},
// Called from Host_Say
"UTIL_SayText2Filter":
{
"library": "server",
- "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 45 0F B6 F0",
+ "windows": "48 89 5C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 41 8B F8",
"linux": "55 48 8D 05 ? ? ? ? 48 89 E5 41 57 41 56 41 89 D6 31 D2"
},
// idk a good way to find this again, i just brute forced the vtable. offset is 136 on CTriggerPush
@@ -222,12 +222,6 @@
"windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 41 0F B6 F0",
"linux": "55 48 89 E5 41 57 49 89 FF 41 56 41 55 41 54 53 89 D3 48 81 EC ? ? ? ? 40 38 B7"
},
- "CTakeDamageInfo":
- {
- "library": "server",
- "windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 45 33 F6 48 C7 41",
- "linux": "49 BB ? ? ? ? ? ? ? ? 55 48 89 E5 48 83 EC ? 4C 8D 15"
- },
// "%sRecv usercmd %d. Margin:%5.1fms net +%2d queue =%5.1f total\n"
"ProcessUsercmds":
{
diff --git a/sdk b/sdk
index e7ed969ea..bd17582be 160000
--- a/sdk
+++ b/sdk
@@ -1 +1 @@
-Subproject commit e7ed969ea8cb9afebd19ba6c5f262c9048890543
+Subproject commit bd17582be4bc18970e4fe4c518359872fda8276d
diff --git a/src/addresses.cpp b/src/addresses.cpp
index 34d669b0b..f846e2cf9 100644
--- a/src/addresses.cpp
+++ b/src/addresses.cpp
@@ -1,4 +1,4 @@
-/**
+/**
* =============================================================================
* CS2Fixes
* Copyright (C) 2023-2026 Source2ZE
@@ -27,10 +27,10 @@
#include "tier0/memdbgon.h"
-#define RESOLVE_SIG(gameConfig, name, variable) \
- variable = (decltype(variable))gameConfig->ResolveSignature(name); \
- if (!variable) \
- return false; \
+#define RESOLVE_SIG(name, variable) \
+ variable = (decltype(variable))g_GameConfig->ResolveSignature(name); \
+ if (!variable) \
+ return false; \
Message("Found %s at 0x%p\n", name, variable);
#define RESOLVE_SF(scriptDesc, funcName, variable) \
@@ -41,7 +41,7 @@
else \
Message("Found %s::%s at 0x%p\n", scriptDesc->m_pszClassname, funcName, variable.GetPtr());
-bool addresses::Initialize(CGameConfig* g_GameConfig)
+bool addresses::Initialize()
{
modules::engine = new CModule(ROOTBIN, "engine2");
modules::tier0 = new CModule(ROOTBIN, "tier0");
@@ -62,30 +62,29 @@ bool addresses::Initialize(CGameConfig* g_GameConfig)
modules::hammer = new CModule(ROOTBIN, "tools/hammer");
#endif
- RESOLVE_SIG(g_GameConfig, "SetGroundEntity", addresses::SetGroundEntity);
- RESOLVE_SIG(g_GameConfig, "CCSPlayerController_SwitchTeam", addresses::CCSPlayerController_SwitchTeam);
- RESOLVE_SIG(g_GameConfig, "CBasePlayerController_SetPawn", addresses::CBasePlayerController_SetPawn);
- RESOLVE_SIG(g_GameConfig, "CBaseModelEntity_SetModel", addresses::CBaseModelEntity_SetModel);
- RESOLVE_SIG(g_GameConfig, "CEntitySystem_AddEntityIOEvent", addresses::CEntitySystem_AddEntityIOEvent);
- RESOLVE_SIG(g_GameConfig, "CEntityInstance_AcceptInput", addresses::CEntityInstance_AcceptInput);
- RESOLVE_SIG(g_GameConfig, "CGameEntitySystem_FindEntityByClassName", addresses::CGameEntitySystem_FindEntityByClassName);
- RESOLVE_SIG(g_GameConfig, "CGameEntitySystem_FindEntityByName", addresses::CGameEntitySystem_FindEntityByName);
- RESOLVE_SIG(g_GameConfig, "CGameRules_TerminateRound", addresses::CGameRules_TerminateRound);
- RESOLVE_SIG(g_GameConfig, "CreateEntityByName", addresses::CreateEntityByName);
- RESOLVE_SIG(g_GameConfig, "DispatchSpawn", addresses::DispatchSpawn);
- RESOLVE_SIG(g_GameConfig, "DispatchParticleEffect", addresses::DispatchParticleEffect);
- RESOLVE_SIG(g_GameConfig, "CBaseEntity_EmitSoundFilter", addresses::CBaseEntity_EmitSoundFilter);
- RESOLVE_SIG(g_GameConfig, "CBaseEntity_SetMoveType", addresses::CBaseEntity_SetMoveType);
- RESOLVE_SIG(g_GameConfig, "CTakeDamageInfo", addresses::CTakeDamageInfo_Constructor);
- RESOLVE_SIG(g_GameConfig, "CCSPlayer_WeaponServices_EquipWeapon", addresses::CCSPlayer_WeaponServices_EquipWeapon);
- RESOLVE_SIG(g_GameConfig, "GetSpawnGroups", addresses::GetSpawnGroups);
- RESOLVE_SIG(g_GameConfig, "CBasePlayerPawn_SnapViewAngles", addresses::CBasePlayerPawn_SnapViewAngles);
- RESOLVE_SIG(g_GameConfig, "CCSPlayer_WeaponServices_EquipWeapon", addresses::CCSPlayer_WeaponServices_EquipWeapon);
-
- return InitializeBanMap(g_GameConfig);
+ RESOLVE_SIG("SetGroundEntity", addresses::SetGroundEntity);
+ RESOLVE_SIG("CCSPlayerController_SwitchTeam", addresses::CCSPlayerController_SwitchTeam);
+ RESOLVE_SIG("CBasePlayerController_SetPawn", addresses::CBasePlayerController_SetPawn);
+ RESOLVE_SIG("CBaseModelEntity_SetModel", addresses::CBaseModelEntity_SetModel);
+ RESOLVE_SIG("CEntitySystem_AddEntityIOEvent", addresses::CEntitySystem_AddEntityIOEvent);
+ RESOLVE_SIG("CEntityInstance_AcceptInput", addresses::CEntityInstance_AcceptInput);
+ RESOLVE_SIG("CGameEntitySystem_FindEntityByClassName", addresses::CGameEntitySystem_FindEntityByClassName);
+ RESOLVE_SIG("CGameEntitySystem_FindEntityByName", addresses::CGameEntitySystem_FindEntityByName);
+ RESOLVE_SIG("CGameRules_TerminateRound", addresses::CGameRules_TerminateRound);
+ RESOLVE_SIG("CreateEntityByName", addresses::CreateEntityByName);
+ RESOLVE_SIG("DispatchSpawn", addresses::DispatchSpawn);
+ RESOLVE_SIG("DispatchParticleEffect", addresses::DispatchParticleEffect);
+ RESOLVE_SIG("CBaseEntity_EmitSoundFilter", addresses::CBaseEntity_EmitSoundFilter);
+ RESOLVE_SIG("CBaseEntity_SetMoveType", addresses::CBaseEntity_SetMoveType);
+ RESOLVE_SIG("CCSPlayer_WeaponServices_EquipWeapon", addresses::CCSPlayer_WeaponServices_EquipWeapon);
+ RESOLVE_SIG("GetSpawnGroups", addresses::GetSpawnGroups);
+ RESOLVE_SIG("CBasePlayerPawn_SnapViewAngles", addresses::CBasePlayerPawn_SnapViewAngles);
+ RESOLVE_SIG("CBaseEntity_TakeDamageOld", addresses::CBaseEntity_TakeDamageOld);
+
+ return InitializeBanMap();
}
-bool addresses::InitializeBanMap(CGameConfig* g_GameConfig)
+bool addresses::InitializeBanMap()
{
// This signature directly points to the instruction referencing sm_mapGcBanInformation
uintptr_t pAddr = (uintptr_t)g_GameConfig->ResolveSignature("CCSGameRules__sm_mapGcBanInformation");
diff --git a/src/addresses.h b/src/addresses.h
index d70fb3b61..b69d453ac 100644
--- a/src/addresses.h
+++ b/src/addresses.h
@@ -1,4 +1,4 @@
-/**
+/**
* =============================================================================
* CS2Fixes
* Copyright (C) 2023-2026 Source2ZE
@@ -61,6 +61,7 @@ class CBasePlayerWeapon;
class CSpawnGroupMgrGameSystem;
struct EmitSound_t;
struct StartSoundEventInfo;
+struct CTakeDamageResult;
// Can't be forward-declared, can't include cgamerules.h.. just define it here
struct CGcBanInformation_t
@@ -73,8 +74,8 @@ struct CGcBanInformation_t
namespace addresses
{
- bool Initialize(CGameConfig* g_GameConfig);
- bool InitializeBanMap(CGameConfig* g_GameConfig);
+ bool Initialize();
+ bool InitializeBanMap();
bool InitializeVScriptFunctions();
inline CUtlOrderedMap* sm_mapGcBanInformation;
@@ -102,11 +103,10 @@ namespace addresses
char iAttachmentPoint, CUtlSymbolLarge iAttachmentName, bool bResetAllParticlesOnEntity, int nSplitScreenPlayerSlot, IRecipientFilter* a7, byte* a8);
inline StartSoundEventInfo(FASTCALL* CBaseEntity_EmitSoundFilter)(IRecipientFilter& filter, CEntityIndex ent, const EmitSound_t& params);
inline void(FASTCALL* CBaseEntity_SetMoveType)(CBaseEntity* pThis, MoveType_t nMoveType, MoveCollide_t nMoveCollide);
- inline void(FASTCALL* CTakeDamageInfo_Constructor)(CTakeDamageInfo* pThis, CBaseEntity* pInflictor, CBaseEntity* pAttacker, CBaseEntity* pAbility,
- const Vector* vecDamageForce, const Vector* vecDamagePosition, float flDamage, int bitsDamageType, int iCustomDamage, void* a10);
inline void(FASTCALL* CCSPlayer_WeaponServices_EquipWeapon)(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon);
inline void(FASTCALL* GetSpawnGroups)(CSpawnGroupMgrGameSystem* pSpawnGroupMgr, CUtlVector* pList);
inline void(FASTCALL* CBasePlayerPawn_SnapViewAngles)(CBasePlayerPawn* pPawn, QAngle* pAngles);
+ inline int64(FASTCALL* CBaseEntity_TakeDamageOld)(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult);
inline CVScriptFunction SetGravityScale;
inline CVScriptFunction ScriptSetEntityName;
diff --git a/src/buttonwatch.cpp b/src/buttonwatch.cpp
index 3ea424f00..08eeaa9c0 100644
--- a/src/buttonwatch.cpp
+++ b/src/buttonwatch.cpp
@@ -32,21 +32,11 @@
#include "entity/clogiccase.h"
#include "entity/cpointviewcontrol.h"
-CConVar g_cvarEnableButtonWatch(
- "cs2f_enable_button_watch", FCVAR_NONE, "INCOMPATIBLE WITH CS#. Whether to enable button watch or not.", false,
- [](CConVar* cvar, CSplitScreenSlot slot, const bool* new_val, const bool* old_val) {
- if (!(*new_val) || !SetupFireOutputInternalDetour())
- {
- mapIOFunctions.erase("buttonwatch");
- cvar->Set(false);
- }
- else if (!IsButtonWatchEnabled())
- mapIOFunctions["buttonwatch"] = ButtonWatch;
- });
+CConVar g_cvarEnableButtonWatch("cs2f_enable_button_watch", FCVAR_NONE, "Whether to enable button watch or not.", false);
CON_COMMAND_CHAT_FLAGS(bw, "- Toggle button watch display", ADMFLAG_GENERIC)
{
- if (!IsButtonWatchEnabled())
+ if (!g_cvarEnableButtonWatch.Get())
{
ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Button watch is disabled on this server.");
return;
@@ -84,17 +74,10 @@ CON_COMMAND_CHAT_FLAGS(bw, "- Toggle button watch display", ADMFLAG_GENERIC)
}
}
-bool IsButtonWatchEnabled()
-{
- return std::any_of(mapIOFunctions.begin(), mapIOFunctions.end(), [](const auto& p) {
- return p.first == "buttonwatch";
- });
-}
-
std::map mapRecentEnts;
void ButtonWatch(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay)
{
- if (!IsButtonWatchEnabled() || !GetGlobals() || V_stricmp(pThis->m_pDesc->m_pName, "OnPressed") || !pActivator || !((CBaseEntity*)pActivator)->IsPawn() || !pCaller || mapRecentEnts.contains(pCaller->GetEntityIndex().Get()))
+ if (!GetGlobals() || V_stricmp(pThis->m_pDesc->m_pName, "OnPressed") || !pActivator || !((CBaseEntity*)pActivator)->IsPawn() || !pCaller || mapRecentEnts.contains(pCaller->GetEntityIndex().Get()))
return;
CCSPlayerController* ccsPlayer = CCSPlayerController::FromPawn(static_cast(pActivator));
diff --git a/src/buttonwatch.h b/src/buttonwatch.h
index 1d22e9b4c..d7e9a825b 100644
--- a/src/buttonwatch.h
+++ b/src/buttonwatch.h
@@ -20,5 +20,6 @@
#pragma once
#include "detours.h"
-bool IsButtonWatchEnabled();
+extern CConVar g_cvarEnableButtonWatch;
+
void ButtonWatch(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay);
\ No newline at end of file
diff --git a/src/cdetour.h b/src/cdetour.h
deleted file mode 100644
index 2c34db3cf..000000000
--- a/src/cdetour.h
+++ /dev/null
@@ -1,153 +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 "addresses.h"
-#include "funchook.h"
-#include "gameconfig.h"
-#include "module.h"
-#include "plat.h"
-#include "utlvector.h"
-#include
-
-class CDetourBase
-{
-public:
- virtual const char* GetName() = 0;
- virtual bool CreateDetour(CGameConfig* gameConfig) = 0;
- virtual void FreeDetour() = 0;
- virtual void EnableDetour() = 0;
- virtual void DisableDetour() = 0;
-};
-
-extern CUtlVector g_vecDetours;
-
-template
-class CDetour : public CDetourBase
-{
-public:
- CDetour(T* pfnDetour, const char* pszName);
-
- ~CDetour()
- {
- FreeDetour();
- }
-
- bool CreateDetour(CGameConfig* gameConfig) override;
- void EnableDetour();
- void DisableDetour();
- void FreeDetour() override;
- const char* GetName() override { return m_pszName; }
- T* GetFunc() { return m_pfnFunc; }
-
- // Shorthand for calling original.
- template
- auto operator()(Args&&... args)
- {
- return std::invoke(m_pfnFunc, std::forward(args)...);
- }
-
-private:
- CModule** m_pModule;
- T* m_pfnDetour;
- const char* m_pszName;
- byte* m_pSignature;
- const char* m_pSymbol;
- T* m_pfnFunc;
- funchook_t* m_hook;
- bool m_bInstalled;
-};
-
-template
-CDetour::CDetour(T* pfnDetour, const char* pszName) :
- m_pfnDetour(pfnDetour), m_pszName(pszName)
-{
- m_hook = nullptr;
- m_bInstalled = false;
- m_pSignature = nullptr;
- m_pSymbol = nullptr;
- m_pModule = nullptr;
-
- g_vecDetours.AddToTail(this);
-}
-
-template
-bool CDetour::CreateDetour(CGameConfig* gameConfig)
-{
- m_pfnFunc = (T*)gameConfig->ResolveSignature(m_pszName);
- if (!m_pfnFunc)
- return false;
-
- T* pFunc = m_pfnFunc;
-
- m_hook = funchook_create();
- funchook_prepare(m_hook, (void**)&m_pfnFunc, (void*)m_pfnDetour);
-
- Message("Detoured %s at 0x%p\n", m_pszName, pFunc);
- return true;
-}
-
-template
-void CDetour::EnableDetour()
-{
- if (!m_hook)
- return;
-
- int error = funchook_install(m_hook, 0);
-
- if (!error)
- m_bInstalled = true;
- else
- Warning("funchook_install error for %s: %d %s\n", m_pszName, error, funchook_error_message(m_hook));
-}
-
-template
-void CDetour::DisableDetour()
-{
- if (!m_hook)
- return;
-
- int error = funchook_uninstall(m_hook, 0);
-
- if (!error)
- m_bInstalled = false;
- else
- Warning("funchook_uninstall error for %s: %d %s\n", m_pszName, error, funchook_error_message(m_hook));
-}
-
-template
-void CDetour::FreeDetour()
-{
- if (!m_hook)
- return;
-
- if (m_bInstalled)
- DisableDetour();
-
- int error = funchook_destroy(m_hook);
- m_hook = nullptr;
-
- if (error == 0)
- Message("Removed detour %s\n", m_pszName);
- else
- Warning("funchook_destroy error for %s: %d %s\n", m_pszName, error, funchook_error_message(m_hook));
-}
-
-#define DECLARE_DETOUR(name, detour) \
- CDetour name(detour, #name)
diff --git a/src/commands.cpp b/src/commands.cpp
index a96f12c90..b608cda57 100644
--- a/src/commands.cpp
+++ b/src/commands.cpp
@@ -30,6 +30,7 @@
#include "entity/ccsplayerpawn.h"
#include "entity/ccsweaponbase.h"
#include "entity/cparticlesystem.h"
+#include "entity/customhudlayout.h"
#include "entity/lights.h"
#include "httpmanager.h"
#include "leader.h"
@@ -1172,6 +1173,35 @@ CON_COMMAND_CHAT(discordbot, " - Send a message to a discord webh
g_pDiscordBotManager->PostDiscordMessage(args[1], args[2]);
}
-#endif
-// _DEBUG
+CON_COMMAND_CHAT(uitest, " () - Spawn UI panel")
+{
+ if (args.ArgC() < 2)
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Usage: !uitest ()");
+ return;
+ }
+
+ static CHandle hLayout = nullptr;
+
+ if (hLayout.Get())
+ hLayout->Remove();
+
+ hLayout = CCSCustomHudLayout::Create(args[1], "plugin_custom_hud");
+ hLayout->SetHasClass("dialog", "Dismissed", false, player);
+ hLayout->SetInputCaptureEnabled(true, player);
+
+ if (args.ArgC() > 2)
+ hLayout->SetDialogVariableString("MyLabel", "CustomText", args[2], player);
+
+ hLayout->AddClickCallback([](CCSPlayerController* pController, CCSCustomHudLayout* pHud, std::string sButtonId) {
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "%s clicked %s on %s\n", pController->GetPlayerName().c_str(), sButtonId.c_str(), pHud->GetName());
+
+ if (sButtonId == "dismiss_button")
+ {
+ pHud->SetHasClass("dialog", "Dismissed", true, pController);
+ pHud->SetInputCaptureEnabled(false, pController);
+ }
+ });
+}
+#endif // _DEBUG
diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h
index a22c4f7a5..c5fdcb3b0 100644
--- a/src/cs2_sdk/entity/cbaseentity.h
+++ b/src/cs2_sdk/entity/cbaseentity.h
@@ -170,7 +170,7 @@ class CBaseEntity : public CEntityInstance
void TakeDamage(CTakeDamageInfo& info)
{
- Detour_CBaseEntity_TakeDamageOld(this, &info, 0);
+ addresses::CBaseEntity_TakeDamageOld(this, &info, 0);
}
void Teleport(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity)
diff --git a/src/cs2_sdk/entity/ctakedamageinfo.h b/src/cs2_sdk/entity/ctakedamageinfo.h
index e1976a535..644d6d65f 100644
--- a/src/cs2_sdk/entity/ctakedamageinfo.h
+++ b/src/cs2_sdk/entity/ctakedamageinfo.h
@@ -89,28 +89,35 @@ class CTakeDamageInfo
[[maybe_unused]] uint8_t __pad0000[0x8];
public:
+ DECLARE_SCHEMA_CLASS(CTakeDamageInfo)
+
CTakeDamageInfo()
{
- addresses::CTakeDamageInfo_Constructor(this, nullptr, nullptr, nullptr, &vec3_origin, &vec3_origin, 0.f, 0, 0, nullptr);
+ Construct();
}
CTakeDamageInfo(CBaseEntity* pInflictor, CBaseEntity* pAttacker, CBaseEntity* pAbility, float flDamage, DamageTypes_t bitsDamageType)
{
- addresses::CTakeDamageInfo_Constructor(this, pInflictor, pAttacker, pAbility, &vec3_origin, &vec3_origin, flDamage, bitsDamageType, 0, nullptr);
+ Construct();
+ m_hInflictor = pInflictor;
+ m_hAttacker = pAttacker;
+ m_hAbility = pAbility;
+ m_flDamage = flDamage;
+ m_bitsDamageType = bitsDamageType;
}
Vector m_vecDamageForce; // 0x8 | 8
Vector m_vecDamagePosition; // 0x14 | 20
Vector m_vecReportedPosition; // 0x20 | 32
Vector m_vecDamageDirection; // 0x2c | 44
- CBaseHandle m_hInflictor; // 0x38 | 56
- CBaseHandle m_hAttacker; // 0x3c | 60
- CBaseHandle m_hAbility; // 0x40 | 64
+ CHandle m_hInflictor; // 0x38 | 56
+ CHandle m_hAttacker; // 0x3c | 60
+ CHandle m_hAbility; // 0x40 | 64
float m_flDamage; // 0x44 | 68
float m_flTotalledDamage; // 0x48 | 72
- int32_t m_bitsDamageType; // 0x4c | 76
- int32_t m_iDamageCustom; // 0x50 | 80
- int8_t m_iAmmoType; // 0x54 | 84
+ int32_t m_bitsDamageType; // 0x4c | 76
+ int32_t m_iDamageCustom; // 0x50 | 80
+ int8_t m_iAmmoType; // 0x54 | 84
private:
[[maybe_unused]] uint8_t m_nUnknown0[0xb]; // 0x55 | 85
diff --git a/src/cs2_sdk/entity/customhudlayout.cpp b/src/cs2_sdk/entity/customhudlayout.cpp
new file mode 100644
index 000000000..fc15346c4
--- /dev/null
+++ b/src/cs2_sdk/entity/customhudlayout.cpp
@@ -0,0 +1,132 @@
+/**
+ * =============================================================================
+ * 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 "customhudlayout.h"
+#include "entity.h"
+
+std::unordered_map g_mapClickCallbacks;
+
+CCSCustomHudLayout* CCSCustomHudLayout::Create(std::string sLayout, std::string sTargetName)
+{
+ CCSCustomHudLayout* pLayout = CreateEntityByName("custom_hud_layout");
+
+ if (!pLayout)
+ return nullptr;
+
+ CEntityKeyValues* pKeyValues = new CEntityKeyValues();
+ std::string sLayoutPath = "panorama/layout/custom_game/" + std::string(sLayout) + ".vxml_c";
+ pKeyValues->SetString("layout", sLayoutPath.c_str());
+
+ if (!sTargetName.empty())
+ pKeyValues->SetString("targetname", sTargetName.c_str());
+
+ pLayout->DispatchSpawn(pKeyValues);
+ return pLayout;
+}
+
+void CCSCustomHudLayout::ClearClickCallbacks()
+{
+ g_mapClickCallbacks.clear();
+}
+
+void CCSCustomHudLayout::OnClick(CCSPlayerController* pController, const std::string& sButtonId)
+{
+ if (auto it = g_mapClickCallbacks.find(GetHandle().ToInt()); it != g_mapClickCallbacks.end())
+ it->second(pController, this, sButtonId);
+}
+
+void CCSCustomHudLayout::OnEntityDeleted()
+{
+ g_mapClickCallbacks.erase(GetHandle().ToInt());
+}
+
+CCSCustomHudLayoutState& CCSCustomHudLayout::GetLayoutState(CCSPlayerController* pController)
+{
+ if (!pController)
+ return *m_globalLayoutState;
+
+ return *(CCSCustomHudLayoutState*)m_vecPlayerLayoutStates.GetManipulator()(SCHEMA_COLLECTION_MANIPULATOR_ACTION_GET_ELEMENT, m_vecPlayerLayoutStates, pController->GetPlayerSlot(), 0);
+}
+
+void CCSCustomHudLayout::SetHasClass(std::string sPanelId, std::string sClassName, bool bHasClass, CCSPlayerController* pController)
+{
+ auto panelIndex = m_vecPanelIds->Find(sPanelId.c_str());
+
+ if (panelIndex == -1)
+ panelIndex = m_vecPanelIds->AddToTail(sPanelId.c_str());
+
+ auto classIndex = m_vecClassNames->Find(sClassName.c_str());
+
+ if (classIndex == -1)
+ classIndex = m_vecClassNames->AddToTail(sClassName.c_str());
+
+ auto& layoutState = GetLayoutState(pController);
+
+ HUDPanelHasClass_t hasClass(panelIndex, classIndex, bHasClass);
+
+ auto hasClassIndex = layoutState.m_vecHasClasses->Find(hasClass);
+
+ if (hasClassIndex == -1)
+ layoutState.m_vecHasClasses->AddToTail(hasClass);
+ else
+ layoutState.m_vecHasClasses->Element(hasClassIndex).m_eClassStatus = hasClass.m_eClassStatus;
+
+ layoutState.m_vecHasClasses.NetworkStateChanged();
+}
+
+void CCSCustomHudLayout::SetDialogVariableString(std::string sPanelId, std::string sVariableName, std::string sValue, CCSPlayerController* pController)
+{
+ auto panelIndex = m_vecPanelIds->Find(sPanelId.c_str());
+
+ if (panelIndex == -1)
+ panelIndex = m_vecPanelIds->AddToTail(sPanelId.c_str());
+
+ auto variableIndex = m_vecDialogVariableNames->Find(sVariableName.c_str());
+
+ if (variableIndex == -1)
+ variableIndex = m_vecDialogVariableNames->AddToTail(sVariableName.c_str());
+
+ auto& layoutState = GetLayoutState(pController);
+
+ HUDPanelDialogVariableString_t dialogVariable(panelIndex, variableIndex, sValue.c_str(), true);
+
+ auto dialogVariableIndex = layoutState.m_vecDialogVariableStrings->Find(dialogVariable);
+
+ if (dialogVariableIndex == -1)
+ layoutState.m_vecDialogVariableStrings->AddToTail(dialogVariable);
+ else
+ layoutState.m_vecDialogVariableStrings->Element(dialogVariableIndex).m_sValue = sValue.c_str();
+
+ layoutState.m_vecDialogVariableStrings.NetworkStateChanged();
+}
+
+void CCSCustomHudLayout::SetInputCaptureEnabled(bool bEnable, CCSPlayerController* pController)
+{
+ GetLayoutState(pController).m_bInputCaptureEnabled = bEnable;
+}
+
+bool CCSCustomHudLayout::IsInputCaptureEnabled(CCSPlayerController* pController)
+{
+ return GetLayoutState(pController).m_bInputCaptureEnabled;
+}
+
+void CCSCustomHudLayout::AddClickCallback(CustomHudClickCallback_t callback)
+{
+ g_mapClickCallbacks.insert({GetHandle().ToInt(), callback});
+}
diff --git a/src/cs2_sdk/entity/customhudlayout.h b/src/cs2_sdk/entity/customhudlayout.h
new file mode 100644
index 000000000..30f37c5b3
--- /dev/null
+++ b/src/cs2_sdk/entity/customhudlayout.h
@@ -0,0 +1,120 @@
+/**
+ * =============================================================================
+ * 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 "ccsplayercontroller.h"
+#include "cstrike15_usermessages.pb.h"
+
+class CCSCustomHudLayout;
+
+using CustomHudClickCallback_t = std::function;
+
+enum EHudPanelClassStatus_t : int
+{
+ k_eHudPanelClassStatus_Undefined = -1,
+ k_eHudPanelClassStatus_DoesNotHaveClass = 0,
+ k_eHudPanelClassStatus_HasClass = 1,
+};
+
+struct HUDPanelDialogVariableString_t
+{
+private:
+ virtual void unk00() {}
+
+public:
+ HUDPanelDialogVariableString_t(uint16 nPanelIdIndex, uint16 nDialogVariableIndex, CUtlString sValue, bool bIsSet) :
+ m_nPanelIdIndex(nPanelIdIndex), m_nDialogVariableIndex(nDialogVariableIndex), m_sValue(sValue), m_bIsSet(bIsSet)
+ {
+ // Since we're constructing a new object, the vtable pointer will be incorrect so fix it
+ static const auto pVTable = modules::server->FindVirtualTable("HUDPanelDialogVariableString_t");
+ ((void**)this)[0] = pVTable;
+ }
+
+ bool operator==(const HUDPanelDialogVariableString_t& other) const
+ {
+ return m_nPanelIdIndex == other.m_nPanelIdIndex && m_nDialogVariableIndex == other.m_nDialogVariableIndex;
+ }
+
+ uint16 m_nPanelIdIndex;
+ uint16 m_nDialogVariableIndex;
+ CUtlString m_sValue;
+ bool m_bIsSet;
+};
+
+struct HUDPanelHasClass_t
+{
+public:
+ HUDPanelHasClass_t(uint16 nPanelIdIndex, uint16 nClassNameIndex, bool bHasClass) :
+ m_nPanelIdIndex(nPanelIdIndex), m_nClassNameIndex(nClassNameIndex), m_eClassStatus((EHudPanelClassStatus_t)bHasClass)
+ {
+ }
+
+ bool operator==(const HUDPanelHasClass_t& other) const
+ {
+ return m_nPanelIdIndex == other.m_nPanelIdIndex && m_nClassNameIndex == other.m_nClassNameIndex;
+ }
+
+ uint16 m_nPanelIdIndex;
+ uint16 m_nClassNameIndex;
+ EHudPanelClassStatus_t m_eClassStatus;
+};
+
+class CCSCustomHudLayoutState
+{
+public:
+ DECLARE_SCHEMA_CLASS_INLINE(CCSCustomHudLayoutState)
+
+ SCHEMA_FIELD(int, m_playerSlot)
+ SCHEMA_FIELD(bool, m_bInputCaptureEnabled)
+ SCHEMA_FIELD_POINTER(CUtlVector, m_vecHasClasses)
+ SCHEMA_FIELD_POINTER(CUtlVector, m_vecDialogVariableStrings)
+};
+
+class CCSCustomHudLayout : public CBaseEntity
+{
+public:
+ DECLARE_SCHEMA_CLASS(CCSCustomHudLayout)
+
+ SCHEMA_FIELD(CUtlSymbolLarge, m_strLayout);
+
+ // Is actually a CUtlVectorEmbeddedNetworkVar with a different structure from a normal CUtlVector, get elements using the manipulator function
+ SCHEMA_FIELD_POINTER(CUtlVector, m_vecPlayerLayoutStates);
+
+ SCHEMA_FIELD_POINTER(CCSCustomHudLayoutState, m_globalLayoutState);
+ SCHEMA_FIELD_POINTER(CUtlVector, m_vecPanelIds);
+ SCHEMA_FIELD_POINTER(CUtlVector, m_vecClassNames);
+ SCHEMA_FIELD_POINTER(CUtlVector, m_vecDialogVariableNames);
+
+ static CCSCustomHudLayout* Create(std::string sLayout, std::string sTargetName = "");
+ static void ClearClickCallbacks();
+
+ CCSCustomHudLayoutState& GetLayoutState(CCSPlayerController* pController = nullptr);
+
+ void SetHasClass(std::string sPanelId, std::string sClassName, bool bHasClass, CCSPlayerController* pController = nullptr);
+ void SetDialogVariableString(std::string sPanelId, std::string sVariableName, std::string sValue, CCSPlayerController* pController = nullptr);
+ void SetInputCaptureEnabled(bool bEnable, CCSPlayerController* pController);
+
+ bool IsInputCaptureEnabled(CCSPlayerController* pController);
+
+ void AddClickCallback(CustomHudClickCallback_t callback);
+ void OnClick(CCSPlayerController* pController, const std::string& sButtonId);
+
+ void OnEntityDeleted();
+};
\ No newline at end of file
diff --git a/src/cs2_sdk/schema.cpp b/src/cs2_sdk/schema.cpp
index 7349ed6cd..b3c027f32 100644
--- a/src/cs2_sdk/schema.cpp
+++ b/src/cs2_sdk/schema.cpp
@@ -96,6 +96,9 @@ static void InitSchemaKeyValueMap(SchemaClassInfoData_t* pClassInfo, SchemaKeyVa
keyValuePair.second.offset = field.m_nSingleInheritanceOffset;
keyValuePair.second.networked = IsFieldNetworked(pClassInfo->m_pszName, field);
+ if (field.m_pType->m_eTypeCategory == SCHEMA_TYPE_ATOMIC && field.m_pType->m_eAtomicCategory == SCHEMA_ATOMIC_COLLECTION_OF_T)
+ keyValuePair.second.manipulator = static_cast(field.m_pType)->m_pfnManipulator;
+
keyValueMap.insert(keyValuePair);
}
@@ -106,12 +109,7 @@ static void InitSchemaKeyValueMap(SchemaClassInfoData_t* pClassInfo, SchemaKeyVa
static bool InitSchemaFieldsForClass(SchemaTableMap_t& tableMap, const char* className, uint32_t classKey)
{
- CSchemaSystemTypeScope* pType = g_pSchemaSystem->FindTypeScopeForModule(MODULE_PREFIX "server" MODULE_EXT);
-
- if (!pType)
- return false;
-
- SchemaClassInfoData_t* pClassInfo = pType->FindDeclaredClass(className).Get();
+ SchemaClassInfoData_t* pClassInfo = schema::GetSchemaClassInfo(className);
if (!pClassInfo)
{
@@ -159,6 +157,16 @@ SchemaKey schema::GetOffset(const char* className, uint32_t classKey, const char
return tableMap[memberKey];
}
+SchemaClassInfoData_t* schema::GetSchemaClassInfo(const char* className)
+{
+ static CSchemaSystemTypeScope* pType = g_pSchemaSystem->FindTypeScopeForModule(MODULE_PREFIX "server" MODULE_EXT);
+
+ if (!pType)
+ return nullptr;
+
+ return pType->FindDeclaredClass(className).Get();
+}
+
void NetworkVarStateChanged(uintptr_t pNetworkVar, uint32_t nOffset, uint32 nNetworkStateChangedOffset)
{
NetworkStateChangedData data(nOffset);
diff --git a/src/cs2_sdk/schema.h b/src/cs2_sdk/schema.h
index e6a164093..00fc1a2c6 100644
--- a/src/cs2_sdk/schema.h
+++ b/src/cs2_sdk/schema.h
@@ -41,6 +41,7 @@ struct SchemaKey
{
int32 offset;
bool networked;
+ SchemaCollectionManipulatorFn_t manipulator = nullptr;
};
class CNetworkVarChainer
@@ -66,6 +67,7 @@ namespace schema
{
int16_t FindChainOffset(const char* className, uint32_t classNameHash);
SchemaKey GetOffset(const char* className, uint32_t classKey, const char* memberName, uint32_t memberKey);
+ SchemaClassInfoData_t* GetSchemaClassInfo(const char* className);
} // namespace schema
constexpr uint32_t val_32_const = 0x811c9dc5;
@@ -193,6 +195,11 @@ inline constexpr uint64_t hash_64_fnv1a_const(const char* const str, const uint6
::NetworkVarStateChanged(pThisClass, m_key.offset + extra_offset, m_networkStateChangedOffset); \
} \
} \
+ SchemaCollectionManipulatorFn_t GetManipulator() \
+ { \
+ static const auto m_key = schema::GetOffset(m_className, m_classNameHash, #varName, m_varNameHash); \
+ return m_key.manipulator; \
+ } \
operator type*() \
{ \
return Get(); \
@@ -221,14 +228,27 @@ inline constexpr uint64_t hash_64_fnv1a_const(const char* const str, const uint6
SCHEMA_FIELD_POINTER_OFFSET(type, varName, 0)
// If the class needs a specific offset for its NetworkStateChanged (like CEconItemView), use this and provide the offset
-#define DECLARE_SCHEMA_CLASS_BASE(ClassName, offset) \
-private: \
- typedef ClassName ThisClass; \
- static constexpr const char* m_className = #ClassName; \
- static constexpr uint32_t m_classNameHash = hash_32_fnv1a_const(#ClassName); \
- static constexpr int m_networkStateChangedOffset = offset; \
- \
-public:
+#define DECLARE_SCHEMA_CLASS_BASE(ClassName, offset) \
+private: \
+ typedef ClassName ThisClass; \
+ static constexpr const char* m_className = #ClassName; \
+ static constexpr uint32_t m_classNameHash = hash_32_fnv1a_const(#ClassName); \
+ static constexpr int m_networkStateChangedOffset = offset; \
+ \
+public: \
+ static ThisClass* Create() \
+ { \
+ static auto pClassInfo = schema::GetSchemaClassInfo(m_className); \
+ static auto pfnManipulator = pClassInfo->m_pfnManipulator; \
+ ThisClass* pThis = (ThisClass*)pfnManipulator(SCHEMA_CLASS_MANIPULATOR_ACTION_ALLOCATE, nullptr); \
+ return pThis; \
+ } \
+ void Construct() \
+ { \
+ static auto pClassInfo = schema::GetSchemaClassInfo(m_className); \
+ static auto pfnManipulator = pClassInfo->m_pfnManipulator; \
+ pfnManipulator(SCHEMA_CLASS_MANIPULATOR_ACTION_CONSTRUCT_IN_PLACE, this); \
+ }
#define DECLARE_SCHEMA_CLASS(className) DECLARE_SCHEMA_CLASS_BASE(className, 0)
diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp
index bb8cd6c1c..833ec487c 100644
--- a/src/cs2fixes.cpp
+++ b/src/cs2fixes.cpp
@@ -22,48 +22,36 @@
#include "adminsystem.h"
#include "appframework/IAppSystem.h"
-#include "cchecktransmitinfo.h"
#include "cfgparser.h"
#include "commands.h"
#include "common.h"
-#include "cs_gameevents.pb.h"
#include "ctimer.h"
#include "cvarwhitelist.h"
-#include "detours.h"
+#include "utils/sighook.h"
#include "discord.h"
#include "entities.h"
-#include "entity/ccsplayercontroller.h"
+#include "entity/customhudlayout.h"
#include "entity/services.h"
#include "entitylistener.h"
#include "entitysystem.h"
#include "entwatch.h"
#include "eventlistener.h"
#include "gameconfig.h"
-#include "gameevents.pb.h"
#include "gamesystem.h"
-#include "httpmanager.h"
#include "hud_manager.h"
#include "icvar.h"
#include "idlemanager.h"
-#include "interface.h"
-#include "leader.h"
#include "map_votes.h"
#include "mapmigrations.h"
#include "networkstringtabledefs.h"
#include "panoramavote.h"
#include "patches.h"
-#include "plat.h"
#include "playermanager.h"
#include "schemasystem/schemasystem.h"
-#include "serversideclient.h"
-#include "te.pb.h"
-#include "tier0/dbg.h"
-#include "tier0/vprof.h"
#include "user_preferences.h"
-#include "usermessages.pb.h"
+#include "virtual_hooks.h"
#include "votemanager.h"
#include "zombiereborn.h"
-#include
//#include "Source2Py.h"
#include "PyRuntime.h"
@@ -76,41 +64,6 @@
#include "tier0/memdbgon.h"
-
-
-class GameSessionConfiguration_t
-{};
-
-SH_DECL_HOOK3_void(IServerGameDLL, GameFrame, SH_NOATTRIB, 0, bool, bool, bool);
-SH_DECL_HOOK0_void(IServerGameDLL, GameServerSteamAPIActivated, SH_NOATTRIB, 0);
-SH_DECL_HOOK1_void(IServerGameDLL, ApplyGameSettings, SH_NOATTRIB, 0, KeyValues*);
-SH_DECL_HOOK4_void(IServerGameClients, ClientActive, SH_NOATTRIB, 0, CPlayerSlot, bool, const char*, uint64);
-SH_DECL_HOOK5_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, CPlayerSlot, ENetworkDisconnectionReason, const char*, uint64, const char*);
-SH_DECL_HOOK4_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, CPlayerSlot, char const*, int, uint64);
-SH_DECL_HOOK1_void(IServerGameClients, ClientSettingsChanged, SH_NOATTRIB, 0, CPlayerSlot);
-SH_DECL_HOOK6_void(IServerGameClients, OnClientConnected, SH_NOATTRIB, 0, CPlayerSlot, const char*, uint64, const char*, const char*, bool);
-SH_DECL_HOOK6(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, CPlayerSlot, const char*, uint64, const char*, bool, CBufferString*);
-SH_DECL_HOOK8_void(IGameEventSystem, PostEventAbstract, SH_NOATTRIB, 0, CSplitScreenSlot, bool, int, const uint64*,
- INetworkMessageInternal*, const CNetMessage*, unsigned long, NetChannelBufType_t)
- SH_DECL_HOOK3_void(INetworkServerService, StartupServer, SH_NOATTRIB, 0, const GameSessionConfiguration_t&, ISource2WorldSession*, const char*);
-SH_DECL_HOOK7_void(ISource2GameEntities, CheckTransmit, SH_NOATTRIB, 0, CCheckTransmitInfo**, int, CBitVec<16384>&, CBitVec<16384>&, const Entity2Networkable_t**, const uint16*, int);
-SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, CPlayerSlot, const CCommand&);
-SH_DECL_HOOK3_void(ICvar, DispatchConCommand, SH_NOATTRIB, 0, ConCommandRef, const CCommandContext&, const CCommand&);
-SH_DECL_MANUALHOOK1_void(CGamePlayerEquipUse, 0, 0, 0, InputData_t*);
-SH_DECL_MANUALHOOK1_void(CGamePlayerEquipPrecache, 0, 0, 0, CEntityPrecacheContext*);
-SH_DECL_MANUALHOOK1_void(CTriggerGravityPrecache, 0, 0, 0, CEntityPrecacheContext*);
-SH_DECL_MANUALHOOK1_void(CTriggerGravityEndTouch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK2_void(CreateWorkshopMapGroup, 0, 0, 0, const char*, const CUtlStringList&);
-SH_DECL_MANUALHOOK1(OnTakeDamage_Alive, 0, 0, 0, bool, CTakeDamageResult*);
-SH_DECL_MANUALHOOK1_void(CheckMovingGround, 0, 0, 0, double);
-SH_DECL_HOOK2(IGameEventManager2, LoadEventsFromFile, SH_NOATTRIB, 0, int, const char*, bool);
-SH_DECL_MANUALHOOK2_void(PhysicsTouchShuffle, 0, 0, 0, CUtlVector*, bool);
-SH_DECL_MANUALHOOK3_void(DropWeapon, 0, 0, 0, CBasePlayerWeapon*, Vector*, Vector*);
-SH_DECL_HOOK1_void(IServer, SetGameSpawnGroupMgr, SH_NOATTRIB, 0, IGameSpawnGroupMgr*);
-SH_DECL_HOOK2_void(CEntitySystem, Spawn, SH_NOATTRIB, 0, int, const EntitySpawnInfo_t*);
-SH_DECL_MANUALHOOK3_void(Teleport, 0, 0, 0, const Vector*, const QAngle*, const Vector*);
-SH_DECL_HOOK1(CServerSideClient, ProcessVoiceData, SH_NOATTRIB, 0, bool, const CCLCMsg_VoiceData_t&);
-
CS2Fixes g_CS2Fixes;
IGameEventSystem* g_gameEventSystem = nullptr;
IGameEventManager2* g_gameEventManager = nullptr;
@@ -119,25 +72,10 @@ 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!
-int g_iCGamePlayerEquipUseId = -1;
-int g_iCGamePlayerEquipPrecacheId = -1;
-int g_iCTriggerGravityPrecacheId = -1;
-int g_iCTriggerGravityEndTouchId = -1;
-int g_iCreateWorkshopMapGroupId = -1;
-int g_iOnTakeDamageAliveId = -1;
-int g_iCheckMovingGroundId = -1;
-int g_iLoadEventsFromFileId = -1;
-int g_iPhysicsTouchShuffle = -1;
-int g_iWeaponServiceDropWeaponId = -1;
-int g_iSetGameSpawnGroupMgrId = -1;
-int g_iSpawnId = -1;
-int g_iTeleportPreId = -1;
-int g_iTeleportPostId = -1;
-int g_iProcessVoiceDataId = -1;
-
double g_flUniversalTime = 0.0;
float g_flLastTickedTime = 0.0f;
bool g_bHasTicked = false;
+bool g_bRequiredInitLoaded = true;
CGameEntitySystem* GameEntitySystem()
{
@@ -358,56 +296,33 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
return false;
}
- int offset = g_GameConfig->GetOffset("IGameTypes_CreateWorkshopMapGroup");
- SH_MANUALHOOK_RECONFIGURE(CreateWorkshopMapGroup, offset, 0, 0);
-
- SH_ADD_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameFramePost), true);
- SH_ADD_HOOK(IServerGameDLL, GameServerSteamAPIActivated, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameServerSteamAPIActivated), false);
- SH_ADD_HOOK(IServerGameDLL, ApplyGameSettings, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_ApplyGameSettings), false);
- SH_ADD_HOOK(IServerGameClients, ClientActive, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientActive), true);
- SH_ADD_HOOK(IServerGameClients, ClientDisconnect, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientDisconnect), true);
- SH_ADD_HOOK(IServerGameClients, ClientPutInServer, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientPutInServer), true);
- SH_ADD_HOOK(IServerGameClients, ClientSettingsChanged, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientSettingsChanged), false);
- SH_ADD_HOOK(IServerGameClients, OnClientConnected, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_OnClientConnected), false);
- SH_ADD_HOOK(IServerGameClients, ClientConnect, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientConnect), false);
- SH_ADD_HOOK(IServerGameClients, ClientCommand, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientCommand), false);
- SH_ADD_HOOK(IGameEventSystem, PostEventAbstract, g_gameEventSystem, SH_MEMBER(this, &CS2Fixes::Hook_PostEvent), false);
- SH_ADD_HOOK(INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER(this, &CS2Fixes::Hook_StartupServer), true);
- SH_ADD_HOOK(ISource2GameEntities, CheckTransmit, g_pSource2GameEntities, SH_MEMBER(this, &CS2Fixes::Hook_CheckTransmit), true);
- SH_ADD_HOOK(ICvar, DispatchConCommand, g_pCVar, SH_MEMBER(this, &CS2Fixes::Hook_DispatchConCommand), false);
- g_iCreateWorkshopMapGroupId = SH_ADD_MANUALVPHOOK(CreateWorkshopMapGroup, g_pGameTypes, SH_MEMBER(this, &CS2Fixes::Hook_CreateWorkshopMapGroup), false);
-
- META_CONPRINTF("All hooks started!\n");
-
- bool bRequiredInitLoaded = true;
-
- if (!addresses::Initialize(g_GameConfig))
- bRequiredInitLoaded = false;
+ if (!addresses::Initialize())
+ g_bRequiredInitLoaded = false;
if (!addresses::InitializeVScriptFunctions())
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
- if (!InitPatches(g_GameConfig))
- bRequiredInitLoaded = false;
+ InitVirtualHooks();
+ InitSigHooks();
- if (!InitDetours(g_GameConfig))
- bRequiredInitLoaded = false;
+ if (!InitPatches())
+ g_bRequiredInitLoaded = false;
if (!InitGameSystems())
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
- //this' all i've added here.
+ //this' all i've added here.
{
fs::path exePath = fs::current_path();
fs::current_path(GetPluginBaseDirectory());
if (!Source2Py::PyRuntime::Init())
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
if (!LoadPythonPlugins())
{
Source2Py::PyRuntime::Close();
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
}
// and set the cwd back where the game expects it
@@ -419,14 +334,14 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
if (!pCGamePlayerEquipVTable)
{
snprintf(error, maxlen, "Failed to find CGamePlayerEquip vtable\n");
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
}
offset = g_GameConfig->GetOffset("CBaseEntity::Use");
if (offset == -1)
{
snprintf(error, maxlen, "Failed to find CBaseEntity::Use\n");
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
}
SH_MANUALHOOK_RECONFIGURE(CGamePlayerEquipUse, offset, 0, 0);
g_iCGamePlayerEquipUseId = SH_ADD_MANUALDVPHOOK(CGamePlayerEquipUse, pCGamePlayerEquipVTable, SH_MEMBER(this, &CS2Fixes::Hook_CGamePlayerEquipUse), false);
@@ -435,7 +350,7 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
if (offset == -1)
{
snprintf(error, maxlen, "Failed to find CBaseEntity::Precache\n");
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
}
SH_MANUALHOOK_RECONFIGURE(CGamePlayerEquipPrecache, offset, 0, 0);
g_iCGamePlayerEquipPrecacheId = SH_ADD_MANUALDVPHOOK(CGamePlayerEquipPrecache, pCGamePlayerEquipVTable, SH_MEMBER(this, &CS2Fixes::Hook_CGamePlayerEquipPrecache), true);
@@ -444,94 +359,10 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
if (!pTriggerGravityVTable)
{
snprintf(error, maxlen, "Failed to find TriggerGravity vtable\n");
- bRequiredInitLoaded = false;
- }
-
- offset = g_GameConfig->GetOffset("CBaseEntity::Precache");
- if (offset == -1)
- {
- snprintf(error, maxlen, "Failed to find CBaseEntity::Precache\n");
- bRequiredInitLoaded = false;
- }
- SH_MANUALHOOK_RECONFIGURE(CTriggerGravityPrecache, offset, 0, 0);
- g_iCTriggerGravityPrecacheId = SH_ADD_MANUALDVPHOOK(CTriggerGravityPrecache, pTriggerGravityVTable, SH_MEMBER(this, &CS2Fixes::Hook_CTriggerGravityPrecache), true);
-
- offset = g_GameConfig->GetOffset("CBaseEntity::EndTouch");
- if (offset == -1)
- {
- snprintf(error, maxlen, "Failed to find CBaseEntity::EndTouch\n");
- bRequiredInitLoaded = false;
- }
- SH_MANUALHOOK_RECONFIGURE(CTriggerGravityEndTouch, offset, 0, 0);
- g_iCTriggerGravityEndTouchId = SH_ADD_MANUALDVPHOOK(CTriggerGravityEndTouch, pTriggerGravityVTable, SH_MEMBER(this, &CS2Fixes::Hook_CTriggerGravityEndTouch), true);
-
- const auto pCCSPlayerPawnVTable = modules::server->FindVirtualTable("CCSPlayerPawn");
- if (!pCCSPlayerPawnVTable)
- {
- snprintf(error, maxlen, "Failed to find CCSPlayerPawn vtable\n");
- bRequiredInitLoaded = false;
- }
-
- offset = g_GameConfig->GetOffset("CCSPlayerPawn::OnTakeDamage_Alive");
- if (offset == -1)
- {
- snprintf(error, maxlen, "Failed to find CCSPlayerPawn::OnTakeDamage_Alive\n");
- bRequiredInitLoaded = false;
- }
- SH_MANUALHOOK_RECONFIGURE(OnTakeDamage_Alive, offset, 0, 0);
- g_iOnTakeDamageAliveId = SH_ADD_MANUALDVPHOOK(OnTakeDamage_Alive, pCCSPlayerPawnVTable, SH_MEMBER(this, &CS2Fixes::Hook_OnTakeDamage_Alive), false);
-
- offset = g_GameConfig->GetOffset("Teleport");
- if (offset == -1)
- {
- snprintf(error, maxlen, "Failed to find Teleport\n");
- bRequiredInitLoaded = false;
- }
- SH_MANUALHOOK_RECONFIGURE(Teleport, offset, 0, 0);
- g_iTeleportPreId = SH_ADD_MANUALDVPHOOK(Teleport, pCCSPlayerPawnVTable, SH_MEMBER(this, &CS2Fixes::Hook_CCSPlayerPawn_Teleport), false);
- g_iTeleportPostId = SH_ADD_MANUALDVPHOOK(Teleport, pCCSPlayerPawnVTable, SH_MEMBER(this, &CS2Fixes::Hook_CCSPlayerPawn_Teleport_Post), true);
-
- const auto pCCSPlayer_MovementServicesVTable = modules::server->FindVirtualTable("CCSPlayer_MovementServices");
- offset = g_GameConfig->GetOffset("CCSPlayer_MovementServices::CheckMovingGround");
- if (offset == -1)
- {
- snprintf(error, maxlen, "Failed to find CCSPlayer_MovementServices::CheckMovingGround\n");
- bRequiredInitLoaded = false;
+ g_bRequiredInitLoaded = false;
}
- SH_MANUALHOOK_RECONFIGURE(CheckMovingGround, offset, 0, 0);
- g_iCheckMovingGroundId = SH_ADD_MANUALDVPHOOK(CheckMovingGround, pCCSPlayer_MovementServicesVTable, SH_MEMBER(this, &CS2Fixes::Hook_CheckMovingGround), false);
- auto pCVPhys2WorldVTable = modules::vphysics2->FindVirtualTable("CVPhys2World");
-
- offset = g_GameConfig->GetOffset("CVPhys2World::GetTouchingList");
- if (offset == -1)
- {
- snprintf(error, maxlen, "Failed to find CVPhys2World::GetTouchingList\n");
- bRequiredInitLoaded = false;
- }
- SH_MANUALHOOK_RECONFIGURE(PhysicsTouchShuffle, offset, 0, 0);
- g_iPhysicsTouchShuffle = SH_ADD_MANUALDVPHOOK(PhysicsTouchShuffle, pCVPhys2WorldVTable, SH_MEMBER(this, &CS2Fixes::Hook_PhysicsTouchShuffle), true);
-
- const auto pCCSPlayer_WeaponServicesVTable = modules::server->FindVirtualTable("CCSPlayer_WeaponServices");
- offset = g_GameConfig->GetOffset("CCSPlayer_WeaponServices::DropWeapon");
- if (offset == -1)
- {
- snprintf(error, maxlen, "Failed to find CCSPlayer_WeaponServices::DropWeapon\n");
- bRequiredInitLoaded = false;
- }
- SH_MANUALHOOK_RECONFIGURE(DropWeapon, offset, 0, 0);
- g_iWeaponServiceDropWeaponId = SH_ADD_MANUALDVPHOOK(DropWeapon, pCCSPlayer_WeaponServicesVTable, SH_MEMBER(this, &CS2Fixes::Hook_DropWeaponPost), true);
-
- auto pCGameEventManagerVTable = (IGameEventManager2*)modules::server->FindVirtualTable("CGameEventManager");
- g_iLoadEventsFromFileId = SH_ADD_DVPHOOK(IGameEventManager2, LoadEventsFromFile, pCGameEventManagerVTable, SH_MEMBER(this, &CS2Fixes::Hook_LoadEventsFromFile), false);
-
- auto pCEntitySystemVTable = (CEntitySystem*)modules::server->FindVirtualTable("CGameEntitySystem");
- g_iSpawnId = SH_ADD_DVPHOOK(CEntitySystem, Spawn, pCEntitySystemVTable, SH_MEMBER(this, &CS2Fixes::Hook_Spawn), false);
-
- auto pCServerSideClientVTable = (CServerSideClient*)modules::engine->FindVirtualTable("CServerSideClient");
- g_iProcessVoiceDataId = SH_ADD_DVPHOOK(CServerSideClient, ProcessVoiceData, pCServerSideClientVTable, SH_MEMBER(this, &CS2Fixes::Hook_ProcessVoiceData), false);
-
- if (!bRequiredInitLoaded)
+ if (!g_bRequiredInitLoaded)
{
snprintf(error, maxlen, "One or more address lookups, patches or detours failed, please refer to startup logs for more information");
return false;
@@ -613,48 +444,11 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
bool CS2Fixes::Unload(char* error, size_t maxlen)
{
- SH_REMOVE_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameFramePost), true);
- SH_REMOVE_HOOK(IServerGameDLL, GameServerSteamAPIActivated, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_GameServerSteamAPIActivated), false);
- SH_REMOVE_HOOK(IServerGameDLL, ApplyGameSettings, g_pSource2Server, SH_MEMBER(this, &CS2Fixes::Hook_ApplyGameSettings), false);
- SH_REMOVE_HOOK(IServerGameClients, ClientActive, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientActive), true);
- SH_REMOVE_HOOK(IServerGameClients, ClientDisconnect, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientDisconnect), true);
- SH_REMOVE_HOOK(IServerGameClients, ClientPutInServer, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientPutInServer), true);
- SH_REMOVE_HOOK(IServerGameClients, ClientSettingsChanged, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientSettingsChanged), false);
- SH_REMOVE_HOOK(IServerGameClients, OnClientConnected, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_OnClientConnected), false);
- SH_REMOVE_HOOK(IServerGameClients, ClientConnect, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientConnect), false);
- SH_REMOVE_HOOK(IServerGameClients, ClientCommand, g_pSource2GameClients, SH_MEMBER(this, &CS2Fixes::Hook_ClientCommand), false);
- SH_REMOVE_HOOK(IGameEventSystem, PostEventAbstract, g_gameEventSystem, SH_MEMBER(this, &CS2Fixes::Hook_PostEvent), false);
- SH_REMOVE_HOOK(INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER(this, &CS2Fixes::Hook_StartupServer), true);
- SH_REMOVE_HOOK(ISource2GameEntities, CheckTransmit, g_pSource2GameEntities, SH_MEMBER(this, &CS2Fixes::Hook_CheckTransmit), true);
- SH_REMOVE_HOOK(ICvar, DispatchConCommand, g_pCVar, SH_MEMBER(this, &CS2Fixes::Hook_DispatchConCommand), false);
- SH_REMOVE_HOOK_ID(g_iLoadEventsFromFileId);
- SH_REMOVE_HOOK_ID(g_iCreateWorkshopMapGroupId);
- SH_REMOVE_HOOK_ID(g_iOnTakeDamageAliveId);
- SH_REMOVE_HOOK_ID(g_iCheckMovingGroundId);
- SH_REMOVE_HOOK_ID(g_iPhysicsTouchShuffle);
- SH_REMOVE_HOOK_ID(g_iWeaponServiceDropWeaponId);
- SH_REMOVE_HOOK_ID(g_iCGamePlayerEquipUseId);
- SH_REMOVE_HOOK_ID(g_iCGamePlayerEquipPrecacheId);
- SH_REMOVE_HOOK_ID(g_iCTriggerGravityPrecacheId);
- SH_REMOVE_HOOK_ID(g_iCTriggerGravityEndTouchId);
- SH_REMOVE_HOOK_ID(g_iSpawnId);
- SH_REMOVE_HOOK_ID(g_iTeleportPreId);
- SH_REMOVE_HOOK_ID(g_iTeleportPostId);
- SH_REMOVE_HOOK_ID(g_iProcessVoiceDataId);
-
- if (g_iSetGameSpawnGroupMgrId != -1)
- SH_REMOVE_HOOK_ID(g_iSetGameSpawnGroupMgrId);
-
- if (g_iCGamePlayerEquipPrecacheId != -1)
- SH_REMOVE_HOOK_ID(g_iCGamePlayerEquipPrecacheId);
-
ConVar_Unregister();
+ RemoveVirtualHooks();
UnregisterGameSystem();
-
CommandList().clear();
-
- FlushAllDetours();
UndoPatches();
RemoveAllTimers();
UnregisterEventListeners();
@@ -708,11 +502,7 @@ bool CS2Fixes::Unload(char* error, size_t maxlen)
delete g_pPanoramaVoteHandler;
if (g_pEWHandler)
- {
- g_pEWHandler->RemoveAllUseHooks();
- g_pEWHandler->RemoveAllTriggers();
delete g_pEWHandler;
- }
if (g_pConvarWhitelist)
delete g_pConvarWhitelist;
@@ -726,249 +516,6 @@ bool CS2Fixes::Unload(char* error, size_t maxlen)
return true;
}
-void CS2Fixes::Hook_DispatchConCommand(ConCommandRef cmdHandle, const CCommandContext& ctx, const CCommand& args)
-{
- VPROF_BUDGET("CS2Fixes::Hook_DispatchConCommand", "ConCommands");
-
- if (!g_pEntitySystem)
- RETURN_META(MRES_IGNORED);
-
- auto iCommandPlayerSlot = ctx.GetPlayerSlot();
-
- if (!g_cvarEnableCommands.Get())
- RETURN_META(MRES_IGNORED);
-
- bool bSay = !V_stricmp(args.Arg(0), "say");
- bool bTeamSay = !V_stricmp(args.Arg(0), "say_team");
-
- if (iCommandPlayerSlot != -1 && (bSay || bTeamSay))
- {
- CCSPlayerController* pController = CCSPlayerController::FromSlot(iCommandPlayerSlot);
- ZEPlayer* pPlayer = pController ? pController->GetZEPlayer() : nullptr;
-
- // Block chat messages from players not fully ingame, can be interpreted as console messages
- if (!pPlayer || !pPlayer->IsInGame())
- {
- Message("Blocked chat message from user ID %i not fully in game\n", g_pEngineServer2->GetPlayerUserId(iCommandPlayerSlot).Get());
- RETURN_META(MRES_SUPERCEDE);
- }
-
- bool bGagged = pPlayer->IsGagged();
- bool bFlooding = pPlayer->IsFlooding();
- bool bIsAdmin = pPlayer->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)
- {
- SH_CALL(g_pCVar, &ICvar::DispatchConCommand)
- (cmdHandle, ctx, args);
-
- // Reset idle time if message is sent to chat
- if (g_cvarIdleKickTime.Get() > 0.0f)
- pPlayer->UpdateLastInputTime();
- }
- 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_META(MRES_SUPERCEDE);
- }
-
- RETURN_META(MRES_IGNORED);
-}
-
-CConVar g_cvarMotdUrl("cs2f_motd_url", FCVAR_NONE, "Server MOTD URL, shows up as a \"Server Website\" button in scoreboard", "");
-
-void CS2Fixes::Hook_StartupServer(const GameSessionConfiguration_t& config, ISource2WorldSession* pSession, const char* pszMapName)
-{
- g_pEntitySystem = GameEntitySystem();
- g_pEntitySystem->AddListenerEntity(g_pEntityListener);
-
- if (g_pNetworkServerService->GetIGameServer())
- g_iSetGameSpawnGroupMgrId = SH_ADD_HOOK(IServer, SetGameSpawnGroupMgr, g_pNetworkServerService->GetIGameServer(), SH_MEMBER(this, &CS2Fixes::Hook_SetGameSpawnGroupMgr), false);
-
- 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);
- }
-}
-
-class CGamePlayerEquip;
-void CS2Fixes::Hook_CGamePlayerEquipUse(InputData_t* pInput)
-{
- CGamePlayerEquipHandler::Use(META_IFACEPTR(CGamePlayerEquip), pInput);
- RETURN_META(MRES_IGNORED);
-}
-void CS2Fixes::Hook_CGamePlayerEquipPrecache(CEntityPrecacheContext* param)
-{
- const auto kv = param->m_pKeyValues;
- CGamePlayerEquipHandler::OnPrecache(META_IFACEPTR(CGamePlayerEquip), kv);
- RETURN_META(MRES_IGNORED);
-}
-void CS2Fixes::Hook_CTriggerGravityPrecache(CEntityPrecacheContext* param)
-{
- const auto kv = param->m_pKeyValues;
- CTriggerGravityHandler::OnPrecache(META_IFACEPTR(CBaseEntity), kv);
- RETURN_META(MRES_IGNORED);
-}
-void CS2Fixes::Hook_CTriggerGravityEndTouch(CBaseEntity* pOther)
-{
- CTriggerGravityHandler::OnEndTouch(META_IFACEPTR(CBaseEntity), pOther);
- RETURN_META(MRES_IGNORED);
-}
-void CS2Fixes::Hook_GameServerSteamAPIActivated()
-{
- g_playerManager->OnSteamAPIActivated();
-
- if (g_cvarVoteManagerEnable.Get() && !g_pMapVoteSystem->IsMapListLoaded())
- g_pMapVoteSystem->LoadMapList();
-
- RETURN_META(MRES_IGNORED);
-}
-
-CConVar g_cvarBlockParticleMsgs("cs2f_block_particle_msgs", FCVAR_NONE, "Whether to block CUserMsg_ParticleManager messages to fix lag/crashes, experimental", false);
-
-void CS2Fixes::Hook_PostEvent(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 );
- // Need to explicitly get a pointer to the right function as it's overloaded and SH_CALL can't resolve that
- static void (IGameEventSystem::*PostEventAbstract)(CSplitScreenSlot, bool, int, const uint64*,
- INetworkMessageInternal*, const CNetMessage*, unsigned long, NetChannelBufType_t) = &IGameEventSystem::PostEventAbstract;
-
- 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();
-
- SH_CALL(g_gameEventSystem, PostEventAbstract)
- (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);
- }
-}
-
void CS2Fixes::AllPluginsLoaded()
{
/* This is where we'd do stuff that relies on the mod or other plugins
@@ -1060,158 +607,11 @@ void CS2Fixes::AllPluginsLoaded()
}
}
-}
-
- 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;
-
- uint64 stopSoundMask = g_playerManager->GetStopSoundMask();
- uint64 silenceSoundMask = g_playerManager->GetSilenceSoundMask();
-
- if (!msg->has_source_entity_index())
- return;
-
- CBaseEntity* pSourceEntity = (CBaseEntity*)g_pEntitySystem->GetEntityInstance(CEntityIndex(msg->source_entity_index()));
- int playerSlot = -1;
-
- if (!pSourceEntity)
- return;
-
- 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;
- }
- }
-}
-
-void CS2Fixes::AllPluginsLoaded()
-{
- /* This is where we'd do stuff that relies on the mod or other plugins
- * being initialized (for example, cvars added and events registered).
- */
Message("AllPluginsLoaded\n");
}
-void CS2Fixes::Hook_ClientActive(CPlayerSlot slot, bool bLoadGame, const char* pszName, uint64 xuid)
-{
- Message("Hook_ClientActive(%d, %d, \"%s\", %lli)\n", slot, bLoadGame, pszName, xuid);
-}
-
-void CS2Fixes::Hook_ClientCommand(CPlayerSlot slot, const CCommand& args)
-{
-#ifdef _DEBUG
- Message("Hook_ClientCommand(%d, \"%s\")\n", slot, args.GetCommandString());
-#endif
-
- ZEPlayer* pPlayer = g_playerManager->GetPlayer(slot);
-
- if (g_cvarIdleKickTime.Get() > 0.0f)
- {
- CCSPlayerController* pController = CCSPlayerController::FromSlot(slot);
- if (pPlayer && pController)
- {
- // Only spectators doing spectator commands reset idle timer
- if (pController->m_iTeamNum() == CS_TEAM_SPECTATOR &&
- (!V_stricmp(args[0], "spec_mode") ||
- !V_stricmp(args[0], "spec_prev") ||
- !V_stricmp(args[0], "spec_next")))
- 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_META(MRES_HANDLED);
- else
- RETURN_META(MRES_SUPERCEDE);
- }
-
- if (g_cvarEnableZR.Get() && slot != -1 && !V_strnicmp(args.Arg(0), "jointeam", 8))
- {
- ZR_Hook_ClientCommand_JoinTeam(slot, args);
- RETURN_META(MRES_SUPERCEDE);
- }
-
- for (auto& plugin : g_CS2Fixes.m_Plugins)
- plugin.PyClientCommand(slot.Get(), args.GetCommandString());
-}
-
-void CS2Fixes::Hook_ClientSettingsChanged(CPlayerSlot slot)
-{
-#ifdef _DEBUG
- Message("Hook_ClientSettingsChanged(%d)\n", slot);
-#endif
-}
-
-void CS2Fixes::Hook_OnClientConnected(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);
-}
-
-bool CS2Fixes::Hook_ClientConnect(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_META_VALUE(MRES_SUPERCEDE, false);
-
- RETURN_META_VALUE(MRES_IGNORED, true);
-}
-
+/* TODO: Find new home for Hook_ClientPutInServer and add Py call
void CS2Fixes::Hook_ClientPutInServer(CPlayerSlot slot, char const* pszName, int type, uint64 xuid)
{
for (auto& plugin : g_CS2Fixes.m_Plugins)
@@ -1225,21 +625,11 @@ void CS2Fixes::Hook_ClientPutInServer(CPlayerSlot slot, char const* pszName, int
// 2 - unknown
xuid
);
-
- Message("Hook_ClientPutInServer(%d, \"%s\", %d, %d, %lli)\n", slot, pszName, type, xuid);
-
- if (!g_playerManager->GetPlayer(slot))
- return;
-
- g_playerManager->OnClientPutInServer(slot);
-
- if (g_cvarEnableZR.Get())
- ZR_Hook_ClientPutInServer(slot, pszName, type, xuid);
}
-
+*/
+/* TODO: Find new home for Hook_ClientDisconnect and add Py call
void CS2Fixes::Hook_ClientDisconnect(CPlayerSlot slot, ENetworkDisconnectionReason reason, const char* pszName, uint64 xuid, const char* pszNetworkID)
{
-
for (auto& plugin : g_CS2Fixes.m_Plugins)
plugin.PyClientDisconnect(
slot.Get(),
@@ -1248,52 +638,11 @@ void CS2Fixes::Hook_ClientDisconnect(CPlayerSlot slot, ENetworkDisconnectionReas
xuid,
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;
-
- // 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);
-}
-
+/* TODO: Find new home for Hook_GameFramePost and add Py call (or don't)
void CS2Fixes::Hook_GameFramePost(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;
-
- if (simulating && g_bHasTicked)
- g_flUniversalTime += GetGlobals()->curtime - g_flLastTickedTime;
-
- g_flLastTickedTime = GetGlobals()->curtime;
- g_bHasTicked = true;
-
- RunTimers();
- EntityHandler_OnGameFramePost(simulating, GetGlobals()->tickcount);
for (auto& plugin : g_CS2Fixes.m_Plugins)
plugin.PyGameFrame(
simulating,
@@ -1301,312 +650,7 @@ void CS2Fixes::Hook_GameFramePost(bool simulating, bool bFirstTick, bool bLastTi
bLastTick
);
}
-
-void CS2Fixes::Hook_CheckTransmit(CCheckTransmitInfo** ppInfoList, int infoCount, CBitVec<16384>& unionTransmitEdicts,
- CBitVec<16384>&, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntities)
-{
- if (!g_pEntitySystem || !GetGlobals())
- return;
-
- VPROF("CS2Fixes::Hook_CheckTransmit");
-
- for (int i = 0; i < infoCount; i++)
- {
- auto& pInfo = (CCheckTransmitInfoExtended*&)(ppInfoList[i]);
- CCSPlayerController* pSelfController = CCSPlayerController::FromSlot(pInfo->m_nPlayerSlot);
-
- if (!pSelfController || !pSelfController->IsConnected())
- continue;
-
- auto pSelfZEPlayer = g_playerManager->GetPlayer(pInfo->m_nPlayerSlot);
-
- 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 == pInfo->m_nPlayerSlot.Get())
- 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());
- pInfo->m_pTransmitNonPlayers->Set(pPawn->entindex());
- }
- }
-
- // Don't transmit glow model to it's owner
- CBaseModelEntity* pGlowModel = pSelfZEPlayer->GetGlowModel();
-
- if (pGlowModel)
- pInfo->m_pTransmitEntity->Clear(pGlowModel->entindex());
- }
-}
-
-void CS2Fixes::Hook_ApplyGameSettings(KeyValues* pKV)
-{
- const char* pszMapName;
- uint64 iWorkshopId;
-
- if (pKV->FindKey("launchoptions") && pKV->FindKey("launchoptions")->FindKey("levelname"))
- pszMapName = pKV->FindKey("launchoptions")->GetString("levelname");
- else
- pszMapName = "";
-
- if (pKV->FindKey("launchoptions") && pKV->FindKey("launchoptions")->FindKey("customgamemode"))
- iWorkshopId = pKV->FindKey("launchoptions")->GetUint64("customgamemode");
- else
- iWorkshopId = 0;
-
- g_pCfgParser->ApplyGameSettings(pszMapName);
- g_pMapVoteSystem->ApplyGameSettings(pszMapName, iWorkshopId);
- g_pMapMigrations->ApplyGameSettings(iWorkshopId);
-}
-
-void CS2Fixes::Hook_CreateWorkshopMapGroup(const char* name, const CUtlStringList& mapList)
-{
- if (g_cvarVoteManagerEnable.Get() && g_pMapVoteSystem->IsMapListLoaded())
- RETURN_META_MNEWPARAMS(MRES_HANDLED, CreateWorkshopMapGroup, (name, g_pMapVoteSystem->CreateWorkshopMapGroup()));
- else
- RETURN_META(MRES_IGNORED);
-}
-
-CConVar g_cvarDropMapWeapons("cs2f_drop_map_weapons", FCVAR_NONE, "Whether to force drop map-spawned weapons on death", false);
-
-bool CS2Fixes::Hook_OnTakeDamage_Alive(CTakeDamageResult* pDamageResult)
-{
- CCSPlayerPawn* pPawn = META_IFACEPTR(CCSPlayerPawn);
-
- if (g_cvarEnableZR.Get() && ZR_Hook_OnTakeDamage_Alive(pDamageResult->m_pOriginatingInfo, pPawn))
- {
- pDamageResult->m_bWasDamageSuppressed = true;
- pDamageResult->m_flDamageDealt = 0.0f;
- RETURN_META_VALUE(MRES_SUPERCEDE, 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_META_VALUE(MRES_IGNORED, true);
-}
-
-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");
-void CS2Fixes::Hook_PhysicsTouchShuffle(CUtlVector* pList, bool unknown)
-{
- if (!g_cvarFixPhysicsPlayerShuffle.Get() || g_SHPtr->GetStatus() == MRES_SUPERCEDE || pList->Count() <= 1)
- return;
-
- // [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;
-
- 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);
-}
-
-void CS2Fixes::Hook_CheckMovingGround(double frametime)
-{
- CCSPlayer_MovementServices* pMove = META_IFACEPTR(CCSPlayer_MovementServices);
- CCSPlayerPawn* pPawn = pMove->GetPawn();
-
- if (!pPawn || !GetGlobals())
- RETURN_META(MRES_IGNORED);
-
- CCSPlayerController* pController = pPawn->GetOriginalController();
-
- if (!pController)
- RETURN_META(MRES_IGNORED);
-
- 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_META(MRES_SUPERCEDE);
-
- aPlayerTicks[iSlot] = GetGlobals()->tickcount;
-
- RETURN_META(MRES_IGNORED);
-}
-
-void CS2Fixes::Hook_DropWeaponPost(CBasePlayerWeapon* pWeapon, Vector* pVecTarget, Vector* pVelocity)
-{
- if (g_cvarEnableEntWatch.Get())
- {
- CCSPlayer_WeaponServices* pWeaponService = META_IFACEPTR(CCSPlayer_WeaponServices);
- EW_DropWeapon(pWeaponService, pWeapon);
- }
-
- RETURN_META(MRES_IGNORED);
-}
-
-int CS2Fixes::Hook_LoadEventsFromFile(const char* filename, bool bSearchAll)
-{
- ExecuteOnce(g_gameEventManager = META_IFACEPTR(IGameEventManager2));
-
- RETURN_META_VALUE(MRES_IGNORED, 0);
-}
-
-void CS2Fixes::Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr)
-{
- // This also resets our stored pointer on deletion, since null gets passed into this function, nice!
- g_pSpawnGroupMgr = (CSpawnGroupMgrGameSystem*)pSpawnGroupMgr;
-}
-
-void CS2Fixes::Hook_Spawn(int nCount, const EntitySpawnInfo_t* pInfo)
-{
- for (int i = 0; i < nCount; i++)
- g_pMapMigrations->OnEntitySpawned_Pre(reinterpret_cast(pInfo[i].m_pEntity->m_pInstance), pInfo[i].m_pKeyValues);
-}
-
-float g_fTeleportXAngle;
-
-void CS2Fixes::Hook_CCSPlayerPawn_Teleport(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity)
-{
- if (!pAngles)
- RETURN_META(MRES_IGNORED);
-
- g_fTeleportXAngle = pAngles->x;
-
- QAngle* pCastAngles = const_cast(pAngles);
-
- // Post-AG2, changing x or z angles via Teleport on a playermodel will bug out, go through SnapViewAngles for x later instead
- pCastAngles->x = 0.0f;
- pCastAngles->z = 0.0f;
-
- RETURN_META(MRES_HANDLED);
-}
-
-void CS2Fixes::Hook_CCSPlayerPawn_Teleport_Post(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity)
-{
- if (!pAngles)
- RETURN_META(MRES_IGNORED);
-
- // Revert the x edit, z should always be 0
- QAngle* pCastAngles = const_cast(pAngles);
- pCastAngles->x = g_fTeleportXAngle;
-
- META_IFACEPTR(CCSPlayerPawn)->SnapViewAngles(pCastAngles);
- RETURN_META(MRES_HANDLED);
-}
-
-bool CS2Fixes::Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg)
-{
- CServerSideClient* client = META_IFACEPTR(CServerSideClient);
-
- if (!client)
- RETURN_META_VALUE(MRES_IGNORED, true);
-
- ZEPlayer* pPlayer = g_playerManager->GetPlayer(client->GetPlayerSlot());
-
- if (!pPlayer)
- RETURN_META_VALUE(MRES_IGNORED, true);
-
- if (pPlayer->IsMuted())
- RETURN_META_VALUE(MRES_SUPERCEDE, true);
-
- if (GetGlobals())
- pPlayer->SetLastVoiceTime(GetGlobals()->curtime);
-
- RETURN_META_VALUE(MRES_IGNORED, true);
-}
+*/
void* CS2Fixes::OnMetamodQuery(const char* iface, int* ret)
{
@@ -1679,7 +723,7 @@ void CS2Fixes::OnLevelInit(char const* pMapName,
// Only patch BotNavIgnore while a map is loaded, else adding bots will crash
if (V_strcmp(pMapName, "error"))
- g_CommonPatches[1].PerformPatch(g_GameConfig);
+ g_CommonPatches[1].PerformPatch();
g_playerManager->SetupInfiniteAmmo();
g_pMapVoteSystem->OnLevelInit(pMapName);
@@ -1706,6 +750,8 @@ void CS2Fixes::OnLevelShutdown()
if (g_cvarVoteManagerEnable.Get())
g_pMapVoteSystem->OnLevelShutdown();
+
+ CCSCustomHudLayout::ClearClickCallbacks();
}
bool CS2Fixes::Pause(char* error, size_t maxlen)
diff --git a/src/cs2fixes.h b/src/cs2fixes.h
index 97b0b7834..217390043 100644
--- a/src/cs2fixes.h
+++ b/src/cs2fixes.h
@@ -29,7 +29,6 @@
#include "steam/isteamhttp.h"
#include
#include
-#include
#include
#include
@@ -44,12 +43,6 @@ struct CTakeDamageInfoContainer;
#include "version_gen_placeholder.h"
#endif
-class CCSPlayer_MovementServices;
-class CServerSideClient;
-struct TouchLinked_t;
-class CCSPlayer_WeaponServices;
-class CBasePlayerWeapon;
-
extern IGameEventSystem* g_gameEventSystem;
extern IGameEventManager2* g_gameEventManager;
extern CGameEntitySystem* g_pEntitySystem;
@@ -57,6 +50,9 @@ extern IVEngineServer2* g_pEngineServer2;
extern CCSGameRules* g_pGameRules;
extern CSpawnGroupMgrGameSystem* g_pSpawnGroupMgr;
extern double g_flUniversalTime;
+extern float g_flLastTickedTime;
+extern bool g_bHasTicked;
+extern bool g_bRequiredInitLoaded;
extern INetworkGameServer* GetNetworkGameServer();
extern CGlobalVars* GetGlobals();
extern CConVar g_cvarDropMapWeapons;
@@ -65,15 +61,10 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener, public ICS2Fixes
{
public:
bool Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool late);
- void Hook_PostEvent(CSplitScreenSlot nSlot, bool bLocalOnly, int nClientCount, const uint64* clients,
- INetworkMessageInternal* pEvent, const CNetMessage* pData, unsigned long nSize, NetChannelBufType_t bufType);
bool Unload(char* error, size_t maxlen);
bool Pause(char* error, size_t maxlen);
bool Unpause(char* error, size_t maxlen);
void AllPluginsLoaded();
-
-public: // hooks
- void Hook_GameServerSteamAPIActivated();
void OnLevelInit(char const* pMapName,
char const* pMapEntities,
char const* pOldLevel,
@@ -81,42 +72,6 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener, public ICS2Fixes
bool loadGame,
bool background);
void OnLevelShutdown();
- void Hook_GameFramePost(bool simulating, bool bFirstTick, bool bLastTick);
- void Hook_ClientActive(CPlayerSlot slot, bool bLoadGame, const char* pszName, uint64 xuid);
- void Hook_ClientDisconnect(CPlayerSlot slot, ENetworkDisconnectionReason reason, const char* pszName, uint64 xuid, const char* pszNetworkID);
- void Hook_ClientPutInServer(CPlayerSlot slot, char const* pszName, int type, uint64 xuid);
- void Hook_ClientSettingsChanged(CPlayerSlot slot);
- void Hook_OnClientConnected(CPlayerSlot slot, const char* pszName, uint64 xuid, const char* pszNetworkID, const char* pszAddress, bool bFakePlayer);
- bool Hook_ClientConnect(CPlayerSlot slot, const char* pszName, uint64 xuid, const char* pszNetworkID, bool unk1, CBufferString* pRejectReason);
- void Hook_ClientCommand(CPlayerSlot nSlot, const CCommand& _cmd);
-
- void Hook_CheckTransmit(CCheckTransmitInfo** ppInfoList, int infoCount, CBitVec<16384>& unionTransmitEdicts,
- CBitVec<16384>&, const Entity2Networkable_t** pNetworkables, const uint16* pEntityIndicies, int nEntities);
- void Hook_DispatchConCommand(ConCommandRef cmd, const CCommandContext& ctx, const CCommand& args);
- void Hook_CGamePlayerEquipUse(class InputData_t*);
- void Hook_CGamePlayerEquipPrecache(CEntityPrecacheContext*);
- void Hook_CTriggerGravityPrecache(CEntityPrecacheContext* param);
- void Hook_CTriggerGravityEndTouch(CBaseEntity* pOther);
- void Hook_StartupServer(const GameSessionConfiguration_t& config, ISource2WorldSession*, const char*);
- void Hook_ApplyGameSettings(KeyValues* pKV);
- void Hook_CreateWorkshopMapGroup(const char* name, const CUtlStringList& mapList);
- bool Hook_OnTakeDamage_Alive(CTakeDamageResult* pDamageResult);
- void Hook_PhysicsTouchShuffle(CUtlVector* pList, bool unknown);
- void Hook_CCSPlayerPawn_Teleport(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity);
- void Hook_CCSPlayerPawn_Teleport_Post(const Vector* pPosition, const QAngle* pAngles, const Vector* pVelocity);
-#ifdef PLATFORM_WINDOWS
- Vector* Hook_GetEyePosition(Vector*);
- QAngle* Hook_GetEyeAngles(QAngle*);
-#else
- Vector Hook_GetEyePosition();
- QAngle Hook_GetEyeAngles();
-#endif
- void Hook_CheckMovingGround(double frametime);
- void Hook_DropWeaponPost(CBasePlayerWeapon* pWeapon, Vector* pVecTarget, Vector* pVelocity);
- int Hook_LoadEventsFromFile(const char* filename, bool bSearchAll);
- void Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr);
- bool Hook_ProcessVoiceData(const CCLCMsg_VoiceData_t& msg);
- void Hook_Spawn(int nCount, const EntitySpawnInfo_t* pInfo);
public: // MetaMod API
void* OnMetamodQuery(const char* iface, int* ret);
diff --git a/src/detours.cpp b/src/detours.cpp
index eea5ac108..29466c515 100644
--- a/src/detours.cpp
+++ b/src/detours.cpp
@@ -24,13 +24,13 @@
#include "addresses.h"
#include "bspflags.h"
#include "buttonwatch.h"
-#include "cdetour.h"
#include "commands.h"
#include "common.h"
#include "ctimer.h"
#include "customio.h"
#include "cvarwhitelist.h"
#include "detours.h"
+#include "utils/sighook.h"
#include "entities.h"
#include "entity/cbasemodelentity.h"
#include "entity/cbeam.h"
@@ -59,48 +59,58 @@
#include "tier0/memdbgon.h"
-CUtlVector g_vecDetours;
-
-DECLARE_DETOUR(UTIL_SayTextFilter, Detour_UTIL_SayTextFilter);
-DECLARE_DETOUR(UTIL_SayText2Filter, Detour_UTIL_SayText2Filter);
-DECLARE_DETOUR(TriggerPush_Touch, Detour_TriggerPush_Touch);
-DECLARE_DETOUR(CBaseEntity_TakeDamageOld, Detour_CBaseEntity_TakeDamageOld);
-DECLARE_DETOUR(CCSPlayer_WeaponServices_CanUse, Detour_CCSPlayer_WeaponServices_CanUse);
-DECLARE_DETOUR(CCSPlayer_WeaponServices_EquipWeapon, Detour_CCSPlayer_WeaponServices_EquipWeapon);
-DECLARE_DETOUR(CEntityIdentity_AcceptInput, Detour_CEntityIdentity_AcceptInput);
-DECLARE_DETOUR(CNavMesh_GetNearestNavArea, Detour_CNavMesh_GetNearestNavArea);
-DECLARE_DETOUR(ProcessMovement, Detour_ProcessMovement);
-DECLARE_DETOUR(ProcessUsercmds, Detour_ProcessUsercmds);
-DECLARE_DETOUR(CGamePlayerEquip_InputTriggerForAllPlayers, Detour_CGamePlayerEquip_InputTriggerForAllPlayers);
-DECLARE_DETOUR(CGamePlayerEquip_InputTriggerForActivatedPlayer, Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer);
-DECLARE_DETOUR(CTriggerGravity_GravityTouch, Detour_CTriggerGravity_GravityTouch);
-DECLARE_DETOUR(GetFreeClient, Detour_GetFreeClient);
+SIG_HOOK_FUNCTION(UTIL_SayTextFilter, Detour_UTIL_SayTextFilter, nullptr);
+SIG_HOOK_FUNCTION(UTIL_SayText2Filter, Detour_UTIL_SayText2Filter, nullptr);
+SIG_HOOK_MEMBER(TriggerPush_Touch, Detour_TriggerPush_Touch, nullptr);
+SIG_HOOK_MEMBER(CBaseEntity_TakeDamageOld, Detour_CBaseEntity_TakeDamageOld, Detour_CBaseEntity_TakeDamageOld_Post);
+SIG_HOOK_MEMBER(CCSPlayer_WeaponServices_CanUse, Detour_CCSPlayer_WeaponServices_CanUse, nullptr);
+SIG_HOOK_MEMBER(CCSPlayer_WeaponServices_EquipWeapon, Detour_CCSPlayer_WeaponServices_EquipWeapon, nullptr);
+SIG_HOOK_MEMBER(CEntityIdentity_AcceptInput, Detour_CEntityIdentity_AcceptInput, nullptr);
+SIG_HOOK_MEMBER(CNavMesh_GetNearestNavArea, Detour_CNavMesh_GetNearestNavArea, nullptr);
+SIG_HOOK_MEMBER(ProcessMovement, Detour_ProcessMovement, Detour_ProcessMovement_Post);
+SIG_HOOK_MEMBER(ProcessUsercmds, Detour_ProcessUsercmds, nullptr);
+SIG_HOOK_MEMBER(CGamePlayerEquip_InputTriggerForAllPlayers, Detour_CGamePlayerEquip_InputTriggerForAllPlayers, nullptr);
+SIG_HOOK_MEMBER(CGamePlayerEquip_InputTriggerForActivatedPlayer, Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer, nullptr);
+SIG_HOOK_MEMBER(CTriggerGravity_GravityTouch, Detour_CTriggerGravity_GravityTouch, nullptr);
+SIG_HOOK_FUNCTION(GetFreeClient, Detour_GetFreeClient, nullptr);
#ifdef __linux__
// Inlined by MSVC as of 2025-07-28 CS2 update
// TODO: Find some alternative that supports Windows
-DECLARE_DETOUR(CCSPlayerPawn_GetMaxSpeed, Detour_CCSPlayerPawn_GetMaxSpeed);
+SIG_HOOK_MEMBER(CCSPlayerPawn_GetMaxSpeed, Detour_CCSPlayerPawn_GetMaxSpeed, nullptr);
#endif
-DECLARE_DETOUR(FindUseEntity, Detour_FindUseEntity);
-DECLARE_DETOUR(TraceFunc, Detour_TraceFunc);
-DECLARE_DETOUR(TraceShape, Detour_TraceShape);
-DECLARE_DETOUR(CBasePlayerPawn_GetEyePosition, Detour_CBasePlayerPawn_GetEyePosition);
-DECLARE_DETOUR(CBasePlayerPawn_GetEyeAngles, Detour_CBasePlayerPawn_GetEyeAngles);
-DECLARE_DETOUR(CBaseFilter_InputTestActivator, Detour_CBaseFilter_InputTestActivator);
-DECLARE_DETOUR(GameSystem_Think_CheckSteamBan, Detour_GameSystem_Think_CheckSteamBan);
-DECLARE_DETOUR(CCSPlayer_ItemServices_CanAcquire, Detour_CCSPlayer_ItemServices_CanAcquire);
-DECLARE_DETOUR(CS_Script_SetModel, Detour_CS_Script_SetModel);
-DECLARE_DETOUR(CBaseModelEntity_SetModel, Detour_CBaseModelEntity_SetModel);
-DECLARE_DETOUR(CCSGameRules_GoToIntermission, Detour_CCSGameRules_GoToIntermission);
-DECLARE_DETOUR(SetBeamOrigin, Detour_SetBeamOrigin);
-DECLARE_DETOUR(SetBeamEndPos, Detour_SetBeamEndPos);
-DECLARE_DETOUR(IsCommandWhitelisted, Detour_IsCommandWhitelisted);
+SIG_HOOK_MEMBER(FindUseEntity, Detour_FindUseEntity, Detour_FindUseEntity_Post);
+SIG_HOOK_FUNCTION(TraceFunc, Detour_TraceFunc, nullptr);
+SIG_HOOK_FUNCTION(TraceShape, Detour_TraceShape, nullptr);
+SIG_HOOK_MEMBER(CBasePlayerPawn_GetEyePosition, Detour_CBasePlayerPawn_GetEyePosition, nullptr);
+SIG_HOOK_MEMBER(CBasePlayerPawn_GetEyeAngles, Detour_CBasePlayerPawn_GetEyeAngles, nullptr);
+SIG_HOOK_MEMBER(CBaseFilter_InputTestActivator, Detour_CBaseFilter_InputTestActivator, nullptr);
+SIG_HOOK_FUNCTION(GameSystem_Think_CheckSteamBan, Detour_GameSystem_Think_CheckSteamBan, Detour_GameSystem_Think_CheckSteamBan_Post);
+SIG_HOOK_MEMBER(CCSPlayer_ItemServices_CanAcquire, Detour_CCSPlayer_ItemServices_CanAcquire, nullptr);
+SIG_HOOK_FUNCTION(CS_Script_SetModel, Detour_CS_Script_SetModel, Detour_CS_Script_SetModel_Post);
+SIG_HOOK_MEMBER(CBaseModelEntity_SetModel, Detour_CBaseModelEntity_SetModel, nullptr);
+SIG_HOOK_MEMBER(CCSGameRules_GoToIntermission, Detour_CCSGameRules_GoToIntermission, nullptr);
+SIG_HOOK_MEMBER(SetBeamOrigin, Detour_SetBeamOrigin, nullptr);
+SIG_HOOK_MEMBER(SetBeamEndPos, Detour_SetBeamEndPos, nullptr);
+SIG_HOOK_FUNCTION(IsCommandWhitelisted, Detour_IsCommandWhitelisted, nullptr);
+
+std::vector& GetSigHookList()
+{
+ static std::vector s_vecSigHooks;
+ return s_vecSigHooks;
+}
+
+void InitSigHooks()
+{
+ for (auto hook : GetSigHookList())
+ hook->Configure();
+}
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);
CConVar g_cvarPropDamageScale("cs2f_prop_dmg_scale", FCVAR_NONE, "Multiplier on prop damage", 1.0f, true, 0.0f, false, 0.0f);
-int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult)
+KHook::Return Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult)
{
// NOTE valve always return 1 here, since 2025/10/15 update.
@@ -123,7 +133,7 @@ int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageI
// Block all player damage if desired
if (g_cvarBlockAllDamage.Get() && pThis->IsPawn())
- return 1;
+ return {KHook::Action::Supersede, 1};
CEntityInstance* pInflictor = pInfo->m_hInflictor.Get();
const char* pszInflictorClass = pInflictor ? pInflictor->GetClassname() : "";
@@ -149,11 +159,11 @@ int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageI
// Prevent molly on self
if (g_cvarBlockMolotovSelfDmg.Get() && pInfo->m_hAttacker == pThis && !V_strncmp(pszInflictorClass, "inferno", 7))
- return 1;
+ 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 1;
+ return {KHook::Action::Supersede, 1};
if (!V_strncasecmp(pszInflictorClass, "prop_physics", 12))
{
@@ -167,50 +177,47 @@ int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageI
if (pResult == nullptr)
{
damageResult.CopyFrom(pInfo);
- pResult = &damageResult;
+ return KHook::Recall(nullptr, {KHook::Action::Ignore}, pThis, pInfo, &damageResult);
}
- CBaseEntity_TakeDamageOld(pThis, pInfo, pResult);
+ return {KHook::Action::Ignore};
+}
- if (pResult->m_flDamageDealt > 0.0f && !pResult->m_bWasDamageSuppressed && g_cvarEnableZR.Get() && pThis->IsPawn())
+KHook::Return Detour_CBaseEntity_TakeDamageOld_Post(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult)
+{
+ if (!KHook::WasOriginalFunctionSkipped() && pResult->m_flDamageDealt > 0.0f && !pResult->m_bWasDamageSuppressed && g_cvarEnableZR.Get() && pThis->IsPawn())
ZR_OnPlayerTakeDamage(reinterpret_cast(pThis), pInfo, pResult->m_flDamageDealt);
- return 1;
+ 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);
-void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, CBaseEntity* pOther)
+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())
- {
- TriggerPush_Touch(pPush, pOther);
- return;
- }
+ return {KHook::Action::Ignore};
MoveType_t movetype = pOther->m_nActualMoveType();
// VPhysics handling doesn't need any changes
if (movetype == MOVETYPE_VPHYSICS)
- {
- TriggerPush_Touch(pPush, pOther);
- return;
- }
+ return {KHook::Action::Ignore};
if (movetype == MOVETYPE_NONE || movetype == MOVETYPE_PUSH || movetype == MOVETYPE_NOCLIP)
- return;
+ return {KHook::Action::Supersede};
CCollisionProperty* collisionProp = pOther->m_pCollision();
if (!IsSolid(collisionProp->m_nSolidType(), collisionProp->m_usSolidFlags()))
- return;
+ return {KHook::Action::Supersede};
if (!pPush->PassesTriggerFilters(pOther))
- return;
+ return {KHook::Action::Supersede};
if (pOther->m_CBodyComponent()->m_pSceneNode()->m_pParent())
- return;
+ return {KHook::Action::Supersede};
Vector vecAbsDir;
matrix3x4_t matTransform = pPush->m_CBodyComponent()->m_pSceneNode()->EntityToWorldTransform();
@@ -253,15 +260,14 @@ void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, CBaseEntity* pOther)
flags |= (1 << 23); // TODO: is FL_BASEVELOCITY really gone?
pOther->m_fFlags(flags);
+
+ return {KHook::Action::Supersede};
}
-void SayChatMessageWithTimer(IRecipientFilter& filter, const char* pText, CCSPlayerController* pPlayer, uint64 eMessageType)
+KHook::Return SayChatMessageWithTimer(IRecipientFilter& filter, const char* pText, CCSPlayerController* pPlayer, uint64 eMessageType)
{
VPROF("SayChatMessageWithTimer");
- if (!GetGlobals() || !g_pGameRules)
- return;
-
char buf[256];
// Filter console message - remove non-alphanumeric chars and convert to lowercase
@@ -357,29 +363,29 @@ void SayChatMessageWithTimer(IRecipientFilter& filter, const char* pText, CCSPla
else
V_snprintf(buf, sizeof(buf), "%s %s", " \7CONSOLE:\4", pText + sizeof("Console:"));
- UTIL_SayTextFilter(filter, buf, pPlayer, eMessageType);
+ 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);
-void FASTCALL Detour_UTIL_SayTextFilter(IRecipientFilter& filter, const char* pText, CCSPlayerController* pPlayer, uint64 eMessageType)
+KHook::Return Detour_UTIL_SayTextFilter(IRecipientFilter& filter, const char* pText, CCSPlayerController* pPlayer, int eMessageType)
{
if (pPlayer)
- return UTIL_SayTextFilter(filter, pText, pPlayer, eMessageType);
+ return {KHook::Action::Ignore};
- if (g_cvarEnableTriggerTimer.Get())
+ 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:"));
- UTIL_SayTextFilter(filter, buf, pPlayer, eMessageType);
+ return KHook::Recall(nullptr, {KHook::Action::Ignore}, filter, buf, pPlayer, eMessageType);
}
-void FASTCALL Detour_UTIL_SayText2Filter(
+KHook::Return Detour_UTIL_SayText2Filter(
IRecipientFilter& filter,
CCSPlayerController* pEntity,
- uint64 eMessageType,
+ int eMessageType,
const char* msg_name,
const char* param1,
const char* param2,
@@ -394,25 +400,25 @@ void FASTCALL Detour_UTIL_SayText2Filter(
Message("Chat from %s to %s: %s\n", param1, target->GetPlayerName().c_str(), param2);
#endif
- UTIL_SayText2Filter(filter, pEntity, eMessageType, msg_name, pEntity->GetPlayerName().c_str(), param2, param3, param4);
+ return KHook::Recall(nullptr, {KHook::Action::Ignore}, filter, pEntity, eMessageType, msg_name, pEntity->GetPlayerName().c_str(), param2, param3, param4);
}
-bool FASTCALL Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon)
+KHook::Return Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon)
{
if (g_cvarEnableEntWatch.Get() && !EW_Detour_CCSPlayer_WeaponServices_CanUse(pWeaponServices, pPlayerWeapon))
- return false;
+ return {KHook::Action::Supersede, false};
- return CCSPlayer_WeaponServices_CanUse(pWeaponServices, pPlayerWeapon);
+ return {KHook::Action::Ignore};
}
-void FASTCALL Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon)
+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 CCSPlayer_WeaponServices_EquipWeapon(pWeaponServices, 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);
@@ -433,7 +439,7 @@ bool PrepareMapSetModel(CBaseModelEntity* pModel)
return true;
}
-bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, void* a6, void* a7)
+KHook::Return Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, void* a6, void* a7)
{
VPROF_SCOPE_BEGIN("Detour_CEntityIdentity_AcceptInput");
@@ -442,7 +448,7 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym
bool result = ZR_Detour_CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value);
if (!result)
- return result;
+ return {KHook::Action::Supersede, result};
}
// Handle KeyValue(s)
@@ -451,10 +457,10 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym
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 CustomIO_HandleInput(pThis->m_pInstance, value->m_pszString, pActivator, pCaller);
+ 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 false;
+ return {KHook::Action::Supersede, false};
}
if (!V_strnicmp(pInputName->String(), "IgniteL", 7)) // Override IgniteLifetime
@@ -469,7 +475,7 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym
CCSPlayerPawn* pPawn = reinterpret_cast(pThis->m_pInstance);
if (pPawn->IsPawn() && IgnitePawn(pPawn, flDuration, pPawn, pPawn))
- return true;
+ return {KHook::Action::Supersede, true};
}
else if (!V_strnicmp(pInputName->String(), "AddScore", 8))
{
@@ -485,7 +491,7 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym
if (pPawn->IsPawn() && pPawn->GetOriginalController())
{
pPawn->GetOriginalController()->AddScore(iScore);
- return true;
+ return {KHook::Action::Supersede, true};
}
}
else if (!V_strcasecmp(pInputName->String(), "SetMessage"))
@@ -494,7 +500,7 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym
{
if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString)
pHudHint->m_iszMessage(GameEntitySystem()->AllocPooledString(value->m_pszString));
- return true;
+ return {KHook::Action::Supersede, true};
}
}
else if (!V_strcasecmp(pInputName->String(), "SetModel"))
@@ -504,56 +510,57 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym
if ((value->m_type == FIELD_CSTRING || value->m_type == FIELD_STRING) && value->m_pszString && PrepareMapSetModel(pModelEntity))
pModelEntity->SetModel(value->m_pszString);
- return true;
+ return {KHook::Action::Supersede, true};
}
}
else if (const auto pGameUI = reinterpret_cast(pThis->m_pInstance)->AsGameUI())
{
if (!V_strcasecmp(pInputName->String(), "Activate"))
- return CGameUIHandler::OnActivate(pGameUI, reinterpret_cast(pActivator));
+ return {KHook::Action::Supersede, CGameUIHandler::OnActivate(pGameUI, reinterpret_cast(pActivator))};
if (!V_strcasecmp(pInputName->String(), "Deactivate"))
- return CGameUIHandler::OnDeactivate(pGameUI, reinterpret_cast(pActivator));
+ 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 CPointViewControlHandler::OnEnable(pViewControl, reinterpret_cast(pActivator));
+ return {KHook::Action::Supersede, CPointViewControlHandler::OnEnable(pViewControl, reinterpret_cast(pActivator))};
if (!V_strcasecmp(pInputName->String(), "DisableCamera"))
- return CPointViewControlHandler::OnDisable(pViewControl, reinterpret_cast(pActivator));
+ return {KHook::Action::Supersede, CPointViewControlHandler::OnDisable(pViewControl, reinterpret_cast(pActivator))};
if (!V_strcasecmp(pInputName->String(), "EnableCameraAll"))
- return CPointViewControlHandler::OnEnableAll(pViewControl);
+ return {KHook::Action::Supersede, CPointViewControlHandler::OnEnableAll(pViewControl)};
if (!V_strcasecmp(pInputName->String(), "DisableCameraAll"))
- return CPointViewControlHandler::OnDisableAll(pViewControl);
+ return {KHook::Action::Supersede, CPointViewControlHandler::OnDisableAll(pViewControl)};
}
VPROF_SCOPE_END();
- return CEntityIdentity_AcceptInput(pThis, pInputName, pActivator, pCaller, value, a6, a7);
+ 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);
-void* FASTCALL Detour_CNavMesh_GetNearestNavArea(int64_t unk1, float* unk2, unsigned int* unk3, unsigned int unk4, int64_t unk5, float unk6, int64_t unk7)
+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 nullptr;
+ return {KHook::Action::Supersede, nullptr};
- return CNavMesh_GetNearestNavArea(unk1, unk2, unk3, unk4, unk5, unk6, unk7);
+ return {KHook::Action::Ignore};
}
CConVar g_cvarAllowDuckSpam("cs2f_allow_duck_spam", FCVAR_NONE, "Whether to allow duck spamming by removing the duck slowdown, clients will only partially predict [0 = disabled, 1 = both teams, 2 = T only, 3 = CT only]", 0, true, 0, true, CS_TEAM_CT);
+float g_flStoreFrametime = 0.0f;
-void FASTCALL Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove)
+KHook::Return Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove)
{
CCSPlayerPawn* pPawn = pThis->GetPawn();
if (!pPawn->IsAlive() || !GetGlobals())
- return ProcessMovement(pThis, pMove);
+ return {KHook::Action::Ignore};
CCSPlayerController* pController = pPawn->GetOriginalController();
if (!pController || !pController->IsConnected())
- return ProcessMovement(pThis, pMove);
+ return {KHook::Action::Ignore};
int iAllowDuckSpam = g_cvarAllowDuckSpam.Get();
@@ -563,17 +570,25 @@ void FASTCALL Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pM
float flSpeedMod = pController->GetZEPlayer()->GetSpeedMod();
if (flSpeedMod == 1.f)
- return ProcessMovement(pThis, pMove);
+ 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
- float flStoreFrametime = GetGlobals()->frametime;
-
+ g_flStoreFrametime = GetGlobals()->frametime;
GetGlobals()->frametime *= flSpeedMod;
- ProcessMovement(pThis, pMove);
+ return {KHook::Action::Ignore};
+}
+
+KHook::Return Detour_ProcessMovement_Post(CCSPlayer_MovementServices* pThis, void* pMove)
+{
+ if (g_flStoreFrametime != 0.0f)
+ {
+ GetGlobals()->frametime = g_flStoreFrametime;
+ g_flStoreFrametime = 0.0f;
+ }
- GetGlobals()->frametime = flStoreFrametime;
+ return {KHook::Action::Ignore};
}
CConVar g_cvarDisableSubtickMovement("cs2f_disable_subtick_move", FCVAR_NONE, "Whether to disable subtick movement", false);
@@ -590,7 +605,7 @@ class CUserCmd
#endif
};
-void* FASTCALL Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd* cmds, int numcmds, bool paused, float margin)
+KHook::Return Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd* cmds, int numcmds, bool paused, float margin)
{
VPROF_SCOPE_BEGIN("Detour_ProcessUsercmds");
@@ -635,40 +650,42 @@ void* FASTCALL Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd
VPROF_SCOPE_END();
- return ProcessUsercmds(pController, cmds, numcmds, paused, margin);
+ return {KHook::Action::Ignore};
}
-void FASTCALL Detour_CGamePlayerEquip_InputTriggerForAllPlayers(CGamePlayerEquip* pEntity, InputData_t* pInput)
+KHook::Return Detour_CGamePlayerEquip_InputTriggerForAllPlayers(CGamePlayerEquip* pEntity, InputData_t* pInput)
{
CGamePlayerEquipHandler::TriggerForAllPlayers(pEntity, pInput);
- CGamePlayerEquip_InputTriggerForAllPlayers(pEntity, pInput);
+ return {KHook::Action::Ignore};
}
-void FASTCALL Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer(CGamePlayerEquip* pEntity, InputData_t* pInput)
+KHook::Return Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer(CGamePlayerEquip* pEntity, InputData_t* pInput)
{
if (CGamePlayerEquipHandler::TriggerForActivatedPlayer(pEntity, pInput))
- CGamePlayerEquip_InputTriggerForActivatedPlayer(pEntity, pInput);
+ return {KHook::Action::Ignore};
+
+ return {KHook::Action::Supersede};
}
-void FASTCALL Detour_CTriggerGravity_GravityTouch(CBaseEntity* pEntity, CBaseEntity* pOther)
+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;
+ return {KHook::Action::Supersede};
- CTriggerGravity_GravityTouch(pEntity, pOther);
+ return {KHook::Action::Ignore};
}
-CServerSideClient* FASTCALL Detour_GetFreeClient(int64_t unk1, const __m128i* unk2, unsigned int unk3, int64_t unk4, char unk5, void* unk6)
+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 nullptr;
+ 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 GetFreeClient(unk1, unk2, unk3, unk4, unk5, unk6);
+ return {KHook::Action::Ignore};
// Phantom client fix
for (int i = 0; i < GetClientList()->Count(); i++)
@@ -676,49 +693,53 @@ CServerSideClient* FASTCALL Detour_GetFreeClient(int64_t unk1, const __m128i* un
CServerSideClient* pClient = (*GetClientList())[i];
if (pClient && pClient->GetSignonState() < SIGNONSTATE_CONNECTED)
- return pClient;
+ return {KHook::Action::Supersede, pClient};
}
// Server is actually full for real
- return nullptr;
+ return {KHook::Action::Supersede, nullptr};
}
#ifdef __linux__
-float FASTCALL Detour_CCSPlayerPawn_GetMaxSpeed(CCSPlayerPawn* pPawn)
+KHook::Return Detour_CCSPlayerPawn_GetMaxSpeed(CCSPlayerPawn* pPawn)
{
- auto flMaxSpeed = CCSPlayerPawn_GetMaxSpeed(pPawn);
+ auto flMaxSpeed = hookCCSPlayerPawn_GetMaxSpeed->CallOriginal(pPawn);
const auto pController = reinterpret_cast(pPawn->GetController());
if (const auto pPlayer = pController != nullptr ? pController->GetZEPlayer() : nullptr)
flMaxSpeed *= pPlayer->GetMaxSpeed();
- return flMaxSpeed;
+ return {KHook::Action::Supersede, flMaxSpeed};
}
#endif
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;
-int64 FASTCALL Detour_FindUseEntity(CCSPlayer_UseServices* pThis, float a2)
+
+KHook::Return Detour_FindUseEntity(CCSPlayer_UseServices* pThis, float a2)
{
g_bFindingUseEntity = true;
- int64 ent = FindUseEntity(pThis, a2);
+ return {KHook::Action::Ignore};
+}
+
+KHook::Return Detour_FindUseEntity_Post(CCSPlayer_UseServices* pThis, float a2)
+{
g_bFindingUseEntity = false;
- return ent;
+ return {KHook::Action::Ignore};
}
-bool FASTCALL Detour_TraceFunc(int64* a1, int* a2, float* a3, uint64 traceMask)
+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));
- return TraceFunc(a1, a2, a3, newMask);
+ KHook::Recall(nullptr, {KHook::Action::Ignore}, a1, a2, a3, newMask);
}
- return TraceFunc(a1, a2, a3, traceMask);
+ return {KHook::Action::Ignore};
}
-bool FASTCALL Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, CTraceFilter* filter, int64 a6)
+KHook::Return Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, CTraceFilter* filter, int64 a6)
{
if (g_cvarPreventUsingPlayers.Get() && g_bFindingUseEntity)
{
@@ -726,202 +747,187 @@ bool FASTCALL Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, CTraceF
filter->DisableInteractsWithLayer(LAYER_INDEX_CONTENTS_NPC);
}
- return TraceShape(a1, a2, a3, a4, filter, a6);
+ return {KHook::Action::Ignore};
}
-CDetour* CEntityIOOutput_FireOutputInternal = nullptr;
-std::map> mapIOFunctions{};
-void FASTCALL Detour_CEntityIOOutput_FireOutputInternal(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay, void* a6, void* a7)
+KHook::Return Detour_CEntityIOOutput_FireOutputInternal(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay, void* a6, void* a7)
{
- for (const auto& [name, cb] : mapIOFunctions)
- cb(pThis, pActivator, pCaller, value, flDelay);
-
- (*CEntityIOOutput_FireOutputInternal)(pThis, pActivator, pCaller, value, flDelay, a6, a7);
-}
+ if (g_cvarEnableButtonWatch.Get())
+ ButtonWatch(pThis, pActivator, pCaller, value, flDelay);
-// Tries to setup Detour_CEntityIOOutput_FireOutputInternal if it is not already setup. This is not
-// enabled unless a feature needs it, as the detour breaks CS# compatibility
-// Returns true if detour is usable, otherwise false.
-bool SetupFireOutputInternalDetour()
-{
- if (CEntityIOOutput_FireOutputInternal != nullptr)
- return true;
+ if (g_cvarEnableEntWatch.Get())
+ EW_FireOutput(pThis, pActivator, pCaller, value, flDelay);
- CEntityIOOutput_FireOutputInternal = new CDetour(Detour_CEntityIOOutput_FireOutputInternal, "CEntityIOOutput_FireOutputInternal");
- if (!CEntityIOOutput_FireOutputInternal->CreateDetour(g_GameConfig))
- {
- Panic("Failed to detour CEntityIOOutput_FireOutputInternal\n");
- delete CEntityIOOutput_FireOutputInternal;
- CEntityIOOutput_FireOutputInternal = nullptr;
- return false;
- }
- CEntityIOOutput_FireOutputInternal->EnableDetour();
- return true;
+ return {KHook::Action::Ignore};
}
#ifdef PLATFORM_WINDOWS
-Vector* FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn, Vector* pRet)
+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 pRet;
+ return {KHook::Action::Supersede, pRet};
}
- return CBasePlayerPawn_GetEyePosition(pPawn, pRet);
+ return {KHook::Action::Ignore};
}
-QAngle* FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn, QAngle* pRet)
+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 pRet;
+ return {KHook::Action::Supersede, pRet};
}
- return CBasePlayerPawn_GetEyeAngles(pPawn, pRet);
+ return {KHook::Action::Ignore};
}
#else
-Vector FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn)
+KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn)
{
if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn)))
{
const auto& origin = pPawn->GetEyePosition();
- return origin;
+ return {KHook::Action::Supersede, origin};
}
- return CBasePlayerPawn_GetEyePosition(pPawn);
+ return {KHook::Action::Ignore};
}
-QAngle FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn)
+KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn)
{
if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast(pPawn)))
{
const auto& angles = pPawn->v_angle();
- return angles;
+ return {KHook::Action::Supersede, angles};
}
- return CBasePlayerPawn_GetEyeAngles(pPawn);
+ return {KHook::Action::Ignore};
}
#endif
-void FASTCALL Detour_CBaseFilter_InputTestActivator(CBaseEntity* pThis, InputData_t& inputdata)
+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;
+ return {KHook::Action::Supersede};
- CBaseFilter_InputTestActivator(pThis, inputdata);
+ 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);
-void FASTCALL Detour_GameSystem_Think_CheckSteamBan()
+KHook::Return Detour_GameSystem_Think_CheckSteamBan()
{
- // Implementation shared by @aiolos1045
- GameSystem_Think_CheckSteamBan();
+ auto pMap = addresses::sm_mapGcBanInformation;
+ static ConVarRefAbstract sv_kick_players_with_cooldown("sv_kick_players_with_cooldown");
+
+ // Fix competitive cooldowns still being applied without sv_kick_players_with_cooldown 2
+ if (sv_kick_players_with_cooldown.GetInt() < 2)
+ {
+ for (int i = pMap->FirstInorder(); i != pMap->InvalidIndex();)
+ {
+ int next = pMap->NextInorder(i);
+ uint32_t reason = pMap->Element(i).m_uiReason;
- if (!g_cvarFixGameBans.Get())
- return;
+ if (reason == 20 || reason == 22 || reason == 23)
+ pMap->RemoveAt(i);
+ i = next;
+ }
+ }
+
+ return {KHook::Action::Ignore};
+}
+
+KHook::Return Detour_GameSystem_Think_CheckSteamBan_Post()
+{
auto pMap = addresses::sm_mapGcBanInformation;
- unsigned int count = pMap->Count();
// After player has been kicked, remove any ban entries, to prevent spreading to all new joining players
- if (count > 0)
+ // Implementation shared by @aiolos1045
+ if (g_cvarFixGameBans.Get() && pMap->Count() > 0)
pMap->RemoveAll();
+
+ return {KHook::Action::Ignore};
}
-AcquireResult FASTCALL Detour_CCSPlayer_ItemServices_CanAcquire(CCSPlayer_ItemServices* pItemServices, CEconItemView* pEconItem, AcquireMethod iAcquireMethod, uint64_t unk4)
+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 zrResult;
+ return {KHook::Action::Supersede, zrResult};
}
- return CCSPlayer_ItemServices_CanAcquire(pItemServices, pEconItem, iAcquireMethod, unk4);
+ return {KHook::Action::Ignore};
}
bool g_bInScriptSetModel = false;
-void FASTCALL Detour_CS_Script_SetModel(uint64_t unk1)
+KHook::Return Detour_CS_Script_SetModel(uint64_t unk1)
{
g_bInScriptSetModel = true;
- CS_Script_SetModel(unk1);
+ return {KHook::Action::Ignore};
+}
+
+KHook::Return Detour_CS_Script_SetModel_Post(uint64_t unk1)
+{
g_bInScriptSetModel = false;
+ return {KHook::Action::Ignore};
}
-void FASTCALL Detour_CBaseModelEntity_SetModel(CBaseModelEntity* pModel, const char* pszModel)
+KHook::Return Detour_CBaseModelEntity_SetModel(CBaseModelEntity* pModel, const char* pszModel)
{
if (!g_bInScriptSetModel)
- return CBaseModelEntity_SetModel(pModel, pszModel);
+ return {KHook::Action::Ignore};
if (PrepareMapSetModel(pModel))
- return CBaseModelEntity_SetModel(pModel, pszModel);
+ return {KHook::Action::Ignore};
+
+ return {KHook::Action::Supersede};
}
-void FASTCALL Detour_CCSGameRules_GoToIntermission(CCSGameRules* pThis, bool bAbortedMatch)
+KHook::Return Detour_CCSGameRules_GoToIntermission(CCSGameRules* pThis, bool bAbortedMatch)
{
if (!g_pMapVoteSystem->IsIntermissionAllowed(false) && g_cvarVoteManagerEnable.Get())
- return;
+ return {KHook::Action::Supersede};
if (g_cvarVoteManagerEnable.Get())
g_pVoteManager->OnIntermission();
- return CCSGameRules_GoToIntermission(pThis, bAbortedMatch);
+ return {KHook::Action::Ignore};
}
-void FASTCALL Detour_SetBeamOrigin(CBeam* pThis, const Vector* pVecPosition)
+KHook::Return Detour_SetBeamOrigin(CBeam* pThis, const Vector* pVecPosition)
{
// Game code still works for parented beams/lasers
if (pThis->m_CBodyComponent()->m_pSceneNode()->m_pParent())
- SetBeamOrigin(pThis, pVecPosition);
+ return {KHook::Action::Ignore};
// If no parent, then game code would hit infinite loop, just reimplement this simple path ourselves
pThis->SetAbsOrigin(*pVecPosition);
+ return {KHook::Action::Supersede};
}
-void FASTCALL Detour_SetBeamEndPos(CBeam* pThis, const Vector* pVecPosition)
+KHook::Return Detour_SetBeamEndPos(CBeam* pThis, const Vector* pVecPosition)
{
// Game code still works for parented beams/lasers
if (pThis->m_CBodyComponent()->m_pSceneNode()->m_pParent())
- SetBeamEndPos(pThis, pVecPosition);
+ return {KHook::Action::Ignore};
// If no parent, then game code would hit infinite loop, just reimplement this simple path ourselves
pThis->m_vecEndPos = *(VectorWS*)(pVecPosition);
+ return {KHook::Action::Supersede};
}
-bool FASTCALL Detour_IsCommandWhitelisted(void* pAddonManager, const char* pszCommandName)
+KHook::Return Detour_IsCommandWhitelisted(void* pAddonManager, const char* pszCommandName)
{
if (!g_cvarConVarWhitelistEnable.Get() || !g_pConvarWhitelist->IsConfigLoaded())
- return IsCommandWhitelisted(pAddonManager, pszCommandName);
-
- return g_pConvarWhitelist->IsWhitelisted(pszCommandName);
-}
-
-bool InitDetours(CGameConfig* gameConfig)
-{
- bool success = true;
+ return {KHook::Action::Ignore};
- FOR_EACH_VEC(g_vecDetours, i)
- {
- if (!g_vecDetours[i]->CreateDetour(gameConfig))
- success = false;
-
- g_vecDetours[i]->EnableDetour();
- }
-
- return success;
+ return {KHook::Action::Supersede, g_pConvarWhitelist->IsWhitelisted(pszCommandName)};
}
-
-void FlushAllDetours()
-{
- FOR_EACH_VEC_BACK(g_vecDetours, i)
- {
- g_vecDetours[i]->FreeDetour();
- g_vecDetours.FastRemove(i);
- }
-}
\ No newline at end of file
diff --git a/src/detours.h b/src/detours.h
index 2f48e42c4..08d0d677f 100644
--- a/src/detours.h
+++ b/src/detours.h
@@ -18,8 +18,8 @@
*/
#pragma once
-#include "cdetour.h"
#include "cs2_sdk/entityio.h"
+#include "khook.hpp"
#include
#include "cs2_sdk/entityio.h"
@@ -27,11 +27,12 @@ class CCheckTransmitInfo;
class IRecipientFilter;
class ISoundEmitterSystemBase;
class CBaseEntity;
-class CBaseEntity;
+class CBaseFilter;
class CCSPlayerController;
class CEntityIndex;
class CCommand;
class CTriggerPush;
+class CTriggerGravity;
class CGameConfig;
class CGameRules;
class CTakeDamageInfo;
@@ -50,6 +51,8 @@ class InputData_t;
class CCSPlayerPawn;
class CCSPlayer_UseServices;
class CTraceFilter;
+class CNavMesh;
+class CBaseModelEntity;
class Vector;
class QAngle;
class CEconItemView;
@@ -57,10 +60,6 @@ class CCSGameRules;
class CBeam;
struct CTakeDamageResult;
-// Add callback functions to this map that wish to hook into Detour_CEntityIOOutput_FireOutputInternal
-// to make it more modular/cleaner than shoving everything into the detour (buttonwatch, entwatch, etc.)
-extern std::map> mapIOFunctions;
-
enum class AcquireMethod
{
PickUp,
@@ -82,42 +81,43 @@ enum class AcquireResult
NotAllowedByProhibition,
};
-bool InitDetours(CGameConfig* gameConfig);
-void FlushAllDetours();
-bool SetupFireOutputInternalDetour();
-
-void FASTCALL Detour_UTIL_SayTextFilter(IRecipientFilter&, const char*, CCSPlayerController*, uint64);
-void FASTCALL Detour_UTIL_SayText2Filter(IRecipientFilter&, CCSPlayerController*, uint64, const char*, const char*, const char*, const char*, const char*);
-void FASTCALL Detour_TriggerPush_Touch(CTriggerPush* pPush, CBaseEntity* pOther);
-int64 FASTCALL Detour_CBaseEntity_TakeDamageOld(CBaseEntity* pThis, CTakeDamageInfo* pInfo, CTakeDamageResult* pResult);
-bool FASTCALL Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices*, CBasePlayerWeapon*);
-void FASTCALL Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices*, CBasePlayerWeapon*);
-bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSymbolLarge* pInputName, CEntityInstance* pActivator, CEntityInstance* pCaller, variant_t* value, void*, void*);
-void* FASTCALL Detour_CNavMesh_GetNearestNavArea(int64_t unk1, float* unk2, unsigned int* unk3, unsigned int unk4, int64_t unk5, float unk6, int64_t unk7);
-void FASTCALL Detour_ProcessMovement(CCSPlayer_MovementServices* pThis, void* pMove);
-void* FASTCALL Detour_ProcessUsercmds(CCSPlayerController* pController, CUserCmd* cmds, int numcmds, bool paused, float margin);
-void FASTCALL Detour_CGamePlayerEquip_InputTriggerForAllPlayers(CGamePlayerEquip*, InputData_t*);
-void FASTCALL Detour_CGamePlayerEquip_InputTriggerForActivatedPlayer(CGamePlayerEquip*, InputData_t*);
-void FASTCALL Detour_CTriggerGravity_GravityTouch(CBaseEntity* pEntity, CBaseEntity* pOther);
-CServerSideClient* FASTCALL Detour_GetFreeClient(int64_t unk1, const __m128i* unk2, unsigned int unk3, int64_t unk4, char unk5, void* unk6);
-float FASTCALL Detour_CCSPlayerPawn_GetMaxSpeed(CCSPlayerPawn*);
-int64 FASTCALL Detour_FindUseEntity(CCSPlayer_UseServices* pThis, float);
-bool FASTCALL Detour_TraceFunc(int64*, int*, float*, uint64);
-bool FASTCALL Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64);
-void FASTCALL Detour_CEntityIOOutput_FireOutputInternal(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay, void*, void*);
+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_UTIL_SayTextFilter(IRecipientFilter&, const char*, CCSPlayerController*, int);
+KHook::Return Detour_UTIL_SayText2Filter(IRecipientFilter&, CCSPlayerController*, int, 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, 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
-Vector* FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*, Vector*);
-QAngle* FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*, QAngle*);
+KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*, Vector*);
+KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*, QAngle*);
#else
-Vector FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*);
-QAngle FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*);
+KHook::Return Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*);
+KHook::Return Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*);
#endif
-void FASTCALL Detour_CBaseFilter_InputTestActivator(CBaseEntity* pThis, InputData_t& inputdata);
-void FASTCALL Detour_GameSystem_Think_CheckSteamBan();
-AcquireResult FASTCALL Detour_CCSPlayer_ItemServices_CanAcquire(CCSPlayer_ItemServices* pItemServices, CEconItemView* pEconItem, AcquireMethod iAcquireMethod, uint64_t unk4);
-void FASTCALL Detour_CS_Script_SetModel(uint64_t unk1);
-void FASTCALL Detour_CBaseModelEntity_SetModel(CBaseModelEntity* pModel, const char* pszModel);
-void FASTCALL Detour_CCSGameRules_GoToIntermission(CCSGameRules* pThis, bool bAbortedMatch);
-void FASTCALL Detour_SetBeamOrigin(CBeam* pThis, const Vector* pVecWorldPosition);
-void FASTCALL Detour_SetBeamEndPos(CBeam* pThis, const Vector* pVecWorldPosition);
-bool FASTCALL Detour_IsCommandWhitelisted(void* pAddonManager, const char* pszCommandName);
\ No newline at end of file
+KHook::Return Detour_CBaseFilter_InputTestActivator(CBaseFilter* pThis, InputData_t& inputdata);
+KHook::Return Detour_GameSystem_Think_CheckSteamBan();
+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);
+KHook::Return Detour_SetBeamOrigin(CBeam* pThis, const Vector* pVecWorldPosition);
+KHook::Return Detour_SetBeamEndPos(CBeam* pThis, const Vector* pVecWorldPosition);
+KHook::Return Detour_IsCommandWhitelisted(void* pAddonManager, const char* pszCommandName);
diff --git a/src/entities.cpp b/src/entities.cpp
index a9e4f61e8..f38c27494 100644
--- a/src/entities.cpp
+++ b/src/entities.cpp
@@ -122,7 +122,7 @@ namespace CTriggerGravityHandler
{
static std::unordered_map s_gravityMap;
- void OnPrecache(CBaseEntity* pEntity, const CEntityKeyValues* kv)
+ void OnPrecache(CTriggerGravity* pEntity, const CEntityKeyValues* kv)
{
const auto pGravity = kv->GetKeyValue("gravity");
const auto pHammerId = kv->GetKeyValue("hammerUniqueId");
@@ -135,9 +135,9 @@ namespace CTriggerGravityHandler
s_gravityMap[hEntity] = flGravity;
}
- bool GravityTouching(CBaseEntity* pEntity, CBaseEntity* pOther)
+ bool GravityTouching(CTriggerGravity* pEntity, CBaseEntity* pOther)
{
- const auto hEntity = GetEntityUnique(pEntity);
+ const auto hEntity = GetEntityUnique((CBaseEntity*)pEntity);
if (hEntity == ENTITY_UNIQUE_INVALID)
return false;
@@ -151,7 +151,7 @@ namespace CTriggerGravityHandler
return true;
}
- void OnEndTouch(CBaseEntity* pEntity, CBaseEntity* pOther)
+ void OnEndTouch(CTriggerGravity* pEntity, CBaseEntity* pOther)
{
if (pOther->IsPawn())
pOther->SetGravityScale(1);
diff --git a/src/entities.h b/src/entities.h
index df075eeb5..58ea1b7e0 100644
--- a/src/entities.h
+++ b/src/entities.h
@@ -26,12 +26,13 @@ class CGameUI;
class CPointViewControl;
class CCSPlayerPawn;
class CEntityKeyValues;
+class CTriggerGravity;
namespace CTriggerGravityHandler
{
- void OnPrecache(CBaseEntity* pEntity, const CEntityKeyValues* kv);
- bool GravityTouching(CBaseEntity* pEntity, CBaseEntity* pOther);
- void OnEndTouch(CBaseEntity* pEntity, CBaseEntity* pOther);
+ void OnPrecache(CTriggerGravity* pEntity, const CEntityKeyValues* kv);
+ bool GravityTouching(CTriggerGravity* pEntity, CBaseEntity* pOther);
+ void OnEndTouch(CTriggerGravity* pEntity, CBaseEntity* pOther);
} // namespace CTriggerGravityHandler
namespace CGamePlayerEquipHandler
diff --git a/src/entitylistener.cpp b/src/entitylistener.cpp
index 72e6fd611..e4f72ffda 100644
--- a/src/entitylistener.cpp
+++ b/src/entitylistener.cpp
@@ -20,6 +20,7 @@
#include "entitylistener.h"
#include "common.h"
#include "cs2_sdk/entity/cbaseentity.h"
+#include "cs2_sdk/entity/customhudlayout.h"
#include "cs2fixes.h"
#include "entities.h"
#include "entity/cgamerules.h"
@@ -55,12 +56,15 @@ void CEntityListener::OnEntityCreated(CEntityInstance* pEntity)
ExecuteOnce(addresses::UTIL_Remove = pEntity->m_pEntity->m_pClass->m_NameToThinkFunc("CBaseEntitySUB_Remove"));
if (!V_strcmp("cs_gamerules", pEntity->GetClassname()))
- g_pGameRules = ((CCSGameRulesProxy*)pEntity)->m_pGameRules;
+ g_pGameRules = reinterpret_cast(pEntity)->m_pGameRules;
}
void CEntityListener::OnEntityDeleted(CEntityInstance* pEntity)
{
EW_OnEntityDeleted(pEntity);
+
+ if (!V_strcmp("custom_hud_layout", pEntity->GetClassname()))
+ reinterpret_cast(pEntity)->OnEntityDeleted();
}
void CEntityListener::OnEntityParentChanged(CEntityInstance* pEntity, CEntityInstance* pNewParent)
diff --git a/src/entwatch.cpp b/src/entwatch.cpp
index d5be7ca27..55e51adbb 100644
--- a/src/entwatch.cpp
+++ b/src/entwatch.cpp
@@ -53,21 +53,14 @@
CEWHandler* g_pEWHandler = nullptr;
-SH_DECL_MANUALHOOK1_void(CBaseButton_Use, 0, 0, 0, InputData_t*);
-SH_DECL_MANUALHOOK1_void(CPhysBox_Use, 0, 0, 0, InputData_t*);
-SH_DECL_MANUALHOOK1_void(CRotButton_Use, 0, 0, 0, InputData_t*);
-SH_DECL_MANUALHOOK1_void(CMomentaryRotButton_Use, 0, 0, 0, InputData_t*);
-SH_DECL_MANUALHOOK1_void(CPhysicalButton_Use, 0, 0, 0, InputData_t*);
-
-SH_DECL_MANUALHOOK1_void(CTriggerTeleport_StartTouch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerTeleport_Touch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerTeleport_EndTouch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerOnce_StartTouch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerOnce_Touch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerOnce_EndTouch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerMultiple_StartTouch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerMultiple_Touch, 0, 0, 0, CBaseEntity*);
-SH_DECL_MANUALHOOK1_void(CTriggerMultiple_EndTouch, 0, 0, 0, CBaseEntity*);
+CBaseEntity* g_pCBaseButtonVTable = nullptr;
+CBaseEntity* g_pCPhysBoxVTable = nullptr;
+CBaseEntity* g_pCRotButtonVTable = nullptr;
+CBaseEntity* g_pCMomentaryRotButtonVTable = nullptr;
+CBaseEntity* g_pCPhysicalButtonVTable = nullptr;
+CBaseEntity* g_pCTriggerTeleportVTable = nullptr;
+CBaseEntity* g_pCTriggerOnceVTable = nullptr;
+CBaseEntity* g_pCTriggerMultipleVTable = nullptr;
CConVar g_cvarEnableEntWatch("entwatch_enable", FCVAR_NONE, "INCOMPATIBLE WITH CS#. Whether to enable EntWatch features", false);
CConVar g_cvarEnableFiltering("entwatch_auto_filter", FCVAR_NONE, "Whether to automatically block non-item holders from triggering uses", true);
@@ -235,7 +228,7 @@ void EWItemHandler::RegisterEntity(CBaseEntity* pEntity)
switch (type)
{
case Button:
- g_pEWHandler->AddUseHook(pEntity);
+ g_pEWHandler->vecUseHookedEntities.push_back(pEntity->GetHandle());
break;
case CounterDown:
case CounterUp:
@@ -707,7 +700,7 @@ bool EWItemInstance::RemoveHandler(CBaseEntity* pEnt)
if (vecHandlers[i]->iEntIndex == pEnt->entindex())
{
if (vecHandlers[i]->type == EWHandlerType::Button)
- g_pEWHandler->RemoveUseHook(pEnt);
+ g_pEWHandler->RemoveUseEntity(pEnt);
vecHandlers[i]->iEntIndex = -1;
return true;
}
@@ -1055,12 +1048,9 @@ bool EWItemInstance::IsEmpty()
void CEWHandler::UnLoadConfig()
{
- if (!bConfigLoaded)
+ if (!m_bConfigLoaded)
return;
- if (EW_IsFireOutputHooked())
- mapIOFunctions.erase("entwatch");
-
// Clantags first so scores can be set back properly
ResetAllClantags();
@@ -1071,10 +1061,10 @@ void CEWHandler::UnLoadConfig()
vecItems[i]->vecHandlers[j]->RemoveHook();
vecItems.clear();
- RemoveAllUseHooks();
- RemoveAllTriggers();
+ vecUseHookedEntities.clear();
+ vecHookedTriggers.clear();
- bConfigLoaded = false;
+ m_bConfigLoaded = false;
}
/*
@@ -1129,21 +1119,12 @@ void CEWHandler::LoadConfig(const char* sFilePath)
mapItemConfig[hash_32_fnv1a_const(sHammerid.c_str())] = item;
}
- if (mapItemConfig.size() > 0)
- {
- // Hook FireOutput
- if (!SetupFireOutputInternalDetour())
- mapIOFunctions.erase("entwatch");
- else if (!EW_IsFireOutputHooked())
- mapIOFunctions["entwatch"] = EW_FireOutput;
- }
-
- bConfigLoaded = true;
+ m_bConfigLoaded = true;
}
void CEWHandler::PrintLoadedConfig(CCSPlayerController* pController)
{
- if (!bConfigLoaded)
+ if (!m_bConfigLoaded)
{
ClientPrint(pController, HUD_PRINTTALK, EW_PREFIX "No config loaded.");
return;
@@ -1322,7 +1303,7 @@ bool CEWHandler::RegisterTrigger(CBaseEntity* pEnt)
if (strcmp(sHammerid, pItem->vecTriggers[j].c_str()))
continue;
- AddTouchHook(pEnt);
+ vecHookedTriggers.push_back(pEnt->GetHandle());
return true;
}
@@ -1330,74 +1311,116 @@ bool CEWHandler::RegisterTrigger(CBaseEntity* pEnt)
return false;
}
-void CEWHandler::AddTouchHook(CBaseEntity* pEnt)
+void CEWHandler::CreateHooks()
{
- static int startOffset = g_GameConfig->GetOffset("CBaseEntity::StartTouch");
- static int touchOffset = g_GameConfig->GetOffset("CBaseEntity::Touch");
- static int endOffset = g_GameConfig->GetOffset("CBaseEntity::EndTouch");
+ g_pCBaseButtonVTable = (CBaseEntity*)modules::server->FindVirtualTable("CBaseButton");
+ g_pCPhysBoxVTable = (CBaseEntity*)modules::server->FindVirtualTable("CPhysBox");
+ g_pCRotButtonVTable = (CBaseEntity*)modules::server->FindVirtualTable("CRotButton");
+ g_pCMomentaryRotButtonVTable = (CBaseEntity*)modules::server->FindVirtualTable("CMomentaryRotButton");
+ g_pCPhysicalButtonVTable = (CBaseEntity*)modules::server->FindVirtualTable("CPhysicalButton");
+ g_pCTriggerTeleportVTable = (CBaseEntity*)modules::server->FindVirtualTable("CTriggerTeleport");
+ g_pCTriggerOnceVTable = (CBaseEntity*)modules::server->FindVirtualTable("CTriggerOnce");
+ g_pCTriggerMultipleVTable = (CBaseEntity*)modules::server->FindVirtualTable("CTriggerMultiple");
- if (!V_strcmp(pEnt->GetClassname(), "trigger_teleport"))
+ int offset = g_GameConfig->GetOffset("CBaseEntity::Use");
+
+ if (offset == -1)
{
- if (iTriggerTeleportTouchHooks[0] == -1)
- {
- const auto pTeleport_VTable = modules::server->FindVirtualTable("CTriggerTeleport");
+ Panic("Failed to find CBaseEntity::Use offset\n");
+ return;
+ }
- SH_MANUALHOOK_RECONFIGURE(CTriggerTeleport_StartTouch, startOffset, 0, 0);
- iTriggerTeleportTouchHooks[0] = SH_ADD_MANUALDVPHOOK(CTriggerTeleport_StartTouch, pTeleport_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
+ m_hBaseButtonUse.Configure(offset);
+ m_hBaseButtonUse.AddGlobal((CBaseEntity*)&g_pCBaseButtonVTable);
- SH_MANUALHOOK_RECONFIGURE(CTriggerTeleport_Touch, touchOffset, 0, 0);
- iTriggerTeleportTouchHooks[1] = SH_ADD_MANUALDVPHOOK(CTriggerTeleport_Touch, pTeleport_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
+ m_hPhysBoxUse.Configure(offset);
+ m_hPhysBoxUse.AddGlobal((CBaseEntity*)&g_pCPhysBoxVTable);
- SH_MANUALHOOK_RECONFIGURE(CTriggerTeleport_EndTouch, endOffset, 0, 0);
- iTriggerTeleportTouchHooks[2] = SH_ADD_MANUALDVPHOOK(CTriggerTeleport_EndTouch, pTeleport_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
- }
- }
- else if (!V_strcmp(pEnt->GetClassname(), "trigger_multiple"))
- {
- if (iTriggerMultipleTouchHooks[0] == -1)
- {
- const auto pMultiple_VTable = modules::server->FindVirtualTable("CTriggerMultiple");
+ m_hRotButtonUse.Configure(offset);
+ m_hRotButtonUse.AddGlobal((CBaseEntity*)&g_pCRotButtonVTable);
- SH_MANUALHOOK_RECONFIGURE(CTriggerMultiple_StartTouch, startOffset, 0, 0);
- iTriggerMultipleTouchHooks[0] = SH_ADD_MANUALDVPHOOK(CTriggerMultiple_StartTouch, pMultiple_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
+ m_hMomentaryRotButtonUse.Configure(offset);
+ m_hMomentaryRotButtonUse.AddGlobal((CBaseEntity*)&g_pCMomentaryRotButtonVTable);
- SH_MANUALHOOK_RECONFIGURE(CTriggerMultiple_Touch, touchOffset, 0, 0);
- iTriggerMultipleTouchHooks[1] = SH_ADD_MANUALDVPHOOK(CTriggerMultiple_Touch, pMultiple_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
+ m_hPhysicalButtonUse.Configure(offset);
+ m_hPhysicalButtonUse.AddGlobal((CBaseEntity*)&g_pCPhysicalButtonVTable);
- SH_MANUALHOOK_RECONFIGURE(CTriggerMultiple_EndTouch, endOffset, 0, 0);
- iTriggerMultipleTouchHooks[2] = SH_ADD_MANUALDVPHOOK(CTriggerMultiple_EndTouch, pMultiple_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
- }
+ offset = g_GameConfig->GetOffset("CBaseEntity::StartTouch");
+
+ if (offset == -1)
+ {
+ Panic("Failed to find CBaseEntity::StartTouch offset\n");
+ return;
}
- else if (!V_strcmp(pEnt->GetClassname(), "trigger_once"))
+
+ m_hTriggerTeleportStartTouch.Configure(offset);
+ m_hTriggerTeleportStartTouch.AddGlobal((CBaseEntity*)&g_pCTriggerTeleportVTable);
+
+ m_hTriggerOnceStartTouch.Configure(offset);
+ m_hTriggerOnceStartTouch.AddGlobal((CBaseEntity*)&g_pCTriggerOnceVTable);
+
+ m_hTriggerMultipleStartTouch.Configure(offset);
+ m_hTriggerMultipleStartTouch.AddGlobal((CBaseEntity*)&g_pCTriggerMultipleVTable);
+
+ offset = g_GameConfig->GetOffset("CBaseEntity::Touch");
+
+ if (offset == -1)
{
- if (iTriggerOnceTouchHooks[0] == -1)
- {
- const auto pOnce_VTable = modules::server->FindVirtualTable("CTriggerOnce");
+ Panic("Failed to find CBaseEntity::Touch offset\n");
+ return;
+ }
- SH_MANUALHOOK_RECONFIGURE(CTriggerOnce_StartTouch, startOffset, 0, 0);
- iTriggerOnceTouchHooks[0] = SH_ADD_MANUALDVPHOOK(CTriggerOnce_StartTouch, pOnce_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
+ m_hTriggerTeleportTouch.Configure(offset);
+ m_hTriggerTeleportTouch.AddGlobal((CBaseEntity*)&g_pCTriggerTeleportVTable);
- SH_MANUALHOOK_RECONFIGURE(CTriggerOnce_Touch, touchOffset, 0, 0);
- iTriggerOnceTouchHooks[1] = SH_ADD_MANUALDVPHOOK(CTriggerOnce_Touch, pOnce_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
+ m_hTriggerOnceTouch.Configure(offset);
+ m_hTriggerOnceTouch.AddGlobal((CBaseEntity*)&g_pCTriggerOnceVTable);
- SH_MANUALHOOK_RECONFIGURE(CTriggerOnce_EndTouch, endOffset, 0, 0);
- iTriggerOnceTouchHooks[2] = SH_ADD_MANUALDVPHOOK(CTriggerOnce_EndTouch, pOnce_VTable, SH_MEMBER(this, &CEWHandler::Hook_Touch), false);
- }
+ m_hTriggerMultipleTouch.Configure(offset);
+ m_hTriggerMultipleTouch.AddGlobal((CBaseEntity*)&g_pCTriggerMultipleVTable);
+
+ offset = g_GameConfig->GetOffset("CBaseEntity::EndTouch");
+
+ if (offset == -1)
+ {
+ Panic("Failed to find CBaseEntity::EndTouch offset\n");
+ return;
}
- vecHookedTriggers.push_back(pEnt->GetHandle());
+ m_hTriggerTeleportEndTouch.Configure(offset);
+ m_hTriggerTeleportEndTouch.AddGlobal((CBaseEntity*)&g_pCTriggerTeleportVTable);
+
+ m_hTriggerOnceEndTouch.Configure(offset);
+ m_hTriggerOnceEndTouch.AddGlobal((CBaseEntity*)&g_pCTriggerOnceVTable);
+
+ m_hTriggerMultipleEndTouch.Configure(offset);
+ m_hTriggerMultipleEndTouch.AddGlobal((CBaseEntity*)&g_pCTriggerMultipleVTable);
}
-void CEWHandler::Hook_Touch(CBaseEntity* pOther)
+void CEWHandler::RemoveHooks()
{
- CBaseEntity* pEntity = META_IFACEPTR(CBaseEntity);
- if (!pEntity)
- RETURN_META(MRES_IGNORED);
+ m_hBaseButtonUse.RemoveGlobal((CBaseEntity*)&g_pCBaseButtonVTable);
+ m_hPhysBoxUse.RemoveGlobal((CBaseEntity*)&g_pCPhysBoxVTable);
+ m_hRotButtonUse.RemoveGlobal((CBaseEntity*)&g_pCRotButtonVTable);
+ m_hMomentaryRotButtonUse.RemoveGlobal((CBaseEntity*)&g_pCMomentaryRotButtonVTable);
+ m_hPhysicalButtonUse.RemoveGlobal((CBaseEntity*)&g_pCPhysicalButtonVTable);
+ m_hTriggerTeleportStartTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerTeleportVTable);
+ m_hTriggerOnceStartTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerOnceVTable);
+ m_hTriggerMultipleStartTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerMultipleVTable);
+ m_hTriggerTeleportTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerTeleportVTable);
+ m_hTriggerOnceTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerOnceVTable);
+ m_hTriggerMultipleTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerMultipleVTable);
+ m_hTriggerTeleportEndTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerTeleportVTable);
+ m_hTriggerOnceEndTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerOnceVTable);
+ m_hTriggerMultipleEndTouch.RemoveGlobal((CBaseEntity*)&g_pCTriggerMultipleVTable);
+}
+KHook::Return CEWHandler::Hook_Touch(CBaseEntity* pThis, CBaseEntity* pOther)
+{
bool bFound = false;
for (int i = 0; i < (vecHookedTriggers).size(); i++)
{
- if (vecHookedTriggers[i] == pEntity->GetHandle())
+ if (vecHookedTriggers[i] == pThis->GetHandle())
{
bFound = true;
break;
@@ -1405,24 +1428,24 @@ void CEWHandler::Hook_Touch(CBaseEntity* pOther)
}
if (!bFound)
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
CCSPlayerPawn* pPawn = (CCSPlayerPawn*)pOther;
if (!pPawn)
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
CCSPlayerController* pController = pPawn->GetOriginalController();
if (!pController)
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
ZEPlayer* zpPlayer = pController->GetZEPlayer();
if (!zpPlayer)
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
if (zpPlayer->IsEbanned())
- RETURN_META(MRES_SUPERCEDE);
+ return {KHook::Action::Supersede};
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
}
bool CEWHandler::RemoveTrigger(CBaseEntity* pEnt)
@@ -1439,23 +1462,6 @@ bool CEWHandler::RemoveTrigger(CBaseEntity* pEnt)
return false;
}
-void CEWHandler::RemoveAllTriggers()
-{
- Message("[EntWatch] Fully unhooking touch hooks\n");
- for (int i = 0; i < 3; i++)
- {
- SH_REMOVE_HOOK_ID(iTriggerTeleportTouchHooks[i]);
- iTriggerTeleportTouchHooks[i] = -1;
-
- SH_REMOVE_HOOK_ID(iTriggerMultipleTouchHooks[i]);
- iTriggerMultipleTouchHooks[i] = -1;
-
- SH_REMOVE_HOOK_ID(iTriggerOnceTouchHooks[i]);
- iTriggerOnceTouchHooks[i] = -1;
- }
- vecHookedTriggers.clear();
-}
-
void CEWHandler::RemoveHandler(CBaseEntity* pEnt)
{
for (int i = 0; i < (vecItems).size(); i++)
@@ -1798,65 +1804,7 @@ void CEWHandler::Transfer(CCSPlayerController* pCaller, int iItemInstance, CHand
}
}
-void CEWHandler::AddUseHook(CBaseEntity* pEnt)
-{
- static int offset = g_GameConfig->GetOffset("CBaseEntity::Use");
- if (offset == -1)
- {
- Panic("Failed to find CBaseEntity::Use offset in entwatch.cpp::AddUseHook() !!\n");
- return;
- }
-
- if (!V_strcmp(pEnt->GetClassname(), "func_button"))
- {
- if (iBaseBtnUseHookId == -1)
- {
- const auto pBaseButton_VTable = modules::server->FindVirtualTable("CBaseButton");
- SH_MANUALHOOK_RECONFIGURE(CBaseButton_Use, offset, 0, 0);
- iBaseBtnUseHookId = SH_ADD_MANUALDVPHOOK(CBaseButton_Use, pBaseButton_VTable, SH_MEMBER(this, &CEWHandler::Hook_Use), false);
- }
- }
- else if (!V_strcmp(pEnt->GetClassname(), "func_physbox"))
- {
- if (iPhysboxUseHookId == -1)
- {
- const auto pPhysbox_VTable = modules::server->FindVirtualTable("CPhysBox");
- SH_MANUALHOOK_RECONFIGURE(CPhysBox_Use, offset, 0, 0);
- iPhysboxUseHookId = SH_ADD_MANUALDVPHOOK(CPhysBox_Use, pPhysbox_VTable, SH_MEMBER(this, &CEWHandler::Hook_Use), false);
- }
- }
- else if (!V_strcmp(pEnt->GetClassname(), "func_rot_button"))
- {
- if (iRotBtnUseHookId == -1)
- {
- const auto pCRotButton_VTable = modules::server->FindVirtualTable("CRotButton");
- SH_MANUALHOOK_RECONFIGURE(CRotButton_Use, offset, 0, 0);
- iRotBtnUseHookId = SH_ADD_MANUALDVPHOOK(CRotButton_Use, pCRotButton_VTable, SH_MEMBER(this, &CEWHandler::Hook_Use), false);
- }
- }
- else if (!V_strcmp(pEnt->GetClassname(), "momentary_rot_button"))
- {
- if (iMomRotBtnUseHookId == -1)
- {
- const auto pCMomentaryRotButton_VTable = modules::server->FindVirtualTable("CMomentaryRotButton");
- SH_MANUALHOOK_RECONFIGURE(CMomentaryRotButton_Use, offset, 0, 0);
- iMomRotBtnUseHookId = SH_ADD_MANUALDVPHOOK(CMomentaryRotButton_Use, pCMomentaryRotButton_VTable, SH_MEMBER(this, &CEWHandler::Hook_Use), false);
- }
- }
- else if (!V_strcmp(pEnt->GetClassname(), "func_physical_button"))
- {
- if (iPhysicalBtnUseHookId == -1)
- {
- const auto pCPhysicalButton_VTable = modules::server->FindVirtualTable("CPhysicalButton");
- SH_MANUALHOOK_RECONFIGURE(CPhysicalButton_Use, offset, 0, 0);
- iPhysicalBtnUseHookId = SH_ADD_MANUALDVPHOOK(CPhysicalButton_Use, pCPhysicalButton_VTable, SH_MEMBER(this, &CEWHandler::Hook_Use), false);
- }
- }
-
- vecUseHookedEntities.push_back(pEnt->GetHandle());
-}
-
-void CEWHandler::RemoveUseHook(CBaseEntity* pEnt)
+void CEWHandler::RemoveUseEntity(CBaseEntity* pEnt)
{
for (int i = 0; i < (vecUseHookedEntities).size(); i++)
{
@@ -1868,39 +1816,13 @@ void CEWHandler::RemoveUseHook(CBaseEntity* pEnt)
}
}
-void CEWHandler::RemoveAllUseHooks()
-{
- Message("[EntWatch] Fully unhooking use hooks\n");
-
- SH_REMOVE_HOOK_ID(iBaseBtnUseHookId);
- iBaseBtnUseHookId = -1;
-
- SH_REMOVE_HOOK_ID(iPhysboxUseHookId);
- iPhysboxUseHookId = -1;
-
- SH_REMOVE_HOOK_ID(iRotBtnUseHookId);
- iRotBtnUseHookId = -1;
-
- SH_REMOVE_HOOK_ID(iMomRotBtnUseHookId);
- iMomRotBtnUseHookId = -1;
-
- SH_REMOVE_HOOK_ID(iPhysicalBtnUseHookId);
- iPhysicalBtnUseHookId = -1;
-
- vecUseHookedEntities.clear();
-}
-
-void CEWHandler::Hook_Use(InputData_t* pInput)
+KHook::Return CEWHandler::Hook_Use(CBaseEntity* pThis, InputData_t* pInput)
{
// Message("##### USE HOOK #####\n");
- CBaseEntity* pEntity = META_IFACEPTR(CBaseEntity);
- if (!pEntity)
- RETURN_META(MRES_IGNORED);
-
bool bFound = false;
for (int i = 0; i < (vecUseHookedEntities).size(); i++)
{
- if (vecUseHookedEntities[i] == pEntity->GetHandle())
+ if (vecUseHookedEntities[i] == pThis->GetHandle())
{
bFound = true;
break;
@@ -1908,9 +1830,9 @@ void CEWHandler::Hook_Use(InputData_t* pInput)
}
if (!bFound)
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
- int index = pEntity->entindex();
+ int index = pThis->entindex();
int itemIndex = -1;
int handlerIndex = -1;
for (int i = 0; i < (vecItems).size(); i++)
@@ -1925,31 +1847,31 @@ void CEWHandler::Hook_Use(InputData_t* pInput)
}
if (itemIndex == -1 || handlerIndex == -1)
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
// Prevent uses from non item owners if we are filtering
- META_RES resVal = MRES_IGNORED;
+ KHook::Return returnVal = {KHook::Action::Ignore};
if (g_cvarEnableFiltering.Get())
- resVal = MRES_SUPERCEDE;
+ returnVal = {KHook::Action::Supersede};
CBaseEntity* pActivator = pInput->pActivator;
if (!pActivator || !pActivator->IsPawn())
- RETURN_META(resVal);
+ return returnVal;
std::shared_ptr pItem = vecItems[itemIndex];
CCSPlayerPawn* pPawn = (CCSPlayerPawn*)pActivator;
CCSPlayerController* pController = pPawn->GetOriginalController();
if (!pController || pController->GetPlayerSlot() != pItem->iOwnerSlot)
- RETURN_META(resVal);
+ return returnVal;
//
// WE SHOW USE MESSAGE IN FireOutput
// This is just to prevent unnecessary stuff with buttons like movement
//
- RETURN_META(MRES_IGNORED);
+ return {KHook::Action::Ignore};
}
// Update cd and uses of all held items
@@ -2085,7 +2007,7 @@ void EW_RoundPreStart()
void EW_OnEntitySpawned(CEntityInstance* pEntity)
{
- if (!g_pEWHandler || !g_pEWHandler->bConfigLoaded)
+ if (!g_pEWHandler || !g_pEWHandler->m_bConfigLoaded)
return;
CBaseEntity* pEnt = (CBaseEntity*)pEntity;
@@ -2131,7 +2053,7 @@ void EW_OnEntitySpawned(CEntityInstance* pEntity)
void EW_OnEntityDeleted(CEntityInstance* pEntity)
{
- if (!g_pEWHandler || !g_pEWHandler->bConfigLoaded)
+ if (!g_pEWHandler || !g_pEWHandler->m_bConfigLoaded)
return;
CBaseEntity* pEnt = (CBaseEntity*)pEntity;
@@ -2166,7 +2088,7 @@ void EW_OnWeaponDeleted(CBaseEntity* pEntity)
bool EW_Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pPlayerWeapon)
{
// false=block it, true=dont block it
- if (!g_pEWHandler || !g_pEWHandler->bConfigLoaded)
+ if (!g_pEWHandler || !g_pEWHandler->m_bConfigLoaded)
return true;
CCSPlayerPawn* pPawn = pWeaponServices->GetPawn();
@@ -2210,7 +2132,7 @@ bool EW_Detour_CCSPlayer_WeaponServices_CanUse(CCSPlayer_WeaponServices* pWeapon
void EW_Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pWeapon)
{
- if (!g_pEWHandler || !g_pEWHandler->bConfigLoaded)
+ if (!g_pEWHandler || !g_pEWHandler->m_bConfigLoaded)
return;
CCSPlayerPawn* pPawn = pWeaponServices->GetPawn();
@@ -2230,7 +2152,7 @@ void EW_Detour_CCSPlayer_WeaponServices_EquipWeapon(CCSPlayer_WeaponServices* pW
void EW_DropWeapon(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon* pWeapon)
{
- if (!g_pEWHandler || !g_pEWHandler->bConfigLoaded)
+ if (!g_pEWHandler || !g_pEWHandler->m_bConfigLoaded)
return;
CCSPlayerPawn* pPawn = pWeaponServices->GetPawn();
@@ -2317,14 +2239,9 @@ void EW_PlayerDisconnect(int slot)
g_pEWHandler->PlayerDrop(EWDropReason::Disconnect, -1, pController);
}
-bool EW_IsFireOutputHooked()
-{
- return std::any_of(mapIOFunctions.begin(), mapIOFunctions.end(), [](const auto& p) { return p.first == "entwatch"; });
-}
-
void EW_FireOutput(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay)
{
- if (!EW_IsFireOutputHooked() || !pCaller)
+ if (!g_pEWHandler->IsConfigLoaded() || !pCaller)
return;
for (int i = 0; i < (g_pEWHandler->vecItems).size(); i++)
@@ -2399,7 +2316,7 @@ CON_COMMAND_CHAT_FLAGS(ew_reload, "- Reloads the current map's entwatch config",
// LoadConfig unloads the current config already
g_pEWHandler->LoadMapConfig(GetGlobals()->mapname.ToCStr());
- if (!g_pEWHandler->bConfigLoaded)
+ if (!g_pEWHandler->m_bConfigLoaded)
{
ClientPrint(player, HUD_PRINTTALK, EW_PREFIX "Error reloading config, check console log for details.");
return;
diff --git a/src/entwatch.h b/src/entwatch.h
index 32ce33a30..a318446dd 100644
--- a/src/entwatch.h
+++ b/src/entwatch.h
@@ -23,6 +23,7 @@
#include "ctimer.h"
#include "eventlistener.h"
#include "gamesystem.h"
+#include "khook.hpp"
#include "vendor/nlohmann/json_fwd.hpp"
using ordered_json = nlohmann::ordered_json;
@@ -215,26 +216,35 @@ struct EActiveTransfer
class CEWHandler
{
public:
- CEWHandler()
+ CEWHandler() :
+ m_bConfigLoaded(false),
+ m_hBaseButtonUse(0U, this, &CEWHandler::Hook_Use, nullptr),
+ m_hPhysBoxUse(0U, this, &CEWHandler::Hook_Use, nullptr),
+ m_hRotButtonUse(0U, this, &CEWHandler::Hook_Use, nullptr),
+ m_hMomentaryRotButtonUse(0U, this, &CEWHandler::Hook_Use, nullptr),
+ m_hPhysicalButtonUse(0U, this, &CEWHandler::Hook_Use, nullptr),
+ m_hTriggerTeleportStartTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerOnceStartTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerMultipleStartTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerTeleportTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerOnceTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerMultipleTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerTeleportEndTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerOnceEndTouch(0U, this, &CEWHandler::Hook_Touch, nullptr),
+ m_hTriggerMultipleEndTouch(0U, this, &CEWHandler::Hook_Touch, nullptr)
{
- bConfigLoaded = false;
-
- iBaseBtnUseHookId = -1;
- iPhysboxUseHookId = -1;
- iPhysicalBtnUseHookId = -1;
- iRotBtnUseHookId = -1;
- iMomRotBtnUseHookId = -1;
-
- for (int i = 0; i < 3; i++)
- {
- iTriggerTeleportTouchHooks[i] = -1;
- iTriggerMultipleTouchHooks[i] = -1;
- iTriggerOnceTouchHooks[i] = -1;
- }
+ CreateHooks();
}
- bool bConfigLoaded;
- bool IsConfigLoaded() { return bConfigLoaded; }
+ ~CEWHandler()
+ {
+ RemoveHooks();
+ }
+
+ void CreateHooks();
+ void RemoveHooks();
+
+ bool IsConfigLoaded() { return m_bConfigLoaded; }
void UnLoadConfig();
void LoadMapConfig(const char* sMapName);
@@ -250,10 +260,8 @@ class CEWHandler
void RegisterHandler(CBaseEntity* pEnt);
bool RegisterTrigger(CBaseEntity* pEnt);
- void AddTouchHook(CBaseEntity* pEnt);
- void Hook_Touch(CBaseEntity* pOther);
+ KHook::Return Hook_Touch(CBaseEntity* pThis, CBaseEntity* pOther);
bool RemoveTrigger(CBaseEntity* pEnt);
- void RemoveAllTriggers();
void RemoveHandler(CBaseEntity* pEnt);
void ResetAllClantags();
@@ -263,30 +271,35 @@ class CEWHandler
void PlayerDrop(EWDropReason reason, int iItemInstance, CCSPlayerController* pController);
void Transfer(CCSPlayerController* pCaller, int iItemInstance, CHandle hReceiver);
- void AddUseHook(CBaseEntity* pEnt);
- void RemoveUseHook(CBaseEntity* pEnt);
- void RemoveAllUseHooks();
- void Hook_Use(InputData_t* pInput);
+ 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 */
std::vector> vecHookedTriggers;
- int iTriggerTeleportTouchHooks[3];
- int iTriggerMultipleTouchHooks[3];
- int iTriggerOnceTouchHooks[3];
-
std::vector> vecUseHookedEntities;
- int iBaseBtnUseHookId;
- int iPhysboxUseHookId;
- int iPhysicalBtnUseHookId;
- int iRotBtnUseHookId;
- int iMomRotBtnUseHookId;
std::weak_ptr m_pHudTimer;
+ bool m_bConfigLoaded;
std::map> mapTransfers; // Any etransfers that target multiple items
std::vector> vecActiveTransfers; // Active transfers where only the receiver can pickup the weapon
+
+ 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;
@@ -302,7 +315,6 @@ void EW_DropWeapon(CCSPlayer_WeaponServices* pWeaponServices, CBasePlayerWeapon*
void EW_PlayerDeath(IGameEvent* pEvent);
void EW_PlayerDeathPre(CCSPlayerController* pController);
void EW_PlayerDisconnect(int slot);
-bool EW_IsFireOutputHooked();
void EW_FireOutput(const CEntityIOOutput* pThis, CEntityInstance* pActivator, CEntityInstance* pCaller, const CVariant* value, float flDelay);
int GetTemplateSuffixNumber(const char* szName);
float EW_UpdateHud();
\ No newline at end of file
diff --git a/src/gameconfig.cpp b/src/gameconfig.cpp
index ceab26717..b4295bac1 100644
--- a/src/gameconfig.cpp
+++ b/src/gameconfig.cpp
@@ -1,5 +1,6 @@
#include "gameconfig.h"
#include "addresses.h"
+#include "khook.hpp"
#undef snprintf
#include "vendor/nlohmann/json.hpp"
@@ -219,17 +220,12 @@ void* CGameConfig::ResolveSignature(const char* name)
return nullptr;
}
- size_t iLength = 0;
- byte* pSignature = IDASigToUint8Array(signature, iLength);
- if (!pSignature)
- return nullptr;
-
- int error;
+ std::vector bytes;
- address = (*module)->FindSignature(pSignature, iLength, error);
+ if (!IsValidIDASignature(signature, bytes))
+ return nullptr;
- if (error == SIG_FOUND_MULTIPLE)
- Panic("!!!!!!!!!! Signature for %s occurs multiple times! Using first match but this might end up crashing!\n", name);
+ address = KHook::LookupSignature((*module)->m_base, (*module)->m_size, signature);
}
if (!address)
@@ -268,7 +264,7 @@ bool CGameConfig::ParsePatternBytes(const char* pattern, std::vector& b
if (*cursor == '?')
{
- bytes.push_back('\x2A');
+ bytes.push_back('?');
cursor++;
if (*cursor == '?')
cursor++;
@@ -287,21 +283,30 @@ bool CGameConfig::ParsePatternBytes(const char* pattern, std::vector& b
return !bytes.empty();
}
-byte* CGameConfig::IDASigToUint8Array(const char* signature, size_t& length)
+bool CGameConfig::IsValidIDASignature(const char* signature, std::vector& bytes)
{
if (!signature || strlen(signature) <= 0)
{
Panic("Invalid IDA signature string\n");
- return nullptr;
+ return false;
}
- std::vector bytes;
if (!ParsePatternBytes(signature, bytes))
{
Panic("Invalid IDA signature format \"%s\"\n", signature);
- return nullptr;
+ return false;
}
+ return true;
+}
+
+byte* CGameConfig::IDASigToUint8Array(const char* signature, size_t& length)
+{
+ std::vector bytes;
+
+ if (!IsValidIDASignature(signature, bytes))
+ return nullptr;
+
length = bytes.size();
uint8_t* dest = new uint8_t[length];
diff --git a/src/gameconfig.h b/src/gameconfig.h
index 9a296902f..bf5de6fac 100644
--- a/src/gameconfig.h
+++ b/src/gameconfig.h
@@ -27,6 +27,7 @@ class CGameConfig
void* ResolveSignature(const char* name);
int ParseHexNibble(char c);
bool ParsePatternBytes(const char* pattern, std::vector& bytes);
+ bool IsValidIDASignature(const char* signature, std::vector& bytes);
byte* IDASigToUint8Array(const char* signature, size_t& length);
private:
diff --git a/src/mempatch.cpp b/src/mempatch.cpp
index 0cec70b55..5db8fb4ad 100644
--- a/src/mempatch.cpp
+++ b/src/mempatch.cpp
@@ -22,10 +22,11 @@
#include "tier0/dbg.h"
#include "tier1/strtools.h"
#include "utils/module.h"
+#include "khook.hpp"
#include "tier0/memdbgon.h"
-bool CMemPatch::PerformPatch(CGameConfig* gameConfig)
+bool CMemPatch::PerformPatch()
{
// We're patched already
if (m_pOriginalBytes)
@@ -34,25 +35,25 @@ bool CMemPatch::PerformPatch(CGameConfig* gameConfig)
// If we already have an address, no need to look for it again
if (!m_pPatchAddress)
{
- m_pPatchAddress = (uintptr_t)gameConfig->ResolveSignature(m_pSignatureName);
+ m_pPatchAddress = (uintptr_t)g_GameConfig->ResolveSignature(m_pSignatureName);
if (!m_pPatchAddress)
return false;
}
- const char* patch = gameConfig->GetPatch(m_pszName);
+ const char* patch = g_GameConfig->GetPatch(m_pszName);
if (!patch)
{
Panic("Failed to find patch for %s\n", m_pszName);
return false;
}
- m_pPatch = gameConfig->IDASigToUint8Array(patch, m_iPatchLength);
+ m_pPatch = g_GameConfig->IDASigToUint8Array(patch, m_iPatchLength);
if (!m_pPatch)
return false;
if (V_strcmp(m_pOffsetName, ""))
{
- m_iOffset = gameConfig->GetOffset(m_pOffsetName);
+ m_iOffset = g_GameConfig->GetOffset(m_pOffsetName);
if (m_iOffset == -1)
{
Panic("Failed to find offset %s for patch %s\n", m_pOffsetName, m_pszName);
diff --git a/src/mempatch.h b/src/mempatch.h
index ef4899bc0..1d21ec122 100644
--- a/src/mempatch.h
+++ b/src/mempatch.h
@@ -38,7 +38,7 @@ class CMemPatch
m_iOffset = 0;
}
- bool PerformPatch(CGameConfig* gameConfig);
+ bool PerformPatch();
void UndoPatch();
uintptr_t GetPatchAddress() { return m_pPatchAddress; }
diff --git a/src/patches.cpp b/src/patches.cpp
index 573820cda..55df3bac2 100644
--- a/src/patches.cpp
+++ b/src/patches.cpp
@@ -40,18 +40,18 @@ CConVar cs2f_movement_unlocker_enable("cs2f_movement_unlocker_enable", FCV
[](CConVar* cvar, CSplitScreenSlot slot, const bool* new_val, const bool* old_val) {
// Movement unlocker is always the first patch
if (*new_val)
- g_CommonPatches[0].PerformPatch(g_GameConfig);
+ g_CommonPatches[0].PerformPatch();
else
g_CommonPatches[0].UndoPatch();
});
-bool InitPatches(CGameConfig* g_GameConfig)
+bool InitPatches()
{
bool success = true;
// Skip first two patches (movement unlocker & bot nav ignore), they are patched elsewhere
for (int i = 2; i < sizeof(g_CommonPatches) / sizeof(*g_CommonPatches); i++)
- if (!g_CommonPatches[i].PerformPatch(g_GameConfig))
+ if (!g_CommonPatches[i].PerformPatch())
success = false;
return success;
diff --git a/src/patches.h b/src/patches.h
index 0fd2b2015..7edf24784 100644
--- a/src/patches.h
+++ b/src/patches.h
@@ -5,5 +5,5 @@
extern CMemPatch g_CommonPatches[];
-bool InitPatches(CGameConfig* gameConfig);
+bool InitPatches();
void UndoPatches();
diff --git a/src/playermanager.h b/src/playermanager.h
index c5d2d713f..38c02ff14 100644
--- a/src/playermanager.h
+++ b/src/playermanager.h
@@ -36,6 +36,7 @@
extern CConVar g_cvarFlashLightTransmitOthers;
extern CConVar g_cvarFlashLightAttachment;
+extern CConVar g_cvarEnableMapSteamIds;
#define NO_TARGET_BLOCKS (0)
#define NO_RANDOM (1 << 1)
diff --git a/src/utils/module.h b/src/utils/module.h
index 0bd831bff..543af18dd 100644
--- a/src/utils/module.h
+++ b/src/utils/module.h
@@ -31,13 +31,6 @@
#include
#endif
-enum SigError
-{
- SIG_OK,
- SIG_NOT_FOUND,
- SIG_FOUND_MULTIPLE,
-};
-
// equivalent to FindSignature, but allows for multiple signatures to be found and iterated over
class SignatureIterator
{
@@ -53,7 +46,7 @@ class SignatureIterator
for (size_t i = 0; i < m_iSize; i++)
{
size_t Matches = 0;
- while (*(m_pCurrent + i + Matches) == m_pSignature[Matches] || (allowWildcard && m_pSignature[Matches] == '\x2A'))
+ while (*(m_pCurrent + i + Matches) == m_pSignature[Matches] || (allowWildcard && m_pSignature[Matches] == '?'))
{
Matches++;
if (Matches == m_iSigLength)
@@ -108,40 +101,6 @@ class CModule
Message("Initialized module %s base: 0x%p | size: %d\n", m_pszModule, m_base, m_size);
}
- void* FindSignature(const byte* pData, size_t iSigLength, int& error)
- {
- unsigned char* pMemory;
- void* return_addr = nullptr;
- error = 0;
-
- pMemory = (byte*)m_base;
-
- for (size_t i = 0; i < m_size; i++)
- {
- size_t Matches = 0;
- while (i + Matches < m_size && (*(pMemory + i + Matches) == pData[Matches] || pData[Matches] == '\x2A'))
- {
- Matches++;
- if (Matches == iSigLength)
- {
- if (return_addr)
- {
- error = SIG_FOUND_MULTIPLE;
- return return_addr;
- }
-
- return_addr = (void*)(pMemory + i);
- break;
- }
- }
- }
-
- if (!return_addr)
- error = SIG_NOT_FOUND;
-
- return return_addr;
- }
-
void* FindInterface(const char* name)
{
CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(m_hModule, "CreateInterface");
diff --git a/src/utils/sighook.h b/src/utils/sighook.h
new file mode 100644
index 000000000..7adc70483
--- /dev/null
+++ b/src/utils/sighook.h
@@ -0,0 +1,147 @@
+/**
+ * =============================================================================
+ * 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 "../gameconfig.h"
+#include "khook.hpp"
+
+class CSigHookBase
+{
+public:
+ CSigHookBase(const char* pSignature) :
+ m_pSignatureName(pSignature)
+ {
+ }
+
+ virtual void Configure() = 0;
+
+protected:
+ const char* m_pSignatureName;
+};
+
+extern std::vector& GetSigHookList();
+void InitSigHooks();
+
+template
+class CSigHookFunction : public CSigHookBase
+{
+public:
+ using fnCallback = KHook::Return (*)(ARGS...);
+
+ CSigHookFunction(const char* pSignature, fnCallback cbPre = nullptr, fnCallback cbPost = nullptr) :
+ CSigHookBase(pSignature), m_hook(cbPre, cbPost)
+ {
+ GetSigHookList().push_back(this);
+ }
+
+ virtual void Configure() override
+ {
+ auto address = g_GameConfig->ResolveSignature(m_pSignatureName);
+
+ if (!address)
+ g_bRequiredInitLoaded = false;
+
+ m_hook.Configure(address);
+
+ Message("Hooked %s at 0x%p\n", m_pSignatureName, address);
+ }
+
+ RETURN CallOriginal(ARGS... args)
+ {
+ return m_hook.CallOriginal(args...);
+ }
+
+private:
+ KHook::Function m_hook;
+};
+
+template
+auto MakeSigHookFunction(const char* pSignature, KHook::Return (*cbPre)(ARGS...), std::nullptr_t cbPost)
+{
+ return std::make_unique>(pSignature, cbPre, nullptr);
+}
+
+template
+auto MakeSigHookFunction(const char* pSignature, std::nullptr_t cbPre, KHook::Return (*cbPost)(ARGS...))
+{
+ return std::make_unique>(pSignature, nullptr, cbPost);
+}
+
+template
+auto MakeSigHookFunction(const char* pSignature, KHook::Return (*cbPre)(ARGS...), KHook::Return (*cbPost)(ARGS...))
+{
+ return std::make_unique>(pSignature, cbPre, cbPost);
+}
+
+#define SIG_HOOK_FUNCTION(name, pre, post) \
+ static auto hook##name = MakeSigHookFunction(#name, pre, post)
+
+template
+class CSigHookMember : public CSigHookBase
+{
+public:
+ using fnCallback = KHook::Return (*)(CLASS*, ARGS...);
+
+ CSigHookMember(const char* pSignature, fnCallback cbPre = nullptr, fnCallback cbPost = nullptr) :
+ CSigHookBase(pSignature), m_hook(cbPre, cbPost)
+ {
+ GetSigHookList().push_back(this);
+ }
+
+ virtual void Configure() override
+ {
+ auto address = g_GameConfig->ResolveSignature(m_pSignatureName);
+
+ if (!address)
+ g_bRequiredInitLoaded = false;
+
+ m_hook.Configure(address);
+
+ Message("Hooked %s at 0x%p\n", m_pSignatureName, address);
+ }
+
+ RETURN CallOriginal(CLASS* this_ptr, ARGS... args)
+ {
+ return m_hook.CallOriginal(this_ptr, args...);
+ }
+
+private:
+ KHook::Member m_hook;
+};
+
+template
+auto MakeSigHookMember(const char* pSignature, KHook::Return (*cbPre)(CLASS*, ARGS...), std::nullptr_t cbPost)
+{
+ return std::make_unique