Skip to content

Instantly share code, notes, and snippets.

@Python1320
Created March 18, 2015 23:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Python1320/b5026afba803ba83c38b to your computer and use it in GitHub Desktop.
Save Python1320/b5026afba803ba83c38b to your computer and use it in GitHub Desktop.
An attempt to make hook.lua with a single pcall, while maintaining recursion and allowing hooks to run even after an error
local gmod = gmod
local pairs = pairs
local isfunction = isfunction
local isstring = isstring
local IsValid = IsValid
local xpcall=xpcall
local debug=debug
local error=error
local print=print
local next=next
local tostring=tostring
local setmetatable=setmetatable
local ErrorNoHalt=ErrorNoHalt
local PrintTable=PrintTable
function GAMEMODE:a()
print"GM a"
end
function GAMEMODE:b()
print"GM b"
error"gamemode b error"
end
module( "hook3" )
local Hooks = {}
--[[---------------------------------------------------------
Name: GetTable
Desc: Returns a table of all hooks.
-----------------------------------------------------------]]
function GetTable() return Hooks end
--[[---------------------------------------------------------
Name: Add
Args: string hookName, any identifier, function func
Desc: Add a hook to listen to the specified event.
-----------------------------------------------------------]]
function Add( event_name, name, func )
if ( !isfunction( func ) ) then return end
if ( !isstring( event_name ) ) then return end
if (Hooks[ event_name ] == nil) then
Hooks[ event_name ] = {}
end
Hooks[ event_name ][ name ] = func
end
--[[---------------------------------------------------------
Name: Remove
Args: string hookName, identifier
Desc: Removes the hook with the given indentifier.
-----------------------------------------------------------]]
function Remove( event_name, name )
if ( !isstring( event_name ) ) then return end
if ( !Hooks[ event_name ] ) then return end
Hooks[ event_name ][ name ] = nil
end
--[[---------------------------------------------------------
Name: Run
Args: string hookName, vararg args
Desc: Calls hooks associated with the hook name.
-----------------------------------------------------------]]
function Run( name, ... )
return Call( name, gmod and gmod.GetGamemode() or nil, ... )
end
--[[---------------------------------------------------------
Name: Run
Args: string hookName, table gamemodeTable, vararg args
Desc: Calls hooks associated with the hook name.
-----------------------------------------------------------]]
local depth=-1
local depth_data={} or setmetatable({},{__index=function(self,k)
local t="ERROR"
rawset(self,k,t)
return t
end})
local depth_0
local function _Call( lastk, name, gm, ... )
--
-- Run hooks
--
local HookTable = Hooks[ name ]
if ( HookTable != nil ) then
local a, b, c, d, e, f
for k, v in next,HookTable,lastk do
if depth==0 then
depth_0 = k
else
depth_data[depth] = k
end
a, b, c, d, e, f = v( ... )
if ( a != nil ) then
return a, b, c, d, e, f
end
end
end
if depth==0 then
depth_0 = nil
else
depth_data[depth] = nil
end
--
-- Call the gamemode function
--
if ( !gm ) then return end
local GamemodeFunction = gm[ name ]
if ( GamemodeFunction == nil ) then return end
return GamemodeFunction( gm, ... )
end
local lastk_grabbed
local function tracebackhack(err)
--lastk_grabbed=nil
--local fnd
--for i=1,65536 do -- max stack size. UNFEASIBLY EXPENSIVE.
-- local k,v=debug.getlocal(i,1)
-- if k=='lastk' and debug.getinfo(i,'f').func==_Call then
-- lastk_grabbed=v
-- break
-- end
--
--end
return err--debug.traceback(err,2)
end
function Call( name, gm, ... )
depth = depth + 1
lastk_grabbed=nil -- might be necessary if traceback can't be called due to stack overflow or something
local ok,a,b,c,d,e,f = xpcall(_Call,tracebackhack,nil,name, gm, ... )
if not ok then --TODO: while true do for multiple errors
local lastkey
if depth==0 then
lastkey=depth_0
else
lastkey = depth_data[depth]
end
ErrorNoHalt("Hook errored: "..tostring(lastkey or "gamemode?")..': '..tostring(a))
if lastkey==nil then
depth = depth - 1
return
end
a=nil -- nil error
ok,a,b,c,d,e,f = xpcall(_Call,tracebackhack,lastkey,name, gm, ... )
if not ok then
ErrorNoHalt("Hook errored: "..tostring(lastkey or "gamemode?")..': '..tostring(a))
depth = depth - 1
a = nil --
return
end
end
depth = depth - 1
return a,b,c,d,e,f
end
local recursefail function recursefail() recursefail() end
Add("a",1,function()
print"running_a_1"
--error("error_in_hook_a_a")
end)
Add("a",2,function()
print"running_a_2, calling b"
print("called b: ",Run'b')
end)
Add("a",3,function()
print"running_a_3"
return "returned a3"
end)
Add("b",1,function()
print"running_b_1"
--error("error_in_hook_a_a")
end)
Add("b",2,function()
print"running_b_2"
--recursefail()
end)
Add("b",3,function()
print"running_b_3"
-- return "returned b3"
end)
print"=================="
print("Called a: ",Run'a')
PrintTable(depth_data)
-- RESULT
--==================
--running_a_1
--running_a_2, calling b
--running_b_1
--running_b_2
--running_b_3
--GM b
--Hook errored: gamemode?: Python1320:24: gamemode b error
--called b:
--running_a_3
--Called a: returned a3 nil nil nil nil nil
--{
--}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment