#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"
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()));
#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
--- /dev/null
+//#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
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.