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);
55 float SLIST_FIELD_CNAME;
56 float SLIST_FIELD_PING;
57 float SLIST_FIELD_GAME;
58 float SLIST_FIELD_MOD;
59 float SLIST_FIELD_MAP;
60 float SLIST_FIELD_NAME;
61 float SLIST_FIELD_MAXPLAYERS;
62 float SLIST_FIELD_NUMPLAYERS;
63 float SLIST_FIELD_NUMHUMANS;
64 float SLIST_FIELD_NUMBOTS;
65 float SLIST_FIELD_PROTOCOL;
66 float SLIST_FIELD_FREESLOTS;
67 float SLIST_FIELD_PLAYERS;
68 float SLIST_FIELD_QCSTATUS;
69 float SLIST_FIELD_ISFAVORITE;
70 void ServerList_UpdateFieldIDs()
72 SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
73 SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
74 SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
75 SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
76 SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
77 SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
78 SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
79 SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
80 SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
81 SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
82 SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
83 SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
84 SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
85 SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
86 SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
89 entity makeNexuizServerList()
92 me = spawnNexuizServerList();
93 me.configureNexuizServerList(me);
96 void configureNexuizServerListNexuizServerList(entity me)
98 me.configureNexuizListBox(me);
100 ServerList_UpdateFieldIDs();
104 void setSelectedNexuizServerList(entity me, float i)
107 save = me.selectedItem;
108 setSelectedListBox(me, i);
110 if(me.selectedItem == save)
115 if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
116 return; // sorry, it would be wrong
117 if(me.selectedServer)
118 strunzone(me.selectedServer);
119 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
121 void refreshServerListNexuizServerList(entity me, float mode)
123 // 0: just reparametrize
124 // 1: also ask for new servers
126 //print("refresh of type ", ftos(mode), "\n");
127 /* if(mode == 2) // borken
130 localcmd("net_slist\n");
131 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
139 m = strstrofs(s, ":", 0);
142 typestr = substring(s, 0, m);
143 s = substring(s, m + 1, strlen(s) - m - 1);
144 while(substring(s, 0, 1) == " ")
145 s = substring(s, 1, strlen(s) - 1);
150 m = SLIST_MASK_AND - 1;
151 resethostcachemasks();
152 if(!me.filterShowFull)
153 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
154 if(!me.filterShowEmpty)
155 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
157 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
158 m = SLIST_MASK_OR - 1;
161 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
162 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
163 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
164 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
166 o = 2; // favorites first
167 if(me.currentSortOrder < 0)
168 o |= 1; // descending
169 sethostcachesort(me.currentSortField, o);
175 void focusEnterNexuizServerList(entity me)
177 if(time < me.nextRefreshTime)
179 //print("sorry, no refresh yet\n");
182 me.nextRefreshTime = time + 10;
183 me.refreshServerList(me, 1);
185 void drawNexuizServerList(entity me)
189 if(me.currentSortField == -1)
191 me.setSortOrder(me, SLIST_FIELD_PING, +1);
192 me.refreshServerList(me, 2);
194 else if(me.needsRefresh == 1)
196 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
198 else if(me.needsRefresh == 2)
201 me.refreshServerList(me, 0);
204 me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
205 me.connectButton.disabled = (me.nItems == 0);
208 if(me.selectedServer)
210 for(i = 0; i < me.nItems; ++i)
211 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
213 if(i != me.selectedItem)
215 me.lastClickedServer = -1;
225 if(me.selectedItem >= me.nItems)
226 me.selectedItem = me.nItems - 1;
227 if(me.selectedServer)
228 strunzone(me.selectedServer);
229 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
234 void ServerList_PingSort_Click(entity btn, entity me)
236 me.setSortOrder(me, SLIST_FIELD_PING, +1);
238 void ServerList_NameSort_Click(entity btn, entity me)
240 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
242 void ServerList_MapSort_Click(entity btn, entity me)
244 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
246 void ServerList_PlayerSort_Click(entity btn, entity me)
248 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
250 void ServerList_TypeSort_Click(entity btn, entity me)
255 m = strstrofs(s, ":", 0);
258 s = substring(s, 0, m);
259 while(substring(s, m+1, 1) == " ") // skip spaces
265 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
267 t = GametypeNameFromType(i);
269 if(t == GametypeNameFromType(0)) // it repeats (default case)
272 // choose the first one
276 if(s == GametypeNameFromType(i))
278 // the type was found
279 // choose the next one
280 s = GametypeNameFromType(i + 1);
281 if(s == GametypeNameFromType(0))
289 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
291 me.controlledTextbox.setText(me.controlledTextbox, s);
292 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
293 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
294 //ServerList_Filter_Change(me.controlledTextbox, me);
296 void ServerList_Filter_Change(entity box, entity me)
299 strunzone(me.filterString);
301 me.filterString = strzone(box.text);
303 me.filterString = string_null;
304 me.refreshServerList(me, 0);
306 void ServerList_ShowEmpty_Click(entity box, entity me)
308 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
309 me.refreshServerList(me, 0);
311 void ServerList_ShowFull_Click(entity box, entity me)
313 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
314 me.refreshServerList(me, 0);
316 void setSortOrderNexuizServerList(entity me, float field, float direction)
318 if(me.currentSortField == field)
319 direction = -me.currentSortOrder;
320 me.currentSortOrder = direction;
321 me.currentSortField = field;
322 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
323 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
324 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
325 me.sortButton4.forcePressed = 0;
326 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
328 if(me.selectedServer)
329 strunzone(me.selectedServer);
330 me.selectedServer = string_null;
331 me.refreshServerList(me, 0);
333 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
335 vector originInLBSpace, sizeInLBSpace;
336 originInLBSpace = eY * (-me.itemHeight);
337 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
339 vector originInDialogSpace, sizeInDialogSpace;
340 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
341 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
343 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
344 btn.Container_size_x = sizeInDialogSpace_x * theSize;
345 btn.setText(btn, theTitle);
346 btn.onClick = theFunc;
347 btn.onClickEntity = me;
350 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
352 resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
354 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
355 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
356 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
358 me.columnPingOrigin = 0;
359 me.columnPingSize = me.realFontSize_x * 4;
360 me.columnMapSize = me.realFontSize_x * 12;
361 me.columnTypeSize = me.realFontSize_x * 4;
362 me.columnPlayersSize = me.realFontSize_x * 6;
363 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
364 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
365 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
366 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
367 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
369 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
370 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
371 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
372 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
373 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
376 f = me.currentSortField;
379 me.currentSortField = -1;
380 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
383 void ServerList_Connect_Click(entity btn, entity me)
386 localcmd("connect ", me.selectedServer, "\n");
388 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
390 if(i == me.lastClickedServer)
391 if(time < me.lastClickedTime + 0.3)
394 ServerList_Connect_Click(NULL, me);
396 me.lastClickedServer = i;
397 me.lastClickedTime = time;
399 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
401 // layout: Ping, Server name, Map name, NP, TP, MP
408 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
410 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
411 theAlpha = SKINALPHA_SERVERLIST_FULL;
412 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
413 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
417 p = gethostcachenumber(SLIST_FIELD_PING, i);
420 #define PING_HIGH 500
422 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
423 else if(p < PING_MED)
424 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
425 else if(p < PING_HIGH)
427 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
428 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
433 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
436 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
438 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
439 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
443 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
444 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
445 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
446 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
447 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);
448 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
449 p = strstrofs(s, ":", 0);
451 s = substring(s, 0, p);
454 s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
455 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);
456 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
457 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);
460 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
467 ServerList_Connect_Click(NULL, me);
470 else if(scan == K_INS || scan == K_MOUSE2 || scan == K_MOUSE3)
475 s = cvar_string("net_slist_favorites");
476 o = strstrofs(strcat(" ", s, " "), strcat(" ", me.selectedServer, " "), 0);
479 cvar_set("net_slist_favorites", strcat(s, " ", me.selectedServer));
483 cvar_set("net_slist_favorites", strcat(
484 substring(s, 0, o - 1), substring(s, o + strlen(me.selectedServer), strlen(s) - o - strlen(me.selectedServer))
489 me.lastClickedServer = -1; // inhibit double clicks using these buttons
491 me.setSelected(me, me.selectedItem);
493 else if(keyDownListBox(me, scan, ascii, shift))
495 else if(!me.controlledTextbox)
498 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);