From d9ef1d4608b685843c0487914f1d35f38ff5a218 Mon Sep 17 00:00:00 2001 From: jeefo Date: Fri, 9 Jun 2023 23:29:31 +0300 Subject: [PATCH] server: implement correct answers to TSourceEngineQuery server queries * count bots as clients as in goldsrc * handle source-style packets after xash's built-in packets to not interfere with them --- engine/server/server.h | 5 + engine/server/sv_client.c | 60 +---------- engine/server/sv_query.c | 203 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+), 59 deletions(-) create mode 100644 engine/server/sv_query.c diff --git a/engine/server/server.h b/engine/server/server.h index a74c117c..fba9d9c3 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -701,4 +701,9 @@ void SV_SetLightStyle( int style, const char* s, float f ); const char *SV_GetLightStyle( int style ); int SV_LightForEntity( edict_t *pEdict ); +// +// sv_query.c +// +qboolean SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from ); + #endif//SERVER_H diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 85dc434e..765ca995 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -3062,64 +3062,6 @@ void SV_ExecuteClientCommand( sv_client_t *cl, const char *s ) } } -/* -================== -SV_TSourceEngineQuery -================== -*/ -void SV_TSourceEngineQuery( netadr_t from ) -{ - // A2S_INFO - char answer[1024] = ""; - int count, bots; - int index; - sizebuf_t buf; - - SV_GetPlayerCount( &count, &bots ); - - MSG_Init( &buf, "TSourceEngineQuery", answer, sizeof( answer )); - - MSG_WriteLong( &buf, -1 ); // Mark as connectionless - MSG_WriteByte( &buf, 'm' ); - MSG_WriteString( &buf, NET_AdrToString( net_local )); - MSG_WriteString( &buf, hostname.string ); - MSG_WriteString( &buf, sv.name ); - MSG_WriteString( &buf, GI->gamefolder ); - MSG_WriteString( &buf, GI->title ); - MSG_WriteByte( &buf, count ); - MSG_WriteByte( &buf, svs.maxclients ); - MSG_WriteByte( &buf, PROTOCOL_VERSION ); - MSG_WriteByte( &buf, Host_IsDedicated() ? 'D' : 'L' ); -#if defined(_WIN32) - MSG_WriteByte( &buf, 'W' ); -#else - MSG_WriteByte( &buf, 'L' ); -#endif - if( Q_stricmp( GI->gamefolder, "valve" )) - { - MSG_WriteByte( &buf, 1 ); // mod - MSG_WriteString( &buf, GI->game_url ); - MSG_WriteString( &buf, GI->update_url ); - MSG_WriteByte( &buf, 0 ); - MSG_WriteLong( &buf, (int)GI->version ); - MSG_WriteLong( &buf, GI->size ); - - if( GI->gamemode == 2 ) - MSG_WriteByte( &buf, 1 ); // multiplayer_only - else MSG_WriteByte( &buf, 0 ); - - if( Q_strstr( GI->game_dll, "hl." )) - MSG_WriteByte( &buf, 0 ); // Half-Life DLL - else MSG_WriteByte( &buf, 1 ); // Own DLL - } - else MSG_WriteByte( &buf, 0 ); // Half-Life - - MSG_WriteByte( &buf, GI->secure ); // unsecure - MSG_WriteByte( &buf, bots ); - - NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from ); -} - /* ================= SV_ConnectionlessPacket @@ -3159,8 +3101,8 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) else if( !Q_strcmp( pcmd, "rcon" )) SV_RemoteCommand( from, msg ); else if( !Q_strcmp( pcmd, "netinfo" )) SV_BuildNetAnswer( from ); else if( !Q_strcmp( pcmd, "s" )) SV_AddToMaster( from, msg ); - else if( !Q_strcmp( pcmd, "T" "Source" )) SV_TSourceEngineQuery( from ); else if( !Q_strcmp( pcmd, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING + else if( SV_SourceQuery_HandleConnnectionlessPacket( pcmd, from ) ) return; else if( !Q_strcmp( pcmd, "c" ) && sv_nat.value && NET_IsMasterAdr( from )) { netadr_t to; diff --git a/engine/server/sv_query.c b/engine/server/sv_query.c new file mode 100644 index 00000000..cb730c65 --- /dev/null +++ b/engine/server/sv_query.c @@ -0,0 +1,203 @@ +/* +sv_query.c - Source-engine like server querying +Copyright (C) 2023 jeefo + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "server.h" + +#define SOURCE_QUERY_INFO 'T' +#define SOURCE_QUERY_DETAILS 'I' + +#define SOURCE_QUERY_RULES 'V' +#define SOURCE_QUERY_RULES_RESPONSE 'E' + +#define SOURCE_QUERY_PLAYERS 'U' +#define SOURCE_QUERY_PLAYERS_RESPONSE 'D' + +#define SOURCE_QUERY_CONNECTIONLESS -1 + +/* +================== +SV_SourceQuery_Details +================== +*/ +void SV_SourceQuery_Details( netadr_t from ) +{ + sizebuf_t buf; + char answer[2048] = ""; + char version[128] = ""; + + int i, bot_count = 0, client_count = 0; + int is_private = 0; + + if ( svs.clients ) + { + for ( i = 0; i < svs.maxclients; i++ ) + { + if ( svs.clients[i].state >= cs_connected ) + { + if ( svs.clients[i].edict->v.flags & FL_FAKECLIENT ) + bot_count++; + + client_count++; + } + } + } + is_private = ( sv_password.string[0] && ( Q_stricmp( sv_password.string, "none" ) || !Q_strlen( sv_password.string ) ) ? 1 : 0 ); + + MSG_Init( &buf, "TSourceEngineQuery", answer, sizeof( answer )); + + MSG_WriteLong( &buf, SOURCE_QUERY_CONNECTIONLESS ); + MSG_WriteByte( &buf, SOURCE_QUERY_DETAILS ); + MSG_WriteByte( &buf, PROTOCOL_VERSION ); + + MSG_WriteString( &buf, hostname.string ); + MSG_WriteString( &buf, sv.name ); + MSG_WriteString( &buf, GI->gamefolder ); + MSG_WriteString( &buf, svgame.dllFuncs.pfnGetGameDescription( ) ); + + MSG_WriteShort( &buf, 0 ); + MSG_WriteByte( &buf, client_count ); + MSG_WriteByte( &buf, svs.maxclients ); + MSG_WriteByte( &buf, bot_count ); + + MSG_WriteByte( &buf, Host_IsDedicated( ) ? 'd' : 'l' ); +#if defined( _WIN32 ) + MSG_WriteByte( &buf, 'w' ); +#elif defined( __APPLE__ ) + MSG_WriteByte( &buf, 'm' ); +#else + MSG_WriteByte( &buf, 'l' ); +#endif + MSG_WriteByte( &buf, is_private ); + MSG_WriteByte( &buf, GI->secure ); + MSG_WriteString( &buf, XASH_VERSION ); + + NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from ); +} + +/* +================== +SV_SourceQuery_Rules +================== +*/ +void SV_SourceQuery_Rules( netadr_t from ) +{ + sizebuf_t buf; + char answer[1024 * 8] = ""; + + cvar_t *cvar; + int cvar_count = 0; + + for ( cvar = Cvar_GetList( ); cvar; cvar = cvar->next ) + { + if ( cvar->flags & FCVAR_SERVER ) + cvar_count++; + } + if ( cvar_count <= 0 ) + return; + + MSG_Init( &buf, "TSourceEngineQueryRules", answer, sizeof( answer ) ); + + MSG_WriteLong( &buf, SOURCE_QUERY_CONNECTIONLESS ); + MSG_WriteByte( &buf, SOURCE_QUERY_RULES_RESPONSE ); + MSG_WriteShort( &buf, cvar_count ); + + for ( cvar = Cvar_GetList( ); cvar; cvar = cvar->next ) + { + if ( !( cvar->flags & FCVAR_SERVER ) ) + continue; + + MSG_WriteString( &buf, cvar->name ); + + if ( cvar->flags & FCVAR_PROTECTED ) + MSG_WriteString( &buf, ( Q_strlen( cvar->string ) > 0 && Q_stricmp( cvar->string, "none" ) ) ? "1" : "0" ); + else + MSG_WriteString( &buf, cvar->string ); + } + NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from ); +} + +/* +================== +SV_SourceQuery_Players +================== +*/ +void SV_SourceQuery_Players( netadr_t from ) +{ + sizebuf_t buf; + char answer[1024 * 8] = ""; + + int i, client_count = 0; + + if ( svs.clients ) + { + for ( i = 0; i < svs.maxclients; i++ ) + { + if ( svs.clients[i].state >= cs_connected ) + client_count++; + } + } + if ( client_count <= 0 ) + return; + + MSG_Init( &buf, "TSourceEngineQueryPlayers", answer, sizeof( answer ) ); + + MSG_WriteLong( &buf, SOURCE_QUERY_CONNECTIONLESS ); + MSG_WriteByte( &buf, SOURCE_QUERY_PLAYERS_RESPONSE ); + MSG_WriteByte( &buf, client_count ); + + for ( i = 0; i < svs.maxclients; i++ ) + { + sv_client_t *cl = &svs.clients[i]; + + if ( cl->state < cs_connected ) + continue; + + MSG_WriteByte( &buf, i ); + MSG_WriteString( &buf, cl->name ); + MSG_WriteLong( &buf, cl->edict->v.frags ); + MSG_WriteFloat( &buf, ( cl->edict->v.flags & FL_FAKECLIENT ) ? -1.0 : ( host.realtime - cl->connecttime ) ); + } + NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from ); +} + +/* +================== +SV_SourceQuery_HandleConnnectionlessPacket +================== +*/ +qboolean SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from ) +{ + int request = c[0]; + + switch ( request ) + { + case SOURCE_QUERY_INFO: + SV_SourceQuery_Details( from ); + return true; + + case SOURCE_QUERY_RULES: + SV_SourceQuery_Rules( from ); + return true; + + case SOURCE_QUERY_PLAYERS: + SV_SourceQuery_Players( from ); + return true; + + default: + return false; + } + return false; +} \ No newline at end of file