2 CLASS(NexuizServerList) EXTENDS(NexuizListBox)
3 METHOD(NexuizServerList, configureNexuizServerList, void(entity))
4 ATTRIB(NexuizServerList, rowsPerItem, float, 1)
5 METHOD(NexuizServerList, draw, void(entity))
6 METHOD(NexuizServerList, drawListBoxItem, void(entity, float, vector, float))
7 METHOD(NexuizServerList, clickListBoxItem, void(entity, float, vector))
8 METHOD(NexuizServerList, resizeNotify, void(entity, vector, vector, vector, vector))
9 METHOD(NexuizServerList, keyDown, float(entity, float, float, float))
11 ATTRIB(NexuizServerList, realFontSize, vector, '0 0 0')
12 ATTRIB(NexuizServerList, realUpperMargin, float, 0)
13 ATTRIB(NexuizServerList, columnPingOrigin, float, 0)
14 ATTRIB(NexuizServerList, columnPingSize, float, 0)
15 ATTRIB(NexuizServerList, columnNameOrigin, float, 0)
16 ATTRIB(NexuizServerList, columnNameSize, float, 0)
17 ATTRIB(NexuizServerList, columnMapOrigin, float, 0)
18 ATTRIB(NexuizServerList, columnMapSize, float, 0)
19 ATTRIB(NexuizServerList, columnTypeOrigin, float, 0)
20 ATTRIB(NexuizServerList, columnTypeSize, float, 0)
21 ATTRIB(NexuizServerList, columnPlayersOrigin, float, 0)
22 ATTRIB(NexuizServerList, columnPlayersSize, float, 0)
24 ATTRIB(NexuizServerList, selectedServer, string, string_null) // to restore selected server when needed
25 METHOD(NexuizServerList, setSelected, void(entity, float))
26 METHOD(NexuizServerList, setSortOrder, void(entity, float, float))
27 ATTRIB(NexuizServerList, filterShowEmpty, float, 1)
28 ATTRIB(NexuizServerList, filterShowFull, float, 1)
29 ATTRIB(NexuizServerList, filterString, string, string_null)
30 ATTRIB(NexuizServerList, controlledTextbox, entity, NULL)
31 ATTRIB(NexuizServerList, ipAddressBox, entity, NULL)
32 ATTRIB(NexuizServerList, favoriteButton, entity, NULL)
33 ATTRIB(NexuizServerList, nextRefreshTime, float, 0)
34 METHOD(NexuizServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
35 ATTRIB(NexuizServerList, needsRefresh, float, 1)
36 METHOD(NexuizServerList, focusEnter, void(entity))
37 METHOD(NexuizServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
38 ATTRIB(NexuizServerList, sortButton1, entity, NULL)
39 ATTRIB(NexuizServerList, sortButton2, entity, NULL)
40 ATTRIB(NexuizServerList, sortButton3, entity, NULL)
41 ATTRIB(NexuizServerList, sortButton4, entity, NULL)
42 ATTRIB(NexuizServerList, sortButton5, entity, NULL)
43 ATTRIB(NexuizServerList, connectButton, entity, NULL)
44 ATTRIB(NexuizServerList, currentSortOrder, float, 0)
45 ATTRIB(NexuizServerList, currentSortField, float, -1)
46 ATTRIB(NexuizServerList, lastClickedServer, float, -1)
47 ATTRIB(NexuizServerList, lastClickedTime, float, 0)
48 ENDCLASS(NexuizServerList)
49 entity makeNexuizServerList();
50 void ServerList_Connect_Click(entity btn, entity me);
51 void ServerList_ShowEmpty_Click(entity box, entity me);
52 void ServerList_ShowFull_Click(entity box, entity me);
53 void ServerList_Filter_Change(entity box, entity me);
54 void ServerList_Favorite_Click(entity btn, entity me);
58 float SLIST_FIELD_CNAME;
59 float SLIST_FIELD_PING;
60 float SLIST_FIELD_GAME;
61 float SLIST_FIELD_MOD;
62 float SLIST_FIELD_MAP;
63 float SLIST_FIELD_NAME;
64 float SLIST_FIELD_MAXPLAYERS;
65 float SLIST_FIELD_NUMPLAYERS;
66 float SLIST_FIELD_NUMHUMANS;
67 float SLIST_FIELD_NUMBOTS;
68 float SLIST_FIELD_PROTOCOL;
69 float SLIST_FIELD_FREESLOTS;
70 float SLIST_FIELD_PLAYERS;
71 float SLIST_FIELD_QCSTATUS;
72 float SLIST_FIELD_ISFAVORITE;
73 void ServerList_UpdateFieldIDs()
75 SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
76 SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
77 SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
78 SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
79 SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
80 SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
81 SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
82 SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
83 SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
84 SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
85 SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
86 SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
87 SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
88 SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
89 SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
92 float IsFavorite(string srv)
96 s = cvar_string("net_slist_favorites");
97 s = strcat(" ", s, " ");
98 srv = strcat(" ", srv, " ");
99 o = strstrofs(s, srv, 0);
103 void ToggleFavorite(string srv)
107 s = cvar_string("net_slist_favorites");
108 o = strstrofs(strcat(" ", s, " "), strcat(" ", srv, " "), 0);
111 cvar_set("net_slist_favorites", strcat(s, " ", srv));
115 cvar_set("net_slist_favorites", strcat(
116 substring(s, 0, o - 1), substring(s, o + strlen(srv), strlen(s) - o - strlen(srv))
122 entity makeNexuizServerList()
125 me = spawnNexuizServerList();
126 me.configureNexuizServerList(me);
129 void configureNexuizServerListNexuizServerList(entity me)
131 me.configureNexuizListBox(me);
133 ServerList_UpdateFieldIDs();
137 void setSelectedNexuizServerList(entity me, float i)
140 save = me.selectedItem;
141 setSelectedListBox(me, i);
143 if(me.selectedItem == save)
148 if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
149 return; // sorry, it would be wrong
151 if(me.selectedServer)
152 strunzone(me.selectedServer);
153 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
155 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
156 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
158 void refreshServerListNexuizServerList(entity me, float mode)
160 // 0: just reparametrize
161 // 1: also ask for new servers
163 //print("refresh of type ", ftos(mode), "\n");
164 /* if(mode == 2) // borken
167 localcmd("net_slist\n");
168 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
176 m = strstrofs(s, ":", 0);
179 typestr = substring(s, 0, m);
180 s = substring(s, m + 1, strlen(s) - m - 1);
181 while(substring(s, 0, 1) == " ")
182 s = substring(s, 1, strlen(s) - 1);
187 m = SLIST_MASK_AND - 1;
188 resethostcachemasks();
189 if(!me.filterShowFull)
190 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
191 if(!me.filterShowEmpty)
192 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
194 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
195 m = SLIST_MASK_OR - 1;
198 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
199 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
200 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
201 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
203 o = 2; // favorites first
204 if(me.currentSortOrder < 0)
205 o |= 1; // descending
206 sethostcachesort(me.currentSortField, o);
212 void focusEnterNexuizServerList(entity me)
214 if(time < me.nextRefreshTime)
216 //print("sorry, no refresh yet\n");
219 me.nextRefreshTime = time + 10;
220 me.refreshServerList(me, 1);
222 void drawNexuizServerList(entity me)
224 float i, found, owned;
226 if(me.currentSortField == -1)
228 me.setSortOrder(me, SLIST_FIELD_PING, +1);
229 me.refreshServerList(me, 2);
231 else if(me.needsRefresh == 1)
233 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
235 else if(me.needsRefresh == 2)
238 me.refreshServerList(me, 0);
241 owned = (me.selectedServer == me.ipAddressBox.text);
243 me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
245 me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
248 if(me.selectedServer)
250 for(i = 0; i < me.nItems; ++i)
251 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
253 if(i != me.selectedItem)
255 me.lastClickedServer = -1;
265 if(me.selectedItem >= me.nItems)
266 me.selectedItem = me.nItems - 1;
267 if(me.selectedServer)
268 strunzone(me.selectedServer);
269 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
274 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
275 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
278 if(IsFavorite(me.ipAddressBox.text))
279 me.favoriteButton.setText(me.favoriteButton, "Remove");
281 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
285 void ServerList_PingSort_Click(entity btn, entity me)
287 me.setSortOrder(me, SLIST_FIELD_PING, +1);
289 void ServerList_NameSort_Click(entity btn, entity me)
291 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
293 void ServerList_MapSort_Click(entity btn, entity me)
295 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
297 void ServerList_PlayerSort_Click(entity btn, entity me)
299 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
301 void ServerList_TypeSort_Click(entity btn, entity me)
306 m = strstrofs(s, ":", 0);
309 s = substring(s, 0, m);
310 while(substring(s, m+1, 1) == " ") // skip spaces
316 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
318 t = GametypeNameFromType(i);
320 if(t == GametypeNameFromType(0)) // it repeats (default case)
323 // choose the first one
327 if(s == GametypeNameFromType(i))
329 // the type was found
330 // choose the next one
331 s = GametypeNameFromType(i + 1);
332 if(s == GametypeNameFromType(0))
340 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
342 me.controlledTextbox.setText(me.controlledTextbox, s);
343 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
344 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
345 //ServerList_Filter_Change(me.controlledTextbox, me);
347 void ServerList_Filter_Change(entity box, entity me)
350 strunzone(me.filterString);
352 me.filterString = strzone(box.text);
354 me.filterString = string_null;
355 me.refreshServerList(me, 0);
357 me.ipAddressBox.setText(me.ipAddressBox, "");
358 me.ipAddressBox.cursorPos = 0;
360 void ServerList_ShowEmpty_Click(entity box, entity me)
362 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
363 me.refreshServerList(me, 0);
365 me.ipAddressBox.setText(me.ipAddressBox, "");
366 me.ipAddressBox.cursorPos = 0;
368 void ServerList_ShowFull_Click(entity box, entity me)
370 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
371 me.refreshServerList(me, 0);
373 me.ipAddressBox.setText(me.ipAddressBox, "");
374 me.ipAddressBox.cursorPos = 0;
376 void setSortOrderNexuizServerList(entity me, float field, float direction)
378 if(me.currentSortField == field)
379 direction = -me.currentSortOrder;
380 me.currentSortOrder = direction;
381 me.currentSortField = field;
382 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
383 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
384 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
385 me.sortButton4.forcePressed = 0;
386 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
388 if(me.selectedServer)
389 strunzone(me.selectedServer);
390 me.selectedServer = string_null;
391 me.refreshServerList(me, 0);
393 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
395 vector originInLBSpace, sizeInLBSpace;
396 originInLBSpace = eY * (-me.itemHeight);
397 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
399 vector originInDialogSpace, sizeInDialogSpace;
400 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
401 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
403 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
404 btn.Container_size_x = sizeInDialogSpace_x * theSize;
405 btn.setText(btn, theTitle);
406 btn.onClick = theFunc;
407 btn.onClickEntity = me;
410 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
412 resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
414 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
415 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
416 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
418 me.columnPingOrigin = 0;
419 me.columnPingSize = me.realFontSize_x * 4;
420 me.columnMapSize = me.realFontSize_x * 12;
421 me.columnTypeSize = me.realFontSize_x * 4;
422 me.columnPlayersSize = me.realFontSize_x * 6;
423 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
424 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
425 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
426 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
427 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
429 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
430 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
431 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
432 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
433 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
436 f = me.currentSortField;
439 me.currentSortField = -1;
440 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
443 void ServerList_Connect_Click(entity btn, entity me)
445 if(me.ipAddressBox.text == "")
446 localcmd("connect ", me.selectedServer, "\n");
448 localcmd("connect ", me.ipAddressBox.text, "\n");
450 void ServerList_Favorite_Click(entity btn, entity me)
453 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
456 me.ipAddressBox.setText(me.ipAddressBox, ipstr);
457 me.ipAddressBox.cursorPos = strlen(ipstr);
458 ToggleFavorite(ipstr);
461 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
463 if(i == me.lastClickedServer)
464 if(time < me.lastClickedTime + 0.3)
467 ServerList_Connect_Click(NULL, me);
469 me.lastClickedServer = i;
470 me.lastClickedTime = time;
472 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
474 // layout: Ping, Server name, Map name, NP, TP, MP
481 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
483 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
484 theAlpha = SKINALPHA_SERVERLIST_FULL;
485 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
486 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
490 p = gethostcachenumber(SLIST_FIELD_PING, i);
493 #define PING_HIGH 500
495 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
496 else if(p < PING_MED)
497 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
498 else if(p < PING_HIGH)
500 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
501 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
506 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
509 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
511 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
512 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
516 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
517 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
518 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
519 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
520 draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
521 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
522 p = strstrofs(s, ":", 0);
524 s = substring(s, 0, p);
527 s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
528 draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
529 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
530 draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
533 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
539 ServerList_Connect_Click(NULL, me);
542 else if(scan == K_INS)
546 ToggleFavorite(me.selectedServer);
548 else if(scan == K_MOUSE2)
550 if(i == me.lastClickedDemo)
551 if(time < me.lastClickedTime + 0.3)
554 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
555 DialogOpenButton_Click(NULL, main.serverInfoDialog);
557 me.lastClickedServer = i;
558 me.lastClickedTime = time;
560 else if(scan == K_MOUSE3 || scan == K_SPACE)
562 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
563 DialogOpenButton_Click(NULL, main.serverInfoDialog);
565 else if(keyDownListBox(me, scan, ascii, shift))
567 else if(!me.controlledTextbox)
570 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);