-- luacheck: globals whosit, std luanti+luajit whosit = _G.whosit or {} _G.whosit = whosit -- provide table.pack if luajit is built without LUA52COMPAT local pack = table.pack or function(...) return {n = select("#", ...), ...} end local unpack = unpack or table.unpack -- use efficient deque or implement our own FIFO type of thing local formspec_log = futil and futil.Deque and futil.Deque() if not formspec_log then -- no deque implementation found local deq = {} deq.size = function(self) return #self end deq.push_front = function(self, v) table.insert(self, v) end deq.pop_back = function(self) local v = self[1] table.remove(self, 1) return v end deq.iterate = function(self) local i = 0 return function() i = i + 1 local v = self[i] if v then return v end end end deq.__index = deq formspec_log = setmetatable({}, deq) end local LOG_SIZE = 30 -- use deque for logging, pushing oldest log item out local function log_add(msg) core.log('error', type(msg) == "string" and msg or dump(msg)) formspec_log:push_front(msg) if formspec_log:size() > LOG_SIZE then formspec_log:pop_back() end end local function formspec_log_clear() formspec_log:clear() end -- passes to xpcall to be able to capture the call stack local function error_printer(err) log_add(err) log_add(debug.traceback("----------------------", 2)) return "error was logged" end -- Takes any function and returns a function that takes and returns -- same [multiple] values if no error happens. On error, logs it. local function safe_wrap(func) local function wrap_f(...) local res = pack(xpcall(func, error_printer, ...)) if res[1] then -- no error, just return rest of the values return select(2, unpack(res)) else -- error was logged by error_printer already local msg = dump(res[2]) if msg then log_add(msg) end return nil end end return wrap_f end local function formspec_log_show(playername) local log_items = {} for item in formspec_log:iterate() do table.insert(log_items, type(item) == "string" and item or dump(item)) end local formspec_template = [[ formspec_version[6] size[16,9] textarea[0.1,0.4;15.8,8.5;log;log;%s] ]] local log_text = core.formspec_escape(table.concat(log_items,'\n')) core.show_formspec(playername, "yl_commons:formspec_log", string.format(formspec_template, log_text)) end -- These commands will allow us to see the errors saved in -- the `formspec_log` after they happen. core.register_chatcommand( "whosit_log_show", { privs = { server = true }, func = function(playername, _params) formspec_log_show(playername) return true end, } ) core.register_chatcommand( "whosit_log_clear", { privs = { server = true }, func = function(playername, _params) formspec_log_clear(playername) return true end, } ) whosit.original_callbacks = {} local original_callbacks = whosit.original_callbacks function whosit.safe_override_on_use(item_name, func) local def = core.registered_items[item_name] if not (original_callbacks[item_name] or {}).on_use then local orig = original_callbacks[item_name] or {} original_callbacks[item_name] = orig orig.on_use = def.on_use end def.on_use = function(itemstack, user, pointed_thing) local status, res = safe_wrap(func)(itemstack, user, pointed_thing) if status then return res else return nil end end end