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{
275 entity me;
277 me.configureXonoticServerList(me);
278 return me;
279}
281{
282 me.configureXonoticListBox(me);
283
284 // update field ID's
285 #define SLIST_FIELD(suffix,name) SLIST_FIELD_##suffix = gethostcacheindexforkey(name);
287 #undef SLIST_FIELD
288
289 // clear list
290 me.nItems = 0;
291}
293{
294 me.lockedSelectedItem = false;
295 //int save = me.selectedItem;
296 SUPER(XonoticServerList).setSelected(me, i);
297 /*
298 if(me.selectedItem == save)
299 return;
300 */
301 if(me.nItems == 0)
302 return;
304 return; // sorry, it would be wrong
305
306 strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
307
308 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
309 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
310 me.ipAddressBoxFocused = -1;
311}
313{
314 //print("refresh of type ", ftos(mode), "\n");
315
317 {
318 string s = me.filterString;
319
320 string typestr;
321 int m = strstrofs(s, ":", 0);
322 if(m >= 0)
323 {
324 typestr = substring(s, 0, m);
325 s = substring(s, m + 1, strlen(s) - m - 1);
326 while(substring(s, 0, 1) == " ")
327 s = substring(s, 1, strlen(s) - 1);
328 }
329 else
330 typestr = "";
331
332 string modstr = cvar_string("menu_slist_modfilter");
333
334 m = SLIST_MASK_AND - 1;
336
337 // show full button
338 if(!me.filterShowFull)
339 {
340 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL); // can connect
341 // There's several reasons to join a server where nJoinAllowed() reported 0 free slots, such as:
342 // to spec the match, chat, vote for a gametype with more players, or join your reserved tournament slot.
343 //sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, ":S0:", SLIST_TEST_NOTCONTAIN); // g_maxplayers support
344 }
345
346 // show empty button
347 if(!me.filterShowEmpty)
348 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
349
350 // show laggy button
351 if(!me.filterShowLaggy && autocvar_menu_slist_maxping > 0)
353
354 // gametype filtering
355 if(typestr != "")
356 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
357
358 // mod filtering
359 if(modstr != "")
360 {
361 if(substring(modstr, 0, 1) == "!")
362 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
363 else
364 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
365 }
366
367 // server banning
369 for(int i = 0; i < n; ++i)
370 if(argv(i) != "")
371 sethostcachemaskstring(++m, SLIST_FIELD_CNAME, argv(i), SLIST_TEST_NOTSTARTSWITH);
372
373 m = SLIST_MASK_OR - 1;
374 if(s != "")
375 {
376 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
377 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
378 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
379 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
380 }
381
382 int sorting_flags = 0;
383 //sorting_flags |= SLSF_FAVORITES;
384 sorting_flags |= SLSF_CATEGORIES;
385 if(me.currentSortOrder < 0) { sorting_flags |= SLSF_DESCENDING; }
386 sethostcachesort(me.currentSortField, sorting_flags);
387 }
388
390 if(mode >= REFRESHSERVERLIST_ASK)
392}
394{
395 SUPER(XonoticServerList).focusEnter(me);
396 if(time < me.nextRefreshTime)
397 {
398 //print("sorry, no refresh yet\n");
399 return;
400 }
401 me.nextRefreshTime = time + 10;
402 me.refreshServerList(me, REFRESHSERVERLIST_ASK);
403}
404
406{
407 me.serversHeight = (autocvar_menu_slist_categories > 0 ? 1.0 : me.categoriesHeight);
408
410 {
411 if(!me.needsRefresh)
412 me.needsRefresh = 2;
414 }
415
417 {
418 if(!me.needsRefresh)
419 me.needsRefresh = 3;
421 }
422
424 {
425 if(!me.needsRefresh)
426 me.needsRefresh = 3;
428 }
429
430 if(me.currentSortField == -1)
431 {
432 me.setSortOrder(me, SLIST_FIELD_PING, +1);
433 me.refreshServerList(me, REFRESHSERVERLIST_RESET);
434 }
435 else if(me.needsRefresh == 1)
436 {
437 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
438 }
439 else if(me.needsRefresh == 2)
440 {
441 me.needsRefresh = 0;
442 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
443 }
444 else if(me.needsRefresh == 3)
445 {
446 me.needsRefresh = 0;
447 me.refreshServerList(me, REFRESHSERVERLIST_RESORT);
448 }
449
450 bool owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
451
452 for(int i = 0; i < category_draw_count; ++i) { category_name[i] = -1; category_item[i] = -1; }
454
456
457 if(autocvar_menu_slist_categories >= 0) // if less than 0, don't even draw a category heading for favorites
458 {
459 int itemcount = me.nItems;
460
461 //float visible = floor(me.scrollPos / me.itemHeight);
462 // ^ unfortunately no such optimization can be made-- we must process through the
463 // entire list, otherwise there is no way to know which item is first in its category.
464
465 // binary search method suggested by div
466 float begin = 0;
467 for(int j = 1; j <= category_ent_count; ++j) {
468 float first = begin;
469 float last = (itemcount - 1);
470 if (first > last) {
471 // List is empty.
472 break;
473 }
474 float catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first);
475 float catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last);
476 if (catf > j) {
477 // The first one is already > j.
478 // Therefore, category j does not exist.
479 // Higher numbered categories do exist though.
480 } else if (catl < j) {
481 // The last one is < j.
482 // Thus this category - and any following -
483 // don't exist.
484 break;
485 } else if (catf == j) {
486 // Starts at first. This breaks the loop
487 // invariant in the binary search and thus has
488 // to be handled separately.
489 if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != j)
490 error("Category mismatch I");
491 if(first > 0)
492 if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == j)
493 error("Category mismatch II");
497 begin = first + 1;
498 } else {
499 // At this point, catf <= j < catl, thus
500 // catf < catl, thus first < last.
501 // INVARIANTS:
502 // last - first >= 1
503 // catf == gethostcachenumber(SLIST_FIELD_CATEGORY(first)
504 // catl == gethostcachenumber(SLIST_FIELD_CATEGORY(last)
505 // catf < j
506 // catl >= j
507 while (last - first > 1) {
508 float middle = floor((first + last) / 2);
509 // By loop condition, middle != first && middle != last.
510 float cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle);
511 if (cat >= j) {
512 last = middle;
513 catl = cat;
514 } else {
515 first = middle;
516 catf = cat;
517 }
518 }
519 if (catl == j) {
520 if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != j)
521 error("Category mismatch III");
522 if(last > 0)
523 if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == j)
524 error("Category mismatch IV");
528 begin = last + 1; // already scanned through these, skip 'em
529 }
530 else
531 begin = last; // already scanned through these, skip 'em
532 }
533 }
535 {
536 category_name[0] = -1;
537 category_item[0] = -1;
539 me.nItems = itemcount;
540 }
541 }
542
543 me.connectButton.disabled = (me.ipAddressBox.text == "");
544 me.infoButton.disabled = !owned;
545 me.favoriteButton.disabled = (me.ipAddressBox.text == "");
546
547 bool found = false;
548 if(me.lockedSelectedItem)
549 {
550 if(me.nItems > 0)
551 {
552 if(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem) != me.selectedServer)
553 {
554 strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
555 }
556 found = true;
557 }
558 }
559 else if(me.selectedServer)
560 {
561 for(int i = 0; i < me.nItems; ++i)
562 {
563 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
564 {
565 // don't follow the selected item with SUPER(XonoticServerList).setSelected(me, i);
566 me.selectedItem = i;
567 found = true;
568 break;
569 }
570 }
571 }
572 if(!found)
573 {
574 if(me.nItems > 0)
575 {
576 // selected server disappeared, select the last server (scrolling to it)
577 if(me.selectedItem >= me.nItems)
578 SUPER(XonoticServerList).setSelected(me, me.nItems - 1);
579 strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
580 }
581 }
582
583 if(owned)
584 {
585 if(me.selectedServer != me.ipAddressBox.text)
586 {
587 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
588 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
589 me.ipAddressBoxFocused = -1;
590 }
591 }
592
593 if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
594 {
595 if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
597 me.ipAddressBoxFocused = me.ipAddressBox.focused;
598 }
599
600 SUPER(XonoticServerList).draw(me);
601}
603{
604 me.setSortOrder(me, SLIST_FIELD_PING, +1);
605}
607{
608 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
609}
611{
612 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
613}
615{
616 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
617}
619{
620 string s = me.filterString;
621 int m = strstrofs(s, ":", 0);
622 if(m >= 0)
623 {
624 s = substring(s, 0, m);
625 while(substring(s, m+1, 1) == " ") // skip spaces
626 ++m;
627 }
628 else
629 s = "";
630
631 Gametype first = NULL; FOREACH(Gametypes, !first, first = it; break);
632 bool flag = false;
633 FOREACH(Gametypes, s == MapInfo_Type_ToString(it), {
634 // the type was found
635 // choose the next one
636 flag = true;
637 s = MapInfo_Type_ToString(REGISTRY_GET(Gametypes, it.m_id + 1));
638 if (s == "") s = MapInfo_Type_ToString(first);
639 break;
640 });
641 if (!flag) {
642 // no type was found
643 // choose the first one
644 s = MapInfo_Type_ToString(first);
645 }
646
647 if(s != "") s = strcat(s, ":");
648 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
649
650 me.controlledTextbox.setText(me.controlledTextbox, s);
651 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
652 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
653 //ServerList_Filter_Change(me.controlledTextbox, me);
654}
656{
657 strfree(me.filterString);
658 if(box.text != "")
659 me.filterString = strzone(box.text);
660 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
661
662 me.ipAddressBox.setText(me.ipAddressBox, "");
663 me.ipAddressBox.cursorPos = 0;
664 me.ipAddressBoxFocused = -1;
665}
667{
669 me.refreshServerList(me, REFRESHSERVERLIST_RESORT);
670
671 me.ipAddressBox.setText(me.ipAddressBox, "");
672 me.ipAddressBox.cursorPos = 0;
673 me.ipAddressBoxFocused = -1;
674}
676{
677 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
678 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
679
680 me.ipAddressBox.setText(me.ipAddressBox, "");
681 me.ipAddressBox.cursorPos = 0;
682 me.ipAddressBoxFocused = -1;
683}
685{
686 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
687 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
688
689 me.ipAddressBox.setText(me.ipAddressBox, "");
690 me.ipAddressBox.cursorPos = 0;
691 me.ipAddressBoxFocused = -1;
692}
694{
695 box.setChecked(box, me.filterShowLaggy = !me.filterShowLaggy);
696 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
697
698 me.ipAddressBox.setText(me.ipAddressBox, "");
699 me.ipAddressBox.cursorPos = 0;
700 me.ipAddressBoxFocused = -1;
701}
702void XonoticServerList_setSortOrder(entity me, int fld, int direction)
703{
704 if(me.currentSortField == fld)
705 direction = -me.currentSortOrder;
706 me.currentSortOrder = direction;
707 me.currentSortField = fld;
708 me.sortButton1.forcePressed = (fld == SLIST_FIELD_PING);
709 me.sortButton2.forcePressed = (fld == SLIST_FIELD_NAME);
710 me.sortButton3.forcePressed = (fld == SLIST_FIELD_MAP);
711 me.sortButton4.forcePressed = false;
712 me.sortButton5.forcePressed = (fld == SLIST_FIELD_NUMHUMANS);
713 me.selectedItem = 0;
714 strfree(me.selectedServer);
715 me.refreshServerList(me, REFRESHSERVERLIST_REFILTER);
716}
717void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
718{
719 vector originInLBSpace = eY * (-me.itemHeight);
720 vector sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
721
722 vector originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
723 vector sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
724
725 btn.Container_origin_x = originInDialogSpace.x + sizeInDialogSpace.x * theOrigin;
726 btn.Container_size_x = sizeInDialogSpace.x * theSize;
727 btn.setText(btn, theTitle);
728 btn.onClick = theFunc;
729 btn.onClickEntity = me;
730 btn.resized = 1;
731}
732void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
733{
734 SUPER(XonoticServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
735
736 me.realFontSize_y = me.fontSize / (absSize.y * me.itemHeight);
737 me.realFontSize_x = me.fontSize / (absSize.x * (1 - me.controlWidth));
738 me.realUpperMargin = 0.5 * (1 - me.realFontSize.y);
739
740 me.columnIconsOrigin = 0;
741 me.columnIconsSize = me.realFontSize.x * 5 * me.iconsSizeFactor;
742 me.columnPingSize = me.realFontSize.x * 3;
743 me.columnMapSize = me.realFontSize.x * 10;
744 me.columnTypeSize = me.realFontSize.x * 4;
745 me.columnPlayersSize = me.realFontSize.x * 5;
746 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnIconsSize - me.columnTypeSize - 4 * me.realFontSize.x;
747 // no me.realFontSize.x separation between icons and ping columns because in practice they are already separated
748 me.columnPingOrigin = me.columnIconsOrigin + me.columnIconsSize;
749 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize.x;
750 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize.x;
751 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize.x;
752 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize.x;
753
754 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, _("Ping"), ServerList_PingSort_Click);
755 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, _("Hostname"), ServerList_NameSort_Click);
756 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, _("Map"), ServerList_MapSort_Click);
757 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, _("Type"), ServerList_TypeSort_Click);
758 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, _("Players"), ServerList_PlayerSort_Click);
759
760 int f = me.currentSortField;
761 if(f >= 0)
762 {
763 me.currentSortField = -1;
764 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
765 }
766}
768{
769 if (me.ipAddressBox.text != "")
770 localcmd(sprintf("connect %s\n", me.ipAddressBox.text));
771}
773{
774 string addr = this.ipAddressBox.text;
775 string ipstr = netaddress_resolve(addr, 26000);
776 if (ipstr == "") return;
778 this.toggleFavorite(this, addr);
779 this.ipAddressBoxFocused = -1;
780}
782{
783 if (me.nItems != 0)
784 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
785
786 float thisPos = me.getItemStart(me, me.selectedItem);
787 float thisHeight = me.getItemHeight(me, me.selectedItem);
788 vector org = boxToGlobal(eY * (thisPos - me.scrollPos), me.origin, me.size);
789 vector sz = boxToGlobalSize(eY * thisHeight + eX * (1 - me.controlWidth), me.size);
790 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
791}
799void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
800{
801 vector oldscale = draw_scale;
802 vector oldshift = draw_shift;
803#define SET_YRANGE(start,end) \
804 draw_scale = boxToGlobalSize(eX + eY * ((end) - (start)), oldscale); \
805 draw_shift = boxToGlobal(eY * (start), oldshift, oldscale);
806
807 int c;
808 for (c = 0; c < category_draw_count; ++c) {
809 // Matches exactly the headings with increased height.
810 if (i == category_item[c])
811 break;
812 }
813
814 if (c < category_draw_count)
815 {
817 if(catent)
818 {
820 (me.categoriesHeight - me.realFontSize.y) / (me.categoriesHeight + me.serversHeight),
821 me.categoriesHeight / (me.categoriesHeight + me.serversHeight)
822 );
823 draw_Text(
824 eY * me.realUpperMargin
825 +
826#if 0
827 eX * (me.columnNameOrigin + (me.columnNameSize - draw_TextWidth(catent.cat_string, 0, me.realFontSize)) * 0.5),
828 catent.cat_string,
829#else
830 eX * (me.columnNameOrigin),
831 strcat(catent.cat_string, ":"),
832#endif
833 me.realFontSize,
834 SKINCOLOR_SERVERLIST_CATEGORY,
835 SKINALPHA_SERVERLIST_CATEGORY,
836 0
837 );
838 SET_YRANGE(me.categoriesHeight / (me.categoriesHeight + me.serversHeight), 1);
839 }
840 }
841
842 // Now categories are done. Set the Y range in stone.
843 oldscale = draw_scale;
844 oldshift = draw_shift;
845
846 if(isSelected && !me.lockedSelectedItem)
847 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
848 else if(isFocused)
849 {
850 me.focusedItemAlpha = getFadedAlpha(me.focusedItemAlpha, SKINALPHA_LISTBOX_FOCUSED, SKINFADEALPHA_LISTBOX_FOCUSED);
851 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_FOCUSED, me.focusedItemAlpha);
852 }
853
854 string s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
855 int m = tokenizebyseparator(s, ":");
856 string typestr = "", versionstr = "";
857 if(m >= 2)
858 {
859 typestr = argv(0);
860 versionstr = argv(1);
861 }
862 bool pure = false, pure_available = false;
863 int qcfreeslots = -1, sflags = -1;
864 string modname = "";
865 for(int j = 2; j < m; ++j)
866 {
867 if(argv(j) == "")
868 break;
869 string key = substring(argv(j), 0, 1);
870 string value = substring(argv(j), 1, -1);
871 if(key == "P")
872 {
873 pure = (stof(value) == 0);
874 pure_available = true;
875 }
876 else if(key == "S")
877 qcfreeslots = stof(value); // from nJoinAllowed()
878 else if(key == "F")
879 sflags = stoi(value);
880 else if(key == "M")
881 modname = value;
882 }
883
884#ifdef COMPAT_NO_MOD_IS_XONOTIC
885 if(modname == "")
886 modname = "Xonotic";
887#endif
888
889 string original_modname = modname;
891
892 /*
893 SLIST_FIELD_MOD = gethostcacheindexforkey("mod");
894 s = gethostcachestring(SLIST_FIELD_MOD, i);
895 if(s != "data")
896 if(modname == "xonotic")
897 modname = s;
898 */
899
900 // list the mods here on which the pure server check actually works
901 if(modname != "xonotic")
902 if(modname != "instagib" || modname != "minstagib")
903 if(modname != "cts")
904 if(modname != "nix")
905 if(modname != "newtoys")
906 pure_available = false;
907
908 float theAlpha;
909 if(gethostcachenumber(SLIST_FIELD_FREESLOTS, i) <= 0) // can't connect
910 theAlpha = SKINALPHA_SERVERLIST_FULL;
911 else if(qcfreeslots == 0) // can connect but may not be able to play yet (duel, g_maxplayers, etc)
912 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
913 else if (!gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
914 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
915 else
916 theAlpha = 1;
917
918 float ping = gethostcachenumber(SLIST_FIELD_PING, i);
922 vector theColor;
923 // this colour selection code should match csqc Scoreboard_GetField()
924 if (ping < PING_LOW)
925 theColor = SKINCOLOR_SERVERLIST_LOWPING;
926 else if(ping < PING_MED)
927 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * ((ping - PING_LOW) / (PING_MED - PING_LOW));
928 else if(ping < PING_HIGH)
929 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((ping - PING_MED) / (PING_HIGH - PING_MED));
930 else
931 {
932 theColor = eX;
933 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((ping - PING_HIGH) / (PING_HIGH * 2 - PING_HIGH));
934 }
935
936 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
937 {
938 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
939 if (gethostcachenumber(SLIST_FIELD_FREESLOTS, i) > 0) // can connect
940 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
941 }
942
943 s = gethostcachestring(SLIST_FIELD_CNAME, i);
944
945 bool isv4 = false, isv6 = false;
946 if(substring(s, 0, 1) == "[")
947 {
948 isv6 = true;
949 ++me.seenIPv6;
950 }
951 else if(IS_DIGIT(substring(s, 0, 1)))
952 {
953 isv4 = true;
954 ++me.seenIPv4;
955 }
956
957 int crypto = stof(substring(crypto_getencryptlevel(s), 0, 1));
958 if((crypto <= 0 && cvar("crypto_aeslevel") >= 3) || (crypto >= 3 && cvar("crypto_aeslevel") <= 0))
959 {
960 theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
961 theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
962 }
963
964 if(crypto == 1)
965 {
966 if(cvar("crypto_aeslevel") >= 2)
967 crypto |= 4;
968 }
969 if(crypto == 2)
970 {
971 if(cvar("crypto_aeslevel") >= 1)
972 crypto |= 4;
973 }
974 if(crypto == 3)
975 crypto = 5;
976 else if(crypto >= 3)
977 crypto -= 2;
978 // possible status:
979 // 0: crypto off
980 // 1: AES possible
981 // 2: AES recommended but not available
982 // 3: AES possible and will be used
983 // 4: AES recommended and will be used
984 // 5: AES required
985
986 // --------------
987 // RENDER ICONS
988 // --------------
989 vector iconSize = '0 0 0';
990 iconSize_y = me.realFontSize.y * me.iconsSizeFactor / me.serversHeight;
991 iconSize_x = me.realFontSize.x * me.iconsSizeFactor;
992
993 vector iconPos = '0 0 0';
994 if(me.seenIPv4 && me.seenIPv6)
995 iconPos_x = (me.columnIconsSize - 4 * iconSize.x) * 0.5;
996 else
997 iconPos_x = (me.columnIconsSize - 3 * iconSize.x) * 0.5;
998 iconPos_y = (1 - iconSize.y) * 0.5;
999
1000 // IP
1001 if(me.seenIPv4 && me.seenIPv6)
1002 {
1003 if(isv6)
1004 draw_Picture(iconPos, "icon_ipv6", iconSize, '1 1 1', 1);
1005 else if(isv4)
1006 draw_Picture(iconPos, "icon_ipv4", iconSize, '1 1 1', 1);
1007
1008 iconPos.x += iconSize.x;
1009 }
1010
1011 // AES
1012 if(crypto > 0)
1013 draw_Picture(iconPos, strcat("icon_aeslevel", ftos(crypto)), iconSize, '1 1 1', 1);
1014
1015 iconPos.x += iconSize.x;
1016
1017 // Mod
1018 if(modname == "xonotic")
1019 {
1020 // Here, pure_available should always be set. If not, consider
1021 // it an impurity.
1022 if(pure_available && pure)
1023 draw_Picture(iconPos, "icon_pure1", iconSize, '1 1 1', 1);
1024 }
1025 else
1026 {
1027 string icon = strcat("icon_mod_", modname);
1028 if(draw_PreloadPicture(icon) == "")
1029 icon = "icon_mod_";
1030
1031 // In mods, pure_available not being present indicates
1032 // non-support of the feature. Just show the mod icon as is
1033 // then.
1034 if(pure_available && !pure)
1035 draw_Picture(iconPos, icon, iconSize, '1 1 1', SKINALPHA_SERVERLIST_ICON_NONPURE);
1036 else
1037 draw_Picture(iconPos, icon, iconSize, '1 1 1', 1);
1038 }
1039
1040 iconPos.x += iconSize.x;
1041
1042 // Stats
1043 if(sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS))
1044 {
1045 if (sflags & SERVERFLAG_PLAYERSTATS_CUSTOM)
1046 draw_Picture(iconPos, "icon_mod_", iconSize, '1 1 1', 1); // TODO: custom stats server icon
1047 else
1048 draw_Picture(iconPos, "icon_stats1", iconSize, '1 1 1', 1);
1049 }
1050
1051 if(isFocused && me.mouseOverIcons && !me.tooltip)
1052 {
1053 string t = "";
1054 if(me.seenIPv4 && me.seenIPv6)
1055 t = strcat(t, (isv6) ? "IPv6, " : "IPv4, ");
1056 t = strcat(t, _("encryption:"), " ", (crypto ? sprintf(_("AES level %d"), crypto) : ZCTX(_("ENC^none"))), ", ");
1057 t = strcat(t, sprintf(_("mod: %s"), ((modname == "xonotic") ? ZCTX(_("MOD^Default")) : original_modname)));
1058 if(pure_available)
1059 t = strcat(t, sprintf(" (%s)", (pure) ? _("official settings") : _("modified settings")));
1060 t = strcat(t, ", ");
1061 t = strcat(t, ((sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS)) ? ((sflags & SERVERFLAG_PLAYERSTATS_CUSTOM) ? _("custom stats server") : _("stats enabled")) : _("stats disabled")));
1063 }
1064 // --------------
1065 // RENDER TEXT
1066 // --------------
1067
1068 // Center it in the row.
1069 SET_YRANGE(
1070 0.5 - 0.5 / me.serversHeight,
1071 0.5 + 0.5 / me.serversHeight
1072 );
1073
1074 // ping
1075 s = ftos(ping);
1076 draw_Text(me.realUpperMargin * eY + (me.columnPingOrigin + me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1077
1078 // server name
1079 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize, 0, me.realFontSize);
1080 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
1081
1082 // server map
1083 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize, 0, me.realFontSize);
1084 draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1085
1086 // server gametype
1087 s = draw_TextShortenToWidth(typestr, me.columnTypeSize, 0, me.realFontSize);
1088 draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1089
1090 // server playercount
1091 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
1092 draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
1093}
1094
1099
1101{
1102 if(!SUPER(XonoticServerList).mouseMove(me, pos))
1103 {
1104 me.mouseOverIcons = false;
1105 clearTooltip(me);
1106 return 1;
1107 }
1108
1109 float iconSize_x = me.realFontSize.x * me.iconsSizeFactor;
1110 float iconPos_x;
1111 if(me.seenIPv4 && me.seenIPv6)
1112 iconPos_x = (me.columnIconsSize - 4 * iconSize_x) * 0.5;
1113 else
1114 iconPos_x = (me.columnIconsSize - 3 * iconSize_x) * 0.5;
1115 me.mouseOverIcons = (pos.x >= me.columnIconsOrigin + iconPos_x && pos.x <= me.columnIconsOrigin + me.columnIconsSize - iconPos_x);
1116 if(!me.mouseOverIcons)
1117 clearTooltip(me);
1118 return 1;
1119}
1120
1121bool XonoticServerList_keyDown(entity me, int scan, bool ascii, bool shift)
1122{
1123 if(scan == K_ENTER || scan == K_KP_ENTER)
1124 {
1126 return true;
1127 }
1128 else if(scan == K_MOUSE2 || scan == K_SPACE || ((shift & S_CTRL) && scan == K_MOUSE1))
1129 {
1130 if(me.nItems != 0)
1131 {
1132 float thisPos = me.getItemStart(me, me.selectedItem);
1133 float thisHeight = me.getItemHeight(me, me.selectedItem);
1134 vector org = boxToGlobal(eY * (thisPos - me.scrollPos), me.origin, me.size);
1135 vector sz = boxToGlobalSize(eY * thisHeight + eX * (1 - me.controlWidth), me.size);
1137 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
1138 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
1139 return true;
1140 }
1141 return false;
1142 }
1143 else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
1144 {
1145 if(me.nItems != 0)
1146 {
1147 me.toggleFavorite(me, me.selectedServer);
1148 me.ipAddressBoxFocused = -1;
1149 return true;
1150 }
1151 return false;
1152 }
1153 else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift))
1154 return true;
1155 else if(!me.controlledTextbox)
1156 return false;
1157 else
1158 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
1159}
1160
1162{
1163 float num_normal_rows = me.nItems;
1164 int num_headers = category_draw_count;
1165 return me.itemHeight * (me.serversHeight * num_normal_rows + me.categoriesHeight * num_headers);
1166}
1168{
1169 pos = pos / me.itemHeight;
1170 for (int i = category_draw_count - 1; i >= 0; --i) {
1171 int itemidx = category_item[i];
1172 float itempos = i * me.categoriesHeight + itemidx * me.serversHeight;
1173 if (pos >= itempos + me.categoriesHeight + me.serversHeight)
1174 return itemidx + 1 + floor((pos - (itempos + me.categoriesHeight + me.serversHeight)) / me.serversHeight);
1175 if (pos >= itempos)
1176 return itemidx;
1177 }
1178 // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
1179 return floor(pos / me.serversHeight);
1180}
1182{
1183 for (int i = category_draw_count - 1; i >= 0; --i) {
1184 int itemidx = category_item[i];
1185 float itempos = i * me.categoriesHeight + itemidx * me.serversHeight;
1186 if (item >= itemidx + 1)
1187 return (itempos + me.categoriesHeight + (1 + item - (itemidx + 1)) * me.serversHeight) * me.itemHeight;
1188 if (item >= itemidx)
1189 return itempos * me.itemHeight;
1190 }
1191 // No category matches? Note that category 0 is... 0. Therefore no headings exist at all.
1192 return item * me.serversHeight * me.itemHeight;
1193}
1195{
1196 for (int i = 0; i < category_draw_count; ++i) {
1197 // Matches exactly the headings with increased height.
1198 if (item == category_item[i])
1199 return me.itemHeight * (me.categoriesHeight + me.serversHeight);
1200 }
1201 return me.itemHeight * me.serversHeight;
1202}
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:656
string draw_TextShortenToWidth(string theText, float maxWidth, float ICanHasKallerz, vector SizeThxBye)
Definition draw.qc:377
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:1106
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:576
#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