/ api.lua
api.lua
1 local WP = minetest.get_worldpath() 2 minetest.mkdir(WP .. "/player_settings/") 3 4 local _ps = player_settings 5 local s = {} 6 local s_nochg = {} 7 8 local function RTN_TRUE() return true end 9 10 local function do_register() 11 local tb = {} 12 local function reg_func(name, def) 13 if not def.allow_show then 14 def.allow_show = RTN_TRUE 15 end 16 def.name = name 17 tb[name] = def 18 end 19 local function unreg_func(name) 20 tb[name] = nil 21 end 22 return tb, reg_func, unreg_func 23 end 24 25 _ps.registered_metacategories, _ps.register_metacategory, _ps.unregister_metacategory = do_register() 26 _ps.registered_categories, _ps.register_category, _ps.unregister_category = do_register() 27 _ps.registered_settings, _ps.register_setting, _ps.unregister_setting = do_register() 28 29 _ps.registered_on_settings_set = {} 30 _ps.register_on_settings_set = function(func) 31 table.insert(_ps.registered_on_settings_set,func) 32 end 33 34 _ps.get_settings_path = function(name) 35 return (WP .. "/player_settings/" .. name .. ".conf.lua") 36 end 37 38 _ps.get_settings = function(name) 39 if type(name) == "userdata" then 40 name = name:get_player_name() 41 end 42 local fp = _ps.get_settings_path(name) 43 local f = io.open(fp, "r") 44 if not f then return {} end 45 local contents = f:read("*a") 46 return minetest.deserialize(contents,true) 47 end 48 49 _ps.write_settings = function(name, tb) 50 if type(name) == "userdata" then 51 name = name:get_player_name() 52 end 53 if not minetest.player_exists(name) then 54 return false, "PLAYER_NOT_EXIST" 55 end 56 minetest.safe_file_write(_ps.get_settings_path(name), minetest.serialize(tb)) 57 return true 58 end 59 60 _ps.set_setting = function(name,key,value) 61 if type(name) == "userdata" then 62 name = name:get_player_name() 63 end 64 if not minetest.player_exists(name) then 65 return false, "PLAYER_NOT_EXIST" 66 end 67 local setting_entry = _ps.registered_settings[key] 68 if not setting_entry then 69 return false, "KEY_NOT_EXIST" 70 end 71 if not s[name] then 72 s[name] = _ps.get_settings(name) 73 s_nochg[name] = 0 74 end 75 -- type = "int" / "string" / "bool" / "float" / "enum", 76 if setting_entry.type == "int" or setting_entry.type == "float" then 77 value = tonumber(value) 78 if not value then 79 return false, "TYPE_CONVERT_FAILED" 80 end 81 if setting_entry.type == "int" then 82 value = math.floor(value + 0.5) 83 end 84 if setting_entry.number_min and setting_entry.number_min > value then 85 return false, "NUMBER_TOO_SMALL" 86 elseif setting_entry.number_max and setting_entry.number_max < value then 87 return false, "NUMBER_TOO_LARGE" 88 end 89 90 elseif setting_entry.type == "string" then 91 value = tostring(value) 92 elseif setting_entry.type == "enum" then 93 -- enum_type = "int" / "string" / "float", 94 if setting_entry.enum_type == "int" or setting_entry.enum_type == "float" then 95 value = tonumber(value) 96 if not value then 97 return false, "TYPE_CONVERT_FAILED" 98 end 99 if setting_entry.enum_type == "int" then 100 value = math.floor(value + 0.5) 101 end 102 elseif setting_entry.enum_type == "string" then 103 value = tostring(value) 104 else 105 return false, "SETTING_ENUM_TYPE_INVALID" 106 end 107 if not (function() 108 for _,y in ipairs(setting_entry.enum_choices) do 109 if value == y then return true end 110 end 111 return false 112 end)() then 113 return false, "SETTING_VALUE_NOT_IN_ENUM" 114 end 115 elseif setting_entry.type == "bool" then 116 if value == true or value == "true" or (tonumber(value) or 0) > 0 then 117 value = true 118 else 119 value = false 120 end 121 else 122 return false, "SETTING_TYPE_INVALID" 123 end 124 -- validator = function(name, key, value) 125 if setting_entry.validator then 126 local status, errmsg = setting_entry.validator(name, key, value) 127 if not status then 128 return false, (errmsg or "VALIDATION_FAILED") 129 end 130 end 131 minetest.log("action", string.format( 132 "[player_settings] %s set setting %s to %s", 133 name, key, value 134 )) 135 local old_value = s[name][key] 136 if setting_entry.default and setting_entry.default == value then 137 value = nil -- to save disk space 138 end 139 s[name][key] = value 140 s_nochg[name] = nil 141 -- after_change = function(name, key, old_value, new_value) end, 142 if setting_entry.after_change then 143 setting_entry.after_change(name, key, old_value, value or setting_entry.default) 144 end 145 for _,func in ipairs(_ps.registered_on_settings_set) do 146 func(name, key, old_value, value or setting_entry.default) 147 end 148 return true 149 end 150 151 _ps.set_default = function(name,key) 152 if type(name) == "userdata" then 153 name = name:get_player_name() 154 end 155 if not minetest.player_exists(name) then 156 return false, "PLAYER_NOT_EXIST" 157 end 158 local setting_entry = _ps.registered_settings[key] 159 if not setting_entry then 160 return false, "KEY_NOT_EXIST" 161 end 162 if not s[name] then 163 s[name] = _ps.get_settings(name) 164 s_nochg[name] = 0 165 end 166 local old_value = s[name][key] 167 minetest.log("action", string.format( 168 "[player_settings] %s set setting %s to default", 169 name, key 170 )) 171 if setting_entry.after_change then 172 setting_entry.after_change(name, key, old_value, setting_entry.default) 173 end 174 for _,func in ipairs(_ps.registered_on_settings_set) do 175 func(name, key, old_value, setting_entry.default) 176 end 177 s[name][key] = nil 178 s_nochg[name] = nil 179 end 180 181 _ps.get_setting = function(name,key) 182 if type(name) == "userdata" then 183 name = name:get_player_name() 184 end 185 if not minetest.player_exists(name) then 186 return false, "PLAYER_NOT_EXIST" 187 end 188 local setting_entry = _ps.registered_settings[key] 189 if not setting_entry then 190 return false, "KEY_NOT_EXIST" 191 end 192 if not s[name] then 193 s[name] = _ps.get_settings(name) 194 s_nochg[name] = 0 195 end 196 local value = s[name][key] 197 if value == nil then 198 value = setting_entry.default 199 end 200 return true, value 201 end 202 203 _ps.erase_settings = function(name) 204 if type(name) == "userdata" then 205 name = name:get_player_name() 206 end 207 minetest.log("action", string.format( 208 "[player_settings] Erasing %s's data", 209 name 210 )) 211 s[name] = nil 212 s_nochg[name] = nil 213 os.remove(_ps.get_settings_path(name)) 214 end 215 216 _ps.save_all_settings = function() 217 for k,v in pairs(s) do 218 if not s_nochg[k] then 219 minetest.log("action", string.format( 220 "[player_settings] Writing %s's data", 221 k 222 )) 223 _ps.write_settings(k, v) 224 s_nochg[k] = 1 225 else 226 if s_nochg[k] > 2 then 227 minetest.log("action", string.format( 228 "[player_settings] %s's data has been idle for 120 seconds. Removing from cache.", 229 k 230 )) 231 s[k] = nil -- Free memory 232 s_nochg[k] = nil 233 else 234 minetest.log("action", string.format( 235 "[player_settings] %s's data has not been edited. Skipped.", 236 k 237 )) 238 s_nochg[k] = s_nochg[k] + 1 239 end 240 end 241 end 242 end 243 244 local function after_loop() 245 _ps.save_all_settings() 246 minetest.after(60, after_loop) 247 end 248 249 minetest.after(5, after_loop) 250 minetest.register_on_shutdown(_ps.save_all_settings) 251 252 do 253 local old_remove_player = minetest.remove_player 254 function minetest.remove_player(name) 255 local success = old_remove_player(name) 256 if success == 0 then 257 _ps.erase_settings(name) 258 end 259 return success 260 end 261 end