engine/plugins/serverb/m_master.c

952 lines
19 KiB
C

#include "cl_master.h"
int K_UPARROW,
K_DOWNARROW,
K_ENTER,
K_DEL,
K_BACKSPACE,
K_ESCAPE,
K_PGDN,
K_PGUP,
K_SPACE,
K_LEFTARROW,
K_RIGHTARROW;
#define RESTRICT_LOCAL 64
enum {
SLISTTYPE_SERVERS,
SLISTTYPE_FAVORITES,
SLISTTYPE_SOURCES,
SLISTTYPE_OPTIONS //must be last
} slist_option;
int slist_numoptions;
int slist_firstoption;
int slist_type;
void M_DrawServers(void);
void M_SListKey(int key);
#define CVAR_ARCHIVE 0
#define cvargroup "Server Browser Vars"
//filtering
vmcvar_t sb_hideempty = {"sb_hideempty", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_hidenotempty = {"sb_hidenotempty", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_hidefull = {"sb_hidefull", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_hidedead = {"sb_hidedead", "1", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_hidequake2 = {"sb_hidequake2", "1", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_hidenetquake = {"sb_hidenetquake", "1", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_hidequakeworld = {"sb_hidequakeworld", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_maxping = {"sb_maxping", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_gamedir = {"sb_gamedir", "", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_mapname = {"sb_mapname", "", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_showping = {"sb_showping", "1", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_showaddress = {"sb_showaddress", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_showmap = {"sb_showmap", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_showgamedir = {"sb_showgamedir", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_showplayers = {"sb_showplayers", "1", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_showfraglimit = {"sb_showfraglimit", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_showtimelimit = {"sb_showtimelimit", "0", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_filterkey = {"sb_filterkey", "hostname", cvargroup, CVAR_ARCHIVE};
vmcvar_t sb_filtervalue = {"sb_filtervalue", "", cvargroup, CVAR_ARCHIVE};
#undef cvargroup
vmcvar_t *cvarlist[] ={
&sb_hideempty,
&sb_hidenotempty,
&sb_hidefull,
&sb_hidedead,
&sb_hidequake2,
&sb_hidenetquake,
&sb_hidequakeworld,
&sb_maxping,
&sb_gamedir,
&sb_mapname,
&sb_showping,
&sb_showaddress,
&sb_showmap,
&sb_showgamedir,
&sb_showplayers,
&sb_showfraglimit,
&sb_showtimelimit,
&sb_filterkey,
&sb_filtervalue
};
void M_Serverlist_InitCvars(void)
{
vmcvar_t *v;
int i;
for (v = cvarlist[0],i=0; i < sizeof(cvarlist)/sizeof(cvarlist[0]); v++, i++)
v->handle = Cvar_Register(v->name, v->string, v->flags, v->group);
}
int msecstime;
int Plug_Tick(int *args)
{
msecstime = args[0];
return true;
}
int Plug_MenuEvent(int *args)
{
switch(args[0])
{
case 0: //draw
M_DrawServers();
break;
case 1: //keydown
M_SListKey(args[1]);
break;
case 2: //keyup
break;
case 3: //menu closed (this is called even if we change it).
break;
}
return 0;
}
void Plug_StartBrowser(void)
{
Menu_Control(1);
if (BUILTINISVALID(LocalSound))
LocalSound("misc/menu2.wav");
MasterInfo_Begin();
}
int Plug_ExecuteCommand(int *args)
{
char cmd[256];
Cmd_Argv(0, cmd, sizeof(cmd));
if (!strcmp("plug_browser", cmd))
{
Plug_StartBrowser();
return 1;
}
return 0;
}
int Plug_Init(int *args)
{
if (Plug_Export("Tick", Plug_Tick) &&
Plug_Export("ExecuteCommand", Plug_ExecuteCommand) &&
Plug_Export("MenuEvent", Plug_MenuEvent))
Con_Print("IRC Client Plugin Loaded\n");
else
{
Con_Print("IRC Client Plugin failed\n");
return false;
}
K_UPARROW = Key_GetKeyCode("uparrow");
K_DOWNARROW = Key_GetKeyCode("downarrow");
K_ENTER = Key_GetKeyCode("enter");
K_DEL = Key_GetKeyCode("del");
K_BACKSPACE = Key_GetKeyCode("backspace");
K_ESCAPE = Key_GetKeyCode("escape");
K_PGDN = Key_GetKeyCode("pgdn");
K_PGUP = Key_GetKeyCode("pgup");
K_SPACE = Key_GetKeyCode("space");
K_LEFTARROW = Key_GetKeyCode("leftarrow");
K_RIGHTARROW = Key_GetKeyCode("rightarrow");
M_Serverlist_InitCvars();
return true;
}
//doesn't use args or return value
int Plugin_CvarUpdate(int *args)
{
vmcvar_t *v;
int i;
for (v = cvarlist[0],i=0; i < sizeof(cvarlist)/sizeof(cvarlist[0]); v++, i++)
v->modificationcount = Cvar_Update(v->handle, v->modificationcount, v->string, &v->value);
return 0;
}
static void NM_DrawCharacter (int cx, int line, unsigned int num)
{
Draw_Character(cx, line, num);
}
static void NM_Print (int cx, int cy, qbyte *str)
{
while (*str)
{
Draw_Character (cx, cy, (*str)|128);
str++;
cx += 8;
}
}
qboolean M_IsFiltered(serverinfo_t *server) //figure out if we should filter a server.
{
if (slist_type == SLISTTYPE_FAVORITES)
if (!(server->special & SS_FAVORITE))
return true;
#ifdef Q2CLIENT
if (sb_hidequake2.value)
#endif
if (server->special & SS_QUAKE2)
return true;
#ifdef NQPROT
if (sb_hidenetquake.value)
#endif
if (server->special & SS_NETQUAKE)
return true;
if (sb_hidequakeworld.value)
if (!(server->special & (SS_QUAKE2|SS_NETQUAKE)))
return true;
if (sb_hideempty.value)
if (!server->players)
return true;
if (sb_hidenotempty.value)
if (server->players)
return true;
if (sb_hidefull.value)
if (server->players == server->maxplayers)
return true;
if (sb_hidedead.value)
if (server->maxplayers == 0)
return true;
if (sb_maxping.value)
if (server->ping > sb_maxping.value)
return true;
if (*sb_gamedir.string)
if (strcmp(server->gamedir, sb_gamedir.string))
return true;
if (*sb_mapname.string)
if (!strstr(server->map, sb_mapname.string))
return true;
return false;
}
qboolean M_MasterIsFiltered(master_t *mast)
{
#ifndef Q2CLIENT
if (mast->type == MT_BCASTQ2 || mast->type == MT_SINGLEQ2 || mast->type == MT_MASTERQ2)
return true;
#endif
#ifndef NQPROT
if (mast->type == MT_BCASTNQ || mast->type == MT_SINGLENQ)
return true;
#endif
return false;
}
static int Sbar_ColorForMap (int m)
{
m = (m < 0) ? 0 : ((m > 13) ? 13 : m);
m *= 16;
return m < 128 ? m + 8 : m + 8;
}
void M_DrawOneServer (int inity)
{
char key[512];
char value[512];
char *o;
int l, i;
char *s;
int miny=8*5;
int y=8*(5-selectedserver.linenum);
miny += inity;
y += inity;
if (!selectedserver.detail)
{
NM_Print (0, y, "No details\n");
return;
}
s = selectedserver.detail->info;
if (*s == '\\')
s++;
while (*s)
{
o = key;
while (*s && *s != '\\')
*o++ = *s++;
l = o - key;
// if (l < 20)
// {
// memset (o, ' ', 20-l);
// key[20] = 0;
// }
// else
*o = 0;
if (y>=miny)
NM_Print (0, y, va("%19s", key));
if (!*s)
{
if (y>=miny)
NM_Print (0, y, "MISSING VALUE\n");
return;
}
o = value;
s++;
while (*s && *s != '\\')
*o++ = *s++;
*o = 0;
if (*s)
s++;
if (y>=miny)
NM_Print (320/2, y, va("%s\n", value));
y+=8;
}
for ( i = 0; i < selectedserver.detail->numplayers; i++)
{
if (y>=miny)
{
if (selectedserver.detail->players[i].frags>=-999) //wow, too low, assume mvd spectators...
{
Draw_Colourp(Sbar_ColorForMap(selectedserver.detail->players[i].topc));
Draw_Fill (12, y, 28, 4);
Draw_Colourp(Sbar_ColorForMap(selectedserver.detail->players[i].botc));
Draw_Fill (12, y+4, 28, 4);
Draw_Colour3f(1,1,1);
NM_Print (12, y, va("%3i", selectedserver.detail->players[i].frags));
}
NM_Print (12+8*4, y, selectedserver.detail->players[i].name);
}
y+=8;
}
if (y<=miny) //whoops, there was a hole at the end, try scrolling up.
selectedserver.linenum--;
}
char *Info_ValueForKey(char *buffer, char *key)
{
char *s;
char *e;
static char ret[64];
for (s = buffer; *s; s++)
{
if (*s == '\\')
{ //key starts here
for (e = s+1; *e; e++)
{
//find value start
if (*e == '\\')
{
if (!strncmp(s+1, key, e - s-1))
{
//find the next \\ or \0
s = e;
for (e = s+1; *e && *e != '\\'; e++)
{
}
if (e-s>=sizeof(ret))
e = s + 63;
strncpy(ret, s+1, e-s-1);
ret[e-s-1] = '\0';
return ret;
}
break;
}
}
s = e;
}
}
return "";
}
int M_AddColumn (int right, int y, char *text, int maxchars)
{
int left;
left = right - maxchars*8;
if (left < 0)
return right;
right = left;
while (*text && maxchars>0)
{
NM_DrawCharacter (right, y, *text);
text++;
right += 8;
maxchars--;
}
return left;
}
void M_DrawServerList(void)
{
serverinfo_t *server;
int op=0, filtered=0;
int snum=0;
int blink = 0;
int x;
int y = 8*3;
CL_QueryServers();
slist_numoptions = 0;
//find total servers.
for (server = firstserver; server; server = server->next)
if (M_IsFiltered(server))
filtered++;
else
slist_numoptions++;
if (!slist_numoptions)
{
char *text, *text2="", *text3="";
if (filtered)
{
if (slist_type == SLISTTYPE_FAVORITES)
{
text = "Highlight a server";
text2 = "and press \'f\'";
text3 = "to add it to this list";
}
else
text = "All servers were filtered out";
}
else
text = "No servers found";
NM_Print((vid.width-strlen(text)*8)/2, 8*5, text);
NM_Print((vid.width-strlen(text2)*8)/2, 8*5+8, text2);
NM_Print((vid.width-strlen(text3)*8)/2, 8*5+16, text3);
return;
}
if (slist_option >= slist_numoptions)
slist_option = slist_numoptions-1;
op = vid.height/2/8;
op/=2;
op=slist_option-op;
snum = op;
if (selectedserver.inuse == true)
{
M_DrawOneServer(8*5);
return;
}
if (op < 0)
op = 0;
if (snum < 0)
snum = 0;
//find the server that we want
for (server = firstserver; op>0; server=server->next)
{
if (M_IsFiltered(server))
continue;
op--;
}
y = 8*2;
x = vid.width;
if (sb_showtimelimit.value)
x = M_AddColumn(x, y, "tl", 3);
if (sb_showfraglimit.value)
x = M_AddColumn(x, y, "fl", 3);
if (sb_showplayers.value)
x = M_AddColumn(x, y, "plyrs", 6);
if (sb_showmap.value)
x = M_AddColumn(x, y, "map", 9);
if (sb_showgamedir.value)
x = M_AddColumn(x, y, "gamedir", 9);
if (sb_showping.value)
x = M_AddColumn(x, y, "png", 4);
if (sb_showaddress.value)
x = M_AddColumn(x, y, "address", 21);
x = M_AddColumn(x, y, "name", x/8-1);
y = 8*3;
while(server)
{
if (M_IsFiltered(server))
{
server = server->next;
continue; //doesn't count
}
if (y > vid.height/2)
break;
if (slist_option == snum)
blink = (msecstime/333)&1;
if (*server->name)
{
if (blink)
Draw_Colour3f(1,1,0);
else if (server->special & SS_FAVORITE)
Draw_Colour3f(0,1,0);
else if (server->special & SS_FTESERVER)
Draw_Colour3f(1,0,0);
else if (server->special & SS_QUAKE2)
Draw_Colour3f(0,0,1);
else if (server->special & SS_NETQUAKE)
Draw_Colour3f(1,0,1);
else
Draw_Colour3f(1,1,1);
x = vid.width;
if (sb_showtimelimit.value)
x = M_AddColumn(x, y, va("%i", server->tl), 3); //time limit
if (sb_showfraglimit.value)
x = M_AddColumn(x, y, va("%i", server->fl), 3); //frag limit
if (sb_showplayers.value)
x = M_AddColumn(x, y, va("%i/%i", server->players, server->maxplayers), 6);
if (sb_showmap.value)
x = M_AddColumn(x, y, server->map, 9);
if (sb_showgamedir.value)
x = M_AddColumn(x, y, server->gamedir, 9);
if (sb_showping.value)
x = M_AddColumn(x, y, va("%i", server->ping), 4); //frag limit
if (sb_showaddress.value)
x = M_AddColumn(x, y, NET_AdrToString(&server->adr), 21);
x = M_AddColumn(x, y, server->name, x/8-1);
}
blink = 0;
if (*server->name)
y+=8;
server = server->next;
snum++;
}
Draw_Colour3f(1,1,1);
selectedserver.inuse=2;
M_DrawOneServer(vid.height/2-4*8);
}
void M_DrawSources (void)
{
int blink;
int snum=0;
int op;
int y = 3*8;
master_t *mast;
slist_numoptions = 0;
//find total sources.
for (mast = master; mast; mast = mast->next)
slist_numoptions++;
if (!slist_numoptions)
{
char *text;
if (0)//filtered)
text = "All servers were filtered out\n";
else
text = "No sources were found\n";
NM_Print((vid.width-strlen(text)*8)/2, 8*5, text);
return;
}
if (slist_option >= slist_numoptions)
slist_option = slist_numoptions-1;
op=slist_option-vid.height/2/8;
snum = op;
if (op < 0)
op = 0;
if (snum < 0)
snum = 0;
//find the server that we want
for (mast = master; op>0; mast=mast->next)
{
if (M_MasterIsFiltered(mast))
continue;
op--;
}
for (; mast; mast = mast->next)
{
if (M_MasterIsFiltered(mast))
continue;
if (slist_option == snum)
blink = (msecstime/333)&1;
else
blink = 0;
if (blink)
Draw_Colour3f(0,1,1);
else
if (mast->type == MT_MASTERQW || mast->type == MT_MASTERQ2)
Draw_Colour3f(1,1,1);
#ifdef NQPROT
else if (mast->type == MT_SINGLENQ)
Draw_Colour3f(0,1,0);
#endif
else if (mast->type == MT_SINGLEQW || mast->type == MT_SINGLEQ2)
Draw_Colour3f(0,0,1);
else
Draw_Colour3f(1,0,0);
NM_Print(46, y, va("%s", mast->name)); //white.
y+=8;
snum++;
}
}
#define NUMSLISTOPTIONS (7+7+3)
struct {
char *title;
vmcvar_t *cvar;
int type;
} options[NUMSLISTOPTIONS] = {
{"Hide Empty", &sb_hideempty},
{"Hide Not Empty", &sb_hidenotempty},
{"Hide Full", &sb_hidefull},
{"Hide Dead", &sb_hidedead},
{"Hide Quake 2", &sb_hidequake2},
{"Hide Quake 1", &sb_hidenetquake},
{"Hide QuakeWorld", &sb_hidequakeworld},
{"Show pings", &sb_showping},
{"Show Addresses", &sb_showaddress},
{"Show map", &sb_showmap},
{"Show Game Dir", &sb_showgamedir},
{"Show Players", &sb_showplayers},
{"Show Fraglimit", &sb_showfraglimit},
{"Show Timelimit", &sb_showtimelimit},
{"Max ping", &sb_maxping, 1},
{"GameDir", &sb_gamedir, 2},
{"Using map", &sb_mapname, 2}
};
void M_DrawSListOptions (void)
{
int op;
slist_numoptions = NUMSLISTOPTIONS;
for (op = 0; op < NUMSLISTOPTIONS; op++)
{
if (slist_option == op && (msecstime/333)&1)
Draw_Colour3f(0.5, 0.5, 1);
else
{
if (options[op].cvar->value>0 || (*options[op].cvar->string && *options[op].cvar->string != '0'))
Draw_Colour3f(1, 0, 0);
else
Draw_Colour3f(1, 1, 1);
}
switch(options[op].type)
{
default:
NM_Print(46, op*8+8*3, options[op].title);
break;
case 1:
if (!options[op].cvar->value)
{
NM_Print(46, op*8+8*3, va("%s ", options[op].title));
break;
}
case 2:
NM_Print(46, op*8+8*3, va("%s %s", options[op].title, options[op].cvar->string));
break;
}
}
}
void M_SListOptions_Key (int key)
{
if (key == K_UPARROW)
{
slist_option--;
if (slist_option<0)
slist_option=0;
}
else if (key == K_DOWNARROW)
{
slist_option++;
if (slist_option >= slist_numoptions)
slist_option = slist_numoptions-1;
}
switch(options[slist_option].type)
{
default:
if (key == K_ENTER)
{
if (options[slist_option].cvar->value)
VMCvar_SetString(options[slist_option].cvar, "0");
else
VMCvar_SetString(options[slist_option].cvar, "1");
}
break;
case 1:
if (key >= '0' && key <= '9')
VMCvar_SetFloat(options[slist_option].cvar, options[slist_option].cvar->value*10+key-'0');
else if (key == K_DEL)
VMCvar_SetFloat(options[slist_option].cvar, 0);
else if (key == K_BACKSPACE)
VMCvar_SetFloat(options[slist_option].cvar, (int)options[slist_option].cvar->value/10);
break;
case 2:
if ((key >= '0' && key <= '9') || (key >= 'a' && key <= 'z') || key == '_')
VMCvar_SetString(options[slist_option].cvar, va("%s%c", options[slist_option].cvar->string, key));
else if (key == K_DEL)
VMCvar_SetString(options[slist_option].cvar, "");
else if (key == K_BACKSPACE) //FIXME
VMCvar_SetString(options[slist_option].cvar, "");
break;
}
}
void M_DrawServers(void)
{
#define NUMSLISTHEADERS (SLISTTYPE_OPTIONS+1)
char *titles[NUMSLISTHEADERS] = {
"Servers",
"Favorites",
"Sources",
// "Players",
"Options"
};
int snum=0;
int width, lofs;
NET_CheckPollSockets(); //see if we were told something important.
width = vid.width / NUMSLISTHEADERS;
lofs = width/2 - 7*4;
for (snum = 0; snum < NUMSLISTHEADERS; snum++)
{
NM_Print(width*snum+width/2 - strlen(titles[snum])*4, slist_type==snum, titles[snum]);
}
NM_Print(8, 8, "\35");
for (snum = 16; snum < vid.width-16; snum+=8)
NM_Print(snum, 8, "\36");
NM_Print(snum, 8, "\37");
switch(slist_type)
{
case SLISTTYPE_SERVERS:
case SLISTTYPE_FAVORITES:
M_DrawServerList();
break;
case SLISTTYPE_SOURCES:
M_DrawSources ();
break;
case SLISTTYPE_OPTIONS:
M_DrawSListOptions ();
break;
}
}
serverinfo_t *M_FindCurrentServer(void)
{
serverinfo_t *server;
int op = slist_option;
for (server = firstserver; server; server = server->next)
{
if (M_IsFiltered(server))
continue; //doesn't count
if (!op--)
return server;
}
return NULL;
}
master_t *M_FindCurrentMaster(void)
{
master_t *mast;
int op = slist_option;
for (mast = master; mast; mast = mast->next)
{
if (M_MasterIsFiltered(mast))
continue;
if (!op--)
return mast;
}
return NULL;
}
void M_SListKey(int key)
{
if (key == K_ESCAPE)
{
// if (selectedserver.inuse)
// selectedserver.inuse = false;
// else
{
Menu_Control(0);
// Cmd_AddText("togglemenu\n", false);
}
return;
}
else if (key == K_LEFTARROW)
{
slist_type--;
if (slist_type<0)
slist_type=0;
selectedserver.linenum--;
if (selectedserver.linenum<0)
selectedserver.linenum=0;
slist_numoptions=0;
return;
}
else if (key == K_RIGHTARROW)
{
slist_type++;
if (slist_type>NUMSLISTHEADERS-1)
slist_type=NUMSLISTHEADERS-1;
selectedserver.linenum++;
slist_numoptions = 0;
return;
}
else if (key == 'q')
selectedserver.linenum--;
else if (key == 'a')
selectedserver.linenum++;
if (!slist_numoptions)
return;
if (slist_type == SLISTTYPE_OPTIONS)
{
M_SListOptions_Key(key);
return;
}
if (key == K_UPARROW)
{
slist_option--;
if (slist_option<0)
slist_option=0;
if (slist_type == SLISTTYPE_SERVERS)
SListOptionChanged(M_FindCurrentServer()); //go for these early.
}
else if (key == K_DOWNARROW)
{
slist_option++;
if (slist_option >= slist_numoptions)
slist_option = slist_numoptions-1;
if (slist_type == SLISTTYPE_SERVERS)
SListOptionChanged(M_FindCurrentServer()); //go for these early.
}
else if (key == K_PGDN)
{
slist_option+=10;
if (slist_option >= slist_numoptions)
slist_option = slist_numoptions-1;
if (slist_type == SLISTTYPE_SERVERS)
SListOptionChanged(M_FindCurrentServer()); //go for these early.
}
else if (key == K_PGUP)
{
slist_option-=10;
if (slist_option<0)
slist_option=0;
if (slist_type == SLISTTYPE_SERVERS)
SListOptionChanged(M_FindCurrentServer()); //go for these early.
}
else if (key == 'r')
MasterInfo_Begin();
else if (key == K_SPACE)
{
if (slist_type == SLISTTYPE_SERVERS)
{
selectedserver.inuse = !selectedserver.inuse;
if (selectedserver.inuse)
SListOptionChanged(M_FindCurrentServer());
}
}
else if (key == 'f')
{
serverinfo_t *server;
if (slist_type == SLISTTYPE_SERVERS) //add to favorites
{
server = M_FindCurrentServer();
if (server)
{
server->special |= SS_FAVORITE;
MasterInfo_WriteServers();
}
}
if (slist_type == SLISTTYPE_FAVORITES) //remove from favorites
{
server = M_FindCurrentServer();
if (server)
{
server->special &= ~SS_FAVORITE;
MasterInfo_WriteServers();
}
}
}
else if (key==K_ENTER)
{
serverinfo_t *server;
if (slist_type == SLISTTYPE_SERVERS || slist_type == SLISTTYPE_FAVORITES)
{
if (!selectedserver.inuse)
{
selectedserver.inuse = true;
SListOptionChanged(M_FindCurrentServer());
return;
}
server = M_FindCurrentServer();
if (!server)
return; //ah. off the end.
if (server->special & SS_NETQUAKE)
Cmd_AddText(va("nqconnect %s\n", NET_AdrToString(&server->adr)), false);
else
Cmd_AddText(va("connect %s\n", NET_AdrToString(&server->adr)), false);
}
else if (slist_type == SLISTTYPE_SOURCES)
{
MasterInfo_Request(M_FindCurrentMaster());
}
return;
}
}