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, realFontSize, vector, '0 0 0')
12 ATTRIB(XonoticServerList, realUpperMargin, float, 0)
13 ATTRIB(XonoticServerList, columnPingOrigin, float, 0)
14 ATTRIB(XonoticServerList, columnPingSize, float, 0)
15 ATTRIB(XonoticServerList, columnNameOrigin, float, 0)
16 ATTRIB(XonoticServerList, columnNameSize, float, 0)
17 ATTRIB(XonoticServerList, columnMapOrigin, float, 0)
18 ATTRIB(XonoticServerList, columnMapSize, float, 0)
19 ATTRIB(XonoticServerList, columnTypeOrigin, float, 0)
20 ATTRIB(XonoticServerList, columnTypeSize, float, 0)
21 ATTRIB(XonoticServerList, columnPlayersOrigin, float, 0)
22 ATTRIB(XonoticServerList, columnPlayersSize, float, 0)
24 ATTRIB(XonoticServerList, selectedServer, string, string_null) // to restore selected server when needed
25 METHOD(XonoticServerList, setSelected, void(entity, float))
26 METHOD(XonoticServerList, setSortOrder, void(entity, float, float))
27 ATTRIB(XonoticServerList, filterShowEmpty, float, 1)
28 ATTRIB(XonoticServerList, filterShowFull, float, 1)
29 ATTRIB(XonoticServerList, filterString, string, string_null)
30 ATTRIB(XonoticServerList, controlledTextbox, entity, NULL)
31 ATTRIB(XonoticServerList, ipAddressBox, entity, NULL)
32 ATTRIB(XonoticServerList, favoriteButton, entity, NULL)
33 ATTRIB(XonoticServerList, nextRefreshTime, float, 0)
34 METHOD(XonoticServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
35 ATTRIB(XonoticServerList, needsRefresh, float, 1)
36 METHOD(XonoticServerList, focusEnter, void(entity))
37 METHOD(XonoticServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
38 ATTRIB(XonoticServerList, sortButton1, entity, NULL)
39 ATTRIB(XonoticServerList, sortButton2, entity, NULL)
40 ATTRIB(XonoticServerList, sortButton3, entity, NULL)
41 ATTRIB(XonoticServerList, sortButton4, entity, NULL)
42 ATTRIB(XonoticServerList, sortButton5, entity, NULL)
43 ATTRIB(XonoticServerList, connectButton, entity, NULL)
44 ATTRIB(XonoticServerList, infoButton, entity, NULL)
45 ATTRIB(XonoticServerList, currentSortOrder, float, 0)
46 ATTRIB(XonoticServerList, currentSortField, float, -1)
47 ATTRIB(XonoticServerList, lastClickedServer, float, -1)
48 ATTRIB(XonoticServerList, lastClickedTime, float, 0)
50 ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1)
51 ENDCLASS(XonoticServerList)
52 entity makeXonoticServerList();
53 void ServerList_Connect_Click(entity btn, entity me);
54 void ServerList_ShowEmpty_Click(entity box, entity me);
55 void ServerList_ShowFull_Click(entity box, entity me);
56 void ServerList_Filter_Change(entity box, entity me);
57 void ServerList_Favorite_Click(entity btn, entity me);
58 void ServerList_Info_Click(entity btn, entity me);
62 float SLIST_FIELD_CNAME;
63 float SLIST_FIELD_PING;
64 float SLIST_FIELD_GAME;
65 float SLIST_FIELD_MOD;
66 float SLIST_FIELD_MAP;
67 float SLIST_FIELD_NAME;
68 float SLIST_FIELD_MAXPLAYERS;
69 float SLIST_FIELD_NUMPLAYERS;
70 float SLIST_FIELD_NUMHUMANS;
71 float SLIST_FIELD_NUMBOTS;
72 float SLIST_FIELD_PROTOCOL;
73 float SLIST_FIELD_FREESLOTS;
74 float SLIST_FIELD_PLAYERS;
75 float SLIST_FIELD_QCSTATUS;
76 float SLIST_FIELD_ISFAVORITE;
77 void ServerList_UpdateFieldIDs()
79 SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
80 SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
81 SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
82 SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
83 SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
84 SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
85 SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
86 SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
87 SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
88 SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
89 SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
90 SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
91 SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
92 SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
93 SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
96 float IsFavorite(string srv)
102 srv = netaddress_resolve(srv, 26000);
103 p = crypto_getidfp(srv);
104 n = tokenize_console(cvar_string("net_slist_favorites"));
105 for(i = 0; i < n; ++i)
107 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
115 if(srv == netaddress_resolve(argv(i), 26000))
122 void ToggleFavorite(string srv)
124 string s, s0, s1, s2, srv_resolved, p;
126 srv_resolved = netaddress_resolve(srv, 26000);
127 p = crypto_getidfp(srv_resolved);
128 s = cvar_string("net_slist_favorites");
129 n = tokenize_console(s);
131 for(i = 0; i < n; ++i)
133 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
141 if(srv_resolved != netaddress_resolve(argv(i), 26000))
146 s0 = substring(s, 0, argv_end_index(i - 1));
148 s2 = substring(s, argv_start_index(i + 1), -1);
149 if(s0 != "" && s2 != "")
151 cvar_set("net_slist_favorites", strcat(s0, s1, s2));
152 s = cvar_string("net_slist_favorites");
153 n = tokenize_console(s);
164 cvar_set("net_slist_favorites", strcat(s, s1, p));
166 cvar_set("net_slist_favorites", strcat(s, s1, srv));
172 entity makeXonoticServerList()
175 me = spawnXonoticServerList();
176 me.configureXonoticServerList(me);
179 void XonoticServerList_configureXonoticServerList(entity me)
181 me.configureXonoticListBox(me);
183 ServerList_UpdateFieldIDs();
187 void XonoticServerList_setSelected(entity me, float i)
190 save = me.selectedItem;
191 SUPER(XonoticServerList).setSelected(me, i);
193 if(me.selectedItem == save)
198 if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
199 return; // sorry, it would be wrong
201 if(me.selectedServer)
202 strunzone(me.selectedServer);
203 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
205 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
206 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
207 me.ipAddressBoxFocused = -1;
209 void XonoticServerList_refreshServerList(entity me, float mode)
211 // 0: just reparametrize
212 // 1: also ask for new servers
214 //print("refresh of type ", ftos(mode), "\n");
215 /* if(mode == 2) // borken
218 localcmd("net_slist\n");
219 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
224 string s, typestr, modstr;
227 m = strstrofs(s, ":", 0);
230 typestr = substring(s, 0, m);
231 s = substring(s, m + 1, strlen(s) - m - 1);
232 while(substring(s, 0, 1) == " ")
233 s = substring(s, 1, strlen(s) - 1);
238 modstr = cvar_string("menu_slist_modfilter");
240 m = SLIST_MASK_AND - 1;
241 resethostcachemasks();
242 if(!me.filterShowFull)
243 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
244 if(!me.filterShowEmpty)
245 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
247 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
250 if(substring(modstr, 0, 1) == "!")
251 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
253 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
255 m = SLIST_MASK_OR - 1;
258 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
259 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
260 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
261 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
263 o = 2; // favorites first
264 if(me.currentSortOrder < 0)
265 o |= 1; // descending
266 sethostcachesort(me.currentSortField, o);
272 void XonoticServerList_focusEnter(entity me)
274 if(time < me.nextRefreshTime)
276 //print("sorry, no refresh yet\n");
279 me.nextRefreshTime = time + 10;
280 me.refreshServerList(me, 1);
282 void XonoticServerList_draw(entity me)
284 float i, found, owned;
286 if(me.currentSortField == -1)
288 me.setSortOrder(me, SLIST_FIELD_PING, +1);
289 me.refreshServerList(me, 2);
291 else if(me.needsRefresh == 1)
293 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
295 else if(me.needsRefresh == 2)
298 me.refreshServerList(me, 0);
301 owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
303 me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
305 me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
306 me.infoButton.disabled = ((me.nItems == 0) || !owned);
309 if(me.selectedServer)
311 for(i = 0; i < me.nItems; ++i)
312 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
314 if(i != me.selectedItem)
316 me.lastClickedServer = -1;
326 if(me.selectedItem >= me.nItems)
327 me.selectedItem = me.nItems - 1;
328 if(me.selectedServer)
329 strunzone(me.selectedServer);
330 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
335 if(me.selectedServer != me.ipAddressBox.text)
337 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
338 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
339 me.ipAddressBoxFocused = -1;
343 if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
345 if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
347 if(IsFavorite(me.ipAddressBox.text))
348 me.favoriteButton.setText(me.favoriteButton, "Remove");
350 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
352 me.ipAddressBoxFocused = me.ipAddressBox.focused;
355 SUPER(XonoticServerList).draw(me);
357 void ServerList_PingSort_Click(entity btn, entity me)
359 me.setSortOrder(me, SLIST_FIELD_PING, +1);
361 void ServerList_NameSort_Click(entity btn, entity me)
363 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
365 void ServerList_MapSort_Click(entity btn, entity me)
367 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
369 void ServerList_PlayerSort_Click(entity btn, entity me)
371 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
373 void ServerList_TypeSort_Click(entity btn, entity me)
378 m = strstrofs(s, ":", 0);
381 s = substring(s, 0, m);
382 while(substring(s, m+1, 1) == " ") // skip spaces
388 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
390 t = GametypeNameFromType(i);
392 if(t == GametypeNameFromType(0)) // it repeats (default case)
395 // choose the first one
399 if(s == GametypeNameFromType(i))
401 // the type was found
402 // choose the next one
403 s = GametypeNameFromType(i + 1);
404 if(s == GametypeNameFromType(0))
412 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
414 me.controlledTextbox.setText(me.controlledTextbox, s);
415 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
416 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
417 //ServerList_Filter_Change(me.controlledTextbox, me);
419 void ServerList_Filter_Change(entity box, entity me)
422 strunzone(me.filterString);
424 me.filterString = strzone(box.text);
426 me.filterString = string_null;
427 me.refreshServerList(me, 0);
429 me.ipAddressBox.setText(me.ipAddressBox, "");
430 me.ipAddressBox.cursorPos = 0;
431 me.ipAddressBoxFocused = -1;
433 void ServerList_ShowEmpty_Click(entity box, entity me)
435 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
436 me.refreshServerList(me, 0);
438 me.ipAddressBox.setText(me.ipAddressBox, "");
439 me.ipAddressBox.cursorPos = 0;
440 me.ipAddressBoxFocused = -1;
442 void ServerList_ShowFull_Click(entity box, entity me)
444 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
445 me.refreshServerList(me, 0);
447 me.ipAddressBox.setText(me.ipAddressBox, "");
448 me.ipAddressBox.cursorPos = 0;
449 me.ipAddressBoxFocused = -1;
451 void XonoticServerList_setSortOrder(entity me, float field, float direction)
453 if(me.currentSortField == field)
454 direction = -me.currentSortOrder;
455 me.currentSortOrder = direction;
456 me.currentSortField = field;
457 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
458 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
459 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
460 me.sortButton4.forcePressed = 0;
461 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
463 if(me.selectedServer)
464 strunzone(me.selectedServer);
465 me.selectedServer = string_null;
466 me.refreshServerList(me, 0);
468 void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
470 vector originInLBSpace, sizeInLBSpace;
471 originInLBSpace = eY * (-me.itemHeight);
472 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
474 vector originInDialogSpace, sizeInDialogSpace;
475 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
476 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
478 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
479 btn.Container_size_x = sizeInDialogSpace_x * theSize;
480 btn.setText(btn, theTitle);
481 btn.onClick = theFunc;
482 btn.onClickEntity = me;
485 void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
487 SUPER(XonoticServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
489 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
490 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
491 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
493 me.columnPingOrigin = 0;
494 me.columnPingSize = me.realFontSize_x * 4;
495 me.columnMapSize = me.realFontSize_x * 12;
496 me.columnTypeSize = me.realFontSize_x * 4;
497 me.columnPlayersSize = me.realFontSize_x * 6;
498 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
499 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
500 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
501 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
502 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
504 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
505 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
506 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
507 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
508 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
511 f = me.currentSortField;
514 me.currentSortField = -1;
515 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
518 void ServerList_Connect_Click(entity btn, entity me)
520 if(me.ipAddressBox.text == "")
521 localcmd("connect ", me.selectedServer, "\n");
523 localcmd("connect ", me.ipAddressBox.text, "\n");
525 void ServerList_Favorite_Click(entity btn, entity me)
528 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
531 ToggleFavorite(me.ipAddressBox.text);
532 me.ipAddressBoxFocused = -1;
535 void ServerList_Info_Click(entity btn, entity me)
537 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
538 DialogOpenButton_Click(me, main.serverInfoDialog);
540 void XonoticServerList_clickListBoxItem(entity me, float i, vector where)
542 if(i == me.lastClickedServer)
543 if(time < me.lastClickedTime + 0.3)
546 ServerList_Connect_Click(NULL, me);
548 me.lastClickedServer = i;
549 me.lastClickedTime = time;
551 void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
553 // layout: Ping, Server name, Map name, NP, TP, MP
560 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
562 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
563 theAlpha = SKINALPHA_SERVERLIST_FULL;
564 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
565 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
569 p = gethostcachenumber(SLIST_FIELD_PING, i);
572 #define PING_HIGH 500
574 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
575 else if(p < PING_MED)
576 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
577 else if(p < PING_HIGH)
579 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
580 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
585 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
588 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
590 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
591 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
594 s = gethostcachestring(SLIST_FIELD_CNAME, i);
595 q = stof(substring(crypto_getencryptlevel(s), 0, 1));
596 if((q <= 0 && cvar("crypto_aeslevel") >= 3) || (q >= 3 && cvar("crypto_aeslevel") <= 0))
598 theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
599 theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
601 // TODO show an icon for encryption status
604 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
605 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize, 0, me.realFontSize);
606 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
607 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize, 0, me.realFontSize);
608 draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
609 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
610 p = strstrofs(s, ":", 0);
612 s = substring(s, 0, p);
615 s = draw_TextShortenToWidth(s, me.columnMapSize, 0, me.realFontSize);
616 draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
617 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
618 draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
621 float XonoticServerList_keyDown(entity me, float scan, float ascii, float shift)
626 org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
627 sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
629 if(scan == K_ENTER || scan == K_KP_ENTER)
631 ServerList_Connect_Click(NULL, me);
634 else if(scan == K_MOUSE2 || scan == K_SPACE)
636 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
637 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
639 else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
644 ToggleFavorite(me.selectedServer);
645 me.ipAddressBoxFocused = -1;
648 else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift))
650 else if(!me.controlledTextbox)
653 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);