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:
FireSiku 2018-06-03 01:14:39 -04:00
parent 7a08f1c32b
commit 9d554418f4

View file

@ -77,6 +77,32 @@ local function is_existing_hook(self, orig, hook_type)
end end
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 ################################################################################################ -- ##### Hook Creation ################################################################################################
-- #################################################################################################################### -- ####################################################################################################################
@ -169,7 +195,7 @@ local function create_internal_hook(orig, obj, method)
end end
local function create_hook(self, orig, obj, method, handler, hook_type) 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 if not is_orig_hooked(obj, method) then
create_internal_hook(orig, obj, method) 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. -- Add to the hook to registry. Raw hooks are unique, so we check for that too.
if hook_type == HOOK_TYPE_RAW then if hook_type == HOOK_TYPE_RAW then
if _registry.hooks[hook_type][orig] 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 else
_registry.hooks[hook_type][orig] = create_specialized_hook(self, orig, handler, hook_type) _registry.hooks[hook_type][orig] = create_specialized_hook(self, orig, handler, hook_type)
end 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)) table.insert(_registry.hooks[hook_type][orig], create_specialized_hook(self, orig, handler, hook_type))
end end
else 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
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 vmf.check_wrong_argument_type(self, func_name, "handler", handler, "function", "nil") then
return return
end end
-- Adjust the arguments. -- Adjust the arguments.
if type(method) == "function" then if type(method) == "function" then
handler = method handler = method
method, obj = split_function_string(obj)
-- 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
end end
-- Check if hook should be delayed. -- Check if hook should be delayed.
if type(obj) == "string" then local obj, sucess = get_object_from_string(obj) --luacheck: ignore
local obj_table = rawget(_G, obj) if not sucess then
if obj_table then -- Call this func at a later time, using upvalues.
-- No delay required, grab object and move on vmf:info("[%s.%s] needs to be delayed.", obj, method)
obj = obj_table table.insert(_delayed, function()
else generic_hook(self, obj, method, handler, hook_type)
-- Call this func at a later time, using upvalues. end)
vmf:info("[%s.%s] needs to be delayed.", obj, method) return
table.insert(_delayed, function()
generic_hook(self, obj, method, handler, hook_type)
end)
return
end
end 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) local orig = get_orig_function(self, obj, method)
return create_hook(self, orig, obj, method, handler, hook_type) return create_hook(self, orig, obj, method, handler, hook_type)
end 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 ####################################################################################################### -- ##### VMFMod #######################################################################################################
-- #################################################################################################################### -- ####################################################################################################################
@ -301,6 +351,17 @@ function VMFMod:rawhook(obj, method, handler)
return generic_hook(self, obj, method, handler, HOOK_TYPE_RAW) return generic_hook(self, obj, method, handler, HOOK_TYPE_RAW)
end 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() function VMFMod:enable_all_hooks()
-- Using pairs because the self table may contain nils, and order isnt important. -- Using pairs because the self table may contain nils, and order isnt important.
for _, hooks in pairs(_registry[self]) do for _, hooks in pairs(_registry[self]) do