2 CLASS(XonoticServerList) EXTENDS(XonoticListBox)
3 METHOD(XonoticServerList, configureXonoticServerList, void(entity))
4 ATTRIB(XonoticServerList, rowsPerItem, float, 1)
5 METHOD(XonoticServerList, draw, void(entity))
6 METHOD(XonoticServerList, drawListBoxItem, void(entity, float, vector, float))
7 METHOD(XonoticServerList, clickListBoxItem, void(entity, float, vector))
8 METHOD(XonoticServerList, resizeNotify, void(entity, vector, vector, vector, vector))
9 METHOD(XonoticServerList, keyDown, float(entity, float, float, float))
11 ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85)
13 ATTRIB(XonoticServerList, realFontSize, vector, '0 0 0')
14 ATTRIB(XonoticServerList, realUpperMargin, float, 0)
15 ATTRIB(XonoticServerList, columnIconsOrigin, float, 0)
16 ATTRIB(XonoticServerList, columnIconsSize, float, 0)
17 ATTRIB(XonoticServerList, columnPingOrigin, float, 0)
18 ATTRIB(XonoticServerList, columnPingSize, float, 0)
19 ATTRIB(XonoticServerList, columnNameOrigin, float, 0)
20 ATTRIB(XonoticServerList, columnNameSize, float, 0)
21 ATTRIB(XonoticServerList, columnMapOrigin, float, 0)
22 ATTRIB(XonoticServerList, columnMapSize, float, 0)
23 ATTRIB(XonoticServerList, columnTypeOrigin, float, 0)
24 ATTRIB(XonoticServerList, columnTypeSize, float, 0)
25 ATTRIB(XonoticServerList, columnPlayersOrigin, float, 0)
26 ATTRIB(XonoticServerList, columnPlayersSize, float, 0)
28 ATTRIB(XonoticServerList, selectedServer, string, string_null) // to restore selected server when needed
29 METHOD(XonoticServerList, setSelected, void(entity, float))
30 METHOD(XonoticServerList, setSortOrder, void(entity, float, float))
31 ATTRIB(XonoticServerList, filterShowEmpty, float, 1)
32 ATTRIB(XonoticServerList, filterShowFull, float, 1)
33 ATTRIB(XonoticServerList, filterString, string, string_null)
34 ATTRIB(XonoticServerList, controlledTextbox, entity, NULL)
35 ATTRIB(XonoticServerList, ipAddressBox, entity, NULL)
36 ATTRIB(XonoticServerList, favoriteButton, entity, NULL)
37 ATTRIB(XonoticServerList, nextRefreshTime, float, 0)
38 METHOD(XonoticServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
39 ATTRIB(XonoticServerList, needsRefresh, float, 1)
40 METHOD(XonoticServerList, focusEnter, void(entity))
41 METHOD(XonoticServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
42 ATTRIB(XonoticServerList, sortButton1, entity, NULL)
43 ATTRIB(XonoticServerList, sortButton2, entity, NULL)
44 ATTRIB(XonoticServerList, sortButton3, entity, NULL)
45 ATTRIB(XonoticServerList, sortButton4, entity, NULL)
46 ATTRIB(XonoticServerList, sortButton5, entity, NULL)
47 ATTRIB(XonoticServerList, connectButton, entity, NULL)
48 ATTRIB(XonoticServerList, infoButton, entity, NULL)
49 ATTRIB(XonoticServerList, currentSortOrder, float, 0)
50 ATTRIB(XonoticServerList, currentSortField, float, -1)
51 ATTRIB(XonoticServerList, lastClickedServer, float, -1)
52 ATTRIB(XonoticServerList, lastClickedTime, float, 0)
54 ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1)
56 ATTRIB(XonoticServerList, seenIPv4, float, 0)
57 ATTRIB(XonoticServerList, seenIPv6, float, 0)
58 ENDCLASS(XonoticServerList)
59 entity makeXonoticServerList();
61 void ServerList_Connect_Click(entity btn, entity me);
62 void ServerList_ShowEmpty_Click(entity box, entity me);
63 void ServerList_ShowFull_Click(entity box, entity me);
64 void ServerList_Filter_Change(entity box, entity me);
65 void ServerList_Favorite_Click(entity btn, entity me);
66 void ServerList_Info_Click(entity btn, entity me);
67 void ServerList_Update_favoriteButton(entity btn, entity me);
69 #ifndef IMPLEMENTATION
70 float SLIST_FIELD_CNAME;
71 float SLIST_FIELD_PING;
72 float SLIST_FIELD_GAME;
73 float SLIST_FIELD_MOD;
74 float SLIST_FIELD_MAP;
75 float SLIST_FIELD_NAME;
76 float SLIST_FIELD_MAXPLAYERS;
77 float SLIST_FIELD_NUMPLAYERS;
78 float SLIST_FIELD_NUMHUMANS;
79 float SLIST_FIELD_NUMBOTS;
80 float SLIST_FIELD_PROTOCOL;
81 float SLIST_FIELD_FREESLOTS;
82 float SLIST_FIELD_PLAYERS;
83 float SLIST_FIELD_QCSTATUS;
84 float SLIST_FIELD_CATEGORY;
85 float SLIST_FIELD_ISFAVORITE;
86 float SLSF_DESCENDING = 1;
87 float SLSF_FAVORITES = 2;
88 float SLSF_CATEGORIES = 4;
90 #define SLIST_MAX_CATEGORIES 9
91 float category_name[SLIST_MAX_CATEGORIES];
92 float category_item[SLIST_MAX_CATEGORIES];
95 SLIST_CATEGORY(1, SLIST_CAT_FAVORITED, 1, _("Favorites")) \
96 SLIST_CATEGORY(1, SLIST_CAT_RECOMMENDED, 2, _("Recommended")) \
97 SLIST_CATEGORY(1, SLIST_CAT_NORMAL, 3, _("Normal Servers")) \
98 SLIST_CATEGORY(1, SLIST_CAT_SERVERS, 4, _("Servers")) \
99 SLIST_CATEGORY(1, SLIST_CAT_XPM, 5, _("Competitive Mode")) \
100 SLIST_CATEGORY(1, SLIST_CAT_MODIFIED, 6, _("Modified Servers")) \
101 SLIST_CATEGORY(1, SLIST_CAT_OVERKILL, 7, _("Overkill Mode")) \
102 SLIST_CATEGORY(1, SLIST_CAT_MINSTAGIB, 8, _("MinstaGib Mode")) \
103 SLIST_CATEGORY(1, SLIST_CAT_DEFRAG, 9, _("Defrag Mode"))
105 #define SLIST_CATEGORY(default,name,num,string) const float name = num;
107 #undef SLIST_CATEGORY
109 var float autocvar_menu_serverlist_purethreshold = 10;
110 var float autocvar_menu_serverlist_xpm_is_normal = TRUE;
115 #ifdef IMPLEMENTATION
116 float m_getserverlistentrycategory(float entry)
118 string s, k, v, modtype = "";
120 s = gethostcachestring(SLIST_FIELD_QCSTATUS, entry);
121 m = tokenizebyseparator(s, ":");
125 // typestr = argv(0);
126 // versionstr = argv(1);
132 for(j = 2; j < m; ++j)
136 k = substring(argv(j), 0, 1);
137 v = substring(argv(j), 1, -1);
138 if(k == "P") { impure = stof(v); }
139 else if(k == "M") { modtype = strtolower(v); }
142 //print(sprintf("modtype = %s\n", modtype));
144 if(impure > autocvar_menu_serverlist_purethreshold) { impure = TRUE; }
145 else { impure = FALSE; }
147 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, entry)) { return SLIST_CAT_FAVORITED; }
148 else if(modtype != "xonotic")
152 // old servers which don't report their mod name are considered modified now
153 case "": { return SLIST_CAT_MODIFIED; }
155 case "xpm": { return (autocvar_menu_serverlist_xpm_is_normal ? SLIST_CAT_NORMAL : SLIST_CAT_XPM); }
156 case "minstagib": { return SLIST_CAT_MINSTAGIB; }
157 case "overkill": { return SLIST_CAT_OVERKILL; }
159 // "cts" is allowed as compat, xdf is replacement
161 case "xdf": { return SLIST_CAT_DEFRAG; }
163 //if(modname != "CTS")
164 //if(modname != "NIX")
165 //if(modname != "NewToys")
167 default: { print(sprintf("Found strange mod type: %s\n", modtype)); return SLIST_CAT_MODIFIED; }
170 else { return (impure ? SLIST_CAT_MODIFIED : SLIST_CAT_NORMAL); }
172 // should never hit this point
173 error("wtf m_getserverlistentrycategory fail?");
177 float XonoticServerList_MapItems(float num)
181 if not(totcat) { return num; } // there are no categories to process
183 for(i = 0, n = 1; n <= totcat; ++i, ++n)
185 //print(sprintf("num: %d, i: %d, totcat: %d, category_item[i]: %d\n", num, i, totcat, category_item[i]));
186 if(category_item[i] == (num - i)) { /*print("inserting cat... \\/\n");*/ return -category_name[i]; }
187 else if(n == totcat) { /*print("end item... \\/\n");*/ return (num - n); }
188 else if((num - i) <= category_item[n]) { /*print("next item... \\/\n");*/ return (num - n); }
191 // should never hit this point
192 error("wtf XonoticServerList_MapItems fail?");
196 void ServerList_UpdateFieldIDs()
198 SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
199 SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
200 SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
201 SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
202 SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
203 SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
204 SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
205 SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
206 SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
207 SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
208 SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
209 SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
210 SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
211 SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
212 SLIST_FIELD_CATEGORY = gethostcacheindexforkey( "category" );
213 SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
216 float IsFavorite(string srv)
222 srv = netaddress_resolve(srv, 26000);
225 p = crypto_getidfp(srv);
226 n = tokenize_console(cvar_string("net_slist_favorites"));
227 for(i = 0; i < n; ++i)
229 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
237 if(srv == netaddress_resolve(argv(i), 26000))
244 void ToggleFavorite(string srv)
246 string s, s0, s1, s2, srv_resolved, p;
248 srv_resolved = netaddress_resolve(srv, 26000);
249 p = crypto_getidfp(srv_resolved);
250 s = cvar_string("net_slist_favorites");
251 n = tokenize_console(s);
253 for(i = 0; i < n; ++i)
255 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
263 if(srv_resolved != netaddress_resolve(argv(i), 26000))
268 s0 = substring(s, 0, argv_end_index(i - 1));
270 s2 = substring(s, argv_start_index(i + 1), -1);
271 if(s0 != "" && s2 != "")
273 cvar_set("net_slist_favorites", strcat(s0, s1, s2));
274 s = cvar_string("net_slist_favorites");
275 n = tokenize_console(s);
286 cvar_set("net_slist_favorites", strcat(s, s1, p));
288 cvar_set("net_slist_favorites", strcat(s, s1, srv));
294 void ServerList_Update_favoriteButton(entity btn, entity me)
296 if(IsFavorite(me.ipAddressBox.text))
297 me.favoriteButton.setText(me.favoriteButton, _("Remove"));
299 me.favoriteButton.setText(me.favoriteButton, _("Bookmark"));
302 entity makeXonoticServerList()
305 me = spawnXonoticServerList();
306 me.configureXonoticServerList(me);
309 void XonoticServerList_configureXonoticServerList(entity me)
311 me.configureXonoticListBox(me);
313 ServerList_UpdateFieldIDs();
317 void XonoticServerList_setSelected(entity me, float i)
320 save = me.selectedItem;
321 SUPER(XonoticServerList).setSelected(me, i);
323 if(me.selectedItem == save)
328 //if(XonoticServerList_MapItems(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)) != me.nItems)
329 // { error("^1XonoticServerList_setSelected(); ERROR: ^7Host cache viewcount mismatches nItems!\n"); return; } // sorry, it would be wrong
331 num = XonoticServerList_MapItems(me.selectedItem);
334 if(me.selectedServer)
335 strunzone(me.selectedServer);
336 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, num));
338 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
339 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
340 me.ipAddressBoxFocused = -1;
343 void XonoticServerList_refreshServerList(entity me, float mode)
345 // 0: just reparametrize
346 // 1: also ask for new servers
348 //print("refresh of type ", ftos(mode), "\n");
349 /* if(mode == 2) // borken
352 localcmd("net_slist\n");
353 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
359 string s, typestr, modstr;
362 m = strstrofs(s, ":", 0);
365 typestr = substring(s, 0, m);
366 s = substring(s, m + 1, strlen(s) - m - 1);
367 while(substring(s, 0, 1) == " ")
368 s = substring(s, 1, strlen(s) - 1);
373 modstr = cvar_string("menu_slist_modfilter");
375 m = SLIST_MASK_AND - 1;
376 resethostcachemasks();
378 // ping: reject negative ping (no idea why this happens in the first place, engine bug)
379 sethostcachemasknumber(++m, SLIST_FIELD_PING, 0, SLIST_TEST_GREATEREQUAL);
382 if(!me.filterShowFull)
384 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL); // legacy
385 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, ":S0:", SLIST_TEST_NOTCONTAIN); // g_maxplayers support
389 if(!me.filterShowEmpty)
390 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
392 // gametype filtering
394 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
399 if(substring(modstr, 0, 1) == "!")
400 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
402 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
406 n = tokenizebyseparator(_Nex_ExtResponseSystem_BannedServers, " ");
407 for(i = 0; i < n; ++i)
409 sethostcachemaskstring(++m, SLIST_FIELD_CNAME, argv(i), SLIST_TEST_NOTSTARTSWITH);
411 m = SLIST_MASK_OR - 1;
414 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
415 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
416 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
417 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
421 //listflags |= SLSF_FAVORITES;
422 listflags |= SLSF_CATEGORIES;
423 if(me.currentSortOrder < 0) { listflags |= SLSF_DESCENDING; }
424 sethostcachesort(me.currentSortField, listflags);
430 void XonoticServerList_focusEnter(entity me)
432 if(time < me.nextRefreshTime)
434 //print("sorry, no refresh yet\n");
437 me.nextRefreshTime = time + 10;
438 me.refreshServerList(me, 1);
441 void XonoticServerList_draw(entity me)
443 float i, found, owned, num;
445 if(_Nex_ExtResponseSystem_BannedServersNeedsRefresh)
449 _Nex_ExtResponseSystem_BannedServersNeedsRefresh = 0;
452 if(me.currentSortField == -1)
454 me.setSortOrder(me, SLIST_FIELD_PING, +1);
455 me.refreshServerList(me, 2);
457 else if(me.needsRefresh == 1)
459 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
461 else if(me.needsRefresh == 2)
464 me.refreshServerList(me, 0);
467 owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
469 for(i = 0; i < SLIST_MAX_CATEGORIES; ++i) { category_name[i] = -1; category_item[i] = -1; }
472 float itemcount = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
473 //float visible = floor(me.scrollPos / me.itemHeight);
474 me.nItems = itemcount;
477 for(i = 0; i < itemcount; ++i)
479 cat = gethostcachenumber(SLIST_FIELD_CATEGORY, i);
484 category_name[totcat] = cat;
485 category_item[totcat] = i;
492 for(x = 0; x < totcat; ++x) { if(cat == category_name[x]) { found = 1; } }
495 category_name[totcat] = cat;
496 category_item[totcat] = i;
504 //print(sprintf("^1SERVERLIST_DRAW^7: servercount: %d, nitems: %d\n", itemcount, me.nItems));
506 me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
507 me.infoButton.disabled = ((me.nItems == 0) || !owned);
508 me.favoriteButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
511 if(me.selectedServer)
513 for(i = 0; i < me.nItems; ++i)
515 num = XonoticServerList_MapItems(i);
518 if(gethostcachestring(SLIST_FIELD_CNAME, num) == me.selectedServer)
520 if(i != me.selectedItem)
522 me.lastClickedServer = -1;
535 if(me.selectedItem >= me.nItems) { me.selectedItem = me.nItems - 1; }
536 if(me.selectedServer) { strunzone(me.selectedServer); }
538 num = XonoticServerList_MapItems(me.selectedItem);
539 if(num >= 0) { me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, num)); }
545 if(me.selectedServer != me.ipAddressBox.text)
547 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
548 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
549 me.ipAddressBoxFocused = -1;
553 if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
555 if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
556 ServerList_Update_favoriteButton(NULL, me);
557 me.ipAddressBoxFocused = me.ipAddressBox.focused;
560 SUPER(XonoticServerList).draw(me);
562 void ServerList_PingSort_Click(entity btn, entity me)
564 me.setSortOrder(me, SLIST_FIELD_PING, +1);
566 void ServerList_NameSort_Click(entity btn, entity me)
568 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
570 void ServerList_MapSort_Click(entity btn, entity me)
572 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
574 void ServerList_PlayerSort_Click(entity btn, entity me)
576 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
578 void ServerList_TypeSort_Click(entity btn, entity me)
583 m = strstrofs(s, ":", 0);
586 s = substring(s, 0, m);
587 while(substring(s, m+1, 1) == " ") // skip spaces
593 for(i = 1; ; i *= 2) // 20 modes ought to be enough for anyone
595 t = MapInfo_Type_ToString(i);
597 if(t == "") // it repeats (default case)
600 // choose the first one
601 s = MapInfo_Type_ToString(1);
606 // the type was found
607 // choose the next one
608 s = MapInfo_Type_ToString(i * 2);
610 s = MapInfo_Type_ToString(1);
617 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
619 me.controlledTextbox.setText(me.controlledTextbox, s);
620 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
621 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
622 //ServerList_Filter_Change(me.controlledTextbox, me);
624 void ServerList_Filter_Change(entity box, entity me)
627 strunzone(me.filterString);
629 me.filterString = strzone(box.text);
631 me.filterString = string_null;
632 me.refreshServerList(me, 0);
634 me.ipAddressBox.setText(me.ipAddressBox, "");
635 me.ipAddressBox.cursorPos = 0;
636 me.ipAddressBoxFocused = -1;
638 void ServerList_ShowEmpty_Click(entity box, entity me)
640 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
641 me.refreshServerList(me, 0);
643 me.ipAddressBox.setText(me.ipAddressBox, "");
644 me.ipAddressBox.cursorPos = 0;
645 me.ipAddressBoxFocused = -1;
647 void ServerList_ShowFull_Click(entity box, entity me)
649 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
650 me.refreshServerList(me, 0);
652 me.ipAddressBox.setText(me.ipAddressBox, "");
653 me.ipAddressBox.cursorPos = 0;
654 me.ipAddressBoxFocused = -1;
656 void XonoticServerList_setSortOrder(entity me, float fld, float direction)
658 if(me.currentSortField == fld)
659 direction = -me.currentSortOrder;
660 me.currentSortOrder = direction;
661 me.currentSortField = fld;
662 me.sortButton1.forcePressed = (fld == SLIST_FIELD_PING);
663 me.sortButton2.forcePressed = (fld == SLIST_FIELD_NAME);
664 me.sortButton3.forcePressed = (fld == SLIST_FIELD_MAP);
665 me.sortButton4.forcePressed = 0;
666 me.sortButton5.forcePressed = (fld == SLIST_FIELD_NUMHUMANS);
668 if(me.selectedServer)
669 strunzone(me.selectedServer);
670 me.selectedServer = string_null;
671 me.refreshServerList(me, 0);
673 void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
675 vector originInLBSpace, sizeInLBSpace;
676 originInLBSpace = eY * (-me.itemHeight);
677 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
679 vector originInDialogSpace, sizeInDialogSpace;
680 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
681 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
683 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
684 btn.Container_size_x = sizeInDialogSpace_x * theSize;
685 btn.setText(btn, theTitle);
686 btn.onClick = theFunc;
687 btn.onClickEntity = me;
690 void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
692 SUPER(XonoticServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
694 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
695 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
696 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
698 me.columnIconsOrigin = 0;
699 me.columnIconsSize = me.realFontSize_x * 4 * me.iconsSizeFactor;
700 me.columnPingSize = me.realFontSize_x * 3;
701 me.columnMapSize = me.realFontSize_x * 10;
702 me.columnTypeSize = me.realFontSize_x * 4;
703 me.columnPlayersSize = me.realFontSize_x * 5;
704 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnIconsSize - me.columnTypeSize - 5 * me.realFontSize_x;
705 me.columnPingOrigin = me.columnIconsOrigin + me.columnIconsSize + me.realFontSize_x;
706 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
707 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
708 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
709 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
711 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, _("Ping"), ServerList_PingSort_Click);
712 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, _("Host name"), ServerList_NameSort_Click);
713 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, _("Map"), ServerList_MapSort_Click);
714 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, _("Type"), ServerList_TypeSort_Click);
715 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, _("Players"), ServerList_PlayerSort_Click);
718 f = me.currentSortField;
721 me.currentSortField = -1;
722 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
725 void ServerList_Connect_Click(entity btn, entity me)
727 if(me.ipAddressBox.text == "")
728 localcmd("connect ", me.selectedServer, "\n");
730 localcmd("connect ", me.ipAddressBox.text, "\n");
732 void ServerList_Favorite_Click(entity btn, entity me)
735 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
738 ToggleFavorite(me.ipAddressBox.text);
739 me.ipAddressBoxFocused = -1;
742 void ServerList_Info_Click(entity btn, entity me)
744 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, XonoticServerList_MapItems(me.selectedItem));
745 DialogOpenButton_Click(me, main.serverInfoDialog);
747 void XonoticServerList_clickListBoxItem(entity me, float i, vector where)
749 float num = XonoticServerList_MapItems(i);
750 if(num == me.lastClickedServer)
751 if(time < me.lastClickedTime + 0.3)
754 ServerList_Connect_Click(NULL, me);
756 me.lastClickedServer = num;
757 me.lastClickedTime = time;
759 void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
761 // layout: Ping, Server name, Map name, NP, TP, MP
766 float m, pure, freeslots, j, sflags;
767 string s, typestr, versionstr, k, v, modname;
769 float cache = XonoticServerList_MapItems(i);
770 //print(sprintf("time: %f, i: %d, cache: %d, nitems: %d\n", time, i, cache, me.nItems));
776 #define SLIST_CATEGORY(default,name,num,string) case num: { draw_Text(me.realUpperMargin * eY + (me.columnNameOrigin + (me.columnNameSize - draw_TextWidth(string, 0, me.realFontSize)) * 0.5) * eX, string, me.realFontSize, '1 1 1', SKINALPHA_TEXT, 0); return; }
778 #undef SLIST_CATEGORY
784 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
786 s = gethostcachestring(SLIST_FIELD_QCSTATUS, cache);
787 m = tokenizebyseparator(s, ":");
792 versionstr = argv(1);
798 for(j = 2; j < m; ++j)
802 k = substring(argv(j), 0, 1);
803 v = substring(argv(j), 1, -1);
814 #ifdef COMPAT_NO_MOD_IS_XONOTIC
820 SLIST_FIELD_MOD = gethostcacheindexforkey("mod");
821 s = gethostcachestring(SLIST_FIELD_MOD, cache);
823 if(modname == "Xonotic")
827 // list the mods here on which the pure server check actually works
828 if(modname != "Xonotic")
829 if(modname != "MinstaGib")
832 if(modname != "NewToys")
835 if(gethostcachenumber(SLIST_FIELD_FREESLOTS, cache) <= 0)
836 theAlpha = SKINALPHA_SERVERLIST_FULL;
837 else if(freeslots == 0)
838 theAlpha = SKINALPHA_SERVERLIST_FULL; // g_maxplayers support
839 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, cache))
840 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
844 p = gethostcachenumber(SLIST_FIELD_PING, cache);
847 #define PING_HIGH 500
849 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
850 else if(p < PING_MED)
851 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
852 else if(p < PING_HIGH)
854 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
855 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
860 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
863 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, cache))
865 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
866 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
869 s = gethostcachestring(SLIST_FIELD_CNAME, cache);
872 if(substring(s, 0, 1) == "[")
877 else if(strstrofs("0123456789", substring(s, 0, 1), 0) >= 0)
883 q = stof(substring(crypto_getencryptlevel(s), 0, 1));
884 if((q <= 0 && cvar("crypto_aeslevel") >= 3) || (q >= 3 && cvar("crypto_aeslevel") <= 0))
886 theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
887 theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
892 if(cvar("crypto_aeslevel") >= 2)
897 if(cvar("crypto_aeslevel") >= 1)
907 // 2: AES recommended but not available
908 // 3: AES possible and will be used
909 // 4: AES recommended and will be used
913 vector iconSize = '0 0 0';
914 iconSize_y = me.realFontSize_y * me.iconsSizeFactor;
915 iconSize_x = me.realFontSize_x * me.iconsSizeFactor;
917 vector iconPos = '0 0 0';
918 iconPos_x = (me.columnIconsSize - 3 * iconSize_x) * 0.5;
919 iconPos_y = (1 - iconSize_y) * 0.5;
923 if not(me.seenIPv4 && me.seenIPv6)
925 iconPos_x += iconSize_x * 0.5;
927 else if(me.seenIPv4 && me.seenIPv6)
931 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv6"), 0); // PRECACHE_PIC_MIPMAP
933 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv4"), 0); // PRECACHE_PIC_MIPMAP
935 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
936 iconPos_x += iconSize_x;
941 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_aeslevel", ftos(q)), 0); // PRECACHE_PIC_MIPMAP
942 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
944 iconPos_x += iconSize_x;
946 if(modname == "Xonotic")
950 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_pure1"), PRECACHE_PIC_MIPMAP);
951 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
956 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_", modname), PRECACHE_PIC_MIPMAP);
957 if(draw_PictureSize(n) == '0 0 0')
958 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_"), PRECACHE_PIC_MIPMAP);
960 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
962 draw_Picture(iconPos, n, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
964 iconPos_x += iconSize_x;
966 if(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS))
968 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_stats1"), 0); // PRECACHE_PIC_MIPMAP
969 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
971 iconPos_x += iconSize_x;
975 draw_Text(me.realUpperMargin * eY + (me.columnPingOrigin + me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
976 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, cache), me.columnNameSize, 0, me.realFontSize);
977 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
978 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, cache), me.columnMapSize, 0, me.realFontSize);
979 draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
980 s = draw_TextShortenToWidth(typestr, me.columnTypeSize, 0, me.realFontSize);
981 draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
982 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, cache)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, cache)));
983 draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
986 float XonoticServerList_keyDown(entity me, float scan, float ascii, float shift)
988 float i = XonoticServerList_MapItems(me.selectedItem);
991 org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
992 sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
994 if(scan == K_ENTER || scan == K_KP_ENTER)
996 ServerList_Connect_Click(NULL, me);
999 else if(scan == K_MOUSE2 || scan == K_SPACE)
1001 if((me.nItems != 0) && (i >= 0))
1003 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, i);
1004 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
1009 else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
1011 if((me.nItems != 0) && (i >= 0))
1013 ToggleFavorite(me.selectedServer);
1014 me.ipAddressBoxFocused = -1;
1019 else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift))
1021 else if(!me.controlledTextbox)
1024 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);