]> git.lizzy.rs Git - minetest.git/commitdiff
Mainmenu: Improve "Join Game" tab (#11078)
authorsfan5 <sfan5@live.de>
Sat, 20 Mar 2021 18:48:25 +0000 (19:48 +0100)
committerGitHub <noreply@github.com>
Sat, 20 Mar 2021 18:48:25 +0000 (19:48 +0100)
LICENSE.txt
builtin/fstk/tabview.lua
builtin/mainmenu/common.lua
builtin/mainmenu/tab_online.lua
textures/base/pack/server_favorite.png [new file with mode: 0644]
textures/base/pack/server_flags_favorite.png [deleted file]
textures/base/pack/server_incompatible.png [new file with mode: 0644]
textures/base/pack/server_public.png [new file with mode: 0644]

index 9b8ee851aba3f91d7802265621cee64d83fe483a..2d1c0c79546cc81362737793d53167eef3a953a0 100644 (file)
@@ -14,6 +14,9 @@ https://www.apache.org/licenses/LICENSE-2.0.html
 Textures by Zughy are under CC BY-SA 4.0
 https://creativecommons.org/licenses/by-sa/4.0/
 
+textures/base/pack/server_public.png is under CC-BY 4.0, taken from Twitter's Twemoji set
+https://creativecommons.org/licenses/by/4.0/
+
 Authors of media files
 -----------------------
 Everything not listed in here:
@@ -39,10 +42,10 @@ erlehmann:
   misc/minetest.svg
   textures/base/pack/logo.png
 
-JRottm
+JRottm:
   textures/base/pack/player_marker.png
 
-srifqi
+srifqi:
   textures/base/pack/chat_hide_btn.png
   textures/base/pack/chat_show_btn.png
   textures/base/pack/joystick_bg.png
@@ -58,6 +61,9 @@ Zughy:
   textures/base/pack/cdb_update.png
   textures/base/pack/cdb_viewonline.png
 
+appgurueu:
+  textures/base/pack/server_incompatible.png
+
 License of Minetest source code
 -------------------------------
 
index 3715e231b96d2a7764ddbaee02d23a3cbd7a8096..424d329fb08ebf5ccfc3cadd90b18ad03bc4b454 100644 (file)
@@ -58,26 +58,20 @@ end
 
 --------------------------------------------------------------------------------
 local function get_formspec(self)
-       local formspec = ""
+       if self.hidden or (self.parent ~= nil and self.parent.hidden) then
+               return ""
+       end
+       local tab = self.tablist[self.last_tab_index]
 
-       if not self.hidden and (self.parent == nil or not self.parent.hidden) then
+       local content, prepend = tab.get_formspec(self, tab.name, tab.tabdata, tab.tabsize)
 
-               if self.parent == nil then
-                       local tsize = self.tablist[self.last_tab_index].tabsize or
-                                       {width=self.width, height=self.height}
-                       formspec = formspec ..
-                                       string.format("size[%f,%f,%s]",tsize.width,tsize.height,
-                                               dump(self.fixed_size))
-               end
-               formspec = formspec .. self:tab_header()
-               formspec = formspec ..
-                               self.tablist[self.last_tab_index].get_formspec(
-                                       self,
-                                       self.tablist[self.last_tab_index].name,
-                                       self.tablist[self.last_tab_index].tabdata,
-                                       self.tablist[self.last_tab_index].tabsize
-                                       )
+       if self.parent == nil and not prepend then
+               local tsize = tab.tabsize or {width=self.width, height=self.height}
+               prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height,
+                               dump(self.fixed_size))
        end
+
+       local formspec = (prepend or "") .. self:tab_header() .. content
        return formspec
 end
 
@@ -97,14 +91,9 @@ local function handle_buttons(self,fields)
                return true
        end
 
-       if self.tablist[self.last_tab_index].button_handler ~= nil then
-               return
-                       self.tablist[self.last_tab_index].button_handler(
-                                       self,
-                                       fields,
-                                       self.tablist[self.last_tab_index].name,
-                                       self.tablist[self.last_tab_index].tabdata
-                                       )
+       local tab = self.tablist[self.last_tab_index]
+       if tab.button_handler ~= nil then
+               return tab.button_handler(self, fields, tab.name, tab.tabdata)
        end
 
        return false
@@ -122,14 +111,9 @@ local function handle_events(self,event)
                return true
        end
 
-       if self.tablist[self.last_tab_index].evt_handler ~= nil then
-               return
-                       self.tablist[self.last_tab_index].evt_handler(
-                                       self,
-                                       event,
-                                       self.tablist[self.last_tab_index].name,
-                                       self.tablist[self.last_tab_index].tabdata
-                                       )
+       local tab = self.tablist[self.last_tab_index]
+       if tab.evt_handler ~= nil then
+               return tab.evt_handler(self, event, tab.name, tab.tabdata)
        end
 
        return false
index cd896f9ec2001f7f80211ccfe873b21660a81aec..6db3510481e1701c89d2090f8de2f7643513cdc1 100644 (file)
 --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,38 +89,35 @@ 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,"
-       else
-               details = details .. "0,"
-       end
-
-       if spec.damage then
-               details = details .. "1,"
+               table.insert(details, "1") -- creative icon
        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
+       table.insert(details, color)
+       table.insert(details, text)
+
+       return table.concat(details, ",")
 end
 
 --------------------------------------------------------------------------------
@@ -150,14 +126,13 @@ os.tempfolder = function()
        return temp .. DIR_DELIM .. "MT_" .. math.random(0, 10000)
 end
 
---------------------------------------------------------------------------------
 os.tmpname = function()
        local path = os.tempfolder()
        io.open(path, "w"):close()
        return path
 end
-
 --------------------------------------------------------------------------------
+
 function menu_render_worldlist()
        local retval = ""
        local current_worldlist = menudata.worldlist:get_list()
@@ -171,7 +146,6 @@ function menu_render_worldlist()
        return 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 +162,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 +179,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 +186,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 +214,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
index e6748ed882a0a9c9237a093a452f22de1d7a4832..fb7409864fa526b839353fce2d5bf5dd2851f45c 100644 (file)
 --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/textures/base/pack/server_favorite.png b/textures/base/pack/server_favorite.png
new file mode 100644 (file)
index 0000000..6a3fc5e
Binary files /dev/null and b/textures/base/pack/server_favorite.png differ
diff --git a/textures/base/pack/server_flags_favorite.png b/textures/base/pack/server_flags_favorite.png
deleted file mode 100644 (file)
index 6a3fc5e..0000000
Binary files a/textures/base/pack/server_flags_favorite.png and /dev/null differ
diff --git a/textures/base/pack/server_incompatible.png b/textures/base/pack/server_incompatible.png
new file mode 100644 (file)
index 0000000..9076ab5
Binary files /dev/null and b/textures/base/pack/server_incompatible.png differ
diff --git a/textures/base/pack/server_public.png b/textures/base/pack/server_public.png
new file mode 100644 (file)
index 0000000..46a48fa
Binary files /dev/null and b/textures/base/pack/server_public.png differ