From 79e799cfa176dc763e1b34a4729b2e45e7f1fc94 Mon Sep 17 00:00:00 2001 From: 1F616EMO Date: Fri, 25 Oct 2024 18:25:16 +0800 Subject: [PATCH] Move areas:save() into async On newer Minetest servers, handles saving jobs in async environment. To prevent conflicts, the save file is locked whie saving, and if a code requests saving while the file is locked, data is saved again immediately after finishing the current save. --- async.lua | 23 +++++++++++++++++++++++ init.lua | 4 ++++ internal.lua | 48 ++++++++++++++++++++++++++++++------------------ 3 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 async.lua diff --git a/async.lua b/async.lua new file mode 100644 index 0000000..3e313e7 --- /dev/null +++ b/async.lua @@ -0,0 +1,23 @@ +areas = rawget(_G, "areas") or {} + +local safe_file_write = core.safe_file_write +if safe_file_write == nil then + safe_file_write = function(path, content) + local file, err = io.open(path, "w") + if err then + return err + end + file:write(content) + file:close() + end +end + +-- Save the areas table to a file +function areas._internal_do_save(areas_tb, filename) + local datastr = core.write_json(areas_tb) + if not datastr then + core.log("error", "[areas] Failed to serialize area data!") + return + end + return safe_file_write(filename, datastr) +end diff --git a/init.lua b/init.lua index 4be95d9..a65ad3d 100644 --- a/init.lua +++ b/init.lua @@ -12,6 +12,10 @@ areas.startTime = os.clock() areas.modpath = minetest.get_modpath("areas") dofile(areas.modpath.."/settings.lua") dofile(areas.modpath.."/api.lua") + +local async_dofile = core.register_async_dofile or dofile +async_dofile(areas.modpath.."/async.lua") + dofile(areas.modpath.."/internal.lua") dofile(areas.modpath.."/chatcommands.lua") dofile(areas.modpath.."/pos.lua") diff --git a/internal.lua b/internal.lua index f23fb3c..700af0f 100644 --- a/internal.lua +++ b/internal.lua @@ -4,26 +4,38 @@ function areas:player_exists(name) return minetest.get_auth_handler().get_auth(name) ~= nil end -local safe_file_write = minetest.safe_file_write -if safe_file_write == nil then - function safe_file_write(path, content) - local file, err = io.open(path, "w") - if err then - return err - end - file:write(content) - file:close() - end -end +-- When saving is done in an async thread, the function will not be present in this global namespace. +if not areas._internal_do_save then + local saving_requested = false + local saving_locked = false --- Save the areas table to a file -function areas:save() - local datastr = minetest.write_json(self.areas) - if not datastr then - minetest.log("error", "[areas] Failed to serialize area data!") - return + -- Required cuz we are referring to _G.areas._internal_do_save *inside* + -- async env (it does not exist in the main thread) + local function async_func(...) + return areas._internal_do_save(...) + end + + local function done_callback() + saving_locked = false + if saving_requested == true then + saving_requested = false + return areas:save() + end + end + + function areas:save() + if saving_locked == true then + saving_requested = true + else + saving_locked = true + return core.handle_async(async_func, done_callback, self.areas, self.config.filename) + end + end +else + -- Save the areas table to a file + function areas:save() + return areas._internal_do_save(self.areas, self.config.filename) end - return safe_file_write(self.config.filename, datastr) end -- Load the areas table from the save file