Added generic_hook_toggle
Instead of having a single enable/disable_hook call that requires modders to know hook type IDs, make separate APIs for all types. This also makes it much easier in the future to improve the system or handle new use case scenarios easily.
This commit is contained in:
parent
7a08f1c32b
commit
9d554418f4
1 changed files with 87 additions and 26 deletions
|
@ -77,6 +77,32 @@ local function is_existing_hook(self, orig, hook_type)
|
|||
end
|
||||
end
|
||||
|
||||
-- Return an object from the global table. Second return value is if it was sucessful.
|
||||
local function get_object_from_string(obj)
|
||||
if type(obj) == "table" then
|
||||
return obj, true
|
||||
elseif type(obj) == "string" then
|
||||
local obj_table = rawget(_G, obj)
|
||||
return obj_table, (type(obj_table) == "table")
|
||||
else
|
||||
return obj, false
|
||||
end
|
||||
end
|
||||
|
||||
-- VT1 hooked everything using a "Obj.Method" string
|
||||
-- Add backward compatibility for that format.
|
||||
local function split_function_string(str)
|
||||
local find_position = string.find(str, "%.")
|
||||
local method, obj
|
||||
if find_position then
|
||||
method = string.sub(str, find_position + 1)
|
||||
obj = string.sub(str, 1, find_position - 1)
|
||||
else
|
||||
method = str
|
||||
end
|
||||
return method, obj
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### Hook Creation ################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
@ -169,7 +195,7 @@ local function create_internal_hook(orig, obj, method)
|
|||
end
|
||||
|
||||
local function create_hook(self, orig, obj, method, handler, hook_type)
|
||||
local err_name = HOOK_ERR_NAME[hook_type]
|
||||
local func_name = HOOK_ERR_NAME[hook_type]
|
||||
|
||||
if not is_orig_hooked(obj, method) then
|
||||
create_internal_hook(orig, obj, method)
|
||||
|
@ -189,7 +215,7 @@ local function create_hook(self, orig, obj, method, handler, hook_type)
|
|||
-- Add to the hook to registry. Raw hooks are unique, so we check for that too.
|
||||
if hook_type == HOOK_TYPE_RAW then
|
||||
if _registry.hooks[hook_type][orig] then
|
||||
self:error("(%s): Attempting to rawhook already hooked function %s", err_name, method)
|
||||
self:error("(%s): Attempting to rawhook already hooked function %s", func_name, method)
|
||||
else
|
||||
_registry.hooks[hook_type][orig] = create_specialized_hook(self, orig, handler, hook_type)
|
||||
end
|
||||
|
@ -197,7 +223,8 @@ local function create_hook(self, orig, obj, method, handler, hook_type)
|
|||
table.insert(_registry.hooks[hook_type][orig], create_specialized_hook(self, orig, handler, hook_type))
|
||||
end
|
||||
else
|
||||
self:error("(%s): Attempting to rehook already active %s %s.", err_name, err_name, method)
|
||||
local hook_type_name = (hook_type == HOOK_TYPE_NORMAL) and func_name or func_name.."-hook"
|
||||
self:error("(%s): Attempting to rehook already active %s %s.", func_name, hook_type_name, method)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -223,41 +250,64 @@ local function generic_hook(self, obj, method, handler, hook_type)
|
|||
vmf.check_wrong_argument_type(self, func_name, "handler", handler, "function", "nil") then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- Adjust the arguments.
|
||||
if type(method) == "function" then
|
||||
handler = method
|
||||
|
||||
-- VT1 hooked everything using a "Obj.Method" string
|
||||
-- Add backward compatibility for that format.
|
||||
local find_position = string.find(obj, "%.")
|
||||
if find_position then
|
||||
method = string.sub(obj, find_position + 1)
|
||||
obj = string.sub(obj, 1, find_position - 1)
|
||||
end
|
||||
method, obj = split_function_string(obj)
|
||||
end
|
||||
|
||||
-- Check if hook should be delayed.
|
||||
if type(obj) == "string" then
|
||||
local obj_table = rawget(_G, obj)
|
||||
if obj_table then
|
||||
-- No delay required, grab object and move on
|
||||
obj = obj_table
|
||||
else
|
||||
-- Call this func at a later time, using upvalues.
|
||||
vmf:info("[%s.%s] needs to be delayed.", obj, method)
|
||||
table.insert(_delayed, function()
|
||||
generic_hook(self, obj, method, handler, hook_type)
|
||||
end)
|
||||
return
|
||||
end
|
||||
local obj, sucess = get_object_from_string(obj) --luacheck: ignore
|
||||
if not sucess then
|
||||
-- Call this func at a later time, using upvalues.
|
||||
vmf:info("[%s.%s] needs to be delayed.", obj, method)
|
||||
table.insert(_delayed, function()
|
||||
generic_hook(self, obj, method, handler, hook_type)
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
-- obj can't be a string for these.
|
||||
-- obj can't be a string for these now.
|
||||
local orig = get_orig_function(self, obj, method)
|
||||
return create_hook(self, orig, obj, method, handler, hook_type)
|
||||
end
|
||||
|
||||
local function generic_hook_toggle(self, obj, method, hook_type, enabled_state)
|
||||
local func_name = HOOK_ERR_NAME[hook_type]
|
||||
if vmf.check_wrong_argument_type(self, func_name, "obj", obj, "string", "table") or
|
||||
vmf.check_wrong_argument_type(self, func_name, "method", method, "string", "nil") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Adjust the arguments.
|
||||
if not method then
|
||||
method, obj = split_function_string(obj)
|
||||
end
|
||||
|
||||
local obj, sucess = get_object_from_string(obj) --luacheck: ignore
|
||||
if not sucess then
|
||||
self:error("(%s): object doesn't exist: %s", func_name, obj)
|
||||
return
|
||||
end
|
||||
|
||||
local registry = _registry[self][hook_type]
|
||||
if registry then
|
||||
local orig = get_orig_function(self, obj, method)
|
||||
|
||||
-- Check if handler exists, because active[orig] would fail if disabled (false)
|
||||
if registry.handler[orig] then
|
||||
registry.active[orig] = enabled_state
|
||||
else
|
||||
self:warning("(%s): trying to toggle hook that doesn't exist: %s", func_name)
|
||||
return
|
||||
end
|
||||
else
|
||||
self:warning("(%s): trying to toggle hook that doesn't exist: %s", func_name)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- ####################################################################################################################
|
||||
-- ##### VMFMod #######################################################################################################
|
||||
-- ####################################################################################################################
|
||||
|
@ -301,6 +351,17 @@ function VMFMod:rawhook(obj, method, handler)
|
|||
return generic_hook(self, obj, method, handler, HOOK_TYPE_RAW)
|
||||
end
|
||||
|
||||
-- Enable/disable functions for all hook types:
|
||||
function VMFMod:enable_hook(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_NORMAL, true) end
|
||||
function VMFMod:enable_before(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_BEFORE, true) end
|
||||
function VMFMod:enable_after(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_AFTER, true) end
|
||||
function VMFMod:enable_rawhook(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_RAW, true) end
|
||||
|
||||
function VMFMod:disable_hook(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_NORMAL, false) end
|
||||
function VMFMod:disable_before(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_BEFORE, false) end
|
||||
function VMFMod:disable_after(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_AFTER, false) end
|
||||
function VMFMod:disable_rawhook(obj, method) generic_hook_toggle(self, obj, method, HOOK_TYPE_RAW, false) end
|
||||
|
||||
function VMFMod:enable_all_hooks()
|
||||
-- Using pairs because the self table may contain nils, and order isnt important.
|
||||
for _, hooks in pairs(_registry[self]) do
|
||||
|
|
Loading…
Add table
Reference in a new issue