forked from your-land-mirror/player_settings
finally all done
This commit is contained in:
parent
0290cd0df5
commit
8fad85ca32
107
README.md
107
README.md
@ -1,4 +1,101 @@
|
||||
# player_settings
|
||||
# Player Settings
|
||||
|
||||
Allow every players to have their own settings screen and tweak their own settings. Type `/settings` to open the settings GUI.
|
||||
|
||||
## Settings in `minetest.conf`
|
||||
|
||||
* `player_settings_register_example` (bool): Register example settings
|
||||
* Default: `false`
|
||||
* If enabled, register setting examples.
|
||||
* This is always set to true in singleplayer mode regardless of the settings.
|
||||
|
||||
## API
|
||||
|
||||
### Register Settings
|
||||
|
||||
* `player_settings.register_metacategory(name, def)`: Register a settings meta category
|
||||
* `name`: The ID of the meta category
|
||||
* `def`: a [meta category definition table](#metacategories)
|
||||
* `player_settings.register_category(name, def)`: Register a settings category
|
||||
* `name`: The ID of the category
|
||||
* `def`: a [category definition table](#categories)
|
||||
* `player_settings.register_setting(name, def)`: Register a setting
|
||||
* `name`: The ID of the setting
|
||||
* `def`: a [setting definition table](#settings)
|
||||
* `player_settings.unregister_metacategory(name)`: Unregister a metacategory by its name
|
||||
* `player_settings.unregister_category(name)`: Unregister a category by its name
|
||||
* `player_settings.unregister_setting(name)`: Unregister a setting by its name
|
||||
|
||||
### Interact with Settings
|
||||
|
||||
* `player_settings.register_on_settings_set(name,key,old_value,new_value)`: Register callacks on settings set
|
||||
* `name`: Name of a player
|
||||
* `key`: The ID of the setting
|
||||
* `old_value`: The value being replaced
|
||||
* `new_value`: The current setting value
|
||||
* *Not recommended.* To monitor individual settings, set `after_change` in the [setting definition table](#settings).
|
||||
* `player_settings.set_setting(name,key,value)`: Set the value of a setting for a player
|
||||
* `name`: Name or [`ObjectRef`][ObjectRef] of a player
|
||||
* `key`: The ID of the setting
|
||||
* `value`: The value to be set to the setting
|
||||
* Return boolean indicating success
|
||||
* `after_change` and `player_settings.register_on_settings_set` callbacks are called
|
||||
* If failed, the second returned value may be one of the following:
|
||||
* `PLAYER_NOT_EXIST`: The player's auth data does not exist
|
||||
* `KEY_NOT_EXIST`: The setting does not exist
|
||||
* `TYPE_CONVERT_FAILED`: The given value cannot be converted to the type specified in the setting definition
|
||||
* `NUMBER_TOO_SMALL` and `NUMBER_TOO_LARGE` (number only): The given value is below or higher than the required range set in the setting definition
|
||||
* `SETTING_ENUM_TYPE_INVALID` (enum only): The type of enum values set in the settings is invalid
|
||||
* `SETTING_VALUE_NOT_IN_ENUM` (enum only): The given value is not in the list of allowed options set in the setting definition
|
||||
* `SETTING_TYPE_INVALID`: The type of the setting specified in the definition is invalid
|
||||
* `player_settings.set_default(name,key)`: Set the value of a settings back to its default for a player
|
||||
* `name`: Name or [`ObjectRef`][ObjectRef] of a player
|
||||
* `key`: The ID of the setting
|
||||
* Return boolean indicating success
|
||||
* `after_change` and `player_settings.register_on_settings_set` callbacks are called
|
||||
* If failed, the second returned value may be one of the following:
|
||||
* `PLAYER_NOT_EXIST`: The player's auth data does not exist
|
||||
* `KEY_NOT_EXIST`: The setting does not exist
|
||||
* `player_settings.get_setting(name,key)`: Get the value of a setting from a player
|
||||
* `name`: Name or [`ObjectRef`][ObjectRef] of a player
|
||||
* `key`: The ID of the setting
|
||||
* Return boolean indicating success
|
||||
* If success, the second returned value is the setting value
|
||||
* If failed, the second returned value may be one of the following:
|
||||
* `PLAYER_NOT_EXIST`: The player's auth data does not exist
|
||||
* `KEY_NOT_EXIST`: The setting does not exist
|
||||
|
||||
#### Internal
|
||||
|
||||
These functions are for internal uses. Avoid using them in your code.
|
||||
|
||||
* `player_settings.get_settings_path(name)`: Get the file path of the setting file of a player
|
||||
* `name`: Name of the player
|
||||
* `player_settings.get_settings(name)`: Get the table of settings of a player
|
||||
* `name`: Name or [`ObjectRef`][ObjectRef] of a player
|
||||
* `player_settings.write_settings(name,tb)`: Write a setting table to the setting file of a player
|
||||
* `name`: Name or [`ObjectRef`][ObjectRef] of a player
|
||||
* `tb`: Key-value pair of player settings
|
||||
* `player_settings.erase_settings(name)`: Erase all the settings of a player
|
||||
* **WARNING: This is irreversable!**
|
||||
* `name`: Name or [`ObjectRef`][ObjectRef] of a player
|
||||
* `player_settings.save_all_settings()`: Save all in-cache changes to settings
|
||||
|
||||
### Constants
|
||||
|
||||
These are the read-only variables.
|
||||
|
||||
* `player_settings.registered_metacategories`: All registered metacategories
|
||||
* Key: The ID of the metacategory
|
||||
* Value: The [meta category definition table](#metacategories)
|
||||
* `player_settings.registered_categories`: All registered categories
|
||||
* Key: The ID of the category
|
||||
* Value: The [category definition table](#categories)
|
||||
* `player_settings.registered_settings`: All registered settings
|
||||
* Key: The ID of the setting
|
||||
* Value: The [setting definition table](#settings)
|
||||
* `player_settings.gui`: A [flow](https://gitlab.com/luk3yx/minetest-flow/) GUI object
|
||||
* Refer to [the `README.md` of flow mod](https://gitlab.com/luk3yx/minetest-flow/-/blob/main/README.md) for further documentations
|
||||
|
||||
## Definition tables
|
||||
|
||||
@ -66,6 +163,12 @@ Used by `player_settings.register_setting`.
|
||||
-- Only applies when type == "enum".
|
||||
-- All the avaliable choices.
|
||||
|
||||
display_type = "string" / "enum" / "bool",
|
||||
-- How the setting is displayed in the GUI.
|
||||
-- string: For all data types (default of int, float and string)
|
||||
-- enum: for enum only
|
||||
-- bool: for bool only
|
||||
|
||||
validator = function(name, key, value) end,
|
||||
-- Validates the input before it is being stored.
|
||||
-- `name` is the player's name.
|
||||
@ -88,3 +191,5 @@ Used by `player_settings.register_setting`.
|
||||
-- If returned false, it will be hidden.
|
||||
}
|
||||
```
|
||||
|
||||
[ObjectRef]: https://github.com/minetest/minetest/blob/master/doc/lua_api.md#objectref
|
||||
|
113
api.lua
113
api.lua
@ -3,6 +3,7 @@ minetest.mkdir(WP .. "/player_settings/")
|
||||
|
||||
local _ps = player_settings
|
||||
local s = {}
|
||||
local s_nochg = {}
|
||||
|
||||
local function RTN_TRUE() return true end
|
||||
|
||||
@ -25,6 +26,11 @@ _ps.registered_metacategories, _ps.register_metacategory, _ps.unregister_metacat
|
||||
_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
|
||||
@ -44,19 +50,27 @@ _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
|
||||
@ -67,6 +81,12 @@ _ps.set_setting = function(name,key,value)
|
||||
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
|
||||
@ -101,7 +121,30 @@ _ps.set_setting = function(name,key,value)
|
||||
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
|
||||
|
||||
@ -109,37 +152,92 @@ _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 value
|
||||
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
|
||||
_ps.write_settings(k, v)
|
||||
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
|
||||
|
||||
@ -150,3 +248,14 @@ 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
|
||||
|
22
example.lua
22
example.lua
@ -40,4 +40,26 @@ _ps.register_setting("ps_example_bool", {
|
||||
long_description = S("Long description. \nExample of @1","bool"),
|
||||
default = true,
|
||||
category = "ps_example",
|
||||
})
|
||||
|
||||
_ps.register_setting("ps_example_enum_string", {
|
||||
type = "enum",
|
||||
description = S("Example of @1", "enum (string)"),
|
||||
long_description = S("Long description. \nExample of @1","enum (string)"),
|
||||
default = "1F616EMO",
|
||||
category = "ps_example",
|
||||
enum_type = "string",
|
||||
enum_choices = {
|
||||
"lorem",
|
||||
"ipsum",
|
||||
"hello",
|
||||
"world",
|
||||
"minetest",
|
||||
"1F616EMO",
|
||||
}
|
||||
})
|
||||
|
||||
_ps.register_category("ps_example_empty",{
|
||||
title = S("Empty Examples"),
|
||||
metacategory = "ps_example_mc"
|
||||
})
|
214
gui.lua
214
gui.lua
@ -4,6 +4,7 @@ local S = minetest.get_translator("player_settings")
|
||||
|
||||
_ps.gui = flow.make_gui(function(player,ctx)
|
||||
ctx.name = player:get_player_name()
|
||||
if not ctx.showinfo then ctx.showinfo = {} end
|
||||
if not ctx.navbarData then
|
||||
local settings_by_category = {}
|
||||
for k,v in pairs(_ps.registered_settings) do
|
||||
@ -30,7 +31,6 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
||||
end
|
||||
end
|
||||
ctx.navbarData = categories_by_metacat
|
||||
print(dump(ctx.navbarData))
|
||||
end
|
||||
local navbar = {}
|
||||
for k,v in pairs(ctx.navbarData) do
|
||||
@ -39,7 +39,9 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
||||
table.insert(navbar,gui.Button {
|
||||
label = _ps.registered_categories[k2].title,
|
||||
w = 1, expand = true,
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
if ctx.current_category == k2 then return end
|
||||
ctx.current_metacat = k
|
||||
ctx.current_category = k2
|
||||
return true
|
||||
@ -61,22 +63,53 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
||||
local list_settings = ctx.navbarData[ctx.current_metacat][ctx.current_category]
|
||||
local svbox = {}
|
||||
for k,v in pairs(list_settings) do
|
||||
if v.type == "bool" then
|
||||
local desc = S("@1 (@2)",v.description,_ps.util.types[v.type] or v.type)
|
||||
if v.type == "enum" then
|
||||
desc = S("@1 (Multiple-choice of @2)",
|
||||
v.description,
|
||||
_ps.util.types[v.enum_type] or v.enum_type)
|
||||
end
|
||||
|
||||
local display_type = v.display_type
|
||||
if display_type == "enum" and v.type ~= "enum" or
|
||||
display_type == "bool" and v.type ~= "bool" then
|
||||
display_type = nil
|
||||
end
|
||||
|
||||
-- TODO: Add support to scrollbar for numbers (waiting for flow mod support)
|
||||
if not display_type then
|
||||
if v.type == "bool" then
|
||||
display_type = "bool"
|
||||
elseif v.type == "enum" then
|
||||
display_type = "enum"
|
||||
else
|
||||
display_type = "string"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local s_status, selected = _ps.get_setting(ctx.name,k)
|
||||
if not s_status then
|
||||
ctx.errmsg = {k, S("Failed to get setting of @1: @2",
|
||||
k, _ps.util.errmsgs[selected] or selected
|
||||
)}
|
||||
elseif display_type == "bool" then
|
||||
table.insert(svbox, gui.HBox {
|
||||
gui.Checkbox {
|
||||
w = 5,h=1,
|
||||
name = "settings_" .. k,
|
||||
label = v.description,
|
||||
selected = _ps.get_setting(ctx.name,k),
|
||||
label = desc,
|
||||
selected = selected,
|
||||
on_event = function(player,ctx)
|
||||
ctx.errmsg = nil
|
||||
local form = ctx.form
|
||||
if type(form["settings_" .. k]) == "boolean" then
|
||||
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
||||
if not status then
|
||||
print(errmsg)
|
||||
ctx.errmsg = {k,errmsg}
|
||||
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end
|
||||
end,
|
||||
expand = true, align_h = "left",
|
||||
@ -85,91 +118,54 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
||||
w = 1, h = 1,
|
||||
texture_name = "settings_reset.png",
|
||||
name = "settingsReset_" .. k,
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.errmsg = nil
|
||||
local form = ctx.form
|
||||
_ps.set_default(ctx.name,k)
|
||||
form["settings_" .. k] = v.default
|
||||
return true
|
||||
end
|
||||
end,
|
||||
drawborder = false,
|
||||
},
|
||||
gui.Image {
|
||||
gui.ImageButton {
|
||||
w = 1, h = 1,
|
||||
name = "settingsInfo_" .. k,
|
||||
texture_name = "settings_info.png",
|
||||
},
|
||||
gui.Tooltip {
|
||||
gui_element_name = "settingsInfo_" .. k,
|
||||
tooltip_text = v.long_description or ""
|
||||
name = "settingsInfo_" .. k,
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.showinfo[k] = not ctx.showinfo[k]
|
||||
return true
|
||||
end,
|
||||
drawborder = false,
|
||||
}
|
||||
})
|
||||
elseif v.type == "enum" then
|
||||
elseif display_type == "enum" then
|
||||
table.insert(svbox, gui.Label {
|
||||
label = desc
|
||||
})
|
||||
table.insert(svbox, gui.HBox {
|
||||
gui.Dropdown {
|
||||
w = 3,h=1,
|
||||
name = "settings_" .. k,
|
||||
label = v.short_description,
|
||||
items = v.enum_choices,
|
||||
selected = _ps.util.idx_in_table(v.enum_choices,_ps.get_setting(ctx.name,k)),
|
||||
selected_idx = _ps.util.idx_in_table(v.enum_choices,selected),
|
||||
},
|
||||
gui.Button {
|
||||
w = 2,h=1,
|
||||
name = "settingsSubmit_" .. k,
|
||||
label = S("Set"),
|
||||
on_event = function(player,ctx)
|
||||
local form = ctx.form
|
||||
local status, errmsg =_ps.set_setting(ctx.name,k,v.enum_choices[form["settings_" .. k]])
|
||||
if not status then
|
||||
print(errmsg)
|
||||
form["settings_" .. k] = _ps.util.idx_in_table(v.enum_choices,_ps.get_setting(ctx.name,k))
|
||||
return true
|
||||
end
|
||||
end,
|
||||
expand = true, align_h = "left",
|
||||
},
|
||||
gui.ImageButton {
|
||||
w = 1, h = 1,
|
||||
texture_name = "settings_reset.png",
|
||||
name = "settingsReset_" .. k,
|
||||
on_event = function(player,ctx)
|
||||
local form = ctx.form
|
||||
_ps.set_default(ctx.name,k)
|
||||
form["settings_" .. k] = _ps.util.idx_in_table(v.enum_choices,v.default)
|
||||
return true
|
||||
end
|
||||
},
|
||||
gui.Image {
|
||||
w = 1, h = 1,
|
||||
name = "settingsInfo_" .. k,
|
||||
texture_name = "settings_info.png",
|
||||
},
|
||||
gui.Tooltip {
|
||||
gui_element_name = "settingsInfo_" .. k,
|
||||
tooltip_text = v.long_description or ""
|
||||
}
|
||||
})
|
||||
else -- String-like
|
||||
table.insert(svbox, gui.Label {
|
||||
label = v.description
|
||||
})
|
||||
print(_ps.get_setting(ctx.name,k))
|
||||
table.insert(svbox, gui.HBox {
|
||||
gui.Field {
|
||||
w = 3,h=1,
|
||||
name = "settings_" .. k,
|
||||
default = _ps.get_setting(ctx.name,k),
|
||||
},
|
||||
gui.Button {
|
||||
w = 2,h=1,
|
||||
w = 2, h = 1,
|
||||
name = "settingsSubmit_" .. k,
|
||||
label = S("Set"),
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.errmsg = nil
|
||||
local form = ctx.form
|
||||
print(form["settings_" .. k])
|
||||
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
||||
if not status then
|
||||
print(errmsg)
|
||||
ctx.errmsg = {k,errmsg}
|
||||
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end,
|
||||
expand = true, align_h = "left",
|
||||
},
|
||||
@ -177,23 +173,95 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
||||
w = 1, h = 1,
|
||||
texture_name = "settings_reset.png",
|
||||
name = "settingsReset_" .. k,
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.errmsg = nil
|
||||
local form = ctx.form
|
||||
_ps.set_default(ctx.name,k)
|
||||
form["settings_" .. k] = v.default
|
||||
return true
|
||||
end
|
||||
end,
|
||||
drawborder = false,
|
||||
},
|
||||
gui.Image {
|
||||
gui.ImageButton {
|
||||
w = 1, h = 1,
|
||||
name = "settingsInfo_" .. k,
|
||||
texture_name = "settings_info.png",
|
||||
},
|
||||
gui.Tooltip {
|
||||
gui_element_name = "settingsInfo_" .. k,
|
||||
tooltip_text = v.long_description or ""
|
||||
name = "settingsInfo_" .. k,
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.showinfo[k] = not ctx.showinfo[k]
|
||||
return true
|
||||
end,
|
||||
drawborder = false,
|
||||
}
|
||||
})
|
||||
elseif display_type == "string" then -- String-like
|
||||
table.insert(svbox, gui.Label {
|
||||
label = desc
|
||||
})
|
||||
table.insert(svbox, gui.HBox {
|
||||
gui.Field {
|
||||
w = 3,h=1,
|
||||
name = "settings_" .. k,
|
||||
default = selected,
|
||||
},
|
||||
gui.Button {
|
||||
w = 2,h=1,
|
||||
name = "settingsSubmit_" .. k,
|
||||
label = S("Set"),
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.errmsg = nil
|
||||
local form = ctx.form
|
||||
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
||||
if not status then
|
||||
ctx.errmsg = {k,errmsg}
|
||||
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
expand = true, align_h = "left",
|
||||
},
|
||||
gui.ImageButton {
|
||||
w = 1, h = 1,
|
||||
texture_name = "settings_reset.png",
|
||||
name = "settingsReset_" .. k,
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.errmsg = nil
|
||||
local form = ctx.form
|
||||
_ps.set_default(ctx.name,k)
|
||||
form["settings_" .. k] = v.default
|
||||
return true
|
||||
end,
|
||||
drawborder = false,
|
||||
},
|
||||
gui.ImageButton {
|
||||
w = 1, h = 1,
|
||||
texture_name = "settings_info.png",
|
||||
name = "settingsInfo_" .. k,
|
||||
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||
on_event = function(player,ctx)
|
||||
ctx.showinfo[k] = not ctx.showinfo[k]
|
||||
return true
|
||||
end,
|
||||
drawborder = false,
|
||||
}
|
||||
})
|
||||
else
|
||||
error("[player_settings] Attempt to display setting with unknown display type " .. display_type)
|
||||
end
|
||||
if ctx.errmsg and ctx.errmsg[1] == k then
|
||||
local user_errmsg = _ps.util.errmsgs[ctx.errmsg[2]] or ctx.errmsg[2]
|
||||
table.insert(svbox, gui.Label {
|
||||
label = minetest.colorize("#FF0000", S("Error: @1", user_errmsg)),
|
||||
-- label = ctx.errmsg[2],
|
||||
})
|
||||
end
|
||||
if ctx.showinfo[k] and v.long_description then
|
||||
table.insert(svbox, gui.Label {
|
||||
label = minetest.get_color_escape_sequence("#00FF00") .. v.long_description,
|
||||
})
|
||||
end
|
||||
end
|
||||
svbox.w = 10; svbox.h = 10;
|
||||
|
4
init.lua
4
init.lua
@ -6,4 +6,6 @@ dofile(MP .. "/util.lua")
|
||||
dofile(MP .. "/api.lua")
|
||||
dofile(MP .. "/gui.lua")
|
||||
|
||||
dofile(MP .. "/example.lua")
|
||||
if minetest.is_singleplayer() or minetest.settings:get_bool("player_settings_register_example", false) then
|
||||
dofile(MP .. "/example.lua")
|
||||
end
|
||||
|
3
settingtypes.txt
Normal file
3
settingtypes.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# If enabled, register setting examples.
|
||||
# This is always set to true in singleplayer mode regardless of the settings.
|
||||
player_settings_register_example (Register example settings) bool false
|
24
util.lua
24
util.lua
@ -1,3 +1,4 @@
|
||||
local S = minetest.get_translator("player_settings")
|
||||
local _ps = player_settings
|
||||
_ps.util = {}
|
||||
|
||||
@ -6,4 +7,25 @@ _ps.util.idx_in_table = function(tb,v)
|
||||
if tv == v then return i end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
_ps.util.errmsgs = {
|
||||
PLAYER_NOT_EXIST = S("Player does not exist"),
|
||||
KEY_NOT_EXIST = S("Key does not exist"),
|
||||
TYPE_CONVERT_FAILED = S("Value not matching the data type"),
|
||||
SETTING_ENUM_TYPE_INVALID = S("Invalid enum data type"),
|
||||
SETTING_VALUE_NOT_IN_ENUM = S("Value not in list of choices"),
|
||||
SETTING_TYPE_INVALID = S("Invalid setting data type"),
|
||||
VALIDATION_FAILED = S("Validation failed"),
|
||||
NUMBER_TOO_SMALL = S("Number too small"),
|
||||
NUMBER_TOO_LARGE = S("Number too large"),
|
||||
}
|
||||
|
||||
-- type = "int" / "string" / "bool" / "float" / "enum"
|
||||
_ps.util.types = {
|
||||
int = S("Integer"),
|
||||
string = S("String"),
|
||||
bool = S("Boolean"),
|
||||
float = S("Float"),
|
||||
enum = S("Multiple-choice")
|
||||
}
|
Loading…
Reference in New Issue
Block a user