forked from your-land-mirror/player_settings
262 lines
6.7 KiB
Lua
262 lines
6.7 KiB
Lua
local WP = minetest.get_worldpath()
|
|
minetest.mkdir(WP .. "/player_settings/")
|
|
|
|
local _ps = player_settings
|
|
local s = {}
|
|
local s_nochg = {}
|
|
|
|
local function RTN_TRUE() return true end
|
|
|
|
local function do_register()
|
|
local tb = {}
|
|
local function reg_func(name, def)
|
|
if not def.allow_show then
|
|
def.allow_show = RTN_TRUE
|
|
end
|
|
def.name = name
|
|
tb[name] = def
|
|
end
|
|
local function unreg_func(name)
|
|
tb[name] = nil
|
|
end
|
|
return tb, reg_func, unreg_func
|
|
end
|
|
|
|
_ps.registered_metacategories, _ps.register_metacategory, _ps.unregister_metacategory = do_register()
|
|
_ps.registered_categories, _ps.register_category, _ps.unregister_category = do_register()
|
|
_ps.registered_settings, _ps.register_setting, _ps.unregister_setting = do_register()
|
|
|
|
_ps.registered_on_settings_set = {}
|
|
_ps.register_on_settings_set = function(func)
|
|
table.insert(_ps.registered_on_settings_set,func)
|
|
end
|
|
|
|
_ps.get_settings_path = function(name)
|
|
return (WP .. "/player_settings/" .. name .. ".conf.lua")
|
|
end
|
|
|
|
_ps.get_settings = function(name)
|
|
if type(name) == "userdata" then
|
|
name = name:get_player_name()
|
|
end
|
|
local fp = _ps.get_settings_path(name)
|
|
local f = io.open(fp, "r")
|
|
if not f then return {} end
|
|
local contents = f:read("*a")
|
|
return minetest.deserialize(contents,true)
|
|
end
|
|
|
|
_ps.write_settings = function(name, tb)
|
|
if type(name) == "userdata" then
|
|
name = name:get_player_name()
|
|
end
|
|
if not minetest.player_exists(name) then
|
|
return false, "PLAYER_NOT_EXIST"
|
|
end
|
|
minetest.safe_file_write(_ps.get_settings_path(name), minetest.serialize(tb))
|
|
return true
|
|
end
|
|
|
|
_ps.set_setting = function(name,key,value)
|
|
if type(name) == "userdata" then
|
|
name = name:get_player_name()
|
|
end
|
|
if not minetest.player_exists(name) then
|
|
return false, "PLAYER_NOT_EXIST"
|
|
end
|
|
local setting_entry = _ps.registered_settings[key]
|
|
if not setting_entry then
|
|
return false, "KEY_NOT_EXIST"
|
|
end
|
|
if not s[name] then
|
|
s[name] = _ps.get_settings(name)
|
|
s_nochg[name] = 0
|
|
end
|
|
-- type = "int" / "string" / "bool" / "float" / "enum",
|
|
if setting_entry.type == "int" or setting_entry.type == "float" then
|
|
value = tonumber(value)
|
|
if not value then
|
|
return false, "TYPE_CONVERT_FAILED"
|
|
end
|
|
if setting_entry.type == "int" then
|
|
value = math.floor(value + 0.5)
|
|
end
|
|
if setting_entry.number_min and setting_entry.number_min > value then
|
|
return false, "NUMBER_TOO_SMALL"
|
|
elseif setting_entry.number_max and setting_entry.number_max < value then
|
|
return false, "NUMBER_TOO_LARGE"
|
|
end
|
|
|
|
elseif setting_entry.type == "string" then
|
|
value = tostring(value)
|
|
elseif setting_entry.type == "enum" then
|
|
-- enum_type = "int" / "string" / "float",
|
|
if setting_entry.enum_type == "int" or setting_entry.enum_type == "float" then
|
|
value = tonumber(value)
|
|
if not value then
|
|
return false, "TYPE_CONVERT_FAILED"
|
|
end
|
|
if setting_entry.enum_type == "int" then
|
|
value = math.floor(value + 0.5)
|
|
end
|
|
elseif setting_entry.enum_type == "string" then
|
|
value = tostring(value)
|
|
else
|
|
return false, "SETTING_ENUM_TYPE_INVALID"
|
|
end
|
|
if not (function()
|
|
for _,y in ipairs(setting_entry.enum_choices) do
|
|
if value == y then return true end
|
|
end
|
|
return false
|
|
end)() then
|
|
return false, "SETTING_VALUE_NOT_IN_ENUM"
|
|
end
|
|
elseif setting_entry.type == "bool" then
|
|
if value == true or value == "true" or (tonumber(value) or 0) > 0 then
|
|
value = true
|
|
else
|
|
value = false
|
|
end
|
|
else
|
|
return false, "SETTING_TYPE_INVALID"
|
|
end
|
|
-- validator = function(name, key, value)
|
|
if setting_entry.validator then
|
|
local status, errmsg = setting_entry.validator(name, key, value)
|
|
if not status then
|
|
return false, (errmsg or "VALIDATION_FAILED")
|
|
end
|
|
end
|
|
minetest.log("action", string.format(
|
|
"[player_settings] %s set setting %s to %s",
|
|
name, key, value
|
|
))
|
|
local old_value = s[name][key]
|
|
if setting_entry.default and setting_entry.default == value then
|
|
value = nil -- to save disk space
|
|
end
|
|
s[name][key] = value
|
|
s_nochg[name] = nil
|
|
-- after_change = function(name, key, old_value, new_value) end,
|
|
if setting_entry.after_change then
|
|
setting_entry.after_change(name, key, old_value, value or setting_entry.default)
|
|
end
|
|
for _,func in ipairs(_ps.registered_on_settings_set) do
|
|
func(name, key, old_value, value or setting_entry.default)
|
|
end
|
|
return true
|
|
end
|
|
|
|
_ps.set_default = function(name,key)
|
|
if type(name) == "userdata" then
|
|
name = name:get_player_name()
|
|
end
|
|
if not minetest.player_exists(name) then
|
|
return false, "PLAYER_NOT_EXIST"
|
|
end
|
|
local setting_entry = _ps.registered_settings[key]
|
|
if not setting_entry then
|
|
return false, "KEY_NOT_EXIST"
|
|
end
|
|
if not s[name] then
|
|
s[name] = _ps.get_settings(name)
|
|
s_nochg[name] = 0
|
|
end
|
|
local old_value = s[name][key]
|
|
minetest.log("action", string.format(
|
|
"[player_settings] %s set setting %s to default",
|
|
name, key
|
|
))
|
|
if setting_entry.after_change then
|
|
setting_entry.after_change(name, key, old_value, setting_entry.default)
|
|
end
|
|
for _,func in ipairs(_ps.registered_on_settings_set) do
|
|
func(name, key, old_value, setting_entry.default)
|
|
end
|
|
s[name][key] = nil
|
|
s_nochg[name] = nil
|
|
end
|
|
|
|
_ps.get_setting = function(name,key)
|
|
if type(name) == "userdata" then
|
|
name = name:get_player_name()
|
|
end
|
|
if not minetest.player_exists(name) then
|
|
return false, "PLAYER_NOT_EXIST"
|
|
end
|
|
local setting_entry = _ps.registered_settings[key]
|
|
if not setting_entry then
|
|
return false, "KEY_NOT_EXIST"
|
|
end
|
|
if not s[name] then
|
|
s[name] = _ps.get_settings(name)
|
|
s_nochg[name] = 0
|
|
end
|
|
local value = s[name][key]
|
|
if value == nil then
|
|
value = setting_entry.default
|
|
end
|
|
return true, value
|
|
end
|
|
|
|
_ps.erase_settings = function(name)
|
|
if type(name) == "userdata" then
|
|
name = name:get_player_name()
|
|
end
|
|
minetest.log("action", string.format(
|
|
"[player_settings] Erasing %s's data",
|
|
name
|
|
))
|
|
s[name] = nil
|
|
s_nochg[name] = nil
|
|
os.remove(_ps.get_settings_path(name))
|
|
end
|
|
|
|
_ps.save_all_settings = function()
|
|
for k,v in pairs(s) do
|
|
if not s_nochg[k] then
|
|
minetest.log("action", string.format(
|
|
"[player_settings] Writing %s's data",
|
|
k
|
|
))
|
|
_ps.write_settings(k, v)
|
|
s_nochg[k] = 1
|
|
else
|
|
if s_nochg[k] > 2 then
|
|
minetest.log("action", string.format(
|
|
"[player_settings] %s's data has been idle for 120 seconds. Removing from cache.",
|
|
k
|
|
))
|
|
s[k] = nil -- Free memory
|
|
s_nochg[k] = nil
|
|
else
|
|
minetest.log("action", string.format(
|
|
"[player_settings] %s's data has not been edited. Skipped.",
|
|
k
|
|
))
|
|
s_nochg[k] = s_nochg[k] + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local after_loop = function()
|
|
_ps.save_all_settings()
|
|
minetest.after(60,_ps.save_all_settings)
|
|
end
|
|
|
|
minetest.after(5, after_loop)
|
|
minetest.register_on_shutdown(_ps.save_all_settings)
|
|
|
|
do
|
|
local old_remove_player = minetest.remove_player
|
|
function minetest.remove_player(name)
|
|
local success = old_remove_player(name)
|
|
if success == 0 then
|
|
_ps.erase_settings(name)
|
|
end
|
|
return success
|
|
end
|
|
end
|