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