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..0b75d18 100644 --- a/scripts/internal.lua +++ b/scripts/internal.lua @@ -1,5 +1,24 @@ local internal = {} +function internal.handleFailure(errorOrResult, owner, creator, caller) + errorOrResult = errorOrResult or "" + if creator then + if owner then + -- creator will already have a newline + creator = "In mod id '"..owner.."'"..creator + end + else + creator = "In mod id '"..(owner and owner or "").."'" + end + 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 +29,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,10 +48,14 @@ 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 + local hook = hookTbl + if type(hookTbl) == "table" then + hook = hookTbl.fn + end -- 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, @@ -39,8 +63,8 @@ function internal:buildBroadcastFunc(hooksField, argsFunc) ) if not ok then - local owner = extObj.owner and extObj.owner.id or "" - LOG("In mod id '" .. owner .. "', ", err) + local owner = extObj.owner and extObj.owner.id or nil + internal.handleFailure(errorOrResult, owner, hookTbl.creator, caller) end end end