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
|
## Definition tables
|
||||||
|
|
||||||
@ -66,6 +163,12 @@ Used by `player_settings.register_setting`.
|
|||||||
-- Only applies when type == "enum".
|
-- Only applies when type == "enum".
|
||||||
-- All the avaliable choices.
|
-- 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,
|
validator = function(name, key, value) end,
|
||||||
-- Validates the input before it is being stored.
|
-- Validates the input before it is being stored.
|
||||||
-- `name` is the player's name.
|
-- `name` is the player's name.
|
||||||
@ -88,3 +191,5 @@ Used by `player_settings.register_setting`.
|
|||||||
-- If returned false, it will be hidden.
|
-- 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 _ps = player_settings
|
||||||
local s = {}
|
local s = {}
|
||||||
|
local s_nochg = {}
|
||||||
|
|
||||||
local function RTN_TRUE() return true end
|
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_categories, _ps.register_category, _ps.unregister_category = do_register()
|
||||||
_ps.registered_settings, _ps.register_setting, _ps.unregister_setting = 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)
|
_ps.get_settings_path = function(name)
|
||||||
return (WP .. "/player_settings/" .. name .. ".conf.lua")
|
return (WP .. "/player_settings/" .. name .. ".conf.lua")
|
||||||
end
|
end
|
||||||
@ -44,19 +50,27 @@ _ps.write_settings = function(name, tb)
|
|||||||
if type(name) == "userdata" then
|
if type(name) == "userdata" then
|
||||||
name = name:get_player_name()
|
name = name:get_player_name()
|
||||||
end
|
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))
|
minetest.safe_file_write(_ps.get_settings_path(name), minetest.serialize(tb))
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
_ps.set_setting = function(name,key,value)
|
_ps.set_setting = function(name,key,value)
|
||||||
if type(name) == "userdata" then
|
if type(name) == "userdata" then
|
||||||
name = name:get_player_name()
|
name = name:get_player_name()
|
||||||
end
|
end
|
||||||
|
if not minetest.player_exists(name) then
|
||||||
|
return false, "PLAYER_NOT_EXIST"
|
||||||
|
end
|
||||||
local setting_entry = _ps.registered_settings[key]
|
local setting_entry = _ps.registered_settings[key]
|
||||||
if not setting_entry then
|
if not setting_entry then
|
||||||
return false, "KEY_NOT_EXIST"
|
return false, "KEY_NOT_EXIST"
|
||||||
end
|
end
|
||||||
if not s[name] then
|
if not s[name] then
|
||||||
s[name] = _ps.get_settings(name)
|
s[name] = _ps.get_settings(name)
|
||||||
|
s_nochg[name] = 0
|
||||||
end
|
end
|
||||||
-- type = "int" / "string" / "bool" / "float" / "enum",
|
-- type = "int" / "string" / "bool" / "float" / "enum",
|
||||||
if setting_entry.type == "int" or setting_entry.type == "float" then
|
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
|
if setting_entry.type == "int" then
|
||||||
value = math.floor(value + 0.5)
|
value = math.floor(value + 0.5)
|
||||||
end
|
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
|
elseif setting_entry.type == "string" then
|
||||||
value = tostring(value)
|
value = tostring(value)
|
||||||
elseif setting_entry.type == "enum" then
|
elseif setting_entry.type == "enum" then
|
||||||
@ -101,7 +121,30 @@ _ps.set_setting = function(name,key,value)
|
|||||||
else
|
else
|
||||||
return false, "SETTING_TYPE_INVALID"
|
return false, "SETTING_TYPE_INVALID"
|
||||||
end
|
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[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
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -109,37 +152,92 @@ _ps.set_default = function(name,key)
|
|||||||
if type(name) == "userdata" then
|
if type(name) == "userdata" then
|
||||||
name = name:get_player_name()
|
name = name:get_player_name()
|
||||||
end
|
end
|
||||||
|
if not minetest.player_exists(name) then
|
||||||
|
return false, "PLAYER_NOT_EXIST"
|
||||||
|
end
|
||||||
local setting_entry = _ps.registered_settings[key]
|
local setting_entry = _ps.registered_settings[key]
|
||||||
if not setting_entry then
|
if not setting_entry then
|
||||||
return false, "KEY_NOT_EXIST"
|
return false, "KEY_NOT_EXIST"
|
||||||
end
|
end
|
||||||
if not s[name] then
|
if not s[name] then
|
||||||
s[name] = _ps.get_settings(name)
|
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
|
end
|
||||||
s[name][key] = nil
|
s[name][key] = nil
|
||||||
|
s_nochg[name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
_ps.get_setting = function(name,key)
|
_ps.get_setting = function(name,key)
|
||||||
if type(name) == "userdata" then
|
if type(name) == "userdata" then
|
||||||
name = name:get_player_name()
|
name = name:get_player_name()
|
||||||
end
|
end
|
||||||
|
if not minetest.player_exists(name) then
|
||||||
|
return false, "PLAYER_NOT_EXIST"
|
||||||
|
end
|
||||||
local setting_entry = _ps.registered_settings[key]
|
local setting_entry = _ps.registered_settings[key]
|
||||||
if not setting_entry then
|
if not setting_entry then
|
||||||
return false, "KEY_NOT_EXIST"
|
return false, "KEY_NOT_EXIST"
|
||||||
end
|
end
|
||||||
if not s[name] then
|
if not s[name] then
|
||||||
s[name] = _ps.get_settings(name)
|
s[name] = _ps.get_settings(name)
|
||||||
|
s_nochg[name] = 0
|
||||||
end
|
end
|
||||||
local value = s[name][key]
|
local value = s[name][key]
|
||||||
if value == nil then
|
if value == nil then
|
||||||
value = setting_entry.default
|
value = setting_entry.default
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
_ps.save_all_settings = function()
|
_ps.save_all_settings = function()
|
||||||
for k,v in pairs(s) do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -150,3 +248,14 @@ end
|
|||||||
|
|
||||||
minetest.after(5, after_loop)
|
minetest.after(5, after_loop)
|
||||||
minetest.register_on_shutdown(_ps.save_all_settings)
|
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"),
|
long_description = S("Long description. \nExample of @1","bool"),
|
||||||
default = true,
|
default = true,
|
||||||
category = "ps_example",
|
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)
|
_ps.gui = flow.make_gui(function(player,ctx)
|
||||||
ctx.name = player:get_player_name()
|
ctx.name = player:get_player_name()
|
||||||
|
if not ctx.showinfo then ctx.showinfo = {} end
|
||||||
if not ctx.navbarData then
|
if not ctx.navbarData then
|
||||||
local settings_by_category = {}
|
local settings_by_category = {}
|
||||||
for k,v in pairs(_ps.registered_settings) do
|
for k,v in pairs(_ps.registered_settings) do
|
||||||
@ -30,7 +31,6 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
ctx.navbarData = categories_by_metacat
|
ctx.navbarData = categories_by_metacat
|
||||||
print(dump(ctx.navbarData))
|
|
||||||
end
|
end
|
||||||
local navbar = {}
|
local navbar = {}
|
||||||
for k,v in pairs(ctx.navbarData) do
|
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 {
|
table.insert(navbar,gui.Button {
|
||||||
label = _ps.registered_categories[k2].title,
|
label = _ps.registered_categories[k2].title,
|
||||||
w = 1, expand = true,
|
w = 1, expand = true,
|
||||||
|
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||||
on_event = function(player,ctx)
|
on_event = function(player,ctx)
|
||||||
|
if ctx.current_category == k2 then return end
|
||||||
ctx.current_metacat = k
|
ctx.current_metacat = k
|
||||||
ctx.current_category = k2
|
ctx.current_category = k2
|
||||||
return true
|
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 list_settings = ctx.navbarData[ctx.current_metacat][ctx.current_category]
|
||||||
local svbox = {}
|
local svbox = {}
|
||||||
for k,v in pairs(list_settings) do
|
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 {
|
table.insert(svbox, gui.HBox {
|
||||||
gui.Checkbox {
|
gui.Checkbox {
|
||||||
w = 5,h=1,
|
w = 5,h=1,
|
||||||
name = "settings_" .. k,
|
name = "settings_" .. k,
|
||||||
label = v.description,
|
label = desc,
|
||||||
selected = _ps.get_setting(ctx.name,k),
|
selected = selected,
|
||||||
on_event = function(player,ctx)
|
on_event = function(player,ctx)
|
||||||
|
ctx.errmsg = nil
|
||||||
local form = ctx.form
|
local form = ctx.form
|
||||||
if type(form["settings_" .. k]) == "boolean" then
|
if type(form["settings_" .. k]) == "boolean" then
|
||||||
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
||||||
if not status then
|
if not status then
|
||||||
print(errmsg)
|
ctx.errmsg = {k,errmsg}
|
||||||
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
expand = true, align_h = "left",
|
expand = true, align_h = "left",
|
||||||
@ -85,91 +118,54 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
|||||||
w = 1, h = 1,
|
w = 1, h = 1,
|
||||||
texture_name = "settings_reset.png",
|
texture_name = "settings_reset.png",
|
||||||
name = "settingsReset_" .. k,
|
name = "settingsReset_" .. k,
|
||||||
|
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||||
on_event = function(player,ctx)
|
on_event = function(player,ctx)
|
||||||
|
ctx.errmsg = nil
|
||||||
local form = ctx.form
|
local form = ctx.form
|
||||||
_ps.set_default(ctx.name,k)
|
_ps.set_default(ctx.name,k)
|
||||||
form["settings_" .. k] = v.default
|
form["settings_" .. k] = v.default
|
||||||
return true
|
return true
|
||||||
end
|
end,
|
||||||
|
drawborder = false,
|
||||||
},
|
},
|
||||||
gui.Image {
|
gui.ImageButton {
|
||||||
w = 1, h = 1,
|
w = 1, h = 1,
|
||||||
name = "settingsInfo_" .. k,
|
|
||||||
texture_name = "settings_info.png",
|
texture_name = "settings_info.png",
|
||||||
},
|
name = "settingsInfo_" .. k,
|
||||||
gui.Tooltip {
|
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||||
gui_element_name = "settingsInfo_" .. k,
|
on_event = function(player,ctx)
|
||||||
tooltip_text = v.long_description or ""
|
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 {
|
table.insert(svbox, gui.HBox {
|
||||||
gui.Dropdown {
|
gui.Dropdown {
|
||||||
w = 3,h=1,
|
w = 3,h=1,
|
||||||
name = "settings_" .. k,
|
name = "settings_" .. k,
|
||||||
label = v.short_description,
|
|
||||||
items = v.enum_choices,
|
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 {
|
gui.Button {
|
||||||
w = 2,h=1,
|
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,
|
|
||||||
name = "settingsSubmit_" .. k,
|
name = "settingsSubmit_" .. k,
|
||||||
label = S("Set"),
|
label = S("Set"),
|
||||||
|
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||||
on_event = function(player,ctx)
|
on_event = function(player,ctx)
|
||||||
|
ctx.errmsg = nil
|
||||||
local form = ctx.form
|
local form = ctx.form
|
||||||
|
print(form["settings_" .. k])
|
||||||
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
||||||
if not status then
|
if not status then
|
||||||
print(errmsg)
|
ctx.errmsg = {k,errmsg}
|
||||||
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
end,
|
end,
|
||||||
expand = true, align_h = "left",
|
expand = true, align_h = "left",
|
||||||
},
|
},
|
||||||
@ -177,23 +173,95 @@ _ps.gui = flow.make_gui(function(player,ctx)
|
|||||||
w = 1, h = 1,
|
w = 1, h = 1,
|
||||||
texture_name = "settings_reset.png",
|
texture_name = "settings_reset.png",
|
||||||
name = "settingsReset_" .. k,
|
name = "settingsReset_" .. k,
|
||||||
|
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||||
on_event = function(player,ctx)
|
on_event = function(player,ctx)
|
||||||
|
ctx.errmsg = nil
|
||||||
local form = ctx.form
|
local form = ctx.form
|
||||||
_ps.set_default(ctx.name,k)
|
_ps.set_default(ctx.name,k)
|
||||||
form["settings_" .. k] = v.default
|
form["settings_" .. k] = v.default
|
||||||
return true
|
return true
|
||||||
end
|
end,
|
||||||
|
drawborder = false,
|
||||||
},
|
},
|
||||||
gui.Image {
|
gui.ImageButton {
|
||||||
w = 1, h = 1,
|
w = 1, h = 1,
|
||||||
name = "settingsInfo_" .. k,
|
|
||||||
texture_name = "settings_info.png",
|
texture_name = "settings_info.png",
|
||||||
},
|
name = "settingsInfo_" .. k,
|
||||||
gui.Tooltip {
|
---@diagnostic disable-next-line: redefined-local, unused-local
|
||||||
gui_element_name = "settingsInfo_" .. k,
|
on_event = function(player,ctx)
|
||||||
tooltip_text = v.long_description or ""
|
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
|
||||||
end
|
end
|
||||||
svbox.w = 10; svbox.h = 10;
|
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 .. "/api.lua")
|
||||||
dofile(MP .. "/gui.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
|
local _ps = player_settings
|
||||||
_ps.util = {}
|
_ps.util = {}
|
||||||
|
|
||||||
@ -6,4 +7,25 @@ _ps.util.idx_in_table = function(tb,v)
|
|||||||
if tv == v then return i end
|
if tv == v then return i end
|
||||||
end
|
end
|
||||||
return nil
|
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