diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index bb4710e26..703db92da 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -493,7 +493,7 @@ void CL_ConnectToDarkPlaces(char *challenge, netadr_t *adr) connectinfo.time = realtime; // for retransmit requests - Q_snprintfz(data, sizeof(data), "%c%c%c%cconnect\\protocol\\darkplaces 3\\protocols\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP2 NEHAHRABJP NEHAHRABJP3 QUAKE\\challenge\\%s\\name\\%s", 255, 255, 255, 255, challenge, name.string); + Q_snprintfz(data, sizeof(data), "%c%c%c%cconnect\\protocol\\darkplaces "STRINGIFY(NQ_NETCHAN_VERSION)"\\protocols\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP2 NEHAHRABJP NEHAHRABJP3 QUAKE\\challenge\\%s\\name\\%s", 255, 255, 255, 255, challenge, name.string); NET_SendPacket (cls.sockets, strlen(data), data, adr); @@ -1160,7 +1160,7 @@ void CL_CheckForResend (void) else if (1) { net_from = connectinfo.adr[connectinfo.nextadr]; - Q_snprintfz(net_message.data, net_message.maxsize, "xxxxconnect\\protocol\\darkplaces 3\\protocols\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP2 NEHAHRABJP NEHAHRABJP3 QUAKE\\challenge\\0x%x\\name\\%s", SV_NewChallenge(), name.string); + Q_snprintfz(net_message.data, net_message.maxsize, "xxxxconnect\\protocol\\darkplaces "STRINGIFY(NQ_NETCHAN_VERSION)"\\protocols\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP2 NEHAHRABJP NEHAHRABJP3 QUAKE\\challenge\\0x%x\\name\\%s", SV_NewChallenge(), name.string); Cmd_TokenizeString (net_message.data+4, false, false); SVC_DirectConnect(0); } diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index 6bf00ed94..67d6f2023 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -22,17 +22,18 @@ enum masterprotocol_e #define SS_UNKNOWN 0 #define SS_QUAKEWORLD 1 #define SS_NETQUAKE 2 -#define SS_DARKPLACES 3 -#define SS_QUAKE2 4 -#define SS_QUAKE3 5 +#define SS_QUAKE2 3 +#define SS_QUAKE3 4 +#define SS_QEPROT 5 //needs dtls and a different ccreq version //#define SS_UNUSED 6 //#define SS_UNUSED 7 #define SS_LOCAL (1<<3u) //local servers are ones we detected without being listed on a master server (masters will report public ips, so these may appear as dupes if they're also public) -#define SS_FTESERVER (1<<4u) //hehehe... +#define SS_FTESERVER (1<<4u) //just highlighting differences, to give some impression of superiority. #define SS_FAVORITE (1<<5u) //filter all others. #define SS_KEEPINFO (1<<6u) -#define SS_PROXY (1<<7u) +#define SS_GETINFO (1<<7u) //explicitly query via getinfo +#define SS_PROXY (1<<8u) //qizmo/qwfwd/qtv/eztv #define PING_DEAD 0xffff //default ping value to denote servers that are not responding. #define PING_UNKNOWN 0xfffe //these servers are considered up, but we can't query them directly so can't determine the final ping from here. diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 338ae4e18..0c3395930 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -145,7 +145,7 @@ static void SL_TitlesDraw (int x, int y, menucustom_t *ths, emenu_t *menu) else clr = 'B'; x = ths->common.width; - if (mx > x || mousecursor_y < y || mousecursor_y >= y+8) + if ((mx > x || mousecursor_y < y || mousecursor_y >= y+8) && !serverpreview) filldraw = true; if (sb_showtimelimit.value) {SL_DrawColumnTitle(&x, y, 3*8, mx, "tl", (sf==SLKEY_TIMELIMIT), clr, &filldraw);} if (sb_showfraglimit.value) {SL_DrawColumnTitle(&x, y, 3*8, mx, "fl", (sf==SLKEY_FRAGLIMIT), clr, &filldraw);} @@ -268,8 +268,9 @@ static servertypes_t flagstoservertype(int flags) switch(flags & SS_PROTOCOLMASK) { + case SS_QEPROT: + return ST_NETQUAKE; case SS_NETQUAKE: - case SS_DARKPLACES: return ST_NETQUAKE; case SS_QUAKE2: return ST_QUAKE2; @@ -308,7 +309,7 @@ static void SL_ServerDraw (int x, int y, menucustom_t *ths, emenu_t *menu) serverhighlight[(int)stype][2], 1.0)); } - else if (thisone == info->scrollpos + (int)(mousecursor_y-info->servers_top)/8 && mousecursor_x < x) + else if (thisone == info->scrollpos + (int)(mousecursor_y-info->servers_top)/8 && mousecursor_x < x && !serverpreview) R2D_ImageColours(SRGBA((sin(realtime*4.4)*0.25)+0.5, (sin(realtime*4.4)*0.25)+0.5, 0.08, sb_alpha.value)); else if (selectedserver.inuse && NET_CompareAdr(&si->adr, &selectedserver.adr) && !strcmp(si->brokerid, selectedserver.brokerid)) R2D_ImageColours(SRGBA(((sin(realtime*4.4)*0.25)+0.5) * 0.5, ((sin(realtime*4.4)*0.25)+0.5)*0.5, 0.08*0.5, sb_alpha.value)); @@ -852,9 +853,11 @@ dojoin: } //which connect command are we using? - if ((server->special & SS_PROTOCOLMASK) == SS_NETQUAKE) - Cbuf_AddText("nqconnect ", RESTRICT_LOCAL); +#ifdef NQPROT + if ((server->special & SS_PROTOCOLMASK) == SS_QEPROT) + Cbuf_AddText("connectqe ", RESTRICT_LOCAL); else +#endif Cbuf_AddText("connect ", RESTRICT_LOCAL); //output the server's address @@ -1108,7 +1111,6 @@ static void CalcFilters(emenu_t *menu) else { if (info->filter[SLFILTER_HIDENETQUAKE]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_NETQUAKE, SLIST_TEST_NOTEQUAL); - if (info->filter[SLFILTER_HIDENETQUAKE]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_DARKPLACES, SLIST_TEST_NOTEQUAL); if (info->filter[SLFILTER_HIDEQUAKEWORLD]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_QUAKEWORLD, SLIST_TEST_NOTEQUAL); } if (info->filter[SLFILTER_HIDEPROXIES]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_PROXY, SLIST_TEST_NOTCONTAIN); @@ -1351,9 +1353,11 @@ static void M_QuickConnect_PreDraw(emenu_t *menu) { Con_Printf("Quick connect found %s (gamedir %s, players %i/%i/%i, ping %ims)\n", best->name, best->gamedir, best->numhumans, best->players, best->maxplayers, best->ping); - if ((best->special & SS_PROTOCOLMASK) == SS_NETQUAKE) - Cbuf_AddText(va("nqconnect %s\n", Master_ServerToString(adr, sizeof(adr), best)), RESTRICT_LOCAL); +#ifdef NQPROT + if ((best->special & SS_PROTOCOLMASK) == SS_QEPROT) + Cbuf_AddText(va("connectqe %s\n", Master_ServerToString(adr, sizeof(adr), best)), RESTRICT_LOCAL); else +#endif Cbuf_AddText(va("join %s\n", Master_ServerToString(adr, sizeof(adr), best)), RESTRICT_LOCAL); M_ToggleMenu_f(); diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 5ac19c126..25b1e3d8e 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -1039,8 +1039,6 @@ char *Master_ServerToString (char *s, int len, serverinfo_t *a) static int Master_BaseGame(serverinfo_t *a) { int prot = a->special&SS_PROTOCOLMASK; - if (prot == SS_DARKPLACES && (a->special&SS_FTESERVER)) - prot = SS_QUAKEWORLD; return prot; } @@ -2256,32 +2254,32 @@ void Master_CheckPollSockets(void) continue; } #endif -#ifdef Q3CLIENT +//#ifdef Q3CLIENT if (!strcmp(s, "statusResponse")) { CL_ReadServerInfo(MSG_ReadString(), MP_QUAKE3, false); continue; } -#endif +//#endif #ifdef HAVE_IPV6 if (!strncmp(s, "getserversResponse6", 19) && (s[19] == '\\' || s[19] == '/')) //parse a bit more... { net_message.currentbit = (c+19-1)<<3; - CL_MasterListParse(NA_IPV6, SS_DARKPLACES, true); + CL_MasterListParse(NA_IPV6, SS_GETINFO, true); continue; } #endif if (!strncmp(s, "getserversExtResponse", 21) && (s[21] == '\\' || s[21] == '/')) //parse a bit more... { net_message.currentbit = (c+21-1)<<3; - CL_MasterListParse(NA_IP, SS_DARKPLACES, true); + CL_MasterListParse(NA_IP, SS_GETINFO, true); continue; } if (!strncmp(s, "getserversResponse", 18) && (s[18] == '\\' || s[18] == '/')) //parse a bit more... { net_message.currentbit = (c+18-1)<<3; - CL_MasterListParse(NA_IP, SS_DARKPLACES, true); + CL_MasterListParse(NA_IP, SS_GETINFO, true); continue; } if (!strcmp(s, "infoResponse")) //parse a bit more... @@ -2495,7 +2493,7 @@ void SListOptionChanged(serverinfo_t *newserver) #if defined(NQPROT) selectedserver.lastplayer = 0; *selectedserver.lastrule = 0; - if ((newserver->special&SS_PROTOCOLMASK) == SS_NETQUAKE) + if ((newserver->special&(SS_PROTOCOLMASK|SS_GETINFO)) == SS_NETQUAKE) { //start spamming the server to get all of its details. silly protocols. SZ_Clear(&net_message); net_message.packing = SZ_RAWBYTES; @@ -2646,7 +2644,7 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl) if (protocoltype == MP_QUAKEWORLD) info->special |= SS_QUAKEWORLD; else if (protocoltype == MP_DPMASTER) - info->special |= SS_DARKPLACES; + info->special |= SS_GETINFO; #if defined(Q2CLIENT) || defined(Q2SERVER) else if (protocoltype == MP_QUAKE2) info->special |= SS_QUAKE2; @@ -2925,16 +2923,17 @@ void Master_QueryServer(serverinfo_t *server) return; //don't even try. we have no direct route. server->refreshtime = Sys_DoubleTime(); - switch(server->special & SS_PROTOCOLMASK) + if (server->special & SS_GETINFO) { - case SS_QUAKE3: - Q_snprintfz(data, sizeof(data), "%c%c%c%cgetstatus", 255, 255, 255, 255); - break; - case SS_DARKPLACES: if (server->moreinfo) Q_snprintfz(data, sizeof(data), "%c%c%c%cgetstatus", 255, 255, 255, 255); else Q_snprintfz(data, sizeof(data), "%c%c%c%cgetinfo", 255, 255, 255, 255); + } + else switch(server->special & SS_PROTOCOLMASK) + { + case SS_QUAKE3: + Q_snprintfz(data, sizeof(data), "%c%c%c%cgetstatus", 255, 255, 255, 255); break; #ifdef NQPROT case SS_NETQUAKE: @@ -3028,14 +3027,13 @@ qboolean CL_QueryServers(void) while(server) { qboolean enabled; - switch(server->special & SS_PROTOCOLMASK) + switch(Master_BaseGame(server)) { case SS_UNKNOWN: enabled = true; break; case SS_QUAKE3: enabled = sb_enablequake3; break; case SS_QUAKE2: enabled = sb_enablequake2; break; case SS_NETQUAKE: enabled = sb_enablenetquake; break; case SS_QUAKEWORLD: enabled = sb_enablequakeworld; break; - case SS_DARKPLACES: enabled = sb_enabledarkplaces; break; default: enabled = false; break; } if (enabled) @@ -3057,14 +3055,13 @@ qboolean CL_QueryServers(void) while (server) { qboolean enabled; - switch(server->special & SS_PROTOCOLMASK) + switch(Master_BaseGame(server)) { case SS_UNKNOWN: enabled = true; break; case SS_QUAKE3: enabled = sb_enablequake3; break; case SS_QUAKE2: enabled = sb_enablequake2; break; case SS_NETQUAKE: enabled = sb_enablenetquake; break; case SS_QUAKEWORLD: enabled = sb_enablequakeworld; break; - case SS_DARKPLACES: enabled = sb_enabledarkplaces; break; default: enabled = false; break; } if (enabled) @@ -3241,38 +3238,54 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea if (!*name) name = Info_ValueForKey(msg, "sv_hostname"); Q_strncpyz(info->name, name, sizeof(info->name)); - info->special = info->special & (SS_FAVORITE | SS_KEEPINFO | SS_LOCAL); //favorite+local is never cleared + info->special = info->special & (SS_FAVORITE | SS_KEEPINFO | SS_LOCAL | SS_GETINFO); //favorite+local is never cleared if (!strcmp(DISTRIBUTION, Info_ValueForKey(msg, "*distrib"))) //outdated info->special |= SS_FTESERVER; else if (!strncmp(DISTRIBUTION, Info_ValueForKey(msg, "*version"), strlen(DISTRIBUTION))) info->special |= SS_FTESERVER; - info->protocol = atoi(Info_ValueForKey(msg, "protocol")); - info->special &= ~SS_PROTOCOLMASK; + info->protocol = strtoul(Info_ValueForKey(msg, "protocol"), &token, 0); if (info->protocol) { switch(info->protocol) { - case PROTOCOL_VERSION_QW: info->special = SS_QUAKEWORLD; break; + case PROTOCOL_VERSION_QW: info->special |= SS_QUAKEWORLD; break; #ifdef NQPROT - case PROTOCOL_VERSION_NQ: info->special = SS_NETQUAKE; break; - case PROTOCOL_VERSION_H2: info->special = SS_NETQUAKE; break; //erk - case PROTOCOL_VERSION_NEHD: info->special = SS_NETQUAKE; break; - case PROTOCOL_VERSION_FITZ: info->special = SS_NETQUAKE; break; - case PROTOCOL_VERSION_RMQ: info->special = SS_NETQUAKE; break; - case PROTOCOL_VERSION_DP5: info->special = SS_DARKPLACES; break; //dp actually says 3... but hey, that's dp being WEIRD. - case PROTOCOL_VERSION_DP6: info->special = SS_DARKPLACES; break; - case PROTOCOL_VERSION_DP7: info->special = SS_DARKPLACES; break; + case PROTOCOL_VERSION_NQ: info->special |= SS_NETQUAKE; break; + case PROTOCOL_VERSION_H2: info->special |= SS_NETQUAKE; break; //erk + case PROTOCOL_VERSION_NEHD: info->special |= SS_NETQUAKE; break; + case PROTOCOL_VERSION_FITZ: info->special |= SS_NETQUAKE; break; + case PROTOCOL_VERSION_RMQ: info->special |= SS_NETQUAKE; break; + case PROTOCOL_VERSION_DP5: info->special |= SS_NETQUAKE; break; //dp actually says 3... but hey, that's dp being WEIRD. + case PROTOCOL_VERSION_DP6: info->special |= SS_NETQUAKE; break; + case PROTOCOL_VERSION_DP7: info->special |= SS_NETQUAKE; break; + case NQ_NETCHAN_VERSION_QEX:info->special |= SS_QEPROT; break; + case NQ_NETCHAN_VERSION: #endif default: - if (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN) - info->special |= SS_QUAKE2; //q2 has a range! - else if (info->protocol > 60) - info->special |= SS_QUAKE3; - else if (!strcmp(Info_ValueForKey(msg, "gamename"), "DarkPlaces-Quake")) - info->special |= SS_DARKPLACES; - else - info->special |= SS_DARKPLACES|SS_FTESERVER; //so its listed under qw-servers (but queried using dpmaster getinfo stuff). + while (*token) + { + if (*token == 'w') + info->special |= SS_QUAKEWORLD; + else if (*token == 'n' || *token == 'd') + info->special |= SS_NETQUAKE; + else if (*token == 'x') + info->special |= SS_QEPROT; + else + continue; + break; + } + if ((info->special&SS_PROTOCOLMASK) == SS_UNKNOWN) + { //guesses... + if (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN) + info->special |= SS_QUAKE2; //q2 has a range! + else if (info->protocol > 60) + info->special |= SS_QUAKE3; + else if (!strcmp(Info_ValueForKey(msg, "gamename"), "DarkPlaces-Quake") || *Info_ValueForKey(msg, "nqprotocol")) + info->special |= SS_NETQUAKE; + else + info->special |= SS_QUAKEWORLD; + } break; } } @@ -3342,7 +3355,7 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea msg = msg+strlen(msg)+1; //clear player info. unless its an NQ server, which have some really annoying protocol to find out the players. - if ((info->special & SS_PROTOCOLMASK) == SS_NETQUAKE) + if ((info->special & (SS_PROTOCOLMASK|SS_GETINFO)) == SS_NETQUAKE) { if (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO))) info->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t)); @@ -3516,7 +3529,7 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea msg++; } } - if ((info->special & SS_PROTOCOLMASK) == SS_DARKPLACES && !info->numbots) + if (!info->numbots) { info->numbots = atoi(Info_ValueForKey(details.info, "bots")); if (info->numbots > info->players) @@ -3624,7 +3637,7 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad) } if ((old = Master_InfoForServer(&info->adr, NULL))) //remove if the server already exists. { - if ((old->special & (SS_PROTOCOLMASK)) != (type & (SS_PROTOCOLMASK))) + if ((old->special & (SS_PROTOCOLMASK|SS_GETINFO)) != (type & (SS_PROTOCOLMASK|SS_GETINFO))) old->special = type | (old->special & (SS_FAVORITE|SS_LOCAL)); old->sends = 1; //reset. old->status |= SRVSTATUS_GLOBAL; diff --git a/engine/common/common.c b/engine/common/common.c index 58b4ed858..59eeb13bd 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -83,7 +83,7 @@ static cvar_t pr_engine = CVARFD("pr_engine",DISTRIBUTION" "STRINGIFY(SVNREVISI #endif cvar_t fs_gamename = CVARAD("com_fullgamename", NULL, "fs_gamename", "The filesystem is trying to run this game"); cvar_t com_protocolname = CVARAD("com_protocolname", NULL, "com_gamename", "The protocol game name used for dpmaster queries. For compatibility with DP, you can set this to 'DarkPlaces-Quake' in order to be listed in DP's master server, and to list DP servers."); -cvar_t com_protocolversion = CVARAD("com_protocolversion", "3", NULL, "The protocol version used for dpmaster queries."); //3 by default, for compat with DP/NQ, even if our QW protocol uses different versions entirely. really it only matters for master servers. +cvar_t com_protocolversion = CVARAD("com_protocolversion", "3", NULL, "The protocol version used for dpmaster queries."); //3 as strong default for compat with DP which uses its netchan rather than protocol version here, even if our QW protocol uses different versions entirely. really it only matters for master servers. cvar_t com_parseutf8 = CVARD("com_parseutf8", "1", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed. cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active."); cvar_t com_gamedirnativecode = CVARFD("com_gamedirnativecode", "0", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger delayed eremote exploits in any engine (including "DISTRIBUTION") which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 1, as well as ensure that you don't run unsafe clients."); diff --git a/engine/common/net.h b/engine/common/net.h index f1c20fb55..371471b26 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -297,7 +297,7 @@ void Net_Master_Init(void); void Netchan_Init (void); int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate); -void Netchan_OutOfBand (netsrc_t sock, netadr_t *adr, int length, qbyte *data); +void Netchan_OutOfBand (netsrc_t sock, netadr_t *adr, int length, const qbyte *data); void VARGS Netchan_OutOfBandPrint (netsrc_t sock, netadr_t *adr, char *format, ...) LIKEPRINTF(3); void VARGS Netchan_OutOfBandTPrintf (netsrc_t sock, netadr_t *adr, int language, translation_t text, ...); qboolean Netchan_Process (netchan_t *chan); diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 6d9a6911c..ee082f841 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -300,7 +300,7 @@ Netchan_OutOfBand Sends an out-of-band datagram ================ */ -void Netchan_OutOfBand (netsrc_t sock, netadr_t *adr, int length, qbyte *data) +void Netchan_OutOfBand (netsrc_t sock, netadr_t *adr, int length, const qbyte *data) { sizebuf_t send; qbyte send_buf[MAX_QWMSGLEN + PACKET_HEADER]; diff --git a/engine/common/net_ice.c b/engine/common/net_ice.c index 7b2ed79ef..d14e8f72c 100644 --- a/engine/common/net_ice.c +++ b/engine/common/net_ice.c @@ -4349,7 +4349,7 @@ static void FTENET_ICE_Heartbeat(ftenet_ice_connection_t *b) } *info = 0; - Info_SetValueForKey(info, "protocol", com_protocolversion.string, sizeof(info)); + Info_SetValueForKey(info, "protocol", SV_GetProtocolVersionString(), sizeof(info)); Info_SetValueForKey(info, "maxclients", maxclients.string, sizeof(info)); Info_SetValueForKey(info, "clients", va("%i", numclients), sizeof(info)); Info_SetValueForKey(info, "hostname", hostname.string, sizeof(info)); diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 4e35d9384..4c1ffaaaa 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -7185,7 +7185,7 @@ static void FTENET_WebRTC_Heartbeat(ftenet_websocket_connection_t *b) info[1] = info[2] = 0xff; //to the broker rather than any actual client info[3] = 0; - Info_SetValueForKey(info+3, "protocol", com_protocolversion.string, sizeof(info)-3); + Info_SetValueForKey(info+3, "protocol", SV_GetProtocolVersionString(), sizeof(info)-3); Info_SetValueForKey(info+3, "maxclients", maxclients.string, sizeof(info)-3); Info_SetValueForKey(info+3, "clients", va("%i", numclients), sizeof(info)-3); Info_SetValueForKey(info+3, "hostname", hostname.string, sizeof(info)-3); diff --git a/engine/server/server.h b/engine/server/server.h index a381b1082..96551ef00 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1157,6 +1157,7 @@ int SV_CalcPing (client_t *cl, qboolean forcecalc); void SV_FullClientUpdate (client_t *client, client_t *to); char *SV_PlayerPublicAddress(client_t *cl); +const char *SV_GetProtocolVersionString(void); //decorate the protocol version field of server queries with extra features... qboolean SVC_GetChallenge (qboolean respond_dp); int SV_NewChallenge (void); void SVC_DirectConnect(int expectedreliablesequence); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index d92672e32..17b42a660 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1227,6 +1227,36 @@ static void SVC_Status (void) } #if 1//def NQPROT +const char *SV_GetProtocolVersionString(void) +{ + char *ret = va("%i", com_protocolversion.ival); //for compat with DP, this is basically locked at 3. our pexts allow this to be mostly graceful. + + switch(svs.gametype) + { + case GT_PROGS: + case GT_Q1QVM: + if (sv_listen_qw.ival) + Q_strncatz(ret, "w", 64); +#ifdef NQPROT + if (progstype == PROG_H2) + break; //don't advertise nq protocols when they're blocked. + if (sv_listen_nq.ival) + { + Q_strncatz(ret, "n", 64); +#ifdef HAVE_DTLS + if (*dtls_psk_user.string) + Q_strncatz(ret, "x", 64); +#endif + } + if (sv_listen_dp.ival) + Q_strncatz(ret, "d", 64); +#endif + break; + default: + break; //these do their own thing, with their own protocols. don't be weird. + } + return ret; +} static void SVC_GetInfo (const char *challenge, int fullstatus) { //dpmaster support @@ -1289,7 +1319,7 @@ static void SVC_GetInfo (const char *challenge, int fullstatus) *resp = 0; Info_SetValueForKey(resp, "challenge", challenge, sizeof(response) - (resp-response)); //the challenge can be important for the master protocol to prevent poisoning Info_SetValueForKey(resp, "gamename", protocolname, sizeof(response) - (resp-response));//distinguishes it from other types of games - Info_SetValueForKey(resp, "protocol", com_protocolversion.string, sizeof(response) - (resp-response)); //should be an int. + Info_SetValueForKey(resp, "protocol", SV_GetProtocolVersionString(), sizeof(response) - (resp-response)); Info_SetValueForKey(resp, "modname", FS_GetGamedir(true), sizeof(response) - (resp-response)); Info_SetValueForKey(resp, "clients", va("%d", numclients), sizeof(response) - (resp-response)); Info_SetValueForKey(resp, "sv_maxclients", maxclients.string, sizeof(response) - (resp-response)); @@ -1549,6 +1579,33 @@ qboolean SVC_GetChallenge (qboolean respond_dp) const qboolean respond_qwoverq3 = false; #endif + //ioq3clchallenge = atoi(Cmd_Argv(1)); + const char *protocols = Cmd_Argv(2); + if (*protocols) + { + const char *pname; + char tprot[64], oprot[64]; + while ((protocols=COM_ParseOut(protocols, tprot,sizeof(tprot)))) + { + pname = com_protocolname.string; + while ((pname=COM_ParseOut(pname, oprot,sizeof(oprot)))) + { + if (!strcmp(tprot, oprot)) + break; + } + if (pname) + break; + } + + if (!protocols) + { + COM_ParseOut(com_protocolname.string, oprot,sizeof(oprot)); + pname = va("print\nGame mismatch: This is a %s server\n", oprot); + Netchan_OutOfBand(NS_SERVER, &net_from, strlen(pname), pname); + return false; + } + } + if (sv_listen_qw.value && !sv_listen_dp.value) { respond_std = true; @@ -3318,7 +3375,7 @@ void SVC_DirectConnect(int expectedreliablesequence) } Q_strncpyz (info.userinfo, net_message.data + 11, sizeof(info.userinfo)-1); - if (strcmp(Info_ValueForKey(info.userinfo, "protocol"), "darkplaces 3")) + if (strcmp(Info_ValueForKey(info.userinfo, "protocol"), "darkplaces "STRINGIFY(NQ_NETCHAN_VERSION))) { SV_RejectMessage (SCP_BAD, "Server is %s.\n", version_string()); Con_TPrintf ("* rejected connect from incompatible client\n"); @@ -4165,8 +4222,9 @@ qboolean SV_ConnectionlessPacket (void) }*/ else if (!strcmp(c,"getchallenge")) { - //qw+q2 always sends "\xff\xff\xff\xffgetchallenge\n" - //dp+q3 always sends "\xff\xff\xff\xffgetchallenge" + //qw+q2 sends "\xff\xff\xff\xffgetchallenge\n" + //dp+q3 sends "\xff\xff\xff\xffgetchallenge" + //ioq3 sends "\xff\xff\xff\xffgetchallenge <$com_gamename>" //its a subtle difference, but means we can avoid wasteful spam for real qw clients. SVC_GetChallenge ((net_message.cursize==16)?true:false); } @@ -4407,13 +4465,15 @@ qboolean SVNQ_ConnectionlessPacket(void) if (SV_ChallengeRecent()) return true; - else if (!strncmp(MSG_ReadString(), "getchallenge", 12) && (sv_listen_qw.ival || sv_listen_dp.ival)) + + Cmd_TokenizeString (MSG_ReadString(), false, false); + if (!strcmp(Cmd_Argv(0), "getchallenge") && (sv_listen_qw.ival || sv_listen_dp.ival)) { /*dual-stack client, supporting either DP or QW protocols*/ SVC_GetChallenge (false); } else - { + { //legacy pure-nq (though often DP). if (progstype == PROG_H2) { SZ_Clear(&sb);