diff --git a/scripts/hooks.lua b/scripts/hooks.lua index 185ad4b..04a75cf 100644 --- a/scripts/hooks.lua +++ b/scripts/hooks.lua @@ -66,12 +66,15 @@ function hooks:addTo(modApiExt) local eventId = "on"..Name local addHook = "add"..Name.."Hook" - events[eventId] = Event() + events[eventId] = Event({ eventName = eventId }) modApiExt[hookId] = {} modApiExt[addHook] = function(self, fn) assert(type(fn) == "function") - table.insert(self[hookId], fn) + table.insert(self[hookId], { + fn = fn, + creator = debug.traceback("", 3) + }) end -- functions to fire the hooks are built in internal.lua diff --git a/scripts/internal.lua b/scripts/internal.lua index bb8aa9f..07924da 100644 --- a/scripts/internal.lua +++ b/scripts/internal.lua @@ -1,5 +1,16 @@ local internal = {} +function internal.handleFailure(errorOrResult, creator, caller) + errorOrResult = errorOrResult or "" + local message = Event.buildErrorMessage("An event callback failed: ", errorOrResult, + nil, creator, caller) + if Event.isStackOverflowError(errorOrResult) then + error(message) + else + LOG(message) + end +end + --[[ Creates a broadcast function for the specified hooks field, allowing to trigger the hook callbacks on all registered modApiExt objects. @@ -10,14 +21,15 @@ local internal = {} --]] function internal:buildBroadcastFunc(hooksField, argsFunc) local errfunc = function(e) - return string.format( - "A '%s' callback has failed:\n%s", - hooksField, e - ) + -- Capture and return the stack trace of the xpcall + -- 2 makes it start a frame higher so it doesn't include + -- this error handling fn + return debug.traceback(tostring(e), 2) end return function(...) local args = {...} + local caller = debug.traceback("") if #args == 0 then -- We didn't receive arguments directly. Fall back to @@ -28,19 +40,18 @@ function internal:buildBroadcastFunc(hooksField, argsFunc) for i, extObj in ipairs(modApiExt_internal.extObjects) do if extObj[hooksField] then - for j, hook in ipairs(extObj[hooksField]) do + for j, hookTbl in ipairs(extObj[hooksField]) do -- invoke the hook in a xpcall, since errors in SkillEffect -- scripts fail silently, making debugging a nightmare. - local ok, err = xpcall( + local ok, errorOrResult = xpcall( args - and function() hook(unpack(args)) end - or function() hook() end, + and function() hookTbl.fn(unpack(args)) end + or function() hookTbl.fn() end, errfunc ) if not ok then - local owner = extObj.owner and extObj.owner.id or "" - LOG("In mod id '" .. owner .. "', ", err) + internal.handleFailure(errorOrResult, hookTbl.creator, caller) end end end