Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
serverlist.qc
Go to the documentation of this file.
1#include "serverlist.qh"
2
3#include "checkbox.qh"
4#include "inputbox.qh"
5#include "mainwindow.qh"
7#include <common/mapinfo.qh>
8
9#define IsFavorite(srv) IsServerInList(cvar_string("net_slist_favorites"), srv)
10#define IsPromoted(srv) IsServerInList(_Nex_ExtResponseSystem_PromotedServers, srv)
11#define IsRecommended(srv) IsServerInList(_Nex_ExtResponseSystem_RecommendedServers, srv)
12
13float m_gethostcachecategory(float entry) { return CategoryOverride(CategoryForEntry(entry)); }
14
15void SL_ProcessCategoryOverrides(.string override_field_string, .float override_field)
16{
17 for (int i = 0; i < category_ent_count; ++i)
18 {
19 string s = categories[i].override_field_string;
20 if (s != "" && s != categories[i].cat_name)
21 {
22 int catnum = 0;
23 for (int x = 0; x < category_ent_count; ++x)
24 {
25 if(categories[x].cat_name == s)
26 {
27 catnum = x + 1;
28 break;
29 }
30 }
31 if (catnum)
32 {
33 strfree(categories[i].override_field_string);
34 categories[i].override_field = catnum;
35 continue;
36 }
37 LOG_INFOF("RegisterSLCategories(): Improper override '%s' for category '%s'!", s, categories[i].cat_name);
38 }
39 strfree(categories[i].override_field_string);
40 categories[i].override_field = 0;
41 }
42}
43
45{
46 entity cat;
47 #define SLIST_CATEGORY(name,enoverride,dioverride,str) \
48 SET_FIELD_COUNT(name, CATEGORY_FIRST, category_ent_count) \
49 CHECK_MAX_COUNT(name, MAX_CATEGORIES, category_ent_count, "SLIST_CATEGORY") \
50 cat = categories[name - 1] = new(slist_category); \
51 cat.cat_name = #name; \
52 cat.cat_enoverride_string = strzone(SLIST_CATEGORY_AUTOCVAR(name)); \
53 cat.cat_dioverride_string = strzone(dioverride); \
54 cat.cat_string = strzone(str);
56 #undef SLIST_CATEGORY
57
60}
61
62// Supporting Functions
64{
65 if((catnum > 0) && (catnum <= category_ent_count))
66 {
67 return categories[catnum - 1];
68 }
69 else
70 {
71 error(sprintf("RetrieveCategoryEnt(%d): Improper category number!\n", catnum));
72 return NULL;
73 }
74}
75
76bool IsServerInList(string list, string srv)
77{
78 if(srv == "")
79 return false;
80 srv = netaddress_resolve(srv, 26000);
81 if(srv == "")
82 return false;
83 string p = crypto_getidfp(srv);
84 int n = tokenize_console(list);
85 for(int i = 0; i < n; ++i)
86 {
87 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
88 {
89 if(p && argv(i) == p)
90 return true;
91 }
92 else
93 {
94 if(srv == netaddress_resolve(argv(i), 26000))
95 return true;
96 }
97 }
98 return false;
99}
100
102{
103 entity catent = RetrieveCategoryEnt(cat);
104 if(catent)
105 {
106 int override = (autocvar_menu_slist_categories ? catent.cat_enoverride : catent.cat_dioverride);
107 if(override) { return override; }
108 else { return cat; }
109 }
110 else
111 {
112 error(sprintf("CategoryOverride(%d): Improper category number!\n", cat));
113 return cat;
114 }
115}
116
117int CategoryForEntry(int entry)
118{
119 string s, k, v, modtype = "";
120 int j, m, impure = 0, freeslots = 0, sflags = 0;
121 s = gethostcachestring(SLIST_FIELD_QCSTATUS, entry);
122 m = tokenizebyseparator(s, ":");
123
124 for(j = 2; j < m; ++j)
125 {
126 if(argv(j) == "") { break; }
127 k = substring(argv(j), 0, 1);
128 v = substring(argv(j), 1, -1);
129 switch(k)
130 {
131 case "P": { impure = stof(v); break; }
132 case "S": { freeslots = stof(v); break; }
133 case "F": { sflags = stof(v); break; }
134 case "M": { modtype = strtolower(v); break; }
135 }
136 }
137
138 if(modtype != "xonotic") { impure += autocvar_menu_slist_modimpurity; }
139
140 // check if this server is favorited
141 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, entry)) { return CAT_FAVORITED; }
142
143 // now check if it's recommended
145 {
146 string cname = gethostcachestring(SLIST_FIELD_CNAME, entry);
147
148 if(IsPromoted(cname)) { return CAT_RECOMMENDED; }
149 else
150 {
151 float recommended = 0;
153 {
154 if(IsRecommended(cname)) { ++recommended; }
155 else { --recommended; }
156 }
158 {
159 if(
162
163 && // check for purity requirement
164 (
166 ||
168 )
169
170 && // check for minimum amount of humans
171 (
172 gethostcachenumber(SLIST_FIELD_NUMHUMANS, entry)
173 >=
175 )
176
177 && // check for maximum latency
178 (
179 gethostcachenumber(SLIST_FIELD_PING, entry)
180 <=
182 )
183 )
184 { ++recommended; }
185 else
186 { --recommended; }
187 }
188 if(recommended > 0) { return CAT_RECOMMENDED; }
189 }
190 }
191
192 // if not favorited or recommended, check modname
193 if(modtype != "xonotic")
194 {
195 switch(modtype)
196 {
197 // old servers which don't report their mod name are considered modified now
198 case "": { return CAT_MODIFIED; }
199
200 case "xpm": { return CAT_XPM; }
201 case "minstagib":
202 case "instagib": { return CAT_INSTAGIB; }
203 case "overkill": { return CAT_OVERKILL; }
204
205 // "cts" is allowed as compat, xdf is replacement
206 case "cts":
207 case "xdf": { return CAT_DEFRAG; }
208
209 default: { LOG_TRACEF("Found strange mod type: %s", modtype); return CAT_MODIFIED; }
210 }
211 }
212
213 // must be normal or impure server
214 //return ((impure > autocvar_menu_slist_purethreshold) ? CAT_MODIFIED : CAT_NORMAL);
215 return CAT_NORMAL; // don't separate normal and modified server categories
216}
217
219{
220 bool adding = true;
221 string srv_resolved = netaddress_resolve(srv, 26000);
222 string p = crypto_getidfp(srv_resolved);
223 string s = cvar_string("net_slist_favorites");
224 string ret = s;
225 for (int i = 0, n = tokenize_console(s); i < n; ++i)
226 {
227 bool match;
228 if (substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
229 {
230 // it's a pubkey hash
231 match = (p && p == argv(i));
232 }
233 else
234 {
235 // it's an ip
236 match = (srv_resolved == netaddress_resolve(argv(i), 26000));
237 }
238 if (!match) continue;
239 // on match: remove
240 adding = false;
241 string before = (i > 0) ? substring(s, 0, argv_end_index(i - 1)) : "";
242 string after = (i < n - 1) ? substring(s, argv_start_index(i + 1), -1) : "";
243 s = strcat(before, (before != "" && after != "" ? " " : ""), after);
244 ret = s;
245 // keep searching
246 // TODO: why not break here?
247 n = tokenize_console(s);
248 --i; // offset the increment that is about to happen
249 }
250 if (adding)
251 {
252 ret = strcat(s, (s != "" ? " " : ""), p ? p : srv);
253 }
254 cvar_set("net_slist_favorites", ret);
256}
257
259{
260 entity e = me.favoriteButton;
261 if(IsFavorite(me.ipAddressBox.text))
262 {
263 e.setText(e, ZCTX(_("SERVER^Remove favorite")));
264 setZonedTooltip(e, _("Remove the currently highlighted server from bookmarks"), string_null);
265 }
266 else
267 {
268 e.setText(e, ZCTX(_("SERVER^Favorite")));
269 setZonedTooltip(e, _("Bookmark the currently highlighted server so that it's faster to find in the future"), string_null);
270 }
271}
272
274{
276 me.configureXonoticServerList(me);
277 return me;
278}
280{
281 me.configureXonoticListBox(me);
282
283 // update field ID's
284 #define SLIST_FIELD(suffix,name) SLIST_FIELD_##suffix = gethostcacheindexforkey(name);
286 #undef SLIST_FIELD
287
288 // clear list
289 me.nItems = 0;
290}
292{
293 me.lockedSelectedItem = false;
294 //int save = me.selectedItem;
295 SUPER(XonoticServerList).setSelected(me, i);
296 /*
297 if(me.selectedItem == save)
298 return;
299 */
300 if(me.nItems == 0)
301 return;
303 return; // sorry, it would be wrong
304
305 strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
306
307 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
308 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
309 me.ipAddressBoxFocused = -1;
310}
312{
313 //print("refresh of type ", ftos(mode), "\n");
314
316 {
317 string s = me.filterString;
318
319 string typestr;
320 int m = strstrofs(s, ":", 0);
321 if(m >= 0)
322 {
323 typestr = substring(s, 0, m);
324 s = substring(s, m + 1, strlen(s) - m - 1);
325 while(substring(s, 0, 1) == " ")
326 s = substring(s, 1, strlen(s) - 1);
327 }
328 else
329 typestr = "";
330
331 string modstr = cvar_string("menu_slist_modfilter");
332
333 m = SLIST_MASK_AND - 1;
335
336 // show full button
337 if(!me.filterShowFull)
338 {
339 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL); // can connect
340 // There's several reasons to join a server where nJoinAllowed() reported 0 free slots, such as:
341 // to spec the match, chat, vote for a gametype with more players, or join your reserved tournament slot.
342 //sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, ":S0:", SLIST_TEST_NOTCONTAIN); // g_maxplayers support
343 }
344
345 // show empty button
346 if(!me.filterShowEmpty)
347 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
348
349 // show laggy button
350 if(!me.filterShowLaggy && autocvar_menu_slist_maxping > 0)
352
353 // gametype filtering
354 if(typestr != "")
355 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
356
357 // mod filtering
358 if(modstr != "")
359 {
360 if(substring(modstr, 0, 1) == "!")
361 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
362 else
363 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
364 }
365
366 // server banning
368 for(int i = 0; i < n; ++i)
369 if(argv(i) != "")
370 sethostcachemaskstring(++m, SLIST_FIELD_CNAME, argv(i), SLIST_TEST_NOTSTARTSWITH);
371
372 m = SLIST_MASK_OR - 1;
373 if(s != "")
374 {
375 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
376 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
377 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
378 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
379 }
380
381 int sorting_flags = 0;
382 //sorting_flags |= SLSF_FAVORITES;
383 sorting_flags |= SLSF_CATEGORIES;
384 if(me.currentSortOrder < 0) { sorting_flags |= SLSF_DESCENDING; }
385 sethostcachesort(me.currentSortField, sorting_flags);
386 }
387
389 if(mode >= REFRESHSERVERLIST_ASK)
391}
393{
394 SUPER(XonoticServerList).focusEnter(me);
395 if(time < me.nextRefreshTime)
396 {
397 //print("sorry, no refresh yet\n");
398 return;
399 }
400 me.nextRefreshTime = time + 10;
401 me.refreshServerList(me, REFRESHSERVERLIST_ASK);
402}
403
405{
406 me.serversHeight = (autocvar_menu_slist_categories > 0 ? 1.0 : me.categoriesHeight);
407
409 {
410 if(!me.needsRefresh)
411 me.needsRefresh = 2;
413 }
414
416 {
417 if(!me.needsRefresh)
418 me.needsRefresh = 3;
420 }
421
423 {
424 if(!me.needsRefresh)
425 me.needsRefresh = 3;
427 }
428
429 if(me.currentSortField == -1)
430 {
431 me.setSortOrder(me, SLIST_FIELD_PING, +1);
432 me.refreshServerList(me, REFRESHSERVERLIST_RESET);
433 }
434 else if(me.needsRefresh == 1)
435 {
436 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
437 }
438 else if(me.needsRefresh == 2)
439 {
440 me.needsRefresh = 0;
441 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
442 }
443 else if(me.needsRefresh == 3)
444 {
445 me.needsRefresh = 0;
446 me.refreshServerList(me, REFRESHSERVERLIST_RESORT);
447 }
448
449 bool owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
450
451 for(int i = 0; i < category_draw_count; ++i) { category_name[i] = -1; category_item[i] = -1; }
453
455
456 if(autocvar_menu_slist_categories >= 0) // if less than 0, don't even draw a category heading for favorites
457 {
458 int itemcount = me.nItems;
459
460 //float visible = floor(me.scrollPos / me.itemHeight);
461 // ^ unfortunately no such optimization can be made-- we must process through the
462 // entire list, otherwise there is no way to know which item is first in its category.
463
464 // binary search method suggested by div
465 float begin = 0;
466 for(int j = 1; j <= category_ent_count; ++j) {
467 float first = begin;
468 float last = (itemcount - 1);
469 if (first > last) {
470 // List is empty.
471 break;
472 }
473 float catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first);
474 float catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last);
475 if (catf > j) {
476 // The first one is already > j.
477 // Therefore, category j does not exist.
478 // Higher numbered categories do exist though.
479 } else if (catl < j) {
480 // The last one is < j.
481 // Thus this category - and any following -
482 // don't exist.
483 break;
484 } else if (catf == j) {
485 // Starts at first. This breaks the loop
486 // invariant in the binary search and thus has
487 // to be handled separately.
488 if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != j)
489 error("Category mismatch I");
490 if(first > 0)
491 if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == j)
492 error("Category mismatch II");
496 begin = first + 1;
497 } else {
498 // At this point, catf <= j < catl, thus
499 // catf < catl, thus first < last.
500 // INVARIANTS:
501 // last - first >= 1
502 // catf == gethostcachenumber(SLIST_FIELD_CATEGORY(first)
503 // catl == gethostcachenumber(SLIST_FIELD_CATEGORY(last)
504 // catf < j
505 // catl >= j
506 while (last - first > 1) {
507 float middle = floor((first + last) / 2);
508 // By loop condition, middle != first && middle != last.
509 float cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
510 if (cat >= j) {
511 last = middle;
512 catl = cat;
513 } else {
514 first = middle;
515 catf = cat;
516 }
517 }
518 if (catl == j) {
519 if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != j)
520 error("Category mismatch III");
521 if(last > 0)
522 if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == j)
523 error("Category mismatch IV");
527 begin = last + 1; // already scanned through these, skip 'em
528 }
529 else
530 begin = last; // already scanned through these, skip 'em
531 }
532 }
534 {
535 category_name[0] = -1;
536 category_item[0] = -1;
538 me.nItems = itemcount;
539 }
540 }
541
542 me.connectButton.disabled = (me.ipAddressBox.text == "");
543 me.infoButton.disabled = !owned;
544 me.favoriteButton.disabled = (me.ipAddressBox.text == "");
545
546 bool found = false;
547 if(me.lockedSelectedItem)
548 {
549 if(me.nItems > 0)
550 {
551 if(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem) != me.selectedServer)
552 {
553 strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
554 }
555 found = true;
556 }
557 }
558 else if(me.selectedServer)
559 {
560 for(int i = 0; i < me.nItems; ++i)
561 {
562 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
563 {
564 // don't follow the selected item with SUPER(XonoticServerList).setSelected(me, i);
565 me.selectedItem = i;
566 found = true;
567 break;
568 }
569 }
570 }
571 if(!found)
572 {
573 if(me.nItems > 0)
574 {
575 // selected server disappeared, select the last server (scrolling to it)
576 if(me.selectedItem >= me.nItems)
577 SUPER(XonoticServerList).setSelected(me, me.nItems - 1);
578 strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
579 }
580 }
581
582 if(owned)
583 {
584 if(me.selectedServer != me.ipAddressBox.text)
585 {
586 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
587 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
588 me.ipAddressBoxFocused = -1;
589 }
590 }
591
592 if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
593 {
594 if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
596 me.ipAddressBoxFocused = me.ipAddressBox.focused;
597 }
598
599 SUPER(XonoticServerList).draw(me);
600}
602{
603 me.setSortOrder(me, SLIST_FIELD_PING, +1);
604}
606{
607 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
608}
610{
611 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
612}
614{
615 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
616}
618{
619 string s = me.filterString;
620 int m = strstrofs(s, ":", 0);
621 if(m >= 0)
622 {
623 s = substring(s, 0, m);
624 while(substring(s, m+1, 1) == " ") // skip spaces
625 ++m;
626 }
627 else
628 s = "";
629
630 Gametype first = NULL; FOREACH(Gametypes, !first, first = it; break);
631 bool flag = false;
632 FOREACH(Gametypes, s == MapInfo_Type_ToString(it), {
633 // the type was found
634 // choose the next one
635 flag = true;
636 s = MapInfo_Type_ToString(REGISTRY_GET(Gametypes, it.m_id + 1));
637 if (s == "") s = MapInfo_Type_ToString(first);
638 break;
639 });
640 if (!flag) {
641 // no type was found
642 // choose the first one
643 s = MapInfo_Type_ToString(first);
644 }
645
646 if(s != "") s = strcat(s, ":");
647 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
648
649 me.controlledTextbox.setText(me.controlledTextbox, s);
650 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
651 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
652 //ServerList_Filter_Change(me.controlledTextbox, me);
653}
655{
656 strfree(me.filterString);
657 if(box.text != "")
658 me.filterString = strzone(box.text);
659 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
660
661 me.ipAddressBox.setText(me.ipAddressBox, "");
662 me.ipAddressBox.cursorPos = 0;
663 me.ipAddressBoxFocused = -1;
664}
666{
668 me.refreshServerList(me, REFRESHSERVERLIST_RESORT);
669
670 me.ipAddressBox.setText(me.ipAddressBox, "");
671 me.ipAddressBox.cursorPos = 0;
672 me.ipAddressBoxFocused = -1;
673}
675{
676 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
677 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
678
679 me.ipAddressBox.setText(me.ipAddressBox, "");
680 me.ipAddressBox.cursorPos = 0;
681 me.ipAddressBoxFocused = -1;
682}
684{
685 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
686 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
687
688 me.ipAddressBox.setText(me.ipAddressBox, "");
689 me.ipAddressBox.cursorPos = 0;
690 me.ipAddressBoxFocused = -1;
691}
693{
694 box.setChecked(box, me.filterShowLaggy = !me.filterShowLaggy);
695 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
696
697 me.ipAddressBox.setText(me.ipAddressBox, "");
698 me.ipAddressBox.cursorPos = 0;
699 me.ipAddressBoxFocused = -1;
700}
701void XonoticServerList_setSortOrder(entity me, int fld, int direction)
702{
703 if(me.currentSortField == fld)
704 direction = -me.currentSortOrder;
705 me.currentSortOrder = direction;
706 me.currentSortField = fld;
707 me.sortButton1.forcePressed = (fld == SLIST_FIELD_PING);
708 me.sortButton2.forcePressed = (fld == SLIST_FIELD_NAME);
709 me.sortButton3.forcePressed = (fld == SLIST_FIELD_MAP);
710 me.sortButton4.forcePressed = false;
711 me.sortButton5.forcePressed = (fld == SLIST_FIELD_NUMHUMANS);
712 me.selectedItem = 0;
713 strfree(me.selectedServer);
714 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
715}
716void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
717{
718 vector originInLBSpace = eY * (-me.itemHeight);
719 vector sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
720
721 vector originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
722 vector sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
723
724 btn.Container_origin_x = originInDialogSpace.x + sizeInDialogSpace.x * theOrigin;
725 btn.Container_size_x = sizeInDialogSpace.x * theSize;
726 btn.setText(btn, theTitle);
727 btn.onClick = theFunc;
728 btn.onClickEntity = me;
729 btn.resized = 1;
730}
731void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
732{
733 SUPER(XonoticServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
734
735 me.realFontSize_y = me.fontSize / (absSize.y * me.itemHeight);
736 me.realFontSize_x = me.fontSize / (absSize.x * (1 - me.controlWidth));
737 me.realUpperMargin = 0.5 * (1 - me.realFontSize.y);
738
739 me.columnIconsOrigin = 0;
740 me.columnIconsSize = me.realFontSize.x * 5 * me.iconsSizeFactor;
741 me.columnPingSize = me.realFontSize.x * 3;
742 me.columnMapSize = me.realFontSize.x * 10;
743 me.columnTypeSize = me.realFontSize.x * 4;
744 me.columnPlayersSize = me.realFontSize.x * 5;
745 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnIconsSize - me.columnTypeSize - 4 * me.realFontSize.x;
746 // no me.realFontSize.x separation between icons and ping columns because in practice they are already separated
747 me.columnPingOrigin = me.columnIconsOrigin + me.columnIconsSize;
748 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize.x;
749 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize.x;
750 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize.x;
751 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize.x;
752
753 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, _("Ping"), ServerList_PingSort_Click);
754 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, _("Hostname"), ServerList_NameSort_Click);
755 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, _("Map"), ServerList_MapSort_Click);
756 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, _("Type"), ServerList_TypeSort_Click);
757 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, _("Players"), ServerList_PlayerSort_Click);
758
759 int f = me.currentSortField;
760 if(f >= 0)
761 {
762 me.currentSortField = -1;
763 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
764 }
765}
767{
768 if (me.ipAddressBox.text != "")
769 localcmd(sprintf("connect %s\n", me.ipAddressBox.text));
770}
772{
773 string addr = this.ipAddressBox.text;
774 string ipstr = netaddress_resolve(addr, 26000);
775 if (ipstr == "") return;
777 this.toggleFavorite(this, addr);
778 this.ipAddressBoxFocused = -1;
779}
781{
782 if (me.nItems != 0)
783 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
784
785 float thisPos = me.getItemStart(me, me.selectedItem);
786 float thisHeight = me.getItemHeight(me, me.selectedItem);
787 vector org = boxToGlobal(eY * (thisPos - me.scrollPos), me.origin, me.size);
788 vector sz = boxToGlobalSize(eY * thisHeight + eX * (1 - me.controlWidth), me.size);
789 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
790}
798void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
799{
800 vector oldscale = draw_scale;
801 vector oldshift = draw_shift;
802#define SET_YRANGE(start,end) \
803 draw_scale = boxToGlobalSize(eX + eY * ((end) - (start)), oldscale); \
804 draw_shift = boxToGlobal(eY * (start), oldshift, oldscale);
805
806 int c;
807 for (c = 0; c < category_draw_count; ++c) {
808 // Matches exactly the headings with increased height.
809 if (i == category_item[c])
810 break;
811 }
812
813 if (c < category_draw_count)
814 {
816 if(catent)
817 {
819 (me.categoriesHeight - me.realFontSize.y) / (me.categoriesHeight + me.serversHeight),
820 me.categoriesHeight / (me.categoriesHeight + me.serversHeight)
821 );
822 draw_Text(
823 eY * me.realUpperMargin
824 +
825#if 0
826 eX * (me.columnNameOrigin + (me.columnNameSize - draw_TextWidth(catent.cat_string, 0, me.realFontSize)) * 0.5),
827 catent.cat_string,
828#else
829 eX * (me.columnNameOrigin),
830 strcat(catent.cat_string, ":"),
831#endif
832 me.realFontSize,
833 SKINCOLOR_SERVERLIST_CATEGORY,
834 SKINALPHA_SERVERLIST_CATEGORY,
835 0
836 );
837 SET_YRANGE(me.categoriesHeight / (me.categoriesHeight + me.serversHeight), 1);
838 }
839 }
840
841 // Now categories are done. Set the Y range in stone.
842 oldscale = draw_scale;
843 oldshift = draw_shift;
844
845 if(isSelected && !me.lockedSelectedItem)
846 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
847 else if(isFocused)
848 {
849 me.focusedItemAlpha = getFadedAlpha(me.focusedItemAlpha, SKINALPHA_LISTBOX_FOCUSED, SKINFADEALPHA_LISTBOX_FOCUSED);
850 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_FOCUSED, me.focusedItemAlpha);
851 }
852
853 string s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
854 int m = tokenizebyseparator(s, ":");
855 string typestr = "", versionstr = "";
856 if(m >= 2)
857 {
858 typestr = argv(0);
859 versionstr = argv(1);
860 }
861 bool pure = false, pure_available = false;
862 int qcfreeslots = -1, sflags = -1;
863 string modname = "";
864 for(int j = 2; j < m; ++j)
865 {
866 if(argv(j) == "")
867 break;
868 string key = substring(argv(j), 0, 1);
869 string value = substring(argv(j), 1, -1);
870 if(key == "P")
871 {
872 pure = (stof(value) == 0);
873 pure_available = true;
874 }
875 else if(key == "S")
876 qcfreeslots = stof(value); // from nJoinAllowed()
877 else if(key == "F")
878 sflags = stoi(value);
879 else if(key == "M")
880 modname = value;
881 }
882
883#ifdef COMPAT_NO_MOD_IS_XONOTIC
884 if(modname == "")
885 modname = "Xonotic";
886#endif
887
888 string original_modname = modname;
890
891 /*
892 SLIST_FIELD_MOD = gethostcacheindexforkey("mod");
893 s = gethostcachestring(SLIST_FIELD_MOD, i);
894 if(s != "data")
895 if(modname == "xonotic")
896 modname = s;
897 */
898
899 // list the mods here on which the pure server check actually works
900 if(modname != "xonotic")
901 if(modname != "instagib" || modname != "minstagib")
902 if(modname != "cts")
903 if(modname != "nix")
904 if(modname != "newtoys")
905 pure_available = false;
906
907 float theAlpha;
908 if(gethostcachenumber(SLIST_FIELD_FREESLOTS, i) <= 0) // can't connect
909 theAlpha = SKINALPHA_SERVERLIST_FULL;
910 else if(qcfreeslots == 0) // can connect but may not be able to play yet (duel, g_maxplayers, etc)
911 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
912 else if (!gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
913 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
914 else
915 theAlpha = 1;
916
917 float ping = gethostcachenumber(SLIST_FIELD_PING, i);
921 vector theColor;
922 // this colour selection code should match csqc Scoreboard_GetField()
923 if (ping < PING_LOW)
924 theColor = SKINCOLOR_SERVERLIST_LOWPING;
925 else if(ping < PING_MED)
926 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * ((ping - PING_LOW) / (PING_MED - PING_LOW));
927 else if(ping < PING_HIGH)
928 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((ping - PING_MED) / (PING_HIGH - PING_MED));
929 else
930 {
931 theColor = eX;
932 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((ping - PING_HIGH) / (PING_HIGH * 2 - PING_HIGH));
933 }
934
935 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
936 {
937 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
938 if (gethostcachenumber(SLIST_FIELD_FREESLOTS, i) > 0) // can connect
939 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
940 }
941
942 s = gethostcachestring(SLIST_FIELD_CNAME, i);
943
944 bool isv4 = false, isv6 = false;
945 if(substring(s, 0, 1) == "[")
946 {
947 isv6 = true;
948 ++me.seenIPv6;
949 }
950 else if(IS_DIGIT(substring(s, 0, 1)))
951 {
952 isv4 = true;
953 ++me.seenIPv4;
954 }
955
956 int crypto = stof(substring(crypto_getencryptlevel(s), 0, 1));
957 if((crypto <= 0 && cvar("crypto_aeslevel") >= 3) || (crypto >= 3 && cvar("crypto_aeslevel") <= 0))
958 {
959 theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
960 theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
961 }
962
963 if(crypto == 1)
964 {
965 if(cvar("crypto_aeslevel") >= 2)
966 crypto |= 4;
967 }
968 if(crypto == 2)
969 {
970 if(cvar("crypto_aeslevel") >= 1)
971 crypto |= 4;
972 }
973 if(crypto == 3)
974 crypto = 5;
975 else if(crypto >= 3)
976 crypto -= 2;
977 // possible status:
978 // 0: crypto off
979 // 1: AES possible
980 // 2: AES recommended but not available
981 // 3: AES possible and will be used
982 // 4: AES recommended and will be used
983 // 5: AES required
984
985 // --------------
986 // RENDER ICONS
987 // --------------
988 vector iconSize = '0 0 0';
989 iconSize_y = me.realFontSize.y * me.iconsSizeFactor / me.serversHeight;
990 iconSize_x = me.realFontSize.x * me.iconsSizeFactor;
991
992 vector iconPos = '0 0 0';
993 if(me.seenIPv4 && me.seenIPv6)
994 iconPos_x = (me.columnIconsSize - 4 * iconSize.x) * 0.5;
995 else
996 iconPos_x = (me.columnIconsSize - 3 * iconSize.x) * 0.5;
997 iconPos_y = (1 - iconSize.y) * 0.5;
998
999 // IP
1000 if(me.seenIPv4 && me.seenIPv6)
1001 {
1002 if(isv6)
1003 draw_Picture(iconPos, "icon_ipv6", iconSize, '1 1 1', 1);
1004 else if(isv4)
1005 draw_Picture(iconPos, "icon_ipv4", iconSize, '1 1 1', 1);
1006
1007 iconPos.x += iconSize.x;
1008 }
1009
1010 // AES
1011 if(crypto > 0)
1012 draw_Picture(iconPos, strcat("icon_aeslevel", ftos(crypto)), iconSize, '1 1 1', 1);
1013
1014 iconPos.x += iconSize.x;
1015
1016 // Mod
1017 if(modname == "xonotic")
1018 {
1019 // Here, pure_available should always be set. If not, consider
1020 // it an impurity.
1021 if(pure_available && pure)
1022 draw_Picture(iconPos, "icon_pure1", iconSize, '1 1 1', 1);
1023 }
1024 else
1025 {
1026 string icon = strcat("icon_mod_", modname);
1027 if(draw_PreloadPicture(icon) == "")
1028 icon = "icon_mod_";
1029
1030 // In mods, pure_available not being present indicates
1031 // non-support of the feature. Just show the mod icon as is
1032 // then.
1033 if(pure_available && !pure)
1034 draw_Picture(iconPos, icon, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
1035 else
1036 draw_Picture(iconPos, icon, iconSize, '1 1 1', 1);
1037 }
1038
1039 iconPos.x += iconSize.x;
1040
1041 // Stats
1042 if(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS))
1043 {
1044 if (sflags & SERVERFLAG_PLAYERSTATS_CUSTOM)
1045 draw_Picture(iconPos, "icon_mod_", iconSize, '1 1 1', 1); // TODO: custom stats server icon
1046 else
1047 draw_Picture(iconPos, "icon_stats1", iconSize, '1 1 1', 1);
1048 }
1049
1050 if(isFocused && me.mouseOverIcons && !me.tooltip)
1051 {
1052 string t = "";
1053 if(me.seenIPv4 && me.seenIPv6)
1054 t = strcat(t, (isv6) ? "IPv6, " : "IPv4, ");
1055 t = strcat(t, _("encryption:"), " ", (crypto ? sprintf(_("AES level %d"), crypto) : ZCTX(_("ENC^none"))), ", ");
1056 t = strcat(t, sprintf(_("mod: %s"), ((modname == "xonotic") ? ZCTX(_("MOD^Default")) : original_modname)));
1057 if(pure_available)
1058 t = strcat(t, sprintf(" (%s)", (pure) ? _("official settings") : _("modified settings")));
1059 t = strcat(t, ", ");
1060 t = strcat(t, ((sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS)) ? ((sflags & SERVERFLAG_PLAYERSTATS_CUSTOM) ? _("custom stats server") : _("stats enabled")) : _("stats disabled")));
1062 }
1063 // --------------
1064 // RENDER TEXT
1065 // --------------
1066
1067 // Center it in the row.
1068 SET_YRANGE(
1069 0.5 - 0.5 / me.serversHeight,
1070 0.5 + 0.5 / me.serversHeight
1071 );
1072
1073 // ping
1074 s = ftos(ping);
1075 draw_Text(me.realUpperMargin * eY + (me.columnPingOrigin + me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1076
1077 // server name
1078 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize, 0, me.realFontSize);
1079 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
1080
1081 // server map
1082 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize, 0, me.realFontSize);
1083 draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1084
1085 // server gametype
1086 s = draw_TextShortenToWidth(typestr, me.columnTypeSize, 0, me.realFontSize);
1087 draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1088
1089 // server playercount
1090 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
1091 draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1092}
1093
1098
1100{
1101 if(!SUPER(XonoticServerList).mouseMove(me, pos))
1102 {
1103 me.mouseOverIcons = false;
1104 clearTooltip(me);
1105 return 1;
1106 }
1107
1108 float iconSize_x = me.realFontSize.x * me.iconsSizeFactor;
1109 float iconPos_x;
1110 if(me.seenIPv4 && me.seenIPv6)
1111 iconPos_x = (me.columnIconsSize - 4 * iconSize_x) * 0.5;
1112 else
1113 iconPos_x = (me.columnIconsSize - 3 * iconSize_x) * 0.5;
1114 me.mouseOverIcons = (pos.x >= me.columnIconsOrigin + iconPos_x && pos.x <= me.columnIconsOrigin + me.columnIconsSize - iconPos_x);
1115 if(!me.mouseOverIcons)
1116 clearTooltip(me);
1117 return 1;
1118}
1119
1120bool XonoticServerList_keyDown(entity me, int scan, bool ascii, bool shift)
1121{
1122 if(scan == K_ENTER || scan == K_KP_ENTER)
1123 {
1125 return true;
1126 }
1127 else if(scan == K_MOUSE2 || scan == K_SPACE || ((shift & S_CTRL) && scan == K_MOUSE1))
1128 {
1129 if(me.nItems != 0)
1130 {
1131 float thisPos = me.getItemStart(me, me.selectedItem);
1132 float thisHeight = me.getItemHeight(me, me.selectedItem);
1133 vector org = boxToGlobal(eY * (thisPos - me.scrollPos), me.origin, me.size);
1134 vector sz = boxToGlobalSize(eY * thisHeight + eX * (1 - me.controlWidth), me.size);
1136 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
1137 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
1138 return true;
1139 }
1140 return false;
1141 }
1142 else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
1143 {
1144 if(me.nItems != 0)
1145 {
1146 me.toggleFavorite(me, me.selectedServer);
1147 me.ipAddressBoxFocused = -1;
1148 return true;
1149 }
1150 return false;
1151 }
1152 else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift))
1153 return true;
1154 else if(!me.controlledTextbox)
1155 return false;
1156 else
1157 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
1158}
1159
1161{
1162 float num_normal_rows = me.nItems;
1163 int num_headers = category_draw_count;
1164 return me.itemHeight * (me.serversHeight * num_normal_rows + me.categoriesHeight * num_headers);
1165}
1167{
1168 pos /= me.itemHeight;
1169 for (int i = category_draw_count - 1; i >= 0; --i) {
1170 int itemidx = category_item[i];
1171 float itempos = i * me.categoriesHeight + itemidx * me.serversHeight;
1172 if (pos >= itempos + me.categoriesHeight + me.serversHeight)
1173 return itemidx + 1 + floor((pos - (itempos + me.categoriesHeight + me.serversHeight)) / me.serversHeight);
1174 if (pos >= itempos)
1175 return itemidx;
1176 }
1177 // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
1178 return floor(pos / me.serversHeight);
1179}
1181{
1182 for (int i = category_draw_count - 1; i >= 0; --i) {
1183 int itemidx = category_item[i];
1184 float itempos = i * me.categoriesHeight + itemidx * me.serversHeight;
1185 if (item >= itemidx + 1)
1186 return (itempos + me.categoriesHeight + (1 + item - (itemidx + 1)) * me.serversHeight) * me.itemHeight;
1187 if (item >= itemidx)
1188 return itempos * me.itemHeight;
1189 }
1190 // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
1191 return item * me.serversHeight * me.itemHeight;
1192}
1194{
1195 for (int i = 0; i < category_draw_count; ++i) {
1196 // Matches exactly the headings with increased height.
1197 if (item == category_item[i])
1198 return me.itemHeight * (me.categoriesHeight + me.serversHeight);
1199 }
1200 return me.itemHeight * me.serversHeight;
1201}
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
virtual void toggleFavorite()
virtual void refreshServerList()
float ping
Definition main.qh:169
string strtolower(string s)
const int SERVERFLAG_PLAYERSTATS
Definition constants.qh:18
const int SERVERFLAG_PLAYERSTATS_CUSTOM
Definition constants.qh:19
float time
#define strstrofs
#define argv_end_index
#define strlen
#define tokenize_console
#define tokenizebyseparator
#define argv_start_index
const int S_CTRL
Definition hud.qh:130
#define ZCTX(s)
Definition i18n.qh:68
#define stoi(s)
Definition int.qh:4
#define FOREACH(list, cond, body)
Definition iter.qh:19
float K_MOUSE1
Definition keycodes.qc:129
float K_MOUSE3
Definition keycodes.qc:131
float K_SPACE
Definition keycodes.qc:10
float K_ENTER
Definition keycodes.qc:8
float K_MOUSE2
Definition keycodes.qc:130
float K_KP_ENTER
Definition keycodes.qc:74
float K_END
Definition keycodes.qc:42
float K_INS
Definition keycodes.qc:37
float K_KP_INS
Definition keycodes.qc:49
#define main
Definition _all.inc:202
#define LOG_TRACEF(...)
Definition log.qh:77
#define LOG_INFOF(...)
Definition log.qh:66
string MapInfo_Type_ToString(Gametype t)
Definition mapinfo.qc:655
string draw_TextShortenToWidth(string theText, float maxWidth, float ICanHasKallerz, vector SizeThxBye)
Definition draw.qc:378
void draw_Picture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha)
Definition draw.qc:72
void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz)
Definition draw.qc:282
float draw_TextWidth(string theText, float ICanHasKallerz, vector SizeThxBye)
Definition draw.qc:304
vector boxToGlobalSize(vector v, vector theScale)
Definition draw.qc:53
vector boxToGlobal(vector v, vector theOrigin, vector theScale)
Definition draw.qc:45
void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha)
Definition draw.qc:97
string draw_PreloadPicture(string pic)
Definition draw.qc:60
vector draw_shift
Definition draw.qh:7
vector draw_scale
Definition draw.qh:8
string resolvemod(string m)
Definition util.qc:596
float getFadedAlpha(float currentAlpha, float startAlpha, float targetAlpha)
Definition util.qc:816
void clearTooltip(entity e)
Definition util.qc:277
void setZonedTooltip(entity e, string theTooltip, string theCvar)
Definition util.qc:257
float _Nex_ExtResponseSystem_BannedServersNeedsRefresh
Definition util.qh:63
float _Nex_ExtResponseSystem_RecommendedServersNeedsRefresh
Definition util.qh:67
string _Nex_ExtResponseSystem_BannedServers
Definition util.qh:62
float _Nex_ExtResponseSystem_PromotedServersNeedsRefresh
Definition util.qh:65
void m_play_click_sound(string soundfile)
Definition menu.qc:1111
const string MENU_SOUND_OPEN
Definition menu.qh:53
const string MENU_SOUND_SELECT
Definition menu.qh:54
void localcmd(string command,...)
void cvar_set(string name, string value)
float SLIST_TEST_LESSEQUAL
Definition menudefs.qc:116
float SLSF_DESCENDING
Definition menudefs.qc:616
float stof(string val,...)
float SLIST_TEST_NOTSTARTSWITH
Definition menudefs.qc:123
string substring(string s, float start, float length)
void sethostcachesort(float fld, float slsf)
float cvar(string name)
float SLIST_TEST_CONTAINS
Definition menudefs.qc:114
float SLIST_TEST_GREATEREQUAL
Definition menudefs.qc:120
string gethostcachestring(float type, float hostnr)
float SLIST_TEST_NOTEQUAL
Definition menudefs.qc:121
float SLIST_MASK_OR
Definition menudefs.qc:125
const string cvar_string(string name)
float gethostcachenumber(float fld, float hostnr)
void sethostcachemaskstring(float mask, float fld, string str, float op)
void resethostcachemasks(void)
float gethostcachevalue(float type)
float SLIST_TEST_STARTSWITH
Definition menudefs.qc:122
void refreshhostcache(...)
string ftos(float f)
void resorthostcache(void)
float SLIST_TEST_EQUAL
Definition menudefs.qc:118
float SLSF_CATEGORIES
Definition menudefs.qc:618
float floor(float f)
void sethostcachemasknumber(float mask, float fld, float num, float op)
string strzone(string s)
string argv(float n)
float SLIST_HOSTCACHEVIEWCOUNT
Definition menudefs.qc:104
float SLIST_MASK_AND
Definition menudefs.qc:124
void DialogOpenButton_Click_withCoords(entity button, entity tab, vector theOrigin, vector theSize)
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NEW(cname,...)
Definition oo.qh:117
#define SUPER(cname)
Definition oo.qh:231
#define METHOD(cname, name, prototype)
Definition oo.qh:269
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
#define REGISTRY_GET(id, i)
Definition registry.qh:43
int autocvar_hud_panel_scoreboard_ping_high
int autocvar_hud_panel_scoreboard_ping_low
#define PING_LOW
#define PING_HIGH
#define PING_MED
int autocvar_hud_panel_scoreboard_ping_medium
vector
Definition self.qh:92
vector org
Definition self.qh:92
void XonoticServerList_draw(entity me)
entity makeXonoticServerList()
void ServerList_Favorite_Click(entity btn, entity this)
void ServerList_Categories_Click(entity box, entity me)
void ServerList_ShowEmpty_Click(entity box, entity me)
void XonoticServerList_setSelected(entity me, int i)
void ServerList_PingSort_Click(entity btn, entity me)
void ServerList_NameSort_Click(entity btn, entity me)
float XonoticServerList_getItemStart(entity me, int item)
int CategoryForEntry(int entry)
int CategoryOverride(int cat)
void XonoticServerList_configureXonoticServerList(entity me)
void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
#define SET_YRANGE(start, end)
entity RetrieveCategoryEnt(int catnum)
Definition serverlist.qc:63
void ServerList_Info_Click(entity btn, entity me)
bool IsServerInList(string list, string srv)
Definition serverlist.qc:76
void ServerList_TypeSort_Click(entity btn, entity me)
float XonoticServerList_mouseMove(entity me, vector pos)
float m_gethostcachecategory(float entry)
Definition serverlist.qc:13
void ServerList_ShowFull_Click(entity box, entity me)
#define IsPromoted(srv)
Definition serverlist.qc:10
void SL_ProcessCategoryOverrides(.string override_field_string,.float override_field)
Definition serverlist.qc:15
void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
float XonoticServerList_getTotalHeight(entity me)
#define IsFavorite(srv)
Definition serverlist.qc:9
void XonoticServerList_refreshServerList(entity me, int mode)
int XonoticServerList_getItemAtPos(entity me, float pos)
void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
void ServerList_PlayerSort_Click(entity btn, entity me)
void XonoticServerList_setSortOrder(entity me, int fld, int direction)
bool XonoticServerList_keyDown(entity me, int scan, bool ascii, bool shift)
void RegisterSLCategories()
Definition serverlist.qc:44
void XonoticServerList_focusEnter(entity me)
void ServerList_MapSort_Click(entity btn, entity me)
void ServerList_Connect_Click(entity btn, entity me)
void XonoticServerList_focusedItemChangeNotify(entity me)
void XonoticServerList_doubleClickListBoxItem(entity me, int i, vector where)
void ServerList_Update_favoriteButton(entity btn, entity me)
void ServerList_Filter_Change(entity box, entity me)
void ServerList_ShowLaggy_Click(entity box, entity me)
float XonoticServerList_getItemHeight(entity me, int item)
#define IsRecommended(srv)
Definition serverlist.qc:11
float autocvar_menu_slist_recommendations_maxping
const float REFRESHSERVERLIST_RESORT
float autocvar_menu_slist_recommendations_minfreeslots
const float REFRESHSERVERLIST_ASK
float autocvar_menu_slist_recommendations_minhumans
float cat_enoverride
string cat_name
int category_item[MAX_CATEGORIES]
float autocvar_menu_slist_recommendations_purethreshold
float autocvar_menu_slist_modimpurity
int category_ent_count
float autocvar_menu_slist_recommendations
int category_name[MAX_CATEGORIES]
const float REFRESHSERVERLIST_RESET
#define SLIST_CATEGORIES
string cat_enoverride_string
float autocvar_menu_slist_maxping
string cat_dioverride_string
const float REFRESHSERVERLIST_REFILTER
int category_draw_count
float autocvar_menu_slist_categories_onlyifmultiple
entity categories[MAX_CATEGORIES]
SLIST_FIELDS float autocvar_menu_slist_categories
float cat_dioverride
#define SLIST_FIELDS
Definition serverlist.qh:87
#define IS_DIGIT(d)
Definition string.qh:575
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
const vector eY
Definition vector.qh:45
const vector eX
Definition vector.qh:44
string modname
Definition world.qh:49