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
175 string s, typestr, modstr;
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 modstr = cvar_string("menu_slist_modfilter");
191 m = SLIST_MASK_AND - 1;
192 resethostcachemasks();
193 if(!me.filterShowFull)
194 sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
195 if(!me.filterShowEmpty)
196 sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
198 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
201 if(substring(modstr, 0, 1) == "!")
202 sethostcachemaskstring(++m, SLIST_FIELD_MOD, substring(modstr, 1, strlen(modstr) - 1), SLIST_TEST_NOTEQUAL);
204 sethostcachemaskstring(++m, SLIST_FIELD_MOD, modstr, SLIST_TEST_EQUAL);
206 m = SLIST_MASK_OR - 1;
209 sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
210 sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
211 sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
212 sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
214 o = 2; // favorites first
215 if(me.currentSortOrder < 0)
216 o |= 1; // descending
217 sethostcachesort(me.currentSortField, o);
223 void focusEnterNexuizServerList(entity me)
225 if(time < me.nextRefreshTime)
227 //print("sorry, no refresh yet\n");
230 me.nextRefreshTime = time + 10;
231 me.refreshServerList(me, 1);
233 void drawNexuizServerList(entity me)
235 float i, found, owned;
237 if(me.currentSortField == -1)
239 me.setSortOrder(me, SLIST_FIELD_PING, +1);
240 me.refreshServerList(me, 2);
242 else if(me.needsRefresh == 1)
244 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
246 else if(me.needsRefresh == 2)
249 me.refreshServerList(me, 0);
252 owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
254 me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
256 me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
257 me.infoButton.disabled = ((me.nItems == 0) || !owned);
260 if(me.selectedServer)
262 for(i = 0; i < me.nItems; ++i)
263 if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
265 if(i != me.selectedItem)
267 me.lastClickedServer = -1;
277 if(me.selectedItem >= me.nItems)
278 me.selectedItem = me.nItems - 1;
279 if(me.selectedServer)
280 strunzone(me.selectedServer);
281 me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
286 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
287 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
290 if(IsFavorite(me.ipAddressBox.text))
291 me.favoriteButton.setText(me.favoriteButton, "Remove");
293 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
297 void ServerList_PingSort_Click(entity btn, entity me)
299 me.setSortOrder(me, SLIST_FIELD_PING, +1);
301 void ServerList_NameSort_Click(entity btn, entity me)
303 me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
305 void ServerList_MapSort_Click(entity btn, entity me)
307 me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
309 void ServerList_PlayerSort_Click(entity btn, entity me)
311 me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
313 void ServerList_TypeSort_Click(entity btn, entity me)
318 m = strstrofs(s, ":", 0);
321 s = substring(s, 0, m);
322 while(substring(s, m+1, 1) == " ") // skip spaces
328 for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
330 t = GametypeNameFromType(i);
332 if(t == GametypeNameFromType(0)) // it repeats (default case)
335 // choose the first one
339 if(s == GametypeNameFromType(i))
341 // the type was found
342 // choose the next one
343 s = GametypeNameFromType(i + 1);
344 if(s == GametypeNameFromType(0))
352 s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
354 me.controlledTextbox.setText(me.controlledTextbox, s);
355 me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
356 me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
357 //ServerList_Filter_Change(me.controlledTextbox, me);
359 void ServerList_Filter_Change(entity box, entity me)
362 strunzone(me.filterString);
364 me.filterString = strzone(box.text);
366 me.filterString = string_null;
367 me.refreshServerList(me, 0);
369 me.ipAddressBox.setText(me.ipAddressBox, "");
370 me.ipAddressBox.cursorPos = 0;
372 void ServerList_ShowEmpty_Click(entity box, entity me)
374 box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
375 me.refreshServerList(me, 0);
377 me.ipAddressBox.setText(me.ipAddressBox, "");
378 me.ipAddressBox.cursorPos = 0;
380 void ServerList_ShowFull_Click(entity box, entity me)
382 box.setChecked(box, me.filterShowFull = !me.filterShowFull);
383 me.refreshServerList(me, 0);
385 me.ipAddressBox.setText(me.ipAddressBox, "");
386 me.ipAddressBox.cursorPos = 0;
388 void setSortOrderNexuizServerList(entity me, float field, float direction)
390 if(me.currentSortField == field)
391 direction = -me.currentSortOrder;
392 me.currentSortOrder = direction;
393 me.currentSortField = field;
394 me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
395 me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
396 me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
397 me.sortButton4.forcePressed = 0;
398 me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
400 if(me.selectedServer)
401 strunzone(me.selectedServer);
402 me.selectedServer = string_null;
403 me.refreshServerList(me, 0);
405 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
407 vector originInLBSpace, sizeInLBSpace;
408 originInLBSpace = eY * (-me.itemHeight);
409 sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
411 vector originInDialogSpace, sizeInDialogSpace;
412 originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
413 sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
415 btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
416 btn.Container_size_x = sizeInDialogSpace_x * theSize;
417 btn.setText(btn, theTitle);
418 btn.onClick = theFunc;
419 btn.onClickEntity = me;
422 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
424 resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
426 me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
427 me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
428 me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
430 me.columnPingOrigin = 0;
431 me.columnPingSize = me.realFontSize_x * 4;
432 me.columnMapSize = me.realFontSize_x * 12;
433 me.columnTypeSize = me.realFontSize_x * 4;
434 me.columnPlayersSize = me.realFontSize_x * 6;
435 me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
436 me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
437 me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
438 me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
439 me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
441 me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
442 me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
443 me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
444 me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
445 me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
448 f = me.currentSortField;
451 me.currentSortField = -1;
452 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
455 void ServerList_Connect_Click(entity btn, entity me)
457 if(me.ipAddressBox.text == "")
458 localcmd("connect ", me.selectedServer, "\n");
460 localcmd("connect ", me.ipAddressBox.text, "\n");
462 void ServerList_Favorite_Click(entity btn, entity me)
465 ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
468 me.ipAddressBox.setText(me.ipAddressBox, ipstr);
469 me.ipAddressBox.cursorPos = strlen(ipstr);
470 ToggleFavorite(ipstr);
473 void ServerList_Info_Click(entity btn, entity me)
475 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
476 DialogOpenButton_Click(me, main.serverInfoDialog);
478 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
480 if(i == me.lastClickedServer)
481 if(time < me.lastClickedTime + 0.3)
484 ServerList_Connect_Click(NULL, me);
486 me.lastClickedServer = i;
487 me.lastClickedTime = time;
489 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
491 // layout: Ping, Server name, Map name, NP, TP, MP
498 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
500 if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
501 theAlpha = SKINALPHA_SERVERLIST_FULL;
502 else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
503 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
507 p = gethostcachenumber(SLIST_FIELD_PING, i);
510 #define PING_HIGH 500
512 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
513 else if(p < PING_MED)
514 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
515 else if(p < PING_HIGH)
517 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
518 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
523 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
526 if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
528 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
529 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
533 draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
534 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
535 draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
536 s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
537 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);
538 s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
539 p = strstrofs(s, ":", 0);
541 s = substring(s, 0, p);
544 s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
545 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);
546 s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
547 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);
550 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
555 org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
556 sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
560 ServerList_Connect_Click(NULL, me);
563 else if(scan == K_MOUSE2 || scan == K_SPACE)
565 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
566 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
568 else if(scan == K_INS || scan == K_MOUSE3)
572 ToggleFavorite(me.selectedServer);
574 else if(keyDownListBox(me, scan, ascii, shift))
576 else if(!me.controlledTextbox)
579 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);