Created
March 18, 2015 23:14
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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