forked from your-land-mirror/player_settings
basic
This commit is contained in:
parent
0df68c2435
commit
0290cd0df5
51
.luacheckrc
Normal file
51
.luacheckrc
Normal file
@ -0,0 +1,51 @@
|
||||
read_globals = {
|
||||
"DIR_DELIM", "INIT",
|
||||
|
||||
"minetest", "core",
|
||||
"dump", "dump2",
|
||||
|
||||
"Raycast",
|
||||
"Settings",
|
||||
"PseudoRandom",
|
||||
"PerlinNoise",
|
||||
"VoxelManip",
|
||||
"SecureRandom",
|
||||
"VoxelArea",
|
||||
"PerlinNoiseMap",
|
||||
"PcgRandom",
|
||||
"ItemStack",
|
||||
"AreaStore",
|
||||
|
||||
"vector",
|
||||
|
||||
"flow",
|
||||
|
||||
table = {
|
||||
fields = {
|
||||
"copy",
|
||||
"indexof",
|
||||
"insert_all",
|
||||
"key_value_swap",
|
||||
"shuffle",
|
||||
}
|
||||
},
|
||||
|
||||
string = {
|
||||
fields = {
|
||||
"split",
|
||||
"trim",
|
||||
}
|
||||
},
|
||||
|
||||
math = {
|
||||
fields = {
|
||||
"hypot",
|
||||
"sign",
|
||||
"factorial"
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
globals = {
|
||||
"player_settings",
|
||||
}
|
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Lua.diagnostics.globals": [
|
||||
"minetest",
|
||||
"flow",
|
||||
"player_settings",
|
||||
"dump"
|
||||
]
|
||||
}
|
10
LICENSE
10
LICENSE
@ -1,6 +1,9 @@
|
||||
License of code
|
||||
==================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 C&C Minetest Server
|
||||
Copyright (c) 2023 1F616EMO
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@ -19,3 +22,8 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
License of Media
|
||||
==================
|
||||
|
||||
* textures/settings_*: by Zughy, under CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/
|
91
README.md
91
README.md
@ -1 +1,90 @@
|
||||
# settings
|
||||
# player_settings
|
||||
|
||||
## Definition tables
|
||||
|
||||
### Metacategories
|
||||
|
||||
Used by `player_settings.register_metacategory`.
|
||||
|
||||
```lua
|
||||
{
|
||||
title = "",
|
||||
-- Display title of the metacategory.
|
||||
|
||||
allow_show = function(player) return true end,
|
||||
-- Determine if the metacategory should be shown to the player.
|
||||
-- If returned false, all its child will be hidden.
|
||||
}
|
||||
```
|
||||
|
||||
### Categories
|
||||
|
||||
Used by `player_settings.register_category`.
|
||||
|
||||
```lua
|
||||
{
|
||||
title = "",
|
||||
-- Display title of the category.
|
||||
|
||||
metacategory = "general",
|
||||
-- The ID of the metacategory.
|
||||
|
||||
allow_show = function(player) return true end,
|
||||
-- Determine if the category should be shown to the player.
|
||||
-- If returned false, all its child will be hidden.
|
||||
}
|
||||
```
|
||||
|
||||
### Settings
|
||||
|
||||
Used by `player_settings.register_setting`.
|
||||
|
||||
```lua
|
||||
{
|
||||
description = "",
|
||||
-- Short description of the setting. It should be as short and as simple as possible.
|
||||
|
||||
long_description = ""
|
||||
-- Long description of the setting.
|
||||
|
||||
type = "int" / "string" / "bool" / "float" / "enum",
|
||||
-- Type of the setting.
|
||||
|
||||
default = "",
|
||||
-- Default value of the setting.
|
||||
|
||||
number_min = math.min,
|
||||
number_max = math.huge,
|
||||
-- Only applies when type == "int" / "float".
|
||||
-- The lowest and the highest value of the setting.
|
||||
|
||||
enum_type = "int" / "string" / "float",
|
||||
-- Only applies when type == "enum".
|
||||
-- The type of the choices.
|
||||
|
||||
enum_choices = ["1", "2", ...]
|
||||
-- Only applies when type == "enum".
|
||||
-- All the avaliable choices.
|
||||
|
||||
validator = function(name, key, value) end,
|
||||
-- Validates the input before it is being stored.
|
||||
-- `name` is the player's name.
|
||||
-- `key` is the ID of the setting.
|
||||
-- `value` is the value of the setting to be modified.
|
||||
-- Should return either `true` or a error message.
|
||||
|
||||
after_change = function(name, key, old_value, new_value) end,
|
||||
-- Function triggered after a player had successfully modified one setting.
|
||||
-- `name` is the player's name.
|
||||
-- `key` is the ID of the setting.
|
||||
-- `old_value` is the value of the setting before modifications.
|
||||
-- `new_value` is the value of the setting after modifications.
|
||||
|
||||
category = "general",
|
||||
-- The ID of the category.
|
||||
|
||||
allow_show = function(name) return true end,
|
||||
-- Determine if the setting should be shown to the player.
|
||||
-- If returned false, it will be hidden.
|
||||
}
|
||||
```
|
||||
|
152
api.lua
Normal file
152
api.lua
Normal file
@ -0,0 +1,152 @@
|
||||
local WP = minetest.get_worldpath()
|
||||
minetest.mkdir(WP .. "/player_settings/")
|
||||
|
||||
local _ps = player_settings
|
||||
local s = {}
|
||||
|
||||
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.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
|
||||
minetest.safe_file_write(_ps.get_settings_path(name), minetest.serialize(tb))
|
||||
end
|
||||
|
||||
_ps.set_setting = function(name,key,value)
|
||||
if type(name) == "userdata" then
|
||||
name = name:get_player_name()
|
||||
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)
|
||||
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
|
||||
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
|
||||
s[name][key] = value
|
||||
return true
|
||||
end
|
||||
|
||||
_ps.set_default = function(name,key)
|
||||
if type(name) == "userdata" then
|
||||
name = name:get_player_name()
|
||||
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)
|
||||
end
|
||||
s[name][key] = nil
|
||||
end
|
||||
|
||||
_ps.get_setting = function(name,key)
|
||||
if type(name) == "userdata" then
|
||||
name = name:get_player_name()
|
||||
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)
|
||||
end
|
||||
local value = s[name][key]
|
||||
if value == nil then
|
||||
value = setting_entry.default
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
_ps.save_all_settings = function()
|
||||
for k,v in pairs(s) do
|
||||
_ps.write_settings(k, v)
|
||||
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)
|
43
example.lua
Normal file
43
example.lua
Normal file
@ -0,0 +1,43 @@
|
||||
local _ps = player_settings
|
||||
local S = minetest.get_translator("player_settings")
|
||||
|
||||
_ps.register_metacategory("ps_example_mc",{
|
||||
title = S("Settings Examples MC"),
|
||||
})
|
||||
|
||||
_ps.register_category("ps_example",{
|
||||
title = S("Settings Examples"),
|
||||
metacategory = "ps_example_mc"
|
||||
})
|
||||
|
||||
_ps.register_setting("ps_example_int", {
|
||||
type = "int",
|
||||
description = S("Example of @1", "int"),
|
||||
long_description = S("Long description. \nExample of @1","int"),
|
||||
default = 1,
|
||||
category = "ps_example",
|
||||
})
|
||||
|
||||
_ps.register_setting("ps_example_float", {
|
||||
type = "float",
|
||||
description = S("Example of @1", "float"),
|
||||
long_description = S("Long description. \nExample of @1","float"),
|
||||
default = 1.2345,
|
||||
category = "ps_example",
|
||||
})
|
||||
|
||||
_ps.register_setting("ps_example_string", {
|
||||
type = "string",
|
||||
description = S("Example of @1", "string"),
|
||||
long_description = S("Long description. \nExample of @1","string"),
|
||||
default = "DEFAULT",
|
||||
category = "ps_example",
|
||||
})
|
||||
|
||||
_ps.register_setting("ps_example_bool", {
|
||||
type = "bool",
|
||||
description = S("Example of @1", "bool"),
|
||||
long_description = S("Long description. \nExample of @1","bool"),
|
||||
default = true,
|
||||
category = "ps_example",
|
||||
})
|
222
gui.lua
Normal file
222
gui.lua
Normal file
@ -0,0 +1,222 @@
|
||||
local gui = flow.widgets
|
||||
local _ps = player_settings
|
||||
local S = minetest.get_translator("player_settings")
|
||||
|
||||
_ps.gui = flow.make_gui(function(player,ctx)
|
||||
ctx.name = player:get_player_name()
|
||||
if not ctx.navbarData then
|
||||
local settings_by_category = {}
|
||||
for k,v in pairs(_ps.registered_settings) do
|
||||
if v.allow_show(ctx.name) then
|
||||
if not settings_by_category[v.category] then
|
||||
settings_by_category[v.category] = {}
|
||||
end
|
||||
settings_by_category[v.category][k] = v
|
||||
end
|
||||
end
|
||||
local categories_by_metacat = {}
|
||||
for k,v in pairs(settings_by_category) do
|
||||
if not (_ps.registered_categories[k] and _ps.registered_categories[k].allow_show(ctx.name)) then
|
||||
settings_by_category[k] = nil
|
||||
end
|
||||
if not categories_by_metacat[_ps.registered_categories[k].metacategory] then
|
||||
categories_by_metacat[_ps.registered_categories[k].metacategory] = {}
|
||||
end
|
||||
categories_by_metacat[_ps.registered_categories[k].metacategory][k] = v
|
||||
end
|
||||
for k,v in pairs(categories_by_metacat) do
|
||||
if not (_ps.registered_metacategories[k] and _ps.registered_metacategories[k].allow_show(ctx.name)) then
|
||||
categories_by_metacat[k] = nil
|
||||
end
|
||||
end
|
||||
ctx.navbarData = categories_by_metacat
|
||||
print(dump(ctx.navbarData))
|
||||
end
|
||||
local navbar = {}
|
||||
for k,v in pairs(ctx.navbarData) do
|
||||
table.insert(navbar,gui.Label{ label = _ps.registered_metacategories[k].title })
|
||||
for k2,v2 in pairs(v) do
|
||||
table.insert(navbar,gui.Button {
|
||||
label = _ps.registered_categories[k2].title,
|
||||
w = 1, expand = true,
|
||||
on_event = function(player,ctx)
|
||||
ctx.current_metacat = k
|
||||
ctx.current_category = k2
|
||||
return true
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
navbar.w = 4; navbar.h = 10;
|
||||
navbar.name = "shbox"
|
||||
|
||||
local settings_screen
|
||||
if not ctx.current_category then
|
||||
settings_screen = gui.Label {
|
||||
w = 10, h = 10,
|
||||
label = S("Select a category on the left."),
|
||||
expand = true, align_h = "centre", align_w = "centre",
|
||||
}
|
||||
else
|
||||
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
|
||||
table.insert(svbox, gui.HBox {
|
||||
gui.Checkbox {
|
||||
w = 5,h=1,
|
||||
name = "settings_" .. k,
|
||||
label = v.description,
|
||||
selected = _ps.get_setting(ctx.name,k),
|
||||
on_event = function(player,ctx)
|
||||
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)
|
||||
form["settings_" .. k] = _ps.get_setting(ctx.name,k)
|
||||
return true
|
||||
end
|
||||
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] = 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 ""
|
||||
}
|
||||
})
|
||||
elseif v.type == "enum" then
|
||||
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)),
|
||||
},
|
||||
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,
|
||||
name = "settingsSubmit_" .. k,
|
||||
label = S("Set"),
|
||||
on_event = function(player,ctx)
|
||||
local form = ctx.form
|
||||
local status, errmsg =_ps.set_setting(ctx.name,k,form["settings_" .. k])
|
||||
if not status then
|
||||
print(errmsg)
|
||||
form["settings_" .. k] = _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] = 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 ""
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
svbox.w = 10; svbox.h = 10;
|
||||
svbox.name = "svbox"
|
||||
settings_screen = gui.ScrollableVBox(svbox)
|
||||
end
|
||||
local rtn_gui = gui.VBox {
|
||||
gui.HBox {
|
||||
gui.Label { label = S("Settings"), expand = true, align_h = "left" },
|
||||
gui.ButtonExit { w = 0.7, h = 0.7, label = "x" }
|
||||
},
|
||||
gui.Box{w = 1, h = 0.05, color = "grey", padding = 0},
|
||||
gui.HBox {
|
||||
gui.ScrollableVBox(navbar),
|
||||
settings_screen
|
||||
}
|
||||
}
|
||||
return rtn_gui
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("settings", {
|
||||
description = S("Open settings menu"),
|
||||
func = function(name,param)
|
||||
_ps.gui:show(minetest.get_player_by_name(name))
|
||||
end
|
||||
})
|
9
init.lua
Normal file
9
init.lua
Normal file
@ -0,0 +1,9 @@
|
||||
local MP = minetest.get_modpath("player_settings")
|
||||
|
||||
player_settings = {}
|
||||
|
||||
dofile(MP .. "/util.lua")
|
||||
dofile(MP .. "/api.lua")
|
||||
dofile(MP .. "/gui.lua")
|
||||
|
||||
dofile(MP .. "/example.lua")
|
4
mod.conf
Normal file
4
mod.conf
Normal file
@ -0,0 +1,4 @@
|
||||
name = player_settings
|
||||
title = Player Settings
|
||||
description = Player-specific settings
|
||||
depends = flow
|
BIN
textures/settings_info.png
Normal file
BIN
textures/settings_info.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 144 B |
BIN
textures/settings_reset.png
Normal file
BIN
textures/settings_reset.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 183 B |
Loading…
Reference in New Issue
Block a user