//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// // HACKHACK fix this include #if defined( _WIN32 ) && !defined( _X360 ) #define WIN32_LEAN_AND_MEAN #include #endif #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 "protocol.h" #include "cl_main.h" #include "sv_steamauth.h" #include "zone.h" #include "GameEventManager.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 "hltvserver.h" #if defined( REPLAY_ENABLED ) #include "replayserver.h" #endif #include "cdll_engine_int.h" #include "cl_steamauth.h" #include "cl_splitscreen.h" #ifndef DEDICATED #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" #elif defined( _PS3 ) #include "ps3/ps3_console.h" #include "tls_ps3.h" #endif #include "filesystem/IQueuedLoader.h" #include "filesystem/IXboxInstaller.h" #include "toolframework/itoolframework.h" #include "fmtstr.h" #include "tier3/tier3.h" #include "matchmaking/imatchframework.h" #include "tier2/tier2.h" #include "shaderapi/gpumemorystats.h" #include "snd_audio_source.h" #include "netconsole.h" #include "tier2/fileutils.h" #if POSIX #include #include #include #include #include #endif #include "ixboxsystem.h" extern IXboxSystem *g_pXboxSystem; extern IVEngineClient *engineClient; // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define STEAM_PREFIX "STEAM_" #ifndef DEDICATED bool g_bInEditMode = false; bool g_bInCommentaryMode = false; #endif KeyValues *g_pLaunchOptions = NULL; void PerformKick( cmd_source_t commandSource, int iSearchIndex, char* szSearchString, bool bForceKick, const char* pszMessage ); ConVar host_name_store( "host_name_store", "1", FCVAR_RELEASE, "Whether hostname is recorded in game events and GOTV." ); ConVar host_players_show( "host_players_show", "1", FCVAR_RELEASE, "How players are disclosed in server queries: 0 - query disabled, 1 - show only max players count, 2 - show all players" ); ConVar host_info_show( "host_info_show", "1", FCVAR_RELEASE, "How server info gets disclosed in server queries: 0 - query disabled, 1 - show only general info, 2 - show full info" ); ConVar host_rules_show( "host_rules_show", "1", FCVAR_RELEASE, "How server rules get disclosed in server queries: 0 - query disabled, 1 - query enabled" ); static void HostnameChanged( IConVar *pConVar, const char *pOldValue, float flOldValue ) { Steam3Server().NotifyOfServerNameChange(); if ( sv.IsActive() && host_name_store.GetBool() ) { // look up the descriptor first to avoid a DevMsg for HL2 and mods that don't define a // hostname_change event CGameEventDescriptor *descriptor = g_GameEventManager.GetEventDescriptor( "hostname_changed" ); if ( descriptor ) { IGameEvent *event = g_GameEventManager.CreateEvent( "hostname_changed" ); if ( event ) { ConVarRef var( pConVar ); event->SetString( "hostname", var.GetString() ); g_GameEventManager.FireEvent( event ); } } } } ConVar host_name( "hostname", "", FCVAR_RELEASE, "Hostname for server.", false, 0.0f, false, 0.0f, HostnameChanged ); ConVar host_map( "host_map", "", FCVAR_RELEASE, "Current map name." ); bool CanShowHostTvStatus() { if ( !serverGameDLL ) return true; if ( serverGameDLL->IsValveDS() ) { // By default OFFICIAL server will NOT print TV information in "status" output // Running with -display_tv_status will reveal GOTV information static bool s_bCanShowHostTvStatusOFFICIAL = !!CommandLine()->FindParm( "-display_tv_status" ); return s_bCanShowHostTvStatusOFFICIAL; } else { // By default COMMUNITY server will print TV information in "status" output // Running with -disable_tv_status will conceal GOTV information static bool s_bCanShowHostTvStatusCOMMUNITY = !CommandLine()->FindParm( "-disable_tv_status" ); return s_bCanShowHostTvStatusCOMMUNITY; } } #ifdef _PS3 ConVar ps3_host_quit_graceperiod( "ps3_host_quit_graceperiod", "7", FCVAR_DEVELOPMENTONLY, "Time granted for save operations to finish" ); ConVar ps3_host_quit_debugpause( "ps3_host_quit_debugpause", "0", FCVAR_DEVELOPMENTONLY, "Time to stall quit for debug purposes" ); #endif ConVar voice_recordtofile("voice_recordtofile", "0", FCVAR_RELEASE, "Record mic data and decompressed voice data into 'voice_micdata.wav' and 'voice_decompressed.wav'"); ConVar voice_inputfromfile("voice_inputfromfile", "0",FCVAR_RELEASE, "Get voice input from 'voice_input.wav' rather than from the microphone."); uint GetSteamAppID() { #ifndef DEDICATED if ( Steam3Client().SteamUtils() ) return Steam3Client().SteamUtils()->GetAppID(); #endif if ( Steam3Server().SteamGameServerUtils() ) return Steam3Server().SteamGameServerUtils()->GetAppID(); return 215; // defaults to Source SDK Base (215) if no steam.inf can be found. } EUniverse GetSteamUniverse() { #ifndef DEDICATED 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( _GAMECONSOLE ) void Host_Quit_f (void); void PS3_sysutil_callback_forwarder( uint64 uiStatus, uint64 uiParam ); void Quit_gameconsole_f( bool bWarmRestart, bool bUnused ) { #if defined( _DEMO ) if ( Host_IsDemoExiting() ) { // for safety, only want to play this under demo exit conditions // which guaranteed us a safe exiting context game->PlayVideoListAndWait( "media/DemoUpsellVids.txt" ); } #endif #if defined( _X360 ) && defined( _DEMO ) // demo version has to support variants of the launch structures // demo version must reply with exact demo launch structure if provided unsigned int launchID; int launchSize; void *pLaunchData; bool bValid = XboxLaunch()->GetLaunchData( &launchID, &pLaunchData, &launchSize ); if ( bValid && Host_IsDemoHostedFromShell() ) { XboxLaunch()->SetLaunchData( pLaunchData, launchSize, LF_UNKNOWNDATA ); g_pMaterialSystem->PersistDisplay(); XBX_DisconnectConsoleMonitor(); const char *pImageName = XLAUNCH_KEYWORD_DEFAULT_APP; if ( launchID == LAUNCH_DATA_DEMO_ID ) { pImageName = ((LD_DEMO*)pLaunchData)->szLauncherXEX; } XboxLaunch()->Launch( pImageName ); return; } #endif #ifdef _X360 // must be first, will cause a reset of the launch if we have never been re-launched // all further XboxLaunch() operations MUST be writes, otherwise reset int launchFlags = LF_EXITFROMGAME; // block until the installer stops g_pXboxInstaller->IsStopped( true ); if ( g_pXboxInstaller->IsFullyInstalled() ) { launchFlags |= LF_INSTALLEDTOCACHE; } // 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 // ... // Collect settings to preserve across restarts int numGameUsers = XBX_GetNumGameUsers(); char slot2ctrlr[4]; char slot2guest[4]; int ctrlr2storage[4]; for ( int k = 0; k < 4; ++ k ) { slot2ctrlr[k] = (char) XBX_GetUserId( k ); slot2guest[k] = (char) XBX_GetUserIsGuest( k ); ctrlr2storage[k] = XBX_GetStorageDeviceId( k ); } // storage device may have changed since previous launch XboxLaunch()->SetStorageID( ctrlr2storage ); // Close the storage devices g_pXboxSystem->CloseAllContainers(); DWORD nUserID = XBX_GetPrimaryUserId(); XboxLaunch()->SetUserID( nUserID ); XboxLaunch()->SetSlotUsers( numGameUsers, slot2ctrlr, slot2guest ); if ( bWarmRestart ) { // a restart is an attempt at a hidden reboot-in-place launchFlags |= LF_WARMRESTART; } // set our own data and relaunch self bool bLaunch = XboxLaunch()->SetLaunchData( pPayload, nPayloadSize, launchFlags ); #if defined( _DEMO ) bLaunch = true; #endif if ( bLaunch ) { // Can't send anything to VXConsole; about to abandon connection // VXConsole tries to respond but can't and throws the timeout crash // COM_TimestampedLog( "Launching: \"%s\" Flags: 0x%8.8x", pCmdLine, XboxLaunch()->GetLaunchFlags() ); g_pMaterialSystem->PersistDisplay(); XBX_DisconnectConsoleMonitor(); #if defined( CSTRIKE15 ) XboxLaunch()->SetLaunchData( NULL, 0, 0 ); XboxLaunch()->Launch( XLAUNCH_KEYWORD_DASH_ARCADE ); #else XboxLaunch()->Launch(); #endif // defined( CSTRIKE15 ) } #elif defined( _PS3 ) // TODO: preserve when a "restart" is requested! Assert( !bWarmRestart ); Assert( !bUnused ); if ( bWarmRestart ) { DevWarning( "TODO: PS3 quit_x360 restart is not implemented yet!\n" ); } // Prevent re-entry static bool s_bQuitPreventReentry = false; if ( s_bQuitPreventReentry ) return; s_bQuitPreventReentry = true; // We must go into single-threaded rendering Host_AllowQueuedMaterialSystem( false ); // Make sure everybody received the EXITGAME callback, might happen multiple times now float const flTimeStampStart = Plat_FloatTime(); float flGracePeriod = ps3_host_quit_graceperiod.GetFloat(); flGracePeriod = MIN( 7, flGracePeriod ); flGracePeriod = MAX( 0, flGracePeriod ); float const flTimeStampForceShutdown = flTimeStampStart + flGracePeriod; uint64 uiLastCountdownNotificationSent = 0; for ( ; ; ) { enum ShutdownSystemsWait_t { kSysSaveRestore, kSysSaveUtilV2, kSysSteamClient, kSysDebugPause, kSysShutdownSystemsCount }; char const *szSystems[kSysShutdownSystemsCount] = {0}; char const *szSystemsRequiredState[kSysShutdownSystemsCount] = {0}; // Poll systems whether they are ready to shutdown if ( saverestore && saverestore->IsSaveInProgress() ) szSystems[kSysSaveRestore] = "saverestore"; extern bool SaveUtilV2_CanShutdown(); if ( !SaveUtilV2_CanShutdown() ) szSystems[kSysSaveUtilV2] = "SaveUtilV2"; if ( Steam3Client().SteamUtils() && !Steam3Client().SteamUtils()->BIsReadyToShutdown() ) szSystems[kSysSteamClient] = "steamclient"; if ( ( ps3_host_quit_debugpause.GetFloat() > 0 ) && ( Plat_FloatTime() < flTimeStampStart + ps3_host_quit_debugpause.GetFloat() ) ) szSystems[kSysDebugPause] = "debugpause"; if ( !Q_memcmp( szSystemsRequiredState, szSystems, sizeof( szSystemsRequiredState ) ) ) { DevMsg( "PS3 shutdown procedure: all systems ready (%.2f sec elapsed)\n", ( Plat_FloatTime() - flTimeStampStart ) ); break; } uint64 uiCountdownNotification = 1 + ( flTimeStampForceShutdown - Plat_FloatTime() ); if ( uiCountdownNotification != uiLastCountdownNotificationSent ) { uiLastCountdownNotificationSent = uiCountdownNotification; PS3_sysutil_callback_forwarder( CELL_SYSUTIL_REQUEST_EXITGAME, uiCountdownNotification ); DevWarning( "PS3 shutdown procedure: %.2f sec elapsed...\n", ( Plat_FloatTime() - flTimeStampStart ) ); int nNotReadySystemsCount = 0; for ( int jj = 0; jj < ARRAYSIZE( szSystems ); ++ jj ) { if ( szSystems[jj] ) { DevWarning( " system not ready : %s\n", szSystems[jj] ); ++ nNotReadySystemsCount; } } DevWarning( "PS3 shutdown procedure: waiting for %d systems to be ready for shutdown (%.2f sec remaining)...\n", nNotReadySystemsCount, ( flTimeStampForceShutdown - Plat_FloatTime() ) ); } if ( Plat_FloatTime() >= flTimeStampForceShutdown ) { DevWarning( "FORCING PS3 SHUTDOWN PROCEDURE: NOT ALL SYSTEMS READY (%.2f sec elapsed)...\n", ( Plat_FloatTime() - flTimeStampStart ) ); break; } // Perform blank vsync'ed flips static ConVarRef mat_vsync( "mat_vsync" ); mat_vsync.SetValue( true ); g_pMaterialSystem->SetFlipPresentFrequency( 1 ); // let it flip every VSYNC, we let interrupt handler throttle this loop to conform with TCR#R092 [no more than 60 fps] // Dummy frame g_pMaterialSystem->BeginFrame( 1.0f/60.0f ); CMatRenderContextPtr pRenderContext; pRenderContext.GetFrom( g_pMaterialSystem ); pRenderContext->ClearColor4ub( 0, 0, 0, 255 ); pRenderContext->ClearBuffers( true, true, true ); pRenderContext.SafeRelease(); g_pMaterialSystem->EndFrame(); g_pMaterialSystem->SwapBuffers(); // Pump system event queue XBX_ProcessEvents(); XBX_DispatchEventsQueue(); } // QUIT Warning( "[PS3 SYSTEM] REQUEST EXITGAME INITIATING QUIT @ %.3f\n", Plat_FloatTime() ); Host_Quit_f(); #else Assert( 0 ); #error #endif } CON_COMMAND( quit_gameconsole, "" ) { Quit_gameconsole_f( args.FindArg( "restart" ) != NULL, args.FindArg( "invite" ) != NULL ); } #endif // store arbitrary launch arguments in KeyValues to avoid having to add code for every new // launch parameter (like edit mode, commentary mode, background, etc. do) void SetLaunchOptions( const CCommand &args ) { if ( g_pLaunchOptions ) { g_pLaunchOptions->deleteThis(); } g_pLaunchOptions = new KeyValues( "LaunchOptions" ); for ( int i = 0 ; i < args.ArgC() ; i++ ) { g_pLaunchOptions->SetString( va("Arg%d", i), args[i] ); } } /* ================== Host_Quit_f ================== */ void Host_Quit_f (void) { #if !defined(DEDICATED) if ( !EngineTool_CheckQuitHandlers() ) { return; } #endif HostState_Shutdown(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CON_COMMAND( _restart, "Shutdown and restart the engine." ) { /* // FIXME: How to handle restarts? #ifndef DEDICATED if ( !EngineTool_CheckQuitHandlers() ) { return; } #endif */ HostState_Restart(); } #ifndef DEDICATED //----------------------------------------------------------------------------- // 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"; if ( nci != NULL ) { print( "# %2i %i \"%s\" %s %s %i %i %s %d", client->GetUserID(), client->GetPlayerSlot() + 1, client->GetClientName(), client->GetNetworkIDString(), COM_FormatSeconds( nci->GetTimeConnected() ), (int)(1000.0f*nci->GetAvgLatency( FLOW_OUTGOING )), (int)(100.0f*nci->GetAvgLoss(FLOW_INCOMING)), state, (int)nci->GetDataRate() ); if ( bShowAddress ) { print( " %s", nci->GetAddress() ); } } else { print( "#%2i \"%s\" %s %s %.0f", client->GetUserID(), client->GetClientName(), client->GetNetworkIDString(), state, client->GetUpdateRate() ); } print( "\n" ); } typedef void ( *FnPrintf_t )(const char *fmt, ...); 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 ); } static void Host_Client_PrintfStub(const char *fmt, ...) { } void Host_PrintStatus( cmd_source_t commandSource, void ( *print )(const char *fmt, ...), bool bShort ) { bool bWithAddresses = ( ( commandSource != kCommandSrcNetClient ) && ( commandSource != kCommandSrcNetServer ) && ( print == ConMsg ) ); // guarantee to never print for remote IClient *client; int j; if ( !print ) { return; } // ============================================================ // 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; /* no such universe anymore case k_EUniverseRC: pchUniverse = "(rc)"; break; */ default: pchUniverse = "(unknown)"; break; } if ( bWithAddresses ) { print( "version : %s/%d %d/%d %s %s %s %s\n", Sys_GetVersionString(), GetHostVersion(), GetServerVersion(), build_number(), bGSSecure ? "secure" : "insecure", pchSecureReasonString, Steam3Server().GetGSSteamID().IsValid() ? Steam3Server().GetGSSteamID().Render() : "[INVALID_STEAMID]", pchUniverse ); } else { print( "version : %s %s\n", Sys_GetVersionString(), bGSSecure ? "secure" : "insecure" ); } if ( NET_IsMultiplayer() ) { CUtlString sPublicIPInfo; if ( !Steam3Server().BLanOnly() ) { uint32 unPublicIP = Steam3Server().GetPublicIP(); if ( ( unPublicIP != 0 ) && bWithAddresses && sv.IsDedicated() ) { 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() ); static ConVarRef sv_steamdatagramtransport_port( "sv_steamdatagramtransport_port" ); if ( bWithAddresses && sv_steamdatagramtransport_port.GetInt() > 0 ) { print( "sdt : =%s on port %d\n", Steam3Server().GetGSSteamID().Render(), sv_steamdatagramtransport_port.GetInt() ); } const char *osType = #if defined( WIN32 ) "Windows"; #elif defined( _LINUX ) "Linux"; #elif defined( PLATFORM_OSX ) "OSX"; #else "Unknown"; #endif print( "os : %s\n", osType ); char const *serverType = sv.IsHLTV() ? "hltv" : ( sv.IsDedicated() ? ( serverGameDLL->IsValveDS() ? "official dedicated" : "community dedicated" ) : "listen" ); print( "type : %s\n", serverType ); } #ifndef DEDICATED // no client on dedicated server if ( !sv.IsDedicated() && GetBaseLocalClient().IsConnected() ) { print( "map : %s at: %d x, %d y, %d z\n", sv.GetMapName(), (int)MainViewOrigin()[0], (int)MainViewOrigin()[1], (int)MainViewOrigin()[2]); } #else { print( "map : %s\n", sv.GetMapName() ); } #endif if ( CanShowHostTvStatus() ) { for ( CActiveHltvServerIterator hltv; hltv; hltv.Next() ) { print( "gotv[%i]: port %i, delay %.1fs, rate %.1f\n", hltv.GetIndex(), hltv->GetUDPPort(), hltv->GetDirector() ? hltv->GetDirector()->GetDelay() : 0.0f, hltv->GetSnapshotRate() ); } } #if defined( REPLAY_ENABLED ) if ( replay && replay->IsActive() ) { print( "replay: port %i, delay %.1fs\n", replay->GetUDPPort(), replay->GetDirector()->GetDelay() ); } #endif int nHumans; int nMaxHumans; int nBots; sv.GetMasterServerPlayerCounts( nHumans, nMaxHumans, nBots ); print( "players : %i humans, %i bots (%i/%i max) (%s)\n\n", nHumans, nBots, nMaxHumans, sv.GetNumGameSlots(), sv.IsHibernating() ? "hibernating" : "not hibernating" ); // ============================================================ #if SUPPORT_NET_CONSOLE if ( g_pNetConsoleMgr && g_pNetConsoleMgr->IsActive() && bWithAddresses ) { print( "netcon : %s:%i\n", net_local_adr.ToString( true), g_pNetConsoleMgr->GetAddress().GetPort() ); } #endif // Early exit for this server. if ( bShort ) { for ( j=0 ; j < sv.GetClientCount() ; j++ ) { client = sv.GetClient( j ); if ( !client->IsActive() ) continue; if ( !bWithAddresses && !CanShowHostTvStatus() && client->IsHLTV() ) continue; print( "#%i - %s\n" , j + 1, client->GetClientName() ); } return; } // the header for the status rows print( "# userid name uniqueid connected ping loss state rate" ); if ( bWithAddresses ) { print( " adr" ); } print( "\n" ); for ( j=0 ; j < sv.GetClientCount() ; j++ ) { client = sv.GetClient( j ); if ( !client->IsConnected() ) continue; // not connected yet, maybe challenging if ( !CanShowHostTvStatus() && client->IsHLTV() ) continue; Host_Status_PrintClient( client, bWithAddresses, print ); } print( "#end\n" ); } //----------------------------------------------------------------------------- // Host_Status_f //----------------------------------------------------------------------------- CON_COMMAND( status, "Display map and connection status." ) { void (*print) (const char *fmt, ...) = Host_Client_PrintfStub; if ( args.Source() != kCommandSrcNetClient ) { if ( !sv.IsActive() ) { Cmd_ForwardToServer( args ); return; } #ifndef DBGFLAG_STRINGS_STRIP print = ConMsg; #endif } else { print = Host_Client_Printf; } bool bShort = false; if ( args.ArgC() == 2 ) { if ( !Q_stricmp( args[1], "short" ) ) { bShort = true; } } Host_PrintStatus( args.Source(), print, bShort ); } CON_COMMAND( hltv_replay_status, "Show Killer Replay status and some statistics, works on listen or dedicated server." ) { HltvReplayStats_t hltvStats; for ( int j = 0; j < sv.GetClientCount(); j++ ) { IClient *client = sv.GetClient( j ); if ( !client->IsConnected() ) continue; // not connected yet, maybe challenging if ( !CanShowHostTvStatus() && client->IsHLTV() ) continue; 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"; if ( nci != NULL ) { ConMsg( "# %2i %i \"%s\" %s %s %s %s", client->GetUserID(), client->GetPlayerSlot() + 1, client->GetClientName(), client->GetNetworkIDString(), COM_FormatSeconds( nci->GetTimeConnected() ), state, client->GetHltvReplayStatus() ); if ( client->GetHltvReplayDelay() ) ConMsg( ", in replay NOW" ); } else { ConMsg( "#%2i \"%s\" %s %s %s", client->GetUserID(), client->GetClientName(), client->GetNetworkIDString(), state, client->GetHltvReplayStatus() ); } if ( CGameClient *pClient = dynamic_cast< CGameClient * >( client ) ) { if ( pClient->m_HltvReplayStats.nStartRequests ) hltvStats += pClient->m_HltvReplayStats; if ( pClient->m_nForceWaitForTick > 0 ) { ConMsg( ", force-waiting for tick %d - server tick %d, current frame %d", pClient->m_nForceWaitForTick, sv.GetTick(), pClient->m_pCurrentFrame ? pClient->m_pCurrentFrame->tick_count : 0 ); } } ConMsg( "\n" ); } extern HltvReplayStats_t m_DisconnectedClientsHltvReplayStats; if ( m_DisconnectedClientsHltvReplayStats.nClients > 1 ) ConMsg( "%u disconnected clients: %s\n", m_DisconnectedClientsHltvReplayStats.nClients - 1, m_DisconnectedClientsHltvReplayStats.AsString() ); if ( hltvStats.nClients > 0 ) { ConMsg( "%u current clients: %s\n", hltvStats.nClients, hltvStats.AsString() ); } } #if defined( _X360 ) CON_COMMAND( vx_mapinfo, "" ) { Vector org; QAngle ang; const char *pName; if ( GetBaseLocalClient().IsActive() ) { pName = GetBaseLocalClient().m_szLevelNameShort; org = MainViewOrigin(); VectorAngles( MainViewForward(), ang ); IClientEntity *localPlayer = entitylist->GetClientEntity( GetBaseLocalClient().m_nPlayerSlot + 1 ); if ( localPlayer ) { org = localPlayer->GetAbsOrigin(); } } else { pName = ""; org.Init(); ang.Init(); } // HACK: This is only relevant for portal2. Msg( "BUG REPORT PORTAL POSITIONS:\n" ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), "portal_report\n" ); // 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]; V_snprintf( mapPath, sizeof( mapPath ), "maps/%s" PLATFORM_EXT ".bsp", pName ); g_pFileSystem->GetLocalPath( mapPath, mapInfo.mapPath, sizeof( mapInfo.mapPath ) ); V_FixSlashes( mapInfo.mapPath ); } else { mapInfo.mapPath[0] = '\0'; } mapInfo.details[0] = '\0'; ConVarRef host_thread_mode( "host_thread_mode" ); ConVarRef mat_queue_mode( "mat_queue_mode" ); ConVarRef snd_surround_speakers( "snd_surround_speakers" ); V_strncat( mapInfo.details, CFmtStr( "Build: %d\n", build_number() ), sizeof( mapInfo.details ) ); XVIDEO_MODE videoMode; XGetVideoMode( &videoMode ); V_strncat( mapInfo.details, CFmtStr( "Display: %dx%d (%s)\n", videoMode.dwDisplayWidth, videoMode.dwDisplayHeight, videoMode.fIsWideScreen ? "widescreen" : "normal" ), sizeof( mapInfo.details ) ); int backbufferWidth, backbufferHeight; materials->GetBackBufferDimensions( backbufferWidth, backbufferHeight ); V_strncat( mapInfo.details, CFmtStr( "BackBuffer: %dx%d\n", backbufferWidth, backbufferHeight ), sizeof( mapInfo.details ) ); // audio info const char *pAudioInfo = "Unknown"; switch ( snd_surround_speakers.GetInt() ) { case 2: pAudioInfo = "Stereo"; break; case 5: pAudioInfo = "5.1 Digital Surround"; break; } V_strncat( mapInfo.details, CFmtStr( "Audio: %s\n", pAudioInfo ), sizeof( mapInfo.details ) ); // ui language V_strncat( mapInfo.details, CFmtStr( "UI: %s\n", cl_language.GetString() ), sizeof( mapInfo.details ) ); // cvars V_strncat( mapInfo.details, CFmtStr( "host_thread_mode: %d\n", host_thread_mode.GetInt() ), sizeof( mapInfo.details ) ); V_strncat( mapInfo.details, CFmtStr( "mat_queue_mode: %d\n", mat_queue_mode.GetInt() ), sizeof( mapInfo.details ) ); XBX_rMapInfo( &mapInfo ); } #elif defined( _PS3 ) #include "ps3/ps3_sn.h" CON_COMMAND( vx_mapinfo, "" ) { Vector org; QAngle ang; const char *pName; if ( GetBaseLocalClient().IsActive() ) { pName = GetBaseLocalClient().m_szLevelNameShort; org = MainViewOrigin(); VectorAngles( MainViewForward(), ang ); IClientEntity *localPlayer = entitylist->GetClientEntity( GetBaseLocalClient().m_nPlayerSlot + 1 ); if ( localPlayer ) { org = localPlayer->GetAbsOrigin(); } } else { pName = ""; org.Init(); ang.Init(); } // HACK: This is only relevant for portal2. Msg( "BUG REPORT PORTAL POSITIONS:\n" ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), "portal_report\n" ); // 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]; V_snprintf( mapPath, sizeof( mapPath ), "maps/%s" PLATFORM_EXT ".bsp", pName ); g_pFileSystem->GetLocalPath( mapPath, mapInfo.mapPath, sizeof( mapInfo.mapPath ) ); V_FixSlashes( mapInfo.mapPath ); } else { mapInfo.mapPath[0] = '\0'; } mapInfo.details[0] = '\0'; ConVarRef host_thread_mode( "host_thread_mode" ); ConVarRef mat_queue_mode( "mat_queue_mode" ); ConVarRef snd_surround_speakers( "snd_surround_speakers" ); V_strncat( mapInfo.details, CFmtStr( "Build: %d\n", build_number() ), sizeof( mapInfo.details ) ); /* XVIDEO_MODE videoMode; XGetVideoMode( &videoMode ); V_strncat( mapInfo.details, CFmtStr( "Display: %dx%d (%s)\n", videoMode.dwDisplayWidth, videoMode.dwDisplayHeight, videoMode.fIsWideScreen ? "widescreen" : "normal" ), sizeof( mapInfo.details ) ); int backbufferWidth, backbufferHeight; materials->GetBackBufferDimensions( backbufferWidth, backbufferHeight ); V_strncat( mapInfo.details, CFmtStr( "BackBuffer: %dx%d\n", backbufferWidth, backbufferHeight ), sizeof( mapInfo.details ) ); */ // audio info const char *pAudioInfo = "Unknown"; switch ( snd_surround_speakers.GetInt() ) { case 2: pAudioInfo = "Stereo"; break; case 5: pAudioInfo = "5.1 Digital Surround"; break; } V_strncat( mapInfo.details, CFmtStr( "Audio: %s\n", pAudioInfo ), sizeof( mapInfo.details ) ); // ui language V_strncat( mapInfo.details, CFmtStr( "UI: %s\n", cl_language.GetString() ), sizeof( mapInfo.details ) ); // cvars V_strncat( mapInfo.details, CFmtStr( "host_thread_mode: %d\n", host_thread_mode.GetInt() ), sizeof( mapInfo.details ) ); V_strncat( mapInfo.details, CFmtStr( "mat_queue_mode: %d\n", mat_queue_mode.GetInt() ), sizeof( mapInfo.details ) ); XBX_rMapInfo( &mapInfo ); } CON_COMMAND( vx_screenshot, "" ) { #if 1 g_pMaterialSystem->TransmitScreenshotToVX( ); #else // COMPILE_TIME_ASSERT( sizeof(g_pfnSwapBufferMarker) == 8); union FunctionPointerIsReallyADescriptor { void (*pFunc_t)(); struct { uint32 funcaddress; int32 iToc; } fn8; }; FunctionPointerIsReallyADescriptor *pBreakpoint = (FunctionPointerIsReallyADescriptor *)g_pfnSwapBufferMarker; // breakpoint.pFunc_t = g_pfnSwapBufferMarker; uint64 uBPAddress; /// Address of a pointer that points to the image in memory char * pFrameBuffer; /// Width of image uint32 uWidth; /// Height of image uint32 uHeight; /// Image pitch (as described in CellGCMSurface) - in bytes uint32 uPitch; /// Image colour settings (0 = X8R8G8B8, 1 = X8B8G8R8, 2 = R16G16B16X16) IMaterialSystem::VRAMScreenShotInfoColor_t colour ; // get one of the screen buffers. Since we breakpoint the game anyway I don't think // it really matters if we're two out of date. (For this test, anyway.) g_pMaterialSystem->GetVRAMScreenShotInfo( &pFrameBuffer, &uWidth, &uHeight, &uPitch, &colour ); g_pValvePS3Console->VRAMDumpingInfo( (uint64)pBreakpoint->fn8.funcaddress, (uint64)pFrameBuffer, uWidth, uHeight, uPitch, colour ); #endif } #endif //----------------------------------------------------------------------------- // Host_Ping_f //----------------------------------------------------------------------------- CON_COMMAND( ping, "Display ping to server." ) { if ( args.Source() != kCommandSrcNetClient ) { Cmd_ForwardToServer( args ); return; } 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() ); } } //----------------------------------------------------------------------------- // Purpose: // Input : editmode - //----------------------------------------------------------------------------- extern void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int maxLength ); 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; } enum EMapFlags { EMAP_NONE = 0, EMAP_EDIT_MODE = (1<<0), EMAP_BACKGROUND = (1<<1), EMAP_COMMENTARY = (1<<2), EMAP_SPLITSCREEN = (1<<3) }; 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[1], commands ) > 0 ) { Q_strncpy( name, &commands[ 0 ][ argv0.Length() ], bufsize ); return true; } return false; } void Host_Changelevel_f( const CCommand &args ); void Host_Map_Helper( const CCommand &args, EMapFlags flags ) { char name[MAX_QPATH]; if (args.ArgC() < 2) { Warning("No map specified\n"); return; } if ( ( sv.IsActive() && !sv.IsSinglePlayerGame() && !sv.IsLevelMainMenuBackground() ) || ( sv.IsActive() && sv.IsDedicated() ) ) { // Using the 'map' command while in a map disconnects all players. // Ease the pain of this common error by forwarding to the correct command. Host_Changelevel_f( args ); return; } bool bBackground = ( flags & EMAP_BACKGROUND ) != 0; bool bSplitScreenConnect = ( flags & EMAP_SPLITSCREEN ) != 0; char ppath[ MAX_QPATH ]; // If there is a .bsp on the end, strip it off! Q_StripExtension( args[ 1 ], ppath, sizeof( ppath ) ); // Call with quiet flag for initial search if ( !modelloader->Map_IsValid( ppath, true ) ) { Host_Map_Helper_FuzzyName( args, ppath, sizeof( ppath ) ); if ( !modelloader->Map_IsValid( ppath ) ) { Warning( "map load failed: %s not found or invalid\n", ppath ); return; } } GetPlatformMapPath( ppath, name, sizeof( name ) ); // If I was in edit mode reload config file // to overwrite WC edit key bindings #if !defined(DEDICATED) bool bCommentary = ( flags & EMAP_COMMENTARY ) != 0; bool bEditmode = ( flags & EMAP_EDIT_MODE ) != 0; if ( !bEditmode ) { if ( g_bInEditMode ) { // Re-read config from disk Host_ReadConfiguration( -1, false ); g_bInEditMode = false; } } else { g_bInEditMode = true; } g_bInCommentaryMode = bCommentary; #endif SetLaunchOptions( args ); if ( !CL_HL2Demo_MapCheck( name ) ) { Warning( "map load failed: %s not found or invalid\n", name ); return; } if ( !CL_PortalDemo_MapCheck( name ) ) { Warning( "map load failed: %s not found or invalid\n", name ); return; } #ifdef DEDICATED if ( sv.IsDedicated() ) #else // Stop demo loop GetBaseLocalClient().demonum = -1; if ( GetBaseLocalClient().m_nMaxClients == 0 || sv.IsDedicated() ) #endif { Host_Disconnect( false ); // stop old game HostState_NewGame( name, false, bBackground, bSplitScreenConnect ); } 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); } } } // Handle a map command from the console. Active clients are kicked off. void Host_Map_f( const CCommand &args ) { Host_Map_Helper( args, (EMapFlags)0 ); } // Handle a map group command from the console void Host_MapGroup_f( const CCommand &args ) { if ( args.ArgC() < 2 ) { Warning( "Host_MapGroup_f: No mapgroup specified\n" ); return; } Msg( "Setting mapgroup to '%s'\n", args[1] ); HostState_SetMapGroupName( args[1] ); } // Handle smap command to connect multiple splitscreen users at once void Host_SplitScreen_Map_f( const CCommand &args ) { #ifndef _DEMO Host_Map_Helper( args, EMAP_SPLITSCREEN ); #endif } //----------------------------------------------------------------------------- // handle a map_edit command from the console. // Active clients are kicked off. // UNDONE: protect this from use if not in dev. mode //----------------------------------------------------------------------------- #ifndef DEDICATED CON_COMMAND( map_edit, "" ) { Host_Map_Helper( args, EMAP_EDIT_MODE ); } #endif //----------------------------------------------------------------------------- // Purpose: Runs a map as the background //----------------------------------------------------------------------------- void Host_Map_Background_f( const CCommand &args ) { Host_Map_Helper( args, EMAP_BACKGROUND ); } //----------------------------------------------------------------------------- // Purpose: Runs a map in commentary mode //----------------------------------------------------------------------------- void Host_Map_Commentary_f( const CCommand &args ) { Host_Map_Helper( args, EMAP_COMMENTARY ); } //----------------------------------------------------------------------------- // 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(DEDICATED) demoplayer->IsPlayingBack() || #endif !sv.IsActive() ) return; if ( sv.IsMultiplayer() ) return; bool bRememberLocation = ( args.ArgC() == 2 && !Q_stricmp( args[1], "setpos" ) ); bool bSplitScreenConnect = GET_NUM_SPLIT_SCREEN_PLAYERS() == 2 ; 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, bSplitScreenConnect ); } //----------------------------------------------------------------------------- // 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 DEDICATED const char *pSaveName; char name[MAX_OSPATH]; #endif if ( #if !defined(DEDICATED) demoplayer->IsPlayingBack() || #endif !sv.IsActive() ) return; if ( sv.IsMultiplayer() ) return; if ( !serverGameDLL->SupportsSaveRestore() ) 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 DEDICATED pSaveName = saverestore->FindRecentSave( name, sizeof( name ) ); // Put up loading plaque SCR_BeginLoadingPlaque(); { // Prepare the offline session for server reload KeyValues *pEvent = new KeyValues( "OnEngineClientSignonStatePrepareChange" ); pEvent->SetString( "reason", "reload" ); g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( pEvent ); } Host_Disconnect( false ); // stop old game if ( pSaveName && saverestore->SaveFileExists( pSaveName ) ) { HostState_LoadGame( pSaveName, remember_location, false ); } 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; } #if !defined( DEDICATED ) if ( pSaveName && pSaveName[0] ) { Warning( "SAVERESTORE PROBLEM: %s not found! Starting new game in %s\n", pSaveName, host_map.GetString() ); } #endif HostState_NewGame( host_map.GetString(), remember_location, false, 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 mapname[MAX_PATH]; Q_StripExtension( args[ 1 ], mapname, sizeof( mapname ) ); bool bMapMustExist = true; static ConVarRef sv_workshop_allow_other_maps( "sv_workshop_allow_other_maps" ); if ( StringHasPrefix( mapname, "workshop" ) && ( ( mapname[8] == '/' ) || ( mapname[8] == '\\' ) ) && sv_workshop_allow_other_maps.GetBool() ) bMapMustExist = false; if ( bMapMustExist && !modelloader->Map_IsValid( mapname, true ) ) { Host_Map_Helper_FuzzyName( args, mapname, sizeof( mapname ) ); if ( !modelloader->Map_IsValid( mapname ) ) { Warning( "changelevel failed: %s not found\n", mapname ); return; } } if ( !CL_HL2Demo_MapCheck( mapname ) ) { Warning( "changelevel failed: %s not found\n", mapname ); return; } if ( !CL_PortalDemo_MapCheck( mapname ) ) { Warning( "changelevel failed: %s not found\n", mapname ); return; } SetLaunchOptions( args ); HostState_ChangeLevelMP( mapname, 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() ) { ConMsg( "Can't changelevel2, not in a map\n" ); return; } if ( !g_pVEngineServer->IsMapValid( args[1] ) ) { if ( !CL_IsHL2Demo() || (CL_IsHL2Demo() && !(!Q_stricmp( args[1], "d1_trainstation_03" ) || !Q_stricmp( args[1], "d1_town_02a" ))) ) { Warning( "changelevel2 failed: %s not found\n", args[1] ); return; } } #if !defined(DEDICATED) // 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( args[1], "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( args[1], "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( args[1], "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( args[1] ) ) { Warning( "changelevel failed: %s not found\n", args[1] ); return; } SetLaunchOptions( args ); HostState_ChangeLevelSP( args[1], args[2] ); } // On PS/3, due to Matchmaking framework event architecture, Host_Disconnect is called recursively on quit. // Bad things happen. Since it's not really necessary to disconnect recursively, we'll track and prevent it on PS/3 during shutdown. int g_nHostDisconnectReentrancyCounter = 0; class CDisconnectReentrancyCounter { public: CDisconnectReentrancyCounter() { g_nHostDisconnectReentrancyCounter++ ;} ~CDisconnectReentrancyCounter() { g_nHostDisconnectReentrancyCounter-- ;} }; //----------------------------------------------------------------------------- // Purpose: Shut down client connection and any server //----------------------------------------------------------------------------- void Host_Disconnect( bool bShowMainMenu ) { #ifdef _PS3 if ( GetTLSGlobals()->bNormalQuitRequested ) { if ( g_nHostDisconnectReentrancyCounter != 0 ) { return; // do not disconnect recursively on QUIT } } #endif IGameEvent *disconnectEvent = g_GameEventManager.CreateEvent( "cs_game_disconnected" ); if ( disconnectEvent ) g_GameEventManager.FireEventClientSide( disconnectEvent ); CDisconnectReentrancyCounter autoReentrancyCounter; #if !defined( DEDICATED ) if ( bShowMainMenu ) { // exiting game // ensure commentary state gets cleared g_bInCommentaryMode = false; } #endif if ( IsGameConsole() ) { g_pQueuedLoader->EndMapLoading( false ); } // Switch to single-threaded rendering during shutdown to // avoid synchronization issues between destructed objects // and the renderer Host_AllowQueuedMaterialSystem( false ); #ifndef DEDICATED if ( !sv.IsDedicated() ) { FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh ) { ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh ); GetLocalClient().Disconnect( bShowMainMenu ); } } #endif #if !defined( DEDICATED ) if ( g_ClientDLL && bShowMainMenu ) { // forcefully stop any of the full screen video panels used for loading or whatever // this is a safety precaution to ensure we don't orpan any of the hacky global video panels g_ClientDLL->ShutdownMovies(); } #endif HostState_GameShutdown(); #ifndef DEDICATED if ( !sv.IsDedicated() ) { if ( bShowMainMenu && !engineClient->IsDrawingLoadingImage() && ( GetBaseLocalClient().demonum == -1 ) ) { if ( IsGameConsole() ) { // Reset larger configuration material system memory (for map) back down for ui work // This must be BEFORE ui gets-rectivated below materials->ResetTempHWMemory( true ); } #ifdef _PS3 if ( GetTLSGlobals()->bNormalQuitRequested ) { return; // do not disconnect recursively on QUIT } #endif EngineVGui()->ActivateGameUI(); } } #endif } //----------------------------------------------------------------------------- // Kill the client and any local server. //----------------------------------------------------------------------------- CON_COMMAND_F( disconnect, "Disconnect game from server.", FCVAR_SERVER_CAN_EXECUTE ) { #ifndef DEDICATED GetBaseLocalClient().demonum = -1; #endif if ( args.ArgC() > 1 ) { COM_ExplainDisconnection( false, "%s", args[ 1 ] ); } Host_Disconnect(true); } CON_COMMAND( version, "Print version info string." ) { ConMsg( "Protocol version %i [%i/%i]\nExe version %s (%s)\n", GetHostVersion(), GetServerVersion(), GetClientVersion(), Sys_GetVersionString(), Sys_GetProductString() ); ConMsg( "Exe build: " __TIME__ " " __DATE__ " (%i) (%i)\n", build_number(), GetSteamAppID() ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CON_COMMAND( pause, "Toggle the server pause state." ) { #if !defined( CLIENT_DLL ) #ifndef DEDICATED if ( !sv.IsDedicated() ) { if ( !GetBaseLocalClient().m_szLevelName[ 0 ] ) return; } #endif if ( !sv.IsPausable() ) return; // toggle paused state sv.SetPaused( !sv.IsPaused() ); // send text message who paused the game if ( host_client ) sv.BroadcastPrintf( "%s %s the game\n", host_client->GetClientName(), sv.IsPaused() ? "paused" : "unpaused" ); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CON_COMMAND( setpause, "Set the pause state of the server." ) { #if !defined( CLIENT_DLL ) #ifndef DEDICATED if ( !sv.IsDedicated() ) { if ( !GetBaseLocalClient().m_szLevelName[ 0 ] ) return; } #endif if ( !sv.IsPausable() ) return; sv.SetPaused( true ); if ( !args.FindArg( "nomsg" ) ) { // send text message who paused the game if ( host_client ) sv.BroadcastPrintf( "%s paused the game\n", host_client->GetClientName() ); } #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CON_COMMAND( unpause, "Unpause the game." ) { #if !defined( CLIENT_DLL ) #ifndef DEDICATED if ( !sv.IsDedicated() ) { if ( !GetBaseLocalClient().m_szLevelName[ 0 ] ) return; } #endif if ( !sv.IsPaused() ) return; sv.SetPaused( false ); if ( !args.FindArg( "nomsg" ) ) { // send text messaage who unpaused the game if ( host_client ) sv.BroadcastPrintf( "%s unpaused the game\n", host_client->GetClientName() ); } #endif } //----------------------------------------------------------------------------- // Kicks a user off of the server using their userid or uniqueid //----------------------------------------------------------------------------- CON_COMMAND( kickid_ex, "Kick a player by userid or uniqueid, provide a force-the-kick flag and also assign a message." ) { const char *pszArg1 = NULL, *pszMessage = NULL; int iSearchIndex = -1; char szSearchString[128]; int argsStartNum = 1; bool bSteamID = false; bool bForce = false; if ( args.ArgC() <= 1 ) { ConMsg( "Usage: kickid_ex < userid | uniqueid > < force ( 0 / 1 ) > { message }\n" ); return; } // get the first argument pszArg1 = args[1]; // if the first letter is a charcter then // we're searching for a uniqueid ( e.g. STEAM_ ) if ( *pszArg1 < '0' || *pszArg1 > '9' ) { // SteamID (need to reassemble it) if ( StringHasPrefix( pszArg1, 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 game type and game mode if ( args.ArgC() > argsStartNum ) { if ( atoi( args[ argsStartNum + 1 ] ) == 1 ) { bForce = true; } argsStartNum++; } // 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; } } PerformKick( args.Source(), iSearchIndex, szSearchString, bForce, pszMessage ); } //----------------------------------------------------------------------------- // 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." ) { const char *pszArg1 = NULL, *pszMessage = NULL; int iSearchIndex = -1; char szSearchString[128]; int argsStartNum = 1; bool bSteamID = false; 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 charcter then // we're searching for a uniqueid ( e.g. STEAM_ ) if ( *pszArg1 < '0' || *pszArg1 > '9' ) { // SteamID (need to reassemble it) if ( StringHasPrefix( pszArg1, 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; } } PerformKick( args.Source(), iSearchIndex, szSearchString, false, pszMessage ); } void PerformKick( cmd_source_t commandSource, int iSearchIndex, char* szSearchString, bool bForceKick, const char* pszMessage ) { IClient *client = NULL; char *who = "Console"; // find this client int i; for ( i = 0; i < sv.GetClientCount(); i++ ) { client = sv.GetClient( i ); if ( !client->IsConnected() ) { 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 ( client->IsSplitScreenUser() && client->GetSplitScreenOwner() ) { client = client->GetSplitScreenOwner(); } if ( commandSource == kCommandSrcNetClient ) { who = host_client->m_Name; } if ( host_client == client && !sv.IsDedicated() && !bForceKick ) { // can't kick yourself! return; } if ( iSearchIndex != -1 || !client->IsFakeClient() ) { if ( pszMessage ) { client->Disconnect( CFmtStr( "Kicked by %s : %s", who, pszMessage ) ); } else { client->Disconnect( CFmtStr( "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 = "Console"; 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; // found! if ( Q_strcasecmp( client->GetClientName(), pszName ) == 0 ) break; } // now kick them if ( i < sv.GetClientCount() ) { if ( client->IsSplitScreenUser() && client->GetSplitScreenOwner() ) { client = client->GetSplitScreenOwner(); } if ( args.Source() == kCommandSrcNetClient ) { who = host_client->m_Name; } // can't kick yourself! if ( host_client == client && !sv.IsDedicated() ) return; client->Disconnect( CFmtStr( "Kicked by %s", who ) ); } else { ConMsg( "Can't kick \"%s\", name not found\n", pszName ); } } } /* =============================================================================== DEBUGGING TOOLS =============================================================================== */ void Host_PrintMemoryStatus( const char *mapname ) { const float MB = 1.0f / ( 1024*1024 ); Assert( mapname ); #ifdef PLATFORM_LINUX struct mallinfo memstats = mallinfo( ); Msg( "[MEMORYSTATUS] [%s] Operating system reports sbrk size: %.2f MB, Used: %.2f MB, #mallocs = %d\n", mapname, MB*memstats.arena, MB*memstats.uordblks, memstats.hblks ); #elif defined(PLATFORM_OSX) struct mstats stats = mstats(); Msg( "[MEMORYSTATUS] [%s] Operating system reports Used: %.2f MB, Free: %.2f Total: %.2f\n", mapname, MB*stats.bytes_used, MB*stats.bytes_free, MB*stats.bytes_total ); #elif defined( _PS3 ) // NOTE: for PS3 nFreeMemory can be negative (on a devkit, we can use more memory than a retail kit has) int nUsedMemory, nFreeMemory, nAvailable; g_pMemAlloc->GlobalMemoryStatus( (size_t *)&nUsedMemory, (size_t *)&nFreeMemory ); nAvailable = nUsedMemory + nFreeMemory; Msg( "[MEMORYSTATUS] [%s] Operating system reports Available: %.2f MB, Used: %.2f MB, Free: %.2f MB\n", mapname, MB*nAvailable, MB*nUsedMemory, MB*nFreeMemory ); #elif defined(PLATFORM_WINDOWS) MEMORYSTATUSEX statex; statex.dwLength = sizeof(statex); GlobalMemoryStatusEx( &statex ); Msg( "[MEMORYSTATUSEX] [%s] Operating system reports Physical Available: %.2f MB, Physical Used: %.2f MB, Physical Free: %.2f MB\n Virtual Size: %.2f, Virtual Free: %.2f MB, PageFile Size: %.2f, PageFile Free: %.2f MB\n", mapname, MB*statex.ullTotalPhys, MB*( statex.ullTotalPhys - statex.ullAvailPhys ), MB*statex.ullAvailPhys, MB*statex.ullTotalVirtual, MB*statex.ullAvailVirtual, MB*statex.ullTotalPageFile, MB*statex.ullAvailPageFile ); #endif if ( IsPS3() ) { // Include stats on GPU memory usage GPUMemoryStats stats; materials->GetGPUMemoryStats( stats ); g_pMemAlloc->SetStatsExtraInfo( mapname, CFmtStr( "%d %d %d %d %d %d %d", stats.nGPUMemSize, stats.nGPUMemFree, stats.nTextureSize, stats.nRTSize, stats.nVBSize, stats.nIBSize, stats.nUnknown ) ); Msg( "[MEMORYSTATUS] [%s] RSX memory: total %.1fkb, free %.1fkb, textures %.1fkb, render targets %.1fkb, vertex buffers %.1fkb, index buffers %.1fkb, unknown %.1fkb\n", mapname, stats.nGPUMemSize/1024.0f, stats.nGPUMemFree/1024.0f, stats.nTextureSize/1024.0f, stats.nRTSize/1024.0f, stats.nVBSize/1024.0f, stats.nIBSize/1024.0f, stats.nUnknown/1024.0f ); } else { g_pMemAlloc->SetStatsExtraInfo( mapname, "" ); } int nTotal = g_pMemAlloc->GetSize( 0 ); if (nTotal == -1) { Msg( "Internal heap corrupted!\n" ); } else { Msg( "Internal heap reports: %5.2f MB (%d bytes)\n", nTotal/(1024.0f*1024.0f), nTotal ); } Msg( "\nHunk Memory Used:\n" ); Hunk_Print(); Msg( "\nDatacache reports:\n" ); g_pDataCache->OutputReport( DC_SUMMARY_REPORT, NULL ); } //----------------------------------------------------------------------------- // Dump memory stats //----------------------------------------------------------------------------- CON_COMMAND( memory, "Print memory stats." ) { ConMsg( "Heap Used:\n" ); int nTotal = g_pMemAlloc->GetSize( 0 ); if (nTotal == -1) { ConMsg( "Corrupted!\n" ); } else { ConMsg( "%5.2f MB (%d bytes)\n", nTotal/(1024.0f*1024.0f), nTotal ); } #ifdef VPROF_ENABLED ConMsg("\nVideo Memory Used:\n"); CVProfile *pProf = &g_VProfCurrentProfile; int prefixLen = V_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 ( StringHasPrefix( pName, "TexGroup_Global_" ) ) { 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 DEDICATED //MOTODO move all demo commands to demoplayer //----------------------------------------------------------------------------- // Purpose: Gets number of valid demo names // Output : int //----------------------------------------------------------------------------- int Host_GetNumDemos() { int c = 0; #ifndef DEDICATED for ( int i = 0; i < MAX_DEMOS; ++i ) { const char *demoname = GetBaseLocalClient().demos[ i ]; if ( !demoname[ 0 ] ) break; ++c; } #endif return c; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Host_PrintDemoList() { int count = Host_GetNumDemos(); #ifndef DEDICATED int next = GetBaseLocalClient().demonum; if ( next >= count || next < 0 ) { next = 0; } for ( int i = 0; i < MAX_DEMOS; ++i ) { const char *demoname = GetBaseLocalClient().demos[ i ]; if ( !demoname[ 0 ] ) break; bool isnextdemo = next == i ? true : false; DevMsg( "%3s % 2i : %20s\n", isnextdemo ? "-->" : " ", i, GetBaseLocalClient().demos[ i ] ); } #endif if ( !count ) { DevMsg( "No demos in list, use startdemos to specify\n" ); } } #ifndef DEDICATED //----------------------------------------------------------------------------- // // 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 { GetBaseLocalClient().demonum = -1; } } //----------------------------------------------------------------------------- // Purpose: Return to looping demos, optional resume demo index //----------------------------------------------------------------------------- CON_COMMAND( demos, "Demo demo file sequence." ) { CClientState &cl = GetBaseLocalClient(); 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 ] ); } } 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 ) { GetBaseLocalClient().demonum = clamp( Q_atoi( args[1] ), 0, numdemos - 1 ); DevMsg( "Jumping to %s\n", GetBaseLocalClient().demos[ GetBaseLocalClient().demonum ] ); } } 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( atof(args[1]), 0.0f, 100.0f ); holdTime = MAX( 0.0f, atof(args[2]) ); inTime = 0.0f; outTime = 0.0f; if (args.ArgC() == 5) { outTime = MAX( 0.0f, atof(args[3]) ); inTime = MAX( 0.0f, atof( args[4]) ); } S_SoundFade( percent, holdTime, outTime, inTime ); } #endif // !DEDICATED #endif //----------------------------------------------------------------------------- // Shutdown the server //----------------------------------------------------------------------------- CON_COMMAND( killserver, "Shutdown the server." ) { Host_Disconnect(true); if ( !sv.IsDedicated() ) { // close network sockets and reopen if multiplayer game NET_SetMultiplayer( false ); NET_SetMultiplayer( !!( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_SETTING_MULTIPLAYER ) ); } } // [hpe:jason] Enable ENGINE_VOICE for Cstrike 1.5, all platforms #if defined( CSTRIKE15 ) ConVar voice_vox( "voice_vox", "false", FCVAR_DEVELOPMENTONLY ); // Controls open microphone (no push to talk) settings #undef NO_ENGINE_VOICE #else #define NO_ENGINE_VOICE #endif #ifdef NO_ENGINE_VOICE ConVar voice_ptt( "voice_ptt", "-1.0", FCVAR_DEVELOPMENTONLY ); // Time when ptt key was released, 0 means to keep transmitting voice #endif #if !defined(DEDICATED) void Host_VoiceRecordStart_f(void) { #ifdef NO_ENGINE_VOICE voice_ptt.SetValue( 0 ); #else ConVarRef voice_vox( "voice_vox" ); if ( voice_vox.GetBool() == true ) return; int iSsSlot = GET_ACTIVE_SPLITSCREEN_SLOT(); if ( GetLocalClient( iSsSlot ).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 !defined( NO_VOICE ) if (Voice_RecordStart(pUncompressedFile, pDecompressedFile, pInputFile)) { } #endif } #endif // #ifndef NO_ENGINE_VOICE } void Host_VoiceRecordStop_f( const CCommand &args ) { #ifdef NO_ENGINE_VOICE voice_ptt.SetValue( (float) Plat_FloatTime() ); #else ConVarRef voice_vox( "voice_vox" ); if ( voice_vox.GetBool() == true ) return; int iSsSlot = GET_ACTIVE_SPLITSCREEN_SLOT(); if ( GetLocalClient( iSsSlot ).IsActive() ) { #if !defined( NO_VOICE ) if (Voice_IsRecording()) { CL_SendVoicePacket(true); Voice_RecordStop(); } if ( args.ArgC() == 2 && V_strcasecmp( args[1], "force" ) == 0 ) { // do nothing } else { voice_vox.SetValue( 0 ); } #endif } #endif // #ifndef NO_ENGINE_VOICE } #endif // TERROR: adding a toggle for voice void Host_VoiceToggle_f( const CCommand &args ) { #ifdef NO_ENGINE_VOICE voice_ptt.SetValue( (float) ( voice_ptt.GetFloat() ? 0.0f : Plat_FloatTime() ) ); #endif #if !defined( DEDICATED ) && !defined( NO_ENGINE_VOICE ) #if !defined( NO_VOICE ) if ( GetBaseLocalClient().IsActive() ) { bool bToggle = false; if ( args.ArgC() == 2 && V_strcasecmp( args[1], "on" ) == 0 ) { bToggle = true; } if ( Voice_IsRecording() && bToggle == false ) { CL_SendVoicePacket(true); Voice_RecordStop(); } else if ( bToggle == true && Voice_IsRecording() == false ) { 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"; } Voice_RecordStart( pUncompressedFile, pDecompressedFile, pInputFile ); } } #endif #endif } //----------------------------------------------------------------------------- // 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( Cbuf_GetCurrentPlayer(), 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( Cbuf_GetCurrentPlayer(), va("%s %f", varName, newValue) ); ConDMsg( "%s = %f\n", var->GetName(), newValue ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CON_COMMAND( dumpstringtables, "Print string tables to console." ) { SV_PrintStringTables(); #ifndef DEDICATED CL_PrintStringTables(); #endif } CON_COMMAND( stringtabledictionary, "Create dictionary for current strings." ) { if ( !sv.IsActive() ) { Warning( "stringtabledictionary: only valid when running a map\n" ); return; } SV_CreateDictionary( sv.GetMapName() ); } // Register shared commands CON_COMMAND_F( quit, "Exit the engine.", FCVAR_NONE ) { if ( args.ArgC() > 1 && V_strcmp( args[ 1 ], "prompt" ) == 0 ) { Cbuf_AddText( Cbuf_GetCurrentPlayer(), "quit_prompt" ); return; } Host_Quit_f(); } static ConCommand cmd_exit("exit", Host_Quit_f, "Exit the engine."); #ifndef DEDICATED #ifdef VOICE_OVER_IP static ConCommand startvoicerecord("+voicerecord", Host_VoiceRecordStart_f); static ConCommand endvoicerecord("-voicerecord", Host_VoiceRecordStop_f); static ConCommand togglevoicerecord("voicerecord_toggle", Host_VoiceToggle_f); #endif // VOICE_OVER_IP #endif //----------------------------------------------------------------------------- // Purpose: Force a null pointer crash. useful for testing minidumps //----------------------------------------------------------------------------- CON_COMMAND_F( crash, "Cause the engine to crash (Debug!!)", FCVAR_CHEAT ) { Msg( "forcing crash\n" ); #if defined( _X360 ) DmCrashDump( FALSE ); #else char *p = 0; *p = 0; #endif } CON_COMMAND_F( spincycle, "Cause the engine to spincycle (Debug!!)", FCVAR_CHEAT ) { if ( args.ArgC() > 1 ) { const char *pParam = args.Arg( 1 ); if ( pParam && *pParam && pParam[ V_strlen( pParam ) - 1 ] == 's' ) { float flSeconds = V_atof( pParam ); if ( flSeconds > 0 ) { Msg( "Sleeping for %.3f seconds\n", flSeconds ); ThreadSleep( flSeconds * 1000 ); return; } } } int numCycles = ( args.ArgC() > 1 ) ? Q_atoi( args.Arg(1) ) : 10; Msg( "forcing spincycle for %d cycles\n", numCycles ); for( int k = 0; k < numCycles; ++ k ) { ( void ) RandomInt( 0, numCycles ); } } #if POSIX CON_COMMAND_F( forktest, "Cause the engine to fork and wait for child PID, parameter can be passed for requested exit code (Debug!!)", FCVAR_CHEAT ) { EndWatchdogTimer(); // End the watchdog in case child takes too long int nExitCodeRequested = ( args.ArgC() > 1 ) ? Q_atoi( args.Arg(1) ) : 0; pid_t pID = fork(); Msg( "forktest: Forked, pID = %d\n", (int) pID ); if ( pID == 0 ) // are we the forked child? { // // Enumerate all open file descriptors that are not #0 (stdin), #1 (stdout), #2 (stderr) // and close them all. // This will close all sockets and file handles that can result in process hanging // when network events occur on the machine and make NFS handles go bad. // if ( !CommandLine()->FindParm( "-forkfdskeepall" ) ) { FileFindHandle_t hFind = NULL; CUtlVector< int > arrHandlesToClose; for ( char const *szFileName = g_pFullFileSystem->FindFirst( "/proc/self/fd/*", &hFind ); szFileName && *szFileName; szFileName = g_pFullFileSystem->FindNext( hFind ) ) { int iFdHandle = Q_atoi( szFileName ); if ( ( iFdHandle > 2 ) && ( arrHandlesToClose.Find( iFdHandle ) == arrHandlesToClose.InvalidIndex() ) ) arrHandlesToClose.AddToTail( iFdHandle ); } g_pFullFileSystem->FindClose( hFind ); FOR_EACH_VEC( arrHandlesToClose, idxFd ) { ::close( arrHandlesToClose[idxFd] ); } if ( !CommandLine()->FindParm( "-forkfdskeepstd" ) ) { // Explicitly close #0 (stdin), #1 (stdout), #2 (stderr) and reopen them to /dev/null to consume 0-1-2 FDs (Posix spec requires to return lowest FDs first) ::close( 0 ); ::close( 1 ); ::close( 2 ); ::open("/dev/null", O_RDONLY); ::open("/dev/null", O_RDWR); ::open("/dev/null", O_RDWR); } } Msg( "Child finished successfully!\n" ); syscall( SYS_exit, nExitCodeRequested ); // don't do a normal c++ exit, don't want to call destructors, etc. Warning( "Forked child just called SYS_exit.\n" ); } else { int nRet = -1; int nWait = waitpid( pID, &nRet, 0 ); Msg( "Parent finished wait: %d, ret: %d, exit: %d, code: %d\n", nWait, nRet, WIFEXITED( nRet ), WEXITSTATUS( nRet ) ); } } #endif CON_COMMAND_F( flush, "Flush unlocked cache memory.", FCVAR_CHEAT ) { #if !defined( DEDICATED ) g_ClientDLL->InvalidateMdlCache(); #endif // DEDICATED serverGameDLL->InvalidateMdlCache(); g_pDataCache->Flush( true ); #if !defined( DEDICATED ) wavedatacache->Flush(); #endif } CON_COMMAND_F( flush_locked, "Flush unlocked and locked cache memory.", FCVAR_CHEAT ) { #if !defined( DEDICATED ) g_ClientDLL->InvalidateMdlCache(); #endif // DEDICATED serverGameDLL->InvalidateMdlCache(); g_pDataCache->Flush( false ); #if !defined( DEDICATED ) wavedatacache->Flush(); #endif } 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 ); } #if defined( _X360 ) CON_COMMAND( vx_datacache_list, "vx_datacache_list" ) { g_pDataCache->OutputReport( DC_DETAIL_REPORT_VXCONSOLE, NULL ); } #endif #ifndef _DEMO #ifndef DEDICATED // NOTE: As of shipping the 360 version of L4D, this command will not work correctly. See changelist 612757 (terror src) for why. CON_COMMAND_F( ss_connect, "If connected with available split screen slots, connects a split screen player to this machine.", FCVAR_DEVELOPMENTONLY ) { if ( host_state.max_splitscreen_players == 1 ) { if ( toolframework->InToolMode() ) { Msg( "Can't ss_connect, split screen not supported when running -tools mode.\n" ); } else { Msg( "Can't ss_connect, game does not support split screen.\n" ); } return; } if ( !GetBaseLocalClient().IsConnected() ) { Msg( "Can't ss_connect, not connected to game.\n" ); return; } int nSlot = 1; #ifndef DEDICATED while ( splitscreen->IsValidSplitScreenSlot( nSlot ) ) { ++nSlot; } #endif if ( nSlot >= host_state.max_splitscreen_players ) { Msg( "Can't ss_connect, no more split screen player slots!\n" ); return; } // Grab convars for next available slot CCLCMsg_SplitPlayerConnect_t msg; Host_BuildUserInfoUpdateMessage( nSlot, msg.mutable_convars(), false ); GetBaseLocalClient().m_NetChannel->SendNetMsg( msg ); } CON_COMMAND_F( ss_disconnect, "If connected with available split screen slots, connects a split screen player to this machine.", FCVAR_DEVELOPMENTONLY ) { if ( args.Source() == kCommandSrcNetClient ) { #ifndef DEDICATED host_client->SplitScreenDisconnect( args ); #endif return; } // Get the first valid slot int nSlot = -1; for ( int i = 1; i < host_state.max_splitscreen_players; ++i ) { if ( IS_VALID_SPLIT_SCREEN_SLOT( i ) ) { nSlot = i; break; } } if ( args.ArgC() > 1 ) { int cmdslot = Q_atoi( args.Arg( 1 ) ); if ( IS_VALID_SPLIT_SCREEN_SLOT( cmdslot ) ) { nSlot = cmdslot; } else { Msg( "Can't ss_disconnect, slot %d not active\n", cmdslot ); return; } } if ( ! IS_VALID_SPLIT_SCREEN_SLOT( nSlot ) ) { Msg( "Can't ss_disconnect, no split screen users active\n" ); return; } char buf[ 256 ]; Q_snprintf( buf, sizeof( buf ), "ss_disconnect %d\n", nSlot ); CCommand argsClient; argsClient.Tokenize( buf, kCommandSrcCode ); Cmd_ForwardToServer( argsClient ); #ifndef DEDICATED splitscreen->SetDisconnecting( nSlot, true ); #endif } #endif #endif #if 0 CON_COMMAND_F( infinite_loop, "Hang server with an infinite loop to test crash recovery.", FCVAR_CHEAT ) { for(;;) { ThreadSleep( 500 ); } } CON_COMMAND_F( null_ptr_references, "Produce a null ptr reference.", FCVAR_CHEAT ) { *((int *) 0 ) = 77; } #endif