From 7042191df6aeb91e9f146489fa4632a405fd93c5 Mon Sep 17 00:00:00 2001 From: Mikusch <25514044+Mikusch@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:09:17 +0200 Subject: [PATCH 1/4] Use KHook --- AMBuildScript | 4 ++ src/extension.cpp | 129 +++++++++++++++++++++++++++++----------------- src/extension.h | 18 +++++-- 3 files changed, 100 insertions(+), 51 deletions(-) diff --git a/AMBuildScript b/AMBuildScript index ac071ab..e2b86e8 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -360,6 +360,10 @@ class ExtensionConfig(object): os.path.join(self.mms_root, 'core', 'sourcehook'), ] + khook_include = os.path.join(self.mms_root, 'third_party', 'khook', 'include') + if os.path.isdir(khook_include): + compiler.cxxincludes += [khook_include] + for other_sdk in self.sdk_manifests: compiler.defines += ['SE_{}={}'.format(other_sdk['define'], other_sdk['code'])] diff --git a/src/extension.cpp b/src/extension.cpp index c0a198d..fcbc983 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -3,15 +3,17 @@ #include "script_context.h" #include "callback_manager.h" +#include + CVScriptExtension g_VScriptExt; CGlobalVars *gpGlobals = nullptr; uint32_t g_vmGeneration = 0; // Used by ScriptCallContext to invalidate cached function lookups across map changes SMEXT_LINK(&g_VScriptExt); -SH_DECL_HOOK1(IScriptManager, CreateVM, SH_NOATTRIB, 0, IScriptVM *, ScriptLanguage_t); -SH_DECL_HOOK1_void(IScriptManager, DestroyVM, SH_NOATTRIB, 0, IScriptVM *); -SH_DECL_HOOK1(IScriptVM, RegisterClass, SH_NOATTRIB, 0, bool, ScriptClassDesc_t *); -SH_DECL_HOOK1_void(IScriptVM, SetErrorCallback, SH_NOATTRIB, 0, ScriptErrorFunc_t); +static KHook::Virtual g_HookCreateVM; +static KHook::Virtual g_HookDestroyVM; +static KHook::Virtual g_HookRegisterClass; +static KHook::Virtual g_HookSetErrorCallback; static void OnScriptOutput(const char *pszText) { @@ -41,49 +43,99 @@ static bool OnScriptError(ScriptErrorLevel_t eLevel, const char *pszText) return false; } -void CVScriptExtension::Hook_SetErrorCallback(ScriptErrorFunc_t pFunc) +KHook::Return CVScriptExtension::Hook_CreateVM(IScriptManager *pManager, ScriptLanguage_t language) +{ + IScriptVM *pVM = g_HookCreateVM.CallOriginal(pManager, language); + OnVMCreated(pVM); + return {KHook::Action::Supersede, pVM}; +} + +KHook::Return CVScriptExtension::Hook_DestroyVM(IScriptManager *pManager, IScriptVM *pVM) +{ + OnVMDestroyed(pVM); + g_HookDestroyVM.CallOriginal(pManager, pVM); + OnVMDestroyedPost(); + return {KHook::Action::Supersede}; +} + +KHook::Return CVScriptExtension::Hook_RegisterClass(IScriptVM *pVM, ScriptClassDesc_t *pClassDesc) +{ + g_CallbackManager.OnRegisterClass(pClassDesc); + return {KHook::Action::Ignore}; +} + +KHook::Return CVScriptExtension::Hook_SetErrorCallback(IScriptVM *pVM, ScriptErrorFunc_t pFunc) { g_pOriginalErrorCallback = pFunc; - SH_CALL(m_pScriptVM, &IScriptVM::SetErrorCallback)(&OnScriptError); - RETURN_META(MRES_SUPERCEDE); + g_HookSetErrorCallback.CallOriginal(pVM, &OnScriptError); + return {KHook::Action::Supersede}; } -IScriptVM *CVScriptExtension::Hook_CreateVM(ScriptLanguage_t language) +void CVScriptExtension::InstallManagerHooks() { - IScriptVM *pVM = META_RESULT_ORIG_RET(IScriptVM *); + g_HookCreateVM.AddContext(this, &CVScriptExtension::Hook_CreateVM, nullptr); + g_HookCreateVM.Configure(&IScriptManager::CreateVM); - if (pVM) - { - m_pScriptVM = pVM; + g_HookDestroyVM.AddContext(this, &CVScriptExtension::Hook_DestroyVM, nullptr); + g_HookDestroyVM.Configure(&IScriptManager::DestroyVM); - SH_ADD_HOOK(IScriptVM, RegisterClass, pVM, SH_MEMBER(this, &CVScriptExtension::Hook_RegisterClass), false); - SH_ADD_HOOK(IScriptVM, SetErrorCallback, pVM, SH_MEMBER(this, &CVScriptExtension::Hook_SetErrorCallback), false); + g_HookRegisterClass.AddContext(this, &CVScriptExtension::Hook_RegisterClass, nullptr); + g_HookRegisterClass.Configure(&IScriptVM::RegisterClass); - pVM->SetOutputCallback(&OnScriptOutput); - SH_CALL(pVM, &IScriptVM::SetErrorCallback)(&OnScriptError); // bypass hook + g_HookSetErrorCallback.AddContext(this, &CVScriptExtension::Hook_SetErrorCallback, nullptr); + g_HookSetErrorCallback.Configure(&IScriptVM::SetErrorCallback); - if (m_pOnVMInit) - m_pOnVMInit->Execute(nullptr); + g_HookCreateVM.Add(m_pScriptManager); + g_HookDestroyVM.Add(m_pScriptManager); +} - g_CallbackManager.OnVMInitialized(pVM); +void CVScriptExtension::RemoveManagerHooks() +{ + if (m_pScriptManager) + { + g_HookCreateVM.Remove(m_pScriptManager); + g_HookDestroyVM.Remove(m_pScriptManager); } +} - RETURN_META_VALUE(MRES_IGNORED, pVM); +void CVScriptExtension::InstallVMHooks(IScriptVM *pVM) +{ + g_HookRegisterClass.Add(pVM); + g_HookSetErrorCallback.Add(pVM); } -bool CVScriptExtension::Hook_RegisterClass(ScriptClassDesc_t *pClassDesc) +void CVScriptExtension::RemoveVMHooks() { - g_CallbackManager.OnRegisterClass(pClassDesc); + if (m_pScriptVM) + { + g_HookRegisterClass.Remove(m_pScriptVM); + g_HookSetErrorCallback.Remove(m_pScriptVM); + } +} + +void CVScriptExtension::OnVMCreated(IScriptVM *pVM) +{ + if (!pVM) + return; + + m_pScriptVM = pVM; + + InstallVMHooks(pVM); + + pVM->SetOutputCallback(&OnScriptOutput); + g_HookSetErrorCallback.CallOriginal(pVM, &OnScriptError); + + if (m_pOnVMInit) + m_pOnVMInit->Execute(nullptr); - RETURN_META_VALUE(MRES_IGNORED, true); + g_CallbackManager.OnVMInitialized(pVM); } -void CVScriptExtension::Hook_DestroyVM(IScriptVM *pVM) +void CVScriptExtension::OnVMDestroyed(IScriptVM *pVM) { if (pVM && pVM == m_pScriptVM) { - SH_REMOVE_HOOK(IScriptVM, RegisterClass, pVM, SH_MEMBER(this, &CVScriptExtension::Hook_RegisterClass), false); - SH_REMOVE_HOOK(IScriptVM, SetErrorCallback, pVM, SH_MEMBER(this, &CVScriptExtension::Hook_SetErrorCallback), false); + RemoveVMHooks(); g_pOriginalErrorCallback = nullptr; g_vmGeneration++; @@ -97,14 +149,11 @@ void CVScriptExtension::Hook_DestroyVM(IScriptVM *pVM) m_pScriptVM = nullptr; } - - RETURN_META(MRES_IGNORED); } -void CVScriptExtension::Hook_DestroyVM_Post(IScriptVM *pVM) +void CVScriptExtension::OnVMDestroyedPost() { g_CallbackManager.CleanupStaleRegistrations(); - RETURN_META(MRES_IGNORED); } bool CVScriptExtension::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) @@ -113,28 +162,16 @@ bool CVScriptExtension::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t max GET_V_IFACE_CURRENT(GetEngineFactory, m_pScriptManager, IScriptManager, VSCRIPT_INTERFACE_VERSION); - SH_ADD_HOOK(IScriptManager, CreateVM, m_pScriptManager, SH_MEMBER(this, &CVScriptExtension::Hook_CreateVM), true); - SH_ADD_HOOK(IScriptManager, DestroyVM, m_pScriptManager, SH_MEMBER(this, &CVScriptExtension::Hook_DestroyVM), false); - SH_ADD_HOOK(IScriptManager, DestroyVM, m_pScriptManager, SH_MEMBER(this, &CVScriptExtension::Hook_DestroyVM_Post), true); + InstallManagerHooks(); return true; } bool CVScriptExtension::SDK_OnMetamodUnload(char *error, size_t maxlen) { - if (m_pScriptVM) - { - SH_REMOVE_HOOK(IScriptVM, RegisterClass, m_pScriptVM, SH_MEMBER(this, &CVScriptExtension::Hook_RegisterClass), false); - SH_REMOVE_HOOK(IScriptVM, SetErrorCallback, m_pScriptVM, SH_MEMBER(this, &CVScriptExtension::Hook_SetErrorCallback), false); - g_pOriginalErrorCallback = nullptr; - } - - if (m_pScriptManager) - { - SH_REMOVE_HOOK(IScriptManager, CreateVM, m_pScriptManager, SH_MEMBER(this, &CVScriptExtension::Hook_CreateVM), true); - SH_REMOVE_HOOK(IScriptManager, DestroyVM, m_pScriptManager, SH_MEMBER(this, &CVScriptExtension::Hook_DestroyVM), false); - SH_REMOVE_HOOK(IScriptManager, DestroyVM, m_pScriptManager, SH_MEMBER(this, &CVScriptExtension::Hook_DestroyVM_Post), true); - } + RemoveVMHooks(); + g_pOriginalErrorCallback = nullptr; + RemoveManagerHooks(); return true; } diff --git a/src/extension.h b/src/extension.h index fda099a..e8f9549 100644 --- a/src/extension.h +++ b/src/extension.h @@ -66,6 +66,10 @@ class CVScriptExtension IForward *GetOnScriptPrintForward() const { return m_pOnScriptPrint; } IForward *GetOnScriptErrorForward() const { return m_pOnScriptError; } + void OnVMCreated(IScriptVM *pVM); + void OnVMDestroyed(IScriptVM *pVM); + void OnVMDestroyedPost(); + private: IScriptManager *m_pScriptManager = nullptr; IScriptVM *m_pScriptVM = nullptr; @@ -89,11 +93,15 @@ class CVScriptExtension void ClearEntityHandleCache(); - IScriptVM *Hook_CreateVM(ScriptLanguage_t language); - void Hook_DestroyVM(IScriptVM *pVM); - void Hook_DestroyVM_Post(IScriptVM *pVM); - bool Hook_RegisterClass(ScriptClassDesc_t *pClassDesc); - void Hook_SetErrorCallback(ScriptErrorFunc_t pFunc); + void InstallManagerHooks(); + void RemoveManagerHooks(); + void InstallVMHooks(IScriptVM *pVM); + void RemoveVMHooks(); + + KHook::Return Hook_CreateVM(IScriptManager *pManager, ScriptLanguage_t language); + KHook::Return Hook_DestroyVM(IScriptManager *pManager, IScriptVM *pVM); + KHook::Return Hook_RegisterClass(IScriptVM *pVM, ScriptClassDesc_t *pClassDesc); + KHook::Return Hook_SetErrorCallback(IScriptVM *pVM, ScriptErrorFunc_t pFunc); }; void ReleaseOwnedHScript(IScriptVM *pVM, HSCRIPT hScript, HScriptType type); From ad902ac1eb228700f13437e696a32efbe99e3a07 Mon Sep 17 00:00:00 2001 From: Mikusch <25514044+Mikusch@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:55:56 +0200 Subject: [PATCH 2/4] Suppress newer-clang SDK warnings under -Werror --- AMBuildScript | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AMBuildScript b/AMBuildScript index e2b86e8..c106a3d 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -249,6 +249,13 @@ class ExtensionConfig(object): '-Wno-tautological-overlap-compare', ] + if cxx.version >= 'clang-16.0': + cxx.cxxflags += [ + '-Wno-nontrivial-memcall', + '-Wno-tautological-constant-out-of-range-compare', + '-Wno-undefined-bool-conversion', + ] + if have_gcc: cxx.cflags += ['-mfpmath=sse'] cxx.cflags += ['-Wno-maybe-uninitialized'] From f8d46238e6949f1af64e23ef88feb346f6a58fd9 Mon Sep 17 00:00:00 2001 From: Mikusch <25514044+Mikusch@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:14:53 +0200 Subject: [PATCH 3/4] CI: build Linux jobs against debian11-clang22 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 866f28d..80ca78a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,11 +18,11 @@ jobs: ref: master build: - os: ubuntu-latest - container: ghcr.io/alliedmodders/build-containers/debian11:latest + container: ghcr.io/alliedmodders/build-containers/debian11-clang22:latest target: x86 platform: linux32 - os: ubuntu-latest - container: ghcr.io/alliedmodders/build-containers/debian11:latest + container: ghcr.io/alliedmodders/build-containers/debian11-clang22:latest target: x86_64 platform: linux64 - os: windows-latest From 357aa958fd3d5059d6bae21249bc41fa6f103545 Mon Sep 17 00:00:00 2001 From: Mikusch <25514044+Mikusch@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:47:02 +0200 Subject: [PATCH 4/4] Drop redundant clang warning suppressions --- AMBuildScript | 2 -- 1 file changed, 2 deletions(-) diff --git a/AMBuildScript b/AMBuildScript index c106a3d..17bf812 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -252,8 +252,6 @@ class ExtensionConfig(object): if cxx.version >= 'clang-16.0': cxx.cxxflags += [ '-Wno-nontrivial-memcall', - '-Wno-tautological-constant-out-of-range-compare', - '-Wno-undefined-bool-conversion', ] if have_gcc: