X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fmenu%2Fxonotic%2Fserverlist.c;h=24694d5f48fe5cd6eea23046ffef8a11b0b3b352;hb=f523e12334ce787dbff08a25dafab55886d9d108;hp=1a94c115dd2c5b75ab10af99d6547c6ea4c1ae2d;hpb=b31fdb3f0d23f507bb97e84b7a3171570f58fa61;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/menu/xonotic/serverlist.c b/qcsrc/menu/xonotic/serverlist.c index 1a94c115d..24694d5f4 100644 --- a/qcsrc/menu/xonotic/serverlist.c +++ b/qcsrc/menu/xonotic/serverlist.c @@ -7,6 +7,7 @@ CLASS(XonoticServerList) EXTENDS(XonoticListBox) METHOD(XonoticServerList, clickListBoxItem, void(entity, float, vector)) METHOD(XonoticServerList, resizeNotify, void(entity, vector, vector, vector, vector)) METHOD(XonoticServerList, keyDown, float(entity, float, float, float)) + METHOD(XonoticServerList, toggleFavorite, void(entity, string)) ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85) @@ -35,7 +36,7 @@ CLASS(XonoticServerList) EXTENDS(XonoticListBox) ATTRIB(XonoticServerList, ipAddressBox, entity, NULL) ATTRIB(XonoticServerList, favoriteButton, entity, NULL) ATTRIB(XonoticServerList, nextRefreshTime, float, 0) - METHOD(XonoticServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear + METHOD(XonoticServerList, refreshServerList, void(entity, float)) // refresh mode: REFRESHSERVERLIST_* ATTRIB(XonoticServerList, needsRefresh, float, 1) METHOD(XonoticServerList, focusEnter, void(entity)) METHOD(XonoticServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity))) @@ -48,6 +49,7 @@ CLASS(XonoticServerList) EXTENDS(XonoticListBox) ATTRIB(XonoticServerList, infoButton, entity, NULL) ATTRIB(XonoticServerList, currentSortOrder, float, 0) ATTRIB(XonoticServerList, currentSortField, float, -1) + ATTRIB(XonoticServerList, lastBumpSelectTime, float, 0) ATTRIB(XonoticServerList, lastClickedServer, float, -1) ATTRIB(XonoticServerList, lastClickedTime, float, 0) @@ -57,7 +59,56 @@ CLASS(XonoticServerList) EXTENDS(XonoticListBox) ATTRIB(XonoticServerList, seenIPv6, float, 0) ENDCLASS(XonoticServerList) entity makeXonoticServerList(); + +#ifndef IMPLEMENTATION +var float autocvar_menu_slist_categories = TRUE; +var float autocvar_menu_slist_categories_onlyifmultiple = TRUE; +var float autocvar_menu_slist_purethreshold = 10; +//var string autocvar_menu_slist_recommended = "76.124.107.5:26004"; + +// 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 +entity RetrieveCategoryEnt(float catnum); + +float IsServerInList(string list, string srv); +#define IsFavorite(srv) IsServerInList(cvar_string("net_slist_favorites"), srv) +#define IsRecommended(srv) IsServerInList(_Nex_ExtResponseSystem_RecommendedServers, srv) + +float CheckCategoryOverride(float cat); +float CheckCategoryForEntry(float entry); +float m_gethostcachecategory(float entry) { return CheckCategoryOverride(CheckCategoryForEntry(entry)); } + +void RegisterSLCategories(); + void ServerList_Connect_Click(entity btn, entity me); +void ServerList_Categories_Click(entity box, entity me); void ServerList_ShowEmpty_Click(entity box, entity me); void ServerList_ShowFull_Click(entity box, entity me); void ServerList_Filter_Change(entity box, entity me); @@ -65,44 +116,114 @@ void ServerList_Favorite_Click(entity btn, entity me); void ServerList_Info_Click(entity btn, entity me); void ServerList_Update_favoriteButton(entity btn, entity me); -float SLIST_FIELD_CNAME; -float SLIST_FIELD_PING; -float SLIST_FIELD_GAME; -float SLIST_FIELD_MOD; -float SLIST_FIELD_MAP; -float SLIST_FIELD_NAME; -float SLIST_FIELD_MAXPLAYERS; -float SLIST_FIELD_NUMPLAYERS; -float SLIST_FIELD_NUMHUMANS; -float SLIST_FIELD_NUMBOTS; -float SLIST_FIELD_PROTOCOL; -float SLIST_FIELD_FREESLOTS; -float SLIST_FIELD_PLAYERS; -float SLIST_FIELD_QCSTATUS; -float SLIST_FIELD_ISFAVORITE; -#endif +// fields for category entities +#define MAX_CATEGORIES 9 +#define CATEGORY_FIRST 1 +entity categories[MAX_CATEGORIES]; +float 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 +float category_name[MAX_CATEGORIES]; +float category_item[MAX_CATEGORIES]; +float category_draw_count; + +#define SLIST_CATEGORIES \ + SLIST_CATEGORY(CAT_FAVORITED, "", "", ZCTX(_("SLCAT^Favorites"))) \ + SLIST_CATEGORY(CAT_RECOMMENDED, "", "CAT_SERVERS", 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_MINSTAGIB, "", "CAT_SERVERS", ZCTX(_("SLCAT^MinstaGib 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) \ + float name; \ + var string SLIST_CATEGORY_AUTOCVAR(name) = enoverride; +SLIST_CATEGORIES +#undef SLIST_CATEGORY +#endif +#endif #ifdef IMPLEMENTATION -void ServerList_UpdateFieldIDs() -{ - SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" ); - SLIST_FIELD_PING = gethostcacheindexforkey( "ping" ); - SLIST_FIELD_GAME = gethostcacheindexforkey( "game" ); - SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" ); - SLIST_FIELD_MAP = gethostcacheindexforkey( "map" ); - SLIST_FIELD_NAME = gethostcacheindexforkey( "name" ); - SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" ); - SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" ); - SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" ); - SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" ); - SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" ); - SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" ); - SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" ); - SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" ); - SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" ); -} - -float IsFavorite(string srv) + +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 + + float 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 \ + { \ + print(sprintf( \ + "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(float 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 IsServerInList(string list, string srv) { string p; float i, n; @@ -112,7 +233,7 @@ float IsFavorite(string srv) if(srv == "") return FALSE; p = crypto_getidfp(srv); - n = tokenize_console(cvar_string("net_slist_favorites")); + 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) @@ -130,7 +251,90 @@ float IsFavorite(string srv) return FALSE; } -void ToggleFavorite(string srv) +float CheckCategoryOverride(float cat) +{ + entity catent = RetrieveCategoryEnt(cat); + if(catent) + { + float 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; + } +} + +float CheckCategoryForEntry(float entry) +{ + string s, k, v, modtype = ""; + float j, m, impure = 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); + if(k == "P") { impure = stof(v); } + else if(k == "M") { modtype = strtolower(v); } + } + + if(impure > autocvar_menu_slist_purethreshold) { impure = TRUE; } + else { impure = FALSE; } + + if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, entry)) { return CAT_FAVORITED; } + if(IsRecommended(gethostcachestring(SLIST_FIELD_CNAME, entry))) { return CAT_RECOMMENDED; } + else 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": { return CAT_MINSTAGIB; } + 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: { dprint(sprintf("Found strange mod type: %s\n", modtype)); return CAT_MODIFIED; } + } + } + else { return (impure ? CAT_MODIFIED : CAT_NORMAL); } + + // should never hit this point + error(sprintf("CheckCategoryForEntry(%d): Function fell through without normal return!\n", entry)); + return FALSE; +} + +float CheckItemNumber(float num) +{ + float i, n; + + if not(category_draw_count) { return num; } // there are no categories to process + + for(i = 0, n = 1; n <= category_draw_count; ++i, ++n) + { + if(category_item[i] == (num - i)) { return -category_name[i]; } + else if(n == category_draw_count) { return (num - n); } + else if((num - i) <= category_item[n]) { return (num - n); } + } + + // should never hit this point + error(sprintf("CheckItemNumber(%d): Function fell through without normal return!\n", num)); + return FALSE; +} + +void XonoticServerList_toggleFavorite(entity me, string srv) { string s, s0, s1, s2, srv_resolved, p; float i, n, f; @@ -177,15 +381,16 @@ void ToggleFavorite(string srv) cvar_set("net_slist_favorites", strcat(s, s1, srv)); } - resorthostcache(); + me.refreshServerList(me, REFRESHSERVERLIST_RESORT); } void ServerList_Update_favoriteButton(entity btn, entity me) { - if(IsFavorite(me.ipAddressBox.text)) - me.favoriteButton.setText(me.favoriteButton, _("Remove")); - else - me.favoriteButton.setText(me.favoriteButton, _("Bookmark")); + me.favoriteButton.setText(me.favoriteButton, + (IsFavorite(me.ipAddressBox.text) ? + _("Remove") : _("Bookmark") + ) + ); } entity makeXonoticServerList() @@ -199,13 +404,21 @@ void XonoticServerList_configureXonoticServerList(entity me) { me.configureXonoticListBox(me); - ServerList_UpdateFieldIDs(); + // update field ID's + #define SLIST_FIELD(suffix,name) SLIST_FIELD_##suffix = gethostcacheindexforkey(name); + SLIST_FIELDS + #undef SLIST_FIELD + // clear list me.nItems = 0; + + // build categories + RegisterSLCategories(); } void XonoticServerList_setSelected(entity me, float i) { - float save; + // todo: add logic to skip categories + float save, num; save = me.selectedItem; SUPER(XonoticServerList).setSelected(me, i); /* @@ -214,33 +427,68 @@ void XonoticServerList_setSelected(entity me, float i) */ 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)); + //if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != CheckItemNumber(me.nItems)) + // { error("^1XonoticServerList_setSelected(); ERROR: ^7Host cache viewcount mismatches nItems!\n"); return; } // sorry, it would be wrong + // ^ todo: make this work somehow? - me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer); - me.ipAddressBox.cursorPos = strlen(me.selectedServer); - me.ipAddressBoxFocused = -1; + #define SET_SELECTED_SERVER(cachenum) \ + if(me.selectedServer) { strunzone(me.selectedServer); } \ + me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, cachenum)); \ + me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer); \ + me.ipAddressBox.cursorPos = strlen(me.selectedServer); \ + me.ipAddressBoxFocused = -1; \ + return; + + num = CheckItemNumber(me.selectedItem); + + if(num >= 0) { SET_SELECTED_SERVER(num); } + else if(save > me.selectedItem) + { + if(me.selectedItem == 0) { return; } + else + { + if(me.lastClickedTime >= me.lastBumpSelectTime) + { + SUPER(XonoticServerList).setSelected(me, me.selectedItem - 1); + num = CheckItemNumber(me.selectedItem); + if(num >= 0) + { + me.lastBumpSelectTime = time; + SET_SELECTED_SERVER(num); + } + } + } + } + else if(save < me.selectedItem) + { + if(me.selectedItem == me.nItems) { return; } + else + { + if(me.lastClickedTime >= me.lastBumpSelectTime) + { + SUPER(XonoticServerList).setSelected(me, me.selectedItem + 1); + num = CheckItemNumber(me.selectedItem); + if(num >= 0) + { + me.lastBumpSelectTime = time; + SET_SELECTED_SERVER(num); + } + } + } + } } + void XonoticServerList_refreshServerList(entity me, float mode) { - // 0: just reparametrize - // 1: also ask for new servers - // 2: clear //print("refresh of type ", ftos(mode), "\n"); - /* if(mode == 2) // borken - { - // clear list - localcmd("net_slist\n"); - me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later - } - else */ + + if(mode >= REFRESHSERVERLIST_REFILTER) { - float m, o, i, n; // moin moin + float m, i, n; + float listflags = 0; string s, typestr, modstr; + s = me.filterString; m = strstrofs(s, ":", 0); @@ -300,14 +548,17 @@ void XonoticServerList_refreshServerList(entity me, float mode) sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS); sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH); } - o = 2; // favorites first - if(me.currentSortOrder < 0) - o |= 1; // descending - sethostcachesort(me.currentSortField, o); - resorthostcache(); - if(mode >= 1) - refreshhostcache(); + + // 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 XonoticServerList_focusEnter(entity me) { @@ -317,11 +568,12 @@ void XonoticServerList_focusEnter(entity me) return; } me.nextRefreshTime = time + 10; - me.refreshServerList(me, 1); + me.refreshServerList(me, REFRESHSERVERLIST_ASK); } + void XonoticServerList_draw(entity me) { - float i, found, owned; + float i, found, owned, num; if(_Nex_ExtResponseSystem_BannedServersNeedsRefresh) { @@ -330,10 +582,17 @@ void XonoticServerList_draw(entity me) _Nex_ExtResponseSystem_BannedServersNeedsRefresh = 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.refreshServerList(me, 2); + me.refreshServerList(me, REFRESHSERVERLIST_RESET); } else if(me.needsRefresh == 1) { @@ -342,12 +601,64 @@ void XonoticServerList_draw(entity me) else if(me.needsRefresh == 2) { me.needsRefresh = 0; - me.refreshServerList(me, 0); + me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); + } + else if(me.needsRefresh == 3) + { + me.needsRefresh = 0; + me.refreshServerList(me, REFRESHSERVERLIST_RESORT); } owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != "")); - me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT); + 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. + + float cat, x; + for(i = 0; i < itemcount; ++i) // FIXME this loop is TOTALLY unacceptable (O(servers)). Make it O(categories * log(servers)). Yes, that is possible. + { + cat = gethostcachenumber(SLIST_FIELD_CATEGORY, i); + if(cat) + { + if(category_draw_count == 0) + { + category_name[category_draw_count] = cat; + category_item[category_draw_count] = i; + ++category_draw_count; + ++me.nItems; + } + else + { + found = 0; + for(x = 0; x < category_draw_count; ++x) { if(cat == category_name[x]) { found = 1; } } + if not(found) + { + category_name[category_draw_count] = cat; + category_item[category_draw_count] = i; + ++category_draw_count; + ++me.nItems; + } + } + } + } + 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); @@ -357,27 +668,35 @@ void XonoticServerList_draw(entity me) if(me.selectedServer) { for(i = 0; i < me.nItems; ++i) - if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer) + { + num = CheckItemNumber(i); + if(num >= 0) { - if(i != me.selectedItem) + if(gethostcachestring(SLIST_FIELD_CNAME, num) == me.selectedServer) { - me.lastClickedServer = -1; - me.selectedItem = i; + if(i != me.selectedItem) + { + me.lastClickedServer = -1; + me.selectedItem = i; + } + found = 1; + break; } - found = 1; - 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(me.selectedItem >= me.nItems) { me.selectedItem = me.nItems - 1; } + if(me.selectedServer) { strunzone(me.selectedServer); } + num = CheckItemNumber(me.selectedItem); + if(num >= 0) { me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, num)); } + } + } + if(owned) { if(me.selectedServer != me.ipAddressBox.text) @@ -467,7 +786,23 @@ void ServerList_Filter_Change(entity box, entity me) me.filterString = strzone(box.text); else me.filterString = string_null; - me.refreshServerList(me, 0); + me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); + + me.ipAddressBox.setText(me.ipAddressBox, ""); + me.ipAddressBox.cursorPos = 0; + me.ipAddressBoxFocused = -1; +} +void ServerList_Categories_Click(entity box, entity me) +{ + box.setChecked(box, autocvar_menu_slist_categories = !autocvar_menu_slist_categories); + ///refreshhostcache(TRUE); + + //cvar_set("net_slist_pause", "0"); + //Destroy_Category_Entities(); + //CALL_ACCUMULATED_FUNCTION(RegisterSLCategories); + //me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); + + me.refreshServerList(me, REFRESHSERVERLIST_RESORT); me.ipAddressBox.setText(me.ipAddressBox, ""); me.ipAddressBox.cursorPos = 0; @@ -476,7 +811,7 @@ void ServerList_Filter_Change(entity box, entity me) void ServerList_ShowEmpty_Click(entity box, entity me) { box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty); - me.refreshServerList(me, 0); + me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); me.ipAddressBox.setText(me.ipAddressBox, ""); me.ipAddressBox.cursorPos = 0; @@ -485,7 +820,7 @@ void ServerList_ShowEmpty_Click(entity box, entity me) void ServerList_ShowFull_Click(entity box, entity me) { box.setChecked(box, me.filterShowFull = !me.filterShowFull); - me.refreshServerList(me, 0); + me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); me.ipAddressBox.setText(me.ipAddressBox, ""); me.ipAddressBox.cursorPos = 0; @@ -506,7 +841,7 @@ void XonoticServerList_setSortOrder(entity me, float fld, float direction) if(me.selectedServer) strunzone(me.selectedServer); me.selectedServer = string_null; - me.refreshServerList(me, 0); + me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); } void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc) { @@ -562,10 +897,11 @@ void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, } void ServerList_Connect_Click(entity btn, entity me) { - if(me.ipAddressBox.text == "") - localcmd("connect ", me.selectedServer, "\n"); - else - localcmd("connect ", me.ipAddressBox.text, "\n"); + localcmd(sprintf("connect %s\n", + ((me.ipAddressBox.text != "") ? + me.ipAddressBox.text : me.selectedServer + ) + )); } void ServerList_Favorite_Click(entity btn, entity me) { @@ -573,25 +909,29 @@ void ServerList_Favorite_Click(entity btn, entity me) ipstr = netaddress_resolve(me.ipAddressBox.text, 26000); if(ipstr != "") { - ToggleFavorite(me.ipAddressBox.text); + me.toggleFavorite(me, me.ipAddressBox.text); me.ipAddressBoxFocused = -1; } } void ServerList_Info_Click(entity btn, entity me) { - main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem); + main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, CheckItemNumber(me.selectedItem)); DialogOpenButton_Click(me, main.serverInfoDialog); } void XonoticServerList_clickListBoxItem(entity me, float i, vector where) { - if(i == me.lastClickedServer) - if(time < me.lastClickedTime + 0.3) - { - // DOUBLE CLICK! - ServerList_Connect_Click(NULL, me); - } - me.lastClickedServer = i; - me.lastClickedTime = time; + float num = CheckItemNumber(i); + if(num >= 0) + { + if(num == me.lastClickedServer) + if(time < me.lastClickedTime + 0.3) + { + // DOUBLE CLICK! + ServerList_Connect_Click(NULL, me); + } + me.lastClickedServer = num; + me.lastClickedTime = time; + } } void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float isSelected) { @@ -603,11 +943,34 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float float m, pure, freeslots, j, sflags; string s, typestr, versionstr, k, v, modname; + float item = CheckItemNumber(i); + //print(sprintf("time: %f, i: %d, item: %d, nitems: %d\n", time, i, item, me.nItems)); + + if(item < 0) + { + entity catent = RetrieveCategoryEnt(-item); + if(catent) + { + draw_Text( + eY * me.realUpperMargin + + + eX * (me.columnNameOrigin + (me.columnNameSize - draw_TextWidth(catent.cat_string, 0, me.realFontSize)) * 0.5), + catent.cat_string, + me.realFontSize, + '1 1 1', + SKINALPHA_TEXT, + 0 + ); + return; + } + } + if(isSelected) draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED); - s = gethostcachestring(SLIST_FIELD_QCSTATUS, i); + s = gethostcachestring(SLIST_FIELD_QCSTATUS, item); m = tokenizebyseparator(s, ":"); + typestr = ""; if(m >= 2) { typestr = argv(0); @@ -616,6 +979,7 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float freeslots = -1; sflags = -1; modname = ""; + pure = 0; for(j = 2; j < m; ++j) { if(argv(j) == "") @@ -639,7 +1003,7 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float /* SLIST_FIELD_MOD = gethostcacheindexforkey("mod"); - s = gethostcachestring(SLIST_FIELD_MOD, i); + s = gethostcachestring(SLIST_FIELD_MOD, item); if(s != "data") if(modname == "Xonotic") modname = s; @@ -649,18 +1013,20 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float if(modname != "Xonotic") if(modname != "MinstaGib") if(modname != "CTS") + if(modname != "NIX") + if(modname != "NewToys") pure = 0; - if(gethostcachenumber(SLIST_FIELD_FREESLOTS, i) <= 0) + if(gethostcachenumber(SLIST_FIELD_FREESLOTS, item) <= 0) theAlpha = SKINALPHA_SERVERLIST_FULL; else if(freeslots == 0) theAlpha = SKINALPHA_SERVERLIST_FULL; // g_maxplayers support - else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)) + else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, item)) theAlpha = SKINALPHA_SERVERLIST_EMPTY; else theAlpha = 1; - p = gethostcachenumber(SLIST_FIELD_PING, i); + p = gethostcachenumber(SLIST_FIELD_PING, item); #define PING_LOW 75 #define PING_MED 200 #define PING_HIGH 500 @@ -679,13 +1045,13 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float theAlpha *= SKINALPHA_SERVERLIST_HIGHPING; } - if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i)) + if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, item)) { 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); + s = gethostcachestring(SLIST_FIELD_CNAME, item); isv4 = isv6 = 0; if(substring(s, 0, 1) == "[") @@ -728,88 +1094,104 @@ void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float // 4: AES recommended and will be used // 5: AES required - { - vector iconSize; - iconSize_y = me.realFontSize_y * me.iconsSizeFactor; - iconSize_x = me.realFontSize_x * me.iconsSizeFactor; - - vector iconPos; - iconPos_x = (me.columnIconsSize - 3 * iconSize_x) * 0.5; - iconPos_y = (1 - iconSize_y) * 0.5; + // -------------- + // RENDER ICONS + // -------------- + vector iconSize = '0 0 0'; + iconSize_y = me.realFontSize_y * me.iconsSizeFactor; + iconSize_x = me.realFontSize_x * me.iconsSizeFactor; - string n; + vector iconPos = '0 0 0'; + iconPos_x = (me.columnIconsSize - 3 * iconSize_x) * 0.5; + iconPos_y = (1 - iconSize_y) * 0.5; - if not(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; - } + string n; - if(q > 0) - { - draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_aeslevel", ftos(q)), 0); // PRECACHE_PIC_MIPMAP + if not(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(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(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(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS)) + if(modname == "Xonotic") + { + if(pure == 0) { - draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_stats1"), 0); // PRECACHE_PIC_MIPMAP + draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_pure1"), PRECACHE_PIC_MIPMAP); draw_Picture(iconPos, n, iconSize, '1 1 1', 1); } - iconPos_x += iconSize_x; } + 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); - s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize, 0, me.realFontSize); + + // server name + s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, item), me.columnNameSize, 0, me.realFontSize); draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0); - s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize, 0, me.realFontSize); + + // server map + s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, item), 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); - s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))); + + // server playercount + s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, item)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, item))); draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0); } float XonoticServerList_keyDown(entity me, float scan, float ascii, float shift) { - float i; + float i = CheckItemNumber(me.selectedItem); 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); + me.lastBumpSelectTime = 0; + if(scan == K_ENTER || scan == K_KP_ENTER) { ServerList_Connect_Click(NULL, me); @@ -817,20 +1199,23 @@ float XonoticServerList_keyDown(entity me, float scan, float ascii, float shift) } else if(scan == K_MOUSE2 || scan == K_SPACE) { - if(me.nItems != 0) + if((me.nItems != 0) && (i >= 0)) { - main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem); + main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, i); DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz); + return 1; } + return 0; } else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS) { - i = me.selectedItem; - if(i < me.nItems) + if((me.nItems != 0) && (i >= 0)) { - ToggleFavorite(me.selectedServer); + me.toggleFavorite(me, me.selectedServer); me.ipAddressBoxFocused = -1; + return 1; } + return 0; } else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift)) return 1;