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 4
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_CORE, 3, _("Core")) \
98 SLIST_CATEGORY(1, SLIST_CAT_MODIFIED, 4, _("Modified"))
100 #define SLIST_CATEGORY(default,name,num,string) const float name = num;
102 #undef SLIST_CATEGORY
107 #ifdef IMPLEMENTATION
108 float XonoticServerList_MapItems(float num)
112 if not(totcat) { return num; } // there are no categories to process
114 for(i = 0, n = 1; n <= totcat; ++i, ++n)
116 //print(sprintf("num: %d, i: %d, totcat: %d, category_item[i]: %d\n", num, i, totcat, category_item[i]));
117 if(category_item[i] == (num - i)) { return -category_name[i]; }
118 else if(num <= category_item[n]) { return (num - n); }
119 else if(n == totcat) { return (num - n); }
122 error("wtf mapitems fail?"); // should not really be hit
123 return FALSE; // so compiler shuts up
126 void ServerList_UpdateFieldIDs()
128 SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
129 SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
130 SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
131 SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
132 SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
133 SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
134 SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
135 SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
136 SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
137 SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
138 SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
139 SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
140 SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
141 SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
142 SLIST_FIELD_CATEGORY = gethostcacheindexforkey( "category" );
143 SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
146 float IsFavorite(string srv)
152 srv = netaddress_resolve(srv, 26000);
155 p = crypto_getidfp(srv);
156 n = tokenize_console(cvar_string("net_slist_favorites"));
157 for(i = 0; i < n; ++i)
159 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
167 if(srv == netaddress_resolve(argv(i), 26000))
174 void ToggleFavorite(string srv)
176 string s, s0, s1, s2, srv_resolved, p;
178 srv_resolved = netaddress_resolve(srv, 26000);
179 p = crypto_getidfp(srv_resolved);
180 s = cvar_string("net_slist_favorites");
181 n = tokenize_console(s);
183 for(i = 0; i < n; ++i)
185 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
193 if(srv_resolved != netaddress_resolve(argv(i), 26000))
198 s0 = substring(s, 0, argv_end_index(i - 1));
200 s2 = substring(s, argv_start_index(i + 1), -1);
201 if(s0 != "" && s2 != "")
203 cvar_set("net_slist_favorites", strcat(s0, s1, s2));
204 s = cvar_string("net_slist_favorites");
205 n = tokenize_console(s);
216 cvar_set("net_slist_favorites", strcat(s, s1, p));
218 cvar_set("net_slist_favorites", strcat(s, s1, srv));
224 void ServerList_Update_favoriteButton(entity btn, entity me)
226 if(IsFavorite(me.ipAddressBox.text))
227 me.favoriteButton.setText(me.favoriteButton, _("Remove"));
229 me.favoriteButton.setText(me.favoriteButton, _("Bookmark"));
232 entity makeXonoticServerList()
235 me = spawnXonoticServerList();
236 me.configureXonoticServerList(me);
239 void XonoticServerList_configureXonoticServerList(entity me)
241 me.configureXonoticListBox(me);
243 ServerList_UpdateFieldIDs();
247 void XonoticServerList_setSelected(entity me, float i)
250 save = me.selectedItem;
251 SUPER(XonoticServerList).setSelected(me, i);
253 if(me.selectedItem == save)
258 //if(XonoticServerList_MapItems(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)) != me.nItems)
259 // { error("^1XonoticServerList_setSelected(); ERROR: ^7Host cache viewcount mismatches nItems!\n"); return; } // sorry, it would be wrong
261 num = XonoticServerList_MapItems(me.selectedItem);
264 if(me.selectedServer)
265 strunzone(me.selectedServer);
266 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, num));
268 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
269 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
270 me.ipAddressBoxFocused = -1;
273 void XonoticServerList_refreshServerList(entity me, float mode)
275 // 0: just reparametrize
276 // 1: also ask for new servers
278 //print("refresh of type ", ftos(mode), "\n");
279 /* if(mode == 2) // borken
282 localcmd("net_slist\n");
283 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
289 string s, typestr, modstr;
292 m = strstrofs(s, ":", 0);
295 typestr = substring(s, 0, m);
296 s = substring(s, m + 1, strlen(s) - m - 1);
297 while(substring(s, 0, 1) == " ")
298 s = substring(s, 1, strlen(s) - 1);
303 modstr = cvar_string("menu_slist_modfilter");
305 m = SLIST_MASK_AND - 1;
306 resethostcachemasks();
308 // ping: reject negative ping (no idea why this happens in the first place, engine bug)
309 sethostcachemasknumber(++m, SLIST_FIELD_PING, 0, SLIST_TEST_GREATEREQUAL);
312 if(!me.filterShowFull)
314 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL); // legacy
315 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, ":S0:", SLIST_TEST_NOTCONTAIN); // g_maxplayers support
319 if(!me.filterShowEmpty)
320 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
322 // gametype filtering
324 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
329 if(substring(modstr, 0, 1) == "!")
330 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
332 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
336 n = tokenizebyseparator(_Nex_ExtResponseSystem_BannedServers, " ");
337 for(i = 0; i < n; ++i)
339 sethostcachemaskstring(++m, SLIST_FIELD_CNAME, argv(i), SLIST_TEST_NOTSTARTSWITH);
341 m = SLIST_MASK_OR - 1;
344 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
345 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
346 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
347 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
351 //listflags |= SLSF_FAVORITES;
352 listflags |= SLSF_CATEGORIES;
353 if(me.currentSortOrder < 0) { listflags |= SLSF_DESCENDING; }
354 sethostcachesort(me.currentSortField, listflags);
361 void XonoticServerList_focusEnter(entity me)
363 if(time < me.nextRefreshTime)
365 //print("sorry, no refresh yet\n");
368 me.nextRefreshTime = time + 10;
369 me.refreshServerList(me, 1);
372 void XonoticServerList_draw(entity me)
374 float i, found, owned, num;
376 if(_Nex_ExtResponseSystem_BannedServersNeedsRefresh)
380 _Nex_ExtResponseSystem_BannedServersNeedsRefresh = 0;
383 if(me.currentSortField == -1)
385 me.setSortOrder(me, SLIST_FIELD_PING, +1);
386 me.refreshServerList(me, 2);
388 else if(me.needsRefresh == 1)
390 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
392 else if(me.needsRefresh == 2)
395 me.refreshServerList(me, 0);
398 owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
400 for(i = 0; i < SLIST_MAX_CATEGORIES; ++i) { category_name[i] = -1; category_item[i] = -1; }
403 float itemcount = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
404 //float visible = floor(me.scrollPos / me.itemHeight);
405 me.nItems = itemcount;
407 float favorites_drawn = 0, core_drawn = 0;
409 for(i = 0; i < itemcount; ++i)
411 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
413 if not(favorites_drawn)
414 if(category_name[totcat] == -1)
416 category_name[totcat] = SLIST_CAT_FAVORITED;
417 category_item[totcat] = i;
420 ++me.nItems; // we just inserted a category, increase number of items
426 if(category_name[totcat] == -1)
428 category_name[totcat] = SLIST_CAT_CORE;
429 category_item[totcat] = i;
432 ++me.nItems; // we just inserted a category, increase number of items
437 print(sprintf("^1SERVERLIST_DRAW^7: servercount: %d, nitems: %d\n", itemcount, me.nItems));
439 me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
440 me.infoButton.disabled = ((me.nItems == 0) || !owned);
441 me.favoriteButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
444 if(me.selectedServer)
446 for(i = 0; i < me.nItems; ++i)
448 num = XonoticServerList_MapItems(i);
451 if(gethostcachestring(SLIST_FIELD_CNAME, num) == me.selectedServer)
453 if(i != me.selectedItem)
455 me.lastClickedServer = -1;
468 if(me.selectedItem >= me.nItems) { me.selectedItem = me.nItems - 1; }
469 if(me.selectedServer) { strunzone(me.selectedServer); }
471 num = XonoticServerList_MapItems(me.selectedItem);
472 if(num >= 0) { me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, num)); }
478 if(me.selectedServer != me.ipAddressBox.text)
480 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
481 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
482 me.ipAddressBoxFocused = -1;
486 if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
488 if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
489 ServerList_Update_favoriteButton(NULL, me);
490 me.ipAddressBoxFocused = me.ipAddressBox.focused;
493 SUPER(XonoticServerList).draw(me);
495 void ServerList_PingSort_Click(entity btn, entity me)
497 me.setSortOrder(me, SLIST_FIELD_PING, +1);
499 void ServerList_NameSort_Click(entity btn, entity me)
501 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
503 void ServerList_MapSort_Click(entity btn, entity me)
505 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
507 void ServerList_PlayerSort_Click(entity btn, entity me)
509 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
511 void ServerList_TypeSort_Click(entity btn, entity me)
516 m = strstrofs(s, ":", 0);
519 s = substring(s, 0, m);
520 while(substring(s, m+1, 1) == " ") // skip spaces
526 for(i = 1; ; i *= 2) // 20 modes ought to be enough for anyone
528 t = MapInfo_Type_ToString(i);
530 if(t == "") // it repeats (default case)
533 // choose the first one
534 s = MapInfo_Type_ToString(1);
539 // the type was found
540 // choose the next one
541 s = MapInfo_Type_ToString(i * 2);
543 s = MapInfo_Type_ToString(1);
550 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
552 me.controlledTextbox.setText(me.controlledTextbox, s);
553 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
554 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
555 //ServerList_Filter_Change(me.controlledTextbox, me);
557 void ServerList_Filter_Change(entity box, entity me)
560 strunzone(me.filterString);
562 me.filterString = strzone(box.text);
564 me.filterString = string_null;
565 me.refreshServerList(me, 0);
567 me.ipAddressBox.setText(me.ipAddressBox, "");
568 me.ipAddressBox.cursorPos = 0;
569 me.ipAddressBoxFocused = -1;
571 void ServerList_ShowEmpty_Click(entity box, entity me)
573 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
574 me.refreshServerList(me, 0);
576 me.ipAddressBox.setText(me.ipAddressBox, "");
577 me.ipAddressBox.cursorPos = 0;
578 me.ipAddressBoxFocused = -1;
580 void ServerList_ShowFull_Click(entity box, entity me)
582 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
583 me.refreshServerList(me, 0);
585 me.ipAddressBox.setText(me.ipAddressBox, "");
586 me.ipAddressBox.cursorPos = 0;
587 me.ipAddressBoxFocused = -1;
589 void XonoticServerList_setSortOrder(entity me, float fld, float direction)
591 if(me.currentSortField == fld)
592 direction = -me.currentSortOrder;
593 me.currentSortOrder = direction;
594 me.currentSortField = fld;
595 me.sortButton1.forcePressed = (fld == SLIST_FIELD_PING);
596 me.sortButton2.forcePressed = (fld == SLIST_FIELD_NAME);
597 me.sortButton3.forcePressed = (fld == SLIST_FIELD_MAP);
598 me.sortButton4.forcePressed = 0;
599 me.sortButton5.forcePressed = (fld == SLIST_FIELD_NUMHUMANS);
601 if(me.selectedServer)
602 strunzone(me.selectedServer);
603 me.selectedServer = string_null;
604 me.refreshServerList(me, 0);
606 void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
608 vector originInLBSpace, sizeInLBSpace;
609 originInLBSpace = eY * (-me.itemHeight);
610 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
612 vector originInDialogSpace, sizeInDialogSpace;
613 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
614 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
616 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
617 btn.Container_size_x = sizeInDialogSpace_x * theSize;
618 btn.setText(btn, theTitle);
619 btn.onClick = theFunc;
620 btn.onClickEntity = me;
623 void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
625 SUPER(XonoticServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
627 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
628 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
629 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
631 me.columnIconsOrigin = 0;
632 me.columnIconsSize = me.realFontSize_x * 4 * me.iconsSizeFactor;
633 me.columnPingSize = me.realFontSize_x * 3;
634 me.columnMapSize = me.realFontSize_x * 10;
635 me.columnTypeSize = me.realFontSize_x * 4;
636 me.columnPlayersSize = me.realFontSize_x * 5;
637 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnIconsSize - me.columnTypeSize - 5 * me.realFontSize_x;
638 me.columnPingOrigin = me.columnIconsOrigin + me.columnIconsSize + me.realFontSize_x;
639 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
640 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
641 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
642 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
644 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, _("Ping"), ServerList_PingSort_Click);
645 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, _("Host name"), ServerList_NameSort_Click);
646 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, _("Map"), ServerList_MapSort_Click);
647 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, _("Type"), ServerList_TypeSort_Click);
648 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, _("Players"), ServerList_PlayerSort_Click);
651 f = me.currentSortField;
654 me.currentSortField = -1;
655 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
658 void ServerList_Connect_Click(entity btn, entity me)
660 if(me.ipAddressBox.text == "")
661 localcmd("connect ", me.selectedServer, "\n");
663 localcmd("connect ", me.ipAddressBox.text, "\n");
665 void ServerList_Favorite_Click(entity btn, entity me)
668 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
671 ToggleFavorite(me.ipAddressBox.text);
672 me.ipAddressBoxFocused = -1;
675 void ServerList_Info_Click(entity btn, entity me)
677 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, XonoticServerList_MapItems(me.selectedItem));
678 DialogOpenButton_Click(me, main.serverInfoDialog);
680 void XonoticServerList_clickListBoxItem(entity me, float i, vector where)
682 float num = XonoticServerList_MapItems(i);
683 if(num == me.lastClickedServer)
684 if(time < me.lastClickedTime + 0.3)
687 ServerList_Connect_Click(NULL, me);
689 me.lastClickedServer = num;
690 me.lastClickedTime = time;
692 void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
694 // layout: Ping, Server name, Map name, NP, TP, MP
699 float m, pure, freeslots, j, sflags;
700 string s, typestr, versionstr, k, v, modname;
702 float cache = XonoticServerList_MapItems(i);
703 print(sprintf("time: %f, i: %d, cache: %d, nitems: %d\n", time, i, cache, me.nItems));
709 #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; }
711 #undef SLIST_CATEGORY
717 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
719 s = gethostcachestring(SLIST_FIELD_QCSTATUS, cache);
720 m = tokenizebyseparator(s, ":");
725 versionstr = argv(1);
731 for(j = 2; j < m; ++j)
735 k = substring(argv(j), 0, 1);
736 v = substring(argv(j), 1, -1);
747 #ifdef COMPAT_NO_MOD_IS_XONOTIC
753 SLIST_FIELD_MOD = gethostcacheindexforkey("mod");
754 s = gethostcachestring(SLIST_FIELD_MOD, cache);
756 if(modname == "Xonotic")
760 // list the mods here on which the pure server check actually works
761 if(modname != "Xonotic")
762 if(modname != "MinstaGib")
765 if(modname != "NewToys")
768 if(gethostcachenumber(SLIST_FIELD_FREESLOTS, cache) <= 0)
769 theAlpha = SKINALPHA_SERVERLIST_FULL;
770 else if(freeslots == 0)
771 theAlpha = SKINALPHA_SERVERLIST_FULL; // g_maxplayers support
772 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, cache))
773 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
777 p = gethostcachenumber(SLIST_FIELD_PING, cache);
780 #define PING_HIGH 500
782 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
783 else if(p < PING_MED)
784 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
785 else if(p < PING_HIGH)
787 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
788 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
793 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
796 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, cache))
798 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
799 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
802 s = gethostcachestring(SLIST_FIELD_CNAME, cache);
805 if(substring(s, 0, 1) == "[")
810 else if(strstrofs("0123456789", substring(s, 0, 1), 0) >= 0)
816 q = stof(substring(crypto_getencryptlevel(s), 0, 1));
817 if((q <= 0 && cvar("crypto_aeslevel") >= 3) || (q >= 3 && cvar("crypto_aeslevel") <= 0))
819 theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
820 theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
825 if(cvar("crypto_aeslevel") >= 2)
830 if(cvar("crypto_aeslevel") >= 1)
840 // 2: AES recommended but not available
841 // 3: AES possible and will be used
842 // 4: AES recommended and will be used
846 vector iconSize = '0 0 0';
847 iconSize_y = me.realFontSize_y * me.iconsSizeFactor;
848 iconSize_x = me.realFontSize_x * me.iconsSizeFactor;
850 vector iconPos = '0 0 0';
851 iconPos_x = (me.columnIconsSize - 3 * iconSize_x) * 0.5;
852 iconPos_y = (1 - iconSize_y) * 0.5;
856 if not(me.seenIPv4 && me.seenIPv6)
858 iconPos_x += iconSize_x * 0.5;
860 else if(me.seenIPv4 && me.seenIPv6)
864 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv6"), 0); // PRECACHE_PIC_MIPMAP
866 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_ipv4"), 0); // PRECACHE_PIC_MIPMAP
868 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
869 iconPos_x += iconSize_x;
874 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_aeslevel", ftos(q)), 0); // PRECACHE_PIC_MIPMAP
875 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
877 iconPos_x += iconSize_x;
879 if(modname == "Xonotic")
883 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_pure1"), PRECACHE_PIC_MIPMAP);
884 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
889 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_", modname), PRECACHE_PIC_MIPMAP);
890 if(draw_PictureSize(n) == '0 0 0')
891 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_mod_"), PRECACHE_PIC_MIPMAP);
893 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
895 draw_Picture(iconPos, n, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
897 iconPos_x += iconSize_x;
899 if(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS))
901 draw_PreloadPictureWithFlags(n = strcat(SKINGFX_SERVERLIST_ICON, "_stats1"), 0); // PRECACHE_PIC_MIPMAP
902 draw_Picture(iconPos, n, iconSize, '1 1 1', 1);
904 iconPos_x += iconSize_x;
908 draw_Text(me.realUpperMargin * eY + (me.columnPingOrigin + me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
909 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, cache), me.columnNameSize, 0, me.realFontSize);
910 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
911 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, cache), me.columnMapSize, 0, me.realFontSize);
912 draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
913 s = draw_TextShortenToWidth(typestr, me.columnTypeSize, 0, me.realFontSize);
914 draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
915 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, cache)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, cache)));
916 draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
919 float XonoticServerList_keyDown(entity me, float scan, float ascii, float shift)
921 float i = XonoticServerList_MapItems(me.selectedItem);
924 org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
925 sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
927 if(scan == K_ENTER || scan == K_KP_ENTER)
929 ServerList_Connect_Click(NULL, me);
932 else if(scan == K_MOUSE2 || scan == K_SPACE)
934 if((me.nItems != 0) && (i >= 0))
936 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, i);
937 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
942 else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
944 if((me.nItems != 0) && (i >= 0))
946 ToggleFavorite(me.selectedServer);
947 me.ipAddressBoxFocused = -1;
952 else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift))
954 else if(!me.controlledTextbox)
957 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);