//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // //===========================================================================// #include "tier0/fasttimer.h" #ifdef _WIN32 #if !defined(_X360) #include "winlite.h" #endif #include "tier0/memdbgon.h" // needed because in release builds crtdbg.h is handled specially if USE_MEM_DEBUG is defined #include "tier0/memdbgoff.h" #include // For getting at current heap size // For maximum compatibility use PSAPI v1 // #define PSAPI_VERSION 1 // #include "psapi.h" // #pragma comment(lib, "psapi.lib") #endif #include "tier1/fmtstr.h" #include "vstdlib/jobthread.h" #include "vstdlib/random.h" #include "server.h" #include "host_jmp.h" #include "screen.h" #include "keys.h" #include "cdll_int.h" #include "eiface.h" #include "sv_main.h" #include "master.h" #include "sv_log.h" #include "shadowmgr.h" #include "zone.h" #include "gl_cvars.h" #include "sv_filter.h" #include "ivideomode.h" #include "vprof_engine.h" #include "iengine.h" #include "matchmaking/imatchframework.h" #include "tier2/tier2.h" #include "enginethreads.h" #include "steam/steam_api.h" #include "LoadScreenUpdate.h" #include "datacache/idatacache.h" #include "profile.h" #include "dbginput.h" #include "filesystem.h" #include "tier0/microprofiler.h" #include "checksum_sha1.h" #if !defined DEDICATED #include "voice.h" #include "sound.h" #include "vaudio/ivaudio.h" #endif #include "icvar.h" #include "sys.h" #include "client.h" #include "cl_pred.h" #include "netconsole.h" #include "console.h" #include "view.h" #include "host.h" #include "decal.h" #include "gl_matsysiface.h" #include "gl_shader.h" #include "sys_dll.h" #include "cmodel_engine.h" #ifndef DEDICATED #include "con_nprint.h" #endif #include "filesystem.h" #include "filesystem_engine.h" #include "traceinit.h" #include "host_saverestore.h" #include "l_studio.h" #include "cl_demo.h" #include "cdll_engine_int.h" #include "host_cmd.h" #include "host_state.h" #include "dt_instrumentation.h" #include "dt_instrumentation_server.h" #include "const.h" #include "bitbuf_errorhandler.h" #include "soundflags.h" #include "enginestats.h" #include "tier1/strtools.h" #include "testscriptmgr.h" #include "tmessage.h" #include "tier0/vprof.h" #include "tier0/etwprof.h" #include "tier0/icommandline.h" #include "materialsystem/imaterialsystemhardwareconfig.h" #include "MapReslistGenerator.h" #include "DownloadListGenerator.h" #include "download.h" #include "staticpropmgr.h" #include "GameEventManager.h" #include "iprediction.h" #include "netmessages.h" #include "cl_main.h" #include "hltvserver.h" #include "hltvtest.h" #if defined( REPLAY_ENABLED ) #include "replayserver.h" #include "replayhistorymanager.h" #endif #include "sys_mainwind.h" #include "host_phonehome.h" #ifndef DEDICATED #include "vgui_baseui_interface.h" #include "cl_steamauth.h" #endif #include "sv_remoteaccess.h" // NotifyDedicatedServerUI() #include "snd_audio_source.h" #include "sv_steamauth.h" #include "MapReslistGenerator.h" #include "DevShotGenerator.h" #include "sv_plugin.h" #include "toolframework/itoolframework.h" #include "ienginetoolinternal.h" #include "inputsystem/iinputsystem.h" #include "vgui_askconnectpanel.h" #include "cvar.h" #include "saverestoretypes.h" #include "filesystem/IQueuedLoader.h" #include "filesystem/IXboxInstaller.h" #include "soundservice.h" #include "steam/isteamremotestorage.h" #include "ConfigManager.h" #include "materialsystem/idebugtextureinfo.h" #if defined( _X360 ) #include "xbox/xbox_win32stubs.h" #endif #include "engine/ips3frontpanelled.h" #include "audio_pch.h" #include "platforminputdevice.h" #include "status.h" #ifdef _X360 #include "xbox/xbox_console.h" #define _XBOX #include #undef _XBOX #endif #if defined ( _GAMECONSOLE ) #include "GameUI/IGameUI.h" #include "vgui_baseui_interface.h" #endif #include "matchmaking/mm_helpers.h" #include "ixboxsystem.h" #if defined( INCLUDE_SCALEFORM ) #include "scaleformui/scaleformui.h" #endif extern IXboxSystem *g_pXboxSystem; extern ConVar cl_cloud_settings; #ifndef DEDICATED extern IVAudio *vaudio; void *g_pMilesAudioEngineRef; #endif #ifdef _PS3 #include "ps3/ps3_helpers.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- void NET_Init( bool bDedicated ); void CL_SetPagedPoolInfo(); extern char *CM_EntityString( void ); bool XBX_SetProfileDefaultSettings( int iController ); extern ConVar host_map; extern ConVar sv_cheats; bool g_bDedicatedServerBenchmarkMode = false; int host_frameticks = 0; int host_tickcount = 0; int host_currentframetick = 0; bool g_bLowViolence = false; static bool g_bAllowSecureServers = true; #if defined( INCLUDE_SCALEFORM ) const char g_szDefaultScaleformMovieName[] = "resource/flash/MainUIRootMovie.swf"; const char g_szDefaultScaleformCursorName[] = "resource/flash/Cursor.swf"; #endif #ifdef USE_SDL #include "appframework/ilaunchermgr.h" extern ILauncherMgr *g_pLauncherMgr; #endif // Engine player info, no game related infos here BEGIN_BYTESWAP_DATADESC( player_info_s ) DEFINE_FIELD( version, FIELD_INTEGER64 ), DEFINE_FIELD( xuid, FIELD_INTEGER64 ), DEFINE_ARRAY( name, FIELD_CHARACTER, MAX_PLAYER_NAME_LENGTH ), DEFINE_FIELD( userID, FIELD_INTEGER ), DEFINE_ARRAY( guid, FIELD_CHARACTER, SIGNED_GUID_LEN + 1 ), DEFINE_FIELD( friendsID, FIELD_INTEGER ), DEFINE_ARRAY( friendsName, FIELD_CHARACTER, MAX_PLAYER_NAME_LENGTH ), DEFINE_FIELD( fakeplayer, FIELD_BOOLEAN ), DEFINE_FIELD( ishltv, FIELD_BOOLEAN ), #if defined( REPLAY_ENABLED ) DEFINE_FIELD( isreplay, FIELD_BOOLEAN ), #endif DEFINE_ARRAY( customFiles, FIELD_INTEGER, MAX_CUSTOM_FILES ), DEFINE_FIELD( filesDownloaded, FIELD_INTEGER ), END_BYTESWAP_DATADESC() //------------------------------------------ enum { FRAME_SEGMENT_INPUT = 0, FRAME_SEGMENT_CLIENT, FRAME_SEGMENT_SERVER, FRAME_SEGMENT_RENDER, FRAME_SEGMENT_SOUND, FRAME_SEGMENT_CLDLL, FRAME_SEGMENT_CMD_EXECUTE, NUM_FRAME_SEGMENTS, }; class CFrameTimer { public: void ResetDeltas(); CFrameTimer() : swaptime(0), framestarttime(0), framestarttimeduration(0) { m_flFPSVariability = 0; m_flFPSStdDeviationSeconds = 0; m_flFPSStdDeviationFrameStartTimeSeconds = 0; ResetDeltas(); } void MarkFrameStartTime() { double newframestarttime = Sys_FloatTime(); framestarttimeduration = framestarttime ? ( newframestarttime - framestarttime ) : 0.0; framestarttime = newframestarttime; } void MarkFrame(); void StartFrameSegment( int i ) { starttime[i] = Sys_FloatTime(); } void EndFrameSegment( int i ) { double dt = Sys_FloatTime() - starttime[i]; deltas[ i ] += dt; } void MarkSwapTime( ) { double newswaptime = Sys_FloatTime(); frametime = newswaptime - swaptime; swaptime = newswaptime; ComputeFrameVariability(); g_EngineStats.SetFrameTime( frametime ); g_EngineStats.SetFPSVariability( m_flFPSVariability ); host_frametime_stddeviation = m_flFPSStdDeviationSeconds; host_framestarttime_stddeviation = m_flFPSStdDeviationFrameStartTimeSeconds; host_frameendtime_computationduration = newswaptime - framestarttime; } float GetServerSimulationFrameTime() { return m_flLastServerTime; } private: enum { FRAME_HISTORY_COUNT = 50 }; friend void Host_Speeds(); void ComputeFrameVariability(); double time_base; double times[9]; double swaptime; double framestarttime; double framestarttimeduration; double frametime; double m_flFPSVariability; double m_flFPSStdDeviationSeconds; double m_flFPSStdDeviationFrameStartTimeSeconds; double starttime[NUM_FRAME_SEGMENTS]; double deltas[NUM_FRAME_SEGMENTS]; float m_flLastServerTime; float m_pFrameStartTimeHistory[FRAME_HISTORY_COUNT]; float m_pFrameTimeHistory[FRAME_HISTORY_COUNT]; int m_nFrameTimeHistoryIndex; }; static CFrameTimer g_HostTimes; float Host_GetServerSimulationFrameTime() { return g_HostTimes.GetServerSimulationFrameTime(); } //------------------------------------------ float host_time = 0.0; static ConVar violence_hblood( "violence_hblood","1", 0, "Draw human blood" ); static ConVar violence_hgibs( "violence_hgibs","1", 0, "Show human gib entities" ); static ConVar violence_ablood( "violence_ablood","1", 0, "Draw alien blood" ); static ConVar violence_agibs( "violence_agibs","1", 0, "Show alien gib entities" ); static bool GetDefaultSubtitlesState() { return XBX_IsLocalized() && !XBX_IsAudioLocalized(); } // Marked as FCVAR_USERINFO so that the server can cull CC messages before networking them down to us!!! ConVar closecaption( "closecaption", GetDefaultSubtitlesState() ? "1" : "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE, "Enable close captioning." ); ConVar cl_configversion( "cl_configversion", "8", FCVAR_DEVELOPMENTONLY, "Configuration layout version. Bump this to force a reset of the PS3 save game / settings." ); extern ConVar sv_unlockedchapters; void Snd_Restart_f() { #ifndef DEDICATED extern bool snd_firsttime; CUtlVector music; // Ask sound system for current music tracks S_GetCurrentlyPlayingMusic( music ); S_Shutdown(); snd_firsttime = true; GetBaseLocalClient().ClearSounds(); S_Init(); for ( int i = 0; i < music.Count(); i++ ) { S_RestartSong( &music[i] ); } // Do this or else it won't have anything in the cache. if ( audiosourcecache && sv.GetMapName()[0] ) { audiosourcecache->LevelInit( sv.GetMapName() ); } // Flush soundscapes so they don't stop. We don't insert text in the buffer here because // cl_soundscape_flush is normally cheat-protected. ConCommand *pCommand = (ConCommand*)dynamic_cast< const ConCommand* >( g_pCVar->FindCommand( "cl_soundscape_flush" ) ); if ( pCommand ) { char const *argv[ 1 ] = { "cl_soundscape_flush" }; CCommand cmd( 1, argv, kCommandSrcCode ); pCommand->Dispatch( cmd ); } #ifndef NO_VOICE Voice_ForceInit(); #endif // NO_VOICE #endif } void Snd_Restart_Cmd() { if( !sv_cheats.GetBool() ) { Msg("Warning: The console command \"snd_restart\" will not work with sv_cheats 0\n"); return; } Snd_Restart_f(); } static ConCommand snd_restart( "snd_restart", Snd_Restart_Cmd, "Restart sound system." ); // In other C files. void Shader_Shutdown( void ); void R_Shutdown( void ); bool g_bAbortServerSet = false; #ifdef _WIN32 static bool s_bInitPME = false; #endif static ConVar mem_test_quiet( "mem_test_quiet", "0", 0, "Don't print stats when memtesting" ); static ConVar mem_test_each_frame( "mem_test_each_frame", "0", 0, "Run heap check at end of every frame\n" ); extern void Host_PrintMemoryStatus( const char *mapname ); const char *GetMapName( void ) { static char mapname[ 256 ]; const char *pTest = sv.GetMapName(); if ( !pTest || !pTest[0] ) { // possibly at menu pTest = "nomap"; } Q_FileBase( pTest, mapname, sizeof( mapname ) ); return mapname; } CON_COMMAND( mem_dump, "Dump memory stats to text file." ) { ConMsg("Writing memory stats to file memstats.txt\n"); const char *mapname = GetMapName(); Host_PrintMemoryStatus( mapname ); g_pMemAlloc->DumpStatsFileBase( mapname ); // Dump memory about other memory, chiefly from CMemoryStack. DumpMemoryInfoStats(); } CON_COMMAND( mem_verify, "Verify the validity of the heap" ) { g_pMemAlloc->heapchk(); } CON_COMMAND( mem_compact, "" ) { g_pMemAlloc->CompactHeap(); } CON_COMMAND( mem_incremental_compact, "" ) { g_pMemAlloc->CompactIncremental(); } CON_COMMAND( mem_eat, "" ) { MemAlloc_Alloc( 1024*1024 ); } ConVar mem_incremental_compact_rate( "mem_incremental_compact_rate", ".5", FCVAR_CHEAT, "Rate at which to attempt internal heap compation" ); static bool MemTest() { bool verbose = ( !mem_test_quiet.GetBool() && !mem_test_each_frame.GetBool() ); if ( verbose ) { Msg( "\nBegin mem_test\n" ); Host_PrintMemoryStatus( GetMapName() ); } bool result = g_pMemAlloc->CrtCheckMemory() ? true : false; if ( verbose ) { Msg( "\nEnd mem_test\n" ); } return result; } CON_COMMAND( mem_test, "" ) { MemTest(); } static ConVar mem_test_every_n_seconds( "mem_test_every_n_seconds", "0", 0, "Run heap check at a specified interval\n" ); static ConVar singlestep( "singlestep", "0", FCVAR_CHEAT, "Run engine in single step mode ( set next to 1 to advance a frame )" ); static ConVar next( "next", "0", FCVAR_CHEAT, "Set to 1 to advance to next frame ( when singlestep == 1 )" ); // Print a debug message when the client or server cache is missed ConVar host_showcachemiss( "host_showcachemiss", "0", 0, "Print a debug message when the client or server cache is missed." ); static ConVar mem_dumpstats( "mem_dumpstats", "0", 0, "Dump current and max heap usage info to console at end of frame ( set to 2 for continuous output )\n" ); static ConVar host_ShowIPCCallCount( "host_ShowIPCCallCount", "0", 0, "Print # of IPC calls this number of times per second. If set to -1, the # of IPC calls is shown every frame." ); ConVar vprof_server_spike_threshold( "vprof_server_spike_threshold", "999.0" ); ConVar vprof_server_thread( "vprof_server_thread", "0" ); #if defined( RAD_TELEMETRY_ENABLED ) static void OnChangeTelemetryPause ( IConVar *var, const char *pOldValue, float flOldValue ) { TM_PAUSE( TELEMETRY_LEVEL0, 1 ); } static void OnChangeTelemetryResume ( IConVar *var, const char *pOldValue, float flOldValue ) { TM_PAUSE( TELEMETRY_LEVEL0, 0 ); } static void OnChangeTelemetryLevel ( IConVar *var, const char *pOldValue, float flOldValue ) { char* pIEnd; const char *pLevel = (( ConVar* )var)->GetString(); TelemetrySetLevel( strtoul( pLevel, &pIEnd, 0 ) ); } static void OnChangeTelemetryFrameCount ( IConVar *var, const char *pOldValue, float flOldValue ) { char* pIEnd; const char *pFrameCount = (( ConVar* )var)->GetString(); g_Telemetry.FrameCount = strtoul( pFrameCount, &pIEnd, 0 ); Msg( " TELEMETRY: Setting Telemetry FrameCount: '%d'\n", g_Telemetry.FrameCount ); } static void OnChangeTelemetryServer ( IConVar *var, const char *pOldValue, float flOldValue ) { const char *pServerAddress = (( ConVar* )var)->GetString(); Q_strncpy( g_Telemetry.ServerAddress, pServerAddress, ARRAYSIZE( g_Telemetry.ServerAddress ) ); Msg( " TELEMETRY: Setting Telemetry server: '%s'\n", pServerAddress ); } static void OnChangeTelemetryZoneFilterVal ( IConVar *var, const char *pOldValue, float flOldValue ) { char* pIEnd; const char *pFilterValue = (( ConVar* )var)->GetString(); g_Telemetry.ZoneFilterVal = strtoul( pFilterValue, &pIEnd, 0 ); Msg( " TELEMETRY: Setting Telemetry ZoneFilterVal: '%d'\n", g_Telemetry.ZoneFilterVal ); } static void OnChangeTelemetryDemoStart ( IConVar *var, const char *pOldValue, float flOldValue ) { char* pIEnd; const char *pVal = (( ConVar* )var)->GetString(); g_Telemetry.DemoTickStart = strtoul( pVal, &pIEnd, 0 ); if( g_Telemetry.DemoTickStart > 2000 ) { char cmd[ 256 ]; // If we're far away from the start of the demo file, then jump to ~1000 ticks before. Q_snprintf( cmd, sizeof( cmd ), "demo_gototick %d", g_Telemetry.DemoTickStart - 1000 ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), cmd, kCommandSrcCode, 100 ); } Msg( " TELEMETRY: Setting Telemetry DemoTickStart: '%d'\n", g_Telemetry.DemoTickStart ); } static void OnChangeTelemetryDemoEnd ( IConVar *var, const char *pOldValue, float flOldValue ) { char* pIEnd; const char *pVal = (( ConVar* )var)->GetString(); g_Telemetry.DemoTickEnd = strtoul( pVal, &pIEnd, 0 ); Msg( " TELEMETRY: Setting Telemetry DemoTickEnd: '%d'\n", g_Telemetry.DemoTickEnd ); } ConVar telemetry_pause( "telemetry_pause", "0", FCVAR_RELEASE, "Pause Telemetry", OnChangeTelemetryPause ); ConVar telemetry_resume( "telemetry_resume", "0", FCVAR_RELEASE, "Resume Telemetry", OnChangeTelemetryResume ); ConVar telemetry_filtervalue( "telemetry_filtervalue", "500", FCVAR_RELEASE, "Set Telemetry ZoneFilterVal (MicroSeconds)", OnChangeTelemetryZoneFilterVal ); ConVar telemetry_framecount( "telemetry_framecount", "0", 0, "Set Telemetry count of frames to capture", OnChangeTelemetryFrameCount ); ConVar telemetry_level( "telemetry_level", "0", FCVAR_RELEASE, "Set Telemetry profile level: 0 being off. Hight bit set for mask: 0x8#######", OnChangeTelemetryLevel ); ConVar telemetry_server( "telemetry_server", "localhost", FCVAR_RELEASE, "Set Telemetry server", OnChangeTelemetryServer ); ConVar telemetry_demostart( "telemetry_demostart", "0", 0, "When playing demo, start telemetry on tick #", OnChangeTelemetryDemoStart ); ConVar telemetry_demoend( "telemetry_demoend", "0", 0, "When playing demo, stop telemetry on tick #", OnChangeTelemetryDemoEnd ); #endif static unsigned g_MainThreadId = ThreadGetCurrentId(); extern bool gfBackground; static bool host_checkheap = false; CCommonHostState host_state; //----------------------------------------------------------------------------- #if defined(_X360) static bool g_bGimped = false; static int64 *g_pRange1 = NULL; static int64 *g_pRange2 = NULL; CON_COMMAND(cache_gimp, "Gimp the cache") { if ( g_bGimped ) { XUnlockL2( XLOCKL2_INDEX_XPS ); XUnlockL2( XLOCKL2_INDEX_TITLE ); XPhysicalFree( g_pRange1 ); XPhysicalFree( g_pRange2 ); g_pRange1 = g_pRange2 = NULL; g_bGimped = false; } else { g_pRange1 = ( int64* )XPhysicalAlloc( XLOCKL2_LOCK_SIZE_2_WAYS, MAXULONG_PTR, XLOCKL2_LOCK_SIZE_2_WAYS, PAGE_READWRITE | MEM_LARGE_PAGES ); g_pRange2 = ( int64* )XPhysicalAlloc( XLOCKL2_LOCK_SIZE_2_WAYS, MAXULONG_PTR, XLOCKL2_LOCK_SIZE_2_WAYS, PAGE_READWRITE | MEM_LARGE_PAGES ); g_bGimped = true; XLockL2( XLOCKL2_INDEX_XPS, g_pRange1, XLOCKL2_LOCK_SIZE_2_WAYS, XLOCKL2_LOCK_SIZE_2_WAYS, 0 ); XLockL2( XLOCKL2_INDEX_TITLE, g_pRange2, XLOCKL2_LOCK_SIZE_2_WAYS, XLOCKL2_LOCK_SIZE_2_WAYS, 0 ); for ( int i = 0; i < XLOCKL2_LOCK_SIZE_2_WAYS/8; i++ ) { g_pRange1[i] = 0; g_pRange2[i] = 0; } } } #endif //----------------------------------------------------------------------------- enum HostThreadMode { HTM_DISABLED, HTM_DEFAULT, HTM_FORCED, }; // On the PS3, we want host_thead_mode and threaded_sound off. ConVar host_thread_mode( "host_thread_mode", ( IsPlatformX360() || IsPlatformPS3() ) ? "1" : "0", FCVAR_DEVELOPMENTONLY, "Run the host in threaded mode, (0 == off, 1 == if multicore, 2 == force)" ); ConVar host_threaded_sound( "host_threaded_sound", ( IsPlatformX360() || IsPlatformPS3()) ? "1" : "0", 0, "Run the sound on a thread (independent of mix)" ); ConVar host_threaded_sound_simplethread( "host_threaded_sound_simplethread", ( IsPlatformPS3()) ? "1" : "0", 0, "Run the sound on a simple thread not a jobthread" ); extern ConVar threadpool_affinity; void OnChangeThreadAffinity( IConVar *var, const char *pOldValue, float flOldValue ) { if ( g_pThreadPool->NumThreads() ) { g_pThreadPool->Distribute( threadpool_affinity.GetBool() ); } } ConVar threadpool_affinity( "threadpool_affinity", "1", 0, "Enable setting affinity", 0, 0, 0, 0, &OnChangeThreadAffinity ); extern ConVar threadpool_reserve; CThreadEvent g_ReleaseThreadReservation( true ); CInterlockedInt g_NumReservedThreads; void ThreadPoolReserverFunction() { g_ReleaseThreadReservation.Wait(); --g_NumReservedThreads; } void ReserveThreads( int nToReserve ) { nToReserve = clamp( nToReserve, 0, g_pThreadPool->NumThreads() ); g_ReleaseThreadReservation.Set(); while ( g_NumReservedThreads != 0 ) { ThreadSleep( 0 ); } g_ReleaseThreadReservation.Reset(); while ( nToReserve-- ) { g_NumReservedThreads++; g_pThreadPool->QueueCall( &ThreadPoolReserverFunction )->Release(); } Msg( "%d threads being reserved\n", (int)g_NumReservedThreads ); } void OnChangeThreadReserve( IConVar *var, const char *pOldValue, float flOldValue ) { ReserveThreads( threadpool_reserve.GetInt() ); } ConVar threadpool_reserve( "threadpool_reserve", "0", 0, "Consume the specified number of threads in the thread pool", 0, 0, 0, 0, &OnChangeThreadReserve ); CON_COMMAND( threadpool_cycle_reserve, "Cycles threadpool reservation by powers of 2" ) { int nCores = g_pThreadPool->NumThreads() + 1; int nAvailableCores = nCores - g_NumReservedThreads; Assert( nAvailableCores ); int ratio = nCores / nAvailableCores; ratio *= 2; if ( ratio > nCores ) { ReserveThreads( 0 ); } else { ReserveThreads( nCores - nCores / ratio ); } } CON_COMMAND( thread_test_tslist, "" ) { int nLengthList = ( args.ArgC() == 1 ) ? 1 : atoi( args.Arg( 1 ) ); int nTests = ( args.ArgC() == 2 ) ? 1 : atoi( args.Arg( 2 ) ); RunTSListTests( nLengthList, nTests ); } CON_COMMAND( thread_test_tsqueue, "" ) { int nLengthList = ( args.ArgC() < 2 ) ? 10000 : atoi( args.Arg( 1 ) ); int nTests = ( args.ArgC() < 3 ) ? 1 : atoi( args.Arg( 2 ) ); RunTSQueueTests( nLengthList, nTests ); } CON_COMMAND( threadpool_run_tests, "" ) { int nTests = ( args.ArgC() == 1 ) ? 1 : atoi( args.Arg( 1 ) ); for ( int i = 0; i < nTests; i++ ) { RunThreadPoolTests(); } } //----------------------------------------------------------------------------- /* A server can always be started, even if the system started out as a client to a remote system. A client can NOT be started if the system started as a dedicated server. Memory is cleared / released when a server or client begins, not when they end. */ // Ear position + orientation #ifndef DEDICATED CAudioState s_AudioState; bool CAudioState::IsAnyPlayerUnderwater() const { FOR_EACH_VALID_SPLITSCREEN_PLAYER( i ) { ACTIVE_SPLITSCREEN_PLAYER_GUARD( i ); if ( GetPerUser().m_bIsUnderwater ) return true; } return false; } AudioState_t &CAudioState::GetPerUser( int nSlot /*= -1*/ ) { if ( nSlot == -1 ) { ASSERT_LOCAL_PLAYER_RESOLVABLE(); return m_PerUser[ GET_ACTIVE_SPLITSCREEN_SLOT() ]; } return m_PerUser[ nSlot ]; } const AudioState_t &CAudioState::GetPerUser( int nSlot /*= -1*/ ) const { if ( nSlot == -1 ) { ASSERT_LOCAL_PLAYER_RESOLVABLE(); return m_PerUser[ GET_ACTIVE_SPLITSCREEN_SLOT() ]; } return m_PerUser[ nSlot ]; } #endif // DEDICATED engineparms_t host_parms; bool host_initialized = false; // true if into command execution float host_frametime = 0.0f; float host_frametime_unbounded = 0.0f; float host_frametime_stddeviation = 0.0f; float host_framestarttime_stddeviation = 0.0f; float host_frameendtime_computationduration = 0.0f; float host_frametime_unscaled = 0.0f; double realtime = 0; // without any filtering or bounding double host_idealtime = 0; // "ideal" server time assuming perfect tick rate float host_nexttick = 0; // next server tick in this many ms float host_jitterhistory[128] = { 0 }; unsigned int host_jitterhistorypos = 0; int host_framecount; static int host_hunklevel; CGameClient *host_client; // current client jmp_buf host_abortserver; jmp_buf host_enddemo; static ConVar host_profile( "host_profile","0" ); ConVar skill( "skill","1", FCVAR_ARCHIVE, "Game skill level (1-3).", true, 1, true, 3 ); // 1 - 3 ConVar host_timescale( "host_timescale","1.0", FCVAR_REPLICATED | FCVAR_CHEAT, "Prescale the clock by this amount." ); ConVar host_limitlocal( "host_limitlocal", "0", 0, "Apply cl_cmdrate and cl_updaterate to loopback connection" ); ConVar host_framerate( "host_framerate","0", FCVAR_REPLICATED | FCVAR_CHEAT, "Set to lock per-frame time elapse." ); ConVar host_speeds( "host_speeds","0", 0, "Show general system running times." ); // set for running times ConVar developer( "developer", "0", FCVAR_RELEASE, "Set developer message level"); ConVar deathmatch( "deathmatch","0", FCVAR_NOTIFY, "Running a deathmatch server." ); // 0, 1, or 2 ConVar coop( "coop","0", FCVAR_NOTIFY, "Cooperative play." ); // 0 or 1 ConVar r_ForceRestore( "r_ForceRestore", "0", 0 ); CON_COMMAND_F( display_elapsedtime, "Displays how much time has elapsed since the game started", FCVAR_CHEAT ) { Msg( "Elapsed time: %.2f\n", realtime ); } CON_COMMAND( host_timer_report, "Spew CPU timer jitter for the last 128 frames in microseconds (dedicated only)" ) { if ( sv.IsDedicated() ) { for (int i = 1; i <= ARRAYSIZE( host_jitterhistory ); ++i) { unsigned int slot = ( i + host_jitterhistorypos ) % ARRAYSIZE( host_jitterhistory ); Msg( "%7d\n", ( int ) ( host_jitterhistory[ slot ] * 1000000 ) ); } } } ConVar host_runframe_input_parcelremainder( "host_runframe_input_parcelremainder", "1" ); //putting this on a ConVar only because we're shipping so soon #ifndef DEDICATED void CL_CheckToDisplayStartupMenus(); // in cl_main.cpp #endif bool GetFileFromRemoteStorage( ISteamRemoteStorage *pRemoteStorage, const char *pszRemoteFileName, const char *pszLocalFileName, char const *pathID ) { bool bSuccess = false; // check if file exists in Steam Cloud first int32 nFileSize = pRemoteStorage->GetFileSize( pszRemoteFileName ); if ( nFileSize > 0 ) { CUtlMemory buf( 0, nFileSize ); if ( pRemoteStorage->FileRead( pszRemoteFileName, buf.Base(), nFileSize ) == nFileSize ) { FileHandle_t hFile = g_pFileSystem->Open( pszLocalFileName, "wb", pathID ); if( hFile ) { bSuccess = g_pFileSystem->Write( buf.Base(), nFileSize, hFile ) == nFileSize; g_pFileSystem->Close( hFile ); if ( bSuccess ) { DevMsg( "[Cloud]: SUCCEESS retrieved %s from remote storage into %s\n", pszRemoteFileName, pszLocalFileName ); } else { DevMsg( "[Cloud]: FAILED retrieved %s from remote storage into %s\n", pszRemoteFileName, pszLocalFileName ); } } } } return bSuccess; } void CCommonHostState::SetWorldModel( model_t *pModel ) { if ( worldmodel == pModel ) return; worldmodel = pModel; if ( pModel ) { worldbrush = pModel->brush.pShared; } else { worldbrush = NULL; } } #ifndef DEDICATED void Host_SetAudioState( const AudioState_t &audioState ) { memcpy( &s_AudioState.GetPerUser(), &audioState, sizeof(AudioState_t) ); } #endif bool Host_IsLocalServer() { return sv.IsActive(); } bool Host_IsSinglePlayerGame( void ) { if ( sv.IsActive() ) { return !sv.IsMultiplayer(); } else { #ifdef DEDICATED return false; #else return GetBaseLocalClient().m_nMaxClients == 1; #endif } } /* ================ Host_EndGame ================ */ void Host_EndGame (bool bShowMainMenu, const char *message, ...) { va_list argptr; char string[1024]; va_start (argptr,message); Q_vsnprintf (string,sizeof(string),message,argptr); va_end (argptr); ConMsg ("Host_EndGame: %s\n",string); #ifndef DEDICATED scr_disabled_for_loading = true; int oldn = GetBaseLocalClient().demonum; GetBaseLocalClient().demonum = -1; #endif Host_Disconnect(bShowMainMenu); #ifndef DEDICATED GetBaseLocalClient().demonum = oldn; #endif if ( sv.IsDedicated() ) { Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit return; } bool bDemoEnd = false; #ifndef DEDICATED if (GetBaseLocalClient().demonum != -1) { bDemoEnd = true; } #endif if ( bDemoEnd ) { #ifndef DEDICATED CL_NextDemo (); #endif longjmp (host_enddemo, 1); } else { #ifndef DEDICATED scr_disabled_for_loading = false; #endif if ( g_bAbortServerSet ) { longjmp (host_abortserver, 1); } } } /* ================ Host_Error This shuts down both the client and server ================ */ void Host_Error (const char *error, ...) { va_list argptr; char string[1024]; static bool inerror = false; if (inerror) { Sys_Error ("Host_Error: recursively entered"); } inerror = true; #ifndef DEDICATED // CL_WriteMessageHistory(); TODO must be done by network layer #endif va_start (argptr,error); Q_vsnprintf(string,sizeof(string),error,argptr); va_end (argptr); if ( sv.IsDedicated() ) { // dedicated servers just exit Sys_Error( "Host_Error: %s\n", string ); return; } #ifndef DEDICATED // Reenable screen updates SCR_EndLoadingPlaque (); #endif ConMsg( "\nHost_Error: %s\n\n", string ); Host_Disconnect(true); #ifndef DEDICATED GetBaseLocalClient().demonum = -1; #endif inerror = false; if ( g_bAbortServerSet ) { longjmp (host_abortserver, 1); } } #ifndef DEDICATED class CHostSubscribeForProfileEvents : public IMatchEventsSink { public: CHostSubscribeForProfileEvents() : m_bSubscribed( false ) {} public: void Subscribe( bool bSubscribe ); virtual void OnEvent( KeyValues *pEvent ); public: bool m_bSubscribed; }; void CHostSubscribeForProfileEvents::Subscribe( bool bSubscribe ) { if ( bSubscribe == m_bSubscribed ) return; if ( !g_pMatchFramework ) return; if ( bSubscribe ) g_pMatchFramework->GetEventsSubscription()->Subscribe( this ); else g_pMatchFramework->GetEventsSubscription()->Unsubscribe( this ); m_bSubscribed = bSubscribe; } void CHostSubscribeForProfileEvents::OnEvent( KeyValues *pEvent ) { char const *szEvent = pEvent->GetName(); if ( !Q_stricmp( szEvent, "OnProfileDataLoaded" ) ) { int iController = pEvent->GetInt( "iController" ); Host_ReadConfiguration( iController, false ); } if ( !Q_stricmp( szEvent, "OnProfileDataLoadFailed" ) ) { #if defined ( _X360 ) int iController = pEvent->GetInt( "iController" ); int iSlot = XBX_GetSlotByUserId( iController ); ECommandMsgBoxSlot slot = CMB_SLOT_FULL_SCREEN; char cmdLine[80]; if ( iSlot == 0 ) { sprintf( cmdLine, "boot_to_start_and_reset_config 0" ); slot = CMB_SLOT_PLAYER_0; } else { sprintf( cmdLine, "boot_to_start_and_reset_config 1" ); slot = CMB_SLOT_PLAYER_1; } if ( !GetGameUI()->IsInLevel() ) { slot = CMB_SLOT_FULL_SCREEN; } if ( !g_pXboxSystem->IsArcadeTitleUnlocked() ) { GetGameUI()->CreateCommandMsgBoxInSlot( slot, "#SFUI_GameUI_ProfileDataLoadFailedTitle", "#SFUI_GameUI_ProfileDataLoadFailedTrialMsg", true, false, "boot_to_start", NULL, NULL, NULL ); return; } GetGameUI()->CreateCommandMsgBoxInSlot( slot, "#SFUI_GameUI_ProfileDataLoadFailedTitle", "#SFUI_GameUI_ProfileDataLoadFailedMsg", true, true, cmdLine, "boot_to_start", NULL, NULL ); #endif } if ( !Q_stricmp( szEvent, "OnProfileDataWriteFailed" ) ) { #if defined ( _X360 ) int iController = pEvent->GetInt( "iController" ); int iSlot = XBX_GetSlotByUserId( iController ); ECommandMsgBoxSlot slot = CMB_SLOT_FULL_SCREEN; if ( GetGameUI()->IsInLevel() ) { if ( iSlot == 0 ) { slot = CMB_SLOT_PLAYER_0; } else { slot = CMB_SLOT_PLAYER_1; } } // are we in trial mode? if ( !g_pXboxSystem->IsArcadeTitleUnlocked() ) { GetGameUI()->CreateCommandMsgBoxInSlot( slot, "#SFUI_TrialMUPullTitle", "#SFUI_TrialMUPullMsg", true, false, "boot_to_start", NULL, NULL, NULL ); return; } GetGameUI()->CreateCommandMsgBoxInSlot( slot, "#SFUI_GameUI_ProfileDataWriteFailedTitle", "#SFUI_GameUI_ProfileDataWriteFailedMsg", true, false, NULL, NULL, NULL, NULL ); #endif } #if defined ( _X360 ) else if ( !Q_stricmp( szEvent, "OnProfilesChanged" ) ) { // $TODO(hpe) this needs reworked for split screen; currently, will reset configs for all players when one active player signs out Host_ResetGlobalConfiguration(); for ( DWORD iSplitscreenSlot = 0; iSplitscreenSlot < XBX_GetNumGameUsers(); ++ iSplitscreenSlot ) { Host_ResetConfiguration( XBX_GetUserId( iSplitscreenSlot ) ); } } #endif #if defined ( _PS3 ) else if ( !Q_stricmp( szEvent, "ResetConfiguration" ) ) { int iController = pEvent->GetInt( "iController" ); // $TODO(hpe) this needs reworked for split screen; currently, will reset configs for all players when one active player signs out Host_ResetGlobalConfiguration(); Host_ResetConfiguration( iController ); Host_WriteConfiguration( iController, "" ); } #endif } #endif #ifdef _GAMECONSOLE void Console_UpdateNotificationPosition() { static ConVarRef closecaption( "closecaption" ); static ConVarRef cc_subtitles( "cc_subtitles" ); bool bSubtitled = ( ( closecaption.IsValid() && closecaption.GetBool() ) || ( cc_subtitles.IsValid() && cc_subtitles.GetBool() ) ); #ifdef _X360 XNotifyPositionUI( bSubtitled ? XNOTIFYUI_POS_TOPRIGHT : XNOTIFYUI_POS_BOTTOMCENTER ); Msg( "XNotifyPositionUI: %s\n", bSubtitled ? "TOPRIGHT" : "BOTTOMCENTER" ); #endif } #endif void Host_SubscribeForProfileEvents( bool bSubscribe ) { #ifndef DEDICATED static CHostSubscribeForProfileEvents s_HostSubscribeForProfileEvents; s_HostSubscribeForProfileEvents.Subscribe( bSubscribe ); #endif } #ifndef DEDICATED //****************************************** // UseDefuaultBinding // // If the config.cfg file is not present, this // function is called to set the default key // bindings to match those defined in kb_def.lst //****************************************** void UseDefaultBindings( void ) { FileHandle_t f; char szFileName[ _MAX_PATH ]; char token[ 1024 ]; char szKeyName[ 256 ]; // read kb_def file to get default key binds Q_snprintf( szFileName, sizeof( szFileName ), "%skb_def.lst", SCRIPT_DIR ); f = g_pFileSystem->Open( szFileName, "r"); if ( !f ) { ConMsg( "Couldn't open kb_def.lst\n" ); return; } // read file into memory int size = g_pFileSystem->Size(f); char *startbuf = new char[ size ]; g_pFileSystem->Read( startbuf, size, f ); g_pFileSystem->Close( f ); const char *buf = startbuf; while ( 1 ) { buf = COM_ParseFile( buf, token, sizeof( token ) ); if ( strlen( token ) <= 0 ) break; Q_strncpy ( szKeyName, token, sizeof( szKeyName ) ); buf = COM_ParseFile( buf, token, sizeof( token ) ); if ( strlen( token ) <= 0 ) // Error break; // finally, bind key Key_SetBinding ( g_pInputSystem->StringToButtonCode( szKeyName ), token ); } delete [] startbuf; // cleanup on the way out } static bool g_bConfigCfgExecuted[MAX_SPLITSCREEN_CLIENTS]; enum SyncCvarValueWithPlayerTitleDataPolicy_t { CVARWRITETD, CVARREADTD }; static void SyncCvarValueWithPlayerTitleData( IPlayerLocal *pPlayer, ConVar *cv, TitleDataFieldsDescription_t const *pField, SyncCvarValueWithPlayerTitleDataPolicy_t eCV ) { switch( pField->m_eDataType ) { case TitleDataFieldsDescription_t::DT_float: if ( eCV == CVARWRITETD ) TitleDataFieldsDescriptionSetValue( pField, pPlayer, cv->GetFloat() ); else if ( eCV == CVARREADTD ) cv->SetValue( TitleDataFieldsDescriptionGetValue( pField, pPlayer ) ); break; case TitleDataFieldsDescription_t::DT_uint32: if ( eCV == CVARWRITETD ) TitleDataFieldsDescriptionSetValue( pField, pPlayer, cv->GetInt() ); else if ( eCV == CVARREADTD ) cv->SetValue( TitleDataFieldsDescriptionGetValue( pField, pPlayer ) ); break; case TitleDataFieldsDescription_t::DT_uint16: if ( eCV == CVARWRITETD ) TitleDataFieldsDescriptionSetValue( pField, pPlayer, cv->GetInt() ); else if ( eCV == CVARREADTD ) cv->SetValue( TitleDataFieldsDescriptionGetValue( pField, pPlayer ) ); break; case TitleDataFieldsDescription_t::DT_uint8: if ( eCV == CVARWRITETD ) TitleDataFieldsDescriptionSetValue( pField, pPlayer, cv->GetInt() ); else if ( eCV == CVARREADTD ) cv->SetValue( TitleDataFieldsDescriptionGetValue( pField, pPlayer ) ); break; case TitleDataFieldsDescription_t::DT_BITFIELD: if ( eCV == CVARWRITETD ) TitleDataFieldsDescriptionSetBit( pField, pPlayer, cv->GetBool() ); else if ( eCV == CVARREADTD ) cv->SetValue( !!TitleDataFieldsDescriptionGetBit( pField, pPlayer ) ); break; } } //----------------------------------------------------------------------------- // Purpose: Write out our 360 exclusive settings to internal storage //----------------------------------------------------------------------------- void Host_WriteConfiguration_Console( const int iController, bool bVideoConfig ) { // sb: FIXME(hpe) only write if we had a valid read so we don't accidentally wipe out valid data #ifdef _GAMECONSOLE DevMsg( "Host_WriteConfiguration_Console for ctrlr%d (%s)\n", iController, bVideoConfig ? "video" : "controls" ); if ( iController < 0 ) return; if ( !g_pMatchFramework || !g_pMatchFramework->GetMatchTitle() ) return; IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController ); if ( !pPlayer ) return; #if defined ( _X360 ) if ( !pPlayer->IsTitleDataValid() ) return; #endif #ifndef GAME_DLL if ( g_pXboxSystem->IsArcadeTitleUnlocked() ) { IGameEvent *event = g_GameEventManager.CreateEvent( "write_game_titledata" ); if ( event ) { event->SetInt( "controllerId", iController ); g_GameEventManager.FireEventClientSide( event ); } } #endif int iSlot = XBX_GetSlotByUserId( iController ); // check version number for title data block 3 TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage(); if ( !fields ) return; #if defined ( _X360 ) TitleDataFieldsDescription_t const *versionField = TitleDataFieldsDescriptionFindByString( fields, "TITLEDATA.BLOCK3.VERSION" ); if ( !versionField || versionField->m_eDataType != TitleDataFieldsDescription_t::DT_uint16) { Warning( "Host_WriteConfiguration_Console missing or incorrect type TITLEDATA.BLOCK3.VERSION\n" ); return; } ConVarRef cl_titledataversionblock3 ( "cl_titledataversionblock3" ); TitleDataFieldsDescriptionSetValue( versionField, pPlayer, cl_titledataversionblock3.GetInt() ); #endif // On consoles - store guest user's convars in primary user's profile data char const *szUsrField = ""; if ( XBX_GetUserIsGuest( iSlot ) && !bVideoConfig ) { IPlayerLocal *pPlayerPrimary = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() ); if ( pPlayerPrimary ) { if ( TitleDataFieldsDescription_t const *pOffset = TitleDataFieldsDescriptionFindByString( fields, TITLE_DATA_PREFIX "CFG.usrSS.version" ) ) { pPlayer = pPlayerPrimary; szUsrField = "SS"; } } } int numLoops = 1; #if defined (CSTRIKE15) // Console CStrike15 we want to always save both usr and sys Convars numLoops = 2; #endif for ( int loopCount=0; loopCount arrCVars; cv->WriteVariables( NULL, iSlot, !bVideoConfig, &arrCVars ); for ( int k = 0; k < arrCVars.Count(); ++ k ) { ConVar *cvSave = arrCVars[k]; char const *cvSaveName = cvSave->GetBaseName(); CFmtStr sFieldLookup( TITLE_DATA_PREFIX "CFG.%s%s.%s", bVideoConfig ? "sys" : "usr", szUsrField, cvSaveName ); TitleDataFieldsDescription_t const *pField = TitleDataFieldsDescriptionFindByString( fields, sFieldLookup ); if ( !pField ) { Warning( "Host_WriteConfiguration_Console (%s#%d) - cannot save cvar %s\n", bVideoConfig ? "video" : "ctrlr", iController, cvSaveName ); continue; } SyncCvarValueWithPlayerTitleData( pPlayer, cvSave, pField, CVARWRITETD ); } // reset the input parameter if ( loopCount == 1 ) bVideoConfig = !bVideoConfig; } // Update the version number for the settings. This is the main version number for PS3. if ( TitleDataFieldsDescription_t const *pVersion = TitleDataFieldsDescriptionFindByString( fields, TITLE_DATA_PREFIX "CFG.sys.version" ) ) { TitleDataFieldsDescriptionSetValue( pVersion, pPlayer, cl_configversion.GetInt() ); } // Let the player manager save the user data g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnProfilesWriteOpportunity", "reason", "settings" ) ); // Update notifications position Console_UpdateNotificationPosition(); #endif // #ifdef _GAMECONSOLE } bool Host_WasConfigCfgExecuted( const int iController ) { int iIndex = iController; if ( iIndex < 0 ) iIndex = 0; Assert( iIndex >= 0 && iIndex < MAX_SPLITSCREEN_CLIENTS ); return g_bConfigCfgExecuted[iIndex]; } void Host_SetConfigCfgExecuted( const int iController, bool bExecuted = true ) { int iIndex = iController; if ( iIndex < 0 ) iIndex = 0; Assert( iIndex >= 0 && iIndex < MAX_SPLITSCREEN_CLIENTS ); g_bConfigCfgExecuted[iIndex] = bExecuted; } void Host_ResetGlobalConfiguration() { #ifdef _GAMECONSOLE ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 ); #endif #ifndef _GAMECONSOLE // We exec our global default configuration for non-consoles Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec config.global" PLATFORM_EXT ".cfg game\n" ); Cbuf_Execute(); #else CUtlVector< ConVar * > arrCVars; cv->WriteVariables( NULL, 0, false, &arrCVars ); for ( int k = 0; k < arrCVars.Count(); ++ k ) { ConVar *cvSave = arrCVars[k]; cvSave->Revert(); DevMsg( "Console reset global configuration: %s = \"%s\"\n", cvSave->GetName(), cvSave->GetString() ); } #endif #ifdef _GAMECONSOLE // Update notifications position Console_UpdateNotificationPosition(); #endif } void Host_ResetConfiguration( const int iController ) { #if defined ( _GAMECONSOLE ) #ifndef SPLIT_SCREEN_STUBS int iSlot = XBX_GetSlotByUserId( iController ); ACTIVE_SPLITSCREEN_PLAYER_GUARD( iSlot ); #endif // First, we exec our default configuration Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec config" PLATFORM_EXT ".cfg game\n" ); Cbuf_Execute(); #if defined ( _X360 ) // This will wipe out all achievement and stats but they'll be loaded from the xlast title data sync. IGameEvent *event = g_GameEventManager.CreateEvent( "reset_game_titledata" ); if ( event ) { event->SetInt( "controllerId", iController ); g_GameEventManager.FireEventClientSide( event ); } // Get and set all our default setting we care about from the Xbox XBX_SetProfileDefaultSettings( iController ); IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController ); if ( pPlayer ) { pPlayer->SetIsTitleDataValid( true ); } #else #if defined( _PS3 ) char szScratch[MAX_PATH]; int iAllDevices = -1; Q_snprintf( szScratch, sizeof(szScratch), "cl_reset_ps3_bindings %d %d", iController, iAllDevices ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), szScratch ); Cbuf_Execute(); #endif // _PS3 // Get and set all our default setting we care about. XBX_SetProfileDefaultSettings( iController ); #endif // _X360 #endif // _GAMECONSOLE } /* =============== Host_WriteConfiguration Writes key bindings and archived cvars to config.cfg =============== */ void Host_WriteConfiguration( const int iController, const char *filename ) { // Set the joystick being force disabled just as we write the config // This allows us to chose this option in the menu with a controller without accidentally disabling our only mode of input static ConVarRef joystick_force_disabled( "joystick_force_disabled" ); static ConVarRef joystick_force_disabled_set_from_options( "joystick_force_disabled_set_from_options" ); if ( joystick_force_disabled.IsValid() && joystick_force_disabled_set_from_options.IsValid() ) { if ( joystick_force_disabled.GetBool() != joystick_force_disabled_set_from_options.GetBool() ) { joystick_force_disabled.SetValue( joystick_force_disabled_set_from_options.GetBool() ); } } if ( !filename ) filename = "config.cfg"; // Write to internal storage on the 360 if ( IsGameConsole() ) { Host_WriteConfiguration_Console( iController, false ); return; } if ( !host_initialized ) { return; } if ( Host_WasConfigCfgExecuted( iController ) == false ) { return; } // If in map editing mode don't save configuration if (g_bInEditMode) { ConMsg( "skipping %s output when in map edit mode\n", filename ); return; } // dedicated servers initialize the host but don't parse and set the // config.cfg cvars if ( !sv.IsDedicated() ) { if ( IsPC() && Key_CountBindings() <= 1 ) { ConMsg( "skipping %s output, no keys bound\n", filename ); return; } // force any queued convar changes to flush before reading/writing them UpdateMaterialSystemConfig(); // Generate a new .cfg file. char szFileName[MAX_PATH]; CUtlBuffer configBuff( 0, 0, CUtlBuffer::TEXT_BUFFER); Q_snprintf( szFileName, sizeof(szFileName), "cfg/%s", filename ); g_pFileSystem->CreateDirHierarchy( "cfg", "USRLOCAL" ); if ( g_pFileSystem->FileExists( szFileName, "USRLOCAL" ) && !g_pFileSystem->IsFileWritable( szFileName, "USRLOCAL" ) ) { ConMsg( "Config file %s is read-only!!\n", szFileName ); return; } // Always throw away all keys that are left over. configBuff.Printf( "unbindall\n" ); Key_WriteBindings( configBuff ); ConVarUtilities->WriteVariables( &configBuff ); #if !defined( DEDICATED ) bool down; for ( int hh = 0; hh < host_state.max_splitscreen_players; ++hh ) { ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh ); if ( g_ClientDLL->IN_IsKeyDown( "in_jlook", down ) && down ) { configBuff.Printf( "cmd%d +jlook\n", hh+1 ); } } #endif // DEDICATED if ( !configBuff.TellMaxPut() ) { // nothing to write return; } #if defined(NO_STEAM) AssertMsg( false, "SteamCloud not available on Xbox 360. Badger Martin to fix this." ); #else ISteamRemoteStorage *pRemoteStorage = #ifdef _PS3 ::SteamRemoteStorage(); #else Steam3Client().SteamClient() ? (ISteamRemoteStorage *) Steam3Client().SteamClient()->GetISteamGenericInterface( SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), STEAMREMOTESTORAGE_INTERFACE_VERSION ):NULL; #endif if ( pRemoteStorage ) { int32 availableBytes, totalBytes = 0; if ( pRemoteStorage->GetQuota( &totalBytes, &availableBytes ) ) { if ( totalBytes > 0 ) { if ( cl_cloud_settings.GetInt() != -1 && ( cl_cloud_settings.GetInt() & STEAMREMOTESTORAGE_CLOUD_CONFIG ) ) { // TODO put MOD dir in pathname if ( pRemoteStorage->FileWrite( szFileName, configBuff.Base(), configBuff.TellMaxPut() ) ) { // Refresh local copy so that on next game boot Host_ReadPreStartupConfiguration() will avoid loading the stale config.cfg // which will only get downloaded & refreshed later during Host_ReadConfiguration() GetFileFromRemoteStorage( pRemoteStorage, "cfg/config.cfg", "cfg/config.cfg", "USRLOCAL" ); DevMsg( "[Cloud]: SUCCEESS saving %s in remote storage\n", szFileName ); } else { // probably a quota issue. TODO what to do ? DevMsg( "[Cloud]: FAILED saving %s in remote storage\n", szFileName ); } } } } // even if SteamCloud worked we still safe the same file locally } #endif // make a persistent copy that async will use and free char *tempBlock = new char[configBuff.TellMaxPut()]; Q_memcpy( tempBlock, configBuff.Base(), configBuff.TellMaxPut() ); // async write the buffer, and then free it char szFileNameToWriteLocal[MAX_PATH] = {}; g_pFileSystem->GetSearchPath( "USRLOCAL", false, szFileNameToWriteLocal, sizeof( szFileNameToWriteLocal ) ); V_strcat_safe( szFileNameToWriteLocal, szFileName ); g_pFileSystem->AsyncWrite( szFileNameToWriteLocal, tempBlock, configBuff.TellMaxPut(), true ); ConMsg( "Host_WriteConfiguration: Wrote %s\n", szFileName ); } } //----------------------------------------------------------------------------- // Purpose: Retrieve and set any defaults from the user's gamer profile //----------------------------------------------------------------------------- bool XBX_SetProfileDefaultSettings( int iController ) { // These defined values can't play nicely with the PC, so we need to ignore them for that build target #ifdef _GAMECONSOLE UserProfileData upd = {0}; if ( IPlayerLocal *pPlayerLocal = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController ) ) { upd = pPlayerLocal->GetPlayerProfileData(); } // // Skill // int nSkillSetting = upd.difficulty; int nResultSkill = 2; #ifdef _X360 switch( nSkillSetting ) { case XPROFILE_GAMER_DIFFICULTY_HARD: nResultSkill = 3; break; case XPROFILE_GAMER_DIFFICULTY_EASY: default: nResultSkill = 1; break; } #endif // If the mod has no difficulty setting, only easy is allowed KeyValues *modinfo = new KeyValues("ModInfo"); if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) ) { if ( stricmp(modinfo->GetString("nodifficulty", "0"), "1") == 0 ) nResultSkill = 1; } modinfo->deleteThis(); char szScratch[MAX_PATH]; Q_snprintf( szScratch, sizeof(szScratch), "skill %d", nResultSkill ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), szScratch ); // // Movement control // int nMovementControl = !!upd.action_movementcontrol; Q_snprintf( szScratch, sizeof(szScratch), "joy_movement_stick %d", nMovementControl ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), szScratch ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), "joyadvancedupdate" ); // // Y-Inversion // int nYinvert = !!upd.yaxis; Q_snprintf( szScratch, sizeof(szScratch), "joy_inverty %d", nYinvert ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), szScratch ); // // Vibration control // int nVibration = !!upd.vibration; Q_snprintf( szScratch, sizeof(szScratch), "cl_rumblescale %d", nVibration ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), szScratch ); // Execute all commands we've queued up Cbuf_Execute(); #endif // _GAMECONSOLE return true; } //----------------------------------------------------------------------------- // Purpose: Read our configuration from the 360, filling in defaults on our first run //----------------------------------------------------------------------------- void Host_ReadConfiguration_Console( const int iController ) { #ifdef _GAMECONSOLE if ( iController < 0 ) return; int iSlot = XBX_GetSlotByUserId( iController ); DevMsg( "Host_ReadConfiguration_Console maps ctrlr%d to slot%d\n", iController, iSlot ); if ( !g_pMatchFramework || !g_pMatchFramework->GetMatchTitle() ) return; IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController ); if ( !pPlayer ) return; TitleDataFieldsDescription_t const *fields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage(); if ( !fields ) return; #if defined( _X360 ) Host_ResetConfiguration( iController ); // dont read any config info if the load was not valid if ( !pPlayer->IsTitleDataValid() ) return; // if the trial block is fresh, reset the trial timer to clear out any previous signed in players time if ( !pPlayer->IsTitleDataBlockValid( 2 ) ) { SplitScreenConVarRef xbox_arcade_remaining_trial_time("xbox_arcade_remaining_trial_time"); xbox_arcade_remaining_trial_time.SetValue( iSlot, xbox_arcade_remaining_trial_time.GetDefault() ); } if ( pPlayer->IsFreshPlayerProfile() ) return; #ifndef GAME_DLL if ( pPlayer->IsTitleDataBlockValid( 0 ) && pPlayer->IsTitleDataBlockValid( 1 ) ) { IGameEvent *event = g_GameEventManager.CreateEvent( "read_game_titledata" ); if ( event ) { event->SetInt( "controllerId", iController ); g_GameEventManager.FireEventClientSide( event ); } } #endif //GAME_DLL // check version numbers ConVarRef cl_titledataversionblock3 ( "cl_titledataversionblock3" ); TitleDataFieldsDescription_t const *versionField3 = TitleDataFieldsDescriptionFindByString( fields, "TITLEDATA.BLOCK3.VERSION" ); #if !defined( _CERT ) // check to see if profile versions match; reset if not ConVarRef cl_titledataversionblock1 ( "cl_titledataversionblock1" ); ConVarRef cl_titledataversionblock2 ( "cl_titledataversionblock2" ); TitleDataFieldsDescription_t const *versionField1 = TitleDataFieldsDescriptionFindByString( fields, "TITLEDATA.BLOCK1.VERSION" ); if ( !versionField1 || versionField1->m_eDataType != TitleDataFieldsDescription_t::DT_uint16) { Warning( "Host_ReadConfiguration_Console missing or incorrect type TITLEDATA.BLOCK1.VERSION\n" ); return; } TitleDataFieldsDescription_t const *versionField2 = TitleDataFieldsDescriptionFindByString( fields, "TITLEDATA.BLOCK2.VERSION" ); if ( !versionField2 || versionField2->m_eDataType != TitleDataFieldsDescription_t::DT_uint16) { Warning( "Host_ReadConfiguration_Console missing or incorrect type TITLEDATA.BLOCK2.VERSION\n" ); return; } bool versionValid = true; if ( g_pXboxSystem->IsArcadeTitleUnlocked() ) { if ( ( pPlayer->IsTitleDataBlockValid( 0 ) && cl_titledataversionblock1.GetInt() != TitleDataFieldsDescriptionGetValue( versionField1, pPlayer ) ) || ( pPlayer->IsTitleDataBlockValid( 1 ) && cl_titledataversionblock2.GetInt() != TitleDataFieldsDescriptionGetValue( versionField2, pPlayer ) ) || ( pPlayer->IsTitleDataBlockValid( 2 ) && cl_titledataversionblock3.GetInt() != TitleDataFieldsDescriptionGetValue( versionField3, pPlayer ) ) ) { versionValid = false; } } else { if ( pPlayer->IsTitleDataBlockValid( 2 ) && cl_titledataversionblock3.GetInt() != TitleDataFieldsDescriptionGetValue( versionField3, pPlayer ) ) { versionValid = false; } } if ( !versionValid ) { Warning( "ProfileVersion is out of date; your profile has been reset\n" ); // zero out title data buffer to remove any stale data since we are basically nuking this profile data IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController ); if ( pPlayer ) { pPlayer->ClearBufTitleData(); } Host_ResetConfiguration( iController ); Host_WriteConfiguration( iController, "" ); GetGameUI()->ShowMessageDialog( "Your profile version was out of date; the profile has been reset.", "Profile Version Mismatch" ); return; } #endif // !_CERT if ( !versionField3 || versionField3->m_eDataType != TitleDataFieldsDescription_t::DT_uint16) { Warning( "Host_ReadConfiguration_Console missing or incorrect type TITLEDATA.BLOCK3.VERSION\n" ); return; } if ( cl_titledataversionblock3.GetInt() != TitleDataFieldsDescriptionGetValue( versionField3, pPlayer ) ) { Warning( "Host_ReadConfiguration_Console wrong version number for TITLEDATA.BLOCK3.VERSION; expected %d, got %d\n", cl_titledataversionblock3.GetInt(), TitleDataFieldsDescriptionGetValue( versionField3, pPlayer ) ); return; } #else // If not on 360, we read the game title data reguardless. IGameEvent *event = g_GameEventManager.CreateEvent( "read_game_titledata" ); if ( event ) { event->SetInt( "controllerId", iController ); g_GameEventManager.FireEventClientSide( event ); } #endif // _X360 // usr data { bool bVideoConfig = false; char const *szUsrField = ""; if ( XBX_GetUserIsGuest( iSlot ) && !bVideoConfig ) { IPlayerLocal *pPlayerPrimary = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() ); if ( pPlayerPrimary ) { if ( TitleDataFieldsDescription_t const *pOffset = TitleDataFieldsDescriptionFindByString( fields, TITLE_DATA_PREFIX "CFG.usrSS.version" ) ) { pPlayer = pPlayerPrimary; szUsrField = "SS"; } } } bool bConfigVersionValid = true; CUtlVector< ConVar * > arrCVars; cv->WriteVariables( NULL, iSlot, !bVideoConfig, &arrCVars ); if ( bConfigVersionValid ) { for ( int k = 0; k < arrCVars.Count(); ++ k ) { ConVar *cvSave = arrCVars[k]; char const *cvSaveName = cvSave->GetBaseName(); CFmtStr sFieldLookup( TITLE_DATA_PREFIX "CFG.%s%s.%s", bVideoConfig ? "sys" : "usr", szUsrField, cvSaveName ); TitleDataFieldsDescription_t const *pField = TitleDataFieldsDescriptionFindByString( fields, sFieldLookup ); if ( !pField ) { Warning( "Host_ReadConfiguration_Console (%s#%d) - cannot read cvar %s\n", bVideoConfig ? "video" : "ctrlr", iController, cvSaveName ); continue; } SyncCvarValueWithPlayerTitleData( pPlayer, cvSave, pField, CVARREADTD ); if ( !Q_stricmp( cvSaveName, "joy_cfg_preset" ) ) // special code to handle joystick config presets { Cbuf_AddText( Cbuf_GetCurrentPlayer(), CFmtStr( "cmd%d exec joy_preset_%d" PLATFORM_EXT ".cfg\n", iSlot + 1, cvSave->GetInt() ) ); } } } } // sys data if ( !XBX_GetUserIsGuest( iSlot ) && ( XBX_GetPrimaryUserId() == iController ) ) { bool bVideoConfig = true; char const *szUsrField = ""; bool bConfigVersionValid = true; CUtlVector< ConVar * > arrCVars; cv->WriteVariables( NULL, iSlot, !bVideoConfig, &arrCVars ); if ( bConfigVersionValid ) { for ( int k = 0; k < arrCVars.Count(); ++ k ) { ConVar *cvSave = arrCVars[k]; char const *cvSaveName = cvSave->GetBaseName(); CFmtStr sFieldLookup( TITLE_DATA_PREFIX "CFG.%s%s.%s", bVideoConfig ? "sys" : "usr", szUsrField, cvSaveName ); TitleDataFieldsDescription_t const *pField = TitleDataFieldsDescriptionFindByString( fields, sFieldLookup ); if ( !pField ) { Warning( "Host_ReadConfiguration_Console (%s#%d) - cannot read cvar %s\n", bVideoConfig ? "video" : "ctrlr", iController, cvSaveName ); continue; } SyncCvarValueWithPlayerTitleData( pPlayer, cvSave, pField, CVARREADTD ); } } } // Update notifications position Console_UpdateNotificationPosition(); #endif // _GAMECONSOLE } //----------------------------------------------------------------------------- // Purpose: // Input : false - //----------------------------------------------------------------------------- void Host_ReadConfiguration( const int iController, const bool readDefault ) { if ( sv.IsDedicated() ) return; // Rebind keys and set cvars if ( !g_pFileSystem ) { Sys_Error( "Host_ReadConfiguration: g_pFileSystem == NULL\n" ); } // Handle the console case #ifdef _GAMECONSOLE { Host_ReadConfiguration_Console( iController ); return; } #else bool saveconfig = false; ISteamRemoteStorage *pRemoteStorage = Steam3Client().SteamClient() ? (ISteamRemoteStorage *)Steam3Client().SteamClient()->GetISteamGenericInterface( SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), STEAMREMOTESTORAGE_INTERFACE_VERSION ):NULL; if ( pRemoteStorage ) { // if cloud settings is default but remote storage does not exist yet, set it to sync all because this is the first // computer the game is run on--default to copying everything to the cloud if ( cl_cloud_settings.GetInt() == -1 && !pRemoteStorage->FileExists( "cfg/config.cfg" ) ) { DevMsg( "[Cloud]: Default setting with remote data non-existent, sync all\n" ); cl_cloud_settings.SetValue( STEAMREMOTESTORAGE_CLOUD_ALL ); } if ( cl_cloud_settings.GetInt() != -1 && ( cl_cloud_settings.GetInt() & STEAMREMOTESTORAGE_CLOUD_CONFIG ) ) { // config files are run through the exec command which got pretty complicated with all the splitscreen // stuff. Steam UFS doens't support split screen well (2 users ?) GetFileFromRemoteStorage( pRemoteStorage, "cfg/config.cfg", "cfg/config.cfg", "USRLOCAL" ); } } if ( g_pFileSystem->FileExists( "//usrlocal/cfg/config.cfg" ) ) { Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec config.cfg usrlocal\n" ); } else if ( g_pFileSystem->FileExists( "//mod/cfg/config.cfg" ) ) { Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec config.cfg mod\n" ); } else { Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec config_default.cfg\n" ); saveconfig = true; } Cbuf_Execute(); // check to see if we actually set any keys, if not, load defaults from kb_def.lst // so we at least have basics setup. int nNumBinds = Key_CountBindings(); if ( nNumBinds == 0 ) { UseDefaultBindings(); } Key_SetBinding( KEY_ESCAPE, "cancelselect" ); // Make sure that something is always bound to console if (NULL == Key_NameForBinding("toggleconsole")) { // If nothing is bound to it then bind it to ' Key_SetBinding( KEY_BACKQUOTE, "toggleconsole" ); } SetupDefaultAskConnectAcceptKey(); Host_SetConfigCfgExecuted( iController ); if ( saveconfig ) { // An ugly hack, but we can probably save this safely bool saveinit = host_initialized; host_initialized = true; Host_WriteConfiguration( iController, "config.cfg" ); host_initialized = saveinit; } #endif } CON_COMMAND( host_writeconfig, "Store current settings to config.cfg (or specified .cfg file)." ) { if ( args.ArgC() > 2 ) { ConMsg( "Usage: writeconfig \n" ); return; } if ( args.ArgC() == 2 ) { char const *filename = args[ 1 ]; if ( !filename || !filename[ 0 ] ) { return; } char outfile[ MAX_QPATH ]; // Strip path and extension from filename Q_FileBase( filename, outfile, sizeof( outfile ) ); Host_WriteConfiguration( -1, va( "%s.cfg", outfile ) ); } else { Host_WriteConfiguration( -1, "config.cfg" ); } } CON_COMMAND( host_writeconfig_ss, "Store current settings to config.cfg (or specified .cfg file) with first param as splitscreen index." ) { if ( args.ArgC() <= 1 || args.ArgC() > 3) { ConMsg( "Usage: writeconfig \n" ); return; } if ( args.ArgC() == 3 ) { char const *filename = args[ 2 ]; if ( !filename || !filename[ 0 ] ) { return; } char outfile[ MAX_QPATH ]; // Strip path and extension from filename Q_FileBase( filename, outfile, sizeof( outfile ) ); int controller = atoi( args[1] ); Host_WriteConfiguration( controller, va( "%s.cfg", outfile ) ); } else if ( args.ArgC() == 2 ) { int controller = atoi( args[1] ); Host_WriteConfiguration( controller, "config.cfg" ); } else { Host_WriteConfiguration( -1, "config.cfg" ); } } CON_COMMAND( host_reset_config, "reset config (for testing) with param as splitscreen index." ) { if ( args.ArgC() <= 1 || args.ArgC() > 2) { ConMsg( "Usage: host_reset_config \n" ); return; } else if ( args.ArgC() == 2 ) { int controller = atoi( args[1] ); Host_ResetConfiguration( controller ); } } #ifdef _GAMECONSOLE CON_COMMAND( host_writeconfig_video_ss, "Store current video settings to config.cfg (or specified .cfg file) with first param as controller index." ) { if ( args.ArgC() != 2 ) { ConMsg( "Usage: writeconfig \n" ); return; } int controller = atoi( args[1] ); Host_WriteConfiguration_Console( controller, true ); } #endif #endif //----------------------------------------------------------------------------- // Purpose: Does a quick parse of the config.cfg to read cvars that // need to be read before any games systems are initialized // assumes only cvars and filesystem are initialized //----------------------------------------------------------------------------- void Host_ReadPreStartupConfiguration() { FileHandle_t f = NULL; if ( IsGameConsole() ) { // 360 config is less restrictive and can be anywhere in the game path f = g_pFileSystem->Open( "//game/cfg/config" PLATFORM_EXT ".cfg", "rt" ); } else { f = g_pFileSystem->Open( "//usrlocal/cfg/config.cfg", "rt" ); if ( !f ) f = g_pFileSystem->Open( "//mod/cfg/config.cfg", "rt" ); } if ( !f ) return; // read file into memory int size = g_pFileSystem->Size(f); char *configBuffer = new char[ size + 1 ]; g_pFileSystem->Read( configBuffer, size, f ); configBuffer[size] = 0; g_pFileSystem->Close( f ); // parse out file static const char *s_PreStartupConfigConVars[] = { "sv_unlockedchapters", // needed to display the startup graphic while loading "snd_legacy_surround", // needed to init the sound system "gameui_xbox", // needed to initialize the correct UI "save_in_memory", // needed to preread data from the correct location in UI #if defined( USE_SDL ) "sdl_displayindex" // needed to set multimonitor displayindex for SDL #endif }; // loop through looking for all the cvars to apply for (int i = 0; i < ARRAYSIZE(s_PreStartupConfigConVars); i++) { const char *search = Q_stristr(configBuffer, s_PreStartupConfigConVars[i]); if (search) { // read over the token search = COM_Parse(search); // read the value COM_Parse(search); // apply the value ConVar *var = (ConVar *)g_pCVar->FindVar( s_PreStartupConfigConVars[i] ); if ( var ) { var->SetValue( com_token ); } } } // free delete [] configBuffer; } void Host_RecomputeSpeed_f( void ) { ConMsg( "Recomputing clock speed...\n" ); CClockSpeedInit::Init(); ConMsg( "Clock speed: %.0f Mhz\n", CFastTimer::GetClockSpeed() / 1000000.0 ); } static ConCommand recompute_speed( "recompute_speed", Host_RecomputeSpeed_f, "Recomputes clock speed (for debugging purposes).", FCVAR_CHEAT ); void DTI_Flush_f() { DTI_Flush(); ServerDTI_Flush(); FlushDeltaBitsTrackingData(); } static ConCommand dti_flush( "dti_flush", DTI_Flush_f, "Write out the datatable instrumentation files (you must run with -dti for this to work)." ); /* ================== Host_ShutdownServer This only happens at the end of a game, not between levels ================== */ void Host_ShutdownServer( void ) { if ( !sv.IsActive() ) return; if ( IGameEvent *event = g_GameEventManager.CreateEvent( "server_pre_shutdown" ) ) { event->SetString( "reason", "restart" ); g_GameEventManager.FireEvent( event ); } // clear structures #if !defined( DEDICATED ) g_pShadowMgr->LevelShutdown(); #endif StaticPropMgr()->LevelShutdown(); Host_FreeStateAndWorld( true ); sv.Shutdown();// sv.Shutdown() references some heap memory, so run it before Host_FreeToLowMark() Host_FreeToLowMark( true ); #if defined( REPLAY_ENABLED ) if ( g_pServerReplayHistoryManager ) { g_pServerReplayHistoryManager->Shutdown(); g_pServerReplayHistoryManager = NULL; } #endif if ( IGameEvent *event = g_GameEventManager.CreateEvent( "server_shutdown" ) ) { event->SetString( "reason", "restart" ); g_GameEventManager.FireEvent( event ); } g_Log.Close(); } //----------------------------------------------------------------------------- // Purpose: // Input : time - // Output : bool //----------------------------------------------------------------------------- void Host_AccumulateTime( float dt ) { // Accumulate some time realtime += dt; bool bUseNormalTickTime = true; #if !defined(DEDICATED) if ( demoplayer->IsPlayingTimeDemo() ) bUseNormalTickTime = false; #endif if ( g_bDedicatedServerBenchmarkMode ) bUseNormalTickTime = false; if ( bUseNormalTickTime ) { host_frametime = dt; } else { // Used to help increase reproducibility of timedemos host_frametime = host_state.interval_per_tick; } extern bool g_bReplayLoadedTools; float flHostTimescale = 1.0f; float flGameTimescale = 1.0f; if ( host_timescale.GetFloat() > 0.0f #if !defined(DEDICATED) && CanCheat() #endif ) { // We are allowed to modify this convar! flHostTimescale = host_timescale.GetFloat(); } if ( flGameTimescale > 0.0f ) { flGameTimescale = sv.GetTimescale(); } float fullscale = flHostTimescale * flGameTimescale; #if !defined(DEDICATED) if ( demoplayer->IsPlayingBack() ) { // adjust time scale if playing back demo fullscale *= demoplayer->GetPlaybackTimeScale(); } #endif #if 1 if ( host_framerate.GetFloat() != 0 #if !defined(DEDICATED) && ( CanCheat() || demoplayer->IsPlayingBack() ) #endif ) { float fps = host_framerate.GetFloat(); if ( fps > 1 ) { fps = 1.0f/fps; } else if ( fps < -1 ) { fps = 1.0f/fabsf(fps); if ( fps > dt ) { fps = dt; } } host_frametime = fps; host_frametime_unbounded = host_frametime; host_frametime_unscaled = host_frametime; } else if ( fullscale != 1.0f ) { host_frametime_unscaled = host_frametime; host_frametime *= fullscale; host_frametime_unbounded = host_frametime; #ifndef NO_TOOLFRAMEWORK if ( CommandLine()->CheckParm( "-tools" ) == NULL && !g_bReplayLoadedTools ) { #endif // !NO_TOOLFRAMEWORK host_frametime = MIN( host_frametime, MAX_FRAMETIME * fullscale); host_frametime_unscaled = MIN( host_frametime_unscaled, MAX_FRAMETIME ); host_frametime_unscaled = MAX( host_frametime_unscaled, MIN_FRAMETIME ); #ifndef NO_TOOLFRAMEWORK } else { host_frametime = MIN( host_frametime, MAX_TOOLS_FRAMETIME * fullscale); host_frametime_unscaled = MIN( host_frametime_unscaled, MAX_TOOLS_FRAMETIME ); host_frametime_unscaled = MAX( host_frametime_unscaled, MIN_TOOLS_FRAMETIME ); } #endif // !NO_TOOLFRAMEWORK } else #ifndef NO_TOOLFRAMEWORK if ( CommandLine()->CheckParm( "-tools" ) != NULL && !g_bReplayLoadedTools ) { host_frametime_unbounded = host_frametime; host_frametime = MIN( host_frametime, MAX_TOOLS_FRAMETIME ); host_frametime = MAX( host_frametime, MIN_TOOLS_FRAMETIME ); host_frametime_unscaled = host_frametime; } else #endif // !NO_TOOLFRAMEWORK { // don't allow really long or short frames host_frametime_unbounded = host_frametime; host_frametime = MIN( host_frametime, MAX_FRAMETIME ); host_frametime = MAX( host_frametime, MIN_FRAMETIME ); host_frametime_unscaled = host_frametime; } #endif // 1 // Adjust the client clock very slightly to keep it in line with the server clock. #ifndef DEDICATED float adj = GetBaseLocalClient().GetClockDriftMgr().AdjustFrameTime( host_frametime ) - host_frametime; host_frametime += adj; host_frametime_unbounded += adj; host_frametime_unscaled += adj; #endif if ( g_pSoundServices ) // not present on linux server g_pSoundServices->SetSoundFrametime(dt, host_frametime); } #define FPS_AVG_FRAC 0.9f float g_fFramesPerSecond = 0.0f; // temporarily a constant until I bother to hook it to a cvar inline static bool cl_ps3ledframerate() // should i make the front LEDs show the framerate (divided by two) { #ifdef _PS3 return (CPS3FrontPanelLED::GetSwitches() & CPS3FrontPanelLED::kPS3SWITCH3) == 0; #else return false; #endif } /* ================== Host_PostFrameRate ================== */ void Host_PostFrameRate( float frameTime ) { extern int r_framecount; frameTime = clamp( frameTime, 0.0001f, 1.0f ); float fps = 1.0f / frameTime; g_fFramesPerSecond = g_fFramesPerSecond * FPS_AVG_FRAC + ( 1.0f - FPS_AVG_FRAC ) * fps; if ( IsPS3() && !IsCert() ) { if ( cl_ps3ledframerate() ) { uint64 switches = CPS3FrontPanelLED::GetSwitches(); int framerate = RoundFloatToInt( g_fFramesPerSecond * 0.5f ); // two modes -- with DIP 0 set to zero, if (( switches & CPS3FrontPanelLED::kPS3SWITCH0 )) { // make the front LEDs display the framerate divided by two in binary code // (ie to scale it so that 0..31 fits into 0..15) framerate = framerate > 31 ? 31 : framerate ; CPS3FrontPanelLED::SetLEDs( framerate >> 1 ); } else // without DIP0 set on, do a snazzy KNIGHT RIDER effect { static CPS3FrontPanelLED::eLEDIndex_t kitt[6] = { CPS3FrontPanelLED::kPS3LED0, CPS3FrontPanelLED::kPS3LED1, CPS3FrontPanelLED::kPS3LED2, // CPS3FrontPanelLED::kPS3LED3, CPS3FrontPanelLED::kPS3LED3, CPS3FrontPanelLED::kPS3LED2, CPS3FrontPanelLED::kPS3LED1 // , CPS3FrontPanelLED::kPS3LED0 }; CPS3FrontPanelLED::SetLEDs( kitt[ ( host_framecount >> 4 ) % 6] ); } } else { CPS3FrontPanelLED::SetLEDs( 0 ); } } } /* ================== Host_GetHostInfo ================== */ void Host_GetHostInfo(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen ) { // Count clients, report int clients = sv.GetNumClients(); *fps = g_fFramesPerSecond; *nActive = clients; if (pszMap) { if (sv.m_szMapname && sv.m_szMapname[0]) Q_strncpy(pszMap, sv.m_szMapname, maxlen ); else pszMap[0] = '\0'; } *nMaxPlayers = sv.GetMaxClients(); } static bool AppearsNumeric( char const *in ) { char const *p = in; int special[ 3 ]; Q_memset( special, 0, sizeof( special ) ); for ( ; *p; p++ ) { if ( *p == '-' ) { special[0]++; continue; } if ( *p == '+' ) { special[1]++; continue; } if ( *p >= '0' && *p <= '9' ) { continue; } if ( *p == '.' ) { special[2]++; continue; } return false; } // Can't have multiple +, -, or decimals for ( int i = 0; i < 3; i++ ) { if ( special[ i ] > 1 ) return false; } // can't be + and - at same time if ( special[ 0 ] && special[ 1 ] ) return false; return true; } //----------------------------------------------------------------------------- // Purpose: If the value is numeric, remove unnecessary trailing zeros // Input : *invalue - // Output : char const //----------------------------------------------------------------------------- char const * Host_CleanupConVarStringValue( char const *invalue ) { static char clean[ 256 ]; Q_snprintf( clean, sizeof( clean ), "%s", invalue ); // Don't mess with empty string // Otherwise, if it appears numeric and has a decimal, try to strip all zeroes after decimal if ( Q_strlen( clean ) >= 1 && AppearsNumeric( clean ) && Q_strstr( clean, "." ) ) { char *end = clean + strlen( clean ) - 1; while ( *end && end >= clean ) { // Removing trailing zeros if ( *end != '0' ) { // Remove decimal, zoo if ( *end == '.' ) { if ( end == clean ) { *end = '0'; } else { *end = 0; } } break; } *end-- = 0; } } return clean; } int Host_CountVariablesWithFlags( int flags, bool nonDefault ) { int c = 0; ICvar::Iterator iter( g_pCVar ); for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() ) { ConCommandBase *var = iter.Get(); if ( var->IsCommand() ) continue; const ConVar *pthiscvar = ( const ConVar * )var; if ( !pthiscvar->IsFlagSet( flags ) ) continue; // It's == to the default value, don't count if ( nonDefault && !Q_strcasecmp( pthiscvar->GetDefault(), pthiscvar->GetString() ) ) continue; ++c; } return c; } //----------------------------------------------------------------------------- // Purpose: // Input : msg - //----------------------------------------------------------------------------- void Host_BuildUserInfoUpdateMessage( int nSplitScreenSlot, CMsg_CVars *rCvarList, bool nonDefault ) { // Slot 0 does the easy version, all userinfo, except _ADDED ones int nRequiredFlags = FCVAR_USERINFO; int nDisallowedFlags = FCVAR_SS_ADDED; if ( nSplitScreenSlot != 0 ) { // Slot one only looks at added ones nDisallowedFlags = FCVAR_SS; nRequiredFlags = FCVAR_USERINFO | FCVAR_SS_ADDED; } int count = Host_CountVariablesWithFlags( nRequiredFlags, nonDefault ); // Nothing to send if ( count <= 0 ) return; #if !defined( _GAMECONSOLE ) && !defined( DEDICATED ) // Add local user SteamID if ( Steam3Client().SteamUser() && Steam3Client().SteamUser()->GetSteamID().GetAccountID() ) { ++ count; NetMsgSetCVarUsingDictionary( rCvarList->add_cvars(), "accountid", CFmtStr( "%u", Steam3Client().SteamUser()->GetSteamID().GetAccountID() ) ); } #endif ICvar::Iterator iter( g_pCVar ); for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() ) { ConCommandBase *var = iter.Get(); if ( var->IsCommand() ) continue; const ConVar *pthiscvar = ( const ConVar * )var; if ( !pthiscvar->IsFlagSet( nRequiredFlags ) || pthiscvar->IsFlagSet( nDisallowedFlags ) ) continue; // It's == to the default value, don't count if ( nonDefault && !Q_strcasecmp( pthiscvar->GetDefault(), pthiscvar->GetString() ) ) continue; if ( pthiscvar->GetSplitScreenPlayerSlot() != nSplitScreenSlot ) continue; NetMsgSetCVarUsingDictionary( rCvarList->add_cvars(), pthiscvar->GetBaseName(), Host_CleanupConVarStringValue( pthiscvar->GetString() ) ); } // Too many to send, error out and have mod author get a clue. if ( rCvarList->cvars_size() > 255 ) { Sys_Error( "Engine only supports 255 ConVars marked %i\n", FCVAR_USERINFO ); return; } // Make sure this count matches original one Assert( rCvarList->cvars_size() <= count ); } //----------------------------------------------------------------------------- // Purpose: // Input : msg - //----------------------------------------------------------------------------- void Host_BuildConVarUpdateMessage( CMsg_CVars *rCvarList, int flags, bool nonDefault ) { int count = Host_CountVariablesWithFlags( flags, nonDefault ); // Nothing to send if ( count <= 0 ) return; // Too many to send, error out and have mod author get a clue. if ( count > 255 ) { Sys_Error( "Engine only supports 255 ConVars marked %i\n", flags ); return; } ICvar::Iterator iter( g_pCVar ); for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() ) { ConCommandBase *var = iter.Get(); if ( var->IsCommand() ) continue; const ConVar *pthiscvar = ( const ConVar * )var; if ( !pthiscvar->IsFlagSet( flags ) ) continue; // It's == to the default value, don't count if ( nonDefault && !Q_strcasecmp( pthiscvar->GetDefault(), pthiscvar->GetString() ) ) continue; NetMsgSetCVarUsingDictionary( rCvarList->add_cvars(), pthiscvar->GetName(), Host_CleanupConVarStringValue( pthiscvar->GetString() ) ); } } #if !defined( DEDICATED ) // FIXME: move me somewhere more appropriate extern IVEngineClient *engineClient; void CL_SendVoicePacket(bool bFinal) { #if !defined( NO_VOICE ) if ( !Voice_IsRecording() ) return; // Get whatever compressed data there is and and send it. char uchVoiceData[8192]; VoiceFormat_t format; uint8 nSectionNumber; uint32 nSectionSequenceNumber; uint32 nUncompressedSampleOffset; int nLength = Voice_GetCompressedData( uchVoiceData, sizeof( uchVoiceData ), bFinal, &format, &nSectionNumber, &nSectionSequenceNumber, &nUncompressedSampleOffset ); if( nLength && GetBaseLocalClient().IsActive() ) { CCLCMsg_VoiceData_t voiceMsg; voiceMsg.set_data( uchVoiceData, nLength ); if ( format == VoiceFormat_Steam ) { voiceMsg.set_format( VOICEDATA_FORMAT_STEAM ); } else { voiceMsg.set_format( VOICEDATA_FORMAT_ENGINE ); } player_info_t playerInfo; if ( engineClient->GetPlayerInfo( engineClient->GetLocalPlayer(), &playerInfo ) ) { voiceMsg.set_xuid( playerInfo.xuid ); } voiceMsg.set_section_number( nSectionNumber ); voiceMsg.set_sequence_bytes( nSectionSequenceNumber ); voiceMsg.set_uncompressed_sample_offset( nUncompressedSampleOffset ); GetBaseLocalClient().m_NetChannel->SendNetMsg( voiceMsg ); } #endif } #if defined ( _GAMECONSOLE ) void CL_ProcessGameConsoleVoiceData() { if ( g_pMatchFramework && !(g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_VOICE_INGAME) ) return; // Title plays voice via lobby system #if defined( _PS3 ) && defined( CLIENT_DLL ) bool restrictedFromChat = engine->PS3_IsUserRestrictedFromChat( ); if ( restrictedFromChat ) return; #endif if ( Audio_GetXVoice() == NULL ) return; for ( int k = 0; k < XBX_GetNumGameUsers(); ++ k ) { int iCtrlr = XBX_GetUserId( k ); ACTIVE_SPLITSCREEN_PLAYER_GUARD( k ); if ( Audio_GetXVoice()->VoiceUpdateData( iCtrlr ) ) { if ( GetLocalClient( k ).IsActive() ) { Audio_GetXVoice()->VoiceSendData( iCtrlr, GetLocalClient( k ).m_NetChannel ); } } } } #endif void CL_ProcessVoiceData() { VPROF_BUDGET( "CL_ProcessVoiceData", VPROF_BUDGETGROUP_OTHER_NETWORKING ); #if !defined( NO_VOICE ) if ( Voice_Idle( host_state.interval_per_tick ) ) { CL_SendVoicePacket( false ); } #endif #if defined ( _GAMECONSOLE ) CL_ProcessGameConsoleVoiceData(); #endif } #endif /* ===================== Host_UpdateScreen Refresh the screen ===================== */ void Host_UpdateScreen( void ) { #ifndef DEDICATED if( r_ForceRestore.GetInt() ) { ForceMatSysRestore(); r_ForceRestore.SetValue(0); } // Refresh the screen SCR_UpdateScreen (); #endif } /* ==================== Host_UpdateSounds Update sound subsystem and cd audio ==================== */ void Host_UpdateSounds( void ) { SNPROF("Host_UpdateSounds"); #if !defined( DEDICATED ) MDLCACHE_COARSE_LOCK_(g_pMDLCache); // update audio if ( GetBaseLocalClient().IsActive() ) { S_Update( &s_AudioState ); } else { S_Update( NULL ); } #endif } /* ============================== Host_Speeds ============================== */ void CFrameTimer::ResetDeltas() { for ( int i = 0; i < NUM_FRAME_SEGMENTS; i++ ) { deltas[ i ] = 0.0f; } } void CFrameTimer::MarkFrame() { double frameTime; double fps; // ConDMsg("%f %f %f\n", time1, time2, time3 ); float fs_input = (deltas[FRAME_SEGMENT_INPUT])*1000.0; float fs_client = (deltas[FRAME_SEGMENT_CLIENT])*1000.0; float fs_server = (deltas[FRAME_SEGMENT_SERVER])*1000.0; float fs_render = (deltas[FRAME_SEGMENT_RENDER])*1000.0; float fs_sound = (deltas[FRAME_SEGMENT_SOUND])*1000.0; float fs_cldll = (deltas[FRAME_SEGMENT_CLDLL])*1000.0; float fs_exec = (deltas[FRAME_SEGMENT_CMD_EXECUTE])*1000.0; m_flLastServerTime = fs_server; ResetDeltas(); frameTime = host_frametime; //frameTime /= 1000.0; if ( frameTime < 0.0001 ) { fps = 999.0; } else { fps = 1.0 / frameTime; } if (host_speeds.GetInt()) { int ent_count = 0; int i; static int last_host_tickcount; int ticks = host_tickcount - last_host_tickcount; last_host_tickcount = host_tickcount; // count used entities for (i=0 ; i= 2 ) { Con_NPrintf ( 0, sz ); } else { ConDMsg ( "%s\n", sz ); } #endif } } #define FRAME_TIME_FILTER_TIME 0.5f void CFrameTimer::ComputeFrameVariability() { m_pFrameTimeHistory[m_nFrameTimeHistoryIndex] = frametime; m_pFrameStartTimeHistory[m_nFrameTimeHistoryIndex] = framestarttimeduration; if ( ++m_nFrameTimeHistoryIndex >= FRAME_HISTORY_COUNT ) { m_nFrameTimeHistoryIndex = 0; } // Compute a low-pass filter of the frame time over the last half-second // Count the number of samples that live within the last half-second int i = m_nFrameTimeHistoryIndex; int nMaxSamples = 0; float flTotalTime = 0.0f; while( (nMaxSamples < FRAME_HISTORY_COUNT) && (flTotalTime <= FRAME_TIME_FILTER_TIME) ) { if ( --i < 0 ) { i = FRAME_HISTORY_COUNT - 1; } if ( m_pFrameTimeHistory[i] == 0.0f ) break; flTotalTime += m_pFrameTimeHistory[i]; ++nMaxSamples; } if ( nMaxSamples == 0 ) { m_flFPSVariability = 0.0f; m_flFPSStdDeviationSeconds = 0.0f; m_flFPSStdDeviationFrameStartTimeSeconds = 0.0f; return; } float flExponent = -2.0f / (int)nMaxSamples; i = m_nFrameTimeHistoryIndex; float flAverageTime = 0.0f; float flExpCurveArea = 0.0f; int n = 0; while( n < nMaxSamples ) { if ( --i < 0 ) { i = FRAME_HISTORY_COUNT - 1; } flExpCurveArea += exp( flExponent * n ); flAverageTime += m_pFrameTimeHistory[i] * exp( flExponent * n ); ++n; } flAverageTime /= flExpCurveArea; float flAveFPS = 0.0f; if ( flAverageTime != 0.0f ) { flAveFPS = 1.0f / flAverageTime; } float flCurrentFPS = 0.0f; if ( frametime != 0.0f ) { flCurrentFPS = 1.0f / frametime; } // Now subtract out the current fps to get variability in FPS m_flFPSVariability = fabs( flCurrentFPS - flAveFPS ); // Now compute variance/stddeviation double sumFrameTime = 0.0f, sumFrameStartTime = 0.0f; int count = 0; for ( int ii1 = 0; ii1 < FRAME_HISTORY_COUNT; ++ii1 ) { if ( m_pFrameTimeHistory[ ii1 ] == 0.0f ) continue; double ft = MIN( m_pFrameTimeHistory[ ii1 ], 0.25 ); sumFrameTime += ft; ft = MIN( m_pFrameStartTimeHistory[ ii1 ], 0.25 ); sumFrameStartTime += ft; ++count; } if ( count <= 1 ) { return; } double avgFrameTime = sumFrameTime / (double)count; double devSquaredFrameTime = 0.0f; double avgFrameStartTime = sumFrameStartTime / ( double ) count; double devSquaredFrameStartTime = 0.0f; for ( int ii2 = 0; ii2 < FRAME_HISTORY_COUNT; ++ii2 ) { if ( m_pFrameTimeHistory[ ii2 ] == 0.0f ) continue; double ft = MIN( m_pFrameTimeHistory[ ii2 ], 0.25 ); double dt = ft - avgFrameTime; devSquaredFrameTime += ( dt * dt ); ft = MIN( m_pFrameStartTimeHistory[ ii2 ], 0.25 ); dt = ft - avgFrameStartTime; devSquaredFrameStartTime += ( dt * dt ); } double variance = devSquaredFrameTime / (double)( count ); m_flFPSStdDeviationSeconds = sqrt( variance ); variance = devSquaredFrameStartTime / ( double ) ( count ); m_flFPSStdDeviationFrameStartTimeSeconds = sqrt( variance ); tmPlot( TELEMETRY_LEVEL0, TMPT_NONE, 0, m_flFPSStdDeviationSeconds * 1000.0f, "m_flFPSStdDeviationSeconds(ms)" ); tmPlot( TELEMETRY_LEVEL0, TMPT_NONE, 0, m_flFPSStdDeviationFrameStartTimeSeconds * 1000.0f, "m_flFPSStdDeviationFrameStartTimeMS(ms)" ); // printf("var: %.2f avg:%.6f frametime:%f\n", m_flFPSStdDeviationSeconds * 1000.0f, avg, frametime); } void Host_Speeds() { g_HostTimes.MarkFrame(); #if !defined(DEDICATED) g_pClientDemoPlayer->MarkFrame( g_HostTimes.m_flFPSVariability ); #endif } //----------------------------------------------------------------------------- // Purpose: When singlestep == 1, then you must set next == 1 to run to the // next frame. // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool Host_ShouldRun( void ) { static int current_tick = -1; // See if we are single stepping if ( !singlestep.GetInt() ) { return true; } // Did user set "next" to 1? if ( next.GetInt() ) { // Did we finally finish this frame ( Host_ShouldRun is called in 3 spots to pause // three different things ). if ( current_tick != (host_tickcount-1) ) { // Okay, time to reset to halt execution again next.SetValue( 0 ); return false; } // Otherwise, keep running this one frame since we are still finishing this frame return true; } else { // Remember last frame without "next" being reset ( time is locked ) current_tick = host_tickcount; // Time is locked return false; } } static ConVar mem_periodicdumps( "mem_periodicdumps", "0", 0, "Write periodic memstats dumps every n seconds." ); static double g_flLastPeriodicMemDump = -1.0f; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- static float g_TimeLastMemTest; void Host_CheckDumpMemoryStats( void ) { if ( mem_test_each_frame.GetBool() ) { if ( !MemTest() ) { DebuggerBreakIfDebugging(); Error( "Heap is corrupt\n" ); } } else if ( mem_test_every_n_seconds.GetInt() > 0 ) { float now = Plat_FloatTime(); if ( now - g_TimeLastMemTest > mem_test_every_n_seconds.GetInt() ) { g_TimeLastMemTest = now; if ( !MemTest() ) { DebuggerBreakIfDebugging(); Error( "Heap is corrupt\n" ); } } } if ( mem_periodicdumps.GetFloat() > 0.0f ) { double curtime = Plat_FloatTime(); if ( curtime - g_flLastPeriodicMemDump > mem_periodicdumps.GetFloat() ) { const char *mapname = GetMapName(); Host_PrintMemoryStatus( mapname ); g_pMemAlloc->DumpStatsFileBase( mapname ); g_flLastPeriodicMemDump = curtime; } } #if defined(_WIN32) if ( mem_dumpstats.GetInt() <= 0 ) return; if ( mem_dumpstats.GetInt() == 1 ) mem_dumpstats.SetValue( 0 ); // reset cvar, dump stats only once _CrtMemState state; Q_memset( &state, 0, sizeof( state ) ); _CrtMemCheckpoint( &state ); unsigned int size = 0; for ( int use = 0; use < _MAX_BLOCKS; use++) { size += state.lSizes[ use ]; } Msg("MEMORY: Run-time Heap\n------------------------------------\n"); Msg( "\tHigh water %s\n", Q_pretifymem( state.lHighWaterCount,4 ) ); Msg( "\tCurrent mem %s\n", Q_pretifymem( size,4 ) ); Msg("------------------------------------\n"); int hunk = Hunk_MallocSize(); Msg("\tAllocated outside hunk: %s\n", Q_pretifymem( size - hunk ) ); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void _Host_SetGlobalTime() { // Server g_ServerGlobalVariables.realtime = realtime; g_ServerGlobalVariables.framecount = host_framecount; g_ServerGlobalVariables.absoluteframetime = host_frametime; g_ServerGlobalVariables.absoluteframestarttimestddev = host_framestarttime_stddeviation; g_ServerGlobalVariables.interval_per_tick = host_state.interval_per_tick; g_ServerGlobalVariables.serverCount = Host_GetServerCount(); #ifndef DEDICATED // Client g_ClientGlobalVariables.realtime = realtime; g_ClientGlobalVariables.framecount = host_framecount; g_ClientGlobalVariables.absoluteframetime = host_frametime; g_ClientGlobalVariables.absoluteframestarttimestddev = host_framestarttime_stddeviation; g_ClientGlobalVariables.interval_per_tick = host_state.interval_per_tick; #endif } /* ================== _Host_RunFrame Runs all active servers ================== */ void _Host_RunFrame_Input( float accumulated_extra_samples, bool bFinalTick ) { VPROF_BUDGET( "_Host_RunFrame_Input", _T("Input") ); // Run a test script? static bool bFirstFrame = true; if ( bFirstFrame ) { bFirstFrame = false; const char *pScriptFilename = CommandLine()->ParmValue( "-testscript" ); if ( pScriptFilename && pScriptFilename[0] ) { GetTestScriptMgr()->StartTestScript( pScriptFilename ); } // init the net console if we haven't yet InitNetConsole(); NET_InitPostFork(); } g_HostTimes.StartFrameSegment( FRAME_SEGMENT_INPUT ); #ifndef DEDICATED // Client can process input ClientDLL_ProcessInput( ); g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CMD_EXECUTE ); // process console commands Cbuf_Execute (); g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CMD_EXECUTE ); // Send any current movement commands to server and flush reliable buffer even if not moving yet. CL_Move( accumulated_extra_samples, bFinalTick ); #endif g_HostTimes.EndFrameSegment( FRAME_SEGMENT_INPUT ); } void _Host_ProcessVoice_Server( void ) { VPROF_BUDGET( "_Host_ProcessVoice_Server", VPROF_BUDGETGROUP_GAME ); SV_ProcessVoice(); } void _Host_RunFrame_Server( bool finaltick ) { VPROF_BUDGET( "_Host_RunFrame_Server", VPROF_BUDGETGROUP_GAME ); VPROF_INCREMENT_COUNTER( "ticks", 1 ); VPROF_TEST_SPIKE( vprof_server_spike_threshold.GetFloat() ); // Run the Server frame ( read, run physics, respond ) g_HostTimes.StartFrameSegment( FRAME_SEGMENT_SERVER ); SV_Frame( finaltick ); g_HostTimes.EndFrameSegment( FRAME_SEGMENT_SERVER ); // Look for connectionless rcon packets on dedicated servers // SV_CheckRcom(); TODO } void _Host_RunFrame_Server_Async( int numticks ) { #ifdef VPROF_ENABLED if ( vprof_server_thread.GetBool() ) { if ( g_VProfTargetThread != ThreadGetCurrentId() ) { g_VProfTargetThread = ThreadGetCurrentId(); } } else { if ( g_VProfTargetThread == ThreadGetCurrentId() ) { g_VProfTargetThread = g_MainThreadId; } } #endif MDLCACHE_COARSE_LOCK_(g_pMDLCache); for ( int tick = 0; tick < numticks; tick++ ) { g_ServerGlobalVariables.tickcount = sv.m_nTickCount; g_ServerGlobalVariables.simTicksThisFrame = numticks - tick; bool bFinalTick = ( tick == (numticks - 1) ); _Host_RunFrame_Server( bFinalTick ); } } void _Host_RunFrame_Client( bool framefinished ) { #ifndef DEDICATED VPROF( "_Host_RunFrame_Client" ); g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CLIENT ); // Get any current state update from server, etc. CL_ReadPackets( framefinished ); GetBaseLocalClient().CheckUpdatingSteamResources(); GetBaseLocalClient().CheckFileCRCsWithServer(); // Resend connection request if needed. GetBaseLocalClient().RunFrame(); if ( CL_IsHL2Demo() || CL_IsPortalDemo() ) // don't need sv.IsDedicated() because ded servers don't run this { void CL_DemoCheckGameUIRevealTime(); CL_DemoCheckGameUIRevealTime(); } Steam3Client().RunFrame(); #if defined( _DEBUG ) // Debug!!! FireGameEvent g_GameEventManager.VerifyListenerList(); #endif g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CLIENT ); // This takes 1 usec, so it's pretty cheap... CL_SetPagedPoolInfo(); #endif } //----------------------------------------------------------------------------- // Purpose: Used to set limits on certain convars in multiplayer/sv_cheats mode. // Returns true if it was called recursively and it early-outed. //----------------------------------------------------------------------------- bool CheckVarRange_Generic( ConVar *pVar, int minVal, int maxVal ) { // Don't reenter (resetting the variable when we're checking the range might cause us to reenter here). static bool bInFunction = false; if ( bInFunction ) return true; bInFunction = true; if ( !CanCheat() && !Host_IsSinglePlayerGame() ) { int clampedValue = clamp( pVar->GetInt(), minVal, maxVal ); if ( clampedValue != pVar->GetInt() ) { Warning( "sv_cheats=0 prevented changing %s outside of the range [0,2] (was %d).\n", pVar->GetName(), pVar->GetInt() ); pVar->SetValue( clampedValue ); } } bInFunction = false; return false; } void CheckSpecialCheatVars() { static ConVar *mat_picmip = NULL; if ( !mat_picmip ) mat_picmip = g_pCVar->FindVar( "mat_picmip" ); // In multiplayer, don't allow them to set mat_picmip > 2. if ( mat_picmip ) CheckVarRange_Generic( mat_picmip, -10, 2 ); CheckVarRange_r_rootlod(); CheckVarRange_r_lod(); } void _Host_RunFrame_Render() { #ifndef DEDICATED VPROF( "_Host_RunFrame_Render" ); CheckSpecialCheatVars(); int nOrgNoRendering = mat_norendering.GetInt(); if ( cl_takesnapshot ) { // turn off no-rendering mode, if taking screenshot mat_norendering.SetValue( 0 ); } // update video if not running in background g_HostTimes.StartFrameSegment( FRAME_SEGMENT_RENDER ); CL_LatchInterpolationAmount(); { VPROF( "_Host_RunFrame_Render - UpdateScreen" ); Host_UpdateScreen(); } { VPROF( "_Host_RunFrame_Render - CL_DecayLights" ); CL_DecayLights (); } g_HostTimes.EndFrameSegment( FRAME_SEGMENT_RENDER ); saverestore->OnFrameRendered(); #ifdef USE_SDL if ( g_pLauncherMgr ) { g_pLauncherMgr->OnFrameRendered(); } #endif mat_norendering.SetValue( nOrgNoRendering ); #endif } void CL_FindInterpolatedAddAngle( float t, float& frac, AddAngle **prev, AddAngle **pnextangle ) { #ifndef DEDICATED int c = GetLocalClient().addangle.Count(); *prev = NULL; *pnextangle = NULL; AddAngle *pentry = NULL; for ( int i = 0; i < c; i++ ) { AddAngle *entry = &GetLocalClient().addangle[ i ]; *pnextangle = entry; // Time is earlier if ( t < entry->starttime ) { if ( i == 0 ) { *prev = *pnextangle; frac = 0.0f; return; } // Avoid div by zero if ( entry->starttime == pentry->starttime ) { frac = 0.0f; return; } // Time spans the two entries frac = ( t - pentry->starttime ) / ( entry->starttime - pentry->starttime ); frac = clamp( frac, 0.0f, 1.0f ); return; } *prev = *pnextangle; pentry = entry; } #endif } void CL_DiscardOldAddAngleEntries( float t ) { #ifndef DEDICATED float killtime = t - host_state.interval_per_tick - 0.1f; for ( int i = 0; i < GetLocalClient().addangle.Count(); i++ ) { AddAngle *p = &GetLocalClient().addangle[ i ]; if ( p->starttime <= killtime ) { GetLocalClient().addangle.Remove( i ); --i; } } // It's safe to reset the master counter once all the entries decay if ( GetLocalClient().addangle.Count() == 0 ) { GetLocalClient().prevaddangletotal = GetLocalClient().addangletotal = 0.0f; } #endif } #ifndef DEDICATED void CL_ApplyAddAngle() { FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh ) { ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh ); float curtime = GetBaseLocalClient().GetTime() - host_state.interval_per_tick; AddAngle *prev = NULL, *pnextangle = NULL; float frac = 0.0f; float addangletotal = 0.0f; CL_FindInterpolatedAddAngle( curtime, frac, &prev, &pnextangle ); if ( prev && pnextangle ) { addangletotal = prev->total + frac * ( pnextangle->total - prev->total ); } else { addangletotal = GetLocalClient().prevaddangletotal; } float amove = addangletotal - GetLocalClient().prevaddangletotal; // Update view angles GetLocalClient().viewangles[ 1 ] += amove; // Update client .dll view of angles g_pClientSidePrediction->SetLocalViewAngles( GetLocalClient().viewangles ); // Remember last total GetLocalClient().prevaddangletotal = addangletotal; CL_DiscardOldAddAngleEntries( curtime ); } } #endif CJob *g_pSoundJob; bool g_bAllowThreadedSound; void _Host_RunFrame_Sound() { #ifndef DEDICATED if ( g_pSoundJob ) { return; } VPROF_BUDGET( "_Host_RunFrame_Sound", VPROF_BUDGETGROUP_OTHER_SOUND ); g_HostTimes.StartFrameSegment( FRAME_SEGMENT_SOUND ); if ( !host_threaded_sound.GetBool() || !g_bAllowThreadedSound ) { Host_UpdateSounds(); } g_HostTimes.EndFrameSegment( FRAME_SEGMENT_SOUND ); #endif } void Host_BeginThreadedSound() { #ifndef DEDICATED #ifdef _PS3 if (sv.IsActive()) { Host_UpdateSounds(); g_pSoundJob = NULL; return; } else if(host_threaded_sound_simplethread.GetBool()) { SNPROF("Kick Sound"); g_pGcmSharedData->RunAudio(Host_UpdateSounds); g_pSoundJob = (CJob*)1; return; } #endif if ( !host_threaded_sound.GetBool() || !g_bAllowThreadedSound ) { return; } g_pSoundJob = new CFunctorJob( CreateFunctor( Host_UpdateSounds ) ); IThreadPool *pSoundThreadPool; #ifdef _X360 pSoundThreadPool = g_pAlternateThreadPool; #else pSoundThreadPool = g_pThreadPool; #endif if ( IsX360() ) { g_pSoundJob->SetServiceThread( g_nServerThread ); } pSoundThreadPool->AddJob( g_pSoundJob ); #endif } void Host_EndThreadedSound() { if ( !g_pSoundJob ) { return; } #ifdef _PS3 if(host_threaded_sound_simplethread.GetBool()) { SNPROF("Wait for Sound"); g_pGcmSharedData->WaitForAudio(); g_pSoundJob = NULL; return; } #endif VPROF_BUDGET( "_Host_RunFrame_Sound", VPROF_BUDGETGROUP_OTHER_SOUND ); g_pSoundJob->WaitForFinishAndRelease(); g_pSoundJob = NULL; } float SV_GetSoundDuration( const char *pSample ); #ifndef DEDICATED float AudioSource_GetSoundDuration( CSfxTable *pSfx ); #endif float Host_GetSoundDuration( const char *pSample ) { #ifndef DEDICATED // bug 27822 (crash when leaving 360 credits map) // If we don't check connected here, then client can be partially through disconnecting (stringtable dictionary wiped, etc) // but still have the m_pStringTableDictionary pointer hanging around and then the server, on another thread, // calls through this case and tries to read the memory and crashes... CClientState &cl = GetBaseLocalClient(); if ( cl.IsConnected() ) { int index = cl.LookupSoundIndex( pSample ); if ( index >= 0 ) { CSfxTable *pSfxTable = cl.GetSound( index ); if ( ( pSfxTable != NULL) && pSfxTable->m_bIsLateLoad ) { DevMsg( " Reason for late load of '%s': Calling Host_GetSoundDuration().\n", pSample ); } return AudioSource_GetSoundDuration( pSfxTable ); } } #endif return SV_GetSoundDuration( pSample ); } CON_COMMAND( host_runofftime, "Run off some time without rendering/updating sounds\n" ) { if ( args.ArgC() != 2 ) { ConMsg( "Usage: host_runofftime \n" ); return; } if ( !sv.IsActive() ) { ConMsg( "host_runofftime: must be running a server\n" ); return; } if ( sv.IsMultiplayer() ) { ConMsg( "host_runofftime: only valid in single player\n" ); return; } float advanceTime = atof( args[1] ); if ( advanceTime <= 0.0f ) return; // 15 minutes is a _long_ time!!! if ( advanceTime > 15.0f * 60.0f ) { ConMsg( "host_runofftime would run off %.2f minutes!!! ignoring\n", advanceTime / 60.0f ); return; } ConMsg( "Skipping ahead for %f seconds\n", advanceTime ); SCR_UpdateScreen(); SCR_UpdateScreen (); } #if !defined( _GAMECONSOLE ) S_API int SteamGameServer_GetIPCCallCount(); #else S_API int SteamGameServer_GetIPCCallCount() { return 0; } #endif void Host_ShowIPCCallCount() { // If set to 0 then get out. if ( host_ShowIPCCallCount.GetInt() == 0 ) return; static float s_flLastTime = 0; static int s_nLastTick = host_tickcount; static int s_nLastFrame = host_framecount; // Figure out how often they want to update. double flInterval = 0; if ( host_ShowIPCCallCount.GetFloat() > 0 ) { flInterval = 1.0f / host_ShowIPCCallCount.GetFloat(); } // This is called every frame so increment the frame counter. double flCurTime = Plat_FloatTime(); if ( flCurTime - s_flLastTime >= flInterval ) { uint32 callCount = 0; #ifndef NO_STEAM ISteamClient *pSteamClient = #ifdef DEDICATED Steam3Server().SteamClient() #else Steam3Client().SteamClient() #endif ; if ( pSteamClient ) { callCount = pSteamClient->GetIPCCallCount(); } else { // Ok, we're a dedicated server and we need to use this to get it. callCount = (uint32)SteamGameServer_GetIPCCallCount(); } #endif // Avoid a divide by zero. int frameCount = host_framecount - s_nLastFrame; int tickCount = host_tickcount - s_nLastTick; if ( frameCount == 0 || tickCount == 0 ) return; Msg( "host_ShowIPCCallCount: %d IPC calls in the past [%d frames, %d ticks] Avg: [%.2f/frame, %.2f/tick]\n", callCount, frameCount, tickCount, (float)callCount / frameCount, (float)callCount / tickCount ); s_flLastTime = flCurTime; s_nLastTick = host_tickcount; s_nLastFrame = host_framecount; } } void Host_SetClientInSimulation( bool bInSimulation ) { #ifndef DEDICATED GetBaseLocalClient().insimulation = bInSimulation; // Compute absolute/render time stamp g_ClientGlobalVariables.curtime = GetBaseLocalClient().GetTime(); g_ClientGlobalVariables.frametime = GetBaseLocalClient().GetFrameTime(); #endif } static ConVar host_Sleep( "host_sleep", "0", FCVAR_CHEAT, "Force the host to sleep a certain number of milliseconds each frame." ); extern ConVar sv_alternateticks; ConVar host_print_frame_times( "host_print_frame_times", "0" ); #ifndef DEDICATED static void PrintHostFrameTimes( int nNumTicks, float flHostRemainder, float flMinimumTickInterval ) { const int nFrameHistorySize = 100; static float flFrameTimes[nFrameHistorySize]; static int nFrameIndex = 0; flFrameTimes[nFrameIndex] = host_frametime; nFrameIndex = ( nFrameIndex + 1 ) % nFrameHistorySize; float flMinFrameTime = 1.0f; float flAvgFrameTime = 0.0f; for ( int i = 0; i < nFrameHistorySize; ++ i ) { if ( flFrameTimes[i] < flMinFrameTime ) { flMinFrameTime = flFrameTimes[i]; } flAvgFrameTime += flFrameTimes[i]; } flAvgFrameTime /= (float)nFrameHistorySize; con_nprint_t printinfo; printinfo.index = 1; printinfo.time_to_live = -1; printinfo.color[0] = printinfo.color[1] = printinfo.color[2] = 1.0f; printinfo.fixed_width_font = true; Con_NXPrintf( &printinfo, "ticks: %d, host_remainder: %f, host_frametime: %f, minimum interval: %f\n", nNumTicks, flHostRemainder, host_frametime, flMinimumTickInterval ); printinfo.index = 2; Con_NXPrintf( &printinfo, "Running min frametime: %f, running avg frametime: %f\n", flMinFrameTime, flAvgFrameTime ); } #endif // DEDICATED #if !( defined( _CERT ) || defined( DEDICATED ) ) ConVar fs_enable_stats( "fs_enable_stats", "0" ); static void PrintFsStats() { const int nFrameHistorySize = 100; const int nStatsSize = 6; static int nStats[nStatsSize][nFrameHistorySize] = { 0 }; static const char * pStatsTitle[] = { "# of seeks", "Time seeking", "# of reads", "Time reading", "Bytes read", "# of fopen", }; enum Mode { NORMAL, IN_MS, IN_BYTES, SKIPPED, }; static const Mode nModes[] = { NORMAL, IN_MS, NORMAL, IN_MS, IN_BYTES, SKIPPED, // In the Source1 engine, there is a very strong correlation between fopen, seeks and reads. Don't display this one. }; static int nFrameIndex = 0; IIoStats *pIoStats = g_pFileSystem->GetIoStats(); if ( pIoStats == NULL ) { con_nprint_t printinfo; printinfo.index = 1; printinfo.time_to_live = -1; printinfo.color[0] = printinfo.color[1] = printinfo.color[2] = 1.0f; printinfo.fixed_width_font = true; Con_NXPrintf( &printinfo, "IO stats is disabled.\n" ); return; } nStats[0][nFrameIndex] = pIoStats->GetNumberOfFileSeeks(); nStats[1][nFrameIndex] = pIoStats->GetTimeInFileSeek(); nStats[2][nFrameIndex] = pIoStats->GetNumberOfFileReads(); nStats[3][nFrameIndex] = pIoStats->GetTimeInFileReads(); nStats[4][nFrameIndex] = pIoStats->GetFileReadTotalSize(); nStats[5][nFrameIndex] = pIoStats->GetNumberOfFileOpens(); pIoStats->Reset(); nFrameIndex = ( nFrameIndex + 1 ) % nFrameHistorySize; int nMinStats[nStatsSize], nMaxStats[nStatsSize], nAvgStats[nStatsSize]; for (int i = 0 ; i < nStatsSize ; ++i ) { if ( nModes[i] == SKIPPED ) { continue; // Skip this one } nMinStats[i] = INT_MAX; nMaxStats[i] = INT_MIN; nAvgStats[i] = 0; for ( int j = 0; j < nFrameHistorySize; ++j ) { if ( nStats[i][j] < nMinStats[i] ) { nMinStats[i] = nStats[i][j]; } else if ( nStats[i][j] > nMaxStats[i] ) { nMaxStats[i] = nStats[i][j]; } nAvgStats[i] += nStats[i][j]; } } con_nprint_t printinfo; printinfo.index = 1; printinfo.time_to_live = -1; printinfo.color[0] = printinfo.color[1] = printinfo.color[2] = 1.0f; printinfo.fixed_width_font = true; Con_NXPrintf( &printinfo, "IO stats from the last %d frames.\n", nFrameHistorySize ); int nPos = 3; for ( int i = 0 ; i < nStatsSize ; ++i ) { if ( nModes[i] == SKIPPED ) { continue; // Skip this stat, not interesting... } printinfo.index = nPos++; float flAverage = ( float )( nAvgStats[i] ) / ( float )( nFrameHistorySize ); const float flFrameRate = 30.0f; // Hardcoded at this moment float flAveragePerSec = flAverage * flFrameRate; // It seems that %3.1f format is not respected (at least on PS3, switch everything to %d). // Also there is no point displaying min, it is pretty much always 0. switch ( nModes[i] ) { case NORMAL: Con_NXPrintf( &printinfo, "%s - Avg:%5d - Max:%5d -%5d /s\n", pStatsTitle[i], ( int )flAverage, nMaxStats[i], ( int )flAveragePerSec ); //Con_NXPrintf( &printinfo, "%s - Min:%2d - Avg:% 3.1f - Max:%5d -% 3.1f /s\n", pStatsTitle[i], nMinStats[i], flAverage, nMaxStats[i], flAveragePerSec ); break; case IN_MS: Con_NXPrintf( &printinfo, "%s - Avg:%5d ms - Max:%5d ms -%5d ms/s\n", pStatsTitle[i], ( int )flAverage, nMaxStats[i], ( int )flAveragePerSec ); //Con_NXPrintf( &printinfo, "%s - Min:%2d ms - Avg:% 3.1f ms - Max:%5d ms -% 3.1f ms/s\n", pStatsTitle[i], nMinStats[i], flAverage, nMaxStats[i], flAveragePerSec ); break; case IN_BYTES: { //int nMin = nMinStats[i] / 1024; flAverage /= 1024.f; int nMax = nMaxStats[i] / 1024; flAveragePerSec /= 1024.f; Con_NXPrintf( &printinfo, "%s - Avg:%5d Kb - Max:%5d Kb -%5d Kb/s\n", pStatsTitle[i], ( int )flAverage, nMax, ( int )flAveragePerSec ); } break; } } extern float g_fDelayForChoreo; printinfo.index = nPos++; Con_NXPrintf( &printinfo, "Delay for choreo: %5d ms\n", ( int )( g_fDelayForChoreo * 1000 ) ); } #endif #define LOG_FRAME_OUTPUT 0 void _Host_RunFrame (float time) { g_HostTimes.MarkFrameStartTime(); MDLCACHE_COARSE_LOCK_(g_pMDLCache); static double host_remainder = 0.0f; double prevremainder; bool shouldrender; #if defined( RAD_TELEMETRY_ENABLED ) if( g_Telemetry.DemoTickEnd == ( uint32 )-1 ) { Cbuf_AddText( Cbuf_GetCurrentPlayer(), "quit\n" ); } #endif #ifndef DEDICATED CClientState &cl = GetBaseLocalClient(); #endif int numticks; { // Profile scope specific to the top of this function, protect from setjmp() problems VPROF( "_Host_RunFrame_Upto_MarkFrame" ); if ( host_checkheap ) { #if defined(_WIN32) if ( _heapchk() != _HEAPOK ) { Sys_Error( "_Host_RunFrame (top): _heapchk() != _HEAPOK\n" ); } #endif } static double timeLastMemCompact; if ( mem_incremental_compact_rate.GetFloat() > 0 ) { double curTime = Plat_FloatTime(); if ( curTime - timeLastMemCompact > (double)mem_incremental_compact_rate.GetFloat() ) { timeLastMemCompact = curTime; g_pMemAlloc->CompactIncremental(); } } if ( host_Sleep.GetInt() ) { Sys_Sleep( host_Sleep.GetInt() ); } // Slow down the playback? if ( g_iVCRPlaybackSleepInterval ) { Sys_Sleep( g_iVCRPlaybackSleepInterval ); } MapReslistGenerator().RunFrame(); static int lastrunoffsecond = -1; if ( setjmp ( host_enddemo ) ) return; // demo finished. // decide the simulation time Host_AccumulateTime ( time ); _Host_SetGlobalTime(); shouldrender = !sv.IsDedicated(); // FIXME: Could track remainder as fractional ticks instead of msec prevremainder = host_remainder; if ( prevremainder < 0 ) prevremainder = 0; #if !defined(DEDICATED) if ( !demoplayer->IsPlaybackPaused() ) #endif { host_remainder += host_frametime; } numticks = 0; // how many ticks we will simulate this frame // If we're using sv_alternateticks, make sure there are at least 2 ticks worth of host_remainder time to consume #if defined( LINUX ) bool bAlternateTicks = false; #else bool bAlternateTicks = sv_alternateticks.GetBool() && ( GetBaseLocalClient().m_nMaxClients == 1 ); #endif float flMinimumTickInterval = bAlternateTicks ? host_state.interval_per_tick * 2.0f : host_state.interval_per_tick; if ( host_remainder >= flMinimumTickInterval ) { numticks = (int)( floor(host_remainder / host_state.interval_per_tick ) ); // round to nearest even ending tick in alternate ticks mode so the last // tick is always simulated prior to updating the network data if ( bAlternateTicks ) { int startTick = g_ServerGlobalVariables.tickcount; int endTick = startTick + numticks; endTick = endTick & ~(0x1); // an even number of ticks, rounding down numticks = endTick - startTick; } host_remainder -= numticks * host_state.interval_per_tick; } host_nexttick = host_state.interval_per_tick - host_remainder; g_pMDLCache->MarkFrame(); #if !( defined( _CERT ) || defined( DEDICATED ) ) if ( host_print_frame_times.GetBool() ) { PrintHostFrameTimes( numticks, host_remainder, flMinimumTickInterval ); } if ( fs_enable_stats.GetBool() ) { PrintFsStats(); } #endif // !_CERT || !DEDICATED } { // Profile scope, protect from setjmp() problems VPROF( "_Host_RunFrame" ); g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CMD_EXECUTE ); // process console commands #if defined( _GAMECONSOLE ) && !defined( _CERT ) static volatile bool s_bMemDebug = !!CommandLine()->FindParm( "-mem_dump_frames" ); if ( s_bMemDebug ) Cbuf_AddText( CBUF_FIRST_PLAYER, "mem_dump;\n" ); static char s_chBufferConCommandHack[256] = {0}; if ( s_chBufferConCommandHack[0] ) { Cbuf_AddText( CBUF_FIRST_PLAYER, s_chBufferConCommandHack ); s_chBufferConCommandHack[0] = 0; } #endif Cbuf_Execute (); if ( NET_IsDedicatedForXbox() ) { if ( NET_IsDedicated() && !NET_IsMultiplayer() ) { NET_SetMultiplayer( true ); } } else { // initialize networking after commandline & autoexec.cfg have been parsed NET_Init( NET_IsDedicated() ); } g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CMD_EXECUTE ); // Msg( "Running %i ticks (%f remainder) for frametime %f total %f tick %f delta %f\n", numticks, remainder, host_frametime, host_time ); g_ServerGlobalVariables.interpolation_amount = 0.0f; #ifndef DEDICATED g_ClientGlobalVariables.interpolation_amount = 0.0f; cl.insimulation = true; #endif host_frameticks = numticks; host_currentframetick = 0; #if !defined( DEDICATED ) // This is to make the tool do both sim + rendering on the initial frame // cl.IsActive changes in the loop below, as does scr_nextdrawtick // We're just caching off the state here so that we have a consistent return value // for enginetool->IsInGame the entire frame g_pEngineToolInternal->SetIsInGame( cl.IsActive() && ( scr_nextdrawtick == 0 ) ); #endif CJob *pGameJob = NULL; // threaded path only supported in listen server #ifndef DEDICATED if ( !IsEngineThreaded() ) #endif { #ifndef DEDICATED if ( g_ClientDLL ) { g_ClientDLL->IN_SetSampleTime(host_frametime); } g_ClientGlobalVariables.simTicksThisFrame = 1; #endif #ifndef DEDICATED cl.m_tickRemainder = host_remainder; cl.SetFrameTime( host_frametime ); #endif g_ServerGlobalVariables.simTicksThisFrame = 1; for ( int tick = 0; tick < numticks; tick++ ) { // Emit an ETW event every simulation frame. ETWSimFrameMark( sv.IsDedicated() ); double now = Plat_FloatTime(); float jitter = now - host_idealtime; // Track jitter (delta between ideal time and actual tick execution time) host_jitterhistory[ host_jitterhistorypos ] = jitter; host_jitterhistorypos = ( host_jitterhistorypos + 1 ) % ARRAYSIZE(host_jitterhistory); // Very slowly decay "ideal" towards current wall clock unless delta is large if ( fabs( jitter ) > 1.0f ) { host_idealtime = now; } else { host_idealtime = 0.99 * host_idealtime + 0.01 * now; } // process any asynchronous network traffic (TCP), set net_time NET_RunFrame( now ); // Only send updates on final tick so we don't re-encode network data multiple times per frame unnecessarily bool bFinalTick = ( tick == (numticks - 1) ); if ( NET_IsDedicatedForXbox() ) { if ( NET_IsDedicated() && !NET_IsMultiplayer() ) { NET_SetMultiplayer( true ); } } else { // initialize networking after commandline & autoexec.cfg have been parsed NET_Init( NET_IsDedicated() ); } g_ServerGlobalVariables.tickcount = sv.m_nTickCount; // NOTE: Do we want do this at start or end of this loop? ++host_tickcount; ++host_currentframetick; #ifndef DEDICATED g_ClientGlobalVariables.tickcount = cl.GetClientTickCount(); // Make sure state is correct CL_CheckClientState(); #endif //------------------- // input processing //------------------- if ( host_runframe_input_parcelremainder.GetBool() ) { //sv_alternateticks creates instances where we're guaranteed to have a first-loop prevremainder of more than a tick, creating negative frame times for input deeper down float fParcelRemainder = MIN( prevremainder, host_state.interval_per_tick ); _Host_RunFrame_Input( fParcelRemainder, bFinalTick ); prevremainder -= fParcelRemainder; } else { _Host_RunFrame_Input( prevremainder, bFinalTick ); prevremainder = 0; } //------------------- // // server operations // //------------------- _Host_RunFrame_Server( bFinalTick ); // Additional networking ops for SPLITPACKET stuff (99.9% of the time this will be an empty list of work) NET_SendQueuedPackets(); //------------------- // // client operations // //------------------- #ifndef DEDICATED if ( !sv.IsDedicated() ) { _Host_RunFrame_Client( bFinalTick ); } toolframework->Think( bFinalTick ); #endif host_idealtime += host_state.interval_per_tick; } #if defined( VOICE_OVER_IP ) && !defined( DEDICATED ) // Send any enqueued voice data to the server CL_ProcessVoiceData(); if ( numticks == 0 ) { _Host_ProcessVoice_Server(); } #endif // VOICE_OVER_IP && !DEDICATED // run HLTV (both active, and not yet connected) for ( CHltvServerIterator hltv; hltv; hltv.Next() ) { hltv->RunFrame(); } if ( hltvtest ) { hltvtest->RunFrame(); } #if defined( REPLAY_ENABLED ) // run replay if active if ( replay ) { replay->RunFrame(); if ( g_pServerReplayHistoryManager ) g_pServerReplayHistoryManager->Update(); } if ( g_pClientReplayHistoryManager && g_pClientReplayHistoryManager->IsInitialized() ) g_pClientReplayHistoryManager->Update(); #endif #ifndef DEDICATED // This is a hack to let timedemo pull messages from the queue faster than every 15 msec // Also when demoplayer is skipping packets to a certain tick we should process the queue // as quickly as we can. if ( numticks == 0 && ( demoplayer->IsPlayingTimeDemo() || demoplayer->IsSkipping() ) ) { _Host_RunFrame_Client( true ); } if ( !sv.IsDedicated() ) { // This causes cl.gettime() to return the true clock being used for rendering (tickcount * rate + remainder) Host_SetClientInSimulation( false ); // Now allow for interpolation on client g_ClientGlobalVariables.interpolation_amount = ( cl.m_tickRemainder / host_state.interval_per_tick ); //------------------- // Run prediction if it hasn't been run yet //------------------- // If we haven't predicted/simulated the player (multiplayer with prediction enabled and // not a listen server with zero frame lag, then go ahead and predict now CL_RunPrediction( PREDICTION_NORMAL ); CL_ApplyAddAngle(); // The mouse is always simulated for the current frame's time // This makes updates smooth in every case // continuous controllers affecting the view are also simulated this way // but they have a cap applied by IN_SetSampleTime() so they are not also // simulated during input gathering CL_ExtraMouseUpdate( g_ClientGlobalVariables.frametime ); } #endif #if LOG_FRAME_OUTPUT if ( !cl.IsPaused() || !sv.IsPaused() ) { Msg("=============SIM: CLIENT %5d + %d, SERVER %5d + %d\t REM: %.2f\n", cl.GetClientTickCount(), numticks, sv.m_nTickCount, numticks, host_remainder*1000.0f ); } #endif } #ifndef DEDICATED else { static int numticks_last_frame = 0; static float host_remainder_last_frame = 0, prev_remainder_last_frame = 0, last_frame_time = 0; int clientticks; int serverticks; clientticks = numticks_last_frame; cl.m_tickRemainder = host_remainder_last_frame; cl.SetFrameTime( last_frame_time ); if ( g_ClientDLL ) { g_ClientDLL->IN_SetSampleTime(last_frame_time); } last_frame_time = host_frametime; serverticks = numticks; g_ClientGlobalVariables.simTicksThisFrame = clientticks; g_ServerGlobalVariables.simTicksThisFrame = serverticks; g_ServerGlobalVariables.tickcount = sv.m_nTickCount; // THREADED: Run Client // ------------------- for ( int tick = 0; tick < clientticks; tick++ ) { // process any asynchronous network traffic (TCP), set net_time NET_RunFrame( Plat_FloatTime() ); // Only send updates on final tick so we don't re-encode network data multiple times per frame unnecessarily bool bFinalTick = ( tick == (clientticks - 1) ); if ( NET_IsDedicatedForXbox() ) { if ( NET_IsDedicated() && !NET_IsMultiplayer() ) { NET_SetMultiplayer( true ); } } else { // initialize networking after commandline & autoexec.cfg have been parsed NET_Init( NET_IsDedicated() ); } g_ClientGlobalVariables.tickcount = cl.GetClientTickCount(); // Make sure state is correct CL_CheckClientState(); // Additional networking ops for SPLITPACKET stuff (99.9% of the time this will be an empty list of work) NET_SendQueuedPackets(); //------------------- // // client operations // //------------------- if ( !sv.IsDedicated() ) { _Host_RunFrame_Client( bFinalTick ); } toolframework->Think( bFinalTick ); } #if defined( VOICE_OVER_IP ) // Send any enqueued voice data to the server CL_ProcessVoiceData(); #endif // VOICE_OVER_IP // This is a hack to let timedemo pull messages from the queue faster than every 15 msec // Also when demoplayer is skipping packets to a certain tick we should process the queue // as quickly as we can. if ( clientticks == 0 && ( demoplayer->IsPlayingTimeDemo() || demoplayer->IsSkipping() ) ) { _Host_RunFrame_Client( true ); } // This causes cl.gettime() to return the true clock being used for rendering (tickcount * rate + remainder) Host_SetClientInSimulation( false ); // Now allow for interpolation on client g_ClientGlobalVariables.interpolation_amount = ( cl.m_tickRemainder / host_state.interval_per_tick ); //------------------- // Run prediction if it hasn't been run yet //------------------- // If we haven't predicted/simulated the player (multiplayer with prediction enabled and // not a listen server with zero frame lag, then go ahead and predict now CL_RunPrediction( PREDICTION_NORMAL ); CL_ApplyAddAngle(); Host_SetClientInSimulation( true ); // THREADED: Run Input // ------------------- int saveTick = g_ClientGlobalVariables.tickcount; for ( int tick = 0; tick < serverticks; tick++ ) { // NOTE: Do we want do this at start or end of this loop? ++host_tickcount; ++host_currentframetick; g_ClientGlobalVariables.tickcount = host_tickcount; bool bFinalTick = tick==(serverticks-1) ? true : false; _Host_RunFrame_Input( prevremainder, bFinalTick ); prevremainder = 0; // process any asynchronous network traffic (TCP), set net_time NET_RunFrame( Plat_FloatTime() ); } #if defined( VOICE_OVER_IP ) if ( serverticks == 0 ) { _Host_ProcessVoice_Server(); } #endif Host_SetClientInSimulation( false ); // The mouse is always simulated for the current frame's time // This makes updates smooth in every case // continuous controllers affecting the view are also simulated this way // but they have a cap applied by IN_SetSampleTime() so they are not also // simulated during input gathering CL_ExtraMouseUpdate( g_ClientGlobalVariables.frametime ); g_ClientGlobalVariables.tickcount = saveTick; numticks_last_frame = numticks; host_remainder_last_frame = host_remainder; // THREADED: Run Server // ------------------- // set net_time once before running the server NET_SetTime( Plat_FloatTime() ); #ifdef _PS3 SNPROF("Kick sv"); g_pGcmSharedData->RunServer(_Host_RunFrame_Server_Async, serverticks); pGameJob = (CJob*)1; #else pGameJob = new CFunctorJob( CreateFunctor( _Host_RunFrame_Server_Async, serverticks ) ); if ( IsX360() ) { pGameJob->SetServiceThread( g_nServerThread ); } g_pThreadPool->AddJob( pGameJob ); #endif #if LOG_FRAME_OUTPUT if ( !cl.IsPaused() || !sv.IsPaused() ) { Msg("=============SIM: CLIENT %5d + %d, SERVER %5d + %d\t REM: %.2f\n", cl.GetClientTickCount(), clientticks, sv.m_nTickCount, serverticks, host_remainder*1000.0f ); } #endif } #endif // DEDICATED #if defined ( INCLUDE_SCALEFORM ) if ( g_pScaleformUI && shouldrender ) { float timeScale = host_timescale.GetFloat() * sv.GetTimescale(); if ( timeScale <= 0.0f ) timeScale = 1.0f; // using realtime here, because we never want scaleform menus paused (they fail to work). // we may want to remove timescale here also static float flLastScaleformRunFrame = g_ClientGlobalVariables.realtime; g_pScaleformUI->RunFrame( ( g_ClientGlobalVariables.realtime - flLastScaleformRunFrame ) / timeScale ); flLastScaleformRunFrame = g_ClientGlobalVariables.realtime; } #endif g_Log.RunFrame(); if ( shouldrender ) { #if LOG_FRAME_OUTPUT if ( !cl.IsPaused() || !sv.IsPaused() ) { static float lastFrameTime = 0; float frametime = g_ClientGlobalVariables.curtime - lastFrameTime; Msg("RENDER AT: %6.4f: %.2fms [%.2fms implicit] frametime\n", g_ClientGlobalVariables.curtime, g_ClientGlobalVariables.frametime*1000.0f, frametime * 1000.0f); lastFrameTime = g_ClientGlobalVariables.curtime; } #endif //------------------- // rendering //------------------- _Host_RunFrame_Render(); //------------------- // sound //------------------- _Host_RunFrame_Sound(); if ( g_bVCRSingleStep ) { VCR_EnterPausedState(); } } //------------------- // simulation //------------------- g_HostTimes.MarkSwapTime( ); #ifndef DEDICATED if ( !sv.IsDedicated() ) { VPROF_BUDGET( "_Host_RunFrame - ClientDLL_Update", VPROF_BUDGETGROUP_CLIENT_SIM ); // Client-side simulation g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CLDLL ); ClientDLL_Update(); g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CLDLL ); } #endif if ( pGameJob ) { { VPROF_BUDGET( "WaitForAsyncServer", "AsyncServer" ); #ifndef _PS3 if ( Host_IsSinglePlayerGame() ) { // This should change to a YieldWait if the server starts wanting to parallel process. If // so, will need some route for the server to queue up work it wants to execute outside // its frame, otherwise some of it would be performed during the yield. Right now // need to wait for server so we don't stall on queued AI operations (toml 7/3/2007) pGameJob->ExecuteAndRelease(); } else { pGameJob->WaitForFinishAndRelease(); } #else SNPROF("WaitFor Sv"); g_pGcmSharedData->WaitForServer(); #endif } SV_FrameExecuteThreadDeferred(); } Host_EndThreadedSound(); //------------------- // time //------------------- Host_Speeds(); Host_UpdateMapList(); host_framecount++; #if !defined(DEDICATED) if ( !demoplayer->IsPlaybackPaused() ) { host_time = host_tickcount * host_state.interval_per_tick + cl.m_tickRemainder; } #endif Host_PostFrameRate( host_frametime ); if ( host_checkheap ) { #ifdef _WIN32 if ( _heapchk() != _HEAPOK ) { Sys_Error( "_Host_RunFrame (bottom): _heapchk() != _HEAPOK\n" ); } #endif } Status_CheckSendETWMark(); Host_CheckDumpMemoryStats(); GetTestScriptMgr()->CheckPoint( "frame_end" ); } // Profile scope, protect from setjmp() problems Host_ShowIPCCallCount(); } /* ============================== Host_Frame ============================== */ void Host_RunFrame( float time ) { static double timetotal = 0; static int timecount = 0; static double timestart = 0; #ifndef DEDICATED if ( !scr_drawloading && sv.IsActive() && GetBaseLocalClient().IsActive() && !sv.m_bLoadgame) { switch ( host_thread_mode.GetInt() ) { case HTM_DISABLED: g_bThreadedEngine = false; break; case HTM_DEFAULT: g_bThreadedEngine = ( g_pThreadPool->NumThreads() > 0 ); break; case HTM_FORCED: g_bThreadedEngine = true; break; } #ifdef _PS3 g_bThreadedEngine = true; // Not reqd anuymore ? #endif } else #endif { g_bThreadedEngine = false; } { VPROF( "UpdateDynamicModels" ); CMDLCacheCriticalSection critsec(g_pMDLCache); modelloader->UpdateDynamicModels(); } if ( !host_profile.GetBool() ) { _Host_RunFrame( time ); return; } double time1 = Sys_FloatTime(); _Host_RunFrame( time ); double time2 = Sys_FloatTime(); timetotal += time2 - time1; // time in seconds timecount++; if (timecount < 1000) return; float fps = 1000/(time2 - timestart); ConMsg ("host_profile : %i clients, %.1f msec, %.1f fps\n", sv.GetNumClients(), timetotal, fps ); timecount = 0; timetotal = 0; timestart = time2; } //----------------------------------------------------------------------------- // If the user passed -lv on the command-line, they want low violence mode. //----------------------------------------------------------------------------- bool IsLowViolence_CommandLine() { #if defined( _LOWVIOLENCE ) // a low violence build can not be-undone return true; #endif return ( CommandLine()->FindParm( "-lv" ) != 0 ); } #ifndef DEDICATED //----------------------------------------------------------------------------- // A more secure means of enforcing low violence. //----------------------------------------------------------------------------- bool IsLowViolence_Secure() { // CS:GO does not have any low violence regions. Ignore what Steam reports. #if defined( CSTRIKE15 ) return false; #endif #ifndef _GAMECONSOLE if ( IsPC() && Steam3Client().SteamApps() ) { // let Steam determine current violence settings return Steam3Client().SteamApps()->BIsLowViolence(); } #endif if ( IsGameConsole() ) { #if defined( _LOWVIOLENCE ) // a low violence build can not be-undone return true; #endif // Users can opt into low violence mode on the command-line. if ( IsLowViolence_CommandLine() ) { return true; } } return false; } //----------------------------------------------------------------------------- // If "User Token 2" exists in HKEY_CURRENT_USER/Software/Valve/Half-Life/Settings // then we disable gore. Obviously not very secure. //----------------------------------------------------------------------------- bool IsLowViolence_Registry() { if ( IsGameConsole() ) { #if defined( _LOWVIOLENCE ) // a low violence build can not be-undone return true; #endif // Users can opt into low violence mode on the command-line. if ( IsLowViolence_CommandLine() ) { return true; } } // CS:GO does not have any low violence regions. Ignore the registry settings. #if defined( CSTRIKE15 ) return false; #endif char szSubKey[128]; int nBufferLen; char szBuffer[128]; bool bReducedGore = false; memset( szBuffer, 0, 128 ); char const *appname = "Source"; Q_snprintf(szSubKey, sizeof( szSubKey ), "Software\\Valve\\%s\\Settings", appname ); nBufferLen = 127; Q_strncpy( szBuffer, "", sizeof( szBuffer ) ); Sys_GetRegKeyValue( szSubKey, "User Token 2", szBuffer, nBufferLen, szBuffer ); // Gore reduction active? bReducedGore = ( Q_strlen( szBuffer ) > 0 ) ? true : false; if ( !bReducedGore ) { Sys_GetRegKeyValue( szSubKey, "User Token 3", szBuffer, nBufferLen, szBuffer ); bReducedGore = ( Q_strlen( szBuffer ) > 0 ) ? true : false; } char gamedir[MAX_OSPATH]; Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) ); // also check mod specific directories for LV changes Q_snprintf(szSubKey, sizeof( szSubKey ), "Software\\Valve\\%s\\%s\\Settings", appname, gamedir ); nBufferLen = 127; Q_strncpy( szBuffer, "", sizeof( szBuffer ) ); Sys_GetRegKeyValue( szSubKey, "User Token 2", szBuffer, nBufferLen, szBuffer ); if ( Q_strlen( szBuffer ) > 0 ) { bReducedGore = true; } Sys_GetRegKeyValue( szSubKey, "User Token 3", szBuffer, nBufferLen, szBuffer ); if ( Q_strlen( szBuffer ) > 0 ) { bReducedGore = true; } return bReducedGore; } #endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void Host_CheckGore( void ) { bool bLowViolenceRegistry = false; bool bLowViolenceSecure = false; bool bLowViolenceCommandLine = IsLowViolence_CommandLine(); #ifndef DEDICATED // // First check the old method of enabling low violence via the registry. // #ifdef WIN32 bLowViolenceRegistry = IsLowViolence_Registry(); #endif // // Next check the new method of enabling low violence based on country of purchase // and other means that are inaccessible by the user. // bLowViolenceSecure = IsLowViolence_Secure(); #endif // // If either method says "yes" to low violence, we're in low violence mode. // if ( bLowViolenceRegistry || bLowViolenceSecure || bLowViolenceCommandLine ) { g_bLowViolence = true; if ( bLowViolenceRegistry ) { violence_hblood.SetValue( 0 ); violence_hgibs.SetValue( 0 ); violence_ablood.SetValue( 0 ); violence_agibs.SetValue( 0 ); } } else { g_bLowViolence = false; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Host_InitProcessor( void ) { const CPUInformation& pi = GetCPUInformation(); // Compute Frequency in Mhz: char* szFrequencyDenomination = "Mhz"; double fFrequency = pi.m_Speed / 1000000.0; // Adjust to Ghz if nessecary: if( fFrequency > 1000.0 ) { fFrequency /= 1000.0; szFrequencyDenomination = "Ghz"; } char szFeatureString[256]; Q_strncpy( szFeatureString, pi.m_szProcessorID, sizeof( szFeatureString ) ); Q_strncat( szFeatureString, " ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); if( pi.m_bSSE ) { if( MathLib_SSEEnabled() ) Q_strncat(szFeatureString, "SSE ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); else Q_strncat(szFeatureString, "(SSE) ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); } if( pi.m_bSSE2 ) { if( MathLib_SSE2Enabled() ) Q_strncat(szFeatureString, "SSE2 ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); else Q_strncat(szFeatureString, "(SSE2) ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); } if( pi.m_bMMX ) { if( MathLib_MMXEnabled() ) Q_strncat(szFeatureString, "MMX ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); else Q_strncat(szFeatureString, "(MMX) ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); } if( pi.m_bRDTSC ) Q_strncat(szFeatureString, "RDTSC ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); if( pi.m_bCMOV ) Q_strncat(szFeatureString, "CMOV ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); if( pi.m_bFCMOV ) Q_strncat(szFeatureString, "FCMOV ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS ); // Remove the trailing space. There will always be one. szFeatureString[Q_strlen(szFeatureString)-1] = '\0'; // Dump CPU information: if( pi.m_nLogicalProcessors == 1 ) { ConDMsg( "1 CPU, Frequency: %.01f %s, Features: %s\n", fFrequency, szFrequencyDenomination, szFeatureString ); } else { char buffer[256] = ""; if( pi.m_nPhysicalProcessors != pi.m_nLogicalProcessors ) { Q_snprintf(buffer, sizeof( buffer ), " (%i physical)", (int) pi.m_nPhysicalProcessors ); } ConDMsg( "%i CPUs%s, Frequency: %.01f %s, Features: %s\n", (int)pi.m_nLogicalProcessors, buffer, fFrequency, szFrequencyDenomination, szFeatureString ); } #if defined( _WIN32 ) if ( s_bInitPME ) { // Initialize the performance monitoring events code. InitPME(); } #endif } //----------------------------------------------------------------------------- // Specifically used by the model loading code to mark models // touched by the current map //----------------------------------------------------------------------------- int Host_GetServerCount( void ) { #ifndef DEDICATED if ( GetBaseLocalClient().m_nSignonState >= SIGNONSTATE_NEW || ( GetBaseLocalClient().m_nSignonState >= SIGNONSTATE_CONNECTED && GetBaseLocalClient().m_bServerInfoProcessed ) ) { // the server count cannot be relied on until the server info message is processed // the mulitple state checking state guarantees its validity return GetBaseLocalClient().m_nServerCount; } else #endif if ( sv.m_State >= ss_loading ) { return sv.GetSpawnCount(); } // this is unfortunate, and happens, but the caller is too early in the protocol or a demo // cannot identify the correct server count // return the same count that demo will use return gHostSpawnCount; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void SpewInstallStatus( void ) { if ( IsGameConsole() && g_pFullFileSystem ) { #if defined( _X360 ) Msg( "\nXbox Launched From %s.\n", g_pFullFileSystem->IsLaunchedFromXboxHDD() ? "HDD" : "DVD" ); if ( g_pXboxInstaller ) { g_pXboxInstaller->SpewStatus(); } #elif defined( _PS3 ) // This is intended to match CXboxInstaller::SpewStatus as closely as possible: Msg( "Install Status:\n" ); Msg( "Version: %u (%s) (ps3)\n", XBX_GetImageChangelist(), XBX_GetLanguageString() ); Msg( "DVD Hosted: Disabled\n" ); // This spew is XBox-specific (could be hooked up to the FIOS installer) //if ( g_pFullFileSystem->IsInstalledToXboxHDDCache() ) //{ // Msg( "Existing Image Found.\n" ); //} //if ( !IsInstallEnabled() ) //{ // Msg( "Install Enabled.\n" ); //} //if ( IsFullyInstalled() ) //{ // Msg( "Fully Installed.\n" ); //} //Msg( "Progress: %d/%d MB\n", GetCopyStats()->m_BytesCopied/(1024*1024), GetTotalSize()/(1024*1024) ); #endif } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Host_PostInit() { if ( serverGameDLL ) { serverGameDLL->PostInit(); } #if !defined( DEDICATED ) if ( g_ClientDLL ) { g_ClientDLL->PostInit(); } toolframework->PostInit(); if ( !sv.IsDedicated() ) { // vgui needs other systems to finalize EngineVGui()->PostInit(); } if ( serverGameDLL ) { serverGameDLL->PostToolsInit(); } SpewInstallStatus(); if ( IsGameConsole() ) { // fully setup, can now publish cvars to vxconsole Cbuf_AddText( Cbuf_GetCurrentPlayer(), "getcvars" ); } #endif } #if defined( REPLAY_ENABLED ) void Replay_Init() { Assert( replay == NULL ); } void Replay_Shutdown() { if ( replay ) { replay->Shutdown(); delete replay; replay = NULL; } } #endif void HLTV_Init() { for ( int i = 0; i < HLTV_SERVER_MAX_COUNT; ++i ) Assert ( g_pHltvServer[ i ] == NULL ); Assert ( hltvtest == NULL ); } void HLTV_Shutdown() { for ( int i = 0; i < HLTV_SERVER_MAX_COUNT; ++i ) { if ( g_pHltvServer[ i ] ) { g_pHltvServer[ i ]->Shutdown(); delete g_pHltvServer[ i ]; g_pHltvServer[ i ] = NULL; } } if ( hltvtest ) { delete hltvtest; hltvtest = NULL; } } void InstallConVarHook( void ); int g_nForkID = 0; int g_nSocketToParentProcess = -1; #ifdef _LINUX #include #include void SendStringToParentProcess( char const *pMsg ) { Assert( IsChildProcess() ); Assert( g_nSocketToParentProcess != -1 ); send( g_nSocketToParentProcess, pMsg, 1 + strlen( pMsg ), MSG_DONTWAIT ); } #endif CDebugInputThread * g_pDebugInputThread; //-------------------------------------------------------------------------------------------------- // PS3 QMS/Server thread //-------------------------------------------------------------------------------------------------- #ifdef _PS3 static uint32 QMSServerThreadEntry(void* param) { while(1) { // Wait on semaphore // For each semaphore posted, we look to see if we should run either "job" // It is also possible to decr the semaphore, only to have nothing to run... // ie The semaphore is a wakeup rather than a count of jobs sys_semaphore_wait(g_pGcmSharedData->m_semaphore, 0); g_pGcmSharedData->CheckForAudioRequest(); g_pGcmSharedData->CheckForServerRequest(); if (g_pGcmSharedData->m_qmsRunFlag) { g_pGcmSharedData->m_qmsRunFlag = 0; void* p1 = (void*)g_pGcmSharedData->m_cmat; void* p2 = (void*)g_pGcmSharedData->m_ptr; g_pGcmSharedData->m_func(p1, p2); g_pGcmSharedData->m_qmsDoneFlag = 1; } } return 0; } static void CreateQMSServerThread() { CreateSimpleThread(QMSServerThreadEntry, 0, 0x40000); } #endif // Check with steam to see if the requested file (requires full path) is a valid, signed binary #define SignatureWarning( ... ) ((void)(0)) static bool Host_IsValidSignature( const char *pFilename, bool bAllowUnknown ) { #if defined( DEDICATED ) || defined( _GAMECONSOLE ) return true; #else if ( Steam3Client().SteamUtils() ) { SteamAPICall_t hAPICall = Steam3Client().SteamUtils()->CheckFileSignature( pFilename ); bool bAPICallFailed = true; while ( !Steam3Client().SteamUtils()->IsAPICallCompleted(hAPICall, &bAPICallFailed) ) { SteamAPI_RunCallbacks(); ThreadSleep( 1 ); } if( bAPICallFailed ) { SignatureWarning( "CheckFileSignature API call on %s failed", pFilename ); } else { CheckFileSignature_t result; Steam3Client().SteamUtils()->GetAPICallResult( hAPICall, &result, sizeof(result), result.k_iCallback, &bAPICallFailed ); if( bAPICallFailed ) { SignatureWarning( "CheckFileSignature API call on %s failed\n", pFilename ); } else { if( result.m_eCheckFileSignature == k_ECheckFileSignatureValidSignature || result.m_eCheckFileSignature == k_ECheckFileSignatureNoSignaturesFoundForThisApp ) return true; if ( bAllowUnknown && result.m_eCheckFileSignature == k_ECheckFileSignatureNoSignaturesFoundForThisFile ) return true; SignatureWarning( "No valid signature found for %s\n", pFilename ); } } } return false; #endif } // Ask steam if it is ok to load this DLL. Unsigned DLLs should not be loaded unless // the client is running -insecure (testing a plugin for example) // This keeps legitimate users with modified binaries from getting VAC banned because of them #if defined( DEDICATED ) || defined( OSX ) || defined( LINUX ) #else static CUtlVector< CUtlString * > g_PendingSignatureChecks; static CUtlVector< CUtlString * > g_FailedSignatureChecks; #endif bool Host_AllowLoadModule( const char *pFilename, const char *pPathID, bool bAllowUnknown ) { #if defined( DEDICATED ) || defined( OSX ) || defined( LINUX ) // dedicated servers don't check signatures // OSX and Linux have no ability to check signatures return true; #else // Allow loading plugins if ( sv.IsDedicated() ) return true; // check signature bool bSignatureIsValid = false; if ( g_bAllowSecureServers && Steam3Client().SteamUtils() ) { char szDllname[512]; V_strncpy( szDllname, pFilename, sizeof(szDllname) ); V_SetExtension( szDllname, DLL_EXT_STRING, sizeof(szDllname) ); if ( pPathID ) { char szFullPath[ 512 ]; const char *pFullPath = g_pFileSystem->RelativePathToFullPath( szDllname, pPathID, szFullPath, sizeof(szFullPath) ); if ( !pFullPath ) { SignatureWarning("Can't find %s on disk\n", szDllname ); bSignatureIsValid = false; } else { if ( CommandLine()->FindParm( "-immediatesignaturechecks" ) ) { bSignatureIsValid = Host_IsValidSignature( pFullPath, bAllowUnknown ); } else { g_PendingSignatureChecks.AddToTail( new CUtlString( pFullPath ) ); bSignatureIsValid = true; } } } else { if ( CommandLine()->FindParm( "-immediatesignaturechecks" ) ) { bSignatureIsValid = Host_IsValidSignature( szDllname, bAllowUnknown ); } else { g_PendingSignatureChecks.AddToTail( new CUtlString( szDllname ) ); bSignatureIsValid = true; } } } else { if ( g_bAllowSecureServers ) { SignatureWarning("Steam is not active, running in -insecure mode.\n"); } g_bAllowSecureServers = false; } if ( bSignatureIsValid ) return true; if ( !g_bAllowSecureServers ) { SignatureWarning("Loading unsigned module %s\nAccess to secure servers is disabled.\n", pFilename ); return true; } return false; #endif } bool Host_IsSecureServerAllowed() { if ( CommandLine()->FindParm("-insecure") || CommandLine()->FindParm("-tools") ) g_bAllowSecureServers = false; return g_bAllowSecureServers; } void Host_DisallowSecureServers() { #if !defined(DEDICATED) if ( g_bAllowSecureServers && ( GetBaseLocalClient().IsConnected() || sv.IsActive() ) ) { // Force-disconnect local client if secure mode changes mid-connection GetBaseLocalClient().Disconnect( true ); } g_bAllowSecureServers = false; #endif } void Host_FinishSecureSignatureChecks() { #if defined( DEDICATED ) || defined( OSX ) || defined( LINUX ) #else while ( g_PendingSignatureChecks.Count() ) { // CSGO May 2017 checking via library loader hook // bool bSignatureIsValid = Host_IsValidSignature( g_PendingSignatureChecks.Head()->Get(), false ); bool bSignatureIsValid = true; if ( !bSignatureIsValid ) { SignatureWarning( "Loaded unsigned module %s\nAccess to secure servers is disabled.\n", g_PendingSignatureChecks.Head()->Get() ); g_bAllowSecureServers = false; g_FailedSignatureChecks.AddToTail( new CUtlString( g_PendingSignatureChecks.Head()->Get() ) ); } delete g_PendingSignatureChecks.Head(); g_PendingSignatureChecks.FastRemove( 0 ); } g_PendingSignatureChecks.PurgeAndDeleteElements(); // Also check the main .exe that is running us TCHAR tchBufExe[ MAX_PATH ] = {}; DWORD dwResult = GetModuleFileName( NULL, tchBufExe, MAX_PATH ); if ( dwResult > 0 && dwResult < MAX_PATH-1 ) { bool bSignatureIsValid = Host_IsValidSignature( tchBufExe, false ); if ( !bSignatureIsValid ) { SignatureWarning( "Loaded unsigned module %s\nAccess to secure servers is disabled.\n", tchBufExe ); g_bAllowSecureServers = false; g_FailedSignatureChecks.AddToTail( new CUtlString( tchBufExe ) ); } } else { SignatureWarning( "Failed to determine exe module filename.\nAccess to secure servers is disabled.\n" ); g_bAllowSecureServers = false; g_FailedSignatureChecks.AddToTail( new CUtlString( "" ) ); } // if ( IScaleformSlotInitController *pCtrlr = g_ClientDLL->GetScaleformSlotInitController() ) // pCtrlr->PassSignaturesArray( &g_FailedSignatureChecks ); #endif } #if !defined( DEDICATED ) && defined( INCLUDE_SCALEFORM ) class CScaleformSlotInitControllerEngineImpl : public IScaleformSlotInitController { public: // A new slot has been created and InitSlot almost finished, perform final configuration virtual void ConfigureNewSlotPostInit( int slot ) { }; // Notification to external systems that a file was loaded by Scaleform libraries virtual bool OnFileLoadedByScaleform( char const *pszFilename, void *pvBuffer, int numBytesLoaded ) { Host_DisallowSecureServers(); return false; } virtual const void * GetStringUserData( const char * pchStringTableName, const char * pchKeyName, int * pLength ) { int numTables = GetBaseLocalClient().m_StringTableContainer->GetNumTables(); CNetworkStringTable *pTable = NULL; for ( int i = 0; i < numTables; i++ ) { // iterate through server tables pTable = ( CNetworkStringTable* )GetBaseLocalClient().m_StringTableContainer->GetTable( i ); if ( !pTable ) continue; if ( !V_strcmp( pTable->GetTableName(), pchStringTableName ) ) break; } if ( pTable ) { int index = pTable->FindStringIndex( pchKeyName ); if ( index != ::INVALID_STRING_INDEX ) { return pTable->GetStringUserData( index, pLength ); } } return NULL; } virtual void PassSignaturesArray( void *pvArray ) { ; } } g_CScaleformSlotInitControllerEngineImpl; IScaleformSlotInitController *g_pIScaleformSlotInitControllerEngineImpl = &g_CScaleformSlotInitControllerEngineImpl; #endif bool Should360EmulatePS3() { return ( IsX360() && CommandLine()->FindParm( "-ps3" ) ); } static bool s_bDedicatedForPurposesOfThreadPool = false; void GetThreadPoolStartParams( ThreadPoolStartParams_t &startParams ) { if ( IsX360() ) { // 360 overrides defaults, 2 computation threads distributed to core 1 and 2 if ( !Should360EmulatePS3() ) { startParams.nThreads = 2; startParams.nStackSize = 256 * 1024; startParams.fDistribute = TRS_TRUE; startParams.bUseAffinityTable = true; startParams.iAffinityTable[ 0 ] = XBOX_PROCESSOR_2; startParams.iAffinityTable[ 1 ] = XBOX_PROCESSOR_4; } else { startParams.nThreads = 1; startParams.nStackSize = 256 * 1024; startParams.fDistribute = TRS_TRUE; startParams.bUseAffinityTable = true; startParams.iAffinityTable[ 0 ] = XBOX_PROCESSOR_1; ConVarRef cl_threaded_bone_setup( "cl_threaded_bone_setup" ); host_threaded_sound.SetValue( 0 ); cl_threaded_bone_setup.SetValue( 2 ); host_thread_mode.SetValue( 0 ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), "cache_gimp\n" ); g_nMaterialSystemThread = 0; } ThreadSetAffinity( NULL, 1 ); } // Dedicated servers should not explicitly set the main thread's affinity so that machines running multiple // copies of the dedicated server can load-balance properly. // For now on the PC we use SetThreadIdealProcessor instead of explicity affinity if ( !s_bDedicatedForPurposesOfThreadPool && IsPC() ) { // this will set ideal processor on each thread startParams.fDistribute = TRS_TRUE; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Host_Init( bool bDedicated ) { realtime = 0; host_idealtime = 0; // this code is called prefork. If we are going to fork, we want to delay some stuff, because we might not even be acting as a "real" server. #ifdef _LINUX if ( CommandLine()->FindParm( "-fork" ) ) { g_nForkID = FORK_ID_PARENT_PROCESS; } #endif #if defined(_WIN32) if ( CommandLine()->FindParm( "-pme" ) ) { s_bInitPME = true; } #endif if ( !bDedicated && Host_IsSecureServerAllowed() ) { // // Signature checking code: // /** Removed for partner depot **/ } ThreadPoolStartParams_t startParams; s_bDedicatedForPurposesOfThreadPool = bDedicated; GetThreadPoolStartParams( startParams ); #ifdef _PS3 { // PS3 overrides defaults, if ( host_threaded_sound.GetBool() && ( !host_threaded_sound_simplethread.GetBool() ) ) { startParams.nThreads = 1; } else { startParams.nThreads = 0; } // Second thread for CSGO, which is not a jobthread, so that we can more easily control "what runs where and whem" g_pGcmSharedData->Init(); CreateQMSServerThread(); } #endif //////// DISABLE FOR SHIP! ////////// if ( !IsCert() || CommandLine()->FindParm( "-dbginput" ) ) { g_pDebugInputThread = new CDebugInputThread(); g_pDebugInputThread->SetName( "Debug Input" ); g_pDebugInputThread->Start( 0, TP_PRIORITY_HIGH ); } #ifndef _CERT if ( CommandLine()->FindParm( "-tslist" ) ) { int nTests = 10000; Msg( "Running TSList tests\n" ); RunTSListTests( nTests ); Msg( "Running TSQueue tests\n" ); RunTSQueueTests( nTests ); Msg( "Running Thread Pool tests\n" ); RunThreadPoolTests(); } #endif if ( g_pThreadPool ) { g_pThreadPool->Start( startParams ); #ifdef _X360 if ( !Should360EmulatePS3() ) { g_pAlternateThreadPool = CreateNewThreadPool(); startParams.iAffinityTable[0] = XBOX_PROCESSOR_3; startParams.iAffinityTable[1] = XBOX_PROCESSOR_5; g_pAlternateThreadPool->Start( startParams ); } #endif } // From const.h, the loaded game .dll will give us the correct value which is transmitted to the client host_state.interval_per_tick = DEFAULT_TICK_INTERVAL; InstallBitBufErrorHandler(); InstallConVarHook(); TRACEINIT( Con_Init(), Con_Shutdown() ); TRACEINIT( Memory_Init(), Memory_Shutdown() ); TRACEINIT( Cbuf_Init(), Cbuf_Shutdown() ); TRACEINIT( Cmd_Init(), Cmd_Shutdown() ); TRACEINIT( g_pCVar->Init(), g_pCVar->Shutdown() ); // So we can list cvars with "cvarlst" #ifndef DEDICATED TRACEINIT( V_Init(), V_Shutdown() ); #endif //TRACEINIT( Host_InitVCR(), Host_ShutdownVCR() ); TRACEINIT( COM_Init(), COM_Shutdown() ); #if !defined(DEDICATED) && !defined(LEFT4DEAD) TRACEINIT( saverestore->Init(), saverestore->Shutdown() ); #endif TRACEINIT( Filter_Init(), Filter_Shutdown() ); #ifndef DEDICATED TRACEINIT( Key_Init(), Key_Shutdown() ); #endif // Check for special -dev flag if ( CommandLine()->FindParm( "-dev" ) || ( CommandLine()->FindParm( "-allowdebug" ) && !CommandLine()->FindParm( "-nodev" ) ) ) { sv_cheats.SetValue( 1 ); developer.SetValue( 1 ); } #ifdef _DEBUG developer.SetValue( 1 ); #endif if ( CommandLine()->FindParm( "-nocrashdialog" ) ) { // stop the various windows error message boxes from showing up (used by the auto-builder so it doesn't block on error) Sys_NoCrashDialog(); } // Seed the random number generator float flTimeToSeed = Plat_FloatTime(); COMPILE_TIME_ASSERT( sizeof( flTimeToSeed ) == sizeof( int ) ); RandomSeed( ( reinterpret_cast< int & >( flTimeToSeed ) ) & 0x7FFFFFFF ); DevMsg( "Seeded random number generator @ %d ( %.3f )\n", ( reinterpret_cast< int & >( flTimeToSeed ) ) & 0x7FFFFFFF, flTimeToSeed ); TRACEINIT( g_pSteamSocketMgr->Init(), g_pSteamSocketMgr->Shutdown() ); if ( CommandLine()->FindParm( "-xlsp" ) != 0 ) { TRACEINIT( NET_Init( bDedicated ), NET_Shutdown() ); } TRACEINIT( g_GameEventManager.Init(), g_GameEventManager.Shutdown() ); TRACEINIT( sv.Init( bDedicated ), sv.Shutdown() ); if ( !CommandLine()->FindParm( "-nogamedll" ) ) { SV_InitGameDLL(); } TRACEINIT( g_Log.Init(), g_Log.Shutdown() ); TRACEINIT( HLTV_Init(), HLTV_Shutdown() ); #if defined( REPLAY_ENABLED ) TRACEINIT( Replay_Init(), Replay_Shutdown() ); #endif ConDMsg( "Heap: %5.2f Mb\n", host_parms.memsize/(1024.0f*1024.0f) ); // Deal with Gore Settings Host_CheckGore(); #if !defined( DEDICATED ) if ( !bDedicated ) { TRACEINIT( CL_Init(), CL_Shutdown() ); // NOTE: This depends on the mod search path being set up TRACEINIT( InitMaterialSystem(), ShutdownMaterialSystem() ); #if defined( INCLUDE_SCALEFORM ) extern IScaleformSlotInitController *g_pIScaleformSlotInitControllerEngineImpl; TRACEINIT( ScaleformInitFullScreenAndCursor(g_pScaleformUI, g_szDefaultScaleformMovieName, g_szDefaultScaleformCursorName, g_pIScaleformSlotInitControllerEngineImpl ), ScaleformReleaseFullScreenAndCursor( g_pScaleformUI ) ); #endif TRACEINIT( modelloader->Init(), modelloader->Shutdown() ); TRACEINIT( StaticPropMgr()->Init(), StaticPropMgr()->Shutdown() ); TRACEINIT( InitStudioRender(), ShutdownStudioRender() ); TRACEINIT( g_pMatchFramework->Init(), g_pMatchFramework->Shutdown() ); //startup vgui TRACEINIT( EngineVGui()->Init(), EngineVGui()->Shutdown() ); TRACEINIT( TextMessageInit(), TextMessageShutdown() ); TRACEINIT( ClientDLL_Init(), ClientDLL_Shutdown() ); TRACEINIT( SCR_Init(), SCR_Shutdown() ); TRACEINIT( R_Init(), R_Shutdown() ); TRACEINIT( Decal_Init(), Decal_Shutdown() ); // hookup interfaces EngineVGui()->Connect(); } else #endif { TRACEINIT( InitMaterialSystem(), ShutdownMaterialSystem() ); TRACEINIT( modelloader->Init(), modelloader->Shutdown() ); TRACEINIT( StaticPropMgr()->Init(), StaticPropMgr()->Shutdown() ); TRACEINIT( InitStudioRender(), ShutdownStudioRender() ); TRACEINIT( g_pMatchFramework->Init(), g_pMatchFramework->Shutdown() ); TRACEINIT( Decal_Init(), Decal_Shutdown() ); #ifndef DEDICATED GetBaseLocalClient().m_nSignonState = SIGNONSTATE_NONE; // disable client #endif } if ( IsPC() ) { #if !defined(NO_STEAM) // on PC, enable FCVAR_DEVELOPMENTONLY cvars if we're logged into beta or dev universes. They remain hidden & disabled otherwise. EUniverse eUniverse = GetSteamUniverse(); if ( ( eUniverse == k_EUniverseBeta ) || ( eUniverse == k_EUniverseDev ) ) { ConVarUtilities->EnableDevCvars(); } #endif } else if ( IsGameConsole() ) { // on 360, enable FCVAR_DEVELOPMENTONLY cvars always. No cvars are accessible to customers on X360. ConVarUtilities->EnableDevCvars(); } #ifndef DEDICATED memset( g_bConfigCfgExecuted, 0, sizeof(g_bConfigCfgExecuted) ); Host_ReadConfiguration( -1, false ); TRACEINIT( S_Init(), S_Shutdown() ); // Audio system initializes after matchmaking, so need to explicitly // set the voice interface extension IEngineVoice *pIEngineVoice = NULL; #ifdef _GAMECONSOLE pIEngineVoice = Audio_GetXVoice(); #elif ( defined( _WIN32 ) || defined( OSX ) || defined( LINUX ) ) && !defined( NO_STEAM ) pIEngineVoice = Audio_GetEngineVoiceSteam(); #else pIEngineVoice = Audio_GetEngineVoiceStub(); #endif if ( !pIEngineVoice ) { Warning( "Using IEngineVoice extension stub!\n" ); pIEngineVoice = Audio_GetEngineVoiceStub(); } g_pMatchFramework->GetMatchExtensions()->RegisterExtensionInterface( IENGINEVOICE_INTERFACE_VERSION, pIEngineVoice ); if (vaudio) { //Pin miles sound system as loaded until Host_Shutdown() is caused. This prevents any //possible path where MSS could unload and then somehow be reloaded. g_pMilesAudioEngineRef = vaudio->CreateMilesAudioEngine(); } #endif // Execute valve.rc #ifdef _GAMECONSOLE // the 360 version loads a 3d background Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec valve.rc\n" ); #else Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec valve.rc\n" ); #endif // Execute mod-specfic settings, without falling back based on search path. // This lets us set overrides for games while letting mods of those games // use the default settings. if ( g_pFileSystem->FileExists( "//mod/cfg/modsettings.cfg" ) ) { Cbuf_AddText( Cbuf_GetCurrentPlayer(), "exec modsettings.cfg mod\n" ); } #ifdef _GAMECONSOLE Cbuf_AddText( Cbuf_GetCurrentPlayer(), "cmd2 exec config" PLATFORM_EXT ".cfg\n" ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), "cmd1 exec config" PLATFORM_EXT ".cfg\n" ); #endif // Mark DLL as active // eng->SetNextState( InEditMode() ? IEngine::DLL_PAUSED : IEngine::DLL_ACTIVE ); TelemetryTick(); // Initialize processor subsystem, and print relevant information: Host_InitProcessor(); // Mark hunklevel at end of startup Hunk_AllocName( 0, "-HOST_HUNKLEVEL-" ); host_hunklevel = Hunk_LowMark(); #ifdef SOURCE_MT if ( CommandLine()->FindParm( "-swapcores" ) && !IsPS3() ) { g_nMaterialSystemThread = 1; g_nServerThread = 0; } #endif Host_AllowQueuedMaterialSystem( false ); // Finished initializing host_initialized = true; host_checkheap = CommandLine()->FindParm( "-heapcheck" ) ? true : false; if ( host_checkheap ) { #if defined( _WIN32 ) if ( _heapchk() != _HEAPOK ) { Sys_Error( "Host_Init: _heapchk() != _HEAPOK\n" ); } #endif } // go directly to run state with no active game HostState_Init(); // check for reslist generation if ( CommandLine()->FindParm( "-makereslists" ) ) { MapReslistGenerator().StartReslistGeneration(); } // check for devshot generation if ( CommandLine()->FindParm( "-makedevshots" ) ) { DevShotGenerator().StartDevShotGeneration(); } // if running outside of steam and NOT a dedicated server then phone home (or if "-phonehome" is passed on the command line) if ( !sv.IsDedicated() || CommandLine()->FindParm( "-phonehome" ) ) { // In debug, only run this check if -phonehome is on the command line (so a debug build will "just work"). if ( IsDebug() && CommandLine()->FindParm( "-phonehome" ) ) { phonehome->Init(); phonehome->Message( IPhoneHome::PHONE_MSG_ENGINESTART, NULL ); } } Host_PostInit(); EndLoadingUpdates(); CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); pRenderContext->SetNonInteractiveTempFullscreenBuffer( NULL, MATERIAL_NON_INTERACTIVE_MODE_STARTUP ); pRenderContext->SetNonInteractivePacifierTexture( NULL, 0, 0, 0 ); pRenderContext->SetNonInteractiveLogoTexture( NULL, 0, 0, 0, 0 ); // disable future render target allocation g_pMaterialSystem->FinishRenderTargetAllocation(); if ( CommandLine()->FindParm( "-profileinit" ) ) { Cbuf_AddText( Cbuf_GetCurrentPlayer(), "quit\n" ); } if ( !bDedicated && Host_IsSecureServerAllowed() ) { Host_FinishSecureSignatureChecks(); } #if !defined(DEDICATED) if ( g_pMatchFramework && !Host_IsSecureServerAllowed() ) { g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnClientInsecureBlocked", "reason", "init" ) ); } #endif // Official valve servers must be running with a certificate if ( CommandLine()->FindParm( "-certificate" ) || ( serverGameDLL && serverGameDLL->IsValveDS() && !CommandLine()->FindParm( "-ignore_certificate_valveds" ) ) ) { const byte *pbNetEncryptPrivateKey = NULL; int cbNetEncryptPrivateKey = 0; bool bHasPrivateKey = NET_CryptGetNetworkCertificate( k_ENetworkCertificate_PublicKey, &pbNetEncryptPrivateKey, &cbNetEncryptPrivateKey ) && NET_CryptGetNetworkCertificate( k_ENetworkCertificate_Signature, &pbNetEncryptPrivateKey, &cbNetEncryptPrivateKey ) && NET_CryptGetNetworkCertificate( k_ENetworkCertificate_PrivateKey, &pbNetEncryptPrivateKey, &cbNetEncryptPrivateKey ); if ( !bHasPrivateKey || !pbNetEncryptPrivateKey || !cbNetEncryptPrivateKey ) { Warning( "NET_CryptGetNetworkCertificate is missing server certificates!\n" ); Plat_ExitProcess( 0 ); } } } class CAddTransitionResourcesCB : public ISaveRestoreDataCallback { public: CAddTransitionResourcesCB( const char *pLevelName, const char *pLandmarkName ) : m_pLevelName( pLevelName ), m_pLandMarkName( pLandmarkName ) {} //----------------------------------------------------------------------------- // Adds hints to the loader to keep resources that are in the transition volume, // as they may not be part of the next map's reslist. //----------------------------------------------------------------------------- virtual void Execute( CSaveRestoreData *pSaveData ) { if ( !pSaveData || IsPC() || ( g_pFileSystem->GetDVDMode() == DVDMODE_OFF ) ) { return; } // get the bit marked for the next level int transitionMask = 0; for ( int i = 0; i < pSaveData->levelInfo.connectionCount; i++ ) { if ( !Q_stricmp( m_pLevelName, pSaveData->levelInfo.levelList[i].mapName ) && !Q_stricmp( m_pLandMarkName, pSaveData->levelInfo.levelList[i].landmarkName ) ) { transitionMask = 1<NumEntities(); i++ ) { if ( pSaveData->GetEntityInfo(i)->flags & transitionMask ) { // this entity will cross the transition and needs to be preserved // add to the next map's resource list which effectively keeps it from being purged // only care about the actual mdl and not any of its dependants pModelName = pSaveData->GetEntityInfo(i)->modelname.ToCStr(); g_pQueuedLoader->AddMapResource( pModelName ); // humans require a post pass if ( !bHasHumans && V_stristr( pModelName, "models/humans" ) ) { bHasHumans = true; } } } if ( bHasHumans ) { // the presence of any human entity in the transition needs to ensure all the human mdls stay int count = modelloader->GetCount(); for ( int i = 0; i < count; i++ ) { pModelName = modelloader->GetName( modelloader->GetModelForIndex( i ) ); if ( V_stristr( pModelName, "models/humans" ) ) { g_pQueuedLoader->AddMapResource( pModelName ); } } } } const char *m_pLevelName; const char *m_pLandMarkName; }; // There's a version of this in bsplib.cpp!!! Make sure that they match. void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int maxLength ) { Q_strncpy( pPlatformMapPath, pMapPath, maxLength ); // It's OK for this to be NULL on the dedicated server. if( g_pMaterialSystemHardwareConfig ) { Q_StripExtension( pMapPath, pPlatformMapPath, maxLength ); Q_strncat( pPlatformMapPath, ".bsp", maxLength, COPY_ALL_CHARACTERS ); } } void AdjustThreadPoolThreadCount() { #if defined( DEDICATED ) && IsPlatformLinux() extern ConVar occlusion_test_async; int nTargetThreads = occlusion_test_async.GetInt(); if ( nTargetThreads != g_pThreadPool->NumThreads() ) { uint64 nTickStart = GetTimebaseRegister(); Msg( "Stopping %d worker threads\n", g_pThreadPool->NumThreads() ); g_pThreadPool->ExecuteAll(); g_pThreadPool->Stop(); if ( nTargetThreads != 0 ) { Msg( "Starting %d worker threads\n", nTargetThreads); ThreadPoolStartParams_t startParams; GetThreadPoolStartParams( startParams ); startParams.nThreads = Max( 1, Min( 4, nTargetThreads ) ); startParams.bEnableOnLinuxDedicatedServer = true; // if we run with -threads parameter, we should also enable the thread pool if ( g_pThreadPool ) { g_pThreadPool->Start( startParams ); } } uint64 nTickEnd = GetTimebaseRegister(); Msg( "%d threads. %s ticks\n", g_pThreadPool->NumThreads(), V_pretifynum( nTickEnd - nTickStart ) ); int nCmdLineThreads = CommandLine()->ParmValue( "-threads", -1 ); if ( nCmdLineThreads >= 0 ) { Msg( "Note that cmd line -threads %d is specified\n", nCmdLineThreads ); } } #endif } void Host_Changelevel( bool loadfromsavedgame, const char *mapname, char *mapGroupName, const char *start ) { char level[ MAX_QPATH ]; char _startspot[ MAX_QPATH ]; char *startspot; char oldlevel[ MAX_QPATH ]; #if !defined(DEDICATED) bool bTransitionBySave = false; #endif if ( !sv.IsActive() ) { ConMsg( "Only the server may changelevel\n" ); return; } #ifndef DEDICATED // FIXME: Even needed? if ( demoplayer->IsPlayingBack() ) { ConMsg( "Changelevel invalid during demo playback\n" ); return; } if ( !sv.IsDedicated() ) { EngineVGui()->SetProgressLevelName( mapname ); } SCR_BeginLoadingPlaque( mapname ); #endif g_pFileSystem->AsyncFinishAll(); #if !defined DEDICATED // stop sounds (especially looping!) S_StopAllSounds( true ); #endif char dxMapName[ MAX_PATH ]; GetPlatformMapPath( mapname, dxMapName, MAX_PATH ); host_map.SetValue( dxMapName ); Q_strncpy( level, mapname, sizeof( level ) ); if ( !start ) startspot = NULL; else { Q_strncpy( _startspot, start, sizeof( _startspot ) ); startspot = _startspot; } Warning( "---- Host_Changelevel ----\n" ); SV_CheckForFlushMemory( sv.GetMapName(), mapname ); if ( IsX360() ) { // Reset material system temporary memory (frees up memory for map loading) materials->ResetTempHWMemory( true ); } materials->OnLevelShutdown(); #if !defined( DEDICATED ) // Always save as an xsave if we're on the xbox saverestore->SetIsXSave( IsX360() ); // Add on time passed since the last time we kept track till this transition int iAdditionalSeconds = g_ServerGlobalVariables.curtime - saverestore->GetMostRecentElapsedTimeSet(); int iElapsedSeconds = saverestore->GetMostRecentElapsedSeconds() + iAdditionalSeconds; int iElapsedMinutes = saverestore->GetMostRecentElapsedMinutes() + ( iElapsedSeconds / 60 ); saverestore->SetMostRecentElapsedMinutes( iElapsedMinutes ); saverestore->SetMostRecentElapsedSeconds( ( iElapsedSeconds % 60 ) ); KeyValues *kvChangelevelEvent = new KeyValues( "OnHostChangeLevel" ); kvChangelevelEvent->SetString( "map", mapname ); kvChangelevelEvent->SetUint64( "elapsed", iElapsedMinutes * 60 + iElapsedSeconds % 60 ); kvChangelevelEvent->SetInt( "bysave", !!bTransitionBySave ); g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( kvChangelevelEvent ); if ( bTransitionBySave ) { char comment[ 80 ]; // Pass in the total elapsed time so it gets added to the elapsed time for this map. serverGameDLL->GetSaveComment( comment, sizeof( comment ), saverestore->GetMostRecentElapsedMinutes(), saverestore->GetMostRecentElapsedSeconds() ); if ( !saverestore->SaveGameSlot( "_transition", comment, false, true, mapname, startspot ) ) { Warning( "Failed to save data for transition\n" ); SCR_EndLoadingPlaque(); return; } // Not going to load a save after the transition, so add this map's elapsed time to the total elapsed time int totalSeconds = g_ServerGlobalVariables.curtime + saverestore->GetMostRecentElapsedSeconds(); saverestore->SetMostRecentElapsedMinutes( ( int )( totalSeconds / 60.0f ) + saverestore->GetMostRecentElapsedMinutes() ); saverestore->SetMostRecentElapsedSeconds( ( int )fmod( totalSeconds, 60.0f ) ); } #endif Q_strncpy( oldlevel, sv.GetMapName(), sizeof( oldlevel ) ); #if !defined(DEDICATED) if ( loadfromsavedgame ) { if ( !bTransitionBySave ) { // This callback ensures resources in the transition volume stay CAddTransitionResourcesCB addTransitionResources( level, startspot ); // save the current level's state if ( !saverestore->SaveGameState( true, &addTransitionResources ) ) { Warning( "Failed to save data for transition\n" ); SCR_EndLoadingPlaque(); return; } } } #endif g_pServerPluginHandler->LevelShutdown(); #if !defined(DEDICATED) audiosourcecache->LevelShutdown(); #endif sv.InactivateClients(); #if !defined(DEDICATED) saverestore->FinishAsyncSave(); #endif if ( sv.RestartOnLevelChange() ) { Cbuf_Clear( Cbuf_GetCurrentPlayer() ); Cbuf_AddText( Cbuf_GetCurrentPlayer(), "quit\n" ); return; } DownloadListGenerator().OnLevelLoadStart( level ); Msg( "*** Map Load: %s: Map Group %s", level, mapGroupName ); if ( !sv.SpawnServer( level, mapGroupName, startspot ) ) { return; } #ifndef DEDICATED if ( loadfromsavedgame ) { g_ServerGlobalVariables.curtime = sv.GetTime(); audiosourcecache->LevelInit( level ); g_pServerPluginHandler->LevelInit( level, CM_EntityString(), oldlevel, startspot, true, false ); sv.SetPaused( true ); // pause until client connects sv.m_bLoadgame = true; } else #endif { g_ServerGlobalVariables.curtime = sv.GetTime(); #if !defined(DEDICATED) audiosourcecache->LevelInit( level ); #endif g_pServerPluginHandler->LevelInit( level, CM_EntityString(), NULL, NULL, false, false ); } SV_ActivateServer(); #if !defined(DEDICATED) // Offset stored elapsed time by the current elapsed time for this new map int maptime = sv.GetTime(); int minutes = ( int )( maptime / 60.0f ); int seconds = ( int )fmod( maptime, 60.0f ); saverestore->SetMostRecentElapsedMinutes( saverestore->GetMostRecentElapsedMinutes() - minutes ); saverestore->SetMostRecentElapsedSeconds( saverestore->GetMostRecentElapsedSeconds() - seconds ); saverestore->ForgetRecentSave(); #endif NotifyDedicatedServerUI( "UpdateMap" ); DownloadListGenerator().OnLevelLoadEnd(); AdjustThreadPoolThreadCount(); } /* =============================================================================== SERVER TRANSITIONS =============================================================================== */ bool Host_NewGame( char *mapName, char *mapGroupName, bool loadGame, bool bBackgroundLevel, bool bSplitScreenConnect, const char *pszOldMap, const char *pszLandmark ) { VPROF( "Host_NewGame" ); COM_TimestampedLog( "Host_NewGame" ); // reset some convars if loading background map if ( bBackgroundLevel ) ResetGameConVarsToDefaults(); char previousMapName[MAX_PATH]; char dxMapName[MAX_PATH]; V_FixSlashes( mapName, '/' ); GetPlatformMapPath( mapName, dxMapName, MAX_PATH ); Q_strncpy( previousMapName, host_map.GetString(), sizeof( previousMapName ) ); host_map.SetValue( dxMapName ); // Setup gamemode based on the settings the map was started with sv.ExecGameTypeCfg( mapName ); #ifndef DEDICATED if ( !sv.IsDedicated() ) { EngineVGui()->SetProgressLevelName( mapName ); } SCR_BeginLoadingPlaque( mapName ); #endif Warning( "---- Host_NewGame ----\n" ); SV_CheckForFlushMemory( previousMapName, mapName ); if ( IsX360() ) { // Reset material system temporary memory (frees up memory for map loading) materials->ResetTempHWMemory( true ); } materials->OnLevelShutdown(); MapReslistGenerator().OnLevelLoadStart(mapName); DownloadListGenerator().OnLevelLoadStart(mapName); if ( !loadGame ) { VPROF( "Host_NewGame_HostState_RunGameInit" ); HostState_RunGameInit(); } // init network mode VPROF_SCOPE_BEGIN( "Host_NewGame_SpawnServer" ); NET_SetMultiplayer( sv.IsMultiplayer() ); if ( !sv.IsMultiplayer() ) NET_SetMultiplayer( ( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_SETTING_MULTIPLAYER ) != 0 ); NET_ListenSocket( sv.m_Socket, true ); // activated server TCP socket // let's not have any servers with no name Host_EnsureHostNameSet(); COM_TimestampedLog( "*** Map Load: %s Map %s Group", mapName, mapGroupName ); HostState_Pre_LoadMapIntoMemory(); // A map is about to be loaded into memory if ( !sv.SpawnServer ( mapName, mapGroupName, NULL ) ) { HostState_Post_FlushMapFromMemory(); // Map load failed, no impact on memory return false; } sv.m_bIsLevelMainMenuBackground = bBackgroundLevel; ConColorMsg( Color( 0, 255, 0 ), "Host_NewGame on map %s%s\n", mapName, sv.IsLevelMainMenuBackground() ? " (background map)" : "" ); VPROF_SCOPE_END(); // make sure the time is set g_ServerGlobalVariables.curtime = sv.GetTime(); COM_TimestampedLog( "serverGameDLL->LevelInit" ); #ifndef DEDICATED EngineVGui()->UpdateProgressBar(PROGRESS_LEVELINIT); audiosourcecache->LevelInit( mapName ); #endif g_pServerPluginHandler->LevelInit( mapName, CM_EntityString(), pszOldMap, pszLandmark, loadGame, bBackgroundLevel ); if ( loadGame ) { sv.SetPaused( true ); // pause until all clients connect sv.m_bLoadgame = true; g_ServerGlobalVariables.curtime = sv.GetTime(); } if( !SV_ActivateServer() ) { return false; } // Connect the local client when a "map" command is issued. if ( !sv.IsDedicated() ) { COM_TimestampedLog( "Stuff 'connect localhost' to console" ); int nNumPlayers = 1; if( IsGameConsole() && !bBackgroundLevel ) { #ifdef _GAMECONSOLE // on the 360 we need to ask matchmaking how many players to connect. nNumPlayers = XBX_GetNumGameUsers(); #endif } if ( bSplitScreenConnect ) { Assert( !bBackgroundLevel ); nNumPlayers = host_state.max_splitscreen_players; } char str[512]; if ( nNumPlayers > 1 ) { Q_snprintf( str, sizeof( str ), "connect_splitscreen localhost:%d %d", sv.GetUDPPort(), nNumPlayers ); } else { Q_snprintf( str, sizeof( str ), "connect localhost:%d", sv.GetUDPPort() ); } Cbuf_AddText( Cbuf_GetCurrentPlayer(), str ); } else { // Dedicated server triggers map load here. GetTestScriptMgr()->CheckPoint( "FinishedMapLoad" ); } #ifndef DEDICATED if ( !loadGame ) { // clear the most recent remember save, so the level will just restart if the player dies saverestore->ForgetRecentSave(); } saverestore->SetMostRecentElapsedMinutes( 0 ); saverestore->SetMostRecentElapsedSeconds( 0 ); #endif if (MapReslistGenerator().IsEnabled()) { MapReslistGenerator().OnLevelLoadEnd(); } DownloadListGenerator().OnLevelLoadEnd(); return true; } void Host_FreeStateAndWorld( bool server ) { Assert( host_initialized ); Assert( host_hunklevel ); // If called by the client and we are running a listen server, just ignore if ( !server && sv.IsActive() ) return; // HACKHACK: You can't clear the hunk unless the client data is free // since this gets called by the server, it's necessary to wipe the client // in case we are on a listen server #ifndef DEDICATED if ( server && !sv.IsDedicated() ) { CL_ClearState(); } #endif // The world model relies on the low hunk, so we need to force it to unload if ( host_state.worldmodel ) { modelloader->UnreferenceModel( host_state.worldmodel, IModelLoader::FMODELLOADER_SERVER ); modelloader->UnreferenceModel( host_state.worldmodel, IModelLoader::FMODELLOADER_CLIENT ); host_state.SetWorldModel( NULL ); } modelloader->UnloadUnreferencedModels(); modelloader->UnMountCompatibilityPaths(); g_TimeLastMemTest = 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Host_FreeToLowMark( bool server ) { Assert( host_initialized ); Assert( host_hunklevel ); // If called by the client and we are running a listen server, just ignore if ( !server && ( sv.IsActive() || sv.IsLoading() ) ) return; CM_FreeMap(); if ( host_hunklevel ) { // See if we are going to obliterate any malloc'd pointers Hunk_FreeToLowMark(host_hunklevel); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Host_Shutdown(void) { if ( host_checkheap ) { #ifdef _WIN32 if ( _heapchk() != _HEAPOK ) { Sys_Error( "Host_Shutdown (top): _heapchk() != _HEAPOK\n" ); } #endif } // Check for recursive shutdown, should never happen static bool shutting_down = false; if ( shutting_down ) { Msg( "Recursive shutdown!!!\n" ); return; } shutting_down = true; if( g_pDebugInputThread ) { g_pDebugInputThread->Stop(); delete g_pDebugInputThread; } phonehome->Message( IPhoneHome::PHONE_MSG_ENGINEEND, NULL ); phonehome->Shutdown(); #ifndef DEDICATED // Store active configuration settings Host_WriteConfiguration( -1, "config.cfg" ); #endif // Disconnect from server Host_Disconnect(true); #ifndef DEDICATED // keep ConMsg from trying to update the screen scr_disabled_for_loading = true; #endif #if defined VOICE_OVER_IP && !defined DEDICATED && !defined( NO_VOICE ) Voice_Deinit(); #endif // VOICE_OVER_IP // TODO, Trace this CM_FreeMap(); host_initialized = false; #if defined(VPROF_ENABLED) VProfRecord_Shutdown(); #endif #if !defined DEDICATED if ( !sv.IsDedicated() ) { if (vaudio && g_pMilesAudioEngineRef) { //let miles sound system exit here. vaudio->DestroyMilesAudioEngine(g_pMilesAudioEngineRef); g_pMilesAudioEngineRef = nullptr; } TRACESHUTDOWN( Decal_Shutdown() ); TRACESHUTDOWN( R_Shutdown() ); TRACESHUTDOWN( SCR_Shutdown() ); if ( g_pMatchFramework ) { TRACESHUTDOWN( g_pMatchFramework->Shutdown() ); } TRACESHUTDOWN( ClientDLL_Shutdown() ); TRACESHUTDOWN( TextMessageShutdown() ); TRACESHUTDOWN( EngineVGui()->Shutdown() ); #if defined( INCLUDE_SCALEFORM ) TRACESHUTDOWN( ScaleformReleaseFullScreenAndCursor( g_pScaleformUI ) ); #endif TRACESHUTDOWN( S_Shutdown() ); TRACESHUTDOWN( StaticPropMgr()->Shutdown() ); // Model loader must shutdown before StudioRender // because it calls into StudioRender TRACESHUTDOWN( modelloader->Shutdown() ); TRACESHUTDOWN( ShutdownStudioRender() ); TRACESHUTDOWN( ShutdownMaterialSystem() ); TRACESHUTDOWN( CL_Shutdown() ); } else #endif { if ( g_pMatchFramework ) { TRACESHUTDOWN( g_pMatchFramework->Shutdown() ); } #ifndef DEDICATED TRACESHUTDOWN( S_Shutdown() ); #endif TRACESHUTDOWN( Decal_Shutdown() ); TRACESHUTDOWN( modelloader->Shutdown() ); TRACESHUTDOWN( ShutdownStudioRender() ); TRACESHUTDOWN( StaticPropMgr()->Shutdown() ); TRACESHUTDOWN( ShutdownMaterialSystem() ); } #if defined( REPLAY_ENABLED ) TRACESHUTDOWN( Replay_Shutdown() ); #endif TRACESHUTDOWN( HLTV_Shutdown() ); TRACESHUTDOWN( g_Log.Shutdown() ); TRACESHUTDOWN( g_GameEventManager.Shutdown() ); #if !defined( DEDICATED ) if ( !sv.IsDedicated() ) { TRACESHUTDOWN( Steam3Client().Shutdown() ); } #endif TRACESHUTDOWN( sv.Shutdown() ); TRACESHUTDOWN( NET_Shutdown() ); TRACESHUTDOWN( g_pSteamSocketMgr->Shutdown() ); #ifndef DEDICATED TRACESHUTDOWN( Key_Shutdown() ); #if !defined( _X360 ) TRACESHUTDOWN( ShutdownMixerControls() ); #endif #endif TRACESHUTDOWN( Filter_Shutdown() ); #if !defined(DEDICATED) && !defined(LEFT4DEAD) TRACESHUTDOWN( saverestore->Shutdown() ); #endif TRACESHUTDOWN( COM_Shutdown() ); // TRACESHUTDOWN( Host_ShutdownVCR() ); #ifndef DEDICATED TRACESHUTDOWN( V_Shutdown() ); #endif TRACESHUTDOWN( g_pCVar->Shutdown() ); TRACESHUTDOWN( Cmd_Shutdown() ); TRACESHUTDOWN( Cbuf_Shutdown() ); TRACESHUTDOWN( Con_Shutdown() ); TRACESHUTDOWN( Memory_Shutdown() ); if ( g_pThreadPool ) g_pThreadPool->Stop(); DTI_Term(); ServerDTI_Term(); #if defined(_WIN32) if ( s_bInitPME ) { ShutdownPME(); } #endif if ( host_checkheap ) { #ifdef _WIN32 if ( _heapchk() != _HEAPOK ) { Sys_Error( "Host_Shutdown (bottom): _heapchk() != _HEAPOK\n" ); } #endif } } //----------------------------------------------------------------------------- // Centralize access to enabling QMS. //----------------------------------------------------------------------------- bool Host_AllowQueuedMaterialSystem( bool bAllow ) { #if !defined DEDICATED g_bAllowThreadedSound = bAllow; // NOTE: Moved this to materialsystem for integrating with other mqm changes return g_pMaterialSystem->AllowThreading( bAllow, g_nMaterialSystemThread ); #endif } void Host_EnsureHostNameSet() { // if there is host name set, set one if ( host_name.GetString()[0] == 0 ) { const char *szHostName = ""; #ifndef DEDICATED // if this is a PC listen server and there is a logged-on Steam user, use the user's Steam name as the host name if ( IsPC() && !sv.IsDedicated() && Steam3Client().SteamUser() ) { if ( Steam3Client().SteamUser()->BLoggedOn() ) { szHostName = Steam3Client().SteamFriends()->GetPersonaName(); } } #endif // if all else fails, use the game description as the host name if ( !szHostName[0] ) { szHostName = serverGameDLL->GetGameDescription(); } host_name.SetValue( szHostName ); } }