Skip to content

Commit

Permalink
In-game settings menu using separate Lua environment
Browse files Browse the repository at this point in the history
  • Loading branch information
grorp committed Jan 16, 2025
1 parent c8b5e3b commit 76685b8
Show file tree
Hide file tree
Showing 48 changed files with 609 additions and 255 deletions.
5 changes: 1 addition & 4 deletions .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ignore = {
read_globals = {
"ItemStack",
"INIT",
"PLATFORM",
"DIR_DELIM",
"dump", "dump2",
"fgettext", "fgettext_ne",
Expand Down Expand Up @@ -75,10 +76,6 @@ files["builtin/mainmenu"] = {
globals = {
"gamedata",
},

read_globals = {
"PLATFORM",
},
}

files["builtin/common/tests"] = {
Expand Down
5 changes: 4 additions & 1 deletion builtin/client/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ local scriptpath = core.get_builtin_path()
local clientpath = scriptpath.."client"..DIR_DELIM
local commonpath = scriptpath.."common"..DIR_DELIM

dofile(clientpath .. "register.lua")
local builtin_shared = {}

assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
assert(loadfile(clientpath .. "register.lua"))(builtin_shared)
dofile(commonpath .. "after.lua")
dofile(commonpath .. "mod_storage.lua")
dofile(commonpath .. "chatcommands.lua")
Expand Down
66 changes: 2 additions & 64 deletions builtin/client/register.lua
Original file line number Diff line number Diff line change
@@ -1,68 +1,6 @@
core.callback_origins = {}
local builtin_shared = ...

local getinfo = debug.getinfo
debug.getinfo = nil

--- Runs given callbacks.
--
-- Note: this function is also called from C++
-- @tparam table callbacks a table with registered callbacks, like `core.registered_on_*`
-- @tparam number mode a RunCallbacksMode, as defined in src/script/common/c_internal.h
-- @param ... arguments for the callback
-- @return depends on mode
function core.run_callbacks(callbacks, mode, ...)
assert(type(callbacks) == "table")
local cb_len = #callbacks
if cb_len == 0 then
if mode == 2 or mode == 3 then
return true
elseif mode == 4 or mode == 5 then
return false
end
end
local ret
for i = 1, cb_len do
local cb_ret = callbacks[i](...)

if mode == 0 and i == 1 or mode == 1 and i == cb_len then
ret = cb_ret
elseif mode == 2 then
if not cb_ret or i == 1 then
ret = cb_ret
end
elseif mode == 3 then
if cb_ret then
return cb_ret
end
ret = cb_ret
elseif mode == 4 then
if (cb_ret and not ret) or i == 1 then
ret = cb_ret
end
elseif mode == 5 and cb_ret then
return cb_ret
end
end
return ret
end

--
-- Callback registration
--

local function make_registration()
local t = {}
local registerfunc = function(func)
t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
name = getinfo(1, "n").name or "??"
}
--local origin = core.callback_origins[func]
--print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
end
return t, registerfunc
end
local make_registration = builtin_shared.make_registration

core.registered_globalsteps, core.register_globalstep = make_registration()
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
Expand Down
6 changes: 4 additions & 2 deletions builtin/common/register.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ function builtin_shared.make_registration()
local registerfunc = function(func)
t[#t + 1] = func
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
-- may be nil or return nil
mod = core.get_current_modname and core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
end
Expand All @@ -66,7 +67,8 @@ function builtin_shared.make_registration_reverse()
local registerfunc = function(func)
table.insert(t, 1, func)
core.callback_origins[func] = {
mod = core.get_current_modname() or "??",
-- may be nil or return nil
mod = core.get_current_modname and core.get_current_modname() or "??",
name = debug.getinfo(1, "n").name or "??"
}
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ local function make_field(converter, validator, stringifier)
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name) -- for pause menu env
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))

return fs, 1.1
Expand Down Expand Up @@ -217,6 +218,8 @@ local function make_path(setting)

local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name) -- for pause menu env
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))

Expand Down Expand Up @@ -249,8 +252,11 @@ local function make_path(setting)
}
end

if PLATFORM == "Android" then
if PLATFORM == "Android" or INIT == "pause_menu" then
-- The Irrlicht file picker doesn't work on Android.
-- Access to the Irrlicht file picker isn't implemented in the pause menu.
-- We want to delete the Irrlicht file picker anyway, so any time spent on
-- that would be wasted.
make.path = make.string
make.filepath = make.string
else
Expand Down Expand Up @@ -282,6 +288,14 @@ function make.v3f(setting)
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)

fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_x")
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_y")
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name .. "_z")
-- for pause menu env
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_x")
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_y")
fs = fs .. ("field_close_on_enter[%s;false]"):format(setting.name .. "_z")

fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))

return fs, 1.4
Expand Down Expand Up @@ -428,8 +442,22 @@ local function make_noise_params(setting)
}
end

make.noise_params_2d = make_noise_params
make.noise_params_3d = make_noise_params
if INIT == "pause_menu" then
-- Making the noise parameter dialog work in the pause menu settings would
-- require porting "FSTK" (at least the dialog API) from the mainmenu formspec
-- API to the in-game formspec API.
-- There's no reason you'd want to adjust mapgen noise parameter settings
-- in-game (they only apply to new worlds), so there's no reason to implement
-- this.
local empty = function()
return { get_formspec = function() return "", 0 end }
end
make.noise_params_2d = empty
make.noise_params_3d = empty
else
make.noise_params_2d = make_noise_params
make.noise_params_3d = make_noise_params
end


return make
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "components.lua")
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM

local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "shadows_component.lua")
local component_funcs = dofile(path .. "components.lua")
local shadows_component = dofile(path .. "shadows_component.lua")

