]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
list internal functions done; remove button done
authorBuddyFriendGuy <bfggeneral@gmail.com>
Tue, 9 Jun 2015 09:04:26 +0000 (05:04 -0400)
committerBuddyFriendGuy <bfggeneral@gmail.com>
Tue, 9 Jun 2015 09:04:26 +0000 (05:04 -0400)
qcsrc/menu/xonotic/dialog_multiplayer_join_private.qc
qcsrc/menu/xonotic/privateserverlist.qc

index c88464264f2262486787a2697c652e893dd69e50..2a71d77888db20f8b8bb97f933a595f8dc77472b 100644 (file)
@@ -22,61 +22,58 @@ entity makeXonoticPrivateServerListTab()
 }
 void XonoticPrivateServerListTab_fill(entity me)
 {
-       //entity e, slist;
-       entity e;
+       entity e, pslist;
 
-       //slist  = makeXonoticPrivateServerList();
+       pslist  = 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));
+               me.TD(me, 1, me.columns - 0.6, 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")));
+                       //e.onChangeEntity = pslist;
+                       //pslist.controlledTextbox = e;
 
        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.TD(me, 1, 1, pslist.sortButton1 = makeXonoticButton("Nickname", '0 0 0'));
+               me.TD(me, 1, 1, pslist.sortButton2 = makeXonoticButton("Address", '0 0 0'));
        me.TR(me);
-               //me.TD(me, me.rows - 5, me.columns, slist);
+               me.TD(me, me.rows - 7, me.columns, pslist);
 
-       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;
+       me.gotoRC(me, me.rows - 4, 0);
+               me.TD(me, 1, 0.4, e = makeXonoticTextLabel(0, _("Nickname:")));
+               me.TD(me, 1, me.columns - 0.4, e = makeXonoticInputBox(0, string_null));
+                       e.onEnter = PrivateServerList_Connect_Click;
+                       e.onEnterEntity = pslist;
+                       //e.onChange = PrivateServerList_Update_favoriteButton;
+                       e.onChangeEntity = pslist;
+                       pslist.nicknameBox = e;
+       me.TR(me);
+               me.TD(me, 1, 0.4, e = makeXonoticTextLabel(0, _("Address:")));
+               me.TD(me, 1, me.columns - 0.4, e = makeXonoticInputBox(0, string_null));
+                       e.onEnter = PrivateServerList_Connect_Click;
+                       e.onEnterEntity = pslist;
                        //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.onChangeEntity = pslist;
+                       pslist.ipAddressBox = e;
+       me.TR(me);
+               me.TD(me, 1, 2.16, e = makeXonoticButton("Add", '0 0 0'));
+                       e.onClick = PrivateServerList_Add_Click;
+                       e.onClickEntity = pslist;
+                       pslist.addButton = e;
+               me.TD(me, 1, 2.16, e = makeXonoticButton(_("Update"), '0 0 0'));
                        //e.onClick = PrivateServerList_Info_Click;
-                       //e.onClickEntity = slist;
-                       //slist.infoButton = e;
+                       //e.onClickEntity = pslist;
+                       //pslist.infoButton = e;
+                       pslist.updateButton = e;
+               me.TD(me, 1, 2.16, e = makeXonoticButton("Remove", '0 0 0'));
+                       e.onClick = PrivateServerList_Remove_Click;
+                       e.onClickEntity = pslist;
+                       pslist.removeButton = e;
+               me.TDempty(me, 0.02);
        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;
+                       e.onClick = PrivateServerList_Connect_Click;
+                       e.onClickEntity = pslist;
+                       pslist.connectButton = e;
 }
 #endif
index c68597df9bc1e2a6eb4e72da2897456d4277f7fb..714948c2f8173ac51b2e7e68b52ccde98afed2ef 100644 (file)
@@ -1,58 +1,52 @@
-//#ifndef SERVERLIST_H
-//#define SERVERLIST_H
-//#include "listbox.qc"
-//CLASS(XonoticPrivateServerList, XonoticListBox)
-//     METHOD(XonoticPrivateServerList, configureXonoticPrivateServerList, void(entity))
+#ifndef PRIVATE_SERVERLIST_H
+#define PRIVATE_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, 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, realFontSize, vector, '0 0 0')
+       ATTRIB(XonoticPrivateServerList, realUpperMargin, float, 0)
+       ATTRIB(XonoticPrivateServerList, columnNicknameOrigin, float, 0)
+       ATTRIB(XonoticPrivateServerList, columnNicknameSize, float, 0)
+       ATTRIB(XonoticPrivateServerList, columnAddressOrigin, float, 0)
+       ATTRIB(XonoticPrivateServerList, columnAddressSize, float, 0)
+
+       ATTRIB(XonoticPrivateServerList, selectedServer, string, string_null) // to restore selected server when needed
+       ATTRIB(XonoticPrivateServerList, selectedServerNickname, 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, ipAddressBox, entity, NULL)
+       ATTRIB(XonoticPrivateServerList, nicknameBox, entity, NULL)
+       ATTRIB(XonoticPrivateServerList, addButton, entity, NULL)
+       ATTRIB(XonoticPrivateServerList, updateButton, entity, NULL)
+       ATTRIB(XonoticPrivateServerList, removeButton, entity, NULL)
+       ATTRIB(XonoticPrivateServerList, connectButton, entity, NULL)
+
 //     ATTRIB(XonoticPrivateServerList, nextRefreshTime, float, 0)
