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.
This commit is contained in:
1F616EMO 2024-10-25 18:25:16 +08:00 committed by SmallJoker
parent ec77a57f42
commit 79e799cfa1
3 changed files with 57 additions and 18 deletions

23
async.lua Normal file
View File

@ -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

View File

@ -12,6 +12,10 @@ areas.startTime = os.clock()
areas.modpath = minetest.get_modpath("areas") areas.modpath = minetest.get_modpath("areas")
dofile(areas.modpath.."/settings.lua") dofile(areas.modpath.."/settings.lua")
dofile(areas.modpath.."/api.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.."/internal.lua")
dofile(areas.modpath.."/chatcommands.lua") dofile(areas.modpath.."/chatcommands.lua")
dofile(areas.modpath.."/pos.lua") dofile(areas.modpath.."/pos.lua")

View File

@ -4,26 +4,38 @@ function areas:player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil return minetest.get_auth_handler().get_auth(name) ~= nil
end end
local safe_file_write = minetest.safe_file_write -- When saving is done in an async thread, the function will not be present in this global namespace.
if safe_file_write == nil then if not areas._internal_do_save then
function safe_file_write(path, content) local saving_requested = false
local file, err = io.open(path, "w") local saving_locked = false
if err then
return err -- 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 end
file:write(content)
file:close() local function done_callback()
saving_locked = false
if saving_requested == true then
saving_requested = false
return areas:save()
end end
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 -- Save the areas table to a file
function areas:save() function areas:save()
local datastr = minetest.write_json(self.areas) return areas._internal_do_save(self.areas, self.config.filename)
if not datastr then
minetest.log("error", "[areas] Failed to serialize area data!")
return
end end
return safe_file_write(self.config.filename, datastr)
end end
-- Load the areas table from the save file -- Load the areas table from the save file