#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; } }