-- Lua Console module — execute Lua code in the game's main thread local M = {} function M.init(fw) local static_dir = fw.modules_dir .. "/console/static" fw.register_module("console", static_dir) fw.on_api("console", "exec", function(body) local ok, data = pcall(fw.json_decode, body) if not ok or not data or not data.code then return '{"ok":false,"error":"invalid body"}' end local code = data.code if #code == 0 then return '{"ok":false,"error":"empty code"}' end -- Try as expression first (return value), then as statement local fn, err = loadstring("return " .. code) if not fn then fn, err = loadstring(code) end if not fn then return fw.json_encode({ok = false, error = "Compile: " .. tostring(err)}) end local results = {pcall(fn)} local success = table.remove(results, 1) if not success then return fw.json_encode({ok = false, error = tostring(results[1])}) end -- Format results local parts = {} for i, v in ipairs(results) do parts[i] = serialize(v, 0) end local output = table.concat(parts, ", ") if #parts == 0 then output = "nil" end fw.log("INFO", "CONSOLE", "Exec: " .. code:sub(1, 80) .. " => " .. output:sub(1, 80)) return fw.json_encode({ok = true, result = output, count = #parts}) end) fw.log("INFO", "CONSOLE", "Module loaded") end -- Serialize a Lua value into a readable string function serialize(val, depth) depth = depth or 0 if depth > 4 then return "..." end local t = type(val) if t == "nil" then return "nil" elseif t == "string" then if #val > 200 then return '"' .. val:sub(1, 200):gsub('[\n\r]', '\\n') .. '..." [' .. #val .. ' chars]' end return '"' .. val:gsub('[\n\r]', '\\n'):gsub('"', '\\"') .. '"' elseif t == "number" or t == "boolean" then return tostring(val) elseif t == "table" then local items = {} local n = 0 -- Array part for i, v in ipairs(val) do if n >= 20 then items[#items + 1] = "... +" .. (#val - n) .. " more" break end items[#items + 1] = serialize(v, depth + 1) n = n + 1 end -- Hash part for k, v in pairs(val) do if type(k) ~= "number" or k < 1 or k > #val or k ~= math.floor(k) then if n >= 20 then items[#items + 1] = "..." break end local ks = type(k) == "string" and k or "[" .. tostring(k) .. "]" items[#items + 1] = ks .. " = " .. serialize(v, depth + 1) n = n + 1 end end if #items == 0 then return "{}" end return "{ " .. table.concat(items, ", ") .. " }" elseif t == "function" then return "function: " .. tostring(val) else return tostring(val) end end return M