//========= 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 )