diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h index a1c1e856..e3744752 100644 --- a/sp/src/public/vscript/ivscript.h +++ b/sp/src/public/vscript/ivscript.h @@ -863,6 +863,15 @@ public: //-------------------------------------------------------- virtual ScriptStatus_t ExecuteFunction( HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; +#ifdef MAPBASE_VSCRIPT + //-------------------------------------------------------- + // Hooks + //-------------------------------------------------------- + virtual bool ScopeIsHooked( HSCRIPT hScope, const char *pszEventName ) = 0; + virtual HSCRIPT LookupHookFunction( const char *pszEventName, HSCRIPT hScope, bool &bLegacy ) = 0; + virtual ScriptStatus_t ExecuteHookFunction( const char *pszEventName, HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) = 0; +#endif + //-------------------------------------------------------- // External functions //-------------------------------------------------------- @@ -1576,12 +1585,13 @@ struct ScriptHook_t // Cached for when CanRunInScope() is called before Call() HSCRIPT m_hFunc; + bool m_bLegacy; // Checks if there's a function of this name which would run in this scope HSCRIPT CanRunInScope( HSCRIPT hScope ) { extern IScriptVM *g_pScriptVM; - m_hFunc = g_pScriptVM->LookupFunction( m_desc.m_pszScriptName, hScope ); + m_hFunc = g_pScriptVM->LookupHookFunction( m_desc.m_pszScriptName, hScope, m_bLegacy ); return m_hFunc; } @@ -1605,7 +1615,8 @@ struct ScriptHook_t // Make sure we have a function in this scope if (!m_hFunc && !CanRunInScope(hScope)) return false; - else + // Legacy + else if (m_bLegacy) { for (int i = 0; i < m_desc.m_Parameters.Count(); i++) { @@ -1626,6 +1637,15 @@ struct ScriptHook_t return true; } + // New Hook System + else + { + g_pScriptVM->ExecuteHookFunction( m_desc.m_pszScriptName, m_hFunc, pArgs, m_desc.m_Parameters.Count(), pReturn, hScope, true ); + if (bRelease) + g_pScriptVM->ReleaseFunction( m_hFunc ); + m_hFunc = NULL; + return true; + } return false; } diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 2c80041b..e671f5ff 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -161,6 +161,13 @@ public: //-------------------------------------------------------- virtual ScriptStatus_t ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) override; + //-------------------------------------------------------- + // Hooks + //-------------------------------------------------------- + virtual bool ScopeIsHooked( HSCRIPT hScope, const char *pszEventName ) override; + virtual HSCRIPT LookupHookFunction( const char *pszEventName, HSCRIPT hScope, bool &bLegacy ) override; + virtual ScriptStatus_t ExecuteHookFunction( const char *pszEventName, HSCRIPT hFunction, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) override; + //-------------------------------------------------------- // External functions //-------------------------------------------------------- @@ -1986,6 +1993,110 @@ ScriptStatus_t SquirrelVM::ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* p return SCRIPT_DONE; } +bool SquirrelVM::ScopeIsHooked( HSCRIPT hScope, const char *pszEventName ) +{ + Assert( hScope && hScope != INVALID_HSCRIPT ); + + sq_pushroottable(vm_); + sq_pushstring(vm_, "Hooks", -1); + sq_get(vm_, -2); + sq_pushstring(vm_, "ScopeHookedToEvent", -1); + sq_get(vm_, -2); + sq_push(vm_, -2); + sq_pushobject(vm_, *((HSQOBJECT*)hScope)); + sq_pushstring(vm_, pszEventName, -1); + sq_call(vm_, 3, SQTrue, SQTrue); + + SQBool val; + if (SQ_FAILED(sq_getbool(vm_, -1, &val))) + { + sq_pop(vm_, 3); + return false; + } + + sq_pop(vm_, 3); + return val ? true : false; +} + +HSCRIPT SquirrelVM::LookupHookFunction(const char *pszEventName, HSCRIPT hScope, bool &bLegacy) +{ + HSCRIPT hFunc = LookupFunction( pszEventName, hScope ); + if (hFunc) + { + bLegacy = true; + return hFunc; + } + else + { + bLegacy = false; + } + + if (!ScopeIsHooked(hScope, pszEventName)) + return nullptr; + + sq_pushroottable(vm_); + sq_pushstring(vm_, "Hooks", -1); + sq_get(vm_, -2); + sq_pushstring(vm_, "CallHooks", -1); + sq_get(vm_, -2); + + HSQOBJECT obj; + sq_resetobject(&obj); + sq_getstackobj(vm_, -1, &obj); + sq_addref(vm_, &obj); + sq_pop(vm_, 2); + + HSQOBJECT* pObj = new HSQOBJECT; + *pObj = obj; + return (HSCRIPT)pObj; +} + +ScriptStatus_t SquirrelVM::ExecuteHookFunction(const char *pszEventName, HSCRIPT hFunction, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait) +{ + SquirrelSafeCheck safeCheck(vm_); + if (!hFunction) + return SCRIPT_ERROR; + + if (hFunction == INVALID_HSCRIPT) + return SCRIPT_ERROR; + + HSQOBJECT* pFunc = (HSQOBJECT*)hFunction; + sq_pushobject(vm_, *pFunc); + + // TODO: Run in hook scope + sq_pushroottable(vm_); + + sq_pushstring(vm_, pszEventName, -1); + sq_pushobject(vm_, *((HSQOBJECT*)hScope)); + + for (int i = 0; i < nArgs; ++i) + { + PushVariant(vm_, pArgs[i]); + } + + bool hasReturn = pReturn != nullptr; + + if (SQ_FAILED(sq_call(vm_, nArgs + 3, hasReturn, SQTrue))) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + if (hasReturn) + { + if (!getVariant(vm_, -1, *pReturn)) + { + sq_pop(vm_, 1); + return SCRIPT_ERROR; + } + + sq_pop(vm_, 1); + } + + sq_pop(vm_, 1); + return SCRIPT_DONE; +} + void SquirrelVM::RegisterFunction(ScriptFunctionBinding_t* pScriptFunction) { SquirrelSafeCheck safeCheck(vm_); diff --git a/sp/src/vscript/vscript_squirrel.nut b/sp/src/vscript/vscript_squirrel.nut index cb929807..bae6bd0d 100644 --- a/sp/src/vscript/vscript_squirrel.nut +++ b/sp/src/vscript/vscript_squirrel.nut @@ -106,6 +106,98 @@ class CSimpleCallChainer chain = null; } +//--------------------------------------------------------- +// Hook handler +//--------------------------------------------------------- +Hooks <- { Registered = {} } + +function Hooks::Add( scope, event, func, name ) +{ + Hooks.Registered[name] <- [event, scope, func]; +} + +function Hooks::Remove( name ) +{ + Hooks.Registered.rawdelete(name); +} + +function Hooks::ScopeHookedToEvent( scope, event ) +{ + //printl("Running ScopeHookedToEvent()") + foreach (elem in Hooks.Registered) + { + if (elem[1] == scope && elem[0] == event) + return true + } + return false +} + +function Hooks::CallHooks(event, scope, ...) +{ + //printl("vargv.len() = " + vargv.len()) + switch (vargv.len()) + { + case 0: + foreach (elem in Hooks.Registered) + { + if (elem[0] == event && elem[1] == scope) + return elem[2]() + } + break; + + case 1: + foreach (elem in Hooks.Registered) + { + if (elem[0] == event && elem[1] == scope) + return elem[2](vargv[0]) + } + break; + + case 2: + foreach (elem in Hooks.Registered) + { + if (elem[0] == event && elem[1] == scope) + return elem[2](vargv[0], vargv[1]) + } + break; + + case 3: + foreach (elem in Hooks.Registered) + { + if (elem[0] == event && elem[1] == scope) + return elem[2](vargv[0], vargv[1], vargv[2]) + } + break; + + case 4: + foreach (elem in Hooks.Registered) + { + if (elem[0] == event && elem[1] == scope) + return elem[2](vargv[0], vargv[1], vargv[2], vargv[3]) + } + break; + + case 5: + foreach (elem in Hooks.Registered) + { + if (elem[0] == event && elem[1] == scope) + return elem[2](vargv[0], vargv[1], vargv[2], vargv[3], vargv[4]) + } + break; + + case 6: + foreach (elem in Hooks.Registered) + { + if (elem[0] == event && elem[1] == scope) + return elem[2](vargv[0], vargv[1], vargv[2], vargv[3], vargv[4], vargv[5]) + } + break; + } +} + +//--------------------------------------------------------- +// Documentation +//--------------------------------------------------------- __Documentation <- {} local DocumentedFuncs = {}