diff --git a/source/src/main.cpp b/source/src/main.cpp index ef796fb2..da82c966 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -692,11 +692,12 @@ int main(int argc, char **argv) audiomgr.initsound(); initlog("cfg"); - extern void *scoremenu, *teammenu, *ctfmenu, *servmenu, *kickmenu, *banmenu, *forceteammenu, *giveadminmenu, *docmenu, *applymenu; + extern void *scoremenu, *teammenu, *ctfmenu, *servmenu, *searchmenu, *kickmenu, *banmenu, *forceteammenu, *giveadminmenu, *docmenu, *applymenu; scoremenu = addmenu("score", "frags\tdeath\tratio\tpj\tping\tcn\tname", false, renderscores, NULL, false, true); teammenu = addmenu("team score", "frags\tdeath\tratio\tpj\tping\tcn\tname", false, renderscores, NULL, false, true); ctfmenu = addmenu("ctf score", "flags\tfrags\tdeath\tratio\tpj\tping\tcn\tname", false, renderscores, NULL, false, true); - servmenu = addmenu("server", "ping\tplr\tserver", true, refreshservers, serverskey); + servmenu = addmenu("server", NULL, true, refreshservers, serverskey); + searchmenu = addmenu("search", NULL, true, refreshservers, serverskey); kickmenu = addmenu("kick player", NULL, true, refreshsopmenu); banmenu = addmenu("ban player", NULL, true, refreshsopmenu); forceteammenu = addmenu("force team", NULL, true, refreshsopmenu); diff --git a/source/src/protocol.h b/source/src/protocol.h index 777e5e97..4b86da28 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -93,6 +93,7 @@ enum { DISC_NONE = 0, DISC_EOP, DISC_CN, DISC_MKICK, DISC_MBAN, DISC_TAGT, DISC_ #define EXT_PLAYERSTATS_RESP_STATS -11 enum { PONGFLAG_PASSWORD = 0, PONGFLAG_BANNED, PONGFLAG_BLACKLIST, PONGFLAG_MASTERMODE = 6, PONGFLAG_NUM }; +enum { EXTPING_NOP = 0, EXTPING_NAMELIST, EXTPING_NUM }; enum { diff --git a/source/src/protos.h b/source/src/protos.h index 90254ad4..3ab0c10d 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -195,11 +195,13 @@ struct serverinfo string sdesc; string description; string cmd; - int mode, numplayers, maxclients, ping, protocol, minremain, resolved, port, lastpingmillis, pongflags; + int mode, numplayers, maxclients, ping, protocol, minremain, resolved, port, lastpingmillis, pongflags, getnames; ENetAddress address; + vector playernames; + uchar namedata[MAXTRANS]; serverinfo() - : mode(0), numplayers(0), maxclients(0), ping(9999), protocol(0), minremain(0), resolved(UNRESOLVED), port(-1), lastpingmillis(0), pongflags(0) + : mode(0), numplayers(0), maxclients(0), ping(9999), protocol(0), minremain(0), resolved(UNRESOLVED), port(-1), lastpingmillis(0), pongflags(0), getnames(0) { name[0] = full[0] = map[0] = sdesc[0] = description[0] = '\0'; } diff --git a/source/src/server.cpp b/source/src/server.cpp index df43829e..c02f80a2 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -3439,6 +3439,16 @@ int getpongflags(enet_uint32 ip) flags |= checkblacklist(ip) ? 1 << PONGFLAG_BLACKLIST : 0; return flags; } + +void extping_namelist(ucharbuf &p) +{ + loopv(clients) + { + if(clients[i]->type == ST_TCPIP && clients[i]->isauthed) sendstring(clients[i]->name, p); + } + sendstring("", p); +} + void extinfo_cnbuf(ucharbuf &p, int cn) { if(cn == -1) // add all available player ids diff --git a/source/src/serverbrowser.cpp b/source/src/serverbrowser.cpp index 7c655834..2e1ce103 100644 --- a/source/src/serverbrowser.cpp +++ b/source/src/serverbrowser.cpp @@ -337,7 +337,7 @@ VARP(servpingrate, 1000, 5000, 60000); VARP(maxservpings, 0, 0, 1000); VAR(searchlan, 0, 1, 2); -void pingservers() +void pingservers(bool issearch) { if(pingsock == ENET_SOCKET_NULL) { @@ -351,9 +351,10 @@ void pingservers() enet_socket_set_option(pingsock, ENET_SOCKOPT_BROADCAST, 1); } ENetBuffer buf; - uchar ping[MAXTRANS]; + static uchar ping[MAXTRANS]; ucharbuf p(ping, sizeof(ping)); putint(p, totalmillis); + int baselen = p.length(); if(searchlan < 2) { static int lastping = 0; @@ -363,6 +364,8 @@ void pingservers() serverinfo &si = *servers[lastping]; if(++lastping >= servers.length()) lastping = 0; if(si.address.host == ENET_HOST_ANY) continue; + p.len = baselen; + putint(p, si.getnames || issearch ? EXTPING_NAMELIST : EXTPING_NOP); buf.data = ping; buf.dataLength = p.length(); enet_socket_send(pingsock, &si.address, &buf, 1); @@ -373,6 +376,8 @@ void pingservers() ENetAddress address; address.host = ENET_HOST_BROADCAST; address.port = CUBE_SERVINFO_PORT_LAN; + p.len = baselen; + putint(p, issearch ? EXTPING_NAMELIST : EXTPING_NOP); buf.data = ping; buf.dataLength = p.length(); enet_socket_send(pingsock, &address, &buf, 1); @@ -419,8 +424,8 @@ void checkpings() enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; ENetBuffer buf; ENetAddress addr; - uchar ping[MAXTRANS]; - char text[MAXTRANS]; + static uchar ping[MAXTRANS]; + static char text[MAXTRANS]; buf.data = ping; buf.dataLength = sizeof(ping); while(enet_socket_wait(pingsock, &events, 0) >= 0 && events) @@ -439,6 +444,7 @@ void checkpings() ucharbuf p(ping, len); si->lastpingmillis = totalmillis; si->ping = totalmillis - getint(p); + int query = getint(p); si->protocol = getint(p); if(si->protocol!=PROTOCOL_VERSION) si->ping = 9998; si->mode = getint(p); @@ -453,6 +459,29 @@ void checkpings() if(p.remaining()) { si->pongflags = getint(p); + if(p.remaining() && getint(p) == query) + { + switch(query) + { + case EXTPING_NAMELIST: + { + si->playernames.setsizenodelete(0); + ucharbuf q(si->namedata, sizeof(si->namedata)); + loopi(si->numplayers) + { + getstring(text, p); + filtertext(text, text, 0); + if(text[0] && !p.overread()) + { + si->playernames.add((const char *)si->namedata + q.length()); + sendstring(text, q); + } + else break; + } + break; + } + } + } } else { @@ -479,6 +508,7 @@ enum { SBS_PING = 0, SBS_NUMPL, SBS_MAXPL, SBS_MINREM, SBS_MAP, SBS_MODE, SBS_IP VARP(serversort, 0, 0, NUMSERVSORT-1); VARP(serversortdir, 0, 0, 1); VARP(showonlygoodservers, 0, 0, 1); +VARP(shownamesinbrowser, 0, 0, 1); VARP(showminremain, 0, 0, 1); int sicompare(serverinfo **ap, serverinfo **bp) @@ -543,15 +573,37 @@ int sicompare(serverinfo **ap, serverinfo **bp) else return -dir; } -void *servmenu = NULL; +void *servmenu = NULL, *searchmenu = NULL; +vector namelists; + +string cursearch, cursearchuc; + +void searchnickname(const char *name) +{ + if(!name || !name[0]) return; + s_strcpy(cursearch, name); + s_strcpy(cursearchuc, name); + for(char *t = cursearchuc; *t; t++) *t = toupper(*t); + showmenu("search"); +} +COMMAND(searchnickname, ARG_1STR); VAR(showallservers, 0, 1, 1); +bool matchplayername(const char *name) +{ + static string nameuc; + s_strcpy(nameuc, name); + for(char *t = nameuc; *t; t++) *t = toupper(*t); + return strstr(nameuc, cursearchuc) != NULL; +} + void refreshservers(void *menu, bool init) { static int servermenumillis; static bool usedselect = false; static string title; + bool issearch = menu == searchmenu; serverinfo *curserver = getconnectedserverinfo(), *oldsel = NULL; if(init) @@ -566,32 +618,41 @@ void refreshservers(void *menu, bool init) checkresolver(); checkpings(); - if(totalmillis - lastinfo >= servpingrate/(maxservpings ? (servers.length() + maxservpings - 1) / maxservpings : 1)) pingservers(); + if((init && issearch) || totalmillis - lastinfo >= (servpingrate * (issearch ? 2 : 1))/(maxservpings ? (servers.length() + maxservpings - 1) / maxservpings : 1)) pingservers(issearch); if(!oldsel && menu && servers.inrange(((gmenu *)menu)->menusel) && (usedselect || ((gmenu *)menu)->menusel > 0)) oldsel = servers[((gmenu *)menu)->menusel]; servers.sort(sicompare); + int cursel = -1; if(oldsel) { - loopv(servers) if(servers[i] == oldsel) { ((gmenu *)menu)->menusel = i; usedselect = true; break; } + loopv(servers) if(servers[i] == oldsel) + { + ((gmenu *)menu)->menusel = i; + usedselect = true; + servers[i]->getnames = shownamesinbrowser ? 1 : 0; + cursel = i; + break; + } } if(menu) { static const char *titles[NUMSERVSORT] = { - "\fs\f0ping\fr\tplr\tserver%s", // 0: ping - "ping\t\fs\f0plr\fr\tserver%s", // 1: player number - "ping\tplr\tserver (\fs\f0max players\fr)%s", // 2: maxplayers - "ping\tplr\fs\f0\fr\tserver (\fs\f0minutes remaining\fr)%s", // 3: minutes remaining - "ping\tplr\tserver (\fs\f0map\fr)%s", // 4: map - "ping\tplr\tserver (\fs\f0game mode\fr)%s", // 5: mode - "ping\tplr\tserver (\fs\f0IP\fr)%s", // 6: IP - "ping\tplr\tserver (\fs\f0description\fr)%s" // 7: description + "\fs\f0ping\fr\tplr\tserver%s%s", // 0: ping + "ping\t\fs\f0plr\fr\tserver%s%s", // 1: player number + "ping\tplr\tserver (\fs\f0max players\fr)%s%s", // 2: maxplayers + "ping\tplr\fs\f0\fr\tserver (\fs\f0minutes remaining\fr)%s%s", // 3: minutes remaining + "ping\tplr\tserver (\fs\f0map\fr)%s%s", // 4: map + "ping\tplr\tserver (\fs\f0game mode\fr)%s%s", // 5: mode + "ping\tplr\tserver (\fs\f0IP\fr)%s%s", // 6: IP + "ping\tplr\tserver (\fs\f0description\fr)%s%s" // 7: description }; bool showmr = showminremain || serversort == SBS_MINREM; - s_sprintf(title)(titles[serversort], /*showmr ? "\tmr" : "",*/ " (F1: Help)"); + s_sprintf(title)(titles[serversort], issearch ? " search results for \f3" : " (F1: Help)", issearch ? cursearch : ""); serverinfo *curserver = getconnectedserverinfo(); menutitle(menu, title); menureset(menu); string text; + int curnl = 0; loopv(servers) { serverinfo &si = *servers[i]; @@ -622,14 +683,52 @@ void refreshservers(void *menu, bool init) if(!showonlygoodservers) s_sprintf(si.full)(si.address.host != ENET_HOST_ANY ? "%s:%d [waiting for server response]" : "%s:%d [unknown host]", si.name, si.port); else showthisone = false; } + if(issearch && showthisone) + { + bool found = false; + loopvj(si.playernames) if(matchplayername(si.playernames[j])) { found = true; break; }; + if(!found) showthisone = false; + } if(showthisone) { si.full[75] = 0; // cut off too long server descriptions si.description[75] = 0; s_sprintf(si.cmd)("connect %s %d", si.name, si.port); menumanual(menu, si.full, si.cmd, NULL, si.description); + if((shownamesinbrowser && cursel == i && si.playernames.length()) || issearch) + { + int cur = 0; + char *t = NULL; + loopvj(si.playernames) + { + if(cur == 0) + { + if(namelists.length() < ++curnl) namelists.add(newstringbuf()); + t = namelists[curnl - 1]; + s_strcpy(t, "\t"); + } + if(!issearch || matchplayername(si.playernames[j])) + { + s_strcat(t, " \t"); + s_strcat(t, si.playernames[j]); + cur++; + }; + if(cur == 4) + { + menumanual(menu, t, NULL, NULL, NULL); + cur = 0; + } + } + if(cur) menumanual(menu, t, NULL, NULL, NULL); + } } } + if(issearch && curnl == 0) + { + static string notfoundmsg; + s_sprintf(notfoundmsg)("\t\tpattern \fs\f3%s\fr not found.", cursearch); + menumanual(menu, notfoundmsg, NULL, NULL, NULL); + } } } @@ -646,14 +745,6 @@ bool serverskey(void *menu, int code, bool isdown, int unicode) serversort = (serversort+1) % NUMSERVSORT; return true; - case SDLK_F1: - showmenu("serverbrowser help"); - return true; - - case SDLK_F3: - showmenu("search player"); - return true; - case SDLK_F5: updatefrommaster(1); return true; @@ -661,6 +752,21 @@ bool serverskey(void *menu, int code, bool isdown, int unicode) case SDLK_F6: serversortdir = serversortdir ? 0 : 1; return true; + } + if(menu == searchmenu) return false; + switch(code) + { + case SDLK_F1: + showmenu("serverbrowser help"); + return true; + + case SDLK_F2: + shownamesinbrowser = shownamesinbrowser ? 0 : 1; + return true; + + case SDLK_F3: + showmenu("search player"); + return true; case SDLK_F7: showonlygoodservers = showonlygoodservers ? 0 : 1; diff --git a/source/src/serverms.cpp b/source/src/serverms.cpp index bd84e914..c81104c7 100644 --- a/source/src/serverms.cpp +++ b/source/src/serverms.cpp @@ -206,6 +206,24 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int milli sendstring(serverdesc, po); putint(po, maxclients); putint(po, getpongflags(addr.host)); + if(pi.remaining()) + { + int query = getint(pi); + switch(query) + { + case EXTPING_NAMELIST: + { + extern void extping_namelist(ucharbuf &p); + putint(po, query); + extping_namelist(po); + break; + } + case EXTPING_NOP: + default: + putint(po, EXTPING_NOP); + break; + } + } } else // ext pong - additional server infos {