Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions AMBuildScript
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ class ExtensionConfig(object):
'-Wno-tautological-overlap-compare',
]

if cxx.version >= 'clang-16.0':
cxx.cxxflags += [
'-Wno-nontrivial-memcall',
]

if have_gcc:
cxx.cflags += ['-mfpmath=sse']
cxx.cflags += ['-Wno-maybe-uninitialized']
Expand Down Expand Up @@ -360,6 +365,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'])]

Expand Down
129 changes: 83 additions & 46 deletions src/extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
#include "script_context.h"
#include "callback_manager.h"

#include <khook.hpp>

CVScriptExtension g_VScriptExt;
CGlobalVars *gpGlobals = nullptr;
uint32_t g_vmGeneration = 0;
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<IScriptManager, IScriptVM *, ScriptLanguage_t> g_HookCreateVM;
static KHook::Virtual<IScriptManager, void, IScriptVM *> g_HookDestroyVM;
static KHook::Virtual<IScriptVM, bool, ScriptClassDesc_t *> g_HookRegisterClass;
static KHook::Virtual<IScriptVM, void, ScriptErrorFunc_t> g_HookSetErrorCallback;

static void OnScriptOutput(const char *pszText)
{
Expand Down Expand Up @@ -41,49 +43,99 @@ static bool OnScriptError(ScriptErrorLevel_t eLevel, const char *pszText)
return false;
}

void CVScriptExtension::Hook_SetErrorCallback(ScriptErrorFunc_t pFunc)
KHook::Return<IScriptVM *> CVScriptExtension::Hook_CreateVM(IScriptManager *pManager, ScriptLanguage_t language)
{
IScriptVM *pVM = g_HookCreateVM.CallOriginal(pManager, language);
OnVMCreated(pVM);
return {KHook::Action::Supersede, pVM};
}

KHook::Return<void> CVScriptExtension::Hook_DestroyVM(IScriptManager *pManager, IScriptVM *pVM)
{
OnVMDestroyed(pVM);
g_HookDestroyVM.CallOriginal(pManager, pVM);
OnVMDestroyedPost();
return {KHook::Action::Supersede};
}

KHook::Return<bool> CVScriptExtension::Hook_RegisterClass(IScriptVM *pVM, ScriptClassDesc_t *pClassDesc)
{
g_CallbackManager.OnRegisterClass(pClassDesc);
return {KHook::Action::Ignore};
}

KHook::Return<void> 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)
{
SetCurrentVM(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);

RETURN_META_VALUE(MRES_IGNORED, true);
if (m_pOnVMInit)
m_pOnVMInit->Execute(nullptr);

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_CallbackManager.OnVMShutdown();
Expand All @@ -95,14 +147,11 @@ void CVScriptExtension::Hook_DestroyVM(IScriptVM *pVM)

SetCurrentVM(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)
Expand All @@ -111,28 +160,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;
}
Expand Down
18 changes: 13 additions & 5 deletions src/extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,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;
Expand All @@ -99,11 +103,15 @@ class CVScriptExtension

void SetCurrentVM(IScriptVM *pVM);

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<IScriptVM *> Hook_CreateVM(IScriptManager *pManager, ScriptLanguage_t language);
KHook::Return<void> Hook_DestroyVM(IScriptManager *pManager, IScriptVM *pVM);
KHook::Return<bool> Hook_RegisterClass(IScriptVM *pVM, ScriptClassDesc_t *pClassDesc);
KHook::Return<void> Hook_SetErrorCallback(IScriptVM *pVM, ScriptErrorFunc_t pFunc);
};

void ReleaseOwnedHScript(IScriptVM *pVM, HSCRIPT hScript, HScriptType type);
Expand Down
Loading