From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- engine/host_cmd.cpp | 2411 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2411 insertions(+) create mode 100644 engine/host_cmd.cpp (limited to 'engine/host_cmd.cpp') diff --git a/engine/host_cmd.cpp b/engine/host_cmd.cpp new file mode 100644 index 0000000..c22ab07 --- /dev/null +++ b/engine/host_cmd.cpp @@ -0,0 +1,2411 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "tier0/vprof.h" +#include "server.h" +#include "host_cmd.h" +#include "keys.h" +#include "screen.h" +#include "vengineserver_impl.h" +#include "host_saverestore.h" +#include "sv_filter.h" +#include "gl_matsysiface.h" +#include "pr_edict.h" +#include "world.h" +#include "checksum_engine.h" +#include "const.h" +#include "sv_main.h" +#include "host.h" +#include "demo.h" +#include "cdll_int.h" +#include "networkstringtableserver.h" +#include "networkstringtableclient.h" +#include "host_state.h" +#include "string_t.h" +#include "tier0/dbg.h" +#include "testscriptmgr.h" +#include "r_local.h" +#include "PlayerState.h" +#include "enginesingleuserfilter.h" +#include "profile.h" +#include "proto_version.h" +#include "protocol.h" +#include "cl_main.h" +#include "sv_steamauth.h" +#include "zone.h" +#include "datacache/idatacache.h" +#include "sys_dll.h" +#include "cmd.h" +#include "tier0/icommandline.h" +#include "filesystem.h" +#include "filesystem_engine.h" +#include "icliententitylist.h" +#include "icliententity.h" +#include "GameEventManager.h" +#include "hltvserver.h" +#if defined( REPLAY_ENABLED ) +#include "replay_internal.h" +#include "replayserver.h" +#endif +#include "cdll_engine_int.h" +#include "cl_steamauth.h" +#ifndef SWDS +#include "vgui_baseui_interface.h" +#endif +#include "sound.h" +#include "voice.h" +#include "sv_rcon.h" +#if defined( _X360 ) +#include "xbox/xbox_console.h" +#include "xbox/xbox_launch.h" +#endif +#include "filesystem/IQueuedLoader.h" +#include "sys.h" + +#include "ixboxsystem.h" +extern IXboxSystem *g_pXboxSystem; + +#include +#include +#ifdef POSIX +// sigh, microsoft put _ in front of its type defines for stat +#define _stat stat +#endif + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define STEAM_PREFIX "STEAM_" + +#define STATUS_COLUMN_LENGTH_LINEPREFIX 1 +#define STATUS_COLUMN_LENGTH_USERID 6 +#define STATUS_COLUMN_LENGTH_USERID_STR "6" +#define STATUS_COLUMN_LENGTH_NAME 19 +#define STATUS_COLUMN_LENGTH_STEAMID 19 +#define STATUS_COLUMN_LENGTH_TIME 9 +#define STATUS_COLUMN_LENGTH_PING 4 +#define STATUS_COLUMN_LENGTH_PING_STR "4" +#define STATUS_COLUMN_LENGTH_LOSS 4 +#define STATUS_COLUMN_LENGTH_LOSS_STR "4" +#define STATUS_COLUMN_LENGTH_STATE 6 +#define STATUS_COLUMN_LENGTH_ADDR 21 + +#define KICKED_BY_CONSOLE "Kicked from server" + +#ifndef SWDS +bool g_bInEditMode = false; +bool g_bInCommentaryMode = false; +#endif + +static void host_name_changed_f( IConVar *var, const char *pOldValue, float flOldValue ) +{ + Steam3Server().NotifyOfServerNameChange(); +} + +ConVar host_name( "hostname", "", 0, "Hostname for server.", host_name_changed_f ); +ConVar host_map( "host_map", "", 0, "Current map name." ); + +void Host_VoiceRecordStop_f(void); +static void voiceconvar_file_changed_f( IConVar *pConVar, const char *pOldValue, float flOldValue ) +{ +#ifndef SWDS + ConVarRef var( pConVar ); + if ( var.GetInt() == 0 ) + { + // Force voice recording to stop if they turn off voice_inputfromfile or if sv_allow_voice_from_file is set to 0. + // Prevents an exploit where clients turn it on, start voice sending a long file, and then turn it off immediately. + Host_VoiceRecordStop_f(); + } +#endif +} + +ConVar voice_recordtofile("voice_recordtofile", "0", 0, "Record mic data and decompressed voice data into 'voice_micdata.wav' and 'voice_decompressed.wav'"); +ConVar voice_inputfromfile("voice_inputfromfile", "0", 0, "Get voice input from 'voice_input.wav' rather than from the microphone.", &voiceconvar_file_changed_f ); +ConVar sv_allow_voice_from_file( "sv_allow_voice_from_file", "1", FCVAR_REPLICATED, "Allow or disallow clients from using voice_inputfromfile on this server.", &voiceconvar_file_changed_f ); + +class CStatusLineBuilder +{ +public: + CStatusLineBuilder() { Reset(); } + void Reset() { m_curPosition = 0; m_szLine[0] = '\0'; } + void AddColumnText( const char *pszText, unsigned int columnWidth ) + { + size_t len = strlen( m_szLine ); + + if ( m_curPosition > len ) + { + for ( size_t i = len; i < m_curPosition; i++ ) + { + m_szLine[i] = ' '; + } + m_szLine[m_curPosition] = '\0'; + } + else if ( len != 0 ) + { + // There is always at least one space between columns. + m_szLine[len] = ' '; + m_szLine[len+1] = '\0'; + } + + V_strncat( m_szLine, pszText, sizeof( m_szLine ) ); + m_curPosition += columnWidth + 1; + } + + void InsertEmptyColumn( unsigned int columnWidth ) + { + m_curPosition += columnWidth + 1; + } + + const char *GetLine() { return m_szLine; } + +private: + size_t m_curPosition; + char m_szLine[512]; +}; + +uint GetSteamAppID() +{ + static uint sunAppID = 0; + static bool bHaveValidSteamInterface = false; + + if ( !bHaveValidSteamInterface ) + { +#ifndef SWDS + if ( Steam3Client().SteamUtils() ) + { + bHaveValidSteamInterface = true; + sunAppID = Steam3Client().SteamUtils()->GetAppID(); + } +#endif + if ( Steam3Server().SteamGameServerUtils() ) + { + bHaveValidSteamInterface = true; + sunAppID = Steam3Server().SteamGameServerUtils()->GetAppID(); + } + + if ( !sunAppID ) + sunAppID = 215; // defaults to Source SDK Base (215) if no steam.inf can be found. + } + + return sunAppID; +} + +EUniverse GetSteamUniverse() +{ +#ifndef SWDS + if ( Steam3Client().SteamUtils() ) + return Steam3Client().SteamUtils()->GetConnectedUniverse(); +#endif + if ( Steam3Server().SteamGameServerUtils() ) + return Steam3Server().SteamGameServerUtils()->GetConnectedUniverse(); + + return k_EUniverseInvalid; +} + +// Globals +int gHostSpawnCount = 0; + +// If any quit handlers balk, then aborts quit sequence +bool EngineTool_CheckQuitHandlers(); + +#if defined( _X360 ) +CON_COMMAND( quit_x360, "" ) +{ + int launchFlags = LF_EXITFROMGAME; + + // allocate the full payload + int nPayloadSize = XboxLaunch()->MaxPayloadSize(); + byte *pPayload = (byte *)stackalloc( nPayloadSize ); + V_memset( pPayload, 0, sizeof( nPayloadSize ) ); + + // payload is at least the command line + // any user data needed must be placed AFTER the command line + const char *pCmdLine = CommandLine()->GetCmdLine(); + int nCmdLineLength = (int)strlen( pCmdLine ) + 1; + V_memcpy( pPayload, pCmdLine, min( nPayloadSize, nCmdLineLength ) ); + + // add any other data here to payload, after the command line + // ... + + // storage device may have changed since previous launch + XboxLaunch()->SetStorageID( XBX_GetStorageDeviceId() ); + + // Close the storage devices + g_pXboxSystem->CloseContainers(); + // persist the user id + bool bInviteRestart = args.FindArg( "invite" ); + DWORD nUserID = ( bInviteRestart ) ? XBX_GetInvitedUserId() : XBX_GetPrimaryUserId(); + XboxLaunch()->SetUserID( nUserID ); + + if ( args.FindArg( "restart" ) ) + { + launchFlags |= LF_GAMERESTART; + } + + // If we're relaunching due to invite + if ( bInviteRestart ) + { + launchFlags |= LF_INVITERESTART; + XNKID nSessionID = XBX_GetInviteSessionId(); + XboxLaunch()->SetInviteSessionID( &nSessionID ); + } + + bool bLaunch = XboxLaunch()->SetLaunchData( pPayload, nPayloadSize, launchFlags ); + if ( bLaunch ) + { + COM_TimestampedLog( "Launching: \"%s\" Flags: 0x%8.8x", pCmdLine, XboxLaunch()->GetLaunchFlags() ); + g_pMaterialSystem->PersistDisplay(); + XBX_DisconnectConsoleMonitor(); + XboxLaunch()->Launch(); + } +} +#endif + +/* +================== +Host_Quit_f +================== +*/ +void Host_Quit_f( const CCommand &args ) +{ +#if !defined(SWDS) + + if ( args.FindArg( "prompt" ) ) + { + // confirm they want to quit + EngineVGui()->ConfirmQuit(); + return; + } + + if ( !EngineTool_CheckQuitHandlers() ) + { + return; + } +#endif + + IGameEvent *event = g_GameEventManager.CreateEvent( "host_quit" ); + if ( event ) + { + g_GameEventManager.FireEventClientSide( event ); + } + + HostState_Shutdown(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CON_COMMAND( _restart, "Shutdown and restart the engine." ) +{ + /* + // FIXME: How to handle restarts? +#ifndef SWDS + if ( !EngineTool_CheckQuitHandlers() ) + { + return; + } +#endif + */ + + HostState_Restart(); +} + +#ifndef SWDS +//----------------------------------------------------------------------------- +// A console command to spew out driver information +//----------------------------------------------------------------------------- +void Host_LightCrosshair (void); + +static ConCommand light_crosshair( "light_crosshair", Host_LightCrosshair, "Show texture color at crosshair", FCVAR_CHEAT ); + +void Host_LightCrosshair (void) +{ + Vector endPoint; + Vector lightmapColor; + + // max_range * sqrt(3) + VectorMA( MainViewOrigin(), COORD_EXTENT * 1.74f, MainViewForward(), endPoint ); + + R_LightVec( MainViewOrigin(), endPoint, true, lightmapColor ); + int r = LinearToTexture( lightmapColor.x ); + int g = LinearToTexture( lightmapColor.y ); + int b = LinearToTexture( lightmapColor.z ); + + ConMsg( "Luxel Value: %d %d %d\n", r, g, b ); +} +#endif + +/* +================== +Host_Status_PrintClient + +Print client info to console +================== +*/ +void Host_Status_PrintClient( IClient *client, bool bShowAddress, void (*print) (const char *fmt, ...) ) +{ + INetChannelInfo *nci = client->GetNetChannel(); + + const char *state = "challenging"; + if ( client->IsActive() ) + state = "active"; + else if ( client->IsSpawned() ) + state = "spawning"; + else if ( client->IsConnected() ) + state = "connecting"; + + CStatusLineBuilder builder; + builder.AddColumnText( "#", STATUS_COLUMN_LENGTH_LINEPREFIX ); + builder.AddColumnText( va( "%" STATUS_COLUMN_LENGTH_USERID_STR "i", client->GetUserID() ), STATUS_COLUMN_LENGTH_USERID ); + builder.AddColumnText( va( "\"%s\"", client->GetClientName() ), STATUS_COLUMN_LENGTH_NAME ); + builder.AddColumnText( client->GetNetworkIDString(), STATUS_COLUMN_LENGTH_STEAMID ); + + if ( nci != NULL ) + { + builder.AddColumnText( COM_FormatSeconds( nci->GetTimeConnected() ), STATUS_COLUMN_LENGTH_TIME ); + builder.AddColumnText( va( "%" STATUS_COLUMN_LENGTH_PING_STR "i", (int)(1000.0f*nci->GetAvgLatency( FLOW_OUTGOING )) ), STATUS_COLUMN_LENGTH_PING ); + builder.AddColumnText( va( "%" STATUS_COLUMN_LENGTH_LOSS_STR "i", (int)(100.0f*nci->GetAvgLoss(FLOW_INCOMING)) ), STATUS_COLUMN_LENGTH_LOSS ); + builder.AddColumnText( state, STATUS_COLUMN_LENGTH_STATE ); + if ( bShowAddress ) + builder.AddColumnText( nci->GetAddress(), STATUS_COLUMN_LENGTH_ADDR ); + } + else + { + builder.InsertEmptyColumn( STATUS_COLUMN_LENGTH_TIME ); + builder.InsertEmptyColumn( STATUS_COLUMN_LENGTH_PING ); + builder.InsertEmptyColumn( STATUS_COLUMN_LENGTH_LOSS ); + builder.AddColumnText( state, STATUS_COLUMN_LENGTH_STATE ); + } + + print( "%s\n", builder.GetLine() ); +} + +void Host_Client_Printf(const char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,fmt); + Q_vsnprintf (string, sizeof( string ), fmt,argptr); + va_end (argptr); + + host_client->ClientPrintf( "%s", string ); +} + +#define LIMIT_PER_CLIENT_COMMAND_EXECUTION_ONCE_PER_INTERVAL(seconds) \ + { \ + static float g_flLastTime__Limit[ABSOLUTE_PLAYER_LIMIT] = { 0.0f }; /* we don't have access to any of the three MAX_PLAYERS #define's here unfortunately */ \ + int playerindex = cmd_clientslot; \ + if ( playerindex >= 0 && playerindex < (ARRAYSIZE(g_flLastTime__Limit)) && realtime - g_flLastTime__Limit[playerindex] > (seconds) ) \ + { \ + g_flLastTime__Limit[playerindex] = realtime; \ + } \ + else \ + { \ + return; \ + } \ + } + +//----------------------------------------------------------------------------- +// Host_Status_f +//----------------------------------------------------------------------------- +CON_COMMAND( status, "Display map and connection status." ) +{ + IClient *client; + int j; + void (*print) (const char *fmt, ...); + +#if defined( _X360 ) + Vector org; + QAngle ang; + const char *pName; + + if ( cl.IsActive() ) + { + pName = cl.m_szLevelNameShort; + org = MainViewOrigin(); + VectorAngles( MainViewForward(), ang ); + IClientEntity *localPlayer = entitylist->GetClientEntity( cl.m_nPlayerSlot + 1 ); + if ( localPlayer ) + { + org = localPlayer->GetAbsOrigin(); + } + } + else + { + pName = ""; + org.Init(); + ang.Init(); + } + + // send to vxconsole + xMapInfo_t mapInfo; + mapInfo.position[0] = org[0]; + mapInfo.position[1] = org[1]; + mapInfo.position[2] = org[2]; + mapInfo.angle[0] = ang[0]; + mapInfo.angle[1] = ang[1]; + mapInfo.angle[2] = ang[2]; + mapInfo.build = build_number(); + mapInfo.skill = skill.GetInt(); + + // generate the qualified path where .sav files are expected to be written + char savePath[MAX_PATH]; + V_snprintf( savePath, sizeof( savePath ), "%s", saverestore->GetSaveDir() ); + V_StripTrailingSlash( savePath ); + g_pFileSystem->RelativePathToFullPath( savePath, "MOD", mapInfo.savePath, sizeof( mapInfo.savePath ) ); + V_FixSlashes( mapInfo.savePath ); + + if ( pName[0] ) + { + // generate the qualified path from where the map was loaded + char mapPath[MAX_PATH]; + Q_snprintf( mapPath, sizeof( mapPath ), "maps/%s.360.bsp", pName ); + g_pFileSystem->GetLocalPath( mapPath, mapInfo.mapPath, sizeof( mapInfo.mapPath ) ); + Q_FixSlashes( mapInfo.mapPath ); + } + else + { + mapInfo.mapPath[0] = '\0'; + } + + XBX_rMapInfo( &mapInfo ); +#endif + + if ( cmd_source == src_command ) + { + if ( !sv.IsActive() ) + { + Cmd_ForwardToServer( args ); + return; + } + print = ConMsg; + } + else + { + print = Host_Client_Printf; + + // limit this to once per 5 seconds + LIMIT_PER_CLIENT_COMMAND_EXECUTION_ONCE_PER_INTERVAL(5.0); + } + + // ============================================================ + // Server status information. + print( "hostname: %s\n", host_name.GetString() ); + + const char *pchSecureReasonString = ""; + const char *pchUniverse = ""; + bool bGSSecure = Steam3Server().BSecure(); + if ( !bGSSecure && Steam3Server().BWantsSecure() ) + { + if ( Steam3Server().BLoggedOn() ) + { + pchSecureReasonString = " (secure mode enabled, connected to Steam3)"; + } + else + { + pchSecureReasonString = " (secure mode enabled, disconnected from Steam3)"; + } + } + + switch ( GetSteamUniverse() ) + { + case k_EUniversePublic: + pchUniverse = ""; + break; + case k_EUniverseBeta: + pchUniverse = " (beta)"; + break; + case k_EUniverseInternal: + pchUniverse = " (internal)"; + break; + case k_EUniverseDev: + pchUniverse = " (dev)"; + break; + default: + pchUniverse = " (unknown)"; + break; + } + + + print( "version : %s/%d %d %s%s%s\n", GetSteamInfIDVersionInfo().szVersionString, + PROTOCOL_VERSION, build_number(), bGSSecure ? "secure" : "insecure", pchSecureReasonString, pchUniverse ); + + if ( NET_IsMultiplayer() ) + { + CUtlString sPublicIPInfo; + if ( !Steam3Server().BLanOnly() ) + { + uint32 unPublicIP = Steam3Server().GetPublicIP(); + if ( unPublicIP != 0 ) + { + netadr_t addr; + addr.SetIP( unPublicIP ); + sPublicIPInfo.Format(" (public ip: %s)", addr.ToString( true ) ); + } + } + print( "udp/ip : %s:%i%s\n", net_local_adr.ToString(true), sv.GetUDPPort(), sPublicIPInfo.String() ); + + if ( !Steam3Server().BLanOnly() ) + { + if ( Steam3Server().BLoggedOn() ) + print( "steamid : %s (%llu)\n", Steam3Server().SteamGameServer()->GetSteamID().Render(), Steam3Server().SteamGameServer()->GetSteamID().ConvertToUint64() ); + else + print( "steamid : not logged in\n" ); + } + } + + // Check if this game uses server registration, then output status + ConVarRef sv_registration_successful( "sv_registration_successful", true ); + if ( sv_registration_successful.IsValid() ) + { + CUtlString sExtraInfo; + ConVarRef sv_registration_message( "sv_registration_message", true ); + if ( sv_registration_message.IsValid() ) + { + const char *msg = sv_registration_message.GetString(); + if ( msg && *msg ) + { + sExtraInfo.Format(" (%s)", msg ); + } + } + + if ( sv_registration_successful.GetBool() ) + { + print( "account : logged in%s\n", sExtraInfo.String() ); + } + else + { + print( "account : not logged in%s\n", sExtraInfo.String() ); + } + } + + print( "map : %s at: %d x, %d y, %d z\n", sv.GetMapName(), (int)MainViewOrigin()[0], (int)MainViewOrigin()[1], (int)MainViewOrigin()[2]); + static ConVarRef sv_tags( "sv_tags" ); + print( "tags : %s\n", sv_tags.GetString() ); + + if ( hltv && hltv->IsActive() ) + { + print( "sourcetv: port %i, delay %.1fs\n", hltv->GetUDPPort(), hltv->GetDirector()->GetDelay() ); + } + +#if defined( REPLAY_ENABLED ) + if ( replay && replay->IsActive() ) + { + print( "replay : %s\n", replay->IsRecording() ? "recording" : "not recording" ); + } +#endif + + int players = sv.GetNumClients(); + int nBots = sv.GetNumFakeClients(); + int nHumans = players - nBots; + + print( "players : %i humans, %i bots (%i max)\n", nHumans, nBots, sv.GetMaxClients() ); + // ============================================================ + + print( "edicts : %d used of %d max\n", sv.num_edicts - sv.free_edicts, sv.max_edicts ); + + if ( ( g_iServerGameDLLVersion >= 10 ) && serverGameDLL ) + { + serverGameDLL->Status( print ); + } + + // Early exit for this server. + if ( args.ArgC() == 2 ) + { + if ( !Q_stricmp( args[1], "short" ) ) + { + for ( j=0 ; j < sv.GetClientCount() ; j++ ) + { + client = sv.GetClient( j ); + + if ( !client->IsActive() ) + continue; + + print( "#%i - %s\n" , j + 1, client->GetClientName() ); + } + return; + } + } + + // the header for the status rows + // print( "# userid %-19s %-19s connected ping loss state%s\n", "name", "uniqueid", cmd_source == src_command ? " adr" : "" ); + CStatusLineBuilder header; + header.AddColumnText( "#", STATUS_COLUMN_LENGTH_LINEPREFIX ); + header.AddColumnText( "userid", STATUS_COLUMN_LENGTH_USERID ); + header.AddColumnText( "name", STATUS_COLUMN_LENGTH_NAME ); + header.AddColumnText( "uniqueid", STATUS_COLUMN_LENGTH_STEAMID ); + header.AddColumnText( "connected", STATUS_COLUMN_LENGTH_TIME ); + header.AddColumnText( "ping", STATUS_COLUMN_LENGTH_PING ); + header.AddColumnText( "loss", STATUS_COLUMN_LENGTH_LOSS ); + header.AddColumnText( "state", STATUS_COLUMN_LENGTH_STATE ); + if ( cmd_source == src_command ) + { + header.AddColumnText( "adr", STATUS_COLUMN_LENGTH_ADDR ); + } + + print( "%s\n", header.GetLine() ); + + for ( j=0 ; j < sv.GetClientCount() ; j++ ) + { + client = sv.GetClient( j ); + + if ( !client->IsConnected() ) + continue; // not connected yet, maybe challenging + + Host_Status_PrintClient( client, (cmd_source == src_command), print ); + } +} + + +//----------------------------------------------------------------------------- +// Host_Ping_f +//----------------------------------------------------------------------------- +CON_COMMAND( ping, "Display ping to server." ) +{ + if ( cmd_source == src_command ) + { + Cmd_ForwardToServer( args ); + return; + } + // limit this to once per 5 seconds + LIMIT_PER_CLIENT_COMMAND_EXECUTION_ONCE_PER_INTERVAL(5.0); + + host_client->ClientPrintf( "Client ping times:\n" ); + + for ( int i=0; i< sv.GetClientCount(); i++ ) + { + IClient *client = sv.GetClient(i); + + if ( !client->IsConnected() || client->IsFakeClient() ) + continue; + + host_client->ClientPrintf ("%4.0f ms : %s\n", + 1000.0f * client->GetNetChannel()->GetAvgLatency( FLOW_OUTGOING ), client->GetClientName() ); + } +} + +bool CL_HL2Demo_MapCheck( const char *name ) +{ + if ( IsPC() && CL_IsHL2Demo() && !sv.IsDedicated() ) + { + if ( !Q_stricmp( name, "d1_trainstation_01" ) || + !Q_stricmp( name, "d1_trainstation_02" ) || + !Q_stricmp( name, "d1_town_01" ) || + !Q_stricmp( name, "d1_town_01a" ) || + !Q_stricmp( name, "d1_town_02" ) || + !Q_stricmp( name, "d1_town_03" ) || + !Q_stricmp( name, "background01" ) || + !Q_stricmp( name, "background03" ) + ) + { + return true; + } + return false; + } + + return true; +} + +bool CL_PortalDemo_MapCheck( const char *name ) +{ + if ( IsPC() && CL_IsPortalDemo() && !sv.IsDedicated() ) + { + if ( !Q_stricmp( name, "testchmb_a_00" ) || + !Q_stricmp( name, "testchmb_a_01" ) || + !Q_stricmp( name, "testchmb_a_02" ) || + !Q_stricmp( name, "testchmb_a_03" ) || + !Q_stricmp( name, "testchmb_a_04" ) || + !Q_stricmp( name, "testchmb_a_05" ) || + !Q_stricmp( name, "testchmb_a_06" ) || + !Q_stricmp( name, "background1" ) + ) + { + return true; + } + return false; + } + + return true; +} + +int _Host_Map_f_CompletionFunc( char const *cmdname, char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ); + +// Note, leaves name alone if no match possible +static bool Host_Map_Helper_FuzzyName( const CCommand &args, char *name, size_t bufsize ) +{ + char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ]; + CUtlString argv0; + argv0 = args.Arg( 0 ); + argv0 += " "; + + if ( _Host_Map_f_CompletionFunc( argv0, args.ArgS(), commands ) > 0 ) + { + Q_strncpy( name, &commands[ 0 ][ argv0.Length() ], bufsize ); + return true; + } + return false; +} + +void Host_Map_Helper( const CCommand &args, bool bEditmode, bool bBackground, bool bCommentary ) +{ + if ( cmd_source != src_command ) + return; + if (args.ArgC() < 2) + { + Warning("No map specified\n"); + return; + } + + const char *pszReason = NULL; + if ( ( g_iServerGameDLLVersion >= 10 ) && !serverGameDLL->IsManualMapChangeOkay( &pszReason ) ) + { + if ( pszReason && pszReason[0] ) + { + Warning( "%s\n", pszReason ); + } + return; + } + + char szMapName[ MAX_QPATH ] = { 0 }; + V_strncpy( szMapName, args[ 1 ], sizeof( szMapName ) ); + + // Call find map, proceed for any value besides NotFound + IVEngineServer::eFindMapResult eResult = g_pVEngineServer->FindMap( szMapName, sizeof( szMapName ) ); + if ( eResult == IVEngineServer::eFindMap_NotFound ) + { + Warning( "map load failed: %s not found or invalid\n", args[ 1 ] ); + return; + } + + COM_TimestampedLog( "*** Map Load: %s", szMapName ); + + // There is a precision issue here, as described Bruce Dawson's blog. + // In our case, we don't care because we're looking for anything on the order of second precision, which + // covers runtime up to around 4 months. + static ConVarRef dev_loadtime_map_start( "dev_loadtime_map_start" ); + dev_loadtime_map_start.SetValue( (float)Plat_FloatTime() ); + + // If I was in edit mode reload config file + // to overwrite WC edit key bindings +#if !defined(SWDS) + if ( !bEditmode ) + { + if ( g_bInEditMode ) + { + // Re-read config from disk + Host_ReadConfiguration(); + g_bInEditMode = false; + } + } + else + { + g_bInEditMode = true; + } + + g_bInCommentaryMode = bCommentary; +#endif + + if ( !CL_HL2Demo_MapCheck( szMapName ) ) + { + Warning( "map load failed: %s not found or invalid\n", szMapName ); + return; + } + + if ( !CL_PortalDemo_MapCheck( szMapName ) ) + { + Warning( "map load failed: %s not found or invalid\n", szMapName ); + return; + } + +#if defined( REPLAY_ENABLED ) + // If we're recording the game, finalize the replay so players can download it. + if ( g_pReplay && g_pReplay->IsRecording() ) + { + g_pReplay->SV_EndRecordingSession(); + } +#endif + + // Stop demo loop + cl.demonum = -1; + + Host_Disconnect( false ); // stop old game + + HostState_NewGame( szMapName, false, bBackground ); + + if (args.ArgC() == 10) + { + if (Q_stricmp(args[2], "setpos") == 0 + && Q_stricmp(args[6], "setang") == 0) + { + Vector newpos; + newpos.x = atof( args[3] ); + newpos.y = atof( args[4] ); + newpos.z = atof( args[5] ); + + QAngle newangle; + newangle.x = atof( args[7] ); + newangle.y = atof( args[8] ); + newangle.z = atof( args[9] ); + + HostState_SetSpawnPoint(newpos, newangle); + } + } +} + +/* +====================== +Host_Map_f + +handle a +map +command from the console. Active clients are kicked off. +====================== +*/ +void Host_Map_f( const CCommand &args ) +{ + Host_Map_Helper( args, false, false, false ); +} + + +//----------------------------------------------------------------------------- +// handle a map_edit command from the console. +// Active clients are kicked off. +// UNDONE: protect this from use if not in dev. mode +//----------------------------------------------------------------------------- +#ifndef SWDS +CON_COMMAND( map_edit, "" ) +{ + Host_Map_Helper( args, true, false, false ); +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: Runs a map as the background +//----------------------------------------------------------------------------- +void Host_Map_Background_f( const CCommand &args ) +{ + Host_Map_Helper( args, false, true, false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Runs a map in commentary mode +//----------------------------------------------------------------------------- +void Host_Map_Commentary_f( const CCommand &args ) +{ + Host_Map_Helper( args, false, false, true ); +} + + +//----------------------------------------------------------------------------- +// Restarts the current server for a dead player +//----------------------------------------------------------------------------- +CON_COMMAND( restart, "Restart the game on the same level (add setpos to jump to current view position on restart)." ) +{ + if ( +#if !defined(SWDS) + demoplayer->IsPlayingBack() || +#endif + !sv.IsActive() ) + return; + + if ( sv.IsMultiplayer() ) + return; + + if ( cmd_source != src_command ) + return; + + bool bRememberLocation = ( args.ArgC() == 2 && !Q_stricmp( args[1], "setpos" ) ); + + Host_Disconnect(false); // stop old game + + if ( !CL_HL2Demo_MapCheck( sv.GetMapName() ) ) + { + Warning( "map load failed: %s not found or invalid\n", sv.GetMapName() ); + return; + } + + if ( !CL_PortalDemo_MapCheck( sv.GetMapName() ) ) + { + Warning( "map load failed: %s not found or invalid\n", sv.GetMapName() ); + return; + } + + HostState_NewGame( sv.GetMapName(), bRememberLocation, false ); +} + + +//----------------------------------------------------------------------------- +// Restarts the current server for a dead player +//----------------------------------------------------------------------------- +CON_COMMAND( reload, "Reload the most recent saved game (add setpos to jump to current view position on reload).") +{ +#ifndef SWDS + const char *pSaveName; + char name[MAX_OSPATH]; +#endif + + if ( +#if !defined(SWDS) + demoplayer->IsPlayingBack() || +#endif + !sv.IsActive() ) + return; + + if ( sv.IsMultiplayer() ) + return; + + if (cmd_source != src_command) + return; + + bool remember_location = false; + if ( args.ArgC() == 2 && + !Q_stricmp( args[1], "setpos" ) ) + { + remember_location = true; + } + + // See if there is a most recently saved game + // Restart that game if there is + // Otherwise, restart the starting game map +#ifndef SWDS + pSaveName = saverestore->FindRecentSave( name, sizeof( name ) ); + + // Put up loading plaque + SCR_BeginLoadingPlaque(); + + Host_Disconnect( false ); // stop old game + + if ( pSaveName && saverestore->SaveFileExists( pSaveName ) ) + { + HostState_LoadGame( pSaveName, remember_location ); + } + else +#endif + { + if ( !CL_HL2Demo_MapCheck( host_map.GetString() ) ) + { + Warning( "map load failed: %s not found or invalid\n", host_map.GetString() ); + return; + } + + if ( !CL_PortalDemo_MapCheck( host_map.GetString() ) ) + { + Warning( "map load failed: %s not found or invalid\n", host_map.GetString() ); + return; + } + + HostState_NewGame( host_map.GetString(), remember_location, false ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Goes to a new map, taking all clients along +// Output : void Host_Changelevel_f +//----------------------------------------------------------------------------- +void Host_Changelevel_f( const CCommand &args ) +{ + if ( args.ArgC() < 2 ) + { + ConMsg( "changelevel : continue game on a new level\n" ); + return; + } + + if ( !sv.IsActive() ) + { + ConMsg( "Can't changelevel, not running server\n" ); + return; + } + + char szName[MAX_PATH] = { 0 }; + V_strncpy( szName, args[1], sizeof( szName ) ); + + // Call find map to attempt to resolve fuzzy/non-canonical map names + IVEngineServer::eFindMapResult eResult = g_pVEngineServer->FindMap( szName, sizeof( szName ) ); + if ( eResult == IVEngineServer::eFindMap_NotFound ) + { + // Warn, but but proceed even if the map is not found, such that we hit the proper server_levelchange_failed + // codepath and event later on. + Warning( "Failed to find map %s\n", args[ 1 ] ); + } + + if ( !CL_HL2Demo_MapCheck(szName) ) + { + Warning( "changelevel failed: %s not found\n", szName ); + return; + } + + if ( !CL_PortalDemo_MapCheck(szName) ) + { + Warning( "changelevel failed: %s not found\n", szName ); + return; + } + + const char *pszReason = NULL; + if ( ( g_iServerGameDLLVersion >= 10 ) && !serverGameDLL->IsManualMapChangeOkay( &pszReason ) ) + { + if ( pszReason && pszReason[0] ) + { + Warning( "%s", pszReason ); + } + return; + } + + HostState_ChangeLevelMP( szName, args[2] ); +} + +//----------------------------------------------------------------------------- +// Purpose: Changing levels within a unit, uses save/restore +//----------------------------------------------------------------------------- +void Host_Changelevel2_f( const CCommand &args ) +{ + if ( args.ArgC() < 2 ) + { + ConMsg ("changelevel2 : continue game on a new level in the unit\n"); + return; + } + + if ( !sv.IsActive() || sv.IsMultiplayer() ) + { + ConMsg( "Can't changelevel2, not in a single-player map\n" ); + return; + } + + char szName[MAX_PATH] = { 0 }; + V_strncpy( szName, args[1], sizeof( szName ) ); + IVEngineServer::eFindMapResult eResult = g_pVEngineServer->FindMap( szName, sizeof( szName ) ); + if ( eResult == IVEngineServer::eFindMap_NotFound ) + { + if ( !CL_IsHL2Demo() || (CL_IsHL2Demo() && !(!Q_stricmp( szName, "d1_trainstation_03" ) || !Q_stricmp( szName, "d1_town_02a" ))) ) + { + Warning( "changelevel2 failed: %s not found\n", szName ); + return; + } + } + +#if !defined(SWDS) + // needs to be before CL_HL2Demo_MapCheck() check as d1_trainstation_03 isn't a valid map + if ( IsPC() && CL_IsHL2Demo() && !sv.IsDedicated() && !Q_stricmp( szName, "d1_trainstation_03" ) ) + { + void CL_DemoTransitionFromTrainstation(); + CL_DemoTransitionFromTrainstation(); + return; + } + + // needs to be before CL_HL2Demo_MapCheck() check as d1_trainstation_03 isn't a valid map + if ( IsPC() && CL_IsHL2Demo() && !sv.IsDedicated() && !Q_stricmp( szName, "d1_town_02a" ) && !Q_stricmp( args[2], "d1_town_02_02a" )) + { + void CL_DemoTransitionFromRavenholm(); + CL_DemoTransitionFromRavenholm(); + return; + } + + if ( IsPC() && CL_IsPortalDemo() && !sv.IsDedicated() && !Q_stricmp( szName, "testchmb_a_07" ) ) + { + void CL_DemoTransitionFromTestChmb(); + CL_DemoTransitionFromTestChmb(); + return; + } + +#endif + + // allow a level transition to d1_trainstation_03 so the Host_Changelevel() can act on it + if ( !CL_HL2Demo_MapCheck( szName ) ) + { + Warning( "changelevel failed: %s not found\n", szName ); + return; + } + + HostState_ChangeLevelSP( szName, args[2] ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Shut down client connection and any server +//----------------------------------------------------------------------------- +void Host_Disconnect( bool bShowMainMenu, const char *pszReason ) +{ + if ( IsX360() ) + { + g_pQueuedLoader->EndMapLoading( false ); + } + +#ifndef SWDS + if ( !sv.IsDedicated() ) + { + cl.Disconnect( pszReason, bShowMainMenu ); + } +#endif + Host_AllowQueuedMaterialSystem( false ); + HostState_GameShutdown(); +} + +void Disconnect() +{ + cl.demonum = -1; + Host_Disconnect(true); + +#if defined( REPLAY_ENABLED ) + // Finalize the recording replay on the server, if is recording. + // NOTE: We don't want this in Host_Disconnect() as that would be called more + // than necessary. + if ( g_pReplay && g_pReplay->IsReplayEnabled() && sv.IsDedicated() ) + { + g_pReplay->SV_EndRecordingSession(); + } +#endif +} + +//----------------------------------------------------------------------------- +// Kill the client and any local server. +//----------------------------------------------------------------------------- +CON_COMMAND( disconnect, "Disconnect game from server." ) +{ +#if !defined( SWDS ) + // Just run the regular Disconnect function if we're not the client or the client didn't handle it for us + if( !g_ClientDLL || !g_ClientDLL->DisconnectAttempt() ) + { + Disconnect(); + } +#else + Disconnect(); +#endif +} + +#ifdef _WIN32 +// manually pull in the GetEnvironmentVariableA defn so we don't need to include windows.h +extern "C" +{ +DWORD __declspec(dllimport) __stdcall GetEnvironmentVariableA( const char *, char *, DWORD ); +} +#endif // _WIN32 + +CON_COMMAND( version, "Print version info string." ) +{ + ConMsg( "Build Label: %8d # Uniquely identifies each build\n", GetSteamInfIDVersionInfo().ServerVersion ); + ConMsg( "Network PatchVersion: %8s # Determines client and server compatibility\n", GetSteamInfIDVersionInfo().szVersionString ); + ConMsg( "Protocol version: %8d # High level network protocol version\n", PROTOCOL_VERSION ); + + if ( sv.IsDedicated() || serverGameDLL ) + { + ConMsg( "Server version: %8i\n", GetSteamInfIDVersionInfo().ServerVersion ); + ConMsg( "Server AppID: %8i\n", GetSteamInfIDVersionInfo().ServerAppID ); + } + if ( !sv.IsDedicated() ) + { + ConMsg( "Client version: %8i\n", GetSteamInfIDVersionInfo().ClientVersion ); + ConMsg( "Client AppID: %8i\n", GetSteamInfIDVersionInfo().AppID ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CON_COMMAND( pause, "Toggle the server pause state." ) +{ +#ifndef SWDS + if ( !sv.IsDedicated() ) + { + if ( !cl.m_szLevelFileName[ 0 ] ) + return; + } +#endif + + if ( cmd_source == src_command ) + { + Cmd_ForwardToServer( args ); + return; + } + + if ( !sv.IsPausable() ) + return; + + // toggle paused state + sv.SetPaused( !sv.IsPaused() ); + + // send text messaage who paused the game + sv.BroadcastPrintf( "%s %s the game\n", host_client->GetClientName(), sv.IsPaused() ? "paused" : "unpaused" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CON_COMMAND( setpause, "Set the pause state of the server." ) +{ +#ifndef SWDS + if ( !cl.m_szLevelFileName[ 0 ] ) + return; +#endif + + if ( cmd_source == src_command ) + { + Cmd_ForwardToServer( args ); + return; + } + + sv.SetPaused( true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CON_COMMAND( unpause, "Unpause the game." ) +{ +#ifndef SWDS + if ( !cl.m_szLevelFileName[ 0 ] ) + return; +#endif + + if ( cmd_source == src_command ) + { + Cmd_ForwardToServer( args ); + return; + } + + sv.SetPaused( false ); +} + +// No non-testing use for this at the moment, though server mods in public will expose similar functionality +#if defined( STAGING_ONLY ) || defined( _DEBUG ) +//----------------------------------------------------------------------------- +// Purpose: Send a string command to a client by userid +//----------------------------------------------------------------------------- +CON_COMMAND( clientcmd, "Send a clientside command to a player by userid" ) +{ + if ( args.ArgC() <= 2 ) + { + ConMsg( "Usage: clientcmd < userid > { command string }\n" ); + return; + } + + // Args + int userid = Q_atoi( args[1] ); + int messageArgStart = 2; + + // Concatenate other arguments into string + CUtlString commandString; + + commandString.SetLength( Q_strlen( args.ArgS() ) ); + commandString.Set( args[ messageArgStart ] ); + for ( int i = messageArgStart + 1; i < args.ArgC(); i++ ) + { + commandString.Append( " " ); + commandString.Append( args[i] ); + } + + // find client + IClient *client = NULL; + for ( int i = 0; i < sv.GetClientCount(); i++ ) + { + IClient *searchclient = sv.GetClient( i ); + + if ( !searchclient->IsConnected() ) + continue; + + if ( userid != -1 && searchclient->GetUserID() == userid ) + { + client = searchclient; + break; + } + } + + if ( !client ) + { + ConMsg( "userid \"%d\" not found\n", userid ); + return; + } + + NET_StringCmd cmdMsg( commandString ) ; + client->SendNetMsg( cmdMsg, true ); +} +#endif // defined( STAGING_ONLY ) || defined( _DEBUG ) + +//----------------------------------------------------------------------------- +// Kicks a user off of the server using their userid or uniqueid +//----------------------------------------------------------------------------- +CON_COMMAND( kickid, "Kick a player by userid or uniqueid, with a message." ) +{ + char *who = NULL; + const char *pszArg1 = NULL, *pszMessage = NULL; + IClient *client = NULL; + int iSearchIndex = -1; + char szSearchString[128]; + int argsStartNum = 1; + bool bSteamID = false; + int i = 0; + + if ( args.ArgC() <= 1 ) + { + ConMsg( "Usage: kickid < userid | uniqueid > { message }\n" ); + return; + } + + // get the first argument + pszArg1 = args[1]; + + // if the first letter is a character then + // we're searching for a uniqueid ( e.g. STEAM_ ) + if ( *pszArg1 < '0' || *pszArg1 > '9' ) + { + // SteamID (need to reassemble it) + if ( !Q_strnicmp( pszArg1, STEAM_PREFIX, strlen( STEAM_PREFIX ) ) && Q_strstr( args[2], ":" ) ) + { + Q_snprintf( szSearchString, sizeof( szSearchString ), "%s:%s:%s", pszArg1, args[3], args[5] ); + argsStartNum = 5; + bSteamID = true; + } + // some other ID (e.g. "UNKNOWN", "STEAM_ID_PENDING", "STEAM_ID_LAN") + // NOTE: assumed to be one argument + else + { + Q_snprintf( szSearchString, sizeof( szSearchString ), "%s", pszArg1 ); + } + } + // this is a userid + else + { + iSearchIndex = Q_atoi( pszArg1 ); + } + + // check for a message + if ( args.ArgC() > argsStartNum ) + { + int j; + int dataLen = 0; + + pszMessage = args.ArgS(); + for ( j = 1; j <= argsStartNum; j++ ) + { + dataLen += Q_strlen( args[j] ) + 1; // +1 for the space between args + } + + if ( bSteamID ) + { + dataLen -= 5; // SteamIDs don't have spaces between the args[) values + } + + if ( dataLen > Q_strlen( pszMessage ) ) // saftey check + { + pszMessage = NULL; + } + else + { + pszMessage += dataLen; + } + } + + // find this client + for ( i = 0; i < sv.GetClientCount(); i++ ) + { + client = sv.GetClient( i ); + + if ( !client->IsConnected() ) + continue; + +#if defined( REPLAY_ENABLED ) + if ( client->IsReplay() ) + continue; +#endif + + if ( client->IsHLTV() ) + continue; + + // searching by UserID + if ( iSearchIndex != -1 ) + { + if ( client->GetUserID() == iSearchIndex ) + { + // found! + break; + } + } + // searching by UniqueID + else + { + if ( Q_stricmp( client->GetNetworkIDString(), szSearchString ) == 0 ) + { + // found! + break; + } + } + } + + // now kick them + if ( i < sv.GetClientCount() ) + { + if ( cmd_source != src_command ) + { + who = host_client->m_Name; + } + + // can't kick yourself! + if ( cmd_source != src_command && host_client == client && !sv.IsDedicated() ) + { + return; + } + + if ( iSearchIndex != -1 || !client->IsFakeClient() ) + { + if ( who == NULL ) + { + if ( pszMessage ) + { + client->Disconnect( "%s", pszMessage ); + } + else + { + client->Disconnect( KICKED_BY_CONSOLE ); + } + } + else + { + if ( pszMessage ) + { + client->Disconnect( "Kicked by %s : %s", who, pszMessage ); + } + else + { + client->Disconnect( "Kicked by %s", who ); + } + } + } + } + else + { + if ( iSearchIndex != -1 ) + { + ConMsg( "userid \"%d\" not found\n", iSearchIndex ); + } + else + { + ConMsg( "uniqueid \"%s\" not found\n", szSearchString ); + } + } +} + +/* +================== +Host_Kick_f + +Kicks a user off of the server using their name +================== +*/ +CON_COMMAND( kick, "Kick a player by name." ) +{ + char *who = NULL; + char *pszName = NULL; + IClient *client = NULL; + int i = 0; + char name[64]; + + if ( args.ArgC() <= 1 ) + { + ConMsg( "Usage: kick < name >\n" ); + return; + } + + // copy the name to a local buffer + memset( name, 0, sizeof(name) ); + Q_strncpy( name, args.ArgS(), sizeof(name) ); + pszName = name; + + // safety check + if ( pszName && pszName[0] != 0 ) + { + //HACK-HACK + // check for the name surrounded by quotes (comes in this way from rcon) + int len = Q_strlen( pszName ) - 1; // (minus one since we start at 0) + if ( pszName[0] == '"' && pszName[len] == '"' ) + { + // get rid of the quotes at the beginning and end + pszName[len] = 0; + pszName++; + } + + for ( i = 0; i < sv.GetClientCount(); i++ ) + { + client = sv.GetClient(i); + + if ( !client->IsConnected() ) + continue; + +#if defined( REPLAY_ENABLED ) + if ( client->IsReplay() ) + continue; +#endif + + if ( client->IsHLTV() ) + continue; + + // found! + if ( Q_strcasecmp( client->GetClientName(), pszName ) == 0 ) + break; + } + + // now kick them + if ( i < sv.GetClientCount() ) + { + if ( cmd_source != src_command ) + { + who = host_client->m_Name; + } + + // can't kick yourself! + if ( cmd_source != src_command && host_client == client && !sv.IsDedicated() ) + return; + + if ( who ) + { + client->Disconnect( "Kicked by %s", who ); + } + else + { + client->Disconnect( KICKED_BY_CONSOLE ); + } + } + else + { + ConMsg( "name \"%s\" not found\n", pszName ); + } + } +} + +//----------------------------------------------------------------------------- +// Kicks all users off of the server +//----------------------------------------------------------------------------- +CON_COMMAND( kickall, "Kicks everybody connected with a message." ) +{ + char *who = NULL; + IClient *client = NULL; + int i = 0; + char szMessage[128]; + + // copy the message to a local buffer + memset( szMessage, 0, sizeof(szMessage) ); + V_strcpy_safe( szMessage, args.ArgS() ); + + if ( cmd_source != src_command ) + { + who = host_client->m_Name; + } + + for ( i = 0; i < sv.GetClientCount(); i++ ) + { + client = sv.GetClient(i); + + if ( !client->IsConnected() ) + continue; + + // can't kick yourself! + if ( cmd_source != src_command && host_client == client && !sv.IsDedicated() ) + continue; + +#if defined( REPLAY_ENABLED ) + if ( client->IsReplay() ) + continue; +#endif + + if ( client->IsHLTV() ) + continue; + + if ( who ) + { + if ( szMessage[0] ) + { + client->Disconnect( "Kicked by %s : %s", who, szMessage ); + } + else + { + client->Disconnect( "Kicked by %s", who ); + } + } + else + { + if ( szMessage[0] ) + { + client->Disconnect( "%s", szMessage ); + } + else + { + client->Disconnect( KICKED_BY_CONSOLE ); + } + } + } +} + +/* +=============================================================================== + +DEBUGGING TOOLS + +=============================================================================== +*/ + + +//----------------------------------------------------------------------------- +// Dump memory stats +//----------------------------------------------------------------------------- +CON_COMMAND( memory, "Print memory stats." ) +{ +#if !defined(NO_MALLOC_OVERRIDE) + ConMsg( "Heap Used:\n" ); + int nTotal = MemAlloc_GetSize( 0 ); + if (nTotal == -1) + { + ConMsg( "Corrupted!\n" ); + } + else + { + ConMsg( "%5.2f MB (%d bytes)\n", nTotal/(1024.0f*1024.0f), nTotal ); + } +#endif + +#ifdef VPROF_ENABLED + ConMsg("\nVideo Memory Used:\n"); + CVProfile *pProf = &g_VProfCurrentProfile; + int prefixLen = strlen( "TexGroup_Global_" ); + float total = 0.0f; + for ( int i=0; i < pProf->GetNumCounters(); i++ ) + { + if ( pProf->GetCounterGroup( i ) == COUNTER_GROUP_TEXTURE_GLOBAL ) + { + float value = pProf->GetCounterValue( i ) * (1.0f/(1024.0f*1024.0f) ); + total += value; + const char *pName = pProf->GetCounterName( i ); + if ( !Q_strnicmp( pName, "TexGroup_Global_", prefixLen ) ) + { + pName += prefixLen; + } + ConMsg( "%5.2f MB: %s\n", value, pName ); + } + } + ConMsg("------------------\n"); + ConMsg( "%5.2f MB: total\n", total ); +#endif + + ConMsg( "\nHunk Memory Used:\n" ); + Hunk_Print(); +} + +/* +=============================================================================== + +DEMO LOOP CONTROL + +=============================================================================== +*/ + + +#ifndef SWDS + +//MOTODO move all demo commands to demoplayer + + +//----------------------------------------------------------------------------- +// Purpose: Gets number of valid demo names +// Output : int +//----------------------------------------------------------------------------- +int Host_GetNumDemos() +{ + int c = 0; +#ifndef SWDS + for ( int i = 0; i < MAX_DEMOS; ++i ) + { + const char *demoname = cl.demos[ i ].Get(); + if ( !demoname[ 0 ] ) + break; + + ++c; + } +#endif + return c; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Host_PrintDemoList() +{ + int count = Host_GetNumDemos(); + + int next = cl.demonum; + if ( next >= count || next < 0 ) + { + next = 0; + } + +#ifndef SWDS + for ( int i = 0; i < MAX_DEMOS; ++i ) + { + const char *demoname = cl.demos[ i ].Get(); + if ( !demoname[ 0 ] ) + break; + + bool isnextdemo = next == i ? true : false; + + DevMsg( "%3s % 2i : %20s\n", isnextdemo ? "-->" : " ", i, cl.demos[ i ].Get() ); + } +#endif + + if ( !count ) + { + DevMsg( "No demos in list, use startdemos to specify\n" ); + } +} + + +#ifndef SWDS +//----------------------------------------------------------------------------- +// +// Con commands related to demos, not available on dedicated servers +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: Specify list of demos for the "demos" command +//----------------------------------------------------------------------------- +CON_COMMAND( startdemos, "Play demos in demo sequence." ) +{ + int c = args.ArgC() - 1; + if (c > MAX_DEMOS) + { + Msg ("Max %i demos in demoloop\n", MAX_DEMOS); + c = MAX_DEMOS; + } + Msg ("%i demo(s) in loop\n", c); + + for ( int i=1 ; iIsPlayingBack() ) + { + CL_NextDemo (); + } + else + { + cl.demonum = -1; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Return to looping demos, optional resume demo index +//----------------------------------------------------------------------------- +CON_COMMAND( demos, "Demo demo file sequence." ) +{ + int oldn = cl.demonum; + cl.demonum = -1; + Host_Disconnect(false); + cl.demonum = oldn; + + if (cl.demonum == -1) + cl.demonum = 0; + + if ( args.ArgC() == 2 ) + { + int numdemos = Host_GetNumDemos(); + if ( numdemos >= 1 ) + { + cl.demonum = clamp( Q_atoi( args[1] ), 0, numdemos - 1 ); + DevMsg( "Jumping to %s\n", cl.demos[ cl.demonum ].Get() ); + } + } + + Host_PrintDemoList(); + + CL_NextDemo (); +} + +//----------------------------------------------------------------------------- +// Purpose: Stop current demo +//----------------------------------------------------------------------------- +CON_COMMAND_F( stopdemo, "Stop playing back a demo.", FCVAR_DONTRECORD ) +{ + if ( !demoplayer->IsPlayingBack() ) + return; + + Host_Disconnect (true); +} + +//----------------------------------------------------------------------------- +// Purpose: Skip to next demo +//----------------------------------------------------------------------------- +CON_COMMAND( nextdemo, "Play next demo in sequence." ) +{ + if ( args.ArgC() == 2 ) + { + int numdemos = Host_GetNumDemos(); + if ( numdemos >= 1 ) + { + cl.demonum = clamp( Q_atoi( args[1] ), 0, numdemos - 1 ); + DevMsg( "Jumping to %s\n", cl.demos[ cl.demonum ].Get() ); + } + } + Host_EndGame( false, "Moving to next demo..." ); +} + +//----------------------------------------------------------------------------- +// Purpose: Print out the current demo play order +//----------------------------------------------------------------------------- +CON_COMMAND( demolist, "Print demo sequence list." ) +{ + Host_PrintDemoList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Host_Soundfade_f +//----------------------------------------------------------------------------- +CON_COMMAND_F( soundfade, "Fade client volume.", FCVAR_SERVER_CAN_EXECUTE ) +{ + float percent; + float inTime, holdTime, outTime; + + if (args.ArgC() != 3 && args.ArgC() != 5) + { + Msg("soundfade [ ]\n"); + return; + } + + percent = clamp( (float) atof(args[1]), 0.0f, 100.0f ); + + holdTime = max( 0., atof(args[2]) ); + + inTime = 0.0f; + outTime = 0.0f; + if (args.ArgC() == 5) + { + outTime = max( 0., atof(args[3]) ); + inTime = max( 0., atof( args[4]) ); + } + + S_SoundFade( percent, holdTime, outTime, inTime ); +} + +#endif // !SWDS + +#endif + + +//----------------------------------------------------------------------------- +// Shutdown the server +//----------------------------------------------------------------------------- +CON_COMMAND( killserver, "Shutdown the server." ) +{ + Host_Disconnect(true); + + if ( !sv.IsDedicated() ) + { + // close network sockets + NET_SetMutiplayer( false ); + } +} + +#if !defined(SWDS) +void Host_VoiceRecordStart_f(void) +{ +#ifdef VOICE_VOX_ENABLE + ConVarRef voice_vox( "voice_vox" ); + if ( voice_vox.IsValid() && voice_vox.GetBool() ) + return; +#endif // VOICE_VOX_ENABLE + + if ( cl.IsActive() ) + { + const char *pUncompressedFile = NULL; + const char *pDecompressedFile = NULL; + const char *pInputFile = NULL; + + if (voice_recordtofile.GetInt()) + { + pUncompressedFile = "voice_micdata.wav"; + pDecompressedFile = "voice_decompressed.wav"; + } + + if (voice_inputfromfile.GetInt()) + { + pInputFile = "voice_input.wav"; + } + if ( !sv_allow_voice_from_file.GetBool() ) + { + pInputFile = NULL; + } +#if !defined( NO_VOICE ) + if (Voice_RecordStart(pUncompressedFile, pDecompressedFile, pInputFile)) + { + } +#endif + } +} + + +void Host_VoiceRecordStop_f(void) +{ +#ifdef VOICE_VOX_ENABLE + ConVarRef voice_vox( "voice_vox" ); + if ( voice_vox.IsValid() && voice_vox.GetBool() ) + return; +#endif // VOICE_VOX_ENABLE + + if ( cl.IsActive() ) + { +#if !defined( NO_VOICE ) + if (Voice_IsRecording()) + { + CL_SendVoicePacket( g_bUsingSteamVoice ? false : true ); + Voice_UserDesiresStop(); + } +#endif + } +} + +#ifdef VOICE_VOX_ENABLE +void Host_VoiceToggle_f( const CCommand &args ) +{ + if ( cl.IsActive() ) + { +#if !defined( NO_VOICE ) + bool bToggle = false; + + if ( args.ArgC() == 2 && V_strcasecmp( args[1], "on" ) == 0 ) + { + bToggle = true; + } + + if ( Voice_IsRecording() && bToggle == false ) + { + CL_SendVoicePacket( g_bUsingSteamVoice ? false : true ); + Voice_UserDesiresStop(); + } + else if ( !Voice_IsRecording() && bToggle == true ) + { + const char *pUncompressedFile = NULL; + const char *pDecompressedFile = NULL; + const char *pInputFile = NULL; + + if (voice_recordtofile.GetInt()) + { + pUncompressedFile = "voice_micdata.wav"; + pDecompressedFile = "voice_decompressed.wav"; + } + + if (voice_inputfromfile.GetInt()) + { + pInputFile = "voice_input.wav"; + } + if ( !sv_allow_voice_from_file.GetBool() ) + { + pInputFile = NULL; + } + + Voice_RecordStart( pUncompressedFile, pDecompressedFile, pInputFile ); + } +#endif // NO_VOICE + } +} +#endif // VOICE_VOX_ENABLE + +#endif // SWDS + +//----------------------------------------------------------------------------- +// Purpose: Wrapper for modelloader->Print() function call +//----------------------------------------------------------------------------- +CON_COMMAND( listmodels, "List loaded models." ) +{ + modelloader->Print(); +} + +/* +================== +Host_IncrementCVar +================== +*/ +CON_COMMAND_F( incrementvar, "Increment specified convar value.", FCVAR_DONTRECORD ) +{ + if( args.ArgC() != 5 ) + { + Warning( "Usage: incrementvar varName minValue maxValue delta\n" ); + return; + } + + const char *varName = args[ 1 ]; + if( !varName ) + { + ConDMsg( "Host_IncrementCVar_f without a varname\n" ); + return; + } + + ConVar *var = ( ConVar * )g_pCVar->FindVar( varName ); + if( !var ) + { + ConDMsg( "cvar \"%s\" not found\n", varName ); + return; + } + + float currentValue = var->GetFloat(); + float startValue = atof( args[ 2 ] ); + float endValue = atof( args[ 3 ] ); + float delta = atof( args[ 4 ] ); + float newValue = currentValue + delta; + if( newValue > endValue ) + { + newValue = startValue; + } + else if ( newValue < startValue ) + { + newValue = endValue; + } + + // Conver incrementvar command to direct sets to avoid any problems with state in a demo loop. + Cbuf_AddText( va("%s %f", varName, newValue) ); + + ConDMsg( "%s = %f\n", var->GetName(), newValue ); +} + + +//----------------------------------------------------------------------------- +// Host_MultiplyCVar_f +//----------------------------------------------------------------------------- +CON_COMMAND_F( multvar, "Multiply specified convar value.", FCVAR_DONTRECORD ) +{ + if (( args.ArgC() != 5 )) + { + Warning( "Usage: multvar varName minValue maxValue factor\n" ); + return; + } + + const char *varName = args[ 1 ]; + if( !varName ) + { + ConDMsg( "multvar without a varname\n" ); + return; + } + + ConVar *var = ( ConVar * )g_pCVar->FindVar( varName ); + if( !var ) + { + ConDMsg( "cvar \"%s\" not found\n", varName ); + return; + } + + float currentValue = var->GetFloat(); + float startValue = atof( args[ 2 ] ); + float endValue = atof( args[ 3 ] ); + float factor = atof( args[ 4 ] ); + float newValue = currentValue * factor; + if( newValue > endValue ) + { + newValue = endValue; + } + else if ( newValue < startValue ) + { + newValue = startValue; + } + + // Conver incrementvar command to direct sets to avoid any problems with state in a demo loop. + Cbuf_AddText( va("%s %f", varName, newValue) ); + + ConDMsg( "%s = %f\n", var->GetName(), newValue ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CON_COMMAND( dumpstringtables, "Print string tables to console." ) +{ + SV_PrintStringTables(); +#ifndef SWDS + CL_PrintStringTables(); +#endif +} + +// Register shared commands +ConCommand quit("quit", Host_Quit_f, "Exit the engine."); +static ConCommand cmd_exit("exit", Host_Quit_f, "Exit the engine."); + +#ifndef SWDS +#ifdef VOICE_OVER_IP +static ConCommand startvoicerecord("+voicerecord", Host_VoiceRecordStart_f); +static ConCommand endvoicerecord("-voicerecord", Host_VoiceRecordStop_f); +#ifdef VOICE_VOX_ENABLE +static ConCommand togglevoicerecord("voicerecord_toggle", Host_VoiceToggle_f); +#endif // VOICE_VOX_ENABLE +#endif // VOICE_OVER_IP + +#endif // SWDS + + +#if defined( STAGING_ONLY ) + +// From Kyle: For the GC we added this so we could call it over and +// over until we got the crash reporter fixed. + +// Visual studio optimizes this away unless we disable optimizations. +#pragma optimize( "", off ) + +class PureCallBase +{ +public: + virtual void PureFunction() = 0; + + PureCallBase() + { + NonPureFunction(); + } + + void NonPureFunction() + { + PureFunction(); + } +}; + +class PureCallDerived : public PureCallBase +{ +public: + void PureFunction() OVERRIDE + { + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Force various crashes. useful for testing minidumps. +// crash : Write 0 to address 0. +// crash sys_error : Call Sys_Error(). +// crash hang : Hang. +// crash purecall : Call virtual function in ctor. +//----------------------------------------------------------------------------- +CON_COMMAND( crash, "[ sys_error | hang | purecall | segfault | minidump ]: Cause the engine to crash." ) +{ + if ( cmd_source != src_command ) + return; + + CUtlString cmd( ( args.ArgC() > 1 ) ? args[ 1 ] : "" ); + + if ( cmd == "hang" ) + { + // Hang. Useful to test watchdog code. + Msg( "Hanging... Watchdog time: %d.\n ", Plat_GetWatchdogTime() ); + for ( ;; ) + { + Msg( "%d ", Plat_MSTime() ); + ThreadSleep( 5000 ); + } + } + else if ( cmd == "purecall" ) + { + Msg( "Instantiating PureCallDerived_derived...\n" ); + PureCallDerived derived; + } + else if ( cmd == "sys_error" ) + { + Msg( "Calling Sys_Error...\n" ); + Sys_Error( "%s: Sys_Error()!!!", __FUNCTION__ ); + } + else if ( cmd == "minidump" ) + { + Msg( "Forcing minidump. build_number: %d.\n", build_number() ); + SteamAPI_WriteMiniDump( 0, NULL, build_number() ); + } + else + { + Msg( "Segfault...\n" ); + char *p = 0; + *p = 0; + } +} + +#pragma optimize( "", on ) + +#endif // STAGING_ONLY + +CON_COMMAND_F( flush, "Flush unlocked cache memory.", FCVAR_CHEAT ) +{ +#if !defined( SWDS ) + g_ClientDLL->InvalidateMdlCache(); +#endif // SWDS + serverGameDLL->InvalidateMdlCache(); + g_pDataCache->Flush( true ); +} + +CON_COMMAND_F( flush_locked, "Flush unlocked and locked cache memory.", FCVAR_CHEAT ) +{ +#if !defined( SWDS ) + g_ClientDLL->InvalidateMdlCache(); +#endif // SWDS + serverGameDLL->InvalidateMdlCache(); + g_pDataCache->Flush( false ); +} + +CON_COMMAND( cache_print, "cache_print [section]\nPrint out contents of cache memory." ) +{ + const char *pszSection = NULL; + if ( args.ArgC() == 2 ) + { + pszSection = args[ 1 ]; + } + g_pDataCache->OutputReport( DC_DETAIL_REPORT, pszSection ); +} + +CON_COMMAND( cache_print_lru, "cache_print_lru [section]\nPrint out contents of cache memory." ) +{ + const char *pszSection = NULL; + if ( args.ArgC() == 2 ) + { + pszSection = args[ 1 ]; + } + g_pDataCache->OutputReport( DC_DETAIL_REPORT_LRU, pszSection ); +} + +CON_COMMAND( cache_print_summary, "cache_print_summary [section]\nPrint out a summary contents of cache memory." ) +{ + const char *pszSection = NULL; + if ( args.ArgC() == 2 ) + { + pszSection = args[ 1 ]; + } + g_pDataCache->OutputReport( DC_SUMMARY_REPORT, pszSection ); +} + +CON_COMMAND( sv_dump_edicts, "Display a list of edicts allocated on the server." ) +{ + if ( !sv.IsActive() ) + return; + + CUtlMap classNameCountMap; + classNameCountMap.SetLessFunc( UtlStringLessFunc ); + + Msg( "\nCurrent server edicts:\n"); + for ( int i = 0; i < sv.num_edicts; ++i ) + { + CUtlMap::IndexType_t index = classNameCountMap.Find( sv.edicts[ i ].GetClassName() ); + if ( index == classNameCountMap.InvalidIndex() ) + { + index = classNameCountMap.Insert( sv.edicts[ i ].GetClassName(), 0 ); + } + + classNameCountMap[ index ]++; + } + + Msg( "Count Classname\n"); + FOR_EACH_MAP( classNameCountMap, i ) + { + Msg("%5d %s\n", classNameCountMap[ i ], classNameCountMap.Key(i).String() ); + } + Msg( "NumEdicts: %d\n", sv.num_edicts ); + Msg( "FreeEdicts: %d\n\n", sv.free_edicts ); +} + +// make valve_ds only? +CON_COMMAND_F( memory_list, "dump memory list (linux only)", FCVAR_CHEAT ) +{ + DumpMemoryLog( 128 * 1024 ); +} + +// make valve_ds only? +CON_COMMAND_F( memory_status, "show memory stats (linux only)", FCVAR_CHEAT ) +{ + DumpMemorySummary(); +} + +// make valve_ds only? +CON_COMMAND_F( memory_mark, "snapshot current allocation status", FCVAR_CHEAT ) +{ + SetMemoryMark(); +} +// make valve_ds only? +CON_COMMAND_F( memory_diff, "show memory stats relative to snapshot", FCVAR_CHEAT ) +{ + DumpChangedMemory( 64 * 1024 ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CON_COMMAND( namelockid, "Prevent name changes for this userID." ) +{ + if ( args.ArgC() <= 2 ) + { + ConMsg( "Usage: namelockid < userid > < 0 | 1 >\n" ); + return; + } + + CBaseClient *pClient = NULL; + + int iIndex = Q_atoi( args[1] ); + if ( iIndex > 0 ) + { + for ( int i = 0; i < sv.GetClientCount(); i++ ) + { + pClient = static_cast< CBaseClient* >( sv.GetClient( i ) ); + + if ( !pClient->IsConnected() ) + continue; + +#if defined( REPLAY_ENABLED ) + if ( pClient->IsReplay() ) + continue; +#endif + + if ( pClient->IsHLTV() ) + continue; + + if ( pClient->GetUserID() == iIndex ) + break; + + pClient = NULL; + } + } + + if ( pClient ) + { + pClient->SetPlayerNameLocked( ( Q_atoi( args[2] ) == 0 ) ? false : true ); + } + else + { + ConMsg( "Player id \"%d\" not found.\n", iIndex ); + } +} + +#if defined( STAGING_ONLY ) || defined( _DEBUG ) +CON_COMMAND( fs_find, "Run virtual filesystem find" ) +{ + if ( args.ArgC() != 3 ) + { + ConMsg( "Usage: fs_find wildcard pathid\n" ); + return; + } + + const char *pWildcard = args.Arg(1); + const char *pPathID = args.Arg(2); + + FileFindHandle_t findhandle; + const char *pFile = NULL; + size_t matches = 0; + for ( pFile = g_pFullFileSystem->FindFirstEx( pWildcard, pPathID, &findhandle ); + pFile; + pFile = g_pFullFileSystem->FindNext( findhandle ) ) + { + ConMsg( "%s\n", pFile ); + matches++; + } + + ConMsg( " %u matching files/directories\n", matches ); +} +#endif // defined( STAGING_ONLY ) || defined( _DEBUG ) -- cgit v1.2.3