]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
create private tab under server; private list is yet TODO
authorBuddyFriendGuy <bfggeneral@gmail.com>
Wed, 3 Jun 2015 07:28:05 +0000 (03:28 -0400)
committerBuddyFriendGuy <bfggeneral@gmail.com>
Wed, 3 Jun 2015 07:28:05 +0000 (03:28 -0400)
qcsrc/menu/classes.inc
qcsrc/menu/xonotic/dialog_multiplayer.qc
qcsrc/menu/xonotic/dialog_multiplayer_join.qc
qcsrc/menu/xonotic/dialog_multiplayer_join_private.qc [new file with mode: 0644]
qcsrc/menu/xonotic/dialog_multiplayer_join_public.qc [new file with mode: 0644]
qcsrc/menu/xonotic/dialog_multiplayer_join_public_serverinfo.qc [new file with mode: 0644]
qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc [deleted file]
qcsrc/menu/xonotic/privateserverlist.qc [new file with mode: 0644]
qcsrc/menu/xonotic/serverlist.qc

index 68b90d586394939a459e81345903a605f7be2e75..39a502bd163f1132dfe0e7c237dc89fba8786bb6 100644 (file)
@@ -65,7 +65,9 @@
 #include "xonotic/dialog_multiplayer_create_mapinfo.qc"
 #include "xonotic/dialog_multiplayer_create_mutators.qc"
 #include "xonotic/dialog_multiplayer_join.qc"
-#include "xonotic/dialog_multiplayer_join_serverinfo.qc"
+#include "xonotic/dialog_multiplayer_join_public.qc"
+#include "xonotic/dialog_multiplayer_join_public_serverinfo.qc"
+#include "xonotic/dialog_multiplayer_join_private.qc"
 #include "xonotic/dialog_multiplayer_media.qc"
 #include "xonotic/dialog_multiplayer_media_demo.qc"
 #include "xonotic/dialog_multiplayer_media_demo_startconfirm.qc"
 #include "xonotic/playerlist.qc"
 #include "xonotic/playermodel.qc"
 #include "xonotic/playlist.qc"
+#include "xonotic/privateserverlist.qc"
 #include "xonotic/radiobutton.qc"
 #include "xonotic/rootdialog.qc"
 #include "xonotic/screenshotimage.qc"
index 6aedde7fd0c79871109fde659c93a034ee7b9a0f..47f5e5742603c9d8aaa76c22c4f3bf452b9affe2 100644 (file)
@@ -17,7 +17,7 @@ void XonoticMultiplayerDialog_fill(entity me)
        entity mc, e;
        mc = makeXonoticTabController(me.rows - 1);
        me.TR(me);
-               me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Servers"), makeXonoticServerListTab()));
+               me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Servers"), makeXonoticJoinTab()));
                me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Create"), makeXonoticServerCreateTab()));
                //me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Demos"), makeXonoticDemoBrowserTab()));
                //me.TD(me, 1, 1, e = mc.makeTabButton(mc, _("Screenshots"), makeXonoticScreenshotBrowserTab()));
index 2b2354c5330f3f0c6cae2909e0b07655f3564a1b..df7a7c531b02716c66130002d5b2325da22fe2b6 100644 (file)
@@ -1,81 +1,35 @@
 #ifndef DIALOG_MULTIPLAYER_JOIN_H
 #define DIALOG_MULTIPLAYER_JOIN_H
 #include "tab.qc"
-CLASS(XonoticServerListTab, XonoticTab)
-       METHOD(XonoticServerListTab, fill, void(entity))
-       ATTRIB(XonoticServerListTab, title, string, _("Join"))
-       ATTRIB(XonoticServerListTab, intendedWidth, float, 0.9)
-       ATTRIB(XonoticServerListTab, rows, float, 23)
-       ATTRIB(XonoticServerListTab, columns, float, 6.5)
-ENDCLASS(XonoticServerListTab)
-entity makeXonoticServerListTab();
+CLASS(XonoticJoinTab, XonoticTab)
+       METHOD(XonoticJoinTab, fill, void(entity))
+       ATTRIB(XonoticJoinTab, title, string, _("Join"))
+       ATTRIB(XonoticJoinTab, intendedWidth, float, 0.9)
+       ATTRIB(XonoticJoinTab, rows, float, 23)
+       ATTRIB(XonoticJoinTab, columns, float, 3)
+       ATTRIB(XonoticJoinTab, name, string, "Join")
+ENDCLASS(XonoticJoinTab)
+entity makeXonoticJoinTab();
 #endif
 
 #ifdef IMPLEMENTATION
-
-entity makeXonoticServerListTab()
+entity makeXonoticJoinTab()
 {
        entity me;
-       me = NEW(XonoticServerListTab);
+       me = NEW(XonoticJoinTab);
        me.configureDialog(me);
        return me;
 }
-void XonoticServerListTab_fill(entity me)
+void XonoticJoinTab_fill(entity me)
 {
-       entity e, slist;
-
-       slist  = makeXonoticServerList();
+       entity mc, e;
+       mc = makeXonoticTabController(me.rows - 2);
 
        me.gotoRC(me, 0.5, 0);
-               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(1, _("Filter:")));
-               me.TD(me, 1, 2.8, e = makeXonoticInputBox(0, string_null));
-                       e.onChange = ServerList_Filter_Change;
-                       e.onChangeEntity = slist;
-                       slist.controlledTextbox = e;
-
-       me.gotoRC(me, 0.5, 3.6);
-               me.TD(me, 1, 0.9, e = makeXonoticCheckBox(0, "menu_slist_categories", ZCTX(_("SRVS^Categories"))));
-                       e.onClickEntity = slist;
-                       e.onClick = ServerList_Categories_Click;
-               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "menu_slist_showempty", ZCTX(_("SRVS^Empty"))));
-                       slist.filterShowEmpty = e.checked;
-                       e.onClickEntity = slist;
-                       e.onClick = ServerList_ShowEmpty_Click;
-               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "menu_slist_showfull", ZCTX(_("SRVS^Full"))));
-                       slist.filterShowFull = e.checked;
-                       e.onClickEntity = slist;
-                       e.onClick = ServerList_ShowFull_Click;
-               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "net_slist_pause", _("Pause")));
-
-       me.gotoRC(me, 2, 0);
-               me.TD(me, 1, 1, slist.sortButton1 = makeXonoticButton(string_null, '0 0 0'));
-               me.TD(me, 1, 1, slist.sortButton2 = makeXonoticButton(string_null, '0 0 0'));
-               me.TD(me, 1, 1, slist.sortButton3 = makeXonoticButton(string_null, '0 0 0'));
-               me.TD(me, 1, 1, slist.sortButton4 = makeXonoticButton(string_null, '0 0 0'));
-               me.TD(me, 1, 1, slist.sortButton5 = makeXonoticButton(string_null, '0 0 0'));
-       me.TR(me);
-               me.TD(me, me.rows - 5, me.columns, slist);
+               me.TD(me, 1, 1.5, e = mc.makeTabButton(mc, _("Public"), makeXonoticServerListTab()));
+               me.TD(me, 1, 1.5, e = mc.makeTabButton(mc, _("Private"), makeXonoticPrivateServerListTab()));
 
-       me.gotoRC(me, me.rows - 2, 0);
-               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(0, _("Address:")));
-               me.TD(me, 1, 2.9, e = makeXonoticInputBox(0, string_null));
-                       e.onEnter = ServerList_Connect_Click;
-                       e.onEnterEntity = slist;
-                       e.onChange = ServerList_Update_favoriteButton;
-                       e.onChangeEntity = slist;
-                       slist.ipAddressBox = e;
-               me.TD(me, 1, 1.5, e = makeXonoticButton("", '0 0 0'));
-                       e.onClick = ServerList_Favorite_Click;
-                       e.onClickEntity = slist;
-                       slist.favoriteButton = e;
-               me.TD(me, 1, 1.5, e = makeXonoticButton(_("Info..."), '0 0 0'));
-                       e.onClick = ServerList_Info_Click;
-                       e.onClickEntity = slist;
-                       slist.infoButton = e;
-       me.TR(me);
-               me.TD(me, 1, me.columns, e = makeXonoticButton(_("Join!"), '0 0 0'));
-                       e.onClick = ServerList_Connect_Click;
-                       e.onClickEntity = slist;
-                       slist.connectButton = e;
+       me.gotoRC(me, 3, 0);
+               me.TD(me, me.rows - 2, me.columns, mc);
 }
 #endif
diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_private.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join_private.qc
new file mode 100644 (file)
index 0000000..c884642
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef DIALOG_MULTIPLAYER_JOIN_PRIVATE_H
+#define DIALOG_MULTIPLAYER_JOIN_PRIVATE_H
+#include "tab.qc"
+CLASS(XonoticPrivateServerListTab, XonoticTab)
+       METHOD(XonoticPrivateServerListTab, fill, void(entity))
+       ATTRIB(XonoticPrivateServerListTab, title, string, _("Private"))
+       ATTRIB(XonoticPrivateServerListTab, intendedWidth, float, 0.9)
+       ATTRIB(XonoticPrivateServerListTab, rows, float, 21)
+       ATTRIB(XonoticPrivateServerListTab, columns, float, 6.5)
+ENDCLASS(XonoticPrivateServerListTab)
+entity makeXonoticPrivateServerListTab();
+#endif
+
+#ifdef IMPLEMENTATION
+
+entity makeXonoticPrivateServerListTab()
+{
+       entity me;
+       me = NEW(XonoticPrivateServerListTab);
+       me.configureDialog(me);
+       return me;
+}
+void XonoticPrivateServerListTab_fill(entity me)
+{
+       //entity e, slist;
+       entity e;
+
+       //slist  = makeXonoticPrivateServerList();
+
+       me.gotoRC(me, 0.5, 0);
+               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(1, _("Filter:")));
+               me.TD(me, 1, 2.8, e = makeXonoticInputBox(0, string_null));
+                       //e.onChange = PrivateServerList_Filter_Change;
+                       //e.onChangeEntity = slist;
+                       //slist.controlledTextbox = e;
+
+       me.gotoRC(me, 0.5, 3.6);
+               me.TD(me, 1, 0.9, e = makeXonoticCheckBox(0, "menu_slist_categories", ZCTX(_("SRVS^Categories"))));
+                       //e.onClickEntity = slist;
+                       //e.onClick = PrivateServerList_Categories_Click;
+               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "menu_slist_showempty", ZCTX(_("SRVS^Empty"))));
+                       //slist.filterShowEmpty = e.checked;
+                       //e.onClickEntity = slist;
+                       //e.onClick = PrivateServerList_ShowEmpty_Click;
+               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "menu_slist_showfull", ZCTX(_("SRVS^Full"))));
+                       //slist.filterShowFull = e.checked;
+                       //e.onClickEntity = slist;
+                       //e.onClick = PrivateServerList_ShowFull_Click;
+               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "net_slist_pause", _("Pause")));
+
+       me.gotoRC(me, 2, 0);
+               //me.TD(me, 1, 1, slist.sortButton1 = makeXonoticButton(string_null, '0 0 0'));
+               //me.TD(me, 1, 1, slist.sortButton2 = makeXonoticButton(string_null, '0 0 0'));
+               //me.TD(me, 1, 1, slist.sortButton3 = makeXonoticButton(string_null, '0 0 0'));
+               //me.TD(me, 1, 1, slist.sortButton4 = makeXonoticButton(string_null, '0 0 0'));
+               //me.TD(me, 1, 1, slist.sortButton5 = makeXonoticButton(string_null, '0 0 0'));
+       me.TR(me);
+               //me.TD(me, me.rows - 5, me.columns, slist);
+
+       me.gotoRC(me, me.rows - 2, 0);
+               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(0, _("Address:")));
+               me.TD(me, 1, 2.9, e = makeXonoticInputBox(0, string_null));
+                       //e.onEnter = PrivateServerList_Connect_Click;
+                       //e.onEnterEntity = slist;
+                       //e.onChange = PrivateServerList_Update_favoriteButton;
+                       //e.onChangeEntity = slist;
+                       //slist.ipAddressBox = e;
+               me.TD(me, 1, 1.5, e = makeXonoticButton("", '0 0 0'));
+                       //e.onClick = PrivateServerList_Favorite_Click;
+                       //e.onClickEntity = slist;
+                       //slist.favoriteButton = e;
+               me.TD(me, 1, 1.5, e = makeXonoticButton(_("Info..."), '0 0 0'));
+                       //e.onClick = PrivateServerList_Info_Click;
+                       //e.onClickEntity = slist;
+                       //slist.infoButton = e;
+       me.TR(me);
+               me.TD(me, 1, me.columns, e = makeXonoticButton(_("Join!"), '0 0 0'));
+                       //e.onClick = PrivateServerList_Connect_Click;
+                       //e.onClickEntity = slist;
+                       //slist.connectButton = e;
+}
+#endif
diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_public.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join_public.qc
new file mode 100644 (file)
index 0000000..8996bf3
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef DIALOG_MULTIPLAYER_JOIN_PUBLIC_H
+#define DIALOG_MULTIPLAYER_JOIN_PUBLIC_H
+#include "tab.qc"
+CLASS(XonoticServerListTab, XonoticTab)
+       METHOD(XonoticServerListTab, fill, void(entity))
+       ATTRIB(XonoticServerListTab, title, string, _("Public"))
+       ATTRIB(XonoticServerListTab, intendedWidth, float, 0.9)
+       ATTRIB(XonoticServerListTab, rows, float, 21)
+       ATTRIB(XonoticServerListTab, columns, float, 6.5)
+ENDCLASS(XonoticServerListTab)
+entity makeXonoticServerListTab();
+#endif
+
+#ifdef IMPLEMENTATION
+
+entity makeXonoticServerListTab()
+{
+       entity me;
+       me = NEW(XonoticServerListTab);
+       me.configureDialog(me);
+       return me;
+}
+void XonoticServerListTab_fill(entity me)
+{
+       entity e, slist;
+
+       slist  = makeXonoticServerList();
+
+       me.gotoRC(me, 0.5, 0);
+               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(1, _("Filter:")));
+               me.TD(me, 1, 2.8, e = makeXonoticInputBox(0, string_null));
+                       e.onChange = ServerList_Filter_Change;
+                       e.onChangeEntity = slist;
+                       slist.controlledTextbox = e;
+
+       me.gotoRC(me, 0.5, 3.6);
+               me.TD(me, 1, 0.9, e = makeXonoticCheckBox(0, "menu_slist_categories", ZCTX(_("SRVS^Categories"))));
+                       e.onClickEntity = slist;
+                       e.onClick = ServerList_Categories_Click;
+               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "menu_slist_showempty", ZCTX(_("SRVS^Empty"))));
+                       slist.filterShowEmpty = e.checked;
+                       e.onClickEntity = slist;
+                       e.onClick = ServerList_ShowEmpty_Click;
+               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "menu_slist_showfull", ZCTX(_("SRVS^Full"))));
+                       slist.filterShowFull = e.checked;
+                       e.onClickEntity = slist;
+                       e.onClick = ServerList_ShowFull_Click;
+               me.TD(me, 1, 0.6, e = makeXonoticCheckBox(0, "net_slist_pause", _("Pause")));
+
+       me.gotoRC(me, 2, 0);
+               me.TD(me, 1, 1, slist.sortButton1 = makeXonoticButton(string_null, '0 0 0'));
+               me.TD(me, 1, 1, slist.sortButton2 = makeXonoticButton(string_null, '0 0 0'));
+               me.TD(me, 1, 1, slist.sortButton3 = makeXonoticButton(string_null, '0 0 0'));
+               me.TD(me, 1, 1, slist.sortButton4 = makeXonoticButton(string_null, '0 0 0'));
+               me.TD(me, 1, 1, slist.sortButton5 = makeXonoticButton(string_null, '0 0 0'));
+       me.TR(me);
+               me.TD(me, me.rows - 5, me.columns, slist);
+
+       me.gotoRC(me, me.rows - 2, 0);
+               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(0, _("Address:")));
+               me.TD(me, 1, 2.9, e = makeXonoticInputBox(0, string_null));
+                       e.onEnter = ServerList_Connect_Click;
+                       e.onEnterEntity = slist;
+                       e.onChange = ServerList_Update_favoriteButton;
+                       e.onChangeEntity = slist;
+                       slist.ipAddressBox = e;
+               me.TD(me, 1, 1.5, e = makeXonoticButton("", '0 0 0'));
+                       e.onClick = ServerList_Favorite_Click;
+                       e.onClickEntity = slist;
+                       slist.favoriteButton = e;
+               me.TD(me, 1, 1.5, e = makeXonoticButton(_("Info..."), '0 0 0'));
+                       e.onClick = ServerList_Info_Click;
+                       e.onClickEntity = slist;
+                       slist.infoButton = e;
+       me.TR(me);
+               me.TD(me, 1, me.columns, e = makeXonoticButton(_("Join!"), '0 0 0'));
+                       e.onClick = ServerList_Connect_Click;
+                       e.onClickEntity = slist;
+                       slist.connectButton = e;
+}
+#endif
diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_public_serverinfo.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join_public_serverinfo.qc
new file mode 100644 (file)
index 0000000..a5ad633
--- /dev/null
@@ -0,0 +1,349 @@
+#include "../../common/mapinfo.qh"
+
+#ifndef DIALOG_MULTIPLAYER_JOIN_SERVERINFO_H
+#define DIALOG_MULTIPLAYER_JOIN_SERVERINFO_H
+#include "dialog.qc"
+CLASS(XonoticServerInfoDialog, XonoticDialog)
+       METHOD(XonoticServerInfoDialog, fill, void(entity))
+       METHOD(XonoticServerInfoDialog, loadServerInfo, void(entity, float))
+       ATTRIB(XonoticServerInfoDialog, title, string, _("Server Information"))
+       ATTRIB(XonoticServerInfoDialog, color, vector, SKINCOLOR_DIALOG_SERVERINFO)
+       ATTRIB(XonoticServerInfoDialog, intendedWidth, float, 0.8)
+       ATTRIB(XonoticServerInfoDialog, rows, float, 18)
+       ATTRIB(XonoticServerInfoDialog, columns, float, 6.2)
+
+       ATTRIB(XonoticServerInfoDialog, currentServerName, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerCName, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerType, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerMap, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerPlayers, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerNumPlayers, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerNumBots, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerNumFreeSlots, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerMod, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerVersion, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerKey, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerID, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerEncrypt, string, string_null)
+       ATTRIB(XonoticServerInfoDialog, currentServerPure, string, string_null)
+
+       ATTRIB(XonoticServerInfoDialog, nameLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, cnameLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, typeLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, mapLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, rawPlayerList, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, numPlayersLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, numBotsLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, numFreeSlotsLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, modLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, versionLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, keyLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, idLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, encryptLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, canConnectLabel, entity, NULL)
+       ATTRIB(XonoticServerInfoDialog, pureLabel, entity, NULL)
+ENDCLASS(XonoticServerInfoDialog)
+
+void Join_Click(entity btn, entity me);
+#endif
+
+#ifdef IMPLEMENTATION
+void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
+{
+       float m, pure, freeslots, j, numh, maxp, numb, sflags;
+       string s, typestr, versionstr, k, v, modname;
+
+       // ====================================
+       //  First clear and unzone the strings
+       // ====================================
+       if(me.currentServerName)
+               strunzone(me.currentServerName);
+       me.currentServerName = string_null;
+
+       if(me.currentServerCName)
+               strunzone(me.currentServerCName);
+       me.currentServerCName = string_null;
+
+       if(me.currentServerType)
+               strunzone(me.currentServerType);
+       me.currentServerType = string_null;
+
+       if(me.currentServerMap)
+               strunzone(me.currentServerMap);
+       me.currentServerMap = string_null;
+
+       if(me.currentServerPlayers)
+               strunzone(me.currentServerPlayers);
+       me.currentServerPlayers = string_null;
+
+       if(me.currentServerNumPlayers)
+               strunzone(me.currentServerNumPlayers);
+       me.currentServerNumPlayers = string_null;
+
+       if(me.currentServerNumBots)
+               strunzone(me.currentServerNumBots);
+       me.currentServerNumBots = string_null;
+
+       if(me.currentServerNumFreeSlots)
+               strunzone(me.currentServerNumFreeSlots);
+       me.currentServerNumFreeSlots = string_null;
+
+       if(me.currentServerMod)
+               strunzone(me.currentServerMod);
+       me.currentServerMod = string_null;
+
+       if(me.currentServerVersion)
+               strunzone(me.currentServerVersion);
+       me.currentServerVersion = string_null;
+
+       // not zoned!
+       //if(me.currentServerEncrypt)
+       //      strunzone(me.currentServerEncrypt);
+       //me.currentServerEncrypt = string_null;
+       if(me.currentServerPure)
+               strunzone(me.currentServerPure);
+       me.currentServerPure = string_null;
+
+       if(me.currentServerKey)
+               strunzone(me.currentServerKey);
+       me.currentServerKey = string_null;
+
+       if(me.currentServerID)
+               strunzone(me.currentServerID);
+       me.currentServerID = string_null;
+
+       // ==========================
+       //  Now, fill in the strings
+       // ==========================
+       me.currentServerName = strzone(gethostcachestring(SLIST_FIELD_NAME, i));
+       me.nameLabel.setText(me.nameLabel, me.currentServerName);
+
+       me.currentServerCName = strzone(gethostcachestring(SLIST_FIELD_CNAME, i));
+       me.cnameLabel.setText(me.cnameLabel, me.currentServerCName);
+
+       pure = -1;
+       typestr = _("N/A");
+       versionstr = _("N/A");
+
+       s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
+       m = tokenizebyseparator(s, ":");
+       if(m >= 2)
+       {
+               typestr = argv(0);
+               versionstr = argv(1);
+       }
+       freeslots = -1;
+       sflags = -1;
+       modname = "";
+       for(j = 2; j < m; ++j)
+       {
+               if(argv(j) == "")
+                       break;
+               k = substring(argv(j), 0, 1);
+               v = substring(argv(j), 1, -1);
+               if(k == "P")
+                       pure = stof(v);
+               else if(k == "S")
+                       freeslots = stof(v);
+               else if(k == "F")
+                       sflags = stof(v);
+               else if(k == "M")
+                       modname = v;
+       }
+
+#ifdef COMPAT_NO_MOD_IS_XONOTIC
+       if(modname == "")
+               modname = "Xonotic";
+#endif
+
+       s = gethostcachestring(SLIST_FIELD_MOD, i);
+       if(s != "data")
+               modname = sprintf("%s (%s)", modname, s);
+
+       j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type
+       if(j) { typestr = MapInfo_Type_ToText(j); } // only set it if we actually found it
+
+       me.currentServerType = strzone(typestr);
+       me.typeLabel.setText(me.typeLabel, me.currentServerType);
+
+       me.currentServerMap = strzone(gethostcachestring(SLIST_FIELD_MAP, i));
+       me.mapLabel.setText(me.mapLabel, me.currentServerMap);
+
+       me.currentServerPlayers = strzone(gethostcachestring(SLIST_FIELD_PLAYERS, i));
+       me.rawPlayerList.setPlayerList(me.rawPlayerList, me.currentServerPlayers);
+
+       numh = gethostcachenumber(SLIST_FIELD_NUMHUMANS, i);
+       maxp = gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i);
+       numb = gethostcachenumber(SLIST_FIELD_NUMBOTS, i);
+       me.currentServerNumPlayers = strzone(sprintf("%d/%d", numh, maxp));
+       me.numPlayersLabel.setText(me.numPlayersLabel, me.currentServerNumPlayers);
+
+       s = ftos(numb);
+       me.currentServerNumBots = strzone(s);
+       me.numBotsLabel.setText(me.numBotsLabel, me.currentServerNumBots);
+
+       if(freeslots < 0) { freeslots = maxp - numh - numb; }
+       s = ftos(freeslots);
+       me.currentServerNumFreeSlots = strzone(s);
+       me.numFreeSlotsLabel.setText(me.numFreeSlotsLabel, me.currentServerNumFreeSlots);
+
+       me.currentServerMod = ((modname == "Xonotic") ? _("Default") : modname);
+       me.currentServerMod = strzone(me.currentServerMod);
+       me.modLabel.setText(me.modLabel, me.currentServerMod);
+
+       me.currentServerVersion = strzone(versionstr);
+       me.versionLabel.setText(me.versionLabel, me.currentServerVersion);
+
+       me.currentServerPure = ((pure < 0) ? _("N/A") : (pure == 0) ? _("Official") : sprintf(_("%d modified"), pure));
+       me.currentServerPure = strzone(me.currentServerPure);
+       me.pureLabel.setText(me.pureLabel, me.currentServerPure);
+
+       s = crypto_getencryptlevel(me.currentServerCName);
+       if(s == "")
+       {
+               if(cvar("crypto_aeslevel") >= 3)
+                       me.currentServerEncrypt = _("N/A (auth library missing, can't connect)");
+               else
+                       me.currentServerEncrypt = _("N/A (auth library missing)");
+       }
+       else switch(stof(substring(s, 0, 1)))
+       {
+               case 0:
+                       if(cvar("crypto_aeslevel") >= 3)
+                               me.currentServerEncrypt = _("Not supported (can't connect)");
+                       else
+                               me.currentServerEncrypt = _("Not supported (won't encrypt)");
+                       break;
+               case 1:
+                       if(cvar("crypto_aeslevel") >= 2)
+                               me.currentServerEncrypt = _("Supported (will encrypt)");
+                       else
+                               me.currentServerEncrypt = _("Supported (won't encrypt)");
+                       break;
+               case 2:
+                       if(cvar("crypto_aeslevel") >= 1)
+                               me.currentServerEncrypt = _("Requested (will encrypt)");
+                       else
+                               me.currentServerEncrypt = _("Requested (won't encrypt)");
+                       break;
+               case 3:
+                       if(cvar("crypto_aeslevel") <= 0)
+                               me.currentServerEncrypt = _("Required (can't connect)");
+                       else
+                               me.currentServerEncrypt = _("Required (will encrypt)");
+                       break;
+       }
+       me.encryptLabel.setText(me.encryptLabel, me.currentServerEncrypt);
+
+       s = crypto_getidfp(me.currentServerCName);
+       if (!s) { s = _("N/A"); }
+       me.currentServerID = strzone(s);
+       me.idLabel.setText(me.idLabel, me.currentServerID);
+
+       s = crypto_getkeyfp(me.currentServerCName);
+       if (!s) { s = _("N/A"); }
+       me.currentServerKey = strzone(s);
+       me.keyLabel.setText(me.keyLabel, me.currentServerKey);
+}
+
+void XonoticServerInfoDialog_fill(entity me)
+{
+       entity e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Hostname:")));
+               me.TD(me, 1, 4.6, e = makeXonoticTextLabel(0.5, ""));
+               e.colorL = SKINCOLOR_SERVERINFO_NAME;
+               e.allowCut = 1;
+               me.nameLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Address:")));
+               me.TD(me, 1, 4.6, e = makeXonoticTextLabel(0.5, ""));
+               e.colorL = SKINCOLOR_SERVERINFO_IP;
+               e.allowCut = 1;
+               me.cnameLabel = e;
+
+       me.TR(me);
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Gametype:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.typeLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Map:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.mapLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Mod:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.modLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Version:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.versionLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Settings:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.pureLabel = e;
+
+       me.TR(me);
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Players:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.numPlayersLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Bots:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.numBotsLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Free slots:")));
+               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
+               e.allowCut = 1;
+               me.numFreeSlotsLabel = e;
+
+       me.gotoRC(me, me.rows - 5, 0);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Encryption:")));
+               me.TD(me, 1, 5.4, e = makeXonoticTextLabel(0, ""));
+                       e.allowCut = 1;
+                       me.encryptLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("ID:")));
+               me.TD(me, 1, 5.4, e = makeXonoticTextLabel(0, ""));
+                       e.allowCut = 1;
+                       me.keyLabel = e;
+       me.TR(me);
+               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Key:")));
+               me.TD(me, 1, 5.4, e = makeXonoticTextLabel(0, ""));
+                       e.allowCut = 1;
+                       me.idLabel = e;
+
+       me.gotoRC(me, 2, 2.2); me.setFirstColumn(me, me.currentColumn);
+               me.TD(me, 1, 3, e = makeXonoticTextLabel(0, _("Players:")));
+       me.TR(me);
+               me.TD(me, me.rows - 8, 4, e = makeXonoticPlayerList());
+                       me.rawPlayerList = e;
+
+       me.gotoRC(me, me.rows - 1, 0);
+               me.TD(me, 1, me.columns/2, e = makeXonoticButton(_("Close"), '0 0 0'));
+                       e.onClick = Dialog_Close;
+                       e.onClickEntity = me;
+               //me.TD(me, 1, me.columns/3, e = makeXonoticButton("", '0 0 0')); // TODO: Add bookmark button here
+               //      e.onClick = ServerList_Favorite_Click;
+               //      e.onClickEntity = slist;
+               //      slist.favoriteButton = e;
+               me.TD(me, 1, me.columns/2, e = makeXonoticButton(_("Join!"), '0 0 0'));
+                       e.onClick = Join_Click;
+                       e.onClickEntity = me;
+}
+
+void Join_Click(entity btn, entity me)
+{
+       localcmd("connect ", me.currentServerCName, "\n");
+}
+
+#endif
diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc
deleted file mode 100644 (file)
index a5ad633..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-#include "../../common/mapinfo.qh"
-
-#ifndef DIALOG_MULTIPLAYER_JOIN_SERVERINFO_H
-#define DIALOG_MULTIPLAYER_JOIN_SERVERINFO_H
-#include "dialog.qc"
-CLASS(XonoticServerInfoDialog, XonoticDialog)
-       METHOD(XonoticServerInfoDialog, fill, void(entity))
-       METHOD(XonoticServerInfoDialog, loadServerInfo, void(entity, float))
-       ATTRIB(XonoticServerInfoDialog, title, string, _("Server Information"))
-       ATTRIB(XonoticServerInfoDialog, color, vector, SKINCOLOR_DIALOG_SERVERINFO)
-       ATTRIB(XonoticServerInfoDialog, intendedWidth, float, 0.8)
-       ATTRIB(XonoticServerInfoDialog, rows, float, 18)
-       ATTRIB(XonoticServerInfoDialog, columns, float, 6.2)
-
-       ATTRIB(XonoticServerInfoDialog, currentServerName, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerCName, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerType, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerMap, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerPlayers, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerNumPlayers, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerNumBots, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerNumFreeSlots, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerMod, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerVersion, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerKey, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerID, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerEncrypt, string, string_null)
-       ATTRIB(XonoticServerInfoDialog, currentServerPure, string, string_null)
-
-       ATTRIB(XonoticServerInfoDialog, nameLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, cnameLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, typeLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, mapLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, rawPlayerList, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, numPlayersLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, numBotsLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, numFreeSlotsLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, modLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, versionLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, keyLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, idLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, encryptLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, canConnectLabel, entity, NULL)
-       ATTRIB(XonoticServerInfoDialog, pureLabel, entity, NULL)
-ENDCLASS(XonoticServerInfoDialog)
-
-void Join_Click(entity btn, entity me);
-#endif
-
-#ifdef IMPLEMENTATION
-void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
-{
-       float m, pure, freeslots, j, numh, maxp, numb, sflags;
-       string s, typestr, versionstr, k, v, modname;
-
-       // ====================================
-       //  First clear and unzone the strings
-       // ====================================
-       if(me.currentServerName)
-               strunzone(me.currentServerName);
-       me.currentServerName = string_null;
-
-       if(me.currentServerCName)
-               strunzone(me.currentServerCName);
-       me.currentServerCName = string_null;
-
-       if(me.currentServerType)
-               strunzone(me.currentServerType);
-       me.currentServerType = string_null;
-
-       if(me.currentServerMap)
-               strunzone(me.currentServerMap);
-       me.currentServerMap = string_null;
-
-       if(me.currentServerPlayers)
-               strunzone(me.currentServerPlayers);
-       me.currentServerPlayers = string_null;
-
-       if(me.currentServerNumPlayers)
-               strunzone(me.currentServerNumPlayers);
-       me.currentServerNumPlayers = string_null;
-
-       if(me.currentServerNumBots)
-               strunzone(me.currentServerNumBots);
-       me.currentServerNumBots = string_null;
-
-       if(me.currentServerNumFreeSlots)
-               strunzone(me.currentServerNumFreeSlots);
-       me.currentServerNumFreeSlots = string_null;
-
-       if(me.currentServerMod)
-               strunzone(me.currentServerMod);
-       me.currentServerMod = string_null;
-
-       if(me.currentServerVersion)
-               strunzone(me.currentServerVersion);
-       me.currentServerVersion = string_null;
-
-       // not zoned!
-       //if(me.currentServerEncrypt)
-       //      strunzone(me.currentServerEncrypt);
-       //me.currentServerEncrypt = string_null;
-       if(me.currentServerPure)
-               strunzone(me.currentServerPure);
-       me.currentServerPure = string_null;
-
-       if(me.currentServerKey)
-               strunzone(me.currentServerKey);
-       me.currentServerKey = string_null;
-
-       if(me.currentServerID)
-               strunzone(me.currentServerID);
-       me.currentServerID = string_null;
-
-       // ==========================
-       //  Now, fill in the strings
-       // ==========================
-       me.currentServerName = strzone(gethostcachestring(SLIST_FIELD_NAME, i));
-       me.nameLabel.setText(me.nameLabel, me.currentServerName);
-
-       me.currentServerCName = strzone(gethostcachestring(SLIST_FIELD_CNAME, i));
-       me.cnameLabel.setText(me.cnameLabel, me.currentServerCName);
-
-       pure = -1;
-       typestr = _("N/A");
-       versionstr = _("N/A");
-
-       s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
-       m = tokenizebyseparator(s, ":");
-       if(m >= 2)
-       {
-               typestr = argv(0);
-               versionstr = argv(1);
-       }
-       freeslots = -1;
-       sflags = -1;
-       modname = "";
-       for(j = 2; j < m; ++j)
-       {
-               if(argv(j) == "")
-                       break;
-               k = substring(argv(j), 0, 1);
-               v = substring(argv(j), 1, -1);
-               if(k == "P")
-                       pure = stof(v);
-               else if(k == "S")
-                       freeslots = stof(v);
-               else if(k == "F")
-                       sflags = stof(v);
-               else if(k == "M")
-                       modname = v;
-       }
-
-#ifdef COMPAT_NO_MOD_IS_XONOTIC
-       if(modname == "")
-               modname = "Xonotic";
-#endif
-
-       s = gethostcachestring(SLIST_FIELD_MOD, i);
-       if(s != "data")
-               modname = sprintf("%s (%s)", modname, s);
-
-       j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type
-       if(j) { typestr = MapInfo_Type_ToText(j); } // only set it if we actually found it
-
-       me.currentServerType = strzone(typestr);
-       me.typeLabel.setText(me.typeLabel, me.currentServerType);
-
-       me.currentServerMap = strzone(gethostcachestring(SLIST_FIELD_MAP, i));
-       me.mapLabel.setText(me.mapLabel, me.currentServerMap);
-
-       me.currentServerPlayers = strzone(gethostcachestring(SLIST_FIELD_PLAYERS, i));
-       me.rawPlayerList.setPlayerList(me.rawPlayerList, me.currentServerPlayers);
-
-       numh = gethostcachenumber(SLIST_FIELD_NUMHUMANS, i);
-       maxp = gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i);
-       numb = gethostcachenumber(SLIST_FIELD_NUMBOTS, i);
-       me.currentServerNumPlayers = strzone(sprintf("%d/%d", numh, maxp));
-       me.numPlayersLabel.setText(me.numPlayersLabel, me.currentServerNumPlayers);
-
-       s = ftos(numb);
-       me.currentServerNumBots = strzone(s);
-       me.numBotsLabel.setText(me.numBotsLabel, me.currentServerNumBots);
-
-       if(freeslots < 0) { freeslots = maxp - numh - numb; }
-       s = ftos(freeslots);
-       me.currentServerNumFreeSlots = strzone(s);
-       me.numFreeSlotsLabel.setText(me.numFreeSlotsLabel, me.currentServerNumFreeSlots);
-
-       me.currentServerMod = ((modname == "Xonotic") ? _("Default") : modname);
-       me.currentServerMod = strzone(me.currentServerMod);
-       me.modLabel.setText(me.modLabel, me.currentServerMod);
-
-       me.currentServerVersion = strzone(versionstr);
-       me.versionLabel.setText(me.versionLabel, me.currentServerVersion);
-
-       me.currentServerPure = ((pure < 0) ? _("N/A") : (pure == 0) ? _("Official") : sprintf(_("%d modified"), pure));
-       me.currentServerPure = strzone(me.currentServerPure);
-       me.pureLabel.setText(me.pureLabel, me.currentServerPure);
-
-       s = crypto_getencryptlevel(me.currentServerCName);
-       if(s == "")
-       {
-               if(cvar("crypto_aeslevel") >= 3)
-                       me.currentServerEncrypt = _("N/A (auth library missing, can't connect)");
-               else
-                       me.currentServerEncrypt = _("N/A (auth library missing)");
-       }
-       else switch(stof(substring(s, 0, 1)))
-       {
-               case 0:
-                       if(cvar("crypto_aeslevel") >= 3)
-                               me.currentServerEncrypt = _("Not supported (can't connect)");
-                       else
-                               me.currentServerEncrypt = _("Not supported (won't encrypt)");
-                       break;
-               case 1:
-                       if(cvar("crypto_aeslevel") >= 2)
-                               me.currentServerEncrypt = _("Supported (will encrypt)");
-                       else
-                               me.currentServerEncrypt = _("Supported (won't encrypt)");
-                       break;
-               case 2:
-                       if(cvar("crypto_aeslevel") >= 1)
-                               me.currentServerEncrypt = _("Requested (will encrypt)");
-                       else
-                               me.currentServerEncrypt = _("Requested (won't encrypt)");
-                       break;
-               case 3:
-                       if(cvar("crypto_aeslevel") <= 0)
-                               me.currentServerEncrypt = _("Required (can't connect)");
-                       else
-                               me.currentServerEncrypt = _("Required (will encrypt)");
-                       break;
-       }
-       me.encryptLabel.setText(me.encryptLabel, me.currentServerEncrypt);
-
-       s = crypto_getidfp(me.currentServerCName);
-       if (!s) { s = _("N/A"); }
-       me.currentServerID = strzone(s);
-       me.idLabel.setText(me.idLabel, me.currentServerID);
-
-       s = crypto_getkeyfp(me.currentServerCName);
-       if (!s) { s = _("N/A"); }
-       me.currentServerKey = strzone(s);
-       me.keyLabel.setText(me.keyLabel, me.currentServerKey);
-}
-
-void XonoticServerInfoDialog_fill(entity me)
-{
-       entity e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Hostname:")));
-               me.TD(me, 1, 4.6, e = makeXonoticTextLabel(0.5, ""));
-               e.colorL = SKINCOLOR_SERVERINFO_NAME;
-               e.allowCut = 1;
-               me.nameLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Address:")));
-               me.TD(me, 1, 4.6, e = makeXonoticTextLabel(0.5, ""));
-               e.colorL = SKINCOLOR_SERVERINFO_IP;
-               e.allowCut = 1;
-               me.cnameLabel = e;
-
-       me.TR(me);
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Gametype:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.typeLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Map:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.mapLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Mod:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.modLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Version:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.versionLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Settings:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.pureLabel = e;
-
-       me.TR(me);
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Players:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.numPlayersLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Bots:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.numBotsLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Free slots:")));
-               me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, ""));
-               e.allowCut = 1;
-               me.numFreeSlotsLabel = e;
-
-       me.gotoRC(me, me.rows - 5, 0);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Encryption:")));
-               me.TD(me, 1, 5.4, e = makeXonoticTextLabel(0, ""));
-                       e.allowCut = 1;
-                       me.encryptLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("ID:")));
-               me.TD(me, 1, 5.4, e = makeXonoticTextLabel(0, ""));
-                       e.allowCut = 1;
-                       me.keyLabel = e;
-       me.TR(me);
-               me.TD(me, 1, 0.8, e = makeXonoticTextLabel(0, _("Key:")));
-               me.TD(me, 1, 5.4, e = makeXonoticTextLabel(0, ""));
-                       e.allowCut = 1;
-                       me.idLabel = e;
-
-       me.gotoRC(me, 2, 2.2); me.setFirstColumn(me, me.currentColumn);
-               me.TD(me, 1, 3, e = makeXonoticTextLabel(0, _("Players:")));
-       me.TR(me);
-               me.TD(me, me.rows - 8, 4, e = makeXonoticPlayerList());
-                       me.rawPlayerList = e;
-
-       me.gotoRC(me, me.rows - 1, 0);
-               me.TD(me, 1, me.columns/2, e = makeXonoticButton(_("Close"), '0 0 0'));
-                       e.onClick = Dialog_Close;
-                       e.onClickEntity = me;
-               //me.TD(me, 1, me.columns/3, e = makeXonoticButton("", '0 0 0')); // TODO: Add bookmark button here
-               //      e.onClick = ServerList_Favorite_Click;
-               //      e.onClickEntity = slist;
-               //      slist.favoriteButton = e;
-               me.TD(me, 1, me.columns/2, e = makeXonoticButton(_("Join!"), '0 0 0'));
-                       e.onClick = Join_Click;
-                       e.onClickEntity = me;
-}
-
-void Join_Click(entity btn, entity me)
-{
-       localcmd("connect ", me.currentServerCName, "\n");
-}
-
-#endif
diff --git a/qcsrc/menu/xonotic/privateserverlist.qc b/qcsrc/menu/xonotic/privateserverlist.qc
new file mode 100644 (file)
index 0000000..c68597d
--- /dev/null
@@ -0,0 +1,1324 @@
+//#ifndef SERVERLIST_H
+//#define SERVERLIST_H
+//#include "listbox.qc"
+//CLASS(XonoticPrivateServerList, XonoticListBox)
+//     METHOD(XonoticPrivateServerList, configureXonoticPrivateServerList, void(entity))
+//     ATTRIB(XonoticPrivateServerList, rowsPerItem, float, 1)
+//     METHOD(XonoticPrivateServerList, draw, void(entity))
+//     METHOD(XonoticPrivateServerList, drawListBoxItem, void(entity, float, vector, float))
+//     METHOD(XonoticPrivateServerList, doubleClickListBoxItem, void(entity, float, vector))
+//     METHOD(XonoticPrivateServerList, resizeNotify, void(entity, vector, vector, vector, vector))
+//     METHOD(XonoticPrivateServerList, keyDown, float(entity, float, float, float))
+//     METHOD(XonoticPrivateServerList, toggleFavorite, void(entity, string))
+//
+//     ATTRIB(XonoticPrivateServerList, iconsSizeFactor, float, 0.85)
+//
+//     ATTRIB(XonoticPrivateServerList, realFontSize, vector, '0 0 0')
+//     ATTRIB(XonoticPrivateServerList, realUpperMargin, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnIconsOrigin, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnIconsSize, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnPingOrigin, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnPingSize, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnNameOrigin, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnNameSize, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnMapOrigin, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnMapSize, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnTypeOrigin, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnTypeSize, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnPlayersOrigin, float, 0)
+//     ATTRIB(XonoticPrivateServerList, columnPlayersSize, float, 0)
+//
+//     ATTRIB(XonoticPrivateServerList, selectedServer, string, string_null) // to restore selected server when needed
+//     METHOD(XonoticPrivateServerList, setSelected, void(entity, float))
+//     METHOD(XonoticPrivateServerList, setSortOrder, void(entity, float, float))
+//     ATTRIB(XonoticPrivateServerList, filterShowEmpty, float, 1)
+//     ATTRIB(XonoticPrivateServerList, filterShowFull, float, 1)
+//     ATTRIB(XonoticPrivateServerList, filterString, string, string_null)
+//     ATTRIB(XonoticPrivateServerList, controlledTextbox, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, ipAddressBox, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, favoriteButton, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, nextRefreshTime, float, 0)
+//     METHOD(XonoticPrivateServerList, refreshPrivateServerList, void(entity, float)) // refresh mode: REFRESHSERVERLIST_*
+//     ATTRIB(XonoticPrivateServerList, needsRefresh, float, 1)
+//     METHOD(XonoticPrivateServerList, focusEnter, void(entity))
+//     METHOD(XonoticPrivateServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
+//     ATTRIB(XonoticPrivateServerList, sortButton1, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, sortButton2, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, sortButton3, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, sortButton4, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, sortButton5, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, connectButton, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, infoButton, entity, NULL)
+//     ATTRIB(XonoticPrivateServerList, currentSortOrder, float, 0)
+//     ATTRIB(XonoticPrivateServerList, currentSortField, float, -1)
+//
+//     ATTRIB(XonoticPrivateServerList, ipAddressBoxFocused, float, -1)
+//
+//     ATTRIB(XonoticPrivateServerList, seenIPv4, float, 0)
+//     ATTRIB(XonoticPrivateServerList, seenIPv6, float, 0)
+//     ATTRIB(XonoticPrivateServerList, categoriesHeight, float, 1.25)
+//
+//     METHOD(XonoticPrivateServerList, getTotalHeight, float(entity))
+//     METHOD(XonoticPrivateServerList, getItemAtPos, float(entity, float))
+//     METHOD(XonoticPrivateServerList, getItemStart, float(entity, float))
+//     METHOD(XonoticPrivateServerList, getItemHeight, float(entity, float))
+//ENDCLASS(XonoticPrivateServerList)
+//entity makeXonoticPrivateServerList();
+//
+//#ifndef IMPLEMENTATION
+//float autocvar_menu_slist_categories;
+//float autocvar_menu_slist_categories_onlyifmultiple;
+//float autocvar_menu_slist_purethreshold;
+//float autocvar_menu_slist_modimpurity;
+//float autocvar_menu_slist_recommendations;
+//float autocvar_menu_slist_recommendations_maxping;
+//float autocvar_menu_slist_recommendations_minfreeslots;
+//float autocvar_menu_slist_recommendations_minhumans;
+//float autocvar_menu_slist_recommendations_purethreshold;
+//
+//// server cache fields
+//#define SLIST_FIELDS \
+//     SLIST_FIELD(CNAME,       "cname") \
+//     SLIST_FIELD(PING,        "ping") \
+//     SLIST_FIELD(GAME,        "game") \
+//     SLIST_FIELD(MOD,         "mod") \
+//     SLIST_FIELD(MAP,         "map") \
+//     SLIST_FIELD(NAME,        "name") \
+//     SLIST_FIELD(MAXPLAYERS,  "maxplayers") \
+//     SLIST_FIELD(NUMPLAYERS,  "numplayers") \
+//     SLIST_FIELD(NUMHUMANS,   "numhumans") \
+//     SLIST_FIELD(NUMBOTS,     "numbots") \
+//     SLIST_FIELD(PROTOCOL,    "protocol") \
+//     SLIST_FIELD(FREESLOTS,   "freeslots") \
+//     SLIST_FIELD(PLAYERS,     "players") \
+//     SLIST_FIELD(QCSTATUS,    "qcstatus") \
+//     SLIST_FIELD(CATEGORY,    "category") \
+//     SLIST_FIELD(ISFAVORITE,  "isfavorite")
+//
+//#define SLIST_FIELD(suffix,name) float SLIST_FIELD_##suffix;
+//SLIST_FIELDS
+//#undef SLIST_FIELD
+//
+//const float REFRESHSERVERLIST_RESORT = 0;    // sort the server list again to update for changes to e.g. favorite status, categories
+//const float REFRESHSERVERLIST_REFILTER = 1;  // ..., also update filter and sort criteria
+//const float REFRESHSERVERLIST_ASK = 2;       // ..., also suggest querying servers now
+//const float REFRESHSERVERLIST_RESET = 3;     // ..., also clear the list first
+//
+//// function declarations
+//float IsServerInList(string list, string srv);
+//#define IsFavorite(srv) IsServerInList(cvar_string("net_slist_favorites"), srv)
+//#define IsPromoted(srv) IsServerInList(_Nex_ExtResponseSystem_PromotedServers, srv)
+//#define IsRecommended(srv) IsServerInList(_Nex_ExtResponseSystem_RecommendedServers, srv)
+//
+//entity RetrieveCategoryEnt(float catnum);
+//
+//float CheckCategoryOverride(float cat);
+//float CheckCategoryForEntry(float entry);
+//float m_gethostcachecategory(float entry) { return CheckCategoryOverride(CheckCategoryForEntry(entry)); }
+//
+//void RegisterSLCategories();
+//
+//void PrivateServerList_Connect_Click(entity btn, entity me);
+//void PrivateServerList_Categories_Click(entity box, entity me);
+//void PrivateServerList_ShowEmpty_Click(entity box, entity me);
+//void PrivateServerList_ShowFull_Click(entity box, entity me);
+//void PrivateServerList_Filter_Change(entity box, entity me);
+//void PrivateServerList_Favorite_Click(entity btn, entity me);
+//void PrivateServerList_Info_Click(entity btn, entity me);
+//void PrivateServerList_Update_favoriteButton(entity btn, entity me);
+//
+//// fields for category entities
+//const int MAX_CATEGORIES = 9;
+//const int CATEGORY_FIRST = 1;
+//entity categories[MAX_CATEGORIES];
+//int category_ent_count;
+//.string cat_name;
+//.string cat_string;
+//.string cat_enoverride_string;
+//.string cat_dioverride_string;
+//.float cat_enoverride;
+//.float cat_dioverride;
+//
+//// fields for drawing categories
+//int category_name[MAX_CATEGORIES];
+//int category_item[MAX_CATEGORIES];
+//int category_draw_count;
+//
+//#define SLIST_CATEGORIES \
+//     SLIST_CATEGORY(CAT_FAVORITED,    "",            "",             ZCTX(_("SLCAT^Favorites"))) \
+//     SLIST_CATEGORY(CAT_RECOMMENDED,  "",            "",             ZCTX(_("SLCAT^Recommended"))) \
+//     SLIST_CATEGORY(CAT_NORMAL,       "",            "CAT_SERVERS",  ZCTX(_("SLCAT^Normal Servers"))) \
+//     SLIST_CATEGORY(CAT_SERVERS,      "CAT_NORMAL",  "CAT_SERVERS",  ZCTX(_("SLCAT^Servers"))) \
+//     SLIST_CATEGORY(CAT_XPM,          "CAT_NORMAL",  "CAT_SERVERS",  ZCTX(_("SLCAT^Competitive Mode"))) \
+//     SLIST_CATEGORY(CAT_MODIFIED,     "",            "CAT_SERVERS",  ZCTX(_("SLCAT^Modified Servers"))) \
+//     SLIST_CATEGORY(CAT_OVERKILL,     "",            "CAT_SERVERS",  ZCTX(_("SLCAT^Overkill Mode"))) \
+//     SLIST_CATEGORY(CAT_INSTAGIB,     "",            "CAT_SERVERS",  ZCTX(_("SLCAT^InstaGib Mode"))) \
+//     SLIST_CATEGORY(CAT_DEFRAG,       "",            "CAT_SERVERS",  ZCTX(_("SLCAT^Defrag Mode")))
+//
+//#define SLIST_CATEGORY_AUTOCVAR(name) autocvar_menu_slist_categories_##name##_override
+//#define SLIST_CATEGORY(name,enoverride,dioverride,str) \
+//     int name; \
+//     string SLIST_CATEGORY_AUTOCVAR(name) = enoverride;
+//SLIST_CATEGORIES
+//#undef SLIST_CATEGORY
+//
+//#endif
+//#endif
+//#ifdef IMPLEMENTATION
+//
+//void RegisterSLCategories()
+//{
+//     entity cat;
+//     #define SLIST_CATEGORY(name,enoverride,dioverride,str) \
+//             SET_FIELD_COUNT(name, CATEGORY_FIRST, category_ent_count) \
+//             CHECK_MAX_COUNT(name, MAX_CATEGORIES, category_ent_count, "SLIST_CATEGORY") \
+//             cat = spawn(); \
+//             categories[name - 1] = cat; \
+//             cat.classname = "slist_category"; \
+//             cat.cat_name = strzone(#name); \
+//             cat.cat_enoverride_string = strzone(SLIST_CATEGORY_AUTOCVAR(name)); \
+//             cat.cat_dioverride_string = strzone(dioverride); \
+//             cat.cat_string = strzone(str);
+//     SLIST_CATEGORIES
+//     #undef SLIST_CATEGORY
+//
+//     int i, x, catnum;
+//     string s;
+//
+//     #define PROCESS_OVERRIDE(override_string,override_field) \
+//             for(i = 0; i < category_ent_count; ++i) \
+//             { \
+//                     s = categories[i].override_string; \
+//                     if((s != "") && (s != categories[i].cat_name)) \
+//                     { \
+//                             catnum = 0; \
+//                             for(x = 0; x < category_ent_count; ++x) \
+//                             { if(categories[x].cat_name == s) { \
+//                                     catnum = (x+1); \
+//                                     break; \
+//                             } } \
+//                             if(catnum) \
+//                             { \
+//                                     strunzone(categories[i].override_string); \
+//                                     categories[i].override_field = catnum; \
+//                                     continue; \
+//                             } \
+//                             else \
+//                             { \
+//                                     printf( \
+//                                             "RegisterSLCategories(): Improper override '%s' for category '%s'!\n", \
+//                                             s, \
+//                                             categories[i].cat_name \
+//                                     ); \
+//                             } \
+//                     } \
+//                     strunzone(categories[i].override_string); \
+//                     categories[i].override_field = 0; \
+//             }
+//     PROCESS_OVERRIDE(cat_enoverride_string, cat_enoverride)
+//     PROCESS_OVERRIDE(cat_dioverride_string, cat_dioverride)
+//     #undef PROCESS_OVERRIDE
+//}
+//
+//// Supporting Functions
+//entity RetrieveCategoryEnt(int catnum)
+//{
+//     if((catnum > 0) && (catnum <= category_ent_count))
+//     {
+//             return categories[catnum - 1];
+//     }
+//     else
+//     {
+//             error(sprintf("RetrieveCategoryEnt(%d): Improper category number!\n", catnum));
+//             return world;
+//     }
+//}
+//
+//bool IsServerInList(string list, string srv)
+//{
+//     string p;
+//     int i, n;
+//     if(srv == "")
+//             return false;
+//     srv = netaddress_resolve(srv, 26000);
+//     if(srv == "")
+//             return false;
+//     p = crypto_getidfp(srv);
+//     n = tokenize_console(list);
+//     for(i = 0; i < n; ++i)
+//     {
+//             if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
+//             {
+//                     if(p)
+//                             if(argv(i) == p)
+//                                     return true;
+//             }
+//             else
+//             {
+//                     if(srv == netaddress_resolve(argv(i), 26000))
+//                             return true;
+//             }
+//     }
+//     return false;
+//}
+//
+//int CheckCategoryOverride(int cat)
+//{
+//     entity catent = RetrieveCategoryEnt(cat);
+//     if(catent)
+//     {
+//             int override = (autocvar_menu_slist_categories ? catent.cat_enoverride : catent.cat_dioverride);
+//             if(override) { return override; }
+//             else { return cat; }
+//     }
+//     else
+//     {
+//             error(sprintf("CheckCategoryOverride(%d): Improper category number!\n", cat));
+//             return cat;
+//     }
+//}
+//
+//int CheckCategoryForEntry(int entry)
+//{
+//     string s, k, v, modtype = "";
+//     int j, m, impure = 0, freeslots = 0, sflags = 0;
+//     s = gethostcachestring(SLIST_FIELD_QCSTATUS, entry);
+//     m = tokenizebyseparator(s, ":");
+//
+//     for(j = 2; j < m; ++j)
+//     {
+//             if(argv(j) == "") { break; }
+//             k = substring(argv(j), 0, 1);
+//             v = substring(argv(j), 1, -1);
+//             switch(k)
+//             {
+//                     case "P": { impure = stof(v); break; }
+//                     case "S": { freeslots = stof(v); break; }
+//                     case "F": { sflags = stof(v); break; }
+//                     case "M": { modtype = strtolower(v); break; }
+//             }
+//     }
+//
+//     if(modtype != "xonotic") { impure += autocvar_menu_slist_modimpurity; }
+//
+//     // check if this server is favorited
+//     if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, entry)) { return CAT_FAVORITED; }
+//
+//     // now check if it's recommended
+//     if(autocvar_menu_slist_recommendations)
+//     {
+//             string cname = gethostcachestring(SLIST_FIELD_CNAME, entry);
+//
+//             if(IsPromoted(cname)) { return CAT_RECOMMENDED; }
+//             else
+//             {
+//                     float recommended = 0;
+//                     if(autocvar_menu_slist_recommendations & 1)
+//                     {
+//                             if(IsRecommended(cname)) { ++recommended; }
+//                             else { --recommended; }
+//                     }
+//                     if(autocvar_menu_slist_recommendations & 2)
+//                     {
+//                             if(
+//                                     ///// check for minimum free slots
+//                                     (freeslots >= autocvar_menu_slist_recommendations_minfreeslots)
+//
+//                                     && // check for purity requirement
+//                                     (
+//                                             (autocvar_menu_slist_recommendations_purethreshold < 0)
+//                                             ||
+//                                             (impure <= autocvar_menu_slist_recommendations_purethreshold)
+//                                     )
+//
+//                                     && // check for minimum amount of humans
+//                                     (
+//                                             gethostcachenumber(SLIST_FIELD_NUMHUMANS, entry)
+//                                             >=
+//                                             autocvar_menu_slist_recommendations_minhumans
+//                                     )
+//
+//                                     && // check for maximum latency
+//                                     (
+//                                             gethostcachenumber(SLIST_FIELD_PING, entry)
+//                                             <=
+//                                             autocvar_menu_slist_recommendations_maxping
+//                                     )
+//                             )
+//                                     { ++recommended; }
+//                             else
+//                                     { --recommended; }
+//                     }
+//                     if(recommended > 0) { return CAT_RECOMMENDED; }
+//             }
+//     }
+//
+//     // if not favorited or recommended, check modname
+//     if(modtype != "xonotic")
+//     {
+//             switch(modtype)
+//             {
+//                     // old servers which don't report their mod name are considered modified now
+//                     case "": { return CAT_MODIFIED; }
+//
+//                     case "xpm": { return CAT_XPM; }
+//                     case "minstagib":
+//                     case "instagib": { return CAT_INSTAGIB; }
+//                     case "overkill": { return CAT_OVERKILL; }
+//                     //case "nix": { return CAT_NIX; }
+//                     //case "newtoys": { return CAT_NEWTOYS; }
+//
+//                     // "cts" is allowed as compat, xdf is replacement
+//                     case "cts":
+//                     case "xdf": { return CAT_DEFRAG; }
+//
+//                     default: { dprintf("Found strange mod type: %s\n", modtype); return CAT_MODIFIED; }
+//             }
+//     }
+//
+//     // must be normal or impure server
+//     return ((impure > autocvar_menu_slist_purethreshold) ? CAT_MODIFIED : CAT_NORMAL);
+//}
+//
+//void XonoticPrivateServerList_toggleFavorite(entity me, string srv)
+//{
+//     string s, s0, s1, s2, srv_resolved, p;
+//     int i, n;
+//     bool f = false;
+//     srv_resolved = netaddress_resolve(srv, 26000);
+//     p = crypto_getidfp(srv_resolved);
+//     s = cvar_string("net_slist_favorites");
+//     n = tokenize_console(s);
+//     for(i = 0; i < n; ++i)
+//     {
+//             if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
+//             {
+//                     if(p)
+//                             if(argv(i) != p)
+//                                     continue;
+//             }
+//             else
+//             {
+//                     if(srv_resolved != netaddress_resolve(argv(i), 26000))
+//                             continue;
+//             }
+//             s0 = s1 = s2 = "";
+//             if(i > 0)
+//                     s0 = substring(s, 0, argv_end_index(i - 1));
+//             if(i < n-1)
+//                     s2 = substring(s, argv_start_index(i + 1), -1);
+//             if(s0 != "" && s2 != "")
+//                     s1 = " ";
+//             cvar_set("net_slist_favorites", strcat(s0, s1, s2));
+//             s = cvar_string("net_slist_favorites");
+//             n = tokenize_console(s);
+//             f = true;
+//             --i;
+//     }
+//
+//     if(!f)
+//     {
+//             s1 = "";
+//             if(s != "")
+//                     s1 = " ";
+//             if(p)
+//                     cvar_set("net_slist_favorites", strcat(s, s1, p));
+//             else
+//                     cvar_set("net_slist_favorites", strcat(s, s1, srv));
+//     }
+//
+//     me.refreshPrivateServerList(me, REFRESHSERVERLIST_RESORT);
+//}
+//
+//void PrivateServerList_Update_favoriteButton(entity btn, entity me)
+//{
+//     me.favoriteButton.setText(me.favoriteButton,
+//             (IsFavorite(me.ipAddressBox.text) ?
+//                     _("Remove") : _("Favorite")
+//             )
+//     );
+//}
+//
+//entity makeXonoticPrivateServerList()
+//{
+//     entity me;
+//     me = NEW(XonoticPrivateServerList);
+//     me.configureXonoticPrivateServerList(me);
+//     return me;
+//}
+//void XonoticPrivateServerList_configureXonoticPrivateServerList(entity me)
+//{
+//     me.configureXonoticListBox(me);
+//
+//     // update field ID's
+//     #define SLIST_FIELD(suffix,name) SLIST_FIELD_##suffix = gethostcacheindexforkey(name);
+//     SLIST_FIELDS
+//     #undef SLIST_FIELD
+//
+//     // clear list
+//     me.nItems = 0;
+//}
+//void XonoticPrivateServerList_setSelected(entity me, int i)
+//{
+//     //int save = me.selectedItem;
+//     SUPER(XonoticPrivateServerList).setSelected(me, i);
+//     /*
+//     if(me.selectedItem == save)
+//             return;
+//     */
+//     if(me.nItems == 0)
+//             return;
+//     if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
+//             return; // sorry, it would be wrong
+//
+//     if(me.selectedServer)
+//             strunzone(me.selectedServer);
+//     me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
+//
+//     me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
+//     me.ipAddressBox.cursorPos = strlen(me.selectedServer);
+//     me.ipAddressBoxFocused = -1;
+//}
+//void XonoticPrivateServerList_refreshPrivateServerList(entity me, int mode)
+//{
+//     //print("refresh of type ", ftos(mode), "\n");
+//
+//     if(mode >= REFRESHSERVERLIST_REFILTER)
+//     {
+//             float m;
+//             int i, n;
+//             int listflags = 0;
+//             string s, typestr, modstr;
+//
+//             s = me.filterString;
+//
+//             m = strstrofs(s, ":", 0);
+//             if(m >= 0)
+//             {
+//                     typestr = substring(s, 0, m);
+//                     s = substring(s, m + 1, strlen(s) - m - 1);
+//                     while(substring(s, 0, 1) == " ")
+//                             s = substring(s, 1, strlen(s) - 1);
+//             }
+//             else
+//                     typestr = "";
+//
+//             modstr = cvar_string("menu_slist_modfilter");
+//
+//             m = SLIST_MASK_AND - 1;
+//             resethostcachemasks();
+//
+//             // ping: reject negative ping (no idea why this happens in the first place, engine bug)
+//             sethostcachemasknumber(++m, SLIST_FIELD_PING, 0, SLIST_TEST_GREATEREQUAL);
+//
+//             // show full button
+//             if(!me.filterShowFull)
+//             {
+//                     sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL); // legacy
+//                     sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, ":S0:", SLIST_TEST_NOTCONTAIN); // g_maxplayers support
+//             }
+//
+//             // show empty button
+//             if(!me.filterShowEmpty)
+//                     sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
+//
+//             // gametype filtering
+//             if(typestr != "")
+//                     sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
+//
+//             // mod filtering
+//             if(modstr != "")
+//             {
+//                     if(substring(modstr, 0, 1) == "!")
+//                             sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
+//                     else
+//                             sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
+//             }
+//
+//             // server banning
+//             n = tokenizebyseparator(_Nex_ExtResponseSystem_BannedServers, " ");
+//             for(i = 0; i < n; ++i)
+//                     if(argv(i) != "")
+//                             sethostcachemaskstring(++m, SLIST_FIELD_CNAME, argv(i), SLIST_TEST_NOTSTARTSWITH);
+//
+//             m = SLIST_MASK_OR - 1;
+//             if(s != "")
+//             {
+//                     sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
+//                     sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
+//                     sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
+//                     sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
+//             }
+//
+//             // sorting flags
+//             //listflags |= SLSF_FAVORITES;
+//             listflags |= SLSF_CATEGORIES;
+//             if(me.currentSortOrder < 0) { listflags |= SLSF_DESCENDING; }
+//             sethostcachesort(me.currentSortField, listflags);
+//     }
+//
+//     resorthostcache();
+//     if(mode >= REFRESHSERVERLIST_ASK)
+//             refreshhostcache(mode >= REFRESHSERVERLIST_RESET);
+//}
+//void XonoticPrivateServerList_focusEnter(entity me)
+//{
+//     SUPER(XonoticPrivateServerList).focusEnter(me);
+//     if(time < me.nextRefreshTime)
+//     {
+//             //print("sorry, no refresh yet\n");
+//             return;
+//     }
+//     me.nextRefreshTime = time + 10;
+//     me.refreshPrivateServerList(me, REFRESHSERVERLIST_ASK);
+//}
+//
+//void XonoticPrivateServerList_draw(entity me)
+//{
+//     int i;
+//     bool found = false, owned;
+//
+//     if(_Nex_ExtResponseSystem_BannedServersNeedsRefresh)
+//     {
+//             if(!me.needsRefresh)
+//                     me.needsRefresh = 2;
+//             _Nex_ExtResponseSystem_BannedServersNeedsRefresh = 0;
+//     }
+//
+//     if(_Nex_ExtResponseSystem_PromotedServersNeedsRefresh)
+//     {
+//             if(!me.needsRefresh)
+//                     me.needsRefresh = 3;
+//             _Nex_ExtResponseSystem_PromotedServersNeedsRefresh = 0;
+//     }
+//
+//     if(_Nex_ExtResponseSystem_RecommendedServersNeedsRefresh)
+//     {
+//             if(!me.needsRefresh)
+//                     me.needsRefresh = 3;
+//             _Nex_ExtResponseSystem_RecommendedServersNeedsRefresh = 0;
+//     }
+//
+//     if(me.currentSortField == -1)
+//     {
+//             me.setSortOrder(me, SLIST_FIELD_PING, +1);
+//             me.refreshPrivateServerList(me, REFRESHSERVERLIST_RESET);
+//     }
+//     else if(me.needsRefresh == 1)
+//     {
+//             me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
+//     }
+//     else if(me.needsRefresh == 2)
+//     {
+//             me.needsRefresh = 0;
+//             me.refreshPrivateServerList(me, REFRESHSERVERLIST_REFILTER);
+//     }
+//     else if(me.needsRefresh == 3)
+//     {
+//             me.needsRefresh = 0;
+//             me.refreshPrivateServerList(me, REFRESHSERVERLIST_RESORT);
+//     }
+//
+//     owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
+//
+//     for(i = 0; i < category_draw_count; ++i) { category_name[i] = -1; category_item[i] = -1; }
+//     category_draw_count = 0;
+//
+//     if(autocvar_menu_slist_categories >= 0) // if less than 0, don't even draw a category heading for favorites
+//     {
+//             float itemcount = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
+//             me.nItems = itemcount;
+//
+//             //float visible = floor(me.scrollPos / me.itemHeight);
+//             // ^ unfortunately no such optimization can be made-- we must process through the
+//             // entire list, otherwise there is no way to know which item is first in its category.
+//
+//             // binary search method suggested by div
+//             float x;
+//             float begin = 0;
+//             for(x = 1; x <= category_ent_count; ++x) {
+//                     float first = begin;
+//                     float last = (itemcount - 1);
+//                     if (first > last) {
+//                             // List is empty.
+//                             break;
+//                     }
+//                     float catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first);
+//                     float catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last);
+//                     if (catf > x) {
+//                             // The first one is already > x.
+//                             // Therefore, category x does not exist.
+//                             // Higher numbered categories do exist though.
+//                     } else if (catl < x) {
+//                             // The last one is < x.
+//                             // Thus this category - and any following -
+//                             // don't exist.
+//                             break;
+//                     } else if (catf == x) {
+//                             // Starts at first. This breaks the loop
+//                             // invariant in the binary search and thus has
+//                             // to be handled separately.
+//                             if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != x)
+//                                     error("Category mismatch I");
+//                             if(first > 0)
+//                                     if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == x)
+//                                             error("Category mismatch II");
+//                             category_name[category_draw_count] = x;
+//                             category_item[category_draw_count] = first;
+//                             ++category_draw_count;
+//                             begin = first + 1;
+//                     } else {
+//                             // At this point, catf <= x < catl, thus
+//                             // catf < catl, thus first < last.
+//                             // INVARIANTS:
+//                             // last - first >= 1
+//                             // catf == gethostcachenumber(SLIST_FIELD_CATEGORY(first)
+//                             // catl == gethostcachenumber(SLIST_FIELD_CATEGORY(last)
+//                             // catf < x
+//                             // catl >= x
+//                             while (last - first > 1) {
+//                                     float middle = floor((first + last) / 2);
+//                                     // By loop condition, middle != first && middle != last.
+//                                     float cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
+//                                     if (cat >= x) {
+//                                             last = middle;
+//                                             catl = cat;
+//                                     } else {
+//                                             first = middle;
+//                                             catf = cat;
+//                                     }
+//                             }
+//                             if (catl == x) {
+//                                     if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != x)
+//                                             error("Category mismatch III");
+//                                     if(last > 0)
+//                                             if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == x)
+//                                                     error("Category mismatch IV");
+//                                     category_name[category_draw_count] = x;
+//                                     category_item[category_draw_count] = last;
+//                                     ++category_draw_count;
+//                                     begin = last + 1; // already scanned through these, skip 'em
+//                             }
+//                             else
+//                                     begin = last; // already scanned through these, skip 'em
+//                     }
+//             }
+//             if(autocvar_menu_slist_categories_onlyifmultiple && (category_draw_count == 1))
+//             {
+//                     category_name[0] = -1;
+//                     category_item[0] = -1;
+//                     category_draw_count = 0;
+//                     me.nItems = itemcount;
+//             }
+//     }
+//     else { me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT); }
+//
+//     me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
+//     me.infoButton.disabled = ((me.nItems == 0) || !owned);
+//     me.favoriteButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
+//
+//     if(me.selectedServer)
+//     {
+//             for(i = 0; i < me.nItems; ++i)
+//             {
+//                     if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
+//                     {
+//                             me.selectedItem = i;
+//                             found = true;
+//                             break;
+//                     }
+//             }
+//     }
+//     if(!found)
+//     {
+//             if(me.nItems > 0)
+//             {
+//                     if(me.selectedItem >= me.nItems)
+//                             me.selectedItem = me.nItems - 1;
+//                     if(me.selectedServer)
+//                             strunzone(me.selectedServer);
+//                     me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
+//             }
+//     }
+//
+//     if(owned)
+//     {
+//             if(me.selectedServer != me.ipAddressBox.text)
+//             {
+//                     me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
+//                     me.ipAddressBox.cursorPos = strlen(me.selectedServer);
+//                     me.ipAddressBoxFocused = -1;
+//             }
+//     }
+//
+//     if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
+//     {
+//             if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
+//                     PrivateServerList_Update_favoriteButton(NULL, me);
+//             me.ipAddressBoxFocused = me.ipAddressBox.focused;
+//     }
+//
+//     SUPER(XonoticPrivateServerList).draw(me);
+//}
+//void PrivateServerList_PingSort_Click(entity btn, entity me)
+//{
+//     me.setSortOrder(me, SLIST_FIELD_PING, +1);
+//}
+//void PrivateServerList_NameSort_Click(entity btn, entity me)
+//{
+//     me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
+//}
+//void PrivateServerList_MapSort_Click(entity btn, entity me)
+//{
+//     me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
+//}
+//void PrivateServerList_PlayerSort_Click(entity btn, entity me)
+//{
+//     me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
+//}
+//void PrivateServerList_TypeSort_Click(entity btn, entity me)
+//{
+//     string s, t;
+//     float i, m;
+//     s = me.filterString;
+//     m = strstrofs(s, ":", 0);
+//     if(m >= 0)
+//     {
+//             s = substring(s, 0, m);
+//             while(substring(s, m+1, 1) == " ") // skip spaces
+//                     ++m;
+//     }
+//     else
+//             s = "";
+//
+//     for(i = 1; ; i *= 2) // 20 modes ought to be enough for anyone
+//     {
+//             t = MapInfo_Type_ToString(i);
+//             if(i > 1)
+//                     if(t == "") // it repeats (default case)
+//                     {
+//                             // no type was found
+//                             // choose the first one
+//                             s = MapInfo_Type_ToString(1);
+//                             break;
+//                     }
+//             if(s == t)
+//             {
+//                     // the type was found
+//                     // choose the next one
+//                     s = MapInfo_Type_ToString(i * 2);
+//                     if(s == "")
+//                             s = MapInfo_Type_ToString(1);
+//                     break;
+//             }
+//     }
+//
+//     if(s != "")
+//             s = strcat(s, ":");
+//     s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
+//
+//     me.controlledTextbox.setText(me.controlledTextbox, s);
+//     me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
+//     me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
+//     //PrivateServerList_Filter_Change(me.controlledTextbox, me);
+//}
+//void PrivateServerList_Filter_Change(entity box, entity me)
+//{
+//     if(me.filterString)
+//             strunzone(me.filterString);
+//     if(box.text != "")
+//             me.filterString = strzone(box.text);
+//     else
+//             me.filterString = string_null;
+//     me.refreshPrivateServerList(me, REFRESHSERVERLIST_REFILTER);
+//
+//     me.ipAddressBox.setText(me.ipAddressBox, "");
+//     me.ipAddressBox.cursorPos = 0;
+//     me.ipAddressBoxFocused = -1;
+//}
+//void PrivateServerList_Categories_Click(entity box, entity me)
+//{
+//     box.setChecked(box, autocvar_menu_slist_categories = !autocvar_menu_slist_categories);
+//     me.refreshPrivateServerList(me, REFRESHSERVERLIST_RESORT);
+//
+//     me.ipAddressBox.setText(me.ipAddressBox, "");
+//     me.ipAddressBox.cursorPos = 0;
+//     me.ipAddressBoxFocused = -1;
+//}
+//void PrivateServerList_ShowEmpty_Click(entity box, entity me)
+//{
+//     box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
+//     me.refreshPrivateServerList(me, REFRESHSERVERLIST_REFILTER);
+//
+//     me.ipAddressBox.setText(me.ipAddressBox, "");
+//     me.ipAddressBox.cursorPos = 0;
+//     me.ipAddressBoxFocused = -1;
+//}
+//void PrivateServerList_ShowFull_Click(entity box, entity me)
+//{
+//     box.setChecked(box, me.filterShowFull = !me.filterShowFull);
+//     me.refreshPrivateServerList(me, REFRESHSERVERLIST_REFILTER);
+//
+//     me.ipAddressBox.setText(me.ipAddressBox, "");
+//     me.ipAddressBox.cursorPos = 0;
+//     me.ipAddressBoxFocused = -1;
+//}
+//void XonoticPrivateServerList_setSortOrder(entity me, int fld, int direction)
+//{
+//     if(me.currentSortField == fld)
+//             direction = -me.currentSortOrder;
+//     me.currentSortOrder = direction;
+//     me.currentSortField = fld;
+//     me.sortButton1.forcePressed = (fld == SLIST_FIELD_PING);
+//     me.sortButton2.forcePressed = (fld == SLIST_FIELD_NAME);
+//     me.sortButton3.forcePressed = (fld == SLIST_FIELD_MAP);
+//     me.sortButton4.forcePressed = 0;
+//     me.sortButton5.forcePressed = (fld == SLIST_FIELD_NUMHUMANS);
+//     me.selectedItem = 0;
+//     if(me.selectedServer)
+//             strunzone(me.selectedServer);
+//     me.selectedServer = string_null;
+//     me.refreshPrivateServerList(me, REFRESHSERVERLIST_REFILTER);
+//}
+//void XonoticPrivateServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
+//{
+//     vector originInLBSpace, sizeInLBSpace;
+//     originInLBSpace = eY * (-me.itemHeight);
+//     sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
+//
+//     vector originInDialogSpace, sizeInDialogSpace;
+//     originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
+//     sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
+//
+//     btn.Container_origin_x = originInDialogSpace.x + sizeInDialogSpace.x * theOrigin;
+//     btn.Container_size_x   =                         sizeInDialogSpace.x * theSize;
+//     btn.setText(btn, theTitle);
+//     btn.onClick = theFunc;
+//     btn.onClickEntity = me;
+//     btn.resized = 1;
+//}
+//void XonoticPrivateServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
+//{
+//     SUPER(XonoticPrivateServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
+//
+//     me.realFontSize_y = me.fontSize / (absSize.y * me.itemHeight);
+//     me.realFontSize_x = me.fontSize / (absSize.x * (1 - me.controlWidth));
+//     me.realUpperMargin = 0.5 * (1 - me.realFontSize.y);
+//
+//     me.columnIconsOrigin = 0;
+//     me.columnIconsSize = me.realFontSize.x * 4 * me.iconsSizeFactor;
+//     me.columnPingSize = me.realFontSize.x * 3;
+//     me.columnMapSize = me.realFontSize.x * 10;
+//     me.columnTypeSize = me.realFontSize.x * 4;
+//     me.columnPlayersSize = me.realFontSize.x * 5;
+//     me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnIconsSize - me.columnTypeSize - 5 * me.realFontSize.x;
+//     me.columnPingOrigin = me.columnIconsOrigin + me.columnIconsSize + me.realFontSize.x;
+//     me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize.x;
+//     me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize.x;
+//     me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize.x;
+//     me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize.x;
+//
+//     me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, _("Ping"), PrivateServerList_PingSort_Click);
+//     me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, _("Host name"), PrivateServerList_NameSort_Click);
+//     me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, _("Map"), PrivateServerList_MapSort_Click);
+//     me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, _("Type"), PrivateServerList_TypeSort_Click);
+//     me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, _("Players"), PrivateServerList_PlayerSort_Click);
+//
+//     int f = me.currentSortField;
+//     if(f >= 0)
+//     {
+//             me.currentSortField = -1;
+//             me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
+//     }
+//}
+//void PrivateServerList_Connect_Click(entity btn, entity me)
+//{
+//     localcmd(sprintf("connect %s\n",
+//             ((me.ipAddressBox.text != "") ?
+//                     me.ipAddressBox.text : me.selectedServer
+//             )
+//     ));
+//}
+//void PrivateServerList_Favorite_Click(entity btn, entity me)
+//{
+//     string ipstr;
+//     ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
+//     if(ipstr != "")
+//     {
+//             m_play_click_sound(MENU_SOUND_SELECT);
+//             me.toggleFavorite(me, me.ipAddressBox.text);
+//             me.ipAddressBoxFocused = -1;
+//     }
+//}
+//void PrivateServerList_Info_Click(entity btn, entity me)
+//{
+//     if (me.nItems != 0)
+//             main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
+//
+//     vector org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
+//     vector sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
+//     DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
+//}
+//void XonoticPrivateServerList_doubleClickListBoxItem(entity me, int i, vector where)
+//{
+//     PrivateServerList_Connect_Click(NULL, me);
+//}
+//void XonoticPrivateServerList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected)
+//{
+//     // layout: Ping, Server name, Map name, NP, TP, MP
+//     float p;
+//     int q;
+//     bool isv4, isv6;
+//     vector theColor;
+//     float theAlpha;
+//     bool pure = false;
+//     int freeslots = -1, sflags = -1, j, m;
+//     string s, typestr, versionstr, k, v, modname;
+//
+//     //printf("time: %f, i: %d, item: %d, nitems: %d\n", time, i, item, me.nItems);
+//
+//     vector oldscale = draw_scale;
+//     vector oldshift = draw_shift;
+//#ifndef SET_YRANGE
+//#define SET_YRANGE(start,end) \
+//     draw_scale = boxToGlobalSize(eX * 1 + eY * (end - start), oldscale); \
+//     draw_shift = boxToGlobal(eY * start, oldshift, oldscale);
+//#endif
+//
+//     for (j = 0; j < category_draw_count; ++j) {
+//             // Matches exactly the headings with increased height.
+//             if (i == category_item[j])
+//                     break;
+//     }
+//
+//     if (j < category_draw_count)
+//     {
+//             entity catent = RetrieveCategoryEnt(category_name[j]);
+//             if(catent)
+//             {
+//                     SET_YRANGE(
+//                             (me.categoriesHeight - 1) / (me.categoriesHeight + 1),
+//                             me.categoriesHeight / (me.categoriesHeight + 1)
+//                     );
+//                     draw_Text(
+//                             eY * me.realUpperMargin
+//                             +
+//#if 0
+//                             eX * (me.columnNameOrigin + (me.columnNameSize - draw_TextWidth(catent.cat_string, 0, me.realFontSize)) * 0.5),
+//                             catent.cat_string,
+//#else
+//                             eX * (me.columnNameOrigin),
+//                             strcat(catent.cat_string, ":"),
+//#endif
+//                             me.realFontSize,
+//                             SKINCOLOR_SERVERLIST_CATEGORY,
+//                             SKINALPHA_SERVERLIST_CATEGORY,
+//                             0
+//                     );
+//                     SET_YRANGE(me.categoriesHeight / (me.categoriesHeight + 1), 1);
+//             }
+//     }
+//
+//     if(isSelected)
+//             draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+//
+//     s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
+//     m = tokenizebyseparator(s, ":");
+//     typestr = "";
+//     if(m >= 2)
+//     {
+//             typestr = argv(0);
+//             versionstr = argv(1);
+//     }
+//     freeslots = -1;
+//     modname = "";
+//     for(j = 2; j < m; ++j)
+//     {
+//             if(argv(j) == "")
+//                     break;
+//             k = substring(argv(j), 0, 1);
+//             v = substring(argv(j), 1, -1);
+//             if(k == "P")
+//                     pure = stob(v);
+//             else if(k == "S")
+//                     freeslots = stof(v);
+//             else if(k == "F")
+//                     sflags = stoi(v);
+//             else if(k == "M")
+//                     modname = v;
+//     }
+//
+//#ifdef COMPAT_NO_MOD_IS_XONOTIC
+//     if(modname == "")
+//             modname = "Xonotic";
+//#endif
+//
+//     /*
+//     SLIST_FIELD_MOD = gethostcacheindexforkey("mod");
+//     s = gethostcachestring(SLIST_FIELD_MOD, i);
+//     if(s != "data")
+//             if(modname == "Xonotic")
+//                     modname = s;
+//     */
+//
+//     // list the mods here on which the pure server check actually works
+//     if(modname != "Xonotic")
+//     if(modname != "InstaGib" || modname != "MinstaGib")
+//     if(modname != "CTS")
+//     if(modname != "NIX")
+//     if(modname != "NewToys")
+//             pure = false;
+//
+//     if(gethostcachenumber(SLIST_FIELD_FREESLOTS, i) <= 0)
+//             theAlpha = SKINALPHA_SERVERLIST_FULL;
+//     else if(freeslots == 0)
+//             theAlpha = SKINALPHA_SERVERLIST_FULL; // g_maxplayers support
+//     else if (!gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
+//             theAlpha = SKINALPHA_SERVERLIST_EMPTY;
+//     else
+//             theAlpha = 1;
+//
+//     p = gethostcachenumber(SLIST_FIELD_PING, i);
+//     const int PING_LOW = 75;
+//     const int PING_MED = 200;
+//     const int PING_HIGH = 500;
+//     if(p < PING_LOW)
+//             theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
+//     else if(p < PING_MED)
+//             theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
+//     else if(p < PING_HIGH)
+//     {
+//             theColor = SKINCOLOR_SERVERLIST_HIGHPING;
+//             theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
+//     }
+//     else
+//     {
+//             theColor = eX;
+//             theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
+//     }
+//
+//     if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
+//     {
+//             theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
+//             theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
+//     }
+//
+//     s = gethostcachestring(SLIST_FIELD_CNAME, i);
+//
+//     isv4 = isv6 = false;
+//     if(substring(s, 0, 1) == "[")
+//     {
+//             isv6 = true;
+//             me.seenIPv6 += 1;
+//     }
+//     else if(strstrofs("0123456789", substring(s, 0, 1), 0) >= 0)
+//     {
+//             isv4 = true;
+//             me.seenIPv4 += 1;
+//     }
+//
+//     q = stof(substring(crypto_getencryptlevel(s), 0, 1));
+//     if((q <= 0 && cvar("crypto_aeslevel") >= 3) || (q >= 3 && cvar("crypto_aeslevel") <= 0))
+//     {
+//             theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
+//             theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
+//     }
+//
+//     if(q == 1)
+//     {
+//             if(cvar("crypto_aeslevel") >= 2)
+//                     q |= 4;
+//     }
+//     if(q == 2)
+//     {
+//             if(cvar("crypto_aeslevel") >= 1)
+//                     q |= 4;
+//     }
+//     if(q == 3)
+//             q = 5;
+//     else if(q >= 3)
+//             q -= 2;
+//     // possible status:
+//     // 0: crypto off
+//     // 1: AES possible
+//     // 2: AES recommended but not available
+//     // 3: AES possible and will be used
+//     // 4: AES recommended and will be used
+//     // 5: AES required
+//
+//     // --------------
+//     //  RENDER ICONS
+//     // --------------
+//     vector iconSize = '0 0 0';
+//     iconSize_y = me.realFontSize.y * me.iconsSizeFactor;
+//     iconSize_x = me.realFontSize.x * me.iconsSizeFactor;
+//
+//     vector iconPos = '0 0 0';
+//     iconPos_x = (me.columnIconsSize - 3 * iconSize.x) * 0.5;
+//     iconPos_y = (1 - iconSize.y) * 0.5;
+//
+//     string n;
+//
+//     if (!(me.seenIPv4 && me.seenIPv6))
+//     {
+//             iconPos.x += iconSize.x * 0.5;
+//     }
+//     else if(me.seenIPv4 && me.seenIPv6)
+//     {
+//             n = string_null;
+//             if(isv6)
+//                     draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv6"), 0); // PRECACHE_PIC_MIPMAP
+//             else if(isv4)
+//                     draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv4"), 0); // PRECACHE_PIC_MIPMAP
+//             if(n)
+//                     draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+//             iconPos.x += iconSize.x;
+//     }
+//
+//     if(q > 0)
+//     {
+//             draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_aeslevel", ftos(q)), 0); // PRECACHE_PIC_MIPMAP
+//             draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+//     }
+//     iconPos.x += iconSize.x;
+//
+//     if(modname == "Xonotic")
+//     {
+//             if(pure == 0)
+//             {
+//                     draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_pure1"), PRECACHE_PIC_MIPMAP);
+//                     draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+//             }
+//     }
+//     else
+//     {
+//             draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_", modname), PRECACHE_PIC_MIPMAP);
+//             if(draw_PictureSize(n) == '0 0 0')
+//                     draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_"), PRECACHE_PIC_MIPMAP);
+//             if(pure == 0)
+//                     draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+//             else
+//                     draw_Picture(iconPos, n, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
+//     }
+//     iconPos.x += iconSize.x;
+//
+//     if(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS))
+//     {
+//             draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_stats1"), 0); // PRECACHE_PIC_MIPMAP
+//             draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
+//     }
+//     iconPos.x += iconSize.x;
+//
+//     // --------------
+//     //  RENDER TEXT
+//     // --------------
+//
+//     // ping
+//     s = ftos(p);
+//     draw_Text(me.realUpperMargin * eY + (me.columnPingOrigin + me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+//
+//     // server name
+//     s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize, 0, me.realFontSize);
+//     draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
+//
+//     // server map
+//     s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize, 0, me.realFontSize);
+//     draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+//
+//     // server gametype
+//     s = draw_TextShortenToWidth(typestr, me.columnTypeSize, 0, me.realFontSize);
+//     draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+//
+//     // server playercount
+//     s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
+//     draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
+//}
+//
+//bool XonoticPrivateServerList_keyDown(entity me, int scan, bool ascii, bool shift)
+//{
+//     vector org, sz;
+//
+//     org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
+//     sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
+//
+//     if(scan == K_ENTER || scan == K_KP_ENTER)
+//     {
+//             PrivateServerList_Connect_Click(NULL, me);
+//             return true;
+//     }
+//     else if(scan == K_MOUSE2 || scan == K_SPACE)
+//     {
+//             if(me.nItems != 0)
+//             {
+//                     m_play_click_sound(MENU_SOUND_OPEN);
+//                     main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
+//                     DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
+//                     return true;
+//             }
+//             return false;
+//     }
+//     else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
+//     {
+//             if(me.nItems != 0)
+//             {
+//                     me.toggleFavorite(me, me.selectedServer);
+//                     me.ipAddressBoxFocused = -1;
+//                     return true;
+//             }
+//             return false;
+//     }
+//     else if(SUPER(XonoticPrivateServerList).keyDown(me, scan, ascii, shift))
+//             return true;
+//     else if(!me.controlledTextbox)
+//             return false;
+//     else
+//             return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
+//}
+//
+//float XonoticPrivateServerList_getTotalHeight(entity me)
+//{
+//     float num_normal_rows = me.nItems;
+//     int num_headers = category_draw_count;
+//     return me.itemHeight * (num_normal_rows + me.categoriesHeight * num_headers);
+//}
+//int XonoticPrivateServerList_getItemAtPos(entity me, float pos)
+//{
+//     pos = pos / me.itemHeight;
+//     int i;
+//     for (i = category_draw_count - 1; i >= 0; --i) {
+//             int itemidx = category_item[i];
+//             float itempos = i * me.categoriesHeight + category_item[i];
+//             if (pos >= itempos + me.categoriesHeight + 1)
+//                     return itemidx + 1 + floor(pos - (itempos + me.categoriesHeight + 1));
+//             if (pos >= itempos)
+//                     return itemidx;
+//     }
+//     // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
+//     return floor(pos);
+//}
+//float XonoticPrivateServerList_getItemStart(entity me, int item)
+//{
+//     int i;
+//     for (i = category_draw_count - 1; i >= 0; --i) {
+//             int itemidx = category_item[i];
+//             float itempos = i * me.categoriesHeight + category_item[i];
+//             if (item >= itemidx + 1)
+//                     return (itempos + me.categoriesHeight + 1 + item - (itemidx + 1)) * me.itemHeight;
+//             if (item >= itemidx)
+//                     return itempos * me.itemHeight;
+//     }
+//     // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
+//     return item * me.itemHeight;
+//}
+//float XonoticPrivateServerList_getItemHeight(entity me, int item)
+//{
+//     int i;
+//     for (i = 0; i < category_draw_count; ++i) {
+//             // Matches exactly the headings with increased height.
+//             if (item == category_item[i])
+//                     return me.itemHeight * (me.categoriesHeight + 1);
+//     }
+//     return me.itemHeight;
+//}
+//
+//#endif
index 3f9648be66566f57ca681862ae82e1f9d4aa4ea5..a2996a115ea9d7ea2ea8c95d5461f02a1cc6df32 100644 (file)
@@ -979,9 +979,11 @@ void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool is
 
        vector oldscale = draw_scale;
        vector oldshift = draw_shift;
+#ifndef SET_YRANGE
 #define SET_YRANGE(start,end) \
        draw_scale = boxToGlobalSize(eX * 1 + eY * (end - start), oldscale); \
        draw_shift = boxToGlobal(eY * start, oldshift, oldscale);
+#endif
 
        for (j = 0; j < category_draw_count; ++j) {
                // Matches exactly the headings with increased height.