local loaded = false
local full_settings
Expand Down Expand Up @@ -531,6 +530,7 @@ local function get_formspec(dialogdata)
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
core.formspec_escape(dialogdata.query or ""), "]",
"field_enter_after_edit[search_query;true]",
"field_close_on_enter[search_query;false]", -- for pause menu env
"container[", tostring(search_width + 0.25), ", 0.25]",
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
Expand Down Expand Up @@ -671,7 +671,8 @@ local function buttonhandler(this, fields)
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
dialogdata.query = fields.search_query

if fields.back then
-- "fields.quit" is for the pause menu env
if fields.back or fields.quit then
this:delete()
return true
end
Expand Down Expand Up @@ -765,11 +766,44 @@ local function eventhandler(event)
end


function create_settings_dlg()
load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
if INIT == "mainmenu" then
function create_settings_dlg()
load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)

dlg.data.page_id = update_filtered_pages("")
dlg.data.page_id = update_filtered_pages("")

return dlg
return dlg
end

else
assert(INIT == "pause_menu")

local dialog

core.register_on_formspec_input(function(formname, fields)
if dialog and formname == "__builtin:settings" then
-- buttonhandler returning true means we should update the formspec.
-- dialog is re-checked since the buttonhandler may have closed it.
if buttonhandler(dialog, fields) and dialog then
core.show_formspec("__builtin:settings", get_formspec(dialog.data))
end
return true
end
end)

core.open_settings = function()
load()
dialog = {}
dialog.data = {}
dialog.data.page_id = update_filtered_pages("")
dialog.delete = function()
dialog = nil
-- only needed for the "fields.back" case, in the "fields.quit"
-- case it's a no-op
core.show_formspec("__builtin:settings", "")
end

core.show_formspec("__builtin:settings", get_formspec(dialog.data))
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

local path = core.get_mainmenu_path() .. DIR_DELIM .. "settings"
local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM

dofile(path .. DIR_DELIM .. "settingtypes.lua")
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
dofile(path .. "settingtypes.lua")
dofile(path .. "dlg_change_mapgen_flags.lua")
dofile(path .. "dlg_settings.lua")

-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,16 @@ function settingtypes.parse_config_file(read_all, parse_mods)
file:close()
end

if parse_mods then
-- TODO: Support game/mod settings in the pause menu too
-- Note that this will need to work different from how it's done in the
-- mainmenu:
-- * Only if in singleplayer / on local server, not on remote servers
-- * Only show settings for the active game and mods
-- (add API function to get them, can return nil if on a remote server)
-- (names are probably not enough, will need paths for uniqueness)
-- This means just making "pkgmgr.lua" work won't get you very far.

if INIT == "mainmenu" and parse_mods then
-- Parse games
local games_category_initialized = false
for _, game in ipairs(pkgmgr.games) do
Expand Down
2 changes: 2 additions & 0 deletions builtin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ elseif INIT == "client" then
dofile(scriptdir .. "client" .. DIR_DELIM .. "init.lua")
elseif INIT == "emerge" then
dofile(scriptdir .. "emerge" .. DIR_DELIM .. "init.lua")
elseif INIT == "pause_menu" then
dofile(scriptdir .. "pause_menu" .. DIR_DELIM .. "init.lua")
else
error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
end
2 changes: 1 addition & 1 deletion builtin/mainmenu/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ dofile(menupath .. DIR_DELIM .. "game_theme.lua")
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")

dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
dofile(basepath .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
Expand Down
12 changes: 12 additions & 0 deletions builtin/pause_menu/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
local scriptpath = core.get_builtin_path()
local pausepath = scriptpath.."pause_menu"..DIR_DELIM
local commonpath = scriptpath.."common"..DIR_DELIM

-- we're in-game, so no absolute paths are needed
defaulttexturedir = ""

local builtin_shared = {}

assert(loadfile(commonpath .. "register.lua"))(builtin_shared)
assert(loadfile(pausepath .. "register.lua"))(builtin_shared)
dofile(commonpath .. "settings" .. DIR_DELIM .. "init.lua")
5 changes: 5 additions & 0 deletions builtin/pause_menu/register.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
local builtin_shared = ...

local make_registration = builtin_shared.make_registration

core.registered_on_formspec_input, core.register_on_formspec_input = make_registration()
5 changes: 0 additions & 5 deletions doc/menu_lua_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,6 @@ of manually putting one, as different OSs use different delimiters. E.g.
* `spec` = `SimpleSoundSpec` (see `lua_api.md`)
* `looped` = bool
* `handle:stop()` or `core.sound_stop(handle)`
* `core.get_video_drivers()`
* get list of video drivers supported by engine (not all modes are guaranteed to work)
* returns list of available video drivers' settings name and 'friendly' display name
e.g. `{ {name="opengl", friendly_name="OpenGL"}, {name="software", friendly_name="Software Renderer"} }`
* first element of returned list is guaranteed to be the NULL driver
* `core.get_mapgen_names([include_hidden=false])` -> table of map generator algorithms
registered in the core (possible in async calls)
* `core.get_cache_path()` -> path of cache
Expand Down
3 changes: 2 additions & 1 deletion src/client/clientevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ enum ClientEventType : u8
CE_PLAYER_FORCE_MOVE,
CE_DEATHSCREEN_LEGACY,
CE_SHOW_FORMSPEC,
CE_SHOW_LOCAL_FORMSPEC,
CE_SHOW_CSM_FORMSPEC,
CE_SHOW_PAUSE_MENU_FORMSPEC,
CE_SPAWN_PARTICLE,
CE_ADD_PARTICLESPAWNER,
CE_DELETE_PARTICLESPAWNER,
Expand Down
Loading

0 comments on commit 76685b8

Please sign in to comment.