diff --git a/vmf/scripts/mods/vmf/modules/core/hooks.lua b/vmf/scripts/mods/vmf/modules/core/hooks.lua index a0c7434..03de458 100644 --- a/vmf/scripts/mods/vmf/modules/core/hooks.lua +++ b/vmf/scripts/mods/vmf/modules/core/hooks.lua @@ -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)