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, infoButton, entity, NULL)
45 ATTRIB(NexuizServerList, currentSortOrder, float, 0)
46 ATTRIB(NexuizServerList, currentSortField, float, -1)
47 ATTRIB(NexuizServerList, lastClickedServer, float, -1)
48 ATTRIB(NexuizServerList, lastClickedTime, float, 0)
49 ENDCLASS(NexuizServerList)
50 entity makeNexuizServerList();
51 void ServerList_Connect_Click(entity btn, entity me);
52 void ServerList_ShowEmpty_Click(entity box, entity me);
53 void ServerList_ShowFull_Click(entity box, entity me);
54 void ServerList_Filter_Change(entity box, entity me);
55 void ServerList_Favorite_Click(entity btn, entity me);
56 void ServerList_Info_Click(entity btn, entity me);
60 float SLIST_FIELD_CNAME;
61 float SLIST_FIELD_PING;
62 float SLIST_FIELD_GAME;
63 float SLIST_FIELD_MOD;
64 float SLIST_FIELD_MAP;
65 float SLIST_FIELD_NAME;
66 float SLIST_FIELD_MAXPLAYERS;
67 float SLIST_FIELD_NUMPLAYERS;
68 float SLIST_FIELD_NUMHUMANS;
69 float SLIST_FIELD_NUMBOTS;
70 float SLIST_FIELD_PROTOCOL;
71 float SLIST_FIELD_FREESLOTS;
72 float SLIST_FIELD_PLAYERS;
73 float SLIST_FIELD_QCSTATUS;
74 float SLIST_FIELD_ISFAVORITE;
75 void ServerList_UpdateFieldIDs()
77 SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
78 SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
79 SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
80 SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
81 SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
82 SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
83 SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
84 SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
85 SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
86 SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
87 SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
88 SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
89 SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
90 SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
91 SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
94 float IsFavorite(string srv)
98 s = cvar_string("net_slist_favorites");
99 s = strcat(" ", s, " ");
100 srv = strcat(" ", srv, " ");
101 o = strstrofs(s, srv, 0);
105 void ToggleFavorite(string srv)
109 s = cvar_string("net_slist_favorites");
110 o = strstrofs(strcat(" ", s, " "), strcat(" ", srv, " "), 0);
113 cvar_set("net_slist_favorites", strcat(s, " ", srv));
117 cvar_set("net_slist_favorites", strcat(
118 substring(s, 0, o - 1), substring(s, o + strlen(srv), strlen(s) - o - strlen(srv))
124 entity makeNexuizServerList()
127 me = spawnNexuizServerList();
128 me.configureNexuizServerList(me);
131 void configureNexuizServerListNexuizServerList(entity me)
133 me.configureNexuizListBox(me);
135 ServerList_UpdateFieldIDs();
139 void setSelectedNexuizServerList(entity me, float i)
142 save = me.selectedItem;
143 setSelectedListBox(me, i);
145 if(me.selectedItem == save)
150 if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
151 return; // sorry, it would be wrong
153 if(me.selectedServer)
154 strunzone(me.selectedServer);
155 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
157 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
158 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
160 void refreshServerListNexuizServerList(entity me, float mode)
162 // 0: just reparametrize
163 // 1: also ask for new servers
165 //print("refresh of type ", ftos(mode), "\n");
166 /* if(mode == 2) // borken
169 localcmd("net_slist\n");
170 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
178 m = strstrofs(s, ":", 0);
181 typestr = substring(s, 0, m);
182 s = substring(s, m + 1, strlen(s) - m - 1);
183 while(substring(s, 0, 1) == " ")
184 s = substring(s, 1, strlen(s) - 1);
189 m = SLIST_MASK_AND - 1;
190 resethostcachemasks();
191 if(!me.filterShowFull)
192 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
193 if(!me.filterShowEmpty)
194 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
196 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
197 m = SLIST_MASK_OR - 1;
200 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
201 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
202 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
203 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
205 o = 2; // favorites first
206 if(me.currentSortOrder < 0)
207 o |= 1; // descending
208 sethostcachesort(me.currentSortField, o);
214 void focusEnterNexuizServerList(entity me)
216 if(time < me.nextRefreshTime)
218 //print("sorry, no refresh yet\n");
221 me.nextRefreshTime = time + 10;
222 me.refreshServerList(me, 1);
224 void drawNexuizServerList(entity me)
226 float i, found, owned;
228 if(me.currentSortField == -1)
230 me.setSortOrder(me, SLIST_FIELD_PING, +1);
231 me.refreshServerList(me, 2);
233 else if(me.needsRefresh == 1)
235 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
237 else if(me.needsRefresh == 2)
240 me.refreshServerList(me, 0);
243 owned = (me.selectedServer == me.ipAddressBox.text);
245 me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
247 me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
248 me.infoButton.disabled = ((me.nItems == 0) || !owned);
251 if(me.selectedServer)
253 for(i = 0; i < me.nItems; ++i)
254 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
256 if(i != me.selectedItem)
258 me.lastClickedServer = -1;
268 if(me.selectedItem >= me.nItems)
269 me.selectedItem = me.nItems - 1;
270 if(me.selectedServer)
271 strunzone(me.selectedServer);
272 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
277 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
278 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
281 if(IsFavorite(me.ipAddressBox.text))
282 me.favoriteButton.setText(me.favoriteButton, "Remove");
284 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
288 void ServerList_PingSort_Click(entity btn, entity me)
290 me.setSortOrder(me, SLIST_FIELD_PING, +1);
292 void ServerList_NameSort_Click(entity btn, entity me)
294 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
296 void ServerList_MapSort_Click(entity btn, entity me)
298 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
300 void ServerList_PlayerSort_Click(entity btn, entity me)
302 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
304 void ServerList_TypeSort_Click(entity btn, entity me)
309 m = strstrofs(s, ":", 0);
312 s = substring(s, 0, m);
313 while(substring(s, m+1, 1) == " ") // skip spaces
319 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
321 t = GametypeNameFromType(i);
323 if(t == GametypeNameFromType(0)) // it repeats (default case)
326 // choose the first one
330 if(s == GametypeNameFromType(i))
332 // the type was found
333 // choose the next one
334 s = GametypeNameFromType(i + 1);
335 if(s == GametypeNameFromType(0))
343 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
345 me.controlledTextbox.setText(me.controlledTextbox, s);
346 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
347 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
348 //ServerList_Filter_Change(me.controlledTextbox, me);
350 void ServerList_Filter_Change(entity box, entity me)
353 strunzone(me.filterString);
355 me.filterString = strzone(box.text);
357 me.filterString = string_null;
358 me.refreshServerList(me, 0);
360 me.ipAddressBox.setText(me.ipAddressBox, "");
361 me.ipAddressBox.cursorPos = 0;
363 void ServerList_ShowEmpty_Click(entity box, entity me)
365 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
366 me.refreshServerList(me, 0);
368 me.ipAddressBox.setText(me.ipAddressBox, "");
369 me.ipAddressBox.cursorPos = 0;
371 void ServerList_ShowFull_Click(entity box, entity me)
373 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
374 me.refreshServerList(me, 0);
376 me.ipAddressBox.setText(me.ipAddressBox, "");
377 me.ipAddressBox.cursorPos = 0;
379 void setSortOrderNexuizServerList(entity me, float field, float direction)
381 if(me.currentSortField == field)
382 direction = -me.currentSortOrder;
383 me.currentSortOrder = direction;
384 me.currentSortField = field;
385 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
386 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
387 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
388 me.sortButton4.forcePressed = 0;
389 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
391 if(me.selectedServer)
392 strunzone(me.selectedServer);
393 me.selectedServer = string_null;
394 me.refreshServerList(me, 0);
396 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
398 vector originInLBSpace, sizeInLBSpace;
399 originInLBSpace = eY * (-me.itemHeight);
400 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
402 vector originInDialogSpace, sizeInDialogSpace;
403 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
404 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
406 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
407 btn.Container_size_x = sizeInDialogSpace_x * theSize;
408 btn.setText(btn, theTitle);
409 btn.onClick = theFunc;
410 btn.onClickEntity = me;
413 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
415 resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
417 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
418 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
419 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
421 me.columnPingOrigin = 0;
422 me.columnPingSize = me.realFontSize_x * 4;
423 me.columnMapSize = me.realFontSize_x * 12;
424 me.columnTypeSize = me.realFontSize_x * 4;
425 me.columnPlayersSize = me.realFontSize_x * 6;
426 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
427 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
428 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
429 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
430 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
432 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
433 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
434 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
435 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
436 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
439 f = me.currentSortField;
442 me.currentSortField = -1;
443 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
446 void ServerList_Connect_Click(entity btn, entity me)
448 if(me.ipAddressBox.text == "")
449 localcmd("connect ", me.selectedServer, "\n");
451 localcmd("connect ", me.ipAddressBox.text, "\n");
453 void ServerList_Favorite_Click(entity btn, entity me)
456 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
459 me.ipAddressBox.setText(me.ipAddressBox, ipstr);
460 me.ipAddressBox.cursorPos = strlen(ipstr);
461 ToggleFavorite(ipstr);
464 void ServerList_Info_Click(entity btn, entity me)
466 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
467 DialogOpenButton_Click(me, main.serverInfoDialog);
469 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
471 if(i == me.lastClickedServer)
472 if(time < me.lastClickedTime + 0.3)
475 ServerList_Connect_Click(NULL, me);
477 me.lastClickedServer = i;
478 me.lastClickedTime = time;
480 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
482 // layout: Ping, Server name, Map name, NP, TP, MP
489 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
491 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
492 theAlpha = SKINALPHA_SERVERLIST_FULL;
493 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
494 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
498 p = gethostcachenumber(SLIST_FIELD_PING, i);
501 #define PING_HIGH 500
503 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
504 else if(p < PING_MED)
505 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
506 else if(p < PING_HIGH)
508 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
509 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
514 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
517 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
519 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
520 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
524 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
525 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
526 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
527 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
528 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);
529 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
530 p = strstrofs(s, ":", 0);
532 s = substring(s, 0, p);
535 s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
536 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);
537 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
538 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);
541 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
546 org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
547 sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
551 ServerList_Connect_Click(NULL, me);
554 else if(scan == K_MOUSE2 || scan == K_SPACE)
556 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
557 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
559 else if(scan == K_INS || scan == K_MOUSE3)
563 ToggleFavorite(me.selectedServer);
565 else if(keyDownListBox(me, scan, ascii, shift))
567 else if(!me.controlledTextbox)
570 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);