mirror of
https://github.com/celisej567/source-sdk-2013.git
synced 2026-09-11 20:11:33 +03:00
Refactor script hook system
This commit is contained in:
@@ -188,9 +188,8 @@ public:
|
||||
//--------------------------------------------------------
|
||||
// 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;
|
||||
virtual HSCRIPT LookupHookFunction( const char *pszEventName, HSCRIPT hScope ) override;
|
||||
virtual ScriptStatus_t ExecuteHookFunction( HSCRIPT hFunction, const char *pszEventName, ScriptVariant_t *pArgs, int nArgs, ScriptVariant_t *pReturn, HSCRIPT hScope, bool bWait ) override;
|
||||
|
||||
//--------------------------------------------------------
|
||||
// External functions
|
||||
@@ -2348,55 +2347,38 @@ ScriptStatus_t SquirrelVM::ExecuteFunction(HSCRIPT hFunction, ScriptVariant_t* p
|
||||
return SCRIPT_DONE;
|
||||
}
|
||||
|
||||
bool SquirrelVM::ScopeIsHooked( HSCRIPT hScope, const char *pszEventName )
|
||||
HSCRIPT SquirrelVM::LookupHookFunction(const char *pszEventName, HSCRIPT hScope)
|
||||
{
|
||||
SquirrelSafeCheck safeCheck(vm_);
|
||||
|
||||
// For now, assume null scope (which is used for global hooks) is always hooked
|
||||
if (!hScope)
|
||||
return true;
|
||||
|
||||
SquirrelSafeCheck safeCheck(vm_);
|
||||
|
||||
Assert(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)))
|
||||
if ( hScope )
|
||||
{
|
||||
sq_pop(vm_, 3);
|
||||
return false;
|
||||
Assert( hScope != INVALID_HSCRIPT );
|
||||
|
||||
sq_pushroottable(vm_);
|
||||
sq_pushstring(vm_, "Hooks", -1);
|
||||
sq_get(vm_, -2);
|
||||
sq_pushstring(vm_, "IsEventHookedInScope", -1);
|
||||
sq_get(vm_, -2);
|
||||
sq_push(vm_, -2);
|
||||
sq_pushstring(vm_, pszEventName, -1);
|
||||
sq_pushobject(vm_, *((HSQOBJECT*)hScope));
|
||||
sq_call(vm_, 3, SQTrue, SQTrue);
|
||||
|
||||
SQBool val;
|
||||
if (SQ_FAILED(sq_getbool(vm_, -1, &val)))
|
||||
{
|
||||
sq_pop(vm_, 3);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sq_pop(vm_, 4);
|
||||
|
||||
if (!val)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sq_pop(vm_, 4);
|
||||
return val ? true : false;
|
||||
}
|
||||
|
||||
HSCRIPT SquirrelVM::LookupHookFunction(const char *pszEventName, HSCRIPT hScope, bool &bLegacy)
|
||||
{
|
||||
HSCRIPT hFunc = hScope ? LookupFunction( pszEventName, hScope ) : nullptr;
|
||||
if (hFunc)
|
||||
{
|
||||
bLegacy = true;
|
||||
return hFunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
bLegacy = false;
|
||||
}
|
||||
|
||||
if (!ScopeIsHooked(hScope, pszEventName))
|
||||
return nullptr;
|
||||
|
||||
SquirrelSafeCheck safeCheck(vm_);
|
||||
|
||||
sq_pushroottable(vm_);
|
||||
sq_pushstring(vm_, "Hooks", -1);
|
||||
sq_get(vm_, -2);
|
||||
@@ -2411,31 +2393,31 @@ HSCRIPT SquirrelVM::LookupHookFunction(const char *pszEventName, HSCRIPT hScope,
|
||||
|
||||
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)
|
||||
ScriptStatus_t SquirrelVM::ExecuteHookFunction(HSCRIPT hFunction, const char *pszEventName, ScriptVariant_t* pArgs, int nArgs, ScriptVariant_t* pReturn, HSCRIPT hScope, bool bWait)
|
||||
{
|
||||
SquirrelSafeCheck safeCheck(vm_);
|
||||
if (!hFunction)
|
||||
if ( !hFunction )
|
||||
return SCRIPT_ERROR;
|
||||
|
||||
if (hFunction == INVALID_HSCRIPT)
|
||||
return SCRIPT_ERROR;
|
||||
SquirrelSafeCheck safeCheck(vm_);
|
||||
|
||||
HSQOBJECT* pFunc = (HSQOBJECT*)hFunction;
|
||||
sq_pushobject(vm_, *pFunc);
|
||||
|
||||
// TODO: Run in hook scope
|
||||
// The call environment of the Hooks::Call function does not matter
|
||||
// as the function does not access any member variables.
|
||||
sq_pushroottable(vm_);
|
||||
|
||||
sq_pushstring(vm_, pszEventName, -1);
|
||||
|
||||
if (hScope)
|
||||
sq_pushobject(vm_, *((HSQOBJECT*)hScope));
|
||||
else
|
||||
sq_pushnull(vm_); // global hook
|
||||
|
||||
sq_pushstring(vm_, pszEventName, -1);
|
||||
|
||||
for (int i = 0; i < nArgs; ++i)
|
||||
{
|
||||
PushVariant(vm_, pArgs[i]);
|
||||
|
||||
@@ -152,114 +152,144 @@ Hooks <-
|
||||
// table, string, closure, string
|
||||
function Add( scope, event, callback, context )
|
||||
{
|
||||
switch ( typeof scope )
|
||||
{
|
||||
case "table":
|
||||
case "instance":
|
||||
case "class":
|
||||
break;
|
||||
default:
|
||||
throw "invalid scope param";
|
||||
}
|
||||
|
||||
if ( typeof event != "string" )
|
||||
throw "invalid event param";
|
||||
|
||||
if ( typeof callback != "function" )
|
||||
throw "invalid callback param"
|
||||
throw "invalid callback param";
|
||||
|
||||
if ( !(scope in s_List) )
|
||||
s_List[scope] <- {}
|
||||
if ( typeof context != "string" )
|
||||
throw "invalid context param";
|
||||
|
||||
local t = s_List[scope]
|
||||
if ( !(event in s_List) )
|
||||
s_List[event] <- {};
|
||||
|
||||
if ( !(event in t) )
|
||||
t[event] <- {}
|
||||
local t = s_List[event];
|
||||
|
||||
t[event][context] <- callback
|
||||
if ( !(scope in t) )
|
||||
t[scope] <- {};
|
||||
|
||||
t[scope][context] <- callback;
|
||||
}
|
||||
|
||||
function Remove( context, event = null )
|
||||
function Remove( event, context )
|
||||
{
|
||||
local rem;
|
||||
|
||||
if ( event )
|
||||
{
|
||||
foreach( k,scope in s_List )
|
||||
if ( event in s_List )
|
||||
{
|
||||
if ( event in scope )
|
||||
foreach ( scope, ctx in s_List[event] )
|
||||
{
|
||||
local t = scope[event]
|
||||
if ( context in t )
|
||||
if ( context in ctx )
|
||||
{
|
||||
delete t[context]
|
||||
delete ctx[context];
|
||||
}
|
||||
|
||||
// cleanup?
|
||||
if ( !t.len() )
|
||||
delete scope[event]
|
||||
if ( !ctx.len() )
|
||||
{
|
||||
if ( !rem )
|
||||
rem = [];
|
||||
rem.append( event );
|
||||
rem.append( scope );
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup?
|
||||
if ( !scope.len() )
|
||||
delete s_List[k]
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach( k,scope in s_List )
|
||||
foreach ( ev, t in s_List )
|
||||
{
|
||||
foreach( kk,ev in scope )
|
||||
foreach ( scope, ctx in t )
|
||||
{
|
||||
if ( context in ev )
|
||||
if ( context in ctx )
|
||||
{
|
||||
delete ev[context]
|
||||
delete ctx[context];
|
||||
}
|
||||
|
||||
// cleanup?
|
||||
if ( !ev.len() )
|
||||
delete scope[kk]
|
||||
}
|
||||
|
||||
// cleanup?
|
||||
if ( !scope.len() )
|
||||
delete s_List[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Call( scope, event, ... )
|
||||
{
|
||||
local firstReturn
|
||||
|
||||
// global hook; call all scopes
|
||||
if ( !scope )
|
||||
{
|
||||
vargv.insert( 0, null )
|
||||
foreach( sc,t in s_List )
|
||||
{
|
||||
if ( event in t )
|
||||
{
|
||||
vargv[0] = sc
|
||||
foreach( context, callback in t[event] )
|
||||
if ( !ctx.len() )
|
||||
{
|
||||
//printf( "(%.4f) Calling hook '%s' of context '%s' in static iteration\n", Time(), event, context )
|
||||
|
||||
local curReturn = callback.acall(vargv)
|
||||
if (firstReturn == null)
|
||||
firstReturn = curReturn
|
||||
if ( !rem )
|
||||
rem = [];
|
||||
rem.append( ev );
|
||||
rem.append( scope );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( scope in s_List )
|
||||
{
|
||||
local t = s_List[scope]
|
||||
if ( event in t )
|
||||
{
|
||||
vargv.insert( 0, scope )
|
||||
foreach( context, callback in t[event] )
|
||||
{
|
||||
//printf( "(%.4f) Calling hook '%s' of context '%s'\n", Time(), event, context )
|
||||
|
||||
local curReturn = callback.acall(vargv)
|
||||
if (firstReturn == null)
|
||||
firstReturn = curReturn
|
||||
if ( rem )
|
||||
{
|
||||
local c = rem.len() - 1;
|
||||
for ( local i = 0; i < c; i += 2 )
|
||||
{
|
||||
local ev = rem[i];
|
||||
local scope = rem[i+1];
|
||||
|
||||
if ( !s_List[ev][scope].len() )
|
||||
delete s_List[ev][scope];
|
||||
|
||||
if ( !s_List[ev].len() )
|
||||
delete s_List[ev];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Call( event, scope, ... )
|
||||
{
|
||||
local firstReturn;
|
||||
|
||||
if ( event in s_List )
|
||||
{
|
||||
vargv.insert( 0, scope );
|
||||
|
||||
local t = s_List[event];
|
||||
if ( scope in t )
|
||||
{
|
||||
foreach ( fn in t[scope] )
|
||||
{
|
||||
//printf( "(%.4f) Calling hook %s:%s\n", Time(), event, context );
|
||||
|
||||
local r = fn.acall( vargv );
|
||||
if ( firstReturn == null )
|
||||
firstReturn = r;
|
||||
}
|
||||
}
|
||||
else if ( !scope ) // global hook
|
||||
{
|
||||
foreach ( sc, ctx in t )
|
||||
{
|
||||
vargv[0] = sc;
|
||||
|
||||
foreach ( context, fn in ctx )
|
||||
{
|
||||
//printf( "(%.4f) Calling hook (g) %s:%s\n", Time(), event, context );
|
||||
|
||||
local r = fn.acall( vargv );
|
||||
if ( firstReturn == null )
|
||||
firstReturn = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstReturn
|
||||
return firstReturn;
|
||||
}
|
||||
|
||||
function ScopeHookedToEvent( scope, event )
|
||||
function IsEventHookedInScope( event, scope )
|
||||
{
|
||||
return ( scope in s_List ) && ( event in s_List[scope] )
|
||||
return ( event in s_List ) && ( scope in s_List[event] )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user