]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/menu/xonotic/serverlist.c
Make Bookmark button update its text (Bookmark/Remove) on address change
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / xonotic / serverlist.c
1 #ifdef INTERFACE
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))
10
11         ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85)
12
13         ATTRIB(XonoticServerList, realFontSize, vector, '0 0 0')
14         ATTRIB(XonoticServerList, realUpperMargin, float, 0)
15         ATTRIB(XonoticServerList, columnIconsOrigin, float, 0)
16         ATTRIB(XonoticServerList, columnIconsSize, float, 0)
17         ATTRIB(XonoticServerList, columnPingOrigin, float, 0)
18         ATTRIB(XonoticServerList, columnPingSize, float, 0)
19         ATTRIB(XonoticServerList, columnNameOrigin, float, 0)
20         ATTRIB(XonoticServerList, columnNameSize, float, 0)
21         ATTRIB(XonoticServerList, columnMapOrigin, float, 0)
22         ATTRIB(XonoticServerList, columnMapSize, float, 0)
23         ATTRIB(XonoticServerList, columnTypeOrigin, float, 0)
24         ATTRIB(XonoticServerList, columnTypeSize, float, 0)
25         ATTRIB(XonoticServerList, columnPlayersOrigin, float, 0)
26         ATTRIB(XonoticServerList, columnPlayersSize, float, 0)
27
28         ATTRIB(XonoticServerList, selectedServer, string, string_null) // to restore selected server when needed
29         METHOD(XonoticServerList, setSelected, void(entity, float))
30         METHOD(XonoticServerList, setSortOrder, void(entity, float, float))
31         ATTRIB(XonoticServerList, filterShowEmpty, float, 1)
32         ATTRIB(XonoticServerList, filterShowFull, float, 1)
33         ATTRIB(XonoticServerList, filterString, string, string_null)
34         ATTRIB(XonoticServerList, controlledTextbox, entity, NULL)
35         ATTRIB(XonoticServerList, ipAddressBox, entity, NULL)
36         ATTRIB(XonoticServerList, favoriteButton, entity, NULL)
37         ATTRIB(XonoticServerList, nextRefreshTime, float, 0)
38         METHOD(XonoticServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
39         ATTRIB(XonoticServerList, needsRefresh, float, 1)
40         METHOD(XonoticServerList, focusEnter, void(entity))
41         METHOD(XonoticServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
42         ATTRIB(XonoticServerList, sortButton1, entity, NULL)
43         ATTRIB(XonoticServerList, sortButton2, entity, NULL)
44         ATTRIB(XonoticServerList, sortButton3, entity, NULL)
45         ATTRIB(XonoticServerList, sortButton4, entity, NULL)
46         ATTRIB(XonoticServerList, sortButton5, entity, NULL)
47         ATTRIB(XonoticServerList, connectButton, entity, NULL)
48         ATTRIB(XonoticServerList, infoButton, entity, NULL)
49         ATTRIB(XonoticServerList, currentSortOrder, float, 0)
50         ATTRIB(XonoticServerList, currentSortField, float, -1)
51         ATTRIB(XonoticServerList, lastClickedServer, float, -1)
52         ATTRIB(XonoticServerList, lastClickedTime, float, 0)
53
54         ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1)
55
56         ATTRIB(XonoticServerList, seenIPv4, float, 0)
57         ATTRIB(XonoticServerList, seenIPv6, float, 0)
58 ENDCLASS(XonoticServerList)
59 entity makeXonoticServerList();
60 void ServerList_Connect_Click(entity btn, entity me);
61 void ServerList_ShowEmpty_Click(entity box, entity me);
62 void ServerList_ShowFull_Click(entity box, entity me);
63 void ServerList_Filter_Change(entity box, entity me);
64 void ServerList_Favorite_Click(entity btn, entity me);
65 void ServerList_Info_Click(entity btn, entity me);
66 void ServerList_Update_favoriteButton(entity btn, entity me);
67 #endif
68
69 #ifdef IMPLEMENTATION
70 float SLIST_FIELD_CNAME;
71 float SLIST_FIELD_PING;
72 float SLIST_FIELD_GAME;
73 float SLIST_FIELD_MOD;
74 float SLIST_FIELD_MAP;
75 float SLIST_FIELD_NAME;
76 float SLIST_FIELD_MAXPLAYERS;
77 float SLIST_FIELD_NUMPLAYERS;
78 float SLIST_FIELD_NUMHUMANS;
79 float SLIST_FIELD_NUMBOTS;
80 float SLIST_FIELD_PROTOCOL;
81 float SLIST_FIELD_FREESLOTS;
82 float SLIST_FIELD_PLAYERS;
83 float SLIST_FIELD_QCSTATUS;
84 float SLIST_FIELD_ISFAVORITE;
85 void ServerList_UpdateFieldIDs()
86 {
87         SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
88         SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
89         SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
90         SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
91         SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
92         SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
93         SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
94         SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
95         SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
96         SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
97         SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
98         SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
99         SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
100         SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
101         SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
102 }
103
104 float IsFavorite(string srv)
105 {
106         string p;
107         float i, n;
108         if(srv == "")
109                 return FALSE;
110         srv = netaddress_resolve(srv, 26000);
111         if(srv == "")
112                 return FALSE;
113         p = crypto_getidfp(srv);
114         n = tokenize_console(cvar_string("net_slist_favorites"));
115         for(i = 0; i < n; ++i)
116         {
117                 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
118                 {
119                         if(p)
120                                 if(argv(i) == p)
121                                         return TRUE;
122                 }
123                 else
124                 {
125                         if(srv == netaddress_resolve(argv(i), 26000))
126                                 return TRUE;
127                 }
128         }
129         return FALSE;
130 }
131
132 void ToggleFavorite(string srv)
133 {
134         string s, s0, s1, s2, srv_resolved, p;
135         float i, n, f;
136         srv_resolved = netaddress_resolve(srv, 26000);
137         p = crypto_getidfp(srv_resolved);
138         s = cvar_string("net_slist_favorites");
139         n = tokenize_console(s);
140         f = 0;
141         for(i = 0; i < n; ++i)
142         {
143                 if(substring(argv(i), 0, 1) != "[" && strlen(argv(i)) == 44 && strstrofs(argv(i), ".", 0) < 0)
144                 {
145                         if(p)
146                                 if(argv(i) != p)
147                                         continue;
148                 }
149                 else
150                 {
151                         if(srv_resolved != netaddress_resolve(argv(i), 26000))
152                                 continue;
153                 }
154                 s0 = s1 = s2 = "";
155                 if(i > 0)
156                         s0 = substring(s, 0, argv_end_index(i - 1));
157                 if(i < n-1)
158                         s2 = substring(s, argv_start_index(i + 1), -1);
159                 if(s0 != "" && s2 != "")
160                         s1 = " ";
161                 cvar_set("net_slist_favorites", strcat(s0, s1, s2));
162                 s = cvar_string("net_slist_favorites");
163                 n = tokenize_console(s);
164                 f = 1;
165                 --i;
166         }
167         
168         if(!f)
169         {
170                 s1 = "";
171                 if(s != "")
172                         s1 = " ";
173                 if(p)
174                         cvar_set("net_slist_favorites", strcat(s, s1, p));
175                 else
176                         cvar_set("net_slist_favorites", strcat(s, s1, srv));
177         }
178
179         resorthostcache();
180 }
181
182 void ServerList_Update_favoriteButton(entity btn, entity me)
183 {
184         if(IsFavorite(me.ipAddressBox.text))
185                 me.favoriteButton.setText(me.favoriteButton, _("Remove"));
186         else
187                 me.favoriteButton.setText(me.favoriteButton, _("Bookmark"));
188 }
189
190 entity makeXonoticServerList()
191 {
192         entity me;
193         me = spawnXonoticServerList();
194         me.configureXonoticServerList(me);
195         return me;
196 }
197 void XonoticServerList_configureXonoticServerList(entity me)
198 {
199         me.configureXonoticListBox(me);
200
201         ServerList_UpdateFieldIDs();
202
203         me.nItems = 0;
204 }
205 void XonoticServerList_setSelected(entity me, float i)
206 {
207         float save;
208         save = me.selectedItem;
209         SUPER(XonoticServerList).setSelected(me, i);
210         /*
211         if(me.selectedItem == save)
212                 return;
213         */
214         if(me.nItems == 0)
215                 return;
216         if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
217                 return; // sorry, it would be wrong
218
219         if(me.selectedServer)
220                 strunzone(me.selectedServer);
221         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
222
223         me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
224         me.ipAddressBox.cursorPos = strlen(me.selectedServer);
225         me.ipAddressBoxFocused = -1;
226 }
227 void XonoticServerList_refreshServerList(entity me, float mode)
228 {
229         // 0: just reparametrize
230         // 1: also ask for new servers
231         // 2: clear
232         //print("refresh of type ", ftos(mode), "\n");
233         /* if(mode == 2) // borken
234         {
235                 // clear list
236                 localcmd("net_slist\n");
237                 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
238         }
239         else */
240         {
241                 float m, o;
242                 string s, typestr, modstr;
243                 s = me.filterString;
244
245                 m = strstrofs(s, ":", 0);
246                 if(m >= 0)
247                 {
248                         typestr = substring(s, 0, m);
249                         s = substring(s, m + 1, strlen(s) - m - 1);
250                         while(substring(s, 0, 1) == " ")
251                                 s = substring(s, 1, strlen(s) - 1);
252                 }
253                 else
254                         typestr = "";
255
256                 modstr = cvar_string("menu_slist_modfilter");
257
258                 m = SLIST_MASK_AND - 1;
259                 resethostcachemasks();
260                 if(!me.filterShowFull)
261                 {
262                         sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL); // legacy
263                         sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, ":S0:", SLIST_TEST_NOTCONTAIN); // g_maxplayers support
264                 }
265                 if(!me.filterShowEmpty)
266                         sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
267                 if(typestr != "")
268                         sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
269                 if(modstr != "")
270                 {
271                         if(substring(modstr, 0, 1) == "!")
272                                 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(substring(modstr, 1, strlen(modstr) - 1)), SLIST_TEST_NOTEQUAL);
273                         else
274                                 sethostcachemaskstring(++m, SLIST_FIELD_MOD, resolvemod(modstr), SLIST_TEST_EQUAL);
275                 }
276                 m = SLIST_MASK_OR - 1;
277                 if(s != "")
278                 {
279                         sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
280                         sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
281                         sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
282                         sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
283                 }
284                 o = 2; // favorites first
285                 if(me.currentSortOrder < 0)
286                         o |= 1; // descending
287                 sethostcachesort(me.currentSortField, o);
288                 resorthostcache();
289                 if(mode >= 1)
290                         refreshhostcache();
291         }
292 }
293 void XonoticServerList_focusEnter(entity me)
294 {
295         if(time < me.nextRefreshTime)
296         {
297                 //print("sorry, no refresh yet\n");
298                 return;
299         }
300         me.nextRefreshTime = time + 10;
301         me.refreshServerList(me, 1);
302 }
303 void XonoticServerList_draw(entity me)
304 {
305         float i, found, owned;
306
307         if(me.currentSortField == -1)
308         {
309                 me.setSortOrder(me, SLIST_FIELD_PING, +1);
310                 me.refreshServerList(me, 2);
311         }
312         else if(me.needsRefresh == 1)
313         {
314                 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
315         }
316         else if(me.needsRefresh == 2)
317         {
318                 me.needsRefresh = 0;
319                 me.refreshServerList(me, 0);
320         }
321
322         owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
323
324         me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
325
326         me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
327         me.infoButton.disabled = ((me.nItems == 0) || !owned);
328         me.favoriteButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
329
330         found = 0;
331         if(me.selectedServer)
332         {
333                 for(i = 0; i < me.nItems; ++i)
334                         if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
335                         {
336                                 if(i != me.selectedItem)
337                                 {
338                                         me.lastClickedServer = -1;
339                                         me.selectedItem = i;
340                                 }
341                                 found = 1;
342                                 break;
343                         }
344         }
345         if(!found)
346                 if(me.nItems > 0)
347                 {
348                         if(me.selectedItem >= me.nItems)
349                                 me.selectedItem = me.nItems - 1;
350                         if(me.selectedServer)
351                                 strunzone(me.selectedServer);
352                         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
353                 }
354
355         if(owned)
356         {
357                 if(me.selectedServer != me.ipAddressBox.text)
358                 {
359                         me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
360                         me.ipAddressBox.cursorPos = strlen(me.selectedServer);
361                         me.ipAddressBoxFocused = -1;
362                 }
363         }
364
365         if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
366         {
367                 if(me.ipAddressBox.focused || me.ipAddressBoxFocused < 0)
368                         ServerList_Update_favoriteButton(NULL, me);
369                 me.ipAddressBoxFocused = me.ipAddressBox.focused;
370         }
371
372         SUPER(XonoticServerList).draw(me);
373 }
374 void ServerList_PingSort_Click(entity btn, entity me)
375 {
376         me.setSortOrder(me, SLIST_FIELD_PING, +1);
377 }
378 void ServerList_NameSort_Click(entity btn, entity me)
379 {
380         me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
381 }
382 void ServerList_MapSort_Click(entity btn, entity me)
383 {
384         me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
385 }
386 void ServerList_PlayerSort_Click(entity btn, entity me)
387 {
388         me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
389 }
390 void ServerList_TypeSort_Click(entity btn, entity me)
391 {
392         string s, t;
393         float i, m;
394         s = me.filterString;
395         m = strstrofs(s, ":", 0);
396         if(m >= 0)
397         {
398                 s = substring(s, 0, m);
399                 while(substring(s, m+1, 1) == " ") // skip spaces
400                         ++m;
401         }
402         else
403                 s = "";
404
405         for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
406         {
407                 t = GametypeNameFromType(i);
408                 if(i > 1)
409                         if(t == GametypeNameFromType(0)) // it repeats (default case)
410                         {
411                                 // no type was found
412                                 // choose the first one
413                                 s = t;
414                                 break;
415                         }
416                 if(s == GametypeNameFromType(i))
417                 {
418                         // the type was found
419                         // choose the next one
420                         s = GametypeNameFromType(i + 1);
421                         if(s == GametypeNameFromType(0))
422                                 s = "";
423                         break;
424                 }
425         }
426
427         if(s != "")
428                 s = strcat(s, ":");
429         s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
430
431         me.controlledTextbox.setText(me.controlledTextbox, s);
432         me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
433         me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
434         //ServerList_Filter_Change(me.controlledTextbox, me);
435 }
436 void ServerList_Filter_Change(entity box, entity me)
437 {
438         if(me.filterString)
439                 strunzone(me.filterString);
440         if(box.text != "")
441                 me.filterString = strzone(box.text);
442         else
443                 me.filterString = string_null;
444         me.refreshServerList(me, 0);
445
446         me.ipAddressBox.setText(me.ipAddressBox, "");
447         me.ipAddressBox.cursorPos = 0;
448         me.ipAddressBoxFocused = -1;
449 }
450 void ServerList_ShowEmpty_Click(entity box, entity me)
451 {
452         box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
453         me.refreshServerList(me, 0);
454
455         me.ipAddressBox.setText(me.ipAddressBox, "");
456         me.ipAddressBox.cursorPos = 0;
457         me.ipAddressBoxFocused = -1;
458 }
459 void ServerList_ShowFull_Click(entity box, entity me)
460 {
461         box.setChecked(box, me.filterShowFull = !me.filterShowFull);
462         me.refreshServerList(me, 0);
463
464         me.ipAddressBox.setText(me.ipAddressBox, "");
465         me.ipAddressBox.cursorPos = 0;
466         me.ipAddressBoxFocused = -1;
467 }
468 void XonoticServerList_setSortOrder(entity me, float field, float direction)
469 {
470         if(me.currentSortField == field)
471                 direction = -me.currentSortOrder;
472         me.currentSortOrder = direction;
473         me.currentSortField = field;
474         me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
475         me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
476         me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
477         me.sortButton4.forcePressed = 0;
478         me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
479         me.selectedItem = 0;
480         if(me.selectedServer)
481                 strunzone(me.selectedServer);
482         me.selectedServer = string_null;
483         me.refreshServerList(me, 0);
484 }
485 void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
486 {
487         vector originInLBSpace, sizeInLBSpace;
488         originInLBSpace = eY * (-me.itemHeight);
489         sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
490
491         vector originInDialogSpace, sizeInDialogSpace;
492         originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
493         sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
494
495         btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
496         btn.Container_size_x   =                         sizeInDialogSpace_x * theSize;
497         btn.setText(btn, theTitle);
498         btn.onClick = theFunc;
499         btn.onClickEntity = me;
500         btn.resized = 1;
501 }
502 void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
503 {
504         SUPER(XonoticServerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
505
506         me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
507         me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
508         me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
509
510         me.columnIconsOrigin = 0;
511         me.columnIconsSize = me.realFontSize_x * 3 * me.iconsSizeFactor;
512         me.columnPingSize = me.realFontSize_x * 3;
513         me.columnMapSize = me.realFontSize_x * 10;
514         me.columnTypeSize = me.realFontSize_x * 4;
515         me.columnPlayersSize = me.realFontSize_x * 5;
516         me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnIconsSize - me.columnTypeSize - 5 * me.realFontSize_x;
517         me.columnPingOrigin = me.columnIconsOrigin + me.columnIconsSize + me.realFontSize_x;
518         me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
519         me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
520         me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
521         me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
522
523         me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, _("Ping"), ServerList_PingSort_Click);
524         me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, _("Host name"), ServerList_NameSort_Click);
525         me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, _("Map"), ServerList_MapSort_Click);
526         me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, _("Type"), ServerList_TypeSort_Click);
527         me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, _("Players"), ServerList_PlayerSort_Click);
528
529         float f;
530         f = me.currentSortField;
531         if(f >= 0)
532         {
533                 me.currentSortField = -1;
534                 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
535         }
536 }
537 void ServerList_Connect_Click(entity btn, entity me)
538 {
539         if(me.ipAddressBox.text == "")
540                 localcmd("connect ", me.selectedServer, "\n");
541         else
542                 localcmd("connect ", me.ipAddressBox.text, "\n");
543 }
544 void ServerList_Favorite_Click(entity btn, entity me)
545 {
546         string ipstr;
547         ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
548         if(ipstr != "")
549         {
550                 ToggleFavorite(me.ipAddressBox.text);
551                 me.ipAddressBoxFocused = -1;
552         }
553 }
554 void ServerList_Info_Click(entity btn, entity me)
555 {
556         main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
557         DialogOpenButton_Click(me, main.serverInfoDialog);
558 }
559 void XonoticServerList_clickListBoxItem(entity me, float i, vector where)
560 {
561         if(i == me.lastClickedServer)
562                 if(time < me.lastClickedTime + 0.3)
563                 {
564                         // DOUBLE CLICK!
565                         ServerList_Connect_Click(NULL, me);
566                 }
567         me.lastClickedServer = i;
568         me.lastClickedTime = time;
569 }
570 void XonoticServerList_drawListBoxItem(entity me, float i, vector absSize, float isSelected)
571 {
572         // layout: Ping, Server name, Map name, NP, TP, MP
573         string s;
574         float p, q;
575         float isv4, isv6;
576         vector theColor;
577         float theAlpha;
578
579         if(isSelected)
580                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
581
582         if(gethostcachenumber(SLIST_FIELD_FREESLOTS, i) <= 0)
583                 theAlpha = SKINALPHA_SERVERLIST_FULL;
584         else if(strstrofs(gethostcachestring(SLIST_FIELD_QCSTATUS, i), ":S0:", 0) >= 0)
585                 theAlpha = SKINALPHA_SERVERLIST_FULL; // g_maxplayers support
586         else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
587                 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
588         else
589                 theAlpha = 1;
590
591         p = gethostcachenumber(SLIST_FIELD_PING, i);
592 #define PING_LOW 75
593 #define PING_MED 200
594 #define PING_HIGH 500
595         if(p < PING_LOW)
596                 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
597         else if(p < PING_MED)
598                 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
599         else if(p < PING_HIGH)
600         {
601                 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
602                 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
603         }
604         else
605         {
606                 theColor = eX;
607                 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
608         }
609
610         if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
611         {
612                 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
613                 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
614         }
615
616         s = gethostcachestring(SLIST_FIELD_CNAME, i);
617
618         isv4 = isv6 = 0;
619         if(substring(s, 0, 1) == "[")
620         {
621                 isv6 = 1;
622                 me.seenIPv6 += 1;
623         }
624         else if(strstrofs("0123456789", substring(s, 0, 1), 0) >= 0)
625         {
626                 isv4 = 1;
627                 me.seenIPv4 += 1;
628         }
629
630         q = stof(substring(crypto_getencryptlevel(s), 0, 1));
631         if((q <= 0 && cvar("crypto_aeslevel") >= 3) || (q >= 3 && cvar("crypto_aeslevel") <= 0))
632         {
633                 theColor = SKINCOLOR_SERVERLIST_IMPOSSIBLE;
634                 theAlpha = SKINALPHA_SERVERLIST_IMPOSSIBLE;
635         }
636
637         if(q == 1)
638         {
639                 if(cvar("crypto_aeslevel") >= 2)
640                         q |= 4;
641         }
642         if(q == 2)
643         {
644                 if(cvar("crypto_aeslevel") >= 1)
645                         q |= 4;
646         }
647         if(q == 3)
648                 q = 5;
649         if(q >= 3)
650                 q -= 2;
651         // possible status:
652         // 0: crypto off
653         // 1: AES possible
654         // 2: AES recommended but not available
655         // 3: AES possible and will be used
656         // 4: AES recommended and will be used
657         // 5: AES required
658
659         s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
660         {
661                 vector iconSize;
662                 iconSize_y = me.realFontSize_y * me.iconsSizeFactor;
663                 iconSize_x = me.realFontSize_x * me.iconsSizeFactor;
664
665                 vector iconPos;
666                 iconPos_x = (me.columnIconsSize - 3 * iconSize_x) * 0.5;
667                 iconPos_y = (1 - iconSize_y) * 0.5;
668
669                 if not(me.seenIPv4 && me.seenIPv6)
670                 {
671                         iconPos_x += iconSize_x * 0.5;
672                 }
673                 else if(me.seenIPv4 && me.seenIPv6)
674                 {
675                         if(isv6)
676                                 draw_Picture(iconPos, strcat(SKINGFX_SERVERLIST_ICON, "_ipv6"), iconSize, '1 1 1', 1);
677                         else if(isv4)
678                                 draw_Picture(iconPos, strcat(SKINGFX_SERVERLIST_ICON, "_ipv4"), iconSize, '1 1 1', 1);
679                         iconPos_x += iconSize_x;
680                 }
681
682                 draw_Picture(iconPos, strcat(SKINGFX_SERVERLIST_ICON, "_aeslevel", ftos(q)), iconSize, '1 1 1', 1);
683                 iconPos_x += iconSize_x;
684
685                 draw_Picture(iconPos, strcat(SKINGFX_SERVERLIST_ICON, "_pure", ftos(strstrofs(s, ":P0:", 0) >= 0)), iconSize, '1 1 1', 1);
686                 iconPos_x += iconSize_x;
687         }
688
689         s = ftos(p);
690         draw_Text(me.realUpperMargin * eY + (me.columnPingOrigin + me.columnPingSize - draw_TextWidth(s, 0, me.realFontSize)) * eX, s, me.realFontSize, theColor, theAlpha, 0);
691         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize, 0, me.realFontSize);
692         draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
693         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize, 0, me.realFontSize);
694         draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
695         s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
696         p = strstrofs(s, ":", 0);
697         if(p >= 0)
698                 s = substring(s, 0, p);
699         else
700                 s = "";
701         s = draw_TextShortenToWidth(s, me.columnMapSize, 0, me.realFontSize);
702         draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
703         s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
704         draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0, me.realFontSize)) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
705 }
706
707 float XonoticServerList_keyDown(entity me, float scan, float ascii, float shift)
708 {
709         float i;
710         vector org, sz;
711
712         org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
713         sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
714
715         if(scan == K_ENTER || scan == K_KP_ENTER)
716         {
717                 ServerList_Connect_Click(NULL, me);
718                 return 1;
719         }
720         else if(scan == K_MOUSE2 || scan == K_SPACE)
721         {
722                 if(me.nItems != 0)
723                 {
724                         main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
725                         DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
726                 }
727         }
728         else if(scan == K_INS || scan == K_MOUSE3 || scan == K_KP_INS)
729         {
730                 i = me.selectedItem;
731                 if(i < me.nItems)
732                 {
733                         ToggleFavorite(me.selectedServer);
734                         me.ipAddressBoxFocused = -1;
735                 }
736         }
737         else if(SUPER(XonoticServerList).keyDown(me, scan, ascii, shift))
738                 return 1;
739         else if(!me.controlledTextbox)
740                 return 0;
741         else
742                 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
743 }
744 #endif