-//     METHOD(XonoticPrivateServerList, refreshPrivateServerList, void(entity, float)) // refresh mode: REFRESHSERVERLIST_*
+       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)
+
+       METHOD(XonoticPrivateServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
+       ATTRIB(XonoticPrivateServerList, sortButton1, entity, NULL)
+       ATTRIB(XonoticPrivateServerList, sortButton2, entity, NULL)
+
+       ATTRIB(XonoticPrivateServerList, currentSortOrder, float, 0)
+       ATTRIB(XonoticPrivateServerList, currentSortField, float, -1)
 //
-//     ATTRIB(XonoticPrivateServerList, ipAddressBoxFocused, float, -1)
+       ATTRIB(XonoticPrivateServerList, ipAddressBoxFocused, float, -1)
+       ATTRIB(XonoticPrivateServerList, nicknameBoxFocused, float, -1)
 //
 //     ATTRIB(XonoticPrivateServerList, seenIPv4, float, 0)
 //     ATTRIB(XonoticPrivateServerList, seenIPv6, float, 0)
 //     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
-//
+ENDCLASS(XonoticPrivateServerList)
+entity makeXonoticPrivateServerList();
+
+#ifndef IMPLEMENTATION
+
+const float REFRESHPRIVATESERVERLIST_RESORT = 0;    // sort the PRIVATESERVER list again
+const float REFRESHPRIVATESERVERLIST_REFILTER = 1;  // ..., also update filter and sort criteria
+
 //// 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;
-//     }
-//}
-//
+float getPrivateServerCount();
+string getPrivateServerInfoFromListByIndex(float idx, string key);
+float findInPrivateServerListByAddress(string address); // returns index if found; or -1 if not; -2 if error
+void removePrivateServerFromList(string address);
+void addPrivateServerToList(string address, string nickname);
+
+void PrivateServerList_Connect_Click(entity btn, entity me);
+void PrivateServerList_Remove_Click(entity btn, entity me);
+void PrivateServerList_Add_Click(entity btn, entity me);
+
+#endif
+#endif
+#ifdef IMPLEMENTATION
+float getPslistFieldIndex(string s)
+{
+       if (s == "Nickname")
+               return 1;
+       else if (s == "Address")
+               return 2;
+       else
+               return 0;
+}
 //bool IsServerInList(string list, string srv)
 //{
 //     string p;
 //             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);
-//}
+entity makeXonoticPrivateServerList()
+{
+       entity me;
+       me = NEW(XonoticPrivateServerList);
+       me.configureXonoticPrivateServerList(me);
+       return me;
+}
+void XonoticPrivateServerList_configureXonoticPrivateServerList(entity me)
+{
+       me.configureXonoticListBox(me);
+       me.nItems = getPrivateServerCount();
+}
+void XonoticPrivateServerList_setSelected(entity me, int i)
+{
+       SUPER(XonoticPrivateServerList).setSelected(me, i);
+       if(me.nItems == 0 || getPrivateServerCount() != me.nItems || i >= me.nItems)
+               return;
+
+       if(me.selectedServer)
+               strunzone(me.selectedServer);
+
+       me.selectedServer = strzone(getPrivateServerInfoFromListByIndex(i, "Address"));
+       me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
+       me.ipAddressBox.cursorPos = strlen(me.selectedServer);
+       me.ipAddressBoxFocused = -1;
+
+       if(me.selectedServerNickname)
+               strunzone(me.selectedServerNickname);
+
+       me.selectedServerNickname = strzone(getPrivateServerInfoFromListByIndex(i, "Nickname"));
+       me.nicknameBox.setText(me.nicknameBox, me.selectedServerNickname);
+       me.nicknameBox.cursorPos = strlen(me.selectedServerNickname);
+       me.nicknameBoxFocused = -1;
+
+       //me.addButton.disabled = true;
+       //me.updateButton.disabled = true;
+}
 //void XonoticPrivateServerList_focusEnter(entity me)
 //{
 //     SUPER(XonoticPrivateServerList).focusEnter(me);
 //     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
+void XonoticPrivateServerList_draw(entity me)
+{
+       me.refreshPrivateServerList(me, REFRESHPRIVATESERVERLIST_RESORT);
+       me.connectButton.disabled = (me.ipAddressBox.text == "");
+       SUPER(XonoticPrivateServerList).draw(me);
+}
+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 == getPslistFieldIndex("Nickname"));
+       me.sortButton2.forcePressed = (fld == getPslistFieldIndex("Address"));
+       me.selectedItem = 0;
+       if(me.selectedServer)
+               strunzone(me.selectedServer);
+       me.selectedServer = string_null;
+       //me.refreshPrivateServerList(me, REFRESHSERVERLIST_REFILTER);
+}
+void XonoticPrivateServerList_refreshPrivateServerList(entity me, float mode)
+{
+       if (mode == REFRESHSERVERLIST_RESORT)
+               me.nItems = getPrivateServerCount();
+       // update selected
+       if (me.selectedItem > me.nItems)
+               me.selectedItem = me.nItems;
+       me.setSelected(me, me.selectedItem);    
+}
+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 PrivateServerList_NicknameSort_Click(entity btn, entity me)
+{
+       me.setSortOrder(me, getPslistFieldIndex("Nickname"), +1);
+}
+void PrivateServerList_AddressSort_Click(entity btn, entity me)
+{
+       me.setSortOrder(me, getPslistFieldIndex("Address"), +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.columnAddressSize = me.realFontSize.x * 22;
+       me.columnNicknameSize = 1 - me.columnAddressSize - me.realFontSize.x * 2;
+       me.columnNicknameOrigin = 0;
+       me.columnAddressOrigin = me.columnNicknameOrigin + me.columnNicknameSize + me.realFontSize.x;
+
+       me.positionSortButton(me, me.sortButton1, me.columnNicknameOrigin, me.columnNicknameSize, _("Nickname"), PrivateServerList_NicknameSort_Click);
+       me.positionSortButton(me, me.sortButton2, me.columnAddressOrigin, me.columnAddressSize, _("Address"), PrivateServerList_AddressSort_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_Remove_Click(entity btn, entity me)
+{
+       if (me.nItems == 0 || me.ipAddressBox.text == "")
+               return;
+       removePrivateServerFromList(me.ipAddressBox.text);
+}
+void PrivateServerList_Add_Click(entity btn, entity me)
+{
+       if (me.ipAddressBox.text == "" || me.nicknameBox.text == "")
+               return;
+       addPrivateServerToList(me.ipAddressBox.text, me.nicknameBox.text);
+}
+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)
+{
+       vector theColor;
+       float theAlpha;
+       string s, nickname, address;
+
+       theAlpha = 1;
+       theColor = SKINCOLOR_SERVERLIST_FAVORITE;
+
+       if(isSelected)
+               draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
+       // layout: Nickname, Address:Port
+       
+       nickname = getPrivateServerInfoFromListByIndex(i, "Nickname");
+       s = draw_TextShortenToWidth(nickname, me.columnNicknameSize, 0, me.realFontSize);
+       draw_Text(me.realUpperMargin * eY + me.columnNicknameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
+
+       address = getPrivateServerInfoFromListByIndex(i, "Address");
+       s = draw_TextShortenToWidth(address, me.columnAddressSize, 0, me.realFontSize);
+       draw_Text(me.realUpperMargin * eY + me.columnAddressOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
+
+}
+
+// functions to manipulate net_private_server_list, which has the format:
+// 192.168.1.50:20006,Server name and descripiton delimited by backslash\[FE80:0000:0000:0000:0202:B3FF:FE1E:8329]:26001,another server
+float getPrivateServerCount()
+{
+       float count;
+       string ps = cvar_string("net_private_server_list");
+       count = tokenizebyseparator(ps, "\\");
+       return count;
+}
+string getPrivateServerInfoFromListByIndex(float idx, string key)
+{
+       float count;
+       string pss = cvar_string("net_private_server_list"); // private servers string
+       count = tokenizebyseparator(pss, "\\");
+       string ps1; // one server from private servers string
+       float delimiter_pos;
+
+       if (idx < 0 || idx > count) {
+               return "";
+       } else {
+               ps1 = argv(idx);
+               // the first comma is used to separate the server address and nickname
+               delimiter_pos = strstrofs(ps1, ",", 0);
+               if (delimiter_pos == -1) {
+                       return "";
+               }
+
+               if (key == "Address") {
+                       return substring(ps1, 0, delimiter_pos);        
+               } else if (key == "Nickname") {
+                       return substring(ps1, delimiter_pos+1, strlen(ps1)-delimiter_pos-1);
+               } else if (key == "All") {
+                       return ps1;
+               } else {
+                       return "";
+               }
+       }
+}
+float findInPrivateServerListByAddress(string address)
+{
+       float count, i;
+       if (address == "")
+               return -2;
+       address = strtolower(address);
+       count = getPrivateServerCount();
+       // TOOD consider compare after resolving
+       //string resolved = netaddress_resolve(address, port);
+       for (i = 0; i < count; i++) {
+               if (address == strtolower(getPrivateServerInfoFromListByIndex(i, "Address"))) {
+                       return i;
+               }
+       }
+       return -1;
+}
+void removePrivateServerFromList(string address)
+{
+       float count, i, searchIdx;
+       string newList = "";
+
+       if (address == "") {
+               return;
+       }
+
+       address = strtolower(address);
+       searchIdx = findInPrivateServerListByAddress(address);
+       if (searchIdx < 0) {
+               return;
+       }
+
+       string pss = cvar_string("net_private_server_list"); // private servers string
+       count = tokenizebyseparator(pss, "\\");
+       for (i = 0; i < count; i++) {
+               if (i == searchIdx) {
+                       // this item is the one to remove; skip it
+                       continue;
+               } else {
+                       // otherwise keep it in the list
+                       if (strlen(newList) == 0) {
+                               newList = argv(i);
+                       } else {
+                               newList = strcat(newList, "\\", argv(i));
+                       }
+               }
+       }
+       localcmd(sprintf("seta net_private_server_list \"%s\"", MakeConsoleSafe(newList)));
+       return;
+}
+void addPrivateServerToList(string address, string nickname)
+{
+       string newServer = "";
+       string pss = cvar_string("net_private_server_list"); // private servers string
+
+       if (findInPrivateServerListByAddress(address) >= 0) {
+               // this shouldn't happen since the button should've been disabled when there's a match
+               return;
+       }
+
+       newServer = sprintf("%s,%s", address, nickname);
+       // sanitize address and nickname
+       newServer = strreplace("\n", " ", newServer);
+       newServer = strreplace("\\", "/", newServer);
+    newServer = strreplace(";", ".", newServer);
+       
+       if (strlen(pss) == 0) {
+               pss = newServer;
+       } else {
+               pss = strcat(pss, "\\", newServer);
+       }
+       // TODO is there a length limit with cvar?
+       localcmd(sprintf("seta net_private_server_list \"%s\"", MakeConsoleSafe(pss)));
+       return;
+}
+
+#endif