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);
246 if(me.selectedServer)
248 for(i = 0; i < me.nItems; ++i)
249 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
251 if(i != me.selectedItem)
253 me.lastClickedServer = -1;
263 if(me.selectedItem >= me.nItems)
264 me.selectedItem = me.nItems - 1;
265 if(me.selectedServer)
266 strunzone(me.selectedServer);
267 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
272 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
273 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
276 if(IsFavorite(me.ipAddressBox.text))
277 me.favoriteButton.setText(me.favoriteButton, "Remove");
279 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
283 void ServerList_PingSort_Click(entity btn, entity me)
285 me.setSortOrder(me, SLIST_FIELD_PING, +1);
287 void ServerList_NameSort_Click(entity btn, entity me)
289 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
291 void ServerList_MapSort_Click(entity btn, entity me)
293 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
295 void ServerList_PlayerSort_Click(entity btn, entity me)
297 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
299 void ServerList_TypeSort_Click(entity btn, entity me)
304 m = strstrofs(s, ":", 0);
307 s = substring(s, 0, m);
308 while(substring(s, m+1, 1) == " ") // skip spaces
314 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
316 t = GametypeNameFromType(i);
318 if(t == GametypeNameFromType(0)) // it repeats (default case)
321 // choose the first one
325 if(s == GametypeNameFromType(i))
327 // the type was found
328 // choose the next one
329 s = GametypeNameFromType(i + 1);
330 if(s == GametypeNameFromType(0))
338 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
340 me.controlledTextbox.setText(me.controlledTextbox, s);
341 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
342 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
343 //ServerList_Filter_Change(me.controlledTextbox, me);
345 void ServerList_Filter_Change(entity box, entity me)
348 strunzone(me.filterString);
350 me.filterString = strzone(box.text);
352 me.filterString = string_null;
353 me.refreshServerList(me, 0);
355 me.ipAddressBox.setText(me.ipAddressBox, "");
356 me.ipAddressBox.cursorPos = 0;
358 void ServerList_ShowEmpty_Click(entity box, entity me)
360 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
361 me.refreshServerList(me, 0);
363 me.ipAddressBox.setText(me.ipAddressBox, "");
364 me.ipAddressBox.cursorPos = 0;
366 void ServerList_ShowFull_Click(entity box, entity me)
368 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
369 me.refreshServerList(me, 0);
371 me.ipAddressBox.setText(me.ipAddressBox, "");
372 me.ipAddressBox.cursorPos = 0;
374 void setSortOrderNexuizServerList(entity me, float field, float direction)
376 if(me.currentSortField == field)
377 direction = -me.currentSortOrder;
378 me.currentSortOrder = direction;
379 me.currentSortField = field;
380 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
381 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
382 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
383 me.sortButton4.forcePressed = 0;
384 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
386 if(me.selectedServer)
387 strunzone(me.selectedServer);
388 me.selectedServer = string_null;
389 me.refreshServerList(me, 0);
391 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
393 vector originInLBSpace, sizeInLBSpace;
394 originInLBSpace = eY * (-me.itemHeight);
395 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
397 vector originInDialogSpace, sizeInDialogSpace;
398 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
399 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
401 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
402 btn.Container_size_x = sizeInDialogSpace_x * theSize;
403 btn.setText(btn, theTitle);
404 btn.onClick = theFunc;
405 btn.onClickEntity = me;
408 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
410 resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
412 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
413 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
414 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
416 me.columnPingOrigin = 0;
417 me.columnPingSize = me.realFontSize_x * 4;
418 me.columnMapSize = me.realFontSize_x * 12;
419 me.columnTypeSize = me.realFontSize_x * 4;
420 me.columnPlayersSize = me.realFontSize_x * 6;
421 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
422 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
423 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
424 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
425 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
427 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
428 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
429 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
430 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
431 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
434 f = me.currentSortField;
437 me.currentSortField = -1;
438 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
441 void ServerList_Connect_Click(entity btn, entity me)
443 localcmd("connect ", me.ipAddressBox.text, "\n");
445 void ServerList_Favorite_Click(entity btn, entity me)
448 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
451 me.ipAddressBox.setText(me.ipAddressBox, ipstr);
452 me.ipAddressBox.cursorPos = strlen(ipstr);
453 ToggleFavorite(ipstr);
456 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
458 if(i == me.lastClickedServer)
459 if(time < me.lastClickedTime + 0.3)
462 ServerList_Connect_Click(NULL, me);
464 me.lastClickedServer = i;
465 me.lastClickedTime = time;
467 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
469 // layout: Ping, Server name, Map name, NP, TP, MP
476 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
478 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
479 theAlpha = SKINALPHA_SERVERLIST_FULL;
480 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
481 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
485 p = gethostcachenumber(SLIST_FIELD_PING, i);
488 #define PING_HIGH 500
490 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
491 else if(p < PING_MED)
492 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
493 else if(p < PING_HIGH)
495 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
496 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
501 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
504 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
506 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
507 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
511 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
512 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
513 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
514 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
515 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);
516 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
517 p = strstrofs(s, ":", 0);
519 s = substring(s, 0, p);
522 s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
523 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);
524 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
525 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);
528 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
534 ServerList_Connect_Click(NULL, me);
537 else if(scan == K_INS)
541 ToggleFavorite(me.selectedServer);
543 else if(keyDownListBox(me, scan, ascii, shift))
545 else if(!me.controlledTextbox)
548 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);