From ecbfdb82f47abe50b90183679af5843ac772f37a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 20 Mar 2025 05:46:49 +0300 Subject: [PATCH] engine: server: add requested server command status. Only expose client input devices for now, as everything else might be considered sensitive information --- engine/common/common.h | 2 +- engine/server/sv_client.c | 126 ++++++++++++++++++++++++++++---------- engine/server/sv_cmds.c | 6 ++ 3 files changed, 102 insertions(+), 32 deletions(-) diff --git a/engine/common/common.h b/engine/common/common.h index 84ef04e7..809b8357 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -790,7 +790,7 @@ void Con_NPrintf( int idx, const char *fmt, ... ) FORMAT_CHECK( 2 ); void Con_NXPrintf( con_nprint_t *info, const char *fmt, ... ) FORMAT_CHECK( 2 ); void UI_NPrintf( int idx, const char *fmt, ... ) FORMAT_CHECK( 2 ); void UI_NXPrintf( con_nprint_t *info, const char *fmt, ... ) FORMAT_CHECK( 2 ); -const char *Info_ValueForKey( const char *s, const char *key ); +const char *Info_ValueForKey( const char *s, const char *key ) RETURNS_NONNULL NONNULL; void Info_RemovePrefixedKeys( char *start, char prefix ); qboolean Info_RemoveKey( char *s, const char *key ); qboolean Info_SetValueForKey( char *s, const char *key, const char *value, int maxsize ); diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index cd7f3114..88e3e8a5 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1882,20 +1882,6 @@ static void SV_UserinfoChanged( sv_client_t *cl ) ent->v.netname = MAKE_STRING( cl->name ); } -/* -================== -SV_UpdateUserinfo_f -================== -*/ -static qboolean SV_UpdateUserinfo_f( sv_client_t *cl ) -{ - Q_strncpy( cl->userinfo, Cmd_Argv( 1 ), sizeof( cl->userinfo )); - - if( cl->state >= cs_connected ) - SetBits( cl->flags, FCL_RESEND_USERINFO ); // needs for update client info - return true; -} - /* ================== SV_SetInfo_f @@ -2188,6 +2174,83 @@ static qboolean SV_SendBuildInfo_f( sv_client_t *cl ) return true; } +/* +================== +SV_ClientStatus_f +================== +*/ +static qboolean SV_ClientStatus_f( sv_client_t *cl ) +{ + netadr_t ip4, ip6; + vec3_t origin = { 0 }; + int clients, bots, i; + + NET_GetLocalAddress( &ip4, &ip6 ); + if( cl->edict ) + VectorCopy( cl->edict->v.origin, origin ); + SV_GetPlayerCount( &clients, &bots ); + + SV_ClientPrintf( cl, + "hostname: %s\n" + "version: %i/%s %d\n", + hostname.string, + PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( )); + + if( ip4.type == NA_IP ) + SV_ClientPrintf( cl, "tcp/ip: %s\n", NET_AdrToString( ip4 )); + if( ip6.type == NA_IP6 ) + SV_ClientPrintf( cl, "tcp/ipv6: %s\n", NET_AdrToString( ip4 )); + + SV_ClientPrintf( cl, + "map:\t%s at %d x, %d y, %d z\n" + "players: %i active (%i max)\n" + "# score ping dev playtime name\n", + sv.name, (int)origin[0], (int)origin[1], (int)origin[2], + clients, svs.maxclients ); + + for( i = 0; i < svs.maxclients; i++ ) + { + const sv_client_t *pcl = &svs.clients[i]; + int j = 0; + int input_devices; + const char *s; + char devices[8]; + + if( pcl->state != cs_spawned ) + continue; + + if( FBitSet( pcl->flags, FCL_FAKECLIENT )) + s = "Bot "; + else + s = va( "%i", SV_CalcPing( pcl )); + + input_devices = Q_atoi( Info_ValueForKey( pcl->useragent, "d" )); + + if( FBitSet( input_devices, INPUT_DEVICE_MOUSE )) + devices[j++] = 'm'; + + if( FBitSet( input_devices, INPUT_DEVICE_TOUCH )) + devices[j++] = 't'; + + if( FBitSet( input_devices, INPUT_DEVICE_JOYSTICK )) + devices[j++] = 'j'; + + if( FBitSet( input_devices, INPUT_DEVICE_VR )) + devices[j++] = 'v'; + + if( j == 0 ) + Q_strncpy( devices, "n/a", sizeof( devices )); + else + devices[j++] = 0; + + SV_ClientPrintf( cl, + "%2i %5i %4s %4s %g %s\n", + i, (int)pcl->edict->v.frags, s, devices, host.realtime - pcl->netchan.connect_time, pcl->name ); + } + + return true; +} + /* ================== SV_GetCrossEnt @@ -2969,32 +3032,33 @@ static qboolean SV_EntGetVars_f( sv_client_t *cl ) return true; } +// keep it sorted static const ucmd_t ucmds[] = { -{ "new", SV_New_f }, -{ "god", SV_Godmode_f }, -{ "kill", SV_Kill_f }, -{ "begin", SV_Begin_f }, -{ "spawn", SV_Spawn_f }, -{ "pause", SV_Pause_f }, -{ "noclip", SV_Noclip_f }, -{ "setinfo", SV_SetInfo_f }, -{ "sendres", SV_SendRes_f }, -{ "notarget", SV_Notarget_f }, -{ "info", SV_ShowServerinfo_f }, -{ "dlfile", SV_DownloadFile_f }, -{ "disconnect", SV_Disconnect_f }, -{ "userinfo", SV_UpdateUserinfo_f }, { "_sv_build_info", SV_SendBuildInfo_f }, +{ "begin", SV_Begin_f }, +{ "disconnect", SV_Disconnect_f }, +{ "dlfile", SV_DownloadFile_f }, +{ "god", SV_Godmode_f }, +{ "info", SV_ShowServerinfo_f }, +{ "kill", SV_Kill_f }, +{ "new", SV_New_f }, +{ "noclip", SV_Noclip_f }, +{ "notarget", SV_Notarget_f }, +{ "pause", SV_Pause_f }, +{ "sendres", SV_SendRes_f }, +{ "setinfo", SV_SetInfo_f }, +{ "spawn", SV_Spawn_f }, +{ "status", SV_ClientStatus_f }, }; static const ucmd_t enttoolscmds[] = { -{ "ent_list", SV_EntList_f }, -{ "ent_info", SV_EntInfo_f }, -{ "ent_fire", SV_EntFire_f }, { "ent_create", SV_EntCreate_f }, +{ "ent_fire", SV_EntFire_f }, { "ent_getvars", SV_EntGetVars_f }, +{ "ent_info", SV_EntInfo_f }, +{ "ent_list", SV_EntList_f }, }; /* diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 0b21b0aa..3c0ec032 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -630,6 +630,12 @@ static void SV_Status_f( void ) sv_client_t *cl; int i; + if( !svs.clients && CL_Active( )) + { + Cmd_ForwardToServer(); + return; + } + if( !svs.clients || sv.background ) { Con_Printf( "^3no server running.\n" );