[Hooks] Use a better unique identifier for registry tables.
Initially used the function reference of the original function, but this caused issues with inherited functions. This commit changes it so that we're using our own internal hook function reference as the identifier.
This commit is contained in:
parent
bf67d37ad8
commit
b409b9b01b
1 changed files with 25 additions and 19 deletions
|
@ -92,16 +92,16 @@ end
|
|||
-- For any given original function, return the newest entry of the hook_chain.
|
||||
-- Since all hooks of the chain contain the call to the previous one, we don't need to do any manual loops.
|
||||
-- This continues until the end of the chain, where the original function is called.
|
||||
local function get_hook_chain(orig)
|
||||
local function get_hook_chain(orig, unique_id)
|
||||
local hook_registry = _hooks
|
||||
local hooks = hook_registry[HOOK_TYPE_NORMAL][orig]
|
||||
local hooks = hook_registry[HOOK_TYPE_NORMAL][unique_id]
|
||||
if hooks and #hooks > 0 then
|
||||
return hooks[#hooks]
|
||||
end
|
||||
-- We can't simply return orig here, or it would cause origins to depend on load order.
|
||||
return function(...)
|
||||
if hook_registry[HOOK_TYPE_ORIGIN][orig] then
|
||||
return hook_registry[HOOK_TYPE_ORIGIN][orig](...)
|
||||
if hook_registry[HOOK_TYPE_ORIGIN][unique_id] then
|
||||
return hook_registry[HOOK_TYPE_ORIGIN][unique_id](...)
|
||||
else
|
||||
return orig(...)
|
||||
end
|
||||
|
@ -109,11 +109,12 @@ local function get_hook_chain(orig)
|
|||
end
|
||||
|
||||
-- Returns a table containing hook data inside of it.
|
||||
local function create_hook_data(mod, obj, handler, hook_type)
|
||||
local function create_hook_data(mod, obj, orig, handler, hook_type)
|
||||
return {
|
||||
active = mod:is_enabled(),
|
||||
hook_type = hook_type,
|
||||
handler = handler,
|
||||
orig = orig,
|
||||
obj = obj,
|
||||
}
|
||||
end
|
||||
|
@ -122,12 +123,13 @@ end
|
|||
-- Note: If a previous hook is removed from the table, these functions wouldn't be updated
|
||||
-- This would break the chain, solution is to not remove the hooks, simply make them inactive
|
||||
-- Make sure inactive hooks that rely on the chain still call the next function seamlessly.
|
||||
local function create_specialized_hook(mod, orig, hook_type)
|
||||
local function create_specialized_hook(mod, unique_id, hook_type)
|
||||
local func
|
||||
local hook_data = _registry[mod][orig]
|
||||
local hook_data = _registry[mod][unique_id]
|
||||
local orig = hook_data.orig
|
||||
|
||||
-- Determine the previous function in the hook stack
|
||||
local previous_hook = get_hook_chain(orig)
|
||||
local previous_hook = get_hook_chain(orig, unique_id)
|
||||
|
||||
if hook_type == HOOK_TYPE_NORMAL then
|
||||
func = function(...)
|
||||
|
@ -160,12 +162,14 @@ end
|
|||
-- Once all hooks that are part of the chain have been executed, we can go over the safe hooks.
|
||||
-- Note: We need to keep the return values in mind in case another function depends on them.
|
||||
-- At this point in the execution, Obj has already been type-checked and cannot be a string anymore.
|
||||
-- We then use this internal hook as a unique identifier, which can we can also call by using obj[method]
|
||||
local function create_internal_hook(orig, obj, method)
|
||||
local fn = function(...)
|
||||
local hook_chain = get_hook_chain(orig)
|
||||
local fn
|
||||
fn = function(...)
|
||||
local hook_chain = get_hook_chain(orig, fn)
|
||||
local num_values, values = get_return_values( hook_chain(...) )
|
||||
|
||||
local safe_hooks = _hooks[HOOK_TYPE_SAFE][orig]
|
||||
local safe_hooks = _hooks[HOOK_TYPE_SAFE][fn]
|
||||
if safe_hooks and #safe_hooks > 0 then
|
||||
for i = 1, #safe_hooks do safe_hooks[i](...) end
|
||||
end
|
||||
|
@ -175,31 +179,33 @@ local function create_internal_hook(orig, obj, method)
|
|||
if not _origs[obj] then _origs[obj] = {} end
|
||||
_origs[obj][method] = orig
|
||||
obj[method] = fn
|
||||
return fn
|
||||
end
|
||||
|
||||
-- This function handles the handling the hook data and adding them to the registry.
|
||||
-- Origin Hooks have to be unique by nature so we have to make sure we don't allow multiple mods to do it.
|
||||
local function create_hook(mod, orig, obj, method, handler, func_name, hook_type)
|
||||
mod:info("(%s): Hooking '%s' from [%s] (Origin: %s)", func_name, method, obj, orig)
|
||||
local unique_id
|
||||
|
||||
if not is_orig_hooked(obj, method) then
|
||||
create_internal_hook(orig, obj, method)
|
||||
unique_id = create_internal_hook(orig, obj, method)
|
||||
end
|
||||
|
||||
-- Check to make sure it wasn't hooked before
|
||||
local hook_data = _registry[mod][orig]
|
||||
local hook_data = _registry[mod][unique_id]
|
||||
if not hook_data then
|
||||
_registry[mod][orig] = create_hook_data(mod, obj, handler, hook_type)
|
||||
_registry[mod][unique_id] = create_hook_data(mod, obj, orig, handler, hook_type)
|
||||
|
||||
local hook_registry = _hooks[hook_type]
|
||||
if hook_type == HOOK_TYPE_ORIGIN then
|
||||
if hook_registry[orig] then
|
||||
if hook_registry[unique_id] then
|
||||
mod:error("(%s): Attempting to hook origin of already hooked function %s", func_name, method)
|
||||
else
|
||||
hook_registry[orig] = create_specialized_hook(mod, orig, hook_type)
|
||||
hook_registry[unique_id] = create_specialized_hook(mod, unique_id, hook_type)
|
||||
end
|
||||
else
|
||||
table.insert(hook_registry[orig], create_specialized_hook(mod, orig, hook_type) )
|
||||
table.insert(hook_registry[unique_id], create_specialized_hook(mod, unique_id, hook_type) )
|
||||
end
|
||||
else
|
||||
-- If hook_data already exists and it's the same hook_type, we can safely change the hook handler.
|
||||
|
@ -319,8 +325,8 @@ local function generic_hook_toggle(mod, obj, method, enabled_state)
|
|||
|
||||
local orig = get_orig_function(obj, method)
|
||||
|
||||
if _registry[mod][orig] then
|
||||
_registry[mod][orig].active = enabled_state
|
||||
if _registry[mod][obj[method]] then
|
||||
_registry[mod][obj[method]].active = enabled_state
|
||||
else
|
||||
-- This has the potential for mod-breaking behavior, but not guaranteed
|
||||
mod:warning("(%s): trying to toggle hook that doesn't exist: %s", func_name, method)
|
||||
|
|
Loading…
Add table
Reference in a new issue