From c30c94dfaad46bede44397c26b4e5d1fb2b024c2 Mon Sep 17 00:00:00 2001 From: grorp Date: Tue, 1 Apr 2025 07:55:47 -0400 Subject: [PATCH] Add server/client annotations to settingtypes.txt and make use of them (#15756) --- builtin/common/settings/components.lua | 4 +- builtin/common/settings/dlg_settings.lua | 37 +++-- builtin/common/settings/settingtypes.lua | 93 +++++++++++-- builtin/common/settings/shadows_component.lua | 1 + builtin/settingtypes.txt | 128 ++++++++++-------- src/client/client.cpp | 11 +- src/client/client.h | 16 ++- src/client/game.cpp | 18 +-- src/gameparams.h | 5 + src/gui/guiMainMenu.h | 4 + src/script/lua_api/l_pause_menu.cpp | 9 ++ src/script/lua_api/l_pause_menu.h | 1 + 12 files changed, 231 insertions(+), 96 deletions(-) diff --git a/builtin/common/settings/components.lua b/builtin/common/settings/components.lua index aec2b8898..de7a63fee 100644 --- a/builtin/common/settings/components.lua +++ b/builtin/common/settings/components.lua @@ -447,8 +447,8 @@ if INIT == "pause_menu" then -- 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. + -- in-game (they only apply to new worlds, hidden as [world_creation]), + -- so there's no reason to implement this. local empty = function() return { get_formspec = function() return "", 0 end } end diff --git a/builtin/common/settings/dlg_settings.lua b/builtin/common/settings/dlg_settings.lua index 6f0b772af..b43d9ebdc 100644 --- a/builtin/common/settings/dlg_settings.lua +++ b/builtin/common/settings/dlg_settings.lua @@ -111,6 +111,7 @@ local function load() requires = { keyboard_mouse = true, }, + context = "client", get_formspec = function(self, avail_w) local btn_w = math.min(avail_w, 3) return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8 @@ -127,6 +128,7 @@ local function load() requires = { touchscreen = true, }, + context = "client", get_formspec = function(self, avail_w) local btn_w = math.min(avail_w, 6) return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8 @@ -173,18 +175,24 @@ local function load() table.insert(content, idx, shadows_component) idx = table.indexof(content, "enable_auto_exposure") + 1 + local setting_info = get_setting_info("enable_auto_exposure") local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)")) - note.requires = get_setting_info("enable_auto_exposure").requires + note.requires = setting_info.requires + note.context = setting_info.context table.insert(content, idx, note) idx = table.indexof(content, "enable_bloom") + 1 + setting_info = get_setting_info("enable_bloom") note = component_funcs.note(fgettext_ne("(The game will need to enable bloom as well)")) - note.requires = get_setting_info("enable_bloom").requires + note.requires = setting_info.requires + note.context = setting_info.context table.insert(content, idx, note) idx = table.indexof(content, "enable_volumetric_lighting") + 1 + setting_info = get_setting_info("enable_volumetric_lighting") note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)")) - note.requires = get_setting_info("enable_volumetric_lighting").requires + note.requires = setting_info.requires + note.context = setting_info.context table.insert(content, idx, note) end @@ -362,7 +370,18 @@ local function update_filtered_pages(query) end -local function check_requirements(name, requires) +local shown_contexts = { + common = true, + client = true, + server = INIT ~= "pause_menu" or core.is_internal_server(), + world_creation = INIT ~= "pause_menu", +} + +local function check_requirements(name, requires, context) + if context and not shown_contexts[context] then + return false + end + if requires == nil then return true end @@ -423,11 +442,11 @@ function page_has_contents(page, actual_content) elseif type(item) == "string" then local setting = get_setting_info(item) assert(setting, "Unknown setting: " .. item) - if check_requirements(setting.name, setting.requires) then + if check_requirements(setting.name, setting.requires, setting.context) then return true end elseif item.get_formspec then - if check_requirements(item.id, item.requires) then + if check_requirements(item.id, item.requires, item.context) then return true end else @@ -449,20 +468,22 @@ local function build_page_components(page) elseif item.heading then last_heading = item else - local name, requires + local name, requires, context if type(item) == "string" then local setting = get_setting_info(item) assert(setting, "Unknown setting: " .. item) name = setting.name requires = setting.requires + context = setting.context elseif item.get_formspec then name = item.id requires = item.requires + context = item.context else error("Unknown content in page: " .. dump(item)) end - if check_requirements(name, requires) then + if check_requirements(name, requires, context) then if last_heading then content[#content + 1] = last_heading last_heading = nil diff --git a/builtin/common/settings/settingtypes.lua b/builtin/common/settings/settingtypes.lua index a4dd28483..39a50e1f4 100644 --- a/builtin/common/settings/settingtypes.lua +++ b/builtin/common/settings/settingtypes.lua @@ -40,12 +40,24 @@ local CHAR_CLASSES = { FLAGS = "[%w_%-%.,]", } +local valid_contexts = {common = true, client = true, server = true, world_creation = true} + +local function check_context_annotation(context, force_context) + if force_context then + return "Context annotations are not allowed, context is always " .. force_context + end + if not valid_contexts[context] then + return "Unknown context" + end + return nil +end + local function flags_to_table(flags) return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split end -- returns error message, or nil -local function parse_setting_line(settings, line, read_all, base_level, allow_secure) +local function parse_setting_line(settings, line, read_all, base_level, allow_secure, force_context) -- strip carriage returns (CR, /r) line = line:gsub("\r", "") @@ -69,9 +81,32 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se -- category local stars, category = line:match("^%[([%*]*)([^%]]+)%]$") + local category_context + if not category then + stars, category, category_context = line:match("^%[([%*]*)([^%]]+)%] %[([^%]]+)%]$") + end if category then local category_level = stars:len() + base_level + if settings.current_context_level and + category_level <= settings.current_context_level then + -- The start of this category marks the end of the context annotation's scope. + settings.current_context_level = nil + settings.current_context = nil + end + + if category_context then + local err = check_context_annotation(category_context, force_context) + if err then + return err + end + if settings.current_context_level then + return "Category context annotations cannot be nested" + end + settings.current_context_level = category_level + settings.current_context = category_context + end + if settings.current_hide_level then if settings.current_hide_level < category_level then -- Skip this category, it's inside a hidden category. @@ -102,7 +137,8 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se end -- settings - local first_part, name, readable_name, setting_type = line:match("^" + local function make_pattern(include_context) + return "^" -- this first capture group matches the whole first part, -- so we can later strip it from the rest of the line .. "(" @@ -110,9 +146,19 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se .. CHAR_CLASSES.SPACE .. "*" .. "%(([^%)]*)%)" -- readable name .. CHAR_CLASSES.SPACE .. "*" + .. (include_context and ( + "%[([^%]]+)%]" -- context annotation + .. CHAR_CLASSES.SPACE .. "*" + ) or "") .. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type .. CHAR_CLASSES.SPACE .. "*" - .. ")") + .. ")" + end + local first_part, name, readable_name, setting_type = line:match(make_pattern(false)) + local setting_context + if not first_part then + first_part, name, readable_name, setting_context, setting_type = line:match(make_pattern(true)) + end if not first_part then return "Invalid line" @@ -122,6 +168,26 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se return "Tried to add \"secure.\" setting" end + if setting_context then + local err = check_context_annotation(setting_context, force_context) + if err then + return err + end + end + + local context + if force_context then + context = force_context + else + if setting_context then + context = setting_context + elseif settings.current_context_level then + context = settings.current_context + else + return "Missing context annotation" + end + end + local requires = {} local last_line = #current_comment > 0 and current_comment[#current_comment]:trim() if last_line and last_line:lower():sub(1, 9) == "requires:" then @@ -170,6 +236,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se min = min, max = max, requires = requires, + context = context, comment = comment, }) return @@ -193,6 +260,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se type = setting_type, default = default, requires = requires, + context = context, comment = comment, }) return @@ -245,6 +313,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se }, values = values, requires = requires, + context = context, comment = comment, noise_params = true, flags = flags_to_table("defaults,eased,absvalue") @@ -263,6 +332,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se type = "bool", default = remaining_line, requires = requires, + context = context, comment = comment, }) return @@ -290,6 +360,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se min = min, max = max, requires = requires, + context = context, comment = comment, }) return @@ -313,6 +384,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se default = default, values = values:split(",", true), requires = requires, + context = context, comment = comment, }) return @@ -331,6 +403,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se type = setting_type, default = default, requires = requires, + context = context, comment = comment, }) return @@ -361,6 +434,7 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se default = default, possible = flags_to_table(possible), requires = requires, + context = context, comment = comment, }) return @@ -369,14 +443,14 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se return "Invalid setting type \"" .. setting_type .. "\"" end -local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure) +local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure, force_context) -- store this helper variable in the table so it's easier to pass to parse_setting_line() result.current_comment = {} result.current_hide_level = nil local line = file:read("*line") while line do - local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure) + local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure, force_context) if error_msg then core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"") end @@ -411,7 +485,8 @@ function settingtypes.parse_config_file(read_all, parse_mods) -- 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 if in singleplayer / on local server, not on remote servers~~ + -- (done now: context annotations) -- * 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) @@ -441,7 +516,7 @@ function settingtypes.parse_config_file(read_all, parse_mods) type = "category", }) - parse_single_file(file, path, read_all, settings, 2, false) + parse_single_file(file, path, read_all, settings, 2, false, "server") file:close() end @@ -474,7 +549,7 @@ function settingtypes.parse_config_file(read_all, parse_mods) type = "category", }) - parse_single_file(file, path, read_all, settings, 2, false) + parse_single_file(file, path, read_all, settings, 2, false, "server") file:close() end @@ -505,7 +580,7 @@ function settingtypes.parse_config_file(read_all, parse_mods) type = "category", }) - parse_single_file(file, path, read_all, settings, 2, false) + parse_single_file(file, path, read_all, settings, 2, false, "client") file:close() end diff --git a/builtin/common/settings/shadows_component.lua b/builtin/common/settings/shadows_component.lua index 2d68f9d3d..405517058 100644 --- a/builtin/common/settings/shadows_component.lua +++ b/builtin/common/settings/shadows_component.lua @@ -84,6 +84,7 @@ return { requires = { opengl = true, }, + context = "client", get_formspec = function(self, avail_w) local labels = table.copy(shadow_levels_labels) local idx = detect_mapping_idx() diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 8604ff539..825a85b97 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2,9 +2,27 @@ # # General format: # name (Readable name) type type_args +# name (Readable name) [context] type type_args # # Note that the parts are separated by exactly one space # +# `context` (optional) is used to document where the setting is read. It can be: +# - common: Read by both client and server. +# - client: Read by the client. +# (Includes settings read by the mainmenu.) +# - server: Read by the server. +# - world_creation: Read at world creation, thus only applied to new worlds. +# (Worlds are commonly created in the mainmenu (part of the client), but +# world creation is conceptually a server-side thing...) +# If not specified, the value is inherited from the context value of the containing +# category instead. +# For the builtin/settingtypes.txt file, every setting needs to have a context defined, +# either via a category containing it or via the setting itself. In game/mod-provided +# settingtypes.txt files, context annotations are invalid. +# Note: For context annotations, it's irrelevant whether changes to a setting +# after startup/game-join will be read. A separate mechanism for declaring that +# is needed. +# # `type` can be: # - int # - string @@ -77,6 +95,8 @@ # Sections are marked by a single line in the format: [Section Name] # Sub-section are marked by adding * in front of the section name: [*Sub-section] # Sub-sub-sections have two * etc. +# A context (see above) can be specified optionally: [Section Name] [context] +# Context annotations on categories cannot be nested. # There shouldn't be too many settings per category. # # The top-level categories "Advanced", "Client and Server" and "Mapgen" are @@ -84,7 +104,7 @@ # They contain settings not intended for the "average user". -[Controls] +[Controls] [client] [*General] @@ -224,7 +244,7 @@ fixed_virtual_joystick (Fixed virtual joystick) bool false # Requires: touchscreen virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false -[Graphics and Audio] +[Graphics and Audio] [client] [*Graphics] @@ -762,13 +782,13 @@ contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3 1 [Client and Server] -[*Client] +[*Client] [client] # Save the map received by the client on disk. enable_local_map_saving (Saving map received from server) bool false # URL to the server list displayed in the Multiplayer Tab. -serverlist_url (Serverlist URL) string https://servers.luanti.org +serverlist_url (Serverlist URL) [common] string https://servers.luanti.org # If enabled, server account registration is separate from login in the UI. # If disabled, connecting to a server will automatically register a new account. @@ -778,7 +798,7 @@ enable_split_login_register (Enable split login/register) bool true # If this is empty the engine will never check for updates. update_information_url (Update information URL) string https://www.luanti.org/release_info.json -[*Server] +[*Server] [server] # Name of the player. # When running a server, a client connecting with this name is admin. @@ -806,7 +826,7 @@ server_announce (Announce server) bool false server_announce_send_players (Send player names to the server list) bool true # Announce to this serverlist. -serverlist_url (Serverlist URL) string https://servers.luanti.org +serverlist_url (Serverlist URL) [common] string https://servers.luanti.org # Message of the day displayed to players connecting. motd (Message of the day) string @@ -852,7 +872,7 @@ remote_media (Remote media) string # Requires: enable_ipv6 ipv6_server (IPv6 server) bool true -[*Server Security] +[*Server Security] [server] # New users need to input this password. default_password (Default password) string @@ -912,7 +932,7 @@ chat_message_limit_per_10sec (Chat message count limit) float 8.0 1.0 # Kick players who sent more than X messages per 10 seconds. chat_message_limit_trigger_kick (Chat message kick threshold) int 50 1 65535 -[*Server Gameplay] +[*Server Gameplay] [server] # Controls length of day/night cycle. # Examples: @@ -920,7 +940,7 @@ chat_message_limit_trigger_kick (Chat message kick threshold) int 50 1 65535 time_speed (Time speed) int 72 0 # Time of day when a new world is started, in millihours (0-23999). -world_start_time (World start time) int 6125 0 23999 +world_start_time (World start time) [world_creation] int 6125 0 23999 # Time in seconds for item entity (dropped items) to live. # Setting it to -1 disables the feature. @@ -975,7 +995,7 @@ movement_liquid_sink (Liquid sinking) float 10.0 movement_gravity (Gravity) float 9.81 -[Mapgen] +[Mapgen] [world_creation] # A chosen map seed for a new map, leave empty for random. # Will be overridden when creating a new world in the main menu. @@ -991,7 +1011,7 @@ mg_name (Mapgen name) enum v7 v7,valleys,carpathian,v5,flat,fractal,singlenode,v water_level (Water level) int 1 -31000 31000 # From how far blocks are generated for clients, stated in mapblocks (16 nodes). -max_block_generate_distance (Max block generate distance) int 10 1 32767 +max_block_generate_distance (Max block generate distance) [server] int 10 1 32767 # Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). # Only mapchunks completely within the mapgen limit are generated. @@ -1704,12 +1724,12 @@ mgvalleys_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), # Enable Lua modding support on client. # This support is experimental and API can change. -enable_client_modding (Client modding) bool false +enable_client_modding (Client modding) [client] bool false # Replaces the default main menu with a custom one. -main_menu_script (Main menu script) string +main_menu_script (Main menu script) [client] string -[**Mod Security] +[**Mod Security] [server] # Prevent mods from doing insecure things like running shell commands. secure.enable_security (Enable mod security) bool true @@ -1733,33 +1753,33 @@ secure.http_mods (HTTP mods) string # - info # - verbose # - trace -debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose,trace +debug_log_level (Debug log level) [common] enum action ,none,error,warning,action,info,verbose,trace # If the file size of debug.txt exceeds the number of megabytes specified in # this setting when it is opened, the file is moved to debug.txt.1, # deleting an older debug.txt.1 if it exists. # debug.txt is only moved if this setting is positive. -debug_log_size_max (Debug log file size threshold) int 50 1 +debug_log_size_max (Debug log file size threshold) [common] int 50 1 # Minimal level of logging to be written to chat. -chat_log_level (Chat log level) enum error ,none,error,warning,action,info,verbose,trace +chat_log_level (Chat log level) [client] enum error ,none,error,warning,action,info,verbose,trace # Handling for deprecated Lua API calls: # - none: Do not log deprecated calls # - log: mimic and log backtrace of deprecated call (default). # - error: abort on usage of deprecated call (suggested for mod developers). -deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error +deprecated_lua_api_handling (Deprecated Lua API handling) [common] enum log none,log,error # Enable random user input (only used for testing). -random_input (Random input) bool false +random_input (Random input) [client] bool false # Enable random mod loading (mainly used for testing). -random_mod_load_order (Random mod load order) bool false +random_mod_load_order (Random mod load order) [server] bool false # Enable mod channels support. -enable_mod_channels (Mod channels) bool false +enable_mod_channels (Mod channels) [server] bool false -[**Mod Profiler] +[**Mod Profiler] [server] # Load the game profiler to collect game profiling data. # Provides a /profiler command to access the compiled profile. @@ -1799,7 +1819,7 @@ instrument.builtin (Builtin) bool false # * Instrument the sampler being used to update the statistics. instrument.profiler (Profiler) bool false -[**Engine Profiler] +[**Engine Profiler] [common] # Print the engine's profiling data in regular intervals (in seconds). # 0 = disable. Useful for developers. @@ -1808,7 +1828,7 @@ profiler_print_interval (Engine profiling data print interval) int 0 0 [*Advanced] -[**Graphics] +[**Graphics] [client] # Enables debug and error-checking in the OpenGL driver. opengl_debug (OpenGL debug) bool false @@ -1912,12 +1932,12 @@ shadow_update_frames (Map shadows update frames) int 16 1 32 # Requires: enable_post_processing, enable_bloom enable_bloom_debug (Enable Bloom Debug) bool false -[**Sound] +[**Sound] [client] # Comma-separated list of AL and ALC extensions that should not be used. # Useful for testing. See al_extensions.[h,cpp] for details. sound_extensions_blacklist (Sound Extensions Blacklist) string -[**Font] +[**Font] [client] font_bold (Font bold by default) bool false @@ -1967,7 +1987,7 @@ mono_font_path_bold_italic (Bold and italic monospace font path) filepath fonts/ # This font will be used for certain languages or if the default font is unavailable. fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf -[**Lighting] +[**Lighting] [client] # Gradient of light curve at minimum light level. # Controls the contrast of the lowest light levels. @@ -1995,47 +2015,47 @@ lighting_boost_spread (Light curve boost spread) float 0.2 0.0 0.4 # Enable IPv6 support (for both client and server). # Required for IPv6 connections to work at all. -enable_ipv6 (IPv6) bool true +enable_ipv6 (IPv6) [common] bool true # Prometheus listener address. # If Luanti is compiled with ENABLE_PROMETHEUS option enabled, # enable metrics listener for Prometheus on that address. # Metrics can be fetched on http://127.0.0.1:30000/metrics -prometheus_listener_address (Prometheus listener address) string 127.0.0.1:30000 +prometheus_listener_address (Prometheus listener address) [server] string 127.0.0.1:30000 -# Maximum size of the outgoing chat queue. +# Maximum size of the client's outgoing chat queue. # 0 to disable queueing and -1 to make the queue size unlimited. -max_out_chat_queue_size (Maximum size of the outgoing chat queue) int 20 -1 32767 +max_out_chat_queue_size (Maximum size of the client's outgoing chat queue) [client] int 20 -1 32767 # Timeout for client to remove unused map data from memory, in seconds. -client_unload_unused_data_timeout (Mapblock unload timeout) float 600.0 0.0 +client_unload_unused_data_timeout (Mapblock unload timeout) [client] float 600.0 0.0 # Maximum number of mapblocks for client to be kept in memory. # Note that there is an internal dynamic minimum number of blocks that # won't be deleted, depending on the current view range. # Set to -1 for no limit. -client_mapblock_limit (Mapblock limit) int 7500 -1 2147483647 +client_mapblock_limit (Mapblock limit) [client] int 7500 -1 2147483647 # Maximum number of blocks that are simultaneously sent per client. # The maximum total count is calculated dynamically: # max_total = ceil((#clients + max_users) * per_client / 4) -max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) int 40 1 4294967295 +max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per client) [server] int 40 1 4294967295 # To reduce lag, block transfers are slowed down when a player is building something. # This determines how long they are slowed down after placing or removing a node. -full_block_send_enable_min_time_from_building (Delay in sending blocks after building) float 2.0 0.0 +full_block_send_enable_min_time_from_building (Delay in sending blocks after building) [server] float 2.0 0.0 # Maximum number of packets sent per send step in the low-level networking code. # You generally don't need to change this, however busy servers may benefit from a higher number. -max_packets_per_iteration (Max. packets per iteration) int 1024 1 65535 +max_packets_per_iteration (Max. packets per iteration) [common] int 1024 1 65535 # Compression level to use when sending mapblocks to the client. # -1 - use default compression level # 0 - least compression, fastest # 9 - best compression, slowest -map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9 +map_compression_level_net (Map Compression Level for Network Transfer) [server] int -1 -1 9 -[**Server] +[**Server] [server] # Format of player chat messages. The following strings are valid placeholders: # @name, @message, @timestamp (optional) @@ -2055,7 +2075,7 @@ kick_msg_crash (Crash message) string This server has experienced an internal er # Set this to true if your server is set up to restart automatically. ask_reconnect_on_crash (Ask to reconnect after crash) bool false -[**Server/Env Performance] +[**Server/Env Performance] [server] # Length of a server tick (the interval at which everything is generally updated), # stated in seconds. @@ -2148,7 +2168,7 @@ server_side_occlusion_culling (Server-side occlusion culling) bool true # Stated in MapBlocks (16 nodes). block_cull_optimize_distance (Block cull optimize distance) int 25 2 2047 -[**Mapgen] +[**Mapgen] [server] # Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes). # WARNING: There is no benefit, and there are several dangers, in @@ -2156,7 +2176,7 @@ block_cull_optimize_distance (Block cull optimize distance) int 25 2 2047 # Reducing this value increases cave and dungeon density. # Altering this value is for special usage, leaving it unchanged is # recommended. -chunksize (Chunk size) int 5 1 10 +chunksize (Chunk size) [world_creation] int 5 1 10 # Dump the mapgen debug information. enable_mapgen_debug_info (Mapgen debug) bool false @@ -2184,7 +2204,7 @@ emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 1 # 'on_generated'. For many users the optimum setting may be '1'. num_emerge_threads (Number of emerge threads) int 1 0 32767 -[**cURL] +[**cURL] [common] # Maximum time an interactive request (e.g. server list fetch) may take, stated in milliseconds. curl_timeout (cURL interactive timeout) int 20000 1000 2147483647 @@ -2202,48 +2222,48 @@ curl_file_download_timeout (cURL file download timeout) int 300000 5000 21474836 [**Miscellaneous] # Clickable weblinks (middle-click or Ctrl+left-click) enabled in chat console output. -clickable_chat_weblinks (Chat weblinks) bool true +clickable_chat_weblinks (Chat weblinks) [client] bool true # If enabled, invalid world data won't cause the server to shut down. # Only enable this if you know what you are doing. -ignore_world_load_errors (Ignore world errors) bool false +ignore_world_load_errors (Ignore world errors) [server] bool false # Adjust the detected display density, used for scaling UI elements. -display_density_factor (Display Density Scaling Factor) float 1 0.5 5.0 +display_density_factor (Display Density Scaling Factor) [client] float 1 0.5 5.0 # Windows systems only: Start Luanti with the command line window in the background. # Contains the same information as the file debug.txt (default name). -enable_console (Enable console window) bool false +enable_console (Enable console window) [common] bool false # Number of extra blocks that can be loaded by /clearobjects at once. # This is a trade-off between SQLite transaction overhead and # memory consumption (4096=100MB, as a rule of thumb). -max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) int 4096 0 4294967295 +max_clearobjects_extra_loaded_blocks (Max. clearobjects extra blocks) [server] int 4096 0 4294967295 # World directory (everything in the world is stored here). # Not needed if starting from the main menu. -map-dir (Map directory) path +map-dir (Map directory) [server] path # See https://www.sqlite.org/pragma.html#pragma_synchronous -sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2 +sqlite_synchronous (Synchronous SQLite) [server] enum 2 0,1,2 # Compression level to use when saving mapblocks to disk. # -1 - use default compression level # 0 - least compression, fastest # 9 - best compression, slowest -map_compression_level_disk (Map Compression Level for Disk Storage) int -1 -1 9 +map_compression_level_disk (Map Compression Level for Disk Storage) [server] int -1 -1 9 # Enable usage of remote media server (if provided by server). # Remote servers offer a significantly faster way to download media (e.g. textures) # when connecting to the server. -enable_remote_media_server (Connect to external media server) bool true +enable_remote_media_server (Connect to external media server) [client] bool true # File in client/serverlist/ that contains your favorite servers displayed in the # Multiplayer Tab. -serverlist_file (Serverlist file) string favoriteservers.json +serverlist_file (Serverlist file) [client] string favoriteservers.json -[*Gamepads] +[*Gamepads] [client] # Enable joysticks. Requires a restart to take effect enable_joysticks (Enable joysticks) bool false @@ -2266,7 +2286,7 @@ joystick_deadzone (Joystick dead zone) int 2048 0 65535 joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170.0 0.001 -[*Hide: Temporary Settings] +[*Hide: Temporary Settings] [common] # Path to texture directory. All textures are first searched from here. texture_path (Texture path) path diff --git a/src/client/client.cpp b/src/client/client.cpp index 214420de5..77853b535 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -367,8 +367,7 @@ Client::~Client() g_fontengine->clearMediaFonts(); } -void Client::connect(const Address &address, const std::string &address_name, - bool is_local_server) +void Client::connect(const Address &address, const std::string &address_name) { if (m_con) { // can't do this if the connection has entered auth phase @@ -389,7 +388,7 @@ void Client::connect(const Address &address, const std::string &address_name, m_con->Connect(address); - initLocalMapSaving(address, m_address_name, is_local_server); + initLocalMapSaving(address, m_address_name); } void Client::step(float dtime) @@ -917,11 +916,9 @@ void Client::request_media(const std::vector &file_requests) << pkt.getSize() << ")" << std::endl; } -void Client::initLocalMapSaving(const Address &address, - const std::string &hostname, - bool is_local_server) +void Client::initLocalMapSaving(const Address &address, const std::string &hostname) { - if (!g_settings->getBool("enable_local_map_saving") || is_local_server) { + if (!g_settings->getBool("enable_local_map_saving") || m_internal_server) { return; } if (m_localdb) { diff --git a/src/client/client.h b/src/client/client.h index e4cbac1f5..d9cb7c6af 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -140,8 +140,7 @@ public: bool isShutdown(); - void connect(const Address &address, const std::string &address_name, - bool is_local_server); + void connect(const Address &address, const std::string &address_name); /* Stuff that references the environment is valid only as @@ -338,8 +337,17 @@ public: u16 getProtoVersion() const { return m_proto_ver; } + // Whether the server is in "simple singleplayer mode". + // This implies "m_internal_server = true". bool m_simple_singleplayer_mode; + // Whether the server is hosted by the same Luanti instance and singletons + // like g_settings are shared between client and server. + // + // This is intentionally *not* true if we're just connecting to a localhost + // server hosted by a different Luanti instance. + bool m_internal_server; + float mediaReceiveProgress(); void drawLoadScreen(const std::wstring &text, float dtime, int percent); @@ -441,9 +449,7 @@ private: void peerAdded(con::IPeer *peer) override; void deletingPeer(con::IPeer *peer, bool timeout) override; - void initLocalMapSaving(const Address &address, - const std::string &hostname, - bool is_local_server); + void initLocalMapSaving(const Address &address, const std::string &hostname); void ReceiveAll(); diff --git a/src/client/game.cpp b/src/client/game.cpp index 87e0d4e11..5fdb35e7e 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -508,7 +508,7 @@ protected: bool init(const std::string &map_dir, const std::string &address, u16 port, const SubgameSpec &gamespec); bool initSound(); - bool createSingleplayerServer(const std::string &map_dir, + bool createServer(const std::string &map_dir, const SubgameSpec &gamespec, u16 port); void copyServerClientCache(); @@ -908,7 +908,6 @@ bool Game::startup(bool *kill, g_client_translations->clear(); - // address can change if simple_singleplayer_mode if (!init(start_data.world_spec.path, start_data.address, start_data.socket_port, start_data.game_spec)) return false; @@ -1138,7 +1137,7 @@ bool Game::init( // Create a server if not connecting to an existing one if (address.empty()) { - if (!createSingleplayerServer(map_dir, gamespec, port)) + if (!createServer(map_dir, gamespec, port)) return false; } @@ -1173,7 +1172,7 @@ bool Game::initSound() return true; } -bool Game::createSingleplayerServer(const std::string &map_dir, +bool Game::createServer(const std::string &map_dir, const SubgameSpec &gamespec, u16 port) { showOverlayMessage(N_("Creating server..."), 0, 5); @@ -1389,7 +1388,6 @@ bool Game::connectToServer(const GameStartData &start_data, { *connect_ok = false; // Let's not be overly optimistic *connection_aborted = false; - bool local_server_mode = false; const auto &address_name = start_data.address; showOverlayMessage(N_("Resolving address..."), 0, 15); @@ -1409,7 +1407,6 @@ bool Game::connectToServer(const GameStartData &start_data, } else { connect_address.setAddress(127, 0, 0, 1); } - local_server_mode = true; } } catch (ResolveError &e) { *error_message = fmtgettext("Couldn't resolve address: %s", e.what()); @@ -1455,13 +1452,13 @@ bool Game::connectToServer(const GameStartData &start_data, client->migrateModStorage(); client->m_simple_singleplayer_mode = simple_singleplayer_mode; + client->m_internal_server = !!server; /* Wait for server to accept connection */ - client->connect(connect_address, address_name, - simple_singleplayer_mode || local_server_mode); + client->connect(connect_address, address_name); try { input->clear(); @@ -1508,12 +1505,11 @@ bool Game::connectToServer(const GameStartData &start_data, } wait_time += dtime; - if (local_server_mode) { + if (server) { // never time out } else if (wait_time > GAME_FALLBACK_TIMEOUT && !did_fallback) { if (!client->hasServerReplied() && fallback_address.isValid()) { - client->connect(fallback_address, address_name, - simple_singleplayer_mode || local_server_mode); + client->connect(fallback_address, address_name); } did_fallback = true; } else if (wait_time > GAME_CONNECTION_TIMEOUT) { diff --git a/src/gameparams.h b/src/gameparams.h index 7c20cccf3..0dc7a3713 100644 --- a/src/gameparams.h +++ b/src/gameparams.h @@ -25,6 +25,7 @@ enum class ELoginRegister { }; // Information processed by main menu +// TODO: unify with MainMenuData struct GameStartData : GameParams { GameStartData() = default; @@ -33,7 +34,11 @@ struct GameStartData : GameParams std::string name; std::string password; + // If empty, we're hosting a server. + // This may or may not be in "simple singleplayer mode". std::string address; + // If true, we're hosting a server and are *not* in "simple singleplayer + // mode". bool local_server; ELoginRegister allow_login_or_register = ELoginRegister::Any; diff --git a/src/gui/guiMainMenu.h b/src/gui/guiMainMenu.h index 5a635c596..e5a5d2717 100644 --- a/src/gui/guiMainMenu.h +++ b/src/gui/guiMainMenu.h @@ -16,10 +16,13 @@ struct MainMenuDataForScript { std::string errormessage = ""; }; +// TODO: unify with GameStartData struct MainMenuData { // Client options std::string servername; std::string serverdescription; + // If empty, we're hosting a server. + // This may or may not be in "simple singleplayer mode". std::string address; std::string port; std::string name; @@ -29,6 +32,7 @@ struct MainMenuData { // Server options int selected_world = 0; + // If true, we're hosting a server and *are* in "simple singleplayer mode". bool simple_singleplayer_mode = false; // Data to be passed to the script diff --git a/src/script/lua_api/l_pause_menu.cpp b/src/script/lua_api/l_pause_menu.cpp index 4fc17766d..c2a9a81e5 100644 --- a/src/script/lua_api/l_pause_menu.cpp +++ b/src/script/lua_api/l_pause_menu.cpp @@ -5,6 +5,7 @@ #include "l_pause_menu.h" #include "gui/mainmenumanager.h" #include "lua_api/l_internal.h" +#include "client/client.h" int ModApiPauseMenu::l_show_keys_menu(lua_State *L) @@ -21,8 +22,16 @@ int ModApiPauseMenu::l_show_touchscreen_layout(lua_State *L) } +int ModApiPauseMenu::l_is_internal_server(lua_State *L) +{ + lua_pushboolean(L, getClient(L)->m_internal_server); + return 1; +} + + void ModApiPauseMenu::Initialize(lua_State *L, int top) { API_FCT(show_keys_menu); API_FCT(show_touchscreen_layout); + API_FCT(is_internal_server); } diff --git a/src/script/lua_api/l_pause_menu.h b/src/script/lua_api/l_pause_menu.h index 507c1c4b7..2d7eb62d7 100644 --- a/src/script/lua_api/l_pause_menu.h +++ b/src/script/lua_api/l_pause_menu.h @@ -11,6 +11,7 @@ class ModApiPauseMenu: public ModApiBase private: static int l_show_keys_menu(lua_State *L); static int l_show_touchscreen_layout(lua_State *L); + static int l_is_internal_server(lua_State *L); public: static void Initialize(lua_State *L, int top);