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);
244 me.connectButton.disabled = (me.nItems == 0);
247 if(me.selectedServer)
249 for(i = 0; i < me.nItems; ++i)
250 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
252 if(i != me.selectedItem)
254 me.lastClickedServer = -1;
264 if(me.selectedItem >= me.nItems)
265 me.selectedItem = me.nItems - 1;
266 if(me.selectedServer)
267 strunzone(me.selectedServer);
268 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
273 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
274 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
277 if(IsFavorite(me.ipAddressBox.text))
278 me.favoriteButton.setText(me.favoriteButton, "Remove");
280 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
284 void ServerList_PingSort_Click(entity btn, entity me)
286 me.setSortOrder(me, SLIST_FIELD_PING, +1);
288 void ServerList_NameSort_Click(entity btn, entity me)
290 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
292 void ServerList_MapSort_Click(entity btn, entity me)
294 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
296 void ServerList_PlayerSort_Click(entity btn, entity me)
298 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
300 void ServerList_TypeSort_Click(entity btn, entity me)
305 m = strstrofs(s, ":", 0);
308 s = substring(s, 0, m);
309 while(substring(s, m+1, 1) == " ") // skip spaces
315 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
317 t = GametypeNameFromType(i);
319 if(t == GametypeNameFromType(0)) // it repeats (default case)
322 // choose the first one
326 if(s == GametypeNameFromType(i))
328 // the type was found
329 // choose the next one
330 s = GametypeNameFromType(i + 1);
331 if(s == GametypeNameFromType(0))
339 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
341 me.controlledTextbox.setText(me.controlledTextbox, s);
342 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
343 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
344 //ServerList_Filter_Change(me.controlledTextbox, me);
346 void ServerList_Filter_Change(entity box, entity me)
349 strunzone(me.filterString);
351 me.filterString = strzone(box.text);
353 me.filterString = string_null;
354 me.refreshServerList(me, 0);
356 void ServerList_ShowEmpty_Click(entity box, entity me)
358 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
359 me.refreshServerList(me, 0);
361 void ServerList_ShowFull_Click(entity box, entity me)
363 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
364 me.refreshServerList(me, 0);
366 void setSortOrderNexuizServerList(entity me, float field, float direction)
368 if(me.currentSortField == field)
369 direction = -me.currentSortOrder;
370 me.currentSortOrder = direction;
371 me.currentSortField = field;
372 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
373 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
374 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
375 me.sortButton4.forcePressed = 0;
376 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
378 if(me.selectedServer)
379 strunzone(me.selectedServer);
380 me.selectedServer = string_null;
381 me.refreshServerList(me, 0);
383 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
385 vector originInLBSpace, sizeInLBSpace;
386 originInLBSpace = eY * (-me.itemHeight);
387 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
389 vector originInDialogSpace, sizeInDialogSpace;
390 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
391 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
393 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
394 btn.Container_size_x = sizeInDialogSpace_x * theSize;
395 btn.setText(btn, theTitle);
396 btn.onClick = theFunc;
397 btn.onClickEntity = me;
400 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
402 resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
404 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
405 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
406 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
408 me.columnPingOrigin = 0;
409 me.columnPingSize = me.realFontSize_x * 4;
410 me.columnMapSize = me.realFontSize_x * 12;
411 me.columnTypeSize = me.realFontSize_x * 4;
412 me.columnPlayersSize = me.realFontSize_x * 6;
413 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
414 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
415 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
416 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
417 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
419 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
420 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
421 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
422 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
423 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
426 f = me.currentSortField;
429 me.currentSortField = -1;
430 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
433 void ServerList_Connect_Click(entity btn, entity me)
435 localcmd("connect ", me.ipAddressBox.text, "\n");
437 void ServerList_Favorite_Click(entity btn, entity me)
440 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
443 me.ipAddressBox.setText(me.ipAddressBox, ipstr);
444 me.ipAddressBox.cursorPos = strlen(ipstr);
445 ToggleFavorite(ipstr);
448 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
450 if(i == me.lastClickedServer)
451 if(time < me.lastClickedTime + 0.3)
454 ServerList_Connect_Click(NULL, me);
456 me.lastClickedServer = i;
457 me.lastClickedTime = time;
459 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
461 // layout: Ping, Server name, Map name, NP, TP, MP
468 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
470 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
471 theAlpha = SKINALPHA_SERVERLIST_FULL;
472 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
473 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
477 p = gethostcachenumber(SLIST_FIELD_PING, i);
480 #define PING_HIGH 500
482 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
483 else if(p < PING_MED)
484 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
485 else if(p < PING_HIGH)
487 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
488 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
493 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
496 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
498 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
499 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
503 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
504 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
505 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
506 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
507 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);
508 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
509 p = strstrofs(s, ":", 0);
511 s = substring(s, 0, p);
514 s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
515 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);
516 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
517 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);
520 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
527 ServerList_Connect_Click(NULL, me);
530 else if(scan == K_INS)
534 ToggleFavorite(me.selectedServer);
536 else if(keyDownListBox(me, scan, ascii, shift))
538 else if(!me.controlledTextbox)
541 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);