From 042131d91d0f61d1252d240aa799f3b12b509cfe Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 20 Mar 2021 19:48:25 +0100 Subject: Mainmenu: Improve "Join Game" tab (#11078) --- builtin/mainmenu/tab_online.lua | 456 +++++++++++++++++++++------------------- 1 file changed, 241 insertions(+), 215 deletions(-) (limited to 'builtin/mainmenu/tab_online.lua') 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"), -- cgit v1.2.3