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, nextRefreshTime, float, 0)
32 METHOD(NexuizServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
33 ATTRIB(NexuizServerList, needsRefresh, float, 1)
34 METHOD(NexuizServerList, focusEnter, void(entity))
35 METHOD(NexuizServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
36 ATTRIB(NexuizServerList, sortButton1, entity, NULL)
37 ATTRIB(NexuizServerList, sortButton2, entity, NULL)
38 ATTRIB(NexuizServerList, sortButton3, entity, NULL)
39 ATTRIB(NexuizServerList, sortButton4, entity, NULL)
40 ATTRIB(NexuizServerList, sortButton5, entity, NULL)
41 ATTRIB(NexuizServerList, connectButton, entity, NULL)
42 ATTRIB(NexuizServerList, currentSortOrder, float, 0)
43 ATTRIB(NexuizServerList, currentSortField, float, -1)
44 ATTRIB(NexuizServerList, lastClickedServer, float, -1)
45 ATTRIB(NexuizServerList, lastClickedTime, float, 0)
46 ENDCLASS(NexuizServerList)
47 entity makeNexuizServerList();
48 void ServerList_Connect_Click(entity btn, entity me);
49 void ServerList_ShowEmpty_Click(entity box, entity me);
50 void ServerList_ShowFull_Click(entity box, entity me);
51 void ServerList_Filter_Change(entity box, entity me);
52 void ServerList_AddRemoveFavorites(string fav, float resolv, float addonly);
56 float SLIST_FIELD_CNAME;
57 float SLIST_FIELD_PING;
58 float SLIST_FIELD_GAME;
59 float SLIST_FIELD_MOD;
60 float SLIST_FIELD_MAP;
61 float SLIST_FIELD_NAME;
62 float SLIST_FIELD_MAXPLAYERS;
63 float SLIST_FIELD_NUMPLAYERS;
64 float SLIST_FIELD_NUMHUMANS;
65 float SLIST_FIELD_NUMBOTS;
66 float SLIST_FIELD_PROTOCOL;
67 float SLIST_FIELD_FREESLOTS;
68 float SLIST_FIELD_PLAYERS;
69 float SLIST_FIELD_QCSTATUS;
70 float SLIST_FIELD_ISFAVORITE;
71 void ServerList_UpdateFieldIDs()
73 SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
74 SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
75 SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
76 SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
77 SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
78 SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
79 SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
80 SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
81 SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
82 SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
83 SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
84 SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
85 SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
86 SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
87 SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
90 entity makeNexuizServerList()
93 me = spawnNexuizServerList();
94 me.configureNexuizServerList(me);
97 void configureNexuizServerListNexuizServerList(entity me)
99 me.configureNexuizListBox(me);
101 ServerList_UpdateFieldIDs();
105 void setSelectedNexuizServerList(entity me, float i)
108 save = me.selectedItem;
109 setSelectedListBox(me, i);
111 if(me.selectedItem == save)
116 if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
117 return; // sorry, it would be wrong
118 if(me.selectedServer)
119 strunzone(me.selectedServer);
120 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
122 void refreshServerListNexuizServerList(entity me, float mode)
124 // 0: just reparametrize
125 // 1: also ask for new servers
127 //print("refresh of type ", ftos(mode), "\n");
128 /* if(mode == 2) // borken
131 localcmd("net_slist\n");
132 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
140 m = strstrofs(s, ":", 0);
143 typestr = substring(s, 0, m);
144 s = substring(s, m + 1, strlen(s) - m - 1);
145 while(substring(s, 0, 1) == " ")
146 s = substring(s, 1, strlen(s) - 1);
151 m = SLIST_MASK_AND - 1;
152 resethostcachemasks();
153 if(!me.filterShowFull)
154 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
155 if(!me.filterShowEmpty)
156 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
158 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
159 m = SLIST_MASK_OR - 1;
162 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
163 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
164 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
165 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
167 o = 2; // favorites first
168 if(me.currentSortOrder < 0)
169 o |= 1; // descending
170 sethostcachesort(me.currentSortField, o);
176 void focusEnterNexuizServerList(entity me)
178 if(time < me.nextRefreshTime)
180 //print("sorry, no refresh yet\n");
183 me.nextRefreshTime = time + 10;
184 me.refreshServerList(me, 1);
186 void drawNexuizServerList(entity me)
190 if(me.currentSortField == -1)
192 me.setSortOrder(me, SLIST_FIELD_PING, +1);
193 me.refreshServerList(me, 2);
195 else if(me.needsRefresh == 1)
197 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
199 else if(me.needsRefresh == 2)
202 me.refreshServerList(me, 0);
205 me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
206 me.connectButton.disabled = (me.nItems == 0);
209 if(me.selectedServer)
211 for(i = 0; i < me.nItems; ++i)
212 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
214 if(i != me.selectedItem)
216 me.lastClickedServer = -1;
226 if(me.selectedItem >= me.nItems)
227 me.selectedItem = me.nItems - 1;
228 if(me.selectedServer)
229 strunzone(me.selectedServer);
230 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
235 void ServerList_PingSort_Click(entity btn, entity me)
237 me.setSortOrder(me, SLIST_FIELD_PING, +1);
239 void ServerList_NameSort_Click(entity btn, entity me)
241 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
243 void ServerList_MapSort_Click(entity btn, entity me)
245 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
247 void ServerList_PlayerSort_Click(entity btn, entity me)
249 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
251 void ServerList_TypeSort_Click(entity btn, entity me)
256 m = strstrofs(s, ":", 0);
259 s = substring(s, 0, m);
260 while(substring(s, m+1, 1) == " ") // skip spaces
266 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
268 t = GametypeNameFromType(i);
270 if(t == GametypeNameFromType(0)) // it repeats (default case)
273 // choose the first one
277 if(s == GametypeNameFromType(i))
279 // the type was found
280 // choose the next one
281 s = GametypeNameFromType(i + 1);
282 if(s == GametypeNameFromType(0))
290 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
292 me.controlledTextbox.setText(me.controlledTextbox, s);
293 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
294 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
295 //ServerList_Filter_Change(me.controlledTextbox, me);
297 void ServerList_Filter_Change(entity box, entity me)
300 strunzone(me.filterString);
302 me.filterString = strzone(box.text);
304 me.filterString = string_null;
305 me.refreshServerList(me, 0);
307 void ServerList_ShowEmpty_Click(entity box, entity me)
309 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
310 me.refreshServerList(me, 0);
312 void ServerList_ShowFull_Click(entity box, entity me)
314 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
315 me.refreshServerList(me, 0);
317 void setSortOrderNexuizServerList(entity me, float field, float direction)
319 if(me.currentSortField == field)
320 direction = -me.currentSortOrder;
321 me.currentSortOrder = direction;
322 me.currentSortField = field;
323 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
324 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
325 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
326 me.sortButton4.forcePressed = 0;
327 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
329 if(me.selectedServer)
330 strunzone(me.selectedServer);
331 me.selectedServer = string_null;
332 me.refreshServerList(me, 0);
334 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
336 vector originInLBSpace, sizeInLBSpace;
337 originInLBSpace = eY * (-me.itemHeight);
338 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
340 vector originInDialogSpace, sizeInDialogSpace;
341 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
342 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
344 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
345 btn.Container_size_x = sizeInDialogSpace_x * theSize;
346 btn.setText(btn, theTitle);
347 btn.onClick = theFunc;
348 btn.onClickEntity = me;
351 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
353 resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
355 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
356 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
357 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
359 me.columnPingOrigin = 0;
360 me.columnPingSize = me.realFontSize_x * 4;
361 me.columnMapSize = me.realFontSize_x * 12;
362 me.columnTypeSize = me.realFontSize_x * 4;
363 me.columnPlayersSize = me.realFontSize_x * 6;
364 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
365 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
366 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
367 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
368 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
370 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
371 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
372 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
373 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
374 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
377 f = me.currentSortField;
380 me.currentSortField = -1;
381 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
384 void ServerList_Connect_Click(entity btn, entity me)
387 localcmd("connect ", me.selectedServer, "\n");
389 void ServerList_AddRemoveFavorites(string fav, float resolv, float addonly)
396 o = strstrofs(fav, ":", 0);
399 i = stof(substring(fav, o + 1, strlen(fav) - o - 1));
400 fav = substring(fav, 0, o);
402 s = netaddress_resolve(fav, i);
406 s = cvar_string("net_slist_favorites");
407 o = strstrofs(strcat(" ", s, " "), strcat(" ", fav, " "), 0);
409 cvar_set("net_slist_favorites", strcat(s, " ", fav));
411 cvar_set("net_slist_favorites", strcat(
412 substring(s, 0, o - 1), substring(s, o + strlen(fav), strlen(s) - o - strlen(fav))));
415 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
417 if(i == me.lastClickedServer)
418 if(time < me.lastClickedTime + 0.3)
421 ServerList_Connect_Click(NULL, me);
423 me.lastClickedServer = i;
424 me.lastClickedTime = time;
426 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
428 // layout: Ping, Server name, Map name, NP, TP, MP
435 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
437 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
438 theAlpha = SKINALPHA_SERVERLIST_FULL;
439 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
440 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
444 p = gethostcachenumber(SLIST_FIELD_PING, i);
447 #define PING_HIGH 500
449 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
450 else if(p < PING_MED)
451 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
452 else if(p < PING_HIGH)
454 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
455 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
460 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
463 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
465 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
466 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
470 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
471 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
472 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
473 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
474 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);
475 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
476 p = strstrofs(s, ":", 0);
478 s = substring(s, 0, p);
481 s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
482 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);
483 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
484 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);
487 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
494 ServerList_Connect_Click(NULL, me);
497 else if(scan == K_INS || scan == K_MOUSE2 || scan == K_MOUSE3)
502 ServerList_AddRemoveFavorites(me.selectedServer, false, false);
504 me.lastClickedServer = -1; // inhibit double clicks using these buttons
506 me.setSelected(me, me.selectedItem);
508 else if(keyDownListBox(me, scan, ascii, shift))
510 else if(!me.controlledTextbox)
513 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);