fteqw/quakec/menusys/menu/servers.qc
2020-07-01 05:06:13 +00:00

421 lines
15 KiB
C++

//very very basic server browser.
//this was written as an example for the api, rather than a truely usable server browser.
//WARNING: make sure you only create one server browser widget otherwise they'll fight each other
#ifndef MOD_GAMEDIR
//look up what its meant to be.
//#define MOD_GAMEDIR cvar_string("game")
#endif
#include "../menusys/mitem_menu.qc" //subwindow
var float autocvar_sb_shownumplayers = 1;
var float autocvar_sb_showmaxplayers = 0;
var float autocvar_sb_showfraglimit = 0;
var float autocvar_sb_showtimelimit = 0;
var float autocvar_sb_showping = 1;
var float autocvar_sb_showgamedir = 0;
var float autocvar_sb_showaddress = 0;
var float autocvar_sb_showmap = 1;
var float autocvar_sb_showname = 1;
//#define COLUMN(width, sortname, title, draw)
#define COLUMN_NUMPLAYERS COLUMN(2*8, numplayers, "Pl", ui.drawstring(pos, sprintf("%-2g", gethostcachenumber(field_numplayers, sv)), '8 8', col, 1, 0);)
#define COLUMN_MAXPLAYERS COLUMN(2*8, maxplayers, "MP", ui.drawstring(pos, sprintf("%-2g", gethostcachenumber(field_maxplayers, sv)), '8 8', col, 1, 0);)
#define COLUMN_PING COLUMN(4*8, ping, "Ping", ui.drawstring(pos, sprintf("%-4g", gethostcachenumber(field_ping, sv)), '8 8', col, 1, 0);)
#define COLUMN_FRAGLIMIT COLUMN(4*8, fraglimit, "FL", ui.drawstring(pos, sprintf("%-3g", gethostcachenumber(field_fraglimit, sv)), '8 8', col, 1, 0);)
#define COLUMN_TIMELIMIT COLUMN(4*8, timelimit, "TL", ui.drawstring(pos, sprintf("%-3g", gethostcachenumber(field_timelimit, sv)), '8 8', col, 1, 0);)
#define COLUMN_GAMEDIR COLUMN(8*8, gamedir, "Gamedir", ui.drawstring(pos, sprintf("%-.8s", gethostcachestring(field_gamedir, sv)), '8 8', col, 1, 0);)
#define COLUMN_ADDRESS COLUMN(16*8, address, "Address", ui.drawstring(pos, sprintf("%-.16s", gethostcachestring(field_address, sv)), '8 8', col, 1, 0);)
#define COLUMN_MAP COLUMN(8*8, map, "Map", ui.drawstring(pos, sprintf("%-.8s", gethostcachestring(field_map, sv)), '8 8', col, 1, 0);)
#define COLUMN_HOSTNAME COLUMN(64*8, name, "Name", ui.drawstring(pos, sprintf("%s", gethostcachestring(field_name, sv)), '8 8', col, 1, 0);)
//FIXME: add a little * icon before the hostname for favourites or something
#define COLUMNS COLUMN_NUMPLAYERS COLUMN_MAXPLAYERS COLUMN_PING COLUMN_FRAGLIMIT COLUMN_TIMELIMIT COLUMN_GAMEDIR COLUMN_ADDRESS COLUMN_MAP COLUMN_HOSTNAME
class mitem_servers : mitem
{
float server_selected;
mitem_vslider slider;
float dbltime;
float dobound;
void() mitem_servers =
{
dbltime = cltime - 10;
this.item_flags |= IF_SELECTABLE;
server_selected = -1;
//clear the filter
resethostcachemasks();
#ifdef MOD_GAMEDIR
if (MOD_GAMEDIR != "")
{
//constrain the list to only servers with the right gamedir.
sethostcachemaskstring(0, gethostcacheindexforkey("gamedir"), MOD_GAMEDIR, SLIST_TEST_EQUAL);
}
#endif
//sort by ping by default
sethostcachesort(gethostcacheindexforkey("ping"), FALSE);
//(re)query the servers.
refreshhostcache();
resorthostcache();
};
virtual void(vector pos) item_draw =
{
local float sv, maxsv = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
local float maxy = self.item_size_y;
float left = pos_x;
local vector omin = ui.drawrectmin, omax = ui.drawrectmax;
local vector cmin = pos + '0 8', cmax = pos + item_size;
cmin_x = max(omin_x, cmin_x);
cmin_y = max(omin_y, cmin_y);
cmax_x = min(omax_x, cmax_x);
cmax_y = min(omax_y, cmax_y);
slider.maxv = maxsv - (floor(maxy/8) - 1);
//constrain the view the lazy way.
if (dobound)
{
dobound = FALSE;
if (slider.val < 0)
slider.val = 0;
while (slider.val + floor(maxy/8) - 1 <= server_selected)
slider.val+=1;
while (server_selected < slider.val && slider.val > 0)
slider.val-=1;
}
float field_name = gethostcacheindexforkey("name");
float field_ping = gethostcacheindexforkey("ping");
float field_numplayers = gethostcacheindexforkey("numhumans");
if (field_numplayers < 0)
field_numplayers = gethostcacheindexforkey("numplayers");
float field_maxplayers = gethostcacheindexforkey("maxplayers");
float field_gamedir = gethostcacheindexforkey("gamedir");
if (field_gamedir < 0)
field_gamedir = gethostcacheindexforkey("mod");
float field_address = gethostcacheindexforkey("cname");
float field_map = gethostcacheindexforkey("map");
float field_timelimit = gethostcacheindexforkey("timelimit");
float field_fraglimit = gethostcacheindexforkey("fraglimit");
maxy = ceil(maxy/8) - 1;
if (maxsv > slider.val + maxy)
maxsv = slider.val + maxy;
float sort = gethostcachevalue(SLIST_SORTFIELD);
string colkey = __NULL__;
#define COLUMN(width, sortname, title, draw) if (field_##sortname<0) autocvar_sb_show##sortname = FALSE; if (autocvar_sb_show##sortname) {if (ui.mousepos[0] > pos_x && ui.mousepos[1] < pos_y+8) colkey = #sortname; pos_x += width+8;}
COLUMNS
#undef COLUMN
vector col;
pos_x = left;
#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {col = '1 1 1'; if (sort == field_##sortname) col_z = 0; if (colkey == #sortname) col_x = 0; ui.drawstring(pos, title, '8 8', col, 1, 0); pos_x += width+8;}
COLUMNS
#undef COLUMN
//make sure things get cut off if we have too many rows (or fractional start)
ui.setcliparea(cmin[0], cmin[1], cmax[0] - cmin[0], cmax[1] - cmin[1]);
pos_y += 8 * (1-(slider.val-floor(slider.val)));
for (float y=pos_y, sv = max(0, floor(slider.val)); sv < maxsv; sv+=1)
{
col = (sv&1)?'0.1 0.1 0.1':'0.15 0.1 0.05';
drawfill([left, y], [item_size_x,8], col, 0.8, 0);
y += 8;
}
for ( sv = max(0, floor(slider.val)); sv < maxsv; sv+=1)
{
col = ((server_selected==sv)?'1 1 0':'1 1 1');
//if isproxy
//if islocal
//if isfavorite
pos_x = left;
#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {draw pos_x += width+8; }
COLUMNS
#undef COLUMN
pos_y += 8;
}
ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);
};
virtual float(vector pos, float scan, float char, float down) item_keypress =
{
float displaysize;
string addr;
/*just sink all inputs*/
if (!down)
return FALSE;
if (scan != K_MOUSE1)
dbltime = cltime - 10;
if (scan == K_MOUSE1)
{
float news = ui.mousepos[1] - (pos_y+8);
news = floor(news / 8);
if (news == -1)
{
string colkey = "";
#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {if (ui.mousepos[0] > pos_x) colkey = #sortname; pos_x += width+8;}
COLUMNS
#undef COLUMN
if (colkey != "")
{
float fld = gethostcacheindexforkey(colkey);
if (colkey == "numplayers")
{
//favour descending order
sethostcachesort(fld, (gethostcachevalue(SLIST_SORTFIELD) != fld) || !gethostcachevalue(SLIST_SORTDESCENDING));
}
else
{
//favour ascending order
sethostcachesort(fld, (gethostcachevalue(SLIST_SORTFIELD) == fld) && !gethostcachevalue(SLIST_SORTDESCENDING));
}
resorthostcache(); //tell the engine that its okay to resort everything.
dobound = TRUE;
}
}
if (news < 0 || news >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))
return FALSE;
news += floor(slider.val);
if (server_selected == news && dbltime > cltime)
{
//connect on double clicks. because we can.
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;connect \"%s\"\n", addr));
}
else
server_selected = news;
dobound = TRUE;
dbltime = cltime + 0.5;
}
else if (scan == K_ENTER)
{ //connect normally
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;connect \"%s\"\n", addr));
}
else if (scan == 's')
{ //s = join as a spectator
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;observe \"%s\"\n", addr));
}
else if (scan == 'j')
{ //s = join as a spectator
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;join \"%s\"\n", addr));
}
else if (scan == K_UPARROW || scan == K_MWHEELUP)
{
this.server_selected -= 1;
if (this.server_selected < 0)
{
this.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
if (this.server_selected)
this.server_selected -= 1;
}
dobound = TRUE;
}
else if (scan == K_DOWNARROW || scan == K_MWHEELDOWN)
{
this.server_selected += 1;
if (this.server_selected >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))
this.server_selected = 0;
dobound = TRUE;
}
else if (scan == K_PGUP)
{
displaysize = (item_size[1]-8)/(8); //this many rows
displaysize = floor(displaysize*0.5);
if (displaysize < 1)
displaysize = 1;
this.server_selected -= displaysize ;
if (this.server_selected < 0)
this.server_selected = 0;
dobound = TRUE;
}
else if (scan == K_PGDN)
{
displaysize = (item_size[1]-8)/(8); //this many rows
displaysize = floor(displaysize*0.5);
if (displaysize < 1)
displaysize = 1;
this.server_selected += displaysize;
if (this.server_selected >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))
this.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)-1;
dobound = TRUE;
}
else if (scan == K_HOME)
{
this.server_selected = 0;
dobound = TRUE;
}
else if (scan == K_END)
{
this.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)-1;
if (this.server_selected < 0)
this.server_selected = 0;
dobound = TRUE;
}
else if (char == 'r' || char == 'R' || scan == K_F5)
{
refreshhostcache();
resorthostcache();
}
else
return FALSE;
return TRUE;
};
};
class mitem_servers_players : mitem
{
mitem_servers listing;
void() mitem_servers_players =
{
// this.item_flags |= IF_SELECTABLE;
};
virtual void(vector pos) item_draw =
{
float player;
float y;
float m;
vector opos = pos;
if (listing.server_selected < 0)
return;
for (player = 0, y = 0; player < 256; player++)
{
string playerinfo = gethostcachestring(gethostcacheindexforkey(sprintf("player%g", player)), listing.server_selected);
if (!playerinfo)
break;
tokenize(playerinfo);
float userid = stof(argv(0));
float frags = stof(argv(1));
float ontime = stof(argv(2));
float ping = stof(argv(3));
string name = argv(4);
string skin = argv(5);
vector top = stov(argv(6));
vector bot = stov(argv(7));
drawfill(pos, '16 4', top, 1, 0);
drawfill(pos+'0 4 0', '16 4', bot, 1, 0);
drawstring(pos, sprintf("%g", frags), '8 8', '1 1 1', 1, 0);
drawstring(pos+'20 0', name, '8 8', '1 1 1', 1, 0);
pos_y += 8;
if (++y == 8)
{
y-= 8;
pos_y = opos_y;
pos_x += 16*8;
}
}
if (y)
{
pos_y = opos_y;
pos_x += 16*6;
y = 0;
}
// drawtextfield(opos, item_size, 3, gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected));
m = tokenizebyseparator(gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected), "\\");
for(player = 1; player <= m; player += 2)
{
drawtextfield(pos, '64 8', 6, argv(player));
drawtextfield(pos+'68 0', [32*8-40, 8], 3, argv(player+1));
pos_y += 8;
if (++y == 8)
{
y-= 8;
pos_y -= 8*8;
pos_x += 32*8;
}
}
};
};
nonstatic void(mitem_desktop desktop) M_Servers =
{
mitem_menu o;
if (checkcommand2("menu_servers", FALSE) && argv(1) != "force")
{
localcmd("menu_servers\n");
return;
}
#ifdef CSQC
if not (checkextension("FTE_CSQC_SERVERBROWSER"))
{
print(_("Sorry, your client does not support FTE_CSQC_SERVERBROWSER\n"));
return;
}
#endif
o = (mitem_menu)desktop.findchildtext(_("Servers List"));
if (o)
o.totop();
else
{
#if 1
mitem_exmenu m;
m = spawn(mitem_exmenu, item_text:_("Options"), item_flags:IF_SELECTABLE, item_command:"m_main");
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
#else
mitem_menu m;
m = menu_spawn(desktop, _("Servers List"), '320 200');
m.item_command = "m_main";
m.item_flags |= IF_RESIZABLE;
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
#endif
mitem_vslider sl = spawn(mitem_vslider, stride:4, item_flags:IF_SELECTABLE);
mitem_servers ls = spawn(mitem_servers, slider:sl);
m.add(ls, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '-16 -68');
m.add(spawn(mitem_servers_players, listing:ls), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [8*20, -8*8], [0, -8*0]);
m.add(sl, RS_X_MIN_PARENT_MAX|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [-16, 8], [0, -68]);
//only add checkboxes for field names accepted by this engine.
if (gethostcacheindexforkey("ping") >= 0)
m.add(menuitemcheck_spawn(_("Ping"), "sb_showping", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*8], [8*20, -8*7]);
if (gethostcacheindexforkey("cname") >= 0)
m.add(menuitemcheck_spawn(_("Address"), "sb_showaddress", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*7], [8*20, -8*6]);
if (gethostcacheindexforkey("map") >= 0)
m.add(menuitemcheck_spawn(_("Map"), "sb_showmap", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*6], [8*20, -8*5]);
if (gethostcacheindexforkey("gamedir") >= 0 || gethostcacheindexforkey("mod") >= 0)
m.add(menuitemcheck_spawn(_("Gamedir"), "sb_showgamedir", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*5], [8*20, -8*4]);
if (gethostcacheindexforkey("numplayers") >= 0)
m.add(menuitemcheck_spawn(_("NumPlayers"), "sb_shownumplayers", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*4], [8*20, -8*3]);
if (gethostcacheindexforkey("maxplayers") >= 0)
m.add(menuitemcheck_spawn(_("MaxPlayers"), "sb_showmaxplayers", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*3], [8*20, -8*2]);
if (gethostcacheindexforkey("fraglimit") >= 0)
m.add(menuitemcheck_spawn(_("Fraglimit"), "sb_showfraglimit", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*2], [8*20, -8*1]);
if (gethostcacheindexforkey("timelimit") >= 0)
m.add(menuitemcheck_spawn(_("Timelimit"), "sb_showtimelimit", [8*8, 8]), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*1], [8*20, -8*0]);
}
};