From c850d11a3ccaf21183a137c0ecaf0ebaee58f514 Mon Sep 17 00:00:00 2001 From: luk3yx Date: Fri, 8 Sep 2023 03:38:26 +1200 Subject: [PATCH] Use JSON in xban.db (#26) This should prevent bans database from resetting with a "function has more than 65536 constants" error. Older databases should still be loaded correctly. Also makes use of minetest.safe_file_write to avoid data corruption. --- init.lua | 17 ++++++----------- serialize.lua | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/init.lua b/init.lua index 09385bd..0940cb0 100644 --- a/init.lua +++ b/init.lua @@ -25,6 +25,7 @@ end local ACTION = make_logger("action") local WARNING = make_logger("warning") +local ERROR = make_logger("error") local unit_to_secs = { s = 1, m = 60, h = 3600, @@ -343,18 +344,12 @@ end local function save_db() minetest.after(SAVE_INTERVAL, save_db) - local f, e = io.open(DB_FILENAME, "wt") db.timestamp = os.time() - if f then - local ok, err = f:write(xban.serialize(db)) - if not ok then - WARNING("Unable to save database: %s", err) - end - else - WARNING("Unable to save database: %s", e) + local contents = assert(xban.serialize_db(db)) + local ok = minetest.safe_file_write(DB_FILENAME, contents) + if not ok then + ERROR("Unable to save database") end - if f then f:close() end - return end local function load_db() @@ -368,7 +363,7 @@ local function load_db() WARNING("Unable to load database: %s", "Read failed") return end - local t, e2 = minetest.deserialize(cont) + local t, e2 = xban.deserialize_db(cont) if not t then WARNING("Unable to load database: %s", "Deserialization failed: "..(e2 or "unknown error")) diff --git a/serialize.lua b/serialize.lua index c159141..0a54212 100644 --- a/serialize.lua +++ b/serialize.lua @@ -27,5 +27,44 @@ local function my_serialize_2(t, level) end function xban.serialize(t) + minetest.log("warning", "[xban2] xban.serialize() is deprecated") return "return {\n"..my_serialize_2(t, 1).."\n}" end + +-- JSON doesn't allow combined string+number keys, this function moves any +-- number keys into an "entries" table +function xban.serialize_db(t) + local res = {} + local entries = {} + for k, v in pairs(t) do + if type(k) == "number" then + entries[k] = v + else + res[k] = v + end + end + res.entries = entries + return minetest.write_json(res, true) +end + +function xban.deserialize_db(s) + if s:sub(1, 1) ~= "{" then + -- Load legacy databases + return minetest.deserialize(s) + end + + local res, err = minetest.parse_json(s) + if not res then + return nil, err + end + + -- Remove all "null"s added by empty tables + for i, entry in ipairs(res.entries or {}) do + entry.names = entry.names or {} + entry.record = entry.record or {} + res[i] = entry + end + res.entries = nil + + return res +end