diff options
Diffstat (limited to 'builtin/mainmenu')
-rw-r--r-- | builtin/mainmenu/common.lua | 134 | ||||
-rw-r--r-- | builtin/mainmenu/dlg_contentstore.lua | 135 | ||||
-rw-r--r-- | builtin/mainmenu/dlg_create_world.lua | 266 | ||||
-rw-r--r-- | builtin/mainmenu/dlg_settings_advanced.lua | 6 | ||||
-rw-r--r-- | builtin/mainmenu/game_theme.lua (renamed from builtin/mainmenu/textures.lua) | 98 | ||||
-rw-r--r-- | builtin/mainmenu/init.lua | 12 | ||||
-rw-r--r-- | builtin/mainmenu/pkgmgr.lua | 88 | ||||
-rw-r--r-- | builtin/mainmenu/tab_about.lua (renamed from builtin/mainmenu/tab_credits.lua) | 61 | ||||
-rw-r--r-- | builtin/mainmenu/tab_content.lua | 15 | ||||
-rw-r--r-- | builtin/mainmenu/tab_local.lua | 180 | ||||
-rw-r--r-- | builtin/mainmenu/tab_online.lua | 456 | ||||
-rw-r--r-- | builtin/mainmenu/tab_settings.lua | 64 | ||||
-rw-r--r-- | builtin/mainmenu/tests/serverlistmgr_spec.lua | 1 |
13 files changed, 836 insertions, 680 deletions
diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index cd896f9ec..8db8bb8d1 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -14,14 +14,11 @@ --You should have received a copy of the GNU Lesser General Public License along --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --------------------------------------------------------------------------------- + -- Global menu data --------------------------------------------------------------------------------- menudata = {} --------------------------------------------------------------------------------- -- Local cached values --------------------------------------------------------------------------------- local min_supp_proto, max_supp_proto function common_update_cached_supp_proto() @@ -29,14 +26,12 @@ function common_update_cached_supp_proto() max_supp_proto = core.get_max_supp_proto() end common_update_cached_supp_proto() --------------------------------------------------------------------------------- + -- Menu helper functions --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- local function render_client_count(n) - if n > 99 then return '99+' - elseif n >= 0 then return tostring(n) + if n > 999 then return '99+' + elseif n >= 0 then return tostring(n) else return '?' end end @@ -50,21 +45,7 @@ local function configure_selected_world_params(idx) end end --------------------------------------------------------------------------------- -function image_column(tooltip, flagname) - return "image,tooltip=" .. core.formspec_escape(tooltip) .. "," .. - "0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," .. - "1=" .. core.formspec_escape(defaulttexturedir .. - (flagname and "server_flags_" .. flagname .. ".png" or "blank.png")) .. "," .. - "2=" .. core.formspec_escape(defaulttexturedir .. "server_ping_4.png") .. "," .. - "3=" .. core.formspec_escape(defaulttexturedir .. "server_ping_3.png") .. "," .. - "4=" .. core.formspec_escape(defaulttexturedir .. "server_ping_2.png") .. "," .. - "5=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png") -end - - --------------------------------------------------------------------------------- -function render_serverlist_row(spec, is_favorite) +function render_serverlist_row(spec) local text = "" if spec.name then text = text .. core.formspec_escape(spec.name:trim()) @@ -75,31 +56,29 @@ function render_serverlist_row(spec, is_favorite) end end - local grey_out = not is_server_protocol_compat(spec.proto_min, spec.proto_max) + local grey_out = not spec.is_compatible - local details - if is_favorite then - details = "1," - else - details = "0," - end + local details = {} - if spec.ping then - local ping = spec.ping * 1000 - if ping <= 50 then - details = details .. "2," - elseif ping <= 100 then - details = details .. "3," - elseif ping <= 250 then - details = details .. "4," + if spec.lag or spec.ping then + local lag = (spec.lag or 0) * 1000 + (spec.ping or 0) * 250 + if lag <= 125 then + table.insert(details, "1") + elseif lag <= 175 then + table.insert(details, "2") + elseif lag <= 250 then + table.insert(details, "3") else - details = details .. "5," + table.insert(details, "4") end else - details = details .. "0," + table.insert(details, "0") end - if spec.clients and spec.clients_max then + table.insert(details, ",") + + local color = (grey_out and "#aaaaaa") or ((spec.is_favorite and "#ddddaa") or "#ffffff") + if spec.clients and (spec.clients_max or 0) > 0 then local clients_percent = 100 * spec.clients / spec.clients_max -- Choose a color depending on how many clients are connected @@ -110,68 +89,59 @@ function render_serverlist_row(spec, is_favorite) elseif clients_percent <= 60 then clients_color = '#a1e587' -- 0-60%: green elseif clients_percent <= 90 then clients_color = '#ffdc97' -- 60-90%: yellow elseif clients_percent == 100 then clients_color = '#dd5b5b' -- full server: red (darker) - else clients_color = '#ffba97' -- 90-100%: orange + else clients_color = '#ffba97' -- 90-100%: orange end - details = details .. clients_color .. ',' .. - render_client_count(spec.clients) .. ',/,' .. - render_client_count(spec.clients_max) .. ',' - - elseif grey_out then - details = details .. '#aaaaaa,?,/,?,' + table.insert(details, clients_color) + table.insert(details, render_client_count(spec.clients) .. " / " .. + render_client_count(spec.clients_max)) else - details = details .. ',?,/,?,' + table.insert(details, color) + table.insert(details, "?") end if spec.creative then - details = details .. "1," + table.insert(details, "1") -- creative icon else - details = details .. "0," - end - - if spec.damage then - details = details .. "1," - else - details = details .. "0," + table.insert(details, "0") end if spec.pvp then - details = details .. "1," + table.insert(details, "2") -- pvp icon + elseif spec.damage then + table.insert(details, "1") -- heart icon else - details = details .. "0," + table.insert(details, "0") end - return details .. (grey_out and '#aaaaaa,' or ',') .. text -end + table.insert(details, color) + table.insert(details, text) --------------------------------------------------------------------------------- -os.tempfolder = function() - local temp = core.get_temp_path() - return temp .. DIR_DELIM .. "MT_" .. math.random(0, 10000) + return table.concat(details, ",") end - --------------------------------------------------------------------------------- +--------------------------------------------------------------------------------- os.tmpname = function() - local path = os.tempfolder() - io.open(path, "w"):close() - return path + error('do not use') -- instead use core.get_temp_path() end - -------------------------------------------------------------------------------- -function menu_render_worldlist() - local retval = "" + +function menu_render_worldlist(show_gameid) + local retval = {} local current_worldlist = menudata.worldlist:get_list() + local row for i, v in ipairs(current_worldlist) do - if retval ~= "" then retval = retval .. "," end - retval = retval .. core.formspec_escape(v.name) .. - " \\[" .. core.formspec_escape(v.gameid) .. "\\]" + row = v.name + if show_gameid == nil or show_gameid == true then + row = row .. " [" .. v.gameid .. "]" + end + retval[#retval+1] = core.formspec_escape(row) + end - return retval + return table.concat(retval, ",") end --------------------------------------------------------------------------------- function menu_handle_key_up_down(fields, textlist, settingname) local oldidx, newidx = core.get_textlist_index(textlist), 1 if fields.key_up or fields.key_down then @@ -188,7 +158,6 @@ function menu_handle_key_up_down(fields, textlist, settingname) return false end --------------------------------------------------------------------------------- function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency) local textlines = core.wrap_text(text, textlen, true) local retval = "textlist[" .. xpos .. "," .. ypos .. ";" .. width .. @@ -206,7 +175,6 @@ function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transp return retval end --------------------------------------------------------------------------------- function is_server_protocol_compat(server_proto_min, server_proto_max) if (not server_proto_min) or (not server_proto_max) then -- There is no info. Assume the best and act as if we would be compatible. @@ -214,7 +182,7 @@ function is_server_protocol_compat(server_proto_min, server_proto_max) end return min_supp_proto <= server_proto_max and max_supp_proto >= server_proto_min end --------------------------------------------------------------------------------- + function is_server_protocol_compat_or_error(server_proto_min, server_proto_max) if not is_server_protocol_compat(server_proto_min, server_proto_max) then local server_prot_ver_info, client_prot_ver_info @@ -242,7 +210,7 @@ function is_server_protocol_compat_or_error(server_proto_min, server_proto_max) return true end --------------------------------------------------------------------------------- + function menu_worldmt(selected, setting, value) local world = menudata.worldlist:get_list()[selected] if world then diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua index b0736a4fd..9db67cf57 100644 --- a/builtin/mainmenu/dlg_contentstore.lua +++ b/builtin/mainmenu/dlg_contentstore.lua @@ -57,34 +57,77 @@ local filter_types_type = { "txp", } +local REASON_NEW = "new" +local REASON_UPDATE = "update" +local REASON_DEPENDENCY = "dependency" -local function download_package(param) - if core.download_file(param.package.url, param.filename) then + +-- encodes for use as URL parameter or path component +local function urlencode(str) + return str:gsub("[^%a%d()._~-]", function(char) + return string.format("%%%02X", string.byte(char)) + end) +end +assert(urlencode("sample text?") == "sample%20text%3F") + + +local function get_download_url(package, reason) + local base_url = core.settings:get("contentdb_url") + local ret = base_url .. ("/packages/%s/releases/%d/download/"):format( + package.url_part, package.release) + if reason then + ret = ret .. "?reason=" .. reason + end + return ret +end + + +local function download_and_extract(param) + local package = param.package + + local filename = core.get_temp_path(true) + if filename == "" or not core.download_file(param.url, filename) then + core.log("error", "Downloading " .. dump(param.url) .. " failed") return { - filename = param.filename, - successful = true, + msg = fgettext("Failed to download $1", package.name) } + end + + local tempfolder = core.get_temp_path() + if tempfolder ~= "" then + tempfolder = tempfolder .. DIR_DELIM .. "MT_" .. math.random(1, 1024000) + if not core.extract_zip(filename, tempfolder) then + tempfolder = nil + end else - core.log("error", "downloading " .. dump(param.package.url) .. " failed") + tempfolder = nil + end + os.remove(filename) + if not tempfolder then return { - successful = false, + msg = fgettext("Install: Unsupported file type or broken archive"), } end + + return { + path = tempfolder + } end -local function start_install(package) +local function start_install(package, reason) local params = { package = package, - filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip", + url = get_download_url(package, reason), } number_downloading = number_downloading + 1 local function callback(result) - if result.successful then - local path, msg = pkgmgr.install(package.type, - result.filename, package.name, - package.path) + if result.msg then + gamedata.errormessage = result.msg + else + local path, msg = pkgmgr.install_dir(package.type, result.path, package.name, package.path) + core.delete_dir(result.path) if not path then gamedata.errormessage = msg else @@ -122,9 +165,6 @@ local function start_install(package) conf:write() end end - os.remove(result.filename) - else - gamedata.errormessage = fgettext("Failed to download $1", package.name) end package.downloading = false @@ -135,7 +175,7 @@ local function start_install(package) if next then table.remove(download_queue, 1) - start_install(next) + start_install(next.package, next.reason) end ui.update() @@ -144,19 +184,19 @@ local function start_install(package) package.queued = false package.downloading = true - if not core.handle_async(download_package, params, callback) then + if not core.handle_async(download_and_extract, params, callback) then core.log("error", "ERROR: async event failed") gamedata.errormessage = fgettext("Failed to download $1", package.name) return end end -local function queue_download(package) +local function queue_download(package, reason) local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads")) if number_downloading < max_concurrent_downloads then - start_install(package) + start_install(package, reason) else - table.insert(download_queue, package) + table.insert(download_queue, { package = package, reason = reason }) package.queued = true end end @@ -169,7 +209,7 @@ local function get_raw_dependencies(package) local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s" local version = core.get_version() local base_url = core.settings:get("contentdb_url") - local url = base_url .. url_fmt:format(package.id, core.get_max_supp_proto(), version.string) + local url = base_url .. url_fmt:format(package.url_part, core.get_max_supp_proto(), urlencode(version.string)) local response = http.fetch_sync({ url = url }) if not response.succeeded then @@ -407,12 +447,12 @@ function install_dialog.handle_submit(this, fields) end if fields.install_all then - queue_download(install_dialog.package) + queue_download(install_dialog.package, REASON_NEW) if install_dialog.will_install_deps then for _, dep in pairs(install_dialog.dependencies) do if not dep.is_optional and not dep.installed and dep.package then - queue_download(dep.package) + queue_download(dep.package, REASON_DEPENDENCY) end end end @@ -544,33 +584,43 @@ function store.load() local base_url = core.settings:get("contentdb_url") local url = base_url .. "/api/packages/?type=mod&type=game&type=txp&protocol_version=" .. - core.get_max_supp_proto() .. "&engine_version=" .. version.string + core.get_max_supp_proto() .. "&engine_version=" .. urlencode(version.string) for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do item = item:trim() if item ~= "" then - url = url .. "&hide=" .. item + url = url .. "&hide=" .. urlencode(item) end end - local timeout = tonumber(core.settings:get("curl_file_download_timeout")) - local response = http.fetch_sync({ url = url, timeout = timeout }) + local response = http.fetch_sync({ url = url }) if not response.succeeded then return end store.packages_full = core.parse_json(response.data) or {} + store.aliases = {} for _, package in pairs(store.packages_full) do - package.url = base_url .. "/packages/" .. - package.author .. "/" .. package.name .. - "/releases/" .. package.release .. "/download/" - local name_len = #package.name + -- This must match what store.update_paths() does! + package.id = package.author:lower() .. "/" if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then - package.id = package.author:lower() .. "/" .. package.name:sub(1, name_len - 5) + package.id = package.id .. package.name:sub(1, name_len - 5) else - package.id = package.author:lower() .. "/" .. package.name + package.id = package.id .. package.name + end + + package.url_part = urlencode(package.author) .. "/" .. urlencode(package.name) + + if package.aliases then + for _, alias in ipairs(package.aliases) do + -- We currently don't support name changing + local suffix = "/" .. package.name + if alias:sub(-#suffix) == suffix then + store.aliases[alias:lower()] = package.id + end + end end end @@ -584,7 +634,8 @@ function store.update_paths() pkgmgr.refresh_globals() for _, mod in pairs(pkgmgr.global_mods:get_list()) do if mod.author and mod.release > 0 then - mod_hash[mod.author:lower() .. "/" .. mod.name] = mod + local id = mod.author:lower() .. "/" .. mod.name + mod_hash[store.aliases[id] or id] = mod end end @@ -592,14 +643,16 @@ function store.update_paths() pkgmgr.update_gamelist() for _, game in pairs(pkgmgr.games) do if game.author ~= "" and game.release > 0 then - game_hash[game.author:lower() .. "/" .. game.id] = game + local id = game.author:lower() .. "/" .. game.id + game_hash[store.aliases[id] or id] = game end end local txp_hash = {} for _, txp in pairs(pkgmgr.get_texture_packs()) do if txp.author and txp.release > 0 then - txp_hash[txp.author:lower() .. "/" .. txp.name] = txp + local id = txp.author:lower() .. "/" .. txp.name + txp_hash[store.aliases[id] or id] = txp end end @@ -915,7 +968,7 @@ function store.handle_submit(this, fields) local package = store.packages_full[i] if package.path and package.installed_release < package.release and not (package.downloading or package.queued) then - queue_download(package) + queue_download(package, REASON_UPDATE) end end return true @@ -948,7 +1001,7 @@ function store.handle_submit(this, fields) this:hide() dlg:show() else - queue_download(package) + queue_download(package, package.path and REASON_UPDATE or REASON_NEW) end end @@ -973,9 +1026,9 @@ function store.handle_submit(this, fields) end if fields["view_" .. i] then - local url = ("%s/packages/%s/%s?protocol_version=%d"):format( - core.settings:get("contentdb_url"), - package.author, package.name, core.get_max_supp_proto()) + local url = ("%s/packages/%s?protocol_version=%d"):format( + core.settings:get("contentdb_url"), package.url_part, + core.get_max_supp_proto()) core.open_url(url) return true end diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua index 1938747fe..8d1509f33 100644 --- a/builtin/mainmenu/dlg_create_world.lua +++ b/builtin/mainmenu/dlg_create_world.lua @@ -15,7 +15,8 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -local worldname = "" +-- cf. tab_local, the gamebar already provides game selection so we hide the list from here +local hide_gamelist = PLATFORM ~= "Android" local function table_to_flags(ftable) -- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves" @@ -31,9 +32,8 @@ local function strflag(flags, flag) return (flags[flag] == true) and "true" or "false" end -local cb_caverns = { "caverns", fgettext("Caverns"), "caverns", +local cb_caverns = { "caverns", fgettext("Caverns"), fgettext("Very large caverns deep in the underground") } -local tt_sea_rivers = fgettext("Sea level rivers") local flag_checkboxes = { v5 = { @@ -41,39 +41,38 @@ local flag_checkboxes = { }, v7 = { cb_caverns, - { "ridges", fgettext("Rivers"), "ridges", tt_sea_rivers }, - { "mountains", fgettext("Mountains"), "mountains" }, - { "floatlands", fgettext("Floatlands (experimental)"), "floatlands", + { "ridges", fgettext("Rivers"), fgettext("Sea level rivers") }, + { "mountains", fgettext("Mountains") }, + { "floatlands", fgettext("Floatlands (experimental)"), fgettext("Floating landmasses in the sky") }, }, carpathian = { cb_caverns, - { "rivers", fgettext("Rivers"), "rivers", tt_sea_rivers }, + { "rivers", fgettext("Rivers"), fgettext("Sea level rivers") }, }, valleys = { - { "altitude-chill", fgettext("Altitude chill"), "altitude_chill", + { "altitude_chill", fgettext("Altitude chill"), fgettext("Reduces heat with altitude") }, - { "altitude-dry", fgettext("Altitude dry"), "altitude_dry", + { "altitude_dry", fgettext("Altitude dry"), fgettext("Reduces humidity with altitude") }, - { "humid-rivers", fgettext("Humid rivers"), "humid_rivers", + { "humid_rivers", fgettext("Humid rivers"), fgettext("Increases humidity around rivers") }, - { "vary-river-depth", fgettext("Vary river depth"), "vary_river_depth", + { "vary_river_depth", fgettext("Vary river depth"), fgettext("Low humidity and high heat causes shallow or dry rivers") }, }, flat = { cb_caverns, - { "hills", fgettext("Hills"), "hills" }, - { "lakes", fgettext("Lakes"), "lakes" }, + { "hills", fgettext("Hills") }, + { "lakes", fgettext("Lakes") }, }, fractal = { - { "terrain", fgettext("Additional terrain"), "terrain", + { "terrain", fgettext("Additional terrain"), fgettext("Generate non-fractal terrain: Oceans and underground") }, }, v6 = { - { "trees", fgettext("Trees and jungle grass"), "trees" }, - { "flat", fgettext("Flat terrain"), "flat" }, - { "mudflow", fgettext("Mud flow"), "mudflow", - fgettext("Terrain surface erosion") }, + { "trees", fgettext("Trees and jungle grass") }, + { "flat", fgettext("Flat terrain") }, + { "mudflow", fgettext("Mud flow"), fgettext("Terrain surface erosion") }, -- Biome settings are in mgv6_biomes below }, } @@ -105,38 +104,26 @@ local function create_world_formspec(dialogdata) "button[4.75,2.5;3,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" end + local current_mg = dialogdata.mg local mapgens = core.get_mapgen_names() - local current_seed = core.settings:get("fixed_map_seed") or "" - local current_mg = core.settings:get("mg_name") local gameid = core.settings:get("menu_last_game") - local flags = { - main = core.settings:get_flags("mg_flags"), - v5 = core.settings:get_flags("mgv5_spflags"), - v6 = core.settings:get_flags("mgv6_spflags"), - v7 = core.settings:get_flags("mgv7_spflags"), - fractal = core.settings:get_flags("mgfractal_spflags"), - carpathian = core.settings:get_flags("mgcarpathian_spflags"), - valleys = core.settings:get_flags("mgvalleys_spflags"), - flat = core.settings:get_flags("mgflat_spflags"), - } - - local gameidx = 0 - if gameid ~= nil then - local _ - _, gameidx = pkgmgr.find_by_gameid(gameid) + local flags = dialogdata.flags - if gameidx == nil then - gameidx = 0 - end + local game, gameidx = pkgmgr.find_by_gameid(gameid) + if game == nil and hide_gamelist then + -- should never happen but just pick the first game + game = pkgmgr.get_game(1) + gameidx = 1 + core.settings:set("menu_last_game", game.id) + elseif game == nil then + gameidx = 0 end - local game_by_gameidx = core.get_game(gameidx) local disallowed_mapgen_settings = {} - if game_by_gameidx ~= nil then - local gamepath = game_by_gameidx.path - local gameconfig = Settings(gamepath.."/game.conf") + if game ~= nil then + local gameconfig = Settings(game.path.."/game.conf") local allowed_mapgens = (gameconfig:get("allowed_mapgens") or ""):split() for key, value in pairs(allowed_mapgens) do @@ -156,7 +143,7 @@ local function create_world_formspec(dialogdata) end end - if disallowed_mapgens then + if #disallowed_mapgens > 0 then for i = #mapgens, 1, -1 do if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then table.remove(mapgens, i) @@ -172,23 +159,29 @@ local function create_world_formspec(dialogdata) local mglist = "" local selindex - local i = 1 - local first_mg - for k,v in pairs(mapgens) do - if not first_mg then - first_mg = v + do -- build the list of mapgens + local i = 1 + local first_mg + for k, v in pairs(mapgens) do + if not first_mg then + first_mg = v + end + if current_mg == v then + selindex = i + end + i = i + 1 + mglist = mglist .. core.formspec_escape(v) .. "," end - if current_mg == v then - selindex = i + if not selindex then + selindex = 1 + current_mg = first_mg end - i = i + 1 - mglist = mglist .. v .. "," - end - if not selindex then - selindex = 1 - current_mg = first_mg + mglist = mglist:sub(1, -2) end - mglist = mglist:sub(1, -2) + + -- The logic of the flag element IDs is as follows: + -- "flag_main_foo-bar-baz" controls dialogdata.flags["main"]["foo_bar_baz"] + -- see the buttonhandler for the implementation of this local mg_main_flags = function(mapgen, y) if mapgen == "singlenode" then @@ -198,11 +191,11 @@ local function create_world_formspec(dialogdata) return "", y end - local form = "checkbox[0," .. y .. ";flag_mg_caves;" .. + local form = "checkbox[0," .. y .. ";flag_main_caves;" .. fgettext("Caves") .. ";"..strflag(flags.main, "caves").."]" y = y + 0.5 - form = form .. "checkbox[0,"..y..";flag_mg_dungeons;" .. + form = form .. "checkbox[0,"..y..";flag_main_dungeons;" .. fgettext("Dungeons") .. ";"..strflag(flags.main, "dungeons").."]" y = y + 0.5 @@ -213,7 +206,7 @@ local function create_world_formspec(dialogdata) else d_tt = fgettext("Structures appearing on the terrain, typically trees and plants") end - form = form .. "checkbox[0,"..y..";flag_mg_decorations;" .. + form = form .. "checkbox[0,"..y..";flag_main_decorations;" .. d_name .. ";" .. strflag(flags.main, "decorations").."]" .. "tooltip[flag_mg_decorations;" .. @@ -221,7 +214,7 @@ local function create_world_formspec(dialogdata) "]" y = y + 0.5 - form = form .. "tooltip[flag_mg_caves;" .. + form = form .. "tooltip[flag_main_caves;" .. fgettext("Network of tunnels and caves") .. "]" return form, y @@ -235,13 +228,13 @@ local function create_world_formspec(dialogdata) return "", y end local form = "" - for _,tab in pairs(flag_checkboxes[mapgen]) do - local id = "flag_mg"..mapgen.."_"..tab[1] + for _, tab in pairs(flag_checkboxes[mapgen]) do + local id = "flag_"..mapgen.."_"..tab[1]:gsub("_", "-") form = form .. ("checkbox[0,%f;%s;%s;%s]"): - format(y, id, tab[2], strflag(flags[mapgen], tab[3])) + format(y, id, tab[2], strflag(flags[mapgen], tab[1])) - if tab[4] then - form = form .. "tooltip["..id..";"..tab[4].."]" + if tab[3] then + form = form .. "tooltip["..id..";"..tab[3].."]" end y = y + 0.5 end @@ -277,16 +270,14 @@ local function create_world_formspec(dialogdata) -- biomeblend y = y + 0.55 - form = form .. "checkbox[0,"..y..";flag_mgv6_biomeblend;" .. + form = form .. "checkbox[0,"..y..";flag_v6_biomeblend;" .. fgettext("Biome blending") .. ";"..strflag(flags.v6, "biomeblend").."]" .. - "tooltip[flag_mgv6_biomeblend;" .. + "tooltip[flag_v6_biomeblend;" .. fgettext("Smooth transition between biomes") .. "]" return form, y end - current_seed = core.formspec_escape(current_seed) - local y_start = 0.0 local y = y_start local str_flags, str_spflags @@ -323,21 +314,27 @@ local function create_world_formspec(dialogdata) "container[0,0]".. "field[0.3,0.6;6,0.5;te_world_name;" .. fgettext("World name") .. - ";" .. core.formspec_escape(worldname) .. "]" .. + ";" .. core.formspec_escape(dialogdata.worldname) .. "]" .. + "set_focus[te_world_name;false]" .. "field[0.3,1.7;6,0.5;te_seed;" .. fgettext("Seed") .. - ";".. current_seed .. "]" .. + ";".. core.formspec_escape(dialogdata.seed) .. "]" .. "label[0,2;" .. fgettext("Mapgen") .. "]".. - "dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" .. + "dropdown[0,2.5;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" + + if not hide_gamelist or devtest_only ~= "" then + retval = retval .. + "label[0,3.35;" .. fgettext("Game") .. "]".. + "textlist[0,3.85;5.8,"..gamelist_height..";games;" .. + pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" .. + "container[0,4.5]" .. + devtest_only .. + "container_end[]" + end - "label[0,3.35;" .. fgettext("Game") .. "]".. - "textlist[0,3.85;5.8,"..gamelist_height..";games;" .. - pkgmgr.gamelist() .. ";" .. gameidx .. ";false]" .. - "container[0,4.5]" .. - devtest_only .. - "container_end[]" .. + retval = retval .. "container_end[]" .. -- Right side @@ -360,9 +357,20 @@ local function create_world_buttonhandler(this, fields) fields["key_enter"] then local worldname = fields["te_world_name"] - local gameindex = core.get_textlist_index("games") + local game, gameindex + if hide_gamelist then + game, gameindex = pkgmgr.find_by_gameid(core.settings:get("menu_last_game")) + else + gameindex = core.get_textlist_index("games") + game = pkgmgr.get_game(gameindex) + end - if gameindex ~= nil then + local message + if game == nil then + message = fgettext("No game selected") + end + + if message == nil then -- For unnamed worlds use the generated name 'world<number>', -- where the number increments: it is set to 1 larger than the largest -- generated name number found. @@ -377,36 +385,48 @@ local function create_world_buttonhandler(this, fields) worldname = "world" .. worldnum_max + 1 end - core.settings:set("fixed_map_seed", fields["te_seed"]) - - local message - if not menudata.worldlist:uid_exists_raw(worldname) then - core.settings:set("mg_name",fields["dd_mapgen"]) - message = core.create_world(worldname,gameindex) - else + if menudata.worldlist:uid_exists_raw(worldname) then message = fgettext("A world named \"$1\" already exists", worldname) end + end - if message ~= nil then - gamedata.errormessage = message - else - core.settings:set("menu_last_game",pkgmgr.games[gameindex].id) - if this.data.update_worldlist_filter then - menudata.worldlist:set_filtercriteria(pkgmgr.games[gameindex].id) - mm_texture.update("singleplayer", pkgmgr.games[gameindex].id) - end - menudata.worldlist:refresh() - core.settings:set("mainmenu_last_selected_world", - menudata.worldlist:raw_index_by_uid(worldname)) + if message == nil then + this.data.seed = fields["te_seed"] + this.data.mg = fields["dd_mapgen"] + + -- actual names as used by engine + local settings = { + fixed_map_seed = this.data.seed, + mg_name = this.data.mg, + mg_flags = table_to_flags(this.data.flags.main), + mgv5_spflags = table_to_flags(this.data.flags.v5), + mgv6_spflags = table_to_flags(this.data.flags.v6), + mgv7_spflags = table_to_flags(this.data.flags.v7), + mgfractal_spflags = table_to_flags(this.data.flags.fractal), + mgcarpathian_spflags = table_to_flags(this.data.flags.carpathian), + mgvalleys_spflags = table_to_flags(this.data.flags.valleys), + mgflat_spflags = table_to_flags(this.data.flags.flat), + } + message = core.create_world(worldname, gameindex, settings) + end + + if message == nil then + core.settings:set("menu_last_game", game.id) + if this.data.update_worldlist_filter then + menudata.worldlist:set_filtercriteria(game.id) end - else - gamedata.errormessage = fgettext("No game selected") + menudata.worldlist:refresh() + core.settings:set("mainmenu_last_selected_world", + menudata.worldlist:raw_index_by_uid(worldname)) end + + gamedata.errormessage = message this:delete() return true end - worldname = fields.te_world_name + this.data.worldname = fields["te_world_name"] + this.data.seed = fields["te_seed"] if fields["games"] then local gameindex = core.get_textlist_index("games") @@ -417,22 +437,11 @@ local function create_world_buttonhandler(this, fields) for k,v in pairs(fields) do local split = string.split(k, "_", nil, 3) if split and split[1] == "flag" then - local setting - if split[2] == "mg" then - setting = "mg_flags" - else - setting = split[2].."_spflags" - end -- We replaced the underscore of flag names with a dash. local flag = string.gsub(split[3], "-", "_") - local ftable = core.settings:get_flags(setting) - if v == "true" then - ftable[flag] = true - else - ftable[flag] = false - end - local flags = table_to_flags(ftable) - core.settings:set(setting, flags) + local ftable = this.data.flags[split[2]] + assert(ftable) + ftable[flag] = v == "true" return true end end @@ -446,18 +455,16 @@ local function create_world_buttonhandler(this, fields) local entry = core.formspec_escape(fields["mgv6_biomes"]) for b=1, #mgv6_biomes do if entry == mgv6_biomes[b][1] then - local ftable = core.settings:get_flags("mgv6_spflags") + local ftable = this.data.flags.v6 ftable.jungles = mgv6_biomes[b][2].jungles ftable.snowbiomes = mgv6_biomes[b][2].snowbiomes - local flags = table_to_flags(ftable) - core.settings:set("mgv6_spflags", flags) return true end end end if fields["dd_mapgen"] then - core.settings:set("mg_name", fields["dd_mapgen"]) + this.data.mg = fields["dd_mapgen"] return true end @@ -466,12 +473,27 @@ end function create_create_world_dlg(update_worldlistfilter) - worldname = "" local retval = dialog_create("sp_create_world", create_world_formspec, create_world_buttonhandler, nil) - retval.update_worldlist_filter = update_worldlistfilter + retval.data = { + update_worldlist_filter = update_worldlistfilter, + worldname = "", + -- settings the world is created with: + seed = core.settings:get("fixed_map_seed") or "", + mg = core.settings:get("mg_name"), + flags = { + main = core.settings:get_flags("mg_flags"), + v5 = core.settings:get_flags("mgv5_spflags"), + v6 = core.settings:get_flags("mgv6_spflags"), + v7 = core.settings:get_flags("mgv7_spflags"), + fractal = core.settings:get_flags("mgfractal_spflags"), + carpathian = core.settings:get_flags("mgcarpathian_spflags"), + valleys = core.settings:get_flags("mgvalleys_spflags"), + flat = core.settings:get_flags("mgflat_spflags"), + } + } return retval end diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index c16e4aad0..06fd32d84 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -31,6 +31,10 @@ end -- returns error message, or nil local function parse_setting_line(settings, line, read_all, base_level, allow_secure) + + -- strip carriage returns (CR, /r) + line = line:gsub("\r", "") + -- comment local comment = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$") if comment then @@ -620,7 +624,7 @@ local function create_change_setting_formspec(dialogdata) -- Third row add_field(0.3, "te_octaves", fgettext("Octaves"), t[7]) - add_field(3.6, "te_persist", fgettext("Persistance"), t[8]) + add_field(3.6, "te_persist", fgettext("Persistence"), t[8]) add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9]) height = height + 1.1 diff --git a/builtin/mainmenu/textures.lua b/builtin/mainmenu/game_theme.lua index a3acbbdec..89e1b66c8 100644 --- a/builtin/mainmenu/textures.lua +++ b/builtin/mainmenu/game_theme.lua @@ -16,23 +16,25 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -mm_texture = {} +mm_game_theme = {} -------------------------------------------------------------------------------- -function mm_texture.init() - mm_texture.defaulttexturedir = core.get_texturepath() .. DIR_DELIM .. "base" .. +function mm_game_theme.init() + mm_game_theme.defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM - mm_texture.basetexturedir = mm_texture.defaulttexturedir + mm_game_theme.basetexturedir = mm_game_theme.defaulttexturedir - mm_texture.texturepack = core.settings:get("texture_path") + mm_game_theme.texturepack = core.settings:get("texture_path") - mm_texture.gameid = nil + mm_game_theme.gameid = nil + + mm_game_theme.music_handle = nil end -------------------------------------------------------------------------------- -function mm_texture.update(tab,gamedetails) +function mm_game_theme.update(tab,gamedetails) if tab ~= "singleplayer" then - mm_texture.reset() + mm_game_theme.reset() return end @@ -40,50 +42,54 @@ function mm_texture.update(tab,gamedetails) return end - mm_texture.update_game(gamedetails) + mm_game_theme.update_game(gamedetails) end -------------------------------------------------------------------------------- -function mm_texture.reset() - mm_texture.gameid = nil +function mm_game_theme.reset() + mm_game_theme.gameid = nil local have_bg = false - local have_overlay = mm_texture.set_generic("overlay") + local have_overlay = mm_game_theme.set_generic("overlay") if not have_overlay then - have_bg = mm_texture.set_generic("background") + have_bg = mm_game_theme.set_generic("background") end - mm_texture.clear("header") - mm_texture.clear("footer") + mm_game_theme.clear("header") + mm_game_theme.clear("footer") core.set_clouds(false) - mm_texture.set_generic("footer") - mm_texture.set_generic("header") + mm_game_theme.set_generic("footer") + mm_game_theme.set_generic("header") if not have_bg then if core.settings:get_bool("menu_clouds") then core.set_clouds(true) else - mm_texture.set_dirt_bg() + mm_game_theme.set_dirt_bg() end end + + if mm_game_theme.music_handle ~= nil then + core.sound_stop(mm_game_theme.music_handle) + end end -------------------------------------------------------------------------------- -function mm_texture.update_game(gamedetails) - if mm_texture.gameid == gamedetails.id then +function mm_game_theme.update_game(gamedetails) + if mm_game_theme.gameid == gamedetails.id then return end local have_bg = false - local have_overlay = mm_texture.set_game("overlay",gamedetails) + local have_overlay = mm_game_theme.set_game("overlay",gamedetails) if not have_overlay then - have_bg = mm_texture.set_game("background",gamedetails) + have_bg = mm_game_theme.set_game("background",gamedetails) end - mm_texture.clear("header") - mm_texture.clear("footer") + mm_game_theme.clear("header") + mm_game_theme.clear("footer") core.set_clouds(false) if not have_bg then @@ -91,34 +97,34 @@ function mm_texture.update_game(gamedetails) if core.settings:get_bool("menu_clouds") then core.set_clouds(true) else - mm_texture.set_dirt_bg() + mm_game_theme.set_dirt_bg() end end - mm_texture.set_game("footer",gamedetails) - mm_texture.set_game("header",gamedetails) + mm_game_theme.set_game("footer",gamedetails) + mm_game_theme.set_game("header",gamedetails) - mm_texture.gameid = gamedetails.id + mm_game_theme.gameid = gamedetails.id end -------------------------------------------------------------------------------- -function mm_texture.clear(identifier) +function mm_game_theme.clear(identifier) core.set_background(identifier,"") end -------------------------------------------------------------------------------- -function mm_texture.set_generic(identifier) +function mm_game_theme.set_generic(identifier) --try texture pack first - if mm_texture.texturepack ~= nil then - local path = mm_texture.texturepack .. DIR_DELIM .."menu_" .. + if mm_game_theme.texturepack ~= nil then + local path = mm_game_theme.texturepack .. DIR_DELIM .."menu_" .. identifier .. ".png" if core.set_background(identifier,path) then return true end end - if mm_texture.defaulttexturedir ~= nil then - local path = mm_texture.defaulttexturedir .. DIR_DELIM .."menu_" .. + if mm_game_theme.defaulttexturedir ~= nil then + local path = mm_game_theme.defaulttexturedir .. DIR_DELIM .."menu_" .. identifier .. ".png" if core.set_background(identifier,path) then return true @@ -129,14 +135,16 @@ function mm_texture.set_generic(identifier) end -------------------------------------------------------------------------------- -function mm_texture.set_game(identifier, gamedetails) +function mm_game_theme.set_game(identifier, gamedetails) if gamedetails == nil then return false end - if mm_texture.texturepack ~= nil then - local path = mm_texture.texturepack .. DIR_DELIM .. + mm_game_theme.set_music(gamedetails) + + if mm_game_theme.texturepack ~= nil then + local path = mm_game_theme.texturepack .. DIR_DELIM .. gamedetails.id .. "_menu_" .. identifier .. ".png" if core.set_background(identifier, path) then return true @@ -171,9 +179,10 @@ function mm_texture.set_game(identifier, gamedetails) return false end -function mm_texture.set_dirt_bg() - if mm_texture.texturepack ~= nil then - local path = mm_texture.texturepack .. DIR_DELIM .."default_dirt.png" +-------------------------------------------------------------------------------- +function mm_game_theme.set_dirt_bg() + if mm_game_theme.texturepack ~= nil then + local path = mm_game_theme.texturepack .. DIR_DELIM .."default_dirt.png" if core.set_background("background", path, true, 128) then return true end @@ -183,3 +192,12 @@ function mm_texture.set_dirt_bg() local minimalpath = defaulttexturedir .. "menu_bg.png" core.set_background("background", minimalpath, true, 128) end + +-------------------------------------------------------------------------------- +function mm_game_theme.set_music(gamedetails) + if mm_game_theme.music_handle ~= nil then + core.sound_stop(mm_game_theme.music_handle) + end + local music_path = gamedetails.path .. DIR_DELIM .. "menu" .. DIR_DELIM .. "theme" + mm_game_theme.music_handle = core.sound_play(music_path, true) +end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 45089c7c9..8e716c2eb 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -35,7 +35,7 @@ dofile(menupath .. DIR_DELIM .. "async_event.lua") dofile(menupath .. DIR_DELIM .. "common.lua") dofile(menupath .. DIR_DELIM .. "pkgmgr.lua") dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua") -dofile(menupath .. DIR_DELIM .. "textures.lua") +dofile(menupath .. DIR_DELIM .. "game_theme.lua") dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua") @@ -49,7 +49,7 @@ local tabs = {} tabs.settings = dofile(menupath .. DIR_DELIM .. "tab_settings.lua") tabs.content = dofile(menupath .. DIR_DELIM .. "tab_content.lua") -tabs.credits = dofile(menupath .. DIR_DELIM .. "tab_credits.lua") +tabs.about = dofile(menupath .. DIR_DELIM .. "tab_about.lua") tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua") tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua") @@ -87,7 +87,7 @@ local function init_globals() core.settings:set("menu_last_game", default_game) end - mm_texture.init() + mm_game_theme.init() -- Create main tabview local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0}) @@ -98,7 +98,7 @@ local function init_globals() tv_main:add(tabs.content) tv_main:add(tabs.settings) - tv_main:add(tabs.credits) + tv_main:add(tabs.about) tv_main:set_global_event_handler(main_event_handler) tv_main:set_fixed_size(false) @@ -113,7 +113,7 @@ local function init_globals() if tv_main.current_tab == "local" then local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game")) if game == nil then - mm_texture.reset() + mm_game_theme.reset() end end @@ -121,8 +121,6 @@ local function init_globals() tv_main:show() ui.update() - - core.sound_play("main_menu", true) end init_globals() diff --git a/builtin/mainmenu/pkgmgr.lua b/builtin/mainmenu/pkgmgr.lua index 787936e31..6de671529 100644 --- a/builtin/mainmenu/pkgmgr.lua +++ b/builtin/mainmenu/pkgmgr.lua @@ -181,21 +181,6 @@ function pkgmgr.get_texture_packs() end -------------------------------------------------------------------------------- -function pkgmgr.extract(modfile) - if modfile.type == "zip" then - local tempfolder = os.tempfolder() - - if tempfolder ~= nil and - tempfolder ~= "" then - core.create_dir(tempfolder) - if core.extract_zip(modfile.name,tempfolder) then - return tempfolder - end - end - end - return nil -end - function pkgmgr.get_folder_type(path) local testfile = io.open(path .. DIR_DELIM .. "init.lua","r") if testfile ~= nil then @@ -450,6 +435,7 @@ function pkgmgr.enable_mod(this, toset) local toggled_mods = {} local enabled_mods = {} toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod) + toset = mod.enabled -- Update if toggled if not toset then -- Mod(s) were disabled, so no dependencies need to be enabled @@ -561,11 +547,10 @@ function pkgmgr.install_dir(type, path, basename, targetpath) local from = basefolder and basefolder.path or path if targetpath then core.delete_dir(targetpath) - core.create_dir(targetpath) else targetpath = core.get_texturepath() .. DIR_DELIM .. basename end - if not core.copy_dir(from, targetpath) then + if not core.copy_dir(from, targetpath, false) then return nil, fgettext("Failed to install $1 to $2", basename, targetpath) end @@ -586,7 +571,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath) -- Get destination name for modpack if targetpath then core.delete_dir(targetpath) - core.create_dir(targetpath) else local clean_path = nil if basename ~= nil then @@ -610,7 +594,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath) if targetpath then core.delete_dir(targetpath) - core.create_dir(targetpath) else local targetfolder = basename if targetfolder == nil then @@ -636,14 +619,13 @@ function pkgmgr.install_dir(type, path, basename, targetpath) if targetpath then core.delete_dir(targetpath) - core.create_dir(targetpath) else targetpath = core.get_gamepath() .. DIR_DELIM .. basename end end -- Copy it - if not core.copy_dir(basefolder.path, targetpath) then + if not core.copy_dir(basefolder.path, targetpath, false) then return nil, fgettext("Failed to install $1 to $2", basename, targetpath) end @@ -658,23 +640,6 @@ function pkgmgr.install_dir(type, path, basename, targetpath) end -------------------------------------------------------------------------------- -function pkgmgr.install(type, modfilename, basename, dest) - local archive_info = pkgmgr.identify_filetype(modfilename) - local path = pkgmgr.extract(archive_info) - - if path == nil then - return nil, - fgettext("Install: file: \"$1\"", archive_info.name) .. "\n" .. - fgettext("Install: Unsupported file type \"$1\" or broken archive", - archive_info.type) - end - - local targetpath, msg = pkgmgr.install_dir(type, path, basename, dest) - core.delete_dir(path) - return targetpath, msg -end - --------------------------------------------------------------------------------- function pkgmgr.preparemodlist(data) local retval = {} @@ -682,11 +647,9 @@ function pkgmgr.preparemodlist(data) local game_mods = {} --read global mods - local modpath = core.get_modpath() - - if modpath ~= nil and - modpath ~= "" then - get_mods(modpath,global_mods) + local modpaths = core.get_modpaths() + for _, modpath in ipairs(modpaths) do + get_mods(modpath, global_mods) end for i=1,#global_mods,1 do @@ -820,45 +783,6 @@ function pkgmgr.refresh_globals() end -------------------------------------------------------------------------------- -function pkgmgr.identify_filetype(name) - - if name:sub(-3):lower() == "zip" then - return { - name = name, - type = "zip" - } - end - - if name:sub(-6):lower() == "tar.gz" or - name:sub(-3):lower() == "tgz"then - return { - name = name, - type = "tgz" - } - end - - if name:sub(-6):lower() == "tar.bz2" then - return { - name = name, - type = "tbz" - } - end - - if name:sub(-2):lower() == "7z" then - return { - name = name, - type = "7z" - } - end - - return { - name = name, - type = "ukn" - } -end - - --------------------------------------------------------------------------------- function pkgmgr.find_by_gameid(gameid) for i=1,#pkgmgr.games,1 do if pkgmgr.games[i].id == gameid then diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_about.lua index a34dd58bb..ba258fd2d 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_about.lua @@ -28,32 +28,35 @@ local core_developers = { "Lars Hofhansl <larsh@apache.org>", "Pierre-Yves Rollo <dev@pyrollo.com>", "v-rob <robinsonvincent89@gmail.com>", + "hecks", + "Hugues Ross <hugues.ross@gmail.com>", + "Dmitry Kostenko (x2048) <codeforsmile@gmail.com>", } -- For updating active/previous contributors, see the script in ./util/gather_git_credits.py local active_contributors = { - "Wuzzy [devtest game, visual corrections]", - "Zughy [Visual improvements, various fixes]", - "Maksim (MoNTE48) [Android]", + "Wuzzy [I18n for builtin, liquid features, fixes]", + "Zughy [Various features and fixes]", "numzero [Graphics and rendering]", - "appgurueu [Various internal fixes]", - "Desour [Formspec and vector API changes]", - "HybridDog [Rendering fixes and documentation]", - "Hugues Ross [Graphics-related improvements]", - "ANAND (ClobberXD) [Mouse buttons rebinding]", - "luk3yx [Fixes]", - "hecks [Audiovisuals, Lua API]", - "LoneWolfHT [Object crosshair, documentation fixes]", - "Lejo [Server-related improvements]", - "EvidenceB [Compass HUD element]", - "Paul Ouellette (pauloue) [Lua API, documentation]", - "TheTermos [Collision detection, physics]", + "Desour [Internal fixes, Clipboard on X11]", + "Lars Müller [Various internal fixes]", + "JosiahWI [CMake, cleanups and fixes]", + "HybridDog [builtin, documentation]", + "Jude Melton-Houghton [Database implementation]", + "savilli [Fixes]", + "Liso [Shadow Mapping]", + "MoNTE48 [Build fix]", + "Jean-Patrick Guerrero (kilbith) [Fixes]", + "ROllerozxa [Code cleanups]", + "Lejo [bitop library integration]", + "LoneWolfHT [Build fixes]", + "NeroBurner [Joystick]", + "Elias Fleckenstein [Internal fixes]", "David CARLIER [Unix & Haiku build fixes]", - "dcbrwn [Object shading]", - "Elias Fleckenstein [API features/fixes]", - "Jean-Patrick Guerrero (kilbith) [model element, visual fixes]", - "k.h.lai [Memory leak fixes, documentation]", + "pecksin [Clickable web links]", + "srfqi [Android & rendering fixes]", + "EvidenceB [Formspec]", } local previous_core_developers = { @@ -70,6 +73,7 @@ local previous_core_developers = { "Zeno", "ShadowNinja <shadowninja@minetest.net>", "Auke Kok (sofar) <sofar@foo-projects.org>", + "Aaron Suen <warr1024@gmail.com>", } local previous_contributors = { @@ -80,10 +84,10 @@ local previous_contributors = { "MirceaKitsune <mirceakitsune@gmail.com>", "Constantin Wenger (SpeedProg)", "Ciaran Gultnieks (CiaranG)", - "stujones11 [Android UX improvements]", - "Rogier <rogier777@gmail.com> [Fixes]", - "Gregory Currie (gregorycu) [optimisation]", - "srifqi [Fixes]", + "Paul Ouellette (pauloue)", + "stujones11", + "Rogier <rogier777@gmail.com>", + "Gregory Currie (gregorycu)", "JacobF", "Jeija <jeija@mesecons.net> [HTTP, particles]", } @@ -97,8 +101,8 @@ local function buildCreditList(source) end return { - name = "credits", - caption = fgettext("Credits"), + name = "about", + caption = fgettext("About"), cbf_formspec = function(tabview, name, tabdata) local logofile = defaulttexturedir .. "logo.png" local version = core.get_version() @@ -119,11 +123,16 @@ return { buildCreditList(previous_contributors) .. "," .. ";1]" + -- Render information + fs = fs .. "label[0.75,4.9;" .. + fgettext("Active renderer:") .. "\n" .. + core.formspec_escape(core.get_screen_info().render_info) .. "]" + if PLATFORM ~= "Android" then fs = fs .. "tooltip[userdata;" .. fgettext("Opens the directory that contains user-provided worlds, games, mods,\n" .. "and texture packs in a file manager / explorer.") .. "]" - fs = fs .. "button[0,4.75;3.5,1;userdata;" .. fgettext("Open User Data Directory") .. "]" + fs = fs .. "button[0,4;3.5,1;userdata;" .. fgettext("Open User Data Directory") .. "]" end return fs diff --git a/builtin/mainmenu/tab_content.lua b/builtin/mainmenu/tab_content.lua index 336730bf4..fb7f121f8 100644 --- a/builtin/mainmenu/tab_content.lua +++ b/builtin/mainmenu/tab_content.lua @@ -146,10 +146,25 @@ local function get_formspec(tabview, name, tabdata) end -------------------------------------------------------------------------------- +local function handle_doubleclick(pkg) + if pkg.type == "txp" then + if core.settings:get("texture_path") == pkg.path then + core.settings:set("texture_path", "") + else + core.settings:set("texture_path", pkg.path) + end + packages = nil + end +end + +-------------------------------------------------------------------------------- local function handle_buttons(tabview, fields, tabname, tabdata) if fields["pkglist"] ~= nil then local event = core.explode_table_event(fields["pkglist"]) tabdata.selected_pkg = event.row + if event.type == "DCL" then + handle_doubleclick(packages:get_list()[tabdata.selected_pkg]) + end return true end diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 0e06c3bef..e77c6f04d 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -18,8 +18,14 @@ local enable_gamebar = PLATFORM ~= "Android" local current_game, singleplayer_refresh_gamebar +local valid_disabled_settings = { + ["enable_damage"]=true, + ["creative_mode"]=true, + ["enable_server"]=true, +} if enable_gamebar then + -- Currently chosen game in gamebar for theming and filtering function current_game() local last_game_id = core.settings:get("menu_last_game") local game = pkgmgr.find_by_gameid(last_game_id) @@ -27,10 +33,30 @@ if enable_gamebar then return game end + -- Apply menu changes from given game + function apply_game(game) + core.set_topleft_text(game.name) + core.settings:set("menu_last_game", game.id) + menudata.worldlist:set_filtercriteria(game.id) + + mm_game_theme.update("singleplayer", game) -- this refreshes the formspec + + local index = filterlist.get_current_index(menudata.worldlist, + tonumber(core.settings:get("mainmenu_last_selected_world"))) + if not index or index < 1 then + local selected = core.get_textlist_index("sp_worlds") + if selected ~= nil and selected < #menudata.worldlist:get_list() then + index = selected + else + index = #menudata.worldlist:get_list() + end + end + menu_worldmt_legacy(index) + end + function singleplayer_refresh_gamebar() local old_bar = ui.find_by_name("game_button_bar") - if old_bar ~= nil then old_bar:delete() end @@ -45,26 +71,10 @@ if enable_gamebar then return true end - for key,value in pairs(fields) do - for j=1,#pkgmgr.games,1 do - if ("game_btnbar_" .. pkgmgr.games[j].id == key) then - mm_texture.update("singleplayer", pkgmgr.games[j]) - core.set_topleft_text(pkgmgr.games[j].name) - core.settings:set("menu_last_game",pkgmgr.games[j].id) - menudata.worldlist:set_filtercriteria(pkgmgr.games[j].id) - local index = filterlist.get_current_index(menudata.worldlist, - tonumber(core.settings:get("mainmenu_last_selected_world"))) - if not index or index < 1 then - local selected = core.get_textlist_index("sp_worlds") - if selected ~= nil and selected < #menudata.worldlist:get_list() then - index = selected - else - index = #menudata.worldlist:get_list() - end - end - menu_worldmt_legacy(index) - return true - end + for _, game in ipairs(pkgmgr.games) do + if fields["game_btnbar_" .. game.id] then + apply_game(game) + return true end end end @@ -73,25 +83,22 @@ if enable_gamebar then game_buttonbar_button_handler, {x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15}) - for i=1,#pkgmgr.games,1 do - local btn_name = "game_btnbar_" .. pkgmgr.games[i].id + for _, game in ipairs(pkgmgr.games) do + local btn_name = "game_btnbar_" .. game.id local image = nil local text = nil - local tooltip = core.formspec_escape(pkgmgr.games[i].name) + local tooltip = core.formspec_escape(game.name) - if pkgmgr.games[i].menuicon_path ~= nil and - pkgmgr.games[i].menuicon_path ~= "" then - image = core.formspec_escape(pkgmgr.games[i].menuicon_path) + if (game.menuicon_path or "") ~= "" then + image = core.formspec_escape(game.menuicon_path) else - - local part1 = pkgmgr.games[i].id:sub(1,5) - local part2 = pkgmgr.games[i].id:sub(6,10) - local part3 = pkgmgr.games[i].id:sub(11) + local part1 = game.id:sub(1,5) + local part2 = game.id:sub(6,10) + local part3 = game.id:sub(11) text = part1 .. "\n" .. part2 - if part3 ~= nil and - part3 ~= "" then + if part3 ~= "" then text = text .. "\n" .. part3 end end @@ -102,37 +109,91 @@ if enable_gamebar then btnbar:add_button("game_open_cdb", "", plus_image, fgettext("Install games from ContentDB")) end else + -- Currently chosen game in gamebar: no gamebar -> no "current" game function current_game() return nil end end +local function get_disabled_settings(game) + if not game then + return {} + end + + local gameconfig = Settings(game.path .. "/game.conf") + local disabled_settings = {} + if gameconfig then + local disabled_settings_str = (gameconfig:get("disabled_settings") or ""):split() + for _, value in pairs(disabled_settings_str) do + local state = false + value = value:trim() + if string.sub(value, 1, 1) == "!" then + state = true + value = string.sub(value, 2) + end + if valid_disabled_settings[value] then + disabled_settings[value] = state + else + core.log("error", "Invalid disabled setting in game.conf: "..tostring(value)) + end + end + end + return disabled_settings +end + local function get_formspec(tabview, name, tabdata) local retval = "" local index = filterlist.get_current_index(menudata.worldlist, - tonumber(core.settings:get("mainmenu_last_selected_world")) - ) + tonumber(core.settings:get("mainmenu_last_selected_world"))) + local list = menudata.worldlist:get_list() + local world = list and index and list[index] + local game + if world then + game = pkgmgr.find_by_gameid(world.gameid) + else + game = current_game() + end + local disabled_settings = get_disabled_settings(game) + + local creative, damage, host = "", "", "" + + -- Y offsets for game settings checkboxes + local y = -0.2 + local yo = 0.45 + + if disabled_settings["creative_mode"] == nil then + creative = "checkbox[0,"..y..";cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. + dump(core.settings:get_bool("creative_mode")) .. "]" + y = y + yo + end + if disabled_settings["enable_damage"] == nil then + damage = "checkbox[0,"..y..";cb_enable_damage;".. fgettext("Enable Damage") .. ";" .. + dump(core.settings:get_bool("enable_damage")) .. "]" + y = y + yo + end + if disabled_settings["enable_server"] == nil then + host = "checkbox[0,"..y..";cb_server;".. fgettext("Host Server") ..";" .. + dump(core.settings:get_bool("enable_server")) .. "]" + y = y + yo + end retval = retval .. "button[3.9,3.8;2.8,1;world_delete;".. fgettext("Delete") .. "]" .. "button[6.55,3.8;2.8,1;world_configure;".. fgettext("Select Mods") .. "]" .. "button[9.2,3.8;2.8,1;world_create;".. fgettext("New") .. "]" .. "label[3.9,-0.05;".. fgettext("Select World:") .. "]".. - "checkbox[0,-0.20;cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. - dump(core.settings:get_bool("creative_mode")) .. "]".. - "checkbox[0,0.25;cb_enable_damage;".. fgettext("Enable Damage") .. ";" .. - dump(core.settings:get_bool("enable_damage")) .. "]".. - "checkbox[0,0.7;cb_server;".. fgettext("Host Server") ..";" .. - dump(core.settings:get_bool("enable_server")) .. "]" .. + creative .. + damage .. + host .. "textlist[3.9,0.4;7.9,3.45;sp_worlds;" .. - menu_render_worldlist() .. + menu_render_worldlist(not enable_gamebar) .. ";" .. index .. "]" - if core.settings:get_bool("enable_server") then + if core.settings:get_bool("enable_server") and disabled_settings["enable_server"] == nil then retval = retval .. "button[7.9,4.75;4.1,1;play;".. fgettext("Host Game") .. "]" .. - "checkbox[0,1.15;cb_server_announce;" .. fgettext("Announce Server") .. ";" .. + "checkbox[0,"..y..";cb_server_announce;" .. fgettext("Announce Server") .. ";" .. dump(core.settings:get_bool("server_announce")) .. "]" .. "field[0.3,2.85;3.8,0.5;te_playername;" .. fgettext("Name") .. ";" .. core.formspec_escape(core.settings:get("name")) .. "]" .. @@ -227,9 +288,21 @@ local function main_button_handler(this, fields, name, tabdata) -- Update last game local world = menudata.worldlist:get_raw_element(gamedata.selected_world) + local game_obj if world then - local game = pkgmgr.find_by_gameid(world.gameid) - core.settings:set("menu_last_game", game.id) + game_obj = pkgmgr.find_by_gameid(world.gameid) + core.settings:set("menu_last_game", game_obj.id) + end + + local disabled_settings = get_disabled_settings(game_obj) + for k, _ in pairs(valid_disabled_settings) do + local v = disabled_settings[k] + if v ~= nil then + if k == "enable_server" and v == true then + error("Setting 'enable_server' cannot be force-enabled! The game.conf needs to be fixed.") + end + core.settings:set_bool(k, disabled_settings[k]) + end end if core.settings:get_bool("enable_server") then @@ -251,11 +324,11 @@ local function main_button_handler(this, fields, name, tabdata) end if fields["world_create"] ~= nil then - local create_world_dlg = create_create_world_dlg(true) + local create_world_dlg = create_create_world_dlg(enable_gamebar) create_world_dlg:set_parent(this) this:hide() create_world_dlg:show() - mm_texture.update("singleplayer", current_game()) + mm_game_theme.update("singleplayer", current_game()) return true end @@ -272,7 +345,7 @@ local function main_button_handler(this, fields, name, tabdata) delete_world_dlg:set_parent(this) this:hide() delete_world_dlg:show() - mm_texture.update("singleplayer",current_game()) + mm_game_theme.update("singleplayer",current_game()) end end @@ -290,7 +363,7 @@ local function main_button_handler(this, fields, name, tabdata) configdialog:set_parent(this) this:hide() configdialog:show() - mm_texture.update("singleplayer",current_game()) + mm_game_theme.update("singleplayer",current_game()) end end @@ -303,11 +376,8 @@ if enable_gamebar then function on_change(type, old_tab, new_tab) if (type == "ENTER") then local game = current_game() - if game then - menudata.worldlist:set_filtercriteria(game.id) - core.set_topleft_text(game.name) - mm_texture.update("singleplayer",game) + apply_game(game) end singleplayer_refresh_gamebar() @@ -319,7 +389,7 @@ if enable_gamebar then gamebar:hide() end core.set_topleft_text("") - mm_texture.update(new_tab,nil) + mm_game_theme.update(new_tab,nil) end end end diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index e6748ed88..fb7409864 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -15,17 +15,50 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --------------------------------------------------------------------------------- +local function get_sorted_servers() + local servers = { + fav = {}, + public = {}, + incompatible = {} + } + + local favs = serverlistmgr.get_favorites() + local taken_favs = {} + local result = menudata.search_result or serverlistmgr.servers + for _, server in ipairs(result) do + server.is_favorite = false + for index, fav in ipairs(favs) do + if server.address == fav.address and server.port == fav.port then + taken_favs[index] = true + server.is_favorite = true + break + end + end + server.is_compatible = is_server_protocol_compat(server.proto_min, server.proto_max) + if server.is_favorite then + table.insert(servers.fav, server) + elseif server.is_compatible then + table.insert(servers.public, server) + else + table.insert(servers.incompatible, server) + end + end + + if not menudata.search_result then + for index, fav in ipairs(favs) do + if not taken_favs[index] then + table.insert(servers.fav, fav) + end + end + end + + return servers +end + local function get_formspec(tabview, name, tabdata) -- Update the cached supported proto info, -- it may have changed after a change by the settings menu. common_update_cached_supp_proto() - local selected - if menudata.search_result then - selected = menudata.search_result[tabdata.selected] - else - selected = serverlistmgr.servers[tabdata.selected] - end if not tabdata.search_for then tabdata.search_for = "" @@ -33,128 +66,221 @@ local function get_formspec(tabview, name, tabdata) local retval = -- Search - "field[0.15,0.075;5.91,1;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" .. - "image_button[5.63,-.165;.83,.83;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" .. - "image_button[6.3,-.165;.83,.83;" .. core.formspec_escape(defaulttexturedir .. "clear.png") .. ";btn_mp_clear;]" .. - "image_button[6.97,-.165;.83,.83;" .. core.formspec_escape(defaulttexturedir .. "refresh.png") - .. ";btn_mp_refresh;]" .. + "field[0.25,0.25;7,0.75;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" .. + "container[7.25,0.25]" .. + "image_button[0,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" .. + "image_button[0.75,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "clear.png") .. ";btn_mp_clear;]" .. + "image_button[1.5,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "refresh.png") .. ";btn_mp_refresh;]" .. + "tooltip[btn_mp_clear;" .. fgettext("Clear") .. "]" .. + "tooltip[btn_mp_search;" .. fgettext("Search") .. "]" .. + "tooltip[btn_mp_refresh;" .. fgettext("Refresh") .. "]" .. + "container_end[]" .. + + "container[9.75,0]" .. + "box[0,0;5.75,7;#666666]" .. -- Address / Port - "label[7.75,-0.25;" .. fgettext("Address / Port") .. "]" .. - "field[8,0.65;3.25,0.5;te_address;;" .. + "label[0.25,0.35;" .. fgettext("Address") .. "]" .. + "label[4.25,0.35;" .. fgettext("Port") .. "]" .. + "field[0.25,0.5;4,0.75;te_address;;" .. core.formspec_escape(core.settings:get("address")) .. "]" .. - "field[11.1,0.65;1.4,0.5;te_port;;" .. + "field[4.25,0.5;1.25,0.75;te_port;;" .. core.formspec_escape(core.settings:get("remote_port")) .. "]" .. -- Name / Password - "label[7.75,0.95;" .. fgettext("Name / Password") .. "]" .. - "field[8,1.85;2.9,0.5;te_name;;" .. + "label[0.25,1.55;" .. fgettext("Name") .. "]" .. + "label[3,1.55;" .. fgettext("Password") .. "]" .. + "field[0.25,1.75;2.75,0.75;te_name;;" .. core.formspec_escape(core.settings:get("name")) .. "]" .. - "pwdfield[10.73,1.85;1.77,0.5;te_pwd;]" .. + "pwdfield[3,1.75;2.5,0.75;te_pwd;]" .. -- Description Background - "box[7.73,2.25;4.25,2.6;#999999]".. + "label[0.25,2.75;" .. fgettext("Server Description") .. "]" .. + "box[0.25,3;5.25,2.75;#999999]".. -- Connect - "button[9.88,4.9;2.3,1;btn_mp_connect;" .. fgettext("Connect") .. "]" + "button[3,6;2.5,0.75;btn_mp_connect;" .. fgettext("Connect") .. "]" - if tabdata.selected and selected then + if tabdata.selected then if gamedata.fav then - retval = retval .. "button[7.73,4.9;2.3,1;btn_delete_favorite;" .. + retval = retval .. "button[0.25,6;2.5,0.75;btn_delete_favorite;" .. fgettext("Del. Favorite") .. "]" end - if selected.description then - retval = retval .. "textarea[8.1,2.3;4.23,2.9;;;" .. - core.formspec_escape((gamedata.serverdescription or ""), true) .. "]" + if gamedata.serverdescription then + retval = retval .. "textarea[0.25,3;5.25,2.75;;;" .. + core.formspec_escape(gamedata.serverdescription) .. "]" end end - --favorites + retval = retval .. "container_end[]" + + -- Table retval = retval .. "tablecolumns[" .. - image_column(fgettext("Favorite"), "favorite") .. ";" .. - image_column(fgettext("Ping")) .. ",padding=0.25;" .. - "color,span=3;" .. - "text,align=right;" .. -- clients - "text,align=center,padding=0.25;" .. -- "/" - "text,align=right,padding=0.25;" .. -- clients_max - image_column(fgettext("Creative mode"), "creative") .. ",padding=1;" .. - image_column(fgettext("Damage enabled"), "damage") .. ",padding=0.25;" .. - --~ PvP = Player versus Player - image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. + "image,tooltip=" .. fgettext("Ping") .. "," .. + "0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," .. + "1=" .. core.formspec_escape(defaulttexturedir .. "server_ping_4.png") .. "," .. + "2=" .. core.formspec_escape(defaulttexturedir .. "server_ping_3.png") .. "," .. + "3=" .. core.formspec_escape(defaulttexturedir .. "server_ping_2.png") .. "," .. + "4=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png") .. "," .. + "5=" .. core.formspec_escape(defaulttexturedir .. "server_favorite.png") .. "," .. + "6=" .. core.formspec_escape(defaulttexturedir .. "server_public.png") .. "," .. + "7=" .. core.formspec_escape(defaulttexturedir .. "server_incompatible.png") .. ";" .. "color,span=1;" .. - "text,padding=1]" .. - "table[-0.15,0.6;7.75,5.15;favorites;" - - if menudata.search_result then - local favs = serverlistmgr.get_favorites() - for i = 1, #menudata.search_result do - local server = menudata.search_result[i] - for fav_id = 1, #favs do - if server.address == favs[fav_id].address and - server.port == favs[fav_id].port then - server.is_favorite = true - end - end - - if i ~= 1 then - retval = retval .. "," - end - - retval = retval .. render_serverlist_row(server, server.is_favorite) - end - elseif #serverlistmgr.servers > 0 then - local favs = serverlistmgr.get_favorites() - if #favs > 0 then - for i = 1, #favs do - for j = 1, #serverlistmgr.servers do - if serverlistmgr.servers[j].address == favs[i].address and - serverlistmgr.servers[j].port == favs[i].port then - table.insert(serverlistmgr.servers, i, table.remove(serverlistmgr.servers, j)) - end - end - if favs[i].address ~= serverlistmgr.servers[i].address then - table.insert(serverlistmgr.servers, i, favs[i]) - end + "text,align=inline;".. + "color,span=1;" .. + "text,align=inline,width=4.25;" .. + "image,tooltip=" .. fgettext("Creative mode") .. "," .. + "0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," .. + "1=" .. core.formspec_escape(defaulttexturedir .. "server_flags_creative.png") .. "," .. + "align=inline,padding=0.25,width=1.5;" .. + --~ PvP = Player versus Player + "image,tooltip=" .. fgettext("Damage / PvP") .. "," .. + "0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," .. + "1=" .. core.formspec_escape(defaulttexturedir .. "server_flags_damage.png") .. "," .. + "2=" .. core.formspec_escape(defaulttexturedir .. "server_flags_pvp.png") .. "," .. + "align=inline,padding=0.25,width=1.5;" .. + "color,align=inline,span=1;" .. + "text,align=inline,padding=1]" .. + "table[0.25,1;9.25,5.75;servers;" + + local servers = get_sorted_servers() + + local dividers = { + fav = "5,#ffff00," .. fgettext("Favorites") .. ",,,0,0,,", + public = "6,#4bdd42," .. fgettext("Public Servers") .. ",,,0,0,,", + incompatible = "7,"..mt_color_grey.."," .. fgettext("Incompatible Servers") .. ",,,0,0,," + } + local order = {"fav", "public", "incompatible"} + + tabdata.lookup = {} -- maps row number to server + local rows = {} + for _, section in ipairs(order) do + local section_servers = servers[section] + if next(section_servers) ~= nil then + rows[#rows + 1] = dividers[section] + for _, server in ipairs(section_servers) do + tabdata.lookup[#rows + 1] = server + rows[#rows + 1] = render_serverlist_row(server) end end - - retval = retval .. render_serverlist_row(serverlistmgr.servers[1], (#favs > 0)) - for i = 2, #serverlistmgr.servers do - retval = retval .. "," .. render_serverlist_row(serverlistmgr.servers[i], (i <= #favs)) - end end + retval = retval .. table.concat(rows, ",") + if tabdata.selected then retval = retval .. ";" .. tabdata.selected .. "]" else retval = retval .. ";0]" end - return retval + return retval, "size[15.5,7,false]real_coordinates[true]" end -------------------------------------------------------------------------------- -local function main_button_handler(tabview, fields, name, tabdata) - local serverlist = menudata.search_result or serverlistmgr.servers +local function search_server_list(input) + menudata.search_result = nil + if #serverlistmgr.servers < 2 then + return + end + + -- setup the keyword list + local keywords = {} + for word in input:gmatch("%S+") do + word = word:gsub("(%W)", "%%%1") + table.insert(keywords, word) + end + + if #keywords == 0 then + return + end + + menudata.search_result = {} + + -- Search the serverlist + local search_result = {} + for i = 1, #serverlistmgr.servers do + local server = serverlistmgr.servers[i] + local found = 0 + for k = 1, #keywords do + local keyword = keywords[k] + if server.name then + local sername = server.name:lower() + local _, count = sername:gsub(keyword, keyword) + found = found + count * 4 + end + + if server.description then + local desc = server.description:lower() + local _, count = desc:gsub(keyword, keyword) + found = found + count * 2 + end + end + if found > 0 then + local points = (#serverlistmgr.servers - i) / 5 + found + server.points = points + table.insert(search_result, server) + end + end + + if #search_result == 0 then + return + end + + table.sort(search_result, function(a, b) + return a.points > b.points + end) + menudata.search_result = search_result +end + +local function set_selected_server(tabdata, idx, server) + -- reset selection + if idx == nil or server == nil then + tabdata.selected = nil + + core.settings:set("address", "") + core.settings:set("remote_port", "30000") + return + end + + local address = server.address + local port = server.port + gamedata.serverdescription = server.description + + gamedata.fav = false + for _, fav in ipairs(serverlistmgr.get_favorites()) do + if address == fav.address and port == fav.port then + gamedata.fav = true + break + end + end + + if address and port then + core.settings:set("address", address) + core.settings:set("remote_port", port) + end + tabdata.selected = idx +end + +local function main_button_handler(tabview, fields, name, tabdata) if fields.te_name then gamedata.playername = fields.te_name core.settings:set("name", fields.te_name) end - if fields.favorites then - local event = core.explode_table_event(fields.favorites) - local fav = serverlist[event.row] + if fields.servers then + local event = core.explode_table_event(fields.servers) + local server = tabdata.lookup[event.row] - if event.type == "DCL" then - if event.row <= #serverlist then + if server then + if event.type == "DCL" then if not is_server_protocol_compat_or_error( - fav.proto_min, fav.proto_max) then + server.proto_min, server.proto_max) then return true end - gamedata.address = fav.address - gamedata.port = fav.port + gamedata.address = server.address + gamedata.port = server.port gamedata.playername = fields.te_name gamedata.selected_world = 0 @@ -162,84 +288,32 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.password = fields.te_pwd end - gamedata.servername = fav.name - gamedata.serverdescription = fav.description + gamedata.servername = server.name + gamedata.serverdescription = server.description if gamedata.address and gamedata.port then core.settings:set("address", gamedata.address) core.settings:set("remote_port", gamedata.port) core.start() end + return true end - return true - end - - if event.type == "CHG" then - if event.row <= #serverlist then - gamedata.fav = false - local favs = serverlistmgr.get_favorites() - local address = fav.address - local port = fav.port - gamedata.serverdescription = fav.description - - for i = 1, #favs do - if fav.address == favs[i].address and - fav.port == favs[i].port then - gamedata.fav = true - end - end - - if address and port then - core.settings:set("address", address) - core.settings:set("remote_port", port) - end - tabdata.selected = event.row - end - return true - end - end - - if fields.key_up or fields.key_down then - local fav_idx = core.get_table_index("favorites") - local fav = serverlist[fav_idx] - - if fav_idx then - if fields.key_up and fav_idx > 1 then - fav_idx = fav_idx - 1 - elseif fields.key_down and fav_idx < #serverlistmgr.servers then - fav_idx = fav_idx + 1 + if event.type == "CHG" then + set_selected_server(tabdata, event.row, server) + return true end - else - fav_idx = 1 - end - - if not serverlistmgr.servers or not fav then - tabdata.selected = 0 - return true - end - - local address = fav.address - local port = fav.port - gamedata.serverdescription = fav.description - if address and port then - core.settings:set("address", address) - core.settings:set("remote_port", port) end - - tabdata.selected = fav_idx - return true end if fields.btn_delete_favorite then - local current_favorite = core.get_table_index("favorites") - if not current_favorite then return end - - serverlistmgr.delete_favorite(serverlistmgr.servers[current_favorite]) - serverlistmgr.sync() - tabdata.selected = nil - - core.settings:set("address", "") - core.settings:set("remote_port", "30000") + local idx = core.get_table_index("servers") + if not idx then return end + local server = tabdata.lookup[idx] + if not server then return end + + serverlistmgr.delete_favorite(server) + -- the server at [idx+1] will be at idx once list is refreshed + set_selected_server(tabdata, idx, tabdata.lookup[idx+1]) return true end @@ -250,63 +324,13 @@ local function main_button_handler(tabview, fields, name, tabdata) end if fields.btn_mp_search or fields.key_enter_field == "te_search" then - tabdata.selected = 1 - local input = fields.te_search:lower() tabdata.search_for = fields.te_search - - if #serverlistmgr.servers < 2 then - return true - end - - menudata.search_result = {} - - -- setup the keyword list - local keywords = {} - for word in input:gmatch("%S+") do - word = word:gsub("(%W)", "%%%1") - table.insert(keywords, word) + search_server_list(fields.te_search:lower()) + if menudata.search_result then + -- first server in row 2 due to header + set_selected_server(tabdata, 2, menudata.search_result[1]) end - if #keywords == 0 then - menudata.search_result = nil - return true - end - - -- Search the serverlist - local search_result = {} - for i = 1, #serverlistmgr.servers do - local server = serverlistmgr.servers[i] - local found = 0 - for k = 1, #keywords do - local keyword = keywords[k] - if server.name then - local sername = server.name:lower() - local _, count = sername:gsub(keyword, keyword) - found = found + count * 4 - end - - if server.description then - local desc = server.description:lower() - local _, count = desc:gsub(keyword, keyword) - found = found + count * 2 - end - end - if found > 0 then - local points = (#serverlistmgr.servers - i) / 5 + found - server.points = points - table.insert(search_result, server) - end - end - if #search_result > 0 then - table.sort(search_result, function(a, b) - return a.points > b.points - end) - menudata.search_result = search_result - local first_server = search_result[1] - core.settings:set("address", first_server.address) - core.settings:set("remote_port", first_server.port) - gamedata.serverdescription = first_server.description - end return true end @@ -322,20 +346,22 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.address = fields.te_address gamedata.port = tonumber(fields.te_port) gamedata.selected_world = 0 - local fav_idx = core.get_table_index("favorites") - local fav = serverlist[fav_idx] - if fav_idx and fav_idx <= #serverlist and - fav.address == gamedata.address and - fav.port == gamedata.port then + local idx = core.get_table_index("servers") + local server = idx and tabdata.lookup[idx] + + set_selected_server(tabdata) - serverlistmgr.add_favorite(fav) + if server and server.address == gamedata.address and + server.port == gamedata.port then - gamedata.servername = fav.name - gamedata.serverdescription = fav.description + serverlistmgr.add_favorite(server) + + gamedata.servername = server.name + gamedata.serverdescription = server.description if not is_server_protocol_compat_or_error( - fav.proto_min, fav.proto_max) then + server.proto_min, server.proto_max) then return true end else @@ -354,6 +380,7 @@ local function main_button_handler(tabview, fields, name, tabdata) core.start() return true end + return false end @@ -362,7 +389,6 @@ local function on_change(type, old_tab, new_tab) serverlistmgr.sync() end --------------------------------------------------------------------------------- return { name = "online", caption = fgettext("Join Game"), diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index 29744048a..700b7390f 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -43,6 +43,14 @@ local labels = { fgettext("2x"), fgettext("4x"), fgettext("8x") + }, + shadow_levels = { + fgettext("Disabled"), + fgettext("Very Low"), + fgettext("Low"), + fgettext("Medium"), + fgettext("High"), + fgettext("Ultra High") } } @@ -66,6 +74,10 @@ local dd_options = { antialiasing = { table.concat(labels.antialiasing, ","), {"0", "2", "4", "8"} + }, + shadow_levels = { + table.concat(labels.shadow_levels, ","), + { "0", "1", "2", "3", "4", "5" } } } @@ -110,6 +122,15 @@ local getSettingIndex = { end end return 1 + end, + ShadowMapping = function() + local shadow_setting = core.settings:get("shadow_levels") + for i = 1, #dd_options.shadow_levels[2] do + if shadow_setting == dd_options.shadow_levels[2][i] then + return i + end + end + return 1 end } @@ -198,6 +219,9 @@ local function formspec(tabview, name, tabdata) .. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" .. "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";" .. dump(core.settings:get_bool("enable_waving_plants")) .. "]" + --"label[8.25,3.0;" .. fgettext("Dynamic shadows: ") .. "]" .. + --"dropdown[8.25,3.5;3.5;dd_shadows;" .. dd_options.shadow_levels[1] .. ";" + -- .. getSettingIndex.ShadowMapping() .. "]" else tab_string = tab_string .. "label[8.38,0.7;" .. core.colorize("#888888", @@ -208,6 +232,8 @@ local function formspec(tabview, name, tabdata) fgettext("Waving Leaves")) .. "]" .. "label[8.38,2.2;" .. core.colorize("#888888", fgettext("Waving Plants")) .. "]" + --"label[8.38,2.7;" .. core.colorize("#888888", + -- fgettext("Dynamic shadows")) .. "]" end return tab_string @@ -221,7 +247,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) adv_settings_dlg:set_parent(this) this:hide() adv_settings_dlg:show() - --mm_texture.update("singleplayer", current_game()) + --mm_game_theme.update("singleplayer", current_game()) return true end if fields["cb_smooth_lighting"] then @@ -249,13 +275,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) return true end if fields["cb_shaders"] then - if (core.settings:get("video_driver") == "direct3d8" or - core.settings:get("video_driver") == "direct3d9") then - core.settings:set("enable_shaders", "false") - gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.") - else - core.settings:set("enable_shaders", fields["cb_shaders"]) - end + core.settings:set("enable_shaders", fields["cb_shaders"]) return true end if fields["cb_tonemapping"] then @@ -333,6 +353,34 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) ddhandled = true end + for i = 1, #labels.shadow_levels do + if fields["dd_shadows"] == labels.shadow_levels[i] then + core.settings:set("shadow_levels", dd_options.shadow_levels[2][i]) + ddhandled = true + end + end + + if fields["dd_shadows"] == labels.shadow_levels[1] then + core.settings:set("enable_dynamic_shadows", "false") + else + local shadow_presets = { + [2] = { 80, 512, "true", 0, "false" }, + [3] = { 120, 1024, "true", 1, "false" }, + [4] = { 350, 2048, "true", 1, "false" }, + [5] = { 350, 2048, "true", 2, "true" }, + [6] = { 450, 4096, "true", 2, "true" }, + } + local s = shadow_presets[table.indexof(labels.shadow_levels, fields["dd_shadows"])] + if s then + core.settings:set("enable_dynamic_shadows", "true") + core.settings:set("shadow_map_max_distance", s[1]) + core.settings:set("shadow_map_texture_size", s[2]) + core.settings:set("shadow_map_texture_32bit", s[3]) + core.settings:set("shadow_filters", s[4]) + core.settings:set("shadow_map_color", s[5]) + end + end + return ddhandled end diff --git a/builtin/mainmenu/tests/serverlistmgr_spec.lua b/builtin/mainmenu/tests/serverlistmgr_spec.lua index 148e9b794..a091959fb 100644 --- a/builtin/mainmenu/tests/serverlistmgr_spec.lua +++ b/builtin/mainmenu/tests/serverlistmgr_spec.lua @@ -2,6 +2,7 @@ _G.core = {} _G.unpack = table.unpack _G.serverlistmgr = {} +dofile("builtin/common/vector.lua") dofile("builtin/common/misc_helpers.lua") dofile("builtin/mainmenu/serverlistmgr.lua") |