Hooks: Allow updating hook handler without affecting order.

It will throw an error if you're trying to update a hook using a different hook_type api to prevent any weirdness by jumping from one chain to the other.

Also created a specialized function for hook_data initialization.
This commit is contained in:
FireSiku 2018-06-14 21:15:00 -04:00
parent 49e4d427d4
commit 28bc668747

View file

@ -130,8 +130,19 @@ local function get_hook_chain(orig)
end end
end end
-- Returns a table containing hook data inside of it.
-- { active = self:is_enabled() }
local function create_hook_data(self, obj, handler, hook_type)
return {
active = self:is_enabled(),
hook_type = hook_type,
handler = handler,
obj = obj,
}
end
-- Returns a function closure with all the information needed for a given hook to be handled correctly. -- Returns a function closure with all the information needed for a given hook to be handled correctly.
local function create_specialized_hook(self, orig, handler, hook_type) local function create_specialized_hook(self, orig, hook_type)
local func local func
local hook_data = _registry[self][orig] local hook_data = _registry[self][orig]
@ -144,7 +155,7 @@ local function create_specialized_hook(self, orig, handler, hook_type)
if hook_type == HOOK_TYPE_NORMAL then if hook_type == HOOK_TYPE_NORMAL then
func = function(...) func = function(...)
if hook_data.active then if hook_data.active then
return handler(previous_hook, ...) return hook_data.handler(previous_hook, ...)
else else
return previous_hook(...) return previous_hook(...)
end end
@ -153,7 +164,7 @@ local function create_specialized_hook(self, orig, handler, hook_type)
elseif hook_type == HOOK_TYPE_ORIGIN then elseif hook_type == HOOK_TYPE_ORIGIN then
func = function(...) func = function(...)
if hook_data.active then if hook_data.active then
return handler(...) return hook_data.handler(...)
else else
return orig(...) return orig(...)
end end
@ -161,7 +172,7 @@ local function create_specialized_hook(self, orig, handler, hook_type)
elseif hook_type == HOOK_TYPE_SAFE then elseif hook_type == HOOK_TYPE_SAFE then
func = function(...) func = function(...)
if hook_data.active then if hook_data.active then
vmf.xpcall_no_return_values(self, "(safe_hook)", handler, ...) vmf.xpcall_no_return_values(self, "(safe_hook)", hook_data.handler, ...)
end end
end end
end end
@ -203,8 +214,9 @@ local function create_hook(self, orig, obj, method, handler, func_name, hook_typ
end end
-- Check to make sure it wasn't hooked before -- Check to make sure it wasn't hooked before
if not _registry[self][orig] then local hook_data = _registry[self][orig]
_registry[self][orig] = { active = self:is_enabled() } if not hook_data then
_registry[self][orig] = create_hook_data(self, obj, handler, hook_type)
local hook_registry = _registry.hooks[hook_type] local hook_registry = _registry.hooks[hook_type]
-- Add to the hook to registry. Origin hooks are unique, so we check for that too. -- Add to the hook to registry. Origin hooks are unique, so we check for that too.
@ -212,18 +224,26 @@ local function create_hook(self, orig, obj, method, handler, func_name, hook_typ
if hook_registry[orig] then if hook_registry[orig] then
self:error("(%s): Attempting to hook origin of already hooked function %s", func_name, method) self:error("(%s): Attempting to hook origin of already hooked function %s", func_name, method)
else else
hook_registry[orig] = create_specialized_hook(self, orig, handler, hook_type) hook_registry[orig] = create_specialized_hook(self, orig, hook_type)
end end
else else
table.insert(hook_registry[orig], create_specialized_hook(self, orig, handler, hook_type) ) table.insert(hook_registry[orig], create_specialized_hook(self, orig, hook_type) )
end end
else else
-- This should be a warning log, but currently there are no differences between warning and error. -- If hook_data already exists and it's the same hook_type, we can safely change the hook handler.
-- Wouldn't want to scare users that mods are broken because this used to be acceptable. -- This should (in practice) only be used for debugging by modders who uses DoFile.
if vmf:get("developer_mode") then -- Revisit purpose when lua files are in plain text.
self:warning("(%s): Attempting to rehook already active hook %s.", func_name, method) if hook_data.obj == obj and hook_data.hook_type == hook_type then
hook_data.handler = handler
else else
self:info("(%s): Attempting to rehook already active hook %s.", func_name, method) -- This should be a warning log, but currently there are no differences between warning and error.
-- Wouldn't want to scare users that mods are broken because this used to be acceptable.
-- This should be changed to permanently be a warning log after new_hooks deprecation period is over.
if vmf:get("developer_mode") then
self:warning("(%s): Attempting to rehook active hook [%s] with different hook_type.", func_name, method)
else
self:info("(%s): Attempting to rehook active hook [%s] with different hook_type.", func_name, method)
end
end end
end end