Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2571 lines
71 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #define DISABLE_PROTECTED_THINGS
  8. #if defined( USE_SDL )
  9. #include "appframework/ilaunchermgr.h"
  10. #endif
  11. #if defined( _WIN32 ) && !defined( _X360 )
  12. #include "winlite.h"
  13. #include <Psapi.h>
  14. #endif
  15. #if defined( OSX )
  16. #include <sys/sysctl.h>
  17. #endif
  18. #if defined( POSIX )
  19. #include <setjmp.h>
  20. #include <signal.h>
  21. #endif
  22. #include <stdarg.h>
  23. #include "quakedef.h"
  24. #include "idedicatedexports.h"
  25. #include "engine_launcher_api.h"
  26. #include "ivideomode.h"
  27. #include "common.h"
  28. #include "iregistry.h"
  29. #include "keys.h"
  30. #include "cdll_engine_int.h"
  31. #include "traceinit.h"
  32. #include "iengine.h"
  33. #include "igame.h"
  34. #include "tier0/etwprof.h"
  35. #include "tier0/vcrmode.h"
  36. #include "tier0/icommandline.h"
  37. #include "tier0/minidump.h"
  38. #include "engine_hlds_api.h"
  39. #include "filesystem_engine.h"
  40. #include "cl_main.h"
  41. #include "client.h"
  42. #include "tier3/tier3.h"
  43. #include "MapReslistGenerator.h"
  44. #include "toolframework/itoolframework.h"
  45. #include "sourcevr/isourcevirtualreality.h"
  46. #include "DevShotGenerator.h"
  47. #include "gl_shader.h"
  48. #include "l_studio.h"
  49. #include "IHammer.h"
  50. #include "sys_dll.h"
  51. #include "materialsystem/materialsystem_config.h"
  52. #include "server.h"
  53. #include "video/ivideoservices.h"
  54. #include "datacache/idatacache.h"
  55. #include "vphysics_interface.h"
  56. #include "inputsystem/iinputsystem.h"
  57. #include "appframework/IAppSystemGroup.h"
  58. #include "tier0/systeminformation.h"
  59. #include "host_cmd.h"
  60. #ifdef _WIN32
  61. #include "VGuiMatSurface/IMatSystemSurface.h"
  62. #endif
  63. #ifdef GPROFILER
  64. #include "gperftools/profiler.h"
  65. #endif
  66. // This is here just for legacy support of older .dlls!!!
  67. #include "SoundEmitterSystem/isoundemittersystembase.h"
  68. #include "eiface.h"
  69. #include "tier1/fmtstr.h"
  70. #include "steam/steam_api.h"
  71. #ifndef SWDS
  72. #include "sys_mainwind.h"
  73. #include "vgui/ISystem.h"
  74. #include "vgui_controls/Controls.h"
  75. #include "IGameUIFuncs.h"
  76. #include "cl_steamauth.h"
  77. #endif // SWDS
  78. #if defined(_WIN32)
  79. #include <eh.h>
  80. #endif
  81. #if POSIX
  82. #include <dlfcn.h>
  83. #endif
  84. #if defined( _X360 )
  85. #include "xbox/xbox_win32stubs.h"
  86. #else
  87. #include "xbox/xboxstubs.h"
  88. #endif
  89. // memdbgon must be the last include file in a .cpp file!!!
  90. #include "tier0/memdbgon.h"
  91. //-----------------------------------------------------------------------------
  92. // Globals
  93. //-----------------------------------------------------------------------------
  94. IDedicatedExports *dedicated = NULL;
  95. extern CreateInterfaceFn g_AppSystemFactory;
  96. IHammer *g_pHammer = NULL;
  97. IPhysics *g_pPhysics = NULL;
  98. ISourceVirtualReality *g_pSourceVR = NULL;
  99. #if defined( USE_SDL )
  100. ILauncherMgr *g_pLauncherMgr = NULL;
  101. #endif
  102. #ifndef SWDS
  103. extern CreateInterfaceFn g_ClientFactory;
  104. #endif
  105. static SteamInfVersionInfo_t g_SteamInfIDVersionInfo;
  106. const SteamInfVersionInfo_t& GetSteamInfIDVersionInfo()
  107. {
  108. Assert( g_SteamInfIDVersionInfo.AppID != k_uAppIdInvalid );
  109. return g_SteamInfIDVersionInfo;
  110. }
  111. int build_number( void )
  112. {
  113. return GetSteamInfIDVersionInfo().ServerVersion;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Forward declarations
  117. //-----------------------------------------------------------------------------
  118. void Host_GetHostInfo(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen );
  119. const char *Key_BindingForKey( int keynum );
  120. void COM_ShutdownFileSystem( void );
  121. void COM_InitFilesystem( const char *pFullModPath );
  122. void Host_ReadPreStartupConfiguration();
  123. //-----------------------------------------------------------------------------
  124. // ConVars and console commands
  125. //-----------------------------------------------------------------------------
  126. #ifndef SWDS
  127. //-----------------------------------------------------------------------------
  128. // Purpose: exports an interface that can be used by the launcher to run the engine
  129. // this is the exported function when compiled as a blob
  130. //-----------------------------------------------------------------------------
  131. void EXPORT F( IEngineAPI **api )
  132. {
  133. CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary to prevent the LTCG compiler from crashing.
  134. *api = ( IEngineAPI * )(factory(VENGINE_LAUNCHER_API_VERSION, NULL));
  135. }
  136. #endif // SWDS
  137. //-----------------------------------------------------------------------------
  138. // Purpose:
  139. //-----------------------------------------------------------------------------
  140. void ClearIOStates( void )
  141. {
  142. #ifndef SWDS
  143. if ( g_ClientDLL )
  144. {
  145. g_ClientDLL->IN_ClearStates();
  146. }
  147. #endif
  148. }
  149. //-----------------------------------------------------------------------------
  150. // The SDK launches the game with the full path to gameinfo.txt, so we need
  151. // to strip off the path.
  152. //-----------------------------------------------------------------------------
  153. const char *GetModDirFromPath( const char *pszPath )
  154. {
  155. char *pszSlash = Q_strrchr( pszPath, '\\' );
  156. if ( pszSlash )
  157. {
  158. return pszSlash + 1;
  159. }
  160. else if ( ( pszSlash = Q_strrchr( pszPath, '/' ) ) != NULL )
  161. {
  162. return pszSlash + 1;
  163. }
  164. // Must just be a mod directory already.
  165. return pszPath;
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose: Main entry
  169. //-----------------------------------------------------------------------------
  170. #ifndef SWDS
  171. #include "gl_matsysiface.h"
  172. #endif
  173. //-----------------------------------------------------------------------------
  174. // Inner loop: initialize, shutdown main systems, load steam to
  175. //-----------------------------------------------------------------------------
  176. class CModAppSystemGroup : public CAppSystemGroup
  177. {
  178. typedef CAppSystemGroup BaseClass;
  179. public:
  180. // constructor
  181. CModAppSystemGroup( bool bServerOnly, CAppSystemGroup *pParentAppSystem = NULL )
  182. : BaseClass( pParentAppSystem ),
  183. m_bServerOnly( bServerOnly )
  184. {
  185. }
  186. CreateInterfaceFn GetFactory()
  187. {
  188. return CAppSystemGroup::GetFactory();
  189. }
  190. // Methods of IApplication
  191. virtual bool Create();
  192. virtual bool PreInit();
  193. virtual int Main();
  194. virtual void PostShutdown();
  195. virtual void Destroy();
  196. private:
  197. bool IsServerOnly() const
  198. {
  199. return m_bServerOnly;
  200. }
  201. bool ModuleAlreadyInList( CUtlVector< AppSystemInfo_t >& list, const char *moduleName, const char *interfaceName );
  202. bool AddLegacySystems();
  203. bool m_bServerOnly;
  204. };
  205. #if defined( STAGING_ONLY )
  206. CON_COMMAND( bigalloc, "huge alloc crash" )
  207. {
  208. Msg( "pre-crash %d\n", MemAlloc_MemoryAllocFailed() );
  209. // Alloc a bit less than UINT_MAX so there is room for heap headers in the malloc functions.
  210. void *buf = malloc( UINT_MAX - 0x4000 );
  211. Msg( "post-alloc %d. buf: %p\n", MemAlloc_MemoryAllocFailed(), buf );
  212. *(int *)buf = 0;
  213. }
  214. #endif
  215. extern void S_ClearBuffer();
  216. extern char g_minidumpinfo[ 4096 ];
  217. extern PAGED_POOL_INFO_t g_pagedpoolinfo;
  218. extern bool g_bUpdateMinidumpComment;
  219. void GetSpew( char *buf, size_t buflen );
  220. extern int gHostSpawnCount;
  221. extern int g_nMapLoadCount;
  222. extern int g_HostServerAbortCount;
  223. extern int g_HostErrorCount;
  224. extern int g_HostEndDemo;
  225. // Turn this to 1 to allow for expanded spew in minidump comments.
  226. static ConVar sys_minidumpexpandedspew( "sys_minidumpexpandedspew", "1" );
  227. #ifdef IS_WINDOWS_PC
  228. extern "C" void __cdecl FailSafe( unsigned int uStructuredExceptionCode, struct _EXCEPTION_POINTERS * pExceptionInfo )
  229. {
  230. // Nothing, this just catches a crash when creating the comment block
  231. }
  232. #endif
  233. #if defined( POSIX )
  234. static sigjmp_buf g_mark;
  235. static void posix_signal_handler( int i )
  236. {
  237. siglongjmp( g_mark, -1 );
  238. }
  239. #define DO_TRY if ( sigsetjmp( g_mark, 1 ) == 0 )
  240. #define DO_CATCH else
  241. #if defined( OSX )
  242. #define __sighandler_t sig_t
  243. #endif
  244. #else
  245. #define DO_TRY try
  246. #define DO_CATCH catch ( ... )
  247. #endif // POSIX
  248. //-----------------------------------------------------------------------------
  249. // Purpose: Check whether any mods are loaded.
  250. // Currently looks for metamod and sourcemod.
  251. //-----------------------------------------------------------------------------
  252. static bool IsSourceModLoaded()
  253. {
  254. #if defined( _WIN32 )
  255. static const char *s_pFileNames[] = { "metamod.2.tf2.dll", "sourcemod.2.tf2.dll", "sdkhooks.ext.2.ep2v.dll", "sdkhooks.ext.2.tf2.dll" };
  256. for ( size_t i = 0; i < Q_ARRAYSIZE( s_pFileNames ); i++ )
  257. {
  258. // GetModuleHandle function returns a handle to a mapped module
  259. // without incrementing its reference count.
  260. if ( GetModuleHandleA( s_pFileNames[ i ] ) )
  261. return true;
  262. }
  263. #else
  264. FILE *fh = fopen( "/proc/self/maps", "r" );
  265. if ( fh )
  266. {
  267. char buf[ 1024 ];
  268. static const char *s_pFileNames[] = { "metamod.2.tf2.so", "sourcemod.2.tf2.so", "sdkhooks.ext.2.ep2v.so", "sdkhooks.ext.2.tf2.so" };
  269. while ( fgets( buf, sizeof( buf ), fh ) )
  270. {
  271. for ( size_t i = 0; i < Q_ARRAYSIZE( s_pFileNames ); i++ )
  272. {
  273. if ( strstr( buf, s_pFileNames[ i ] ) )
  274. {
  275. fclose( fh );
  276. return true;
  277. }
  278. }
  279. }
  280. fclose( fh );
  281. }
  282. #endif
  283. return false;
  284. }
  285. template< int _SIZE >
  286. class CErrorText
  287. {
  288. public:
  289. CErrorText() : m_bIsDedicatedServer( false ) {}
  290. ~CErrorText() {}
  291. void Steam_SetMiniDumpComment()
  292. {
  293. #if !defined( NO_STEAM )
  294. SteamAPI_SetMiniDumpComment( m_errorText );
  295. #endif
  296. }
  297. void CommentCat( const char * str )
  298. {
  299. V_strcat_safe( m_errorText, str );
  300. }
  301. void CommentPrintf( const char *fmt, ... )
  302. {
  303. va_list args;
  304. va_start( args, fmt );
  305. size_t len = strlen( m_errorText );
  306. vsnprintf( m_errorText + len, sizeof( m_errorText ) - len - 1, fmt, args );
  307. m_errorText[ sizeof( m_errorText ) - 1 ] = 0;
  308. va_end( args );
  309. }
  310. void BuildComment( char const *pchSysErrorText, bool bRealCrash )
  311. {
  312. // Try and detect whether this
  313. bool bSourceModLoaded = false;
  314. if ( m_bIsDedicatedServer )
  315. {
  316. bSourceModLoaded = IsSourceModLoaded();
  317. if ( bSourceModLoaded )
  318. {
  319. AppId_t AppId = GetSteamInfIDVersionInfo().ServerAppID;
  320. // Bump up the number and report the crash. This should be something
  321. // like 232251 (instead of 232250). 232251 is for the TF2 Windows client,
  322. // but we actually report those crashes under ID 440, so this should be ok.
  323. SteamAPI_SetBreakpadAppID( AppId + 1 );
  324. }
  325. }
  326. #ifdef IS_WINDOWS_PC
  327. // This warning only applies if you want to catch structured exceptions (crashes)
  328. // using C++ exceptions. We do not want to do that so we can build with C++ exceptions
  329. // completely disabled, and just suppress this warning.
  330. // warning C4535: calling _set_se_translator() requires /EHa
  331. #pragma warning( suppress : 4535 )
  332. _se_translator_function curfilter = _set_se_translator( &FailSafe );
  333. #elif defined( POSIX )
  334. // Only need to worry about this function crashing when we're dealing with a real crash.
  335. __sighandler_t curfilter = bRealCrash ? signal( SIGSEGV, posix_signal_handler ) : 0;
  336. #endif
  337. DO_TRY
  338. {
  339. Q_memset( m_errorText, 0x00, sizeof( m_errorText ) );
  340. if ( pchSysErrorText )
  341. {
  342. CommentCat( "Sys_Error( " );
  343. CommentCat( pchSysErrorText );
  344. // Trim trailing \n.
  345. int len = V_strlen( m_errorText );
  346. if ( len > 0 && m_errorText[ len - 1 ] == '\n' )
  347. m_errorText[ len - 1 ] = 0;
  348. CommentCat( " )\n" );
  349. }
  350. else
  351. {
  352. CommentCat( "Crash\n" );
  353. }
  354. CommentPrintf( "Uptime( %f )\n", Plat_FloatTime() );
  355. CommentPrintf( "SourceMod:%d,DS:%d,Crash:%d\n\n", bSourceModLoaded, m_bIsDedicatedServer, bRealCrash );
  356. // Add g_minidumpinfo from CL_SetSteamCrashComment().
  357. CommentCat( g_minidumpinfo );
  358. // Latch in case extended stuff below crashes
  359. Steam_SetMiniDumpComment();
  360. // Add Memory Status
  361. BuildCommentMemStatus();
  362. // Spew out paged pool stuff, etc.
  363. PAGED_POOL_INFO_t ppi_info;
  364. if ( Plat_GetPagedPoolInfo( &ppi_info ) != SYSCALL_UNSUPPORTED )
  365. {
  366. CommentPrintf( "\nPaged Pool\nprev PP PAGES: used: %lu, free %lu\nfinal PP PAGES: used: %lu, free %lu\n",
  367. g_pagedpoolinfo.numPagesUsed, g_pagedpoolinfo.numPagesFree,
  368. ppi_info.numPagesUsed, ppi_info.numPagesFree );
  369. }
  370. CommentPrintf( "memallocfail? = %u\nActive: %s\nSpawnCount %d MapLoad Count %d\nError count %d, end demo %d, abort count %d\n",
  371. MemAlloc_MemoryAllocFailed(),
  372. ( game && game->IsActiveApp() ) ? "active" : "inactive",
  373. gHostSpawnCount,
  374. g_nMapLoadCount,
  375. g_HostErrorCount,
  376. g_HostEndDemo,
  377. g_HostServerAbortCount );
  378. // Latch in case extended stuff below crashes
  379. Steam_SetMiniDumpComment();
  380. // Add user comment strings. 4096 is just a large sanity number we should
  381. // never ever reach (currently our minidump supports 32 of these.)
  382. for( int i = 0; i < 4096; i++ )
  383. {
  384. const char *pUserStreamInfo = MinidumpUserStreamInfoGet( i );
  385. if( !pUserStreamInfo )
  386. break;
  387. if ( pUserStreamInfo[ 0 ] )
  388. CommentPrintf( "%s", pUserStreamInfo );
  389. }
  390. bool bExtendedSpew = sys_minidumpexpandedspew.GetBool();
  391. if ( bExtendedSpew )
  392. {
  393. BuildCommentExtended();
  394. Steam_SetMiniDumpComment();
  395. #if defined( LINUX )
  396. if ( bRealCrash )
  397. {
  398. // bRealCrash is set when we're actually making a comment for a dump or error.
  399. AddFileToComment( "/proc/meminfo" );
  400. AddFileToComment( "/proc/self/status" );
  401. Steam_SetMiniDumpComment();
  402. // Useful, but really big, so disable for now.
  403. //$ AddFileToComment( "/proc/self/maps" );
  404. }
  405. #endif
  406. }
  407. }
  408. DO_CATCH
  409. {
  410. // Oh oh
  411. }
  412. #ifdef IS_WINDOWS_PC
  413. _set_se_translator( curfilter );
  414. #elif defined( POSIX )
  415. if ( bRealCrash )
  416. signal( SIGSEGV, curfilter );
  417. #endif
  418. }
  419. void BuildCommentMemStatus()
  420. {
  421. #ifdef _WIN32
  422. const double MbDiv = 1024.0 * 1024.0;
  423. MEMORYSTATUSEX memStat;
  424. ZeroMemory( &memStat, sizeof( MEMORYSTATUSEX ) );
  425. memStat.dwLength = sizeof( MEMORYSTATUSEX );
  426. if ( GlobalMemoryStatusEx( &memStat ) )
  427. {
  428. CommentPrintf( "\nMemory\nmemusage( %d %% )\ntotalPhysical Mb(%.2f)\nfreePhysical Mb(%.2f)\ntotalPaging Mb(%.2f)\nfreePaging Mb(%.2f)\ntotalVirtualMem Mb(%.2f)\nfreeVirtualMem Mb(%.2f)\nextendedVirtualFree Mb(%.2f)\n",
  429. memStat.dwMemoryLoad,
  430. (double)memStat.ullTotalPhys / MbDiv,
  431. (double)memStat.ullAvailPhys / MbDiv,
  432. (double)memStat.ullTotalPageFile / MbDiv,
  433. (double)memStat.ullAvailPageFile / MbDiv,
  434. (double)memStat.ullTotalVirtual / MbDiv,
  435. (double)memStat.ullAvailVirtual / MbDiv,
  436. (double)memStat.ullAvailExtendedVirtual / MbDiv);
  437. }
  438. HINSTANCE hInst = LoadLibrary( "Psapi.dll" );
  439. if ( hInst )
  440. {
  441. typedef BOOL (WINAPI *GetProcessMemoryInfoFn)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
  442. GetProcessMemoryInfoFn fn = (GetProcessMemoryInfoFn)GetProcAddress( hInst, "GetProcessMemoryInfo" );
  443. if ( fn )
  444. {
  445. PROCESS_MEMORY_COUNTERS counters;
  446. ZeroMemory( &counters, sizeof( PROCESS_MEMORY_COUNTERS ) );
  447. counters.cb = sizeof( PROCESS_MEMORY_COUNTERS );
  448. if ( fn( GetCurrentProcess(), &counters, sizeof( PROCESS_MEMORY_COUNTERS ) ) )
  449. {
  450. CommentPrintf( "\nProcess Memory\nWorkingSetSize Mb(%.2f)\nQuotaPagedPoolUsage Mb(%.2f)\nQuotaNonPagedPoolUsage: Mb(%.2f)\nPagefileUsage: Mb(%.2f)\n",
  451. (double)counters.WorkingSetSize / MbDiv,
  452. (double)counters.QuotaPagedPoolUsage / MbDiv,
  453. (double)counters.QuotaNonPagedPoolUsage / MbDiv,
  454. (double)counters.PagefileUsage / MbDiv );
  455. }
  456. }
  457. FreeLibrary( hInst );
  458. }
  459. #elif defined( OSX )
  460. static const struct
  461. {
  462. int ctl;
  463. const char *name;
  464. } s_ctl_names[] =
  465. {
  466. #define _XTAG( _x ) { _x, #_x }
  467. _XTAG( HW_PHYSMEM ),
  468. _XTAG( HW_USERMEM ),
  469. _XTAG( HW_MEMSIZE ),
  470. _XTAG( HW_AVAILCPU ),
  471. #undef _XTAG
  472. };
  473. for ( size_t i = 0; i < Q_ARRAYSIZE( s_ctl_names ); i++ )
  474. {
  475. uint64_t val = 0;
  476. size_t len = sizeof( val );
  477. int mib[] = { CTL_HW, s_ctl_names[ i ].ctl };
  478. if ( sysctl( mib, Q_ARRAYSIZE( mib ), &val, &len, NULL, 0 ) == 0 )
  479. {
  480. CommentPrintf( " %s: %" PRIu64 "\n", s_ctl_names[ i ].name, val );
  481. }
  482. }
  483. #endif
  484. }
  485. void BuildCommentExtended()
  486. {
  487. try
  488. {
  489. CommentCat( "\nConVars (non-default)\n\n" );
  490. CommentPrintf( "%s %s %s\n", "var", "value", "default" );
  491. for ( const ConCommandBase *var = g_pCVar->GetCommands() ; var ; var = var->GetNext())
  492. {
  493. if ( var->IsCommand() )
  494. continue;
  495. ConVar *pCvar = ( ConVar * )var;
  496. if ( pCvar->IsFlagSet( FCVAR_SERVER_CANNOT_QUERY | FCVAR_PROTECTED ) )
  497. continue;
  498. if ( !(pCvar->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) )
  499. {
  500. char var1[ MAX_OSPATH ];
  501. char var2[ MAX_OSPATH ];
  502. Q_strncpy( var1, Host_CleanupConVarStringValue( pCvar->GetString() ), sizeof( var1 ) );
  503. Q_strncpy( var2, Host_CleanupConVarStringValue( pCvar->GetDefault() ), sizeof( var2 ) );
  504. if ( !Q_stricmp( var1, var2 ) )
  505. continue;
  506. }
  507. else
  508. {
  509. if ( pCvar->GetFloat() == Q_atof( pCvar->GetDefault() ) )
  510. continue;
  511. }
  512. if ( !(pCvar->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) )
  513. CommentPrintf( "%s '%s' '%s'\n", pCvar->GetName(), Host_CleanupConVarStringValue( pCvar->GetString() ), pCvar->GetDefault() );
  514. else
  515. CommentPrintf( "%s '%f' '%f'\n", pCvar->GetName(), pCvar->GetFloat(), Q_atof( pCvar->GetDefault() ) );
  516. }
  517. CommentCat( "\nConsole History (reversed)\n\n" );
  518. // Get console
  519. int len = V_strlen( m_errorText );
  520. if ( len < sizeof( m_errorText ) )
  521. {
  522. GetSpew( m_errorText + len, sizeof( m_errorText ) - len - 1 );
  523. m_errorText[ sizeof( m_errorText ) - 1 ] = 0;
  524. }
  525. }
  526. catch ( ... )
  527. {
  528. CommentCat( "Exception thrown building console/convar history.\n" );
  529. }
  530. }
  531. #if defined( LINUX )
  532. void AddFileToComment( const char *filename )
  533. {
  534. CommentPrintf( "\n%s:\n", filename );
  535. int nStart = Q_strlen( m_errorText );
  536. int nMaxLen = sizeof( m_errorText ) - nStart - 1;
  537. if ( nMaxLen > 0 )
  538. {
  539. FILE *fh = fopen( filename, "r" );
  540. if ( fh )
  541. {
  542. size_t ret = fread( m_errorText + nStart, 1, nMaxLen, fh );
  543. fclose( fh );
  544. // Replace tab characters with spaces.
  545. for ( size_t i = 0; i < ret; i++ )
  546. {
  547. if ( m_errorText[ nStart + i ] == '\t' )
  548. m_errorText[ nStart + i ] = ' ';
  549. }
  550. }
  551. // Entire buffer should have been zeroed out, but just super sure...
  552. m_errorText[ sizeof( m_errorText ) - 1 ] = 0;
  553. }
  554. }
  555. #endif // LINUX
  556. public:
  557. char m_errorText[ _SIZE ];
  558. bool m_bIsDedicatedServer;
  559. };
  560. #if defined( _X360 )
  561. static CErrorText<3500> errorText;
  562. #else
  563. static CErrorText<95000> errorText;
  564. #endif
  565. void BuildMinidumpComment( char const *pchSysErrorText, bool bRealCrash )
  566. {
  567. #if !defined(NO_STEAM)
  568. /*
  569. // Uncomment this code if you are testing max minidump comment length issues
  570. // It allows you to asked for a dummy comment of a certain length
  571. int nCommentLength = CommandLine()->ParmValue( "-commentlen", 0 );
  572. if ( nCommentLength > 0 )
  573. {
  574. nCommentLength = MIN( nCommentLength, 128*1024 );
  575. char *cbuf = new char[ nCommentLength + 1 ];
  576. for ( int i = 0; i < nCommentLength; ++i )
  577. {
  578. cbuf[ i ] = (char)('0' + (i % 10));
  579. }
  580. cbuf[ nCommentLength ] = 0;
  581. SteamAPI_SetMiniDumpComment( cbuf );
  582. delete[] cbuf;
  583. return;
  584. }
  585. */
  586. errorText.BuildComment( pchSysErrorText, bRealCrash );
  587. #endif
  588. }
  589. #if defined( POSIX )
  590. static void PosixPreMinidumpCallback( void *context )
  591. {
  592. BuildMinidumpComment( NULL, true );
  593. }
  594. #endif
  595. //-----------------------------------------------------------------------------
  596. // Purpose: Attempt to initialize appid/steam.inf/minidump information. May only return partial information if called
  597. // before Filesystem is ready.
  598. //
  599. // The desire is to be able to call this ASAP to init basic minidump and AppID info, then re-call later on when
  600. // the filesystem is setup, so full version # information and such can be propagated to the minidump system.
  601. // (Currently, SDK mods will generally only have partial information prior to filesystem init)
  602. //-----------------------------------------------------------------------------
  603. // steam.inf keys.
  604. #define VERSION_KEY "PatchVersion="
  605. #define PRODUCT_KEY "ProductName="
  606. #define SERVER_VERSION_KEY "ServerVersion="
  607. #define APPID_KEY "AppID="
  608. #define SERVER_APPID_KEY "ServerAppID="
  609. enum eSteamInfoInit
  610. {
  611. eSteamInfo_Uninitialized,
  612. eSteamInfo_Partial,
  613. eSteamInfo_Initialized
  614. };
  615. static eSteamInfoInit Sys_TryInitSteamInfo( void *pvAPI, SteamInfVersionInfo_t& VerInfo, const char *pchMod, const char *pchBaseDir, bool bDedicated )
  616. {
  617. static eSteamInfoInit initState = eSteamInfo_Uninitialized;
  618. eSteamInfoInit previousInitState = initState;
  619. //
  620. //
  621. // Initialize with some defaults.
  622. VerInfo.ClientVersion = 0;
  623. VerInfo.ServerVersion = 0;
  624. V_strcpy_safe( VerInfo.szVersionString, "valve" );
  625. V_strcpy_safe( VerInfo.szProductString, "1.0.1.0" );
  626. VerInfo.AppID = k_uAppIdInvalid;
  627. VerInfo.ServerAppID = k_uAppIdInvalid;
  628. // Filesystem may or may not be up
  629. CUtlBuffer infBuf;
  630. bool bFoundInf = false;
  631. if ( g_pFileSystem )
  632. {
  633. FileHandle_t fh;
  634. fh = g_pFileSystem->Open( "steam.inf", "rb", "GAME" );
  635. bFoundInf = fh && g_pFileSystem->ReadToBuffer( fh, infBuf );
  636. }
  637. if ( !bFoundInf )
  638. {
  639. // We may try to load the steam.inf BEFORE we turn on the filesystem, so use raw filesystem API's here.
  640. char szFullPath[ MAX_PATH ] = { 0 };
  641. char szModSteamInfPath[ MAX_PATH ] = { 0 };
  642. V_ComposeFileName( pchMod, "steam.inf", szModSteamInfPath, sizeof( szModSteamInfPath ) );
  643. V_MakeAbsolutePath( szFullPath, sizeof( szFullPath ), szModSteamInfPath, pchBaseDir );
  644. // Try opening steam.inf
  645. FILE *fp = fopen( szFullPath, "rb" );
  646. if ( fp )
  647. {
  648. // Read steam.inf data.
  649. fseek( fp, 0, SEEK_END );
  650. size_t bufsize = ftell( fp );
  651. fseek( fp, 0, SEEK_SET );
  652. infBuf.EnsureCapacity( bufsize + 1 );
  653. size_t iBytesRead = fread( infBuf.Base(), 1, bufsize, fp );
  654. ((char *)infBuf.Base())[iBytesRead] = 0;
  655. infBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, iBytesRead + 1 );
  656. fclose( fp );
  657. bFoundInf = ( iBytesRead == bufsize );
  658. }
  659. }
  660. if ( bFoundInf )
  661. {
  662. const char *pbuf = (const char*)infBuf.Base();
  663. while ( 1 )
  664. {
  665. pbuf = COM_Parse( pbuf );
  666. if ( !pbuf || !com_token[ 0 ] )
  667. break;
  668. if ( !Q_strnicmp( com_token, VERSION_KEY, Q_strlen( VERSION_KEY ) ) )
  669. {
  670. V_strcpy_safe( VerInfo.szVersionString, com_token + Q_strlen( VERSION_KEY ) );
  671. VerInfo.ClientVersion = atoi( VerInfo.szVersionString );
  672. }
  673. else if ( !Q_strnicmp( com_token, PRODUCT_KEY, Q_strlen( PRODUCT_KEY ) ) )
  674. {
  675. V_strcpy_safe( VerInfo.szProductString, com_token + Q_strlen( PRODUCT_KEY ) );
  676. }
  677. else if ( !Q_strnicmp( com_token, SERVER_VERSION_KEY, Q_strlen( SERVER_VERSION_KEY ) ) )
  678. {
  679. VerInfo.ServerVersion = atoi( com_token + Q_strlen( SERVER_VERSION_KEY ) );
  680. }
  681. else if ( !Q_strnicmp( com_token, APPID_KEY, Q_strlen( APPID_KEY ) ) )
  682. {
  683. VerInfo.AppID = atoi( com_token + Q_strlen( APPID_KEY ) );
  684. }
  685. else if ( !Q_strnicmp( com_token, SERVER_APPID_KEY, Q_strlen( SERVER_APPID_KEY ) ) )
  686. {
  687. VerInfo.ServerAppID = atoi( com_token + Q_strlen( SERVER_APPID_KEY ) );
  688. }
  689. }
  690. // If we found a steam.inf we're as good as we're going to get, but don't tell callers we're fully initialized
  691. // if it doesn't at least have an AppID
  692. initState = ( VerInfo.AppID != k_uAppIdInvalid ) ? eSteamInfo_Initialized : eSteamInfo_Partial;
  693. }
  694. else if ( !bDedicated )
  695. {
  696. // Opening steam.inf failed - try to open gameinfo.txt and read in just SteamAppId from that.
  697. // (gameinfo.txt lacks the dedicated server steamid, so we'll just have to live until filesystem init to setup
  698. // breakpad there when we hit this case)
  699. char szModGameinfoPath[ MAX_PATH ] = { 0 };
  700. char szFullPath[ MAX_PATH ] = { 0 };
  701. V_ComposeFileName( pchMod, "gameinfo.txt", szModGameinfoPath, sizeof( szModGameinfoPath ) );
  702. V_MakeAbsolutePath( szFullPath, sizeof( szFullPath ), szModGameinfoPath, pchBaseDir );
  703. // Try opening gameinfo.txt
  704. FILE *fp = fopen( szFullPath, "rb" );
  705. if( fp )
  706. {
  707. fseek( fp, 0, SEEK_END );
  708. size_t bufsize = ftell( fp );
  709. fseek( fp, 0, SEEK_SET );
  710. char *buffer = ( char * )_alloca( bufsize + 1 );
  711. size_t iBytesRead = fread( buffer, 1, bufsize, fp );
  712. buffer[ iBytesRead ] = 0;
  713. fclose( fp );
  714. KeyValuesAD pkvGameInfo( "gameinfo" );
  715. if ( pkvGameInfo->LoadFromBuffer( "gameinfo.txt", buffer ) )
  716. {
  717. VerInfo.AppID = (AppId_t)pkvGameInfo->GetInt( "FileSystem/SteamAppId", k_uAppIdInvalid );
  718. }
  719. }
  720. initState = eSteamInfo_Partial;
  721. }
  722. // In partial state the ServerAppID might be unknown, but if we found the full steam.inf and it's not set, it shares AppID.
  723. if ( initState == eSteamInfo_Initialized && VerInfo.ServerAppID == k_uAppIdInvalid )
  724. VerInfo.ServerAppID = VerInfo.AppID;
  725. #if !defined(_X360)
  726. if ( VerInfo.AppID )
  727. {
  728. // steamclient.dll doesn't know about steam.inf files in mod folder,
  729. // it accepts a steam_appid.txt in the root directory if the game is
  730. // not started through Steam. So we create one there containing the
  731. // current AppID
  732. FILE *fh = fopen( "steam_appid.txt", "wb" );
  733. if ( fh )
  734. {
  735. CFmtStrN< 128 > strAppID( "%u\n", VerInfo.AppID );
  736. fwrite( strAppID.Get(), strAppID.Length() + 1, 1, fh );
  737. fclose( fh );
  738. }
  739. }
  740. #endif // !_X360
  741. //
  742. // Update minidump info if we have more information than before
  743. //
  744. #ifndef NO_STEAM
  745. // If -nobreakpad was specified or we found metamod or sourcemod, don't register breakpad.
  746. bool bUseBreakpad = !CommandLine()->FindParm( "-nobreakpad" ) && ( !bDedicated || !IsSourceModLoaded() );
  747. AppId_t BreakpadAppId = bDedicated ? VerInfo.ServerAppID : VerInfo.AppID;
  748. Assert( BreakpadAppId != k_uAppIdInvalid || initState < eSteamInfo_Initialized );
  749. if ( BreakpadAppId != k_uAppIdInvalid && initState > previousInitState && bUseBreakpad )
  750. {
  751. void *pvMiniDumpContext = NULL;
  752. PFNPreMinidumpCallback pfnPreMinidumpCallback = NULL;
  753. bool bFullMemoryDump = !bDedicated && IsWindows() && CommandLine()->FindParm( "-full_memory_dumps" );
  754. #if defined( POSIX )
  755. // On Windows we're relying on the try/except to build the minidump comment. On Linux, we don't have that
  756. // so we need to register the minidumpcallback handler here.
  757. pvMiniDumpContext = pvAPI;
  758. pfnPreMinidumpCallback = PosixPreMinidumpCallback;
  759. #endif
  760. CFmtStrN<128> pchVersion( "%d", build_number() );
  761. Msg( "Using Breakpad minidump system. Version: %s AppID: %u\n", pchVersion.Get(), BreakpadAppId );
  762. // We can filter various crash dumps differently in the Socorro backend code:
  763. // Steam/min/web/crash_reporter/socorro/scripts/config/collectorconfig.py
  764. SteamAPI_SetBreakpadAppID( BreakpadAppId );
  765. SteamAPI_UseBreakpadCrashHandler( pchVersion, __DATE__, __TIME__, bFullMemoryDump, pvMiniDumpContext, pfnPreMinidumpCallback );
  766. // Tell errorText class if this is dedicated server.
  767. errorText.m_bIsDedicatedServer = bDedicated;
  768. }
  769. #endif // NO_STEAM
  770. MinidumpUserStreamInfoSetHeader( "%sLaunching \"%s\"\n", ( bDedicated ? "DedicatedServerAPI " : "" ), CommandLine()->GetCmdLine() );
  771. return initState;
  772. }
  773. #ifndef SWDS
  774. //-----------------------------------------------------------------------------
  775. //
  776. // Main engine interface exposed to launcher
  777. //
  778. //-----------------------------------------------------------------------------
  779. class CEngineAPI : public CTier3AppSystem< IEngineAPI >
  780. {
  781. typedef CTier3AppSystem< IEngineAPI > BaseClass;
  782. public:
  783. virtual bool Connect( CreateInterfaceFn factory );
  784. virtual void Disconnect();
  785. virtual void *QueryInterface( const char *pInterfaceName );
  786. virtual InitReturnVal_t Init();
  787. virtual void Shutdown();
  788. // This function must be called before init
  789. virtual void SetStartupInfo( StartupInfo_t &info );
  790. virtual int Run( );
  791. // Sets the engine to run in a particular editor window
  792. virtual void SetEngineWindow( void *hWnd );
  793. // Posts a console command
  794. virtual void PostConsoleCommand( const char *pConsoleCommand );
  795. // Are we running the simulation?
  796. virtual bool IsRunningSimulation( ) const;
  797. // Start/stop running the simulation
  798. virtual void ActivateSimulation( bool bActive );
  799. // Reset the map we're on
  800. virtual void SetMap( const char *pMapName );
  801. bool MainLoop();
  802. int RunListenServer();
  803. private:
  804. // Hooks a particular mod up to the registry
  805. void SetRegistryMod( const char *pModName );
  806. // One-time setup, based on the initially selected mod
  807. // FIXME: This should move into the launcher!
  808. bool OnStartup( void *pInstance, const char *pStartupModName );
  809. void OnShutdown();
  810. // Initialization, shutdown of a mod.
  811. bool ModInit( const char *pModName, const char *pGameDir );
  812. void ModShutdown();
  813. // Initializes, shuts down the registry
  814. bool InitRegistry( const char *pModName );
  815. void ShutdownRegistry();
  816. // Handles there being an error setting up the video mode
  817. InitReturnVal_t HandleSetModeError();
  818. // Initializes, shuts down VR
  819. bool InitVR();
  820. void ShutdownVR();
  821. // Purpose: Message pump when running stand-alone
  822. void PumpMessages();
  823. // Purpose: Message pump when running with the editor
  824. void PumpMessagesEditMode( bool &bIdle, long &lIdleCount );
  825. // Activate/deactivates edit mode shaders
  826. void ActivateEditModeShaders( bool bActive );
  827. private:
  828. void *m_hEditorHWnd;
  829. bool m_bRunningSimulation;
  830. bool m_bSupportsVR;
  831. StartupInfo_t m_StartupInfo;
  832. };
  833. //-----------------------------------------------------------------------------
  834. // Singleton interface
  835. //-----------------------------------------------------------------------------
  836. static CEngineAPI s_EngineAPI;
  837. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineAPI, IEngineAPI, VENGINE_LAUNCHER_API_VERSION, s_EngineAPI );
  838. //-----------------------------------------------------------------------------
  839. // Connect, disconnect
  840. //-----------------------------------------------------------------------------
  841. bool CEngineAPI::Connect( CreateInterfaceFn factory )
  842. {
  843. // Store off the app system factory...
  844. g_AppSystemFactory = factory;
  845. if ( !BaseClass::Connect( factory ) )
  846. return false;
  847. g_pFileSystem = g_pFullFileSystem;
  848. if ( !g_pFileSystem )
  849. return false;
  850. g_pFileSystem->SetWarningFunc( Warning );
  851. if ( !Shader_Connect( true ) )
  852. return false;
  853. g_pPhysics = (IPhysics*)factory( VPHYSICS_INTERFACE_VERSION, NULL );
  854. if ( !g_pStudioRender || !g_pDataCache || !g_pPhysics || !g_pMDLCache || !g_pMatSystemSurface || !g_pInputSystem /* || !g_pVideo */ )
  855. {
  856. Warning( "Engine wasn't able to acquire required interfaces!\n" );
  857. return false;
  858. }
  859. if (!g_pStudioRender)
  860. {
  861. Sys_Error( "Unable to init studio render system version %s\n", STUDIO_RENDER_INTERFACE_VERSION );
  862. return false;
  863. }
  864. g_pHammer = (IHammer*)factory( INTERFACEVERSION_HAMMER, NULL );
  865. #if defined( USE_SDL )
  866. g_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL );
  867. #endif
  868. ConnectMDLCacheNotify();
  869. return true;
  870. }
  871. void CEngineAPI::Disconnect()
  872. {
  873. DisconnectMDLCacheNotify();
  874. #if !defined( SWDS )
  875. TRACESHUTDOWN( Steam3Client().Shutdown() );
  876. #endif
  877. g_pHammer = NULL;
  878. g_pPhysics = NULL;
  879. Shader_Disconnect();
  880. g_pFileSystem = NULL;
  881. BaseClass::Disconnect();
  882. g_AppSystemFactory = NULL;
  883. }
  884. //-----------------------------------------------------------------------------
  885. // Query interface
  886. //-----------------------------------------------------------------------------
  887. void *CEngineAPI::QueryInterface( const char *pInterfaceName )
  888. {
  889. // Loading the engine DLL mounts *all* engine interfaces
  890. CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary
  891. return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing.
  892. }
  893. //-----------------------------------------------------------------------------
  894. // Sets startup info
  895. //-----------------------------------------------------------------------------
  896. void CEngineAPI::SetStartupInfo( StartupInfo_t &info )
  897. {
  898. // Setup and write out steam_appid.txt before we launch
  899. bool bDedicated = false; // Dedicated comes through CDedicatedServerAPI
  900. eSteamInfoInit steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );
  901. g_bTextMode = info.m_bTextMode;
  902. // Set up the engineparms_t which contains global information about the mod
  903. host_parms.basedir = const_cast<char*>( info.m_pBaseDirectory );
  904. // Copy off all the startup info
  905. m_StartupInfo = info;
  906. #if !defined( SWDS )
  907. // turn on the Steam3 API early so we can query app data up front
  908. TRACEINIT( Steam3Client().Activate(), Steam3Client().Shutdown() );
  909. #endif
  910. // Needs to be done prior to init material system config
  911. TRACEINIT( COM_InitFilesystem( m_StartupInfo.m_pInitialMod ), COM_ShutdownFileSystem() );
  912. if ( steamInfo != eSteamInfo_Initialized )
  913. {
  914. // Try again with filesystem available. This is commonly needed for SDK mods which need the filesystem to find
  915. // their steam.inf, due to mounting SDK search paths.
  916. steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );
  917. Assert( steamInfo == eSteamInfo_Initialized );
  918. if ( steamInfo != eSteamInfo_Initialized )
  919. {
  920. Warning( "Failed to find steam.inf or equivalent steam info. May not have proper information to connect to Steam.\n" );
  921. }
  922. }
  923. m_bSupportsVR = false;
  924. if ( IsPC() )
  925. {
  926. KeyValues *modinfo = new KeyValues("ModInfo");
  927. if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) )
  928. {
  929. // Enable file tracking - client always does this in case it connects to a pure server.
  930. // server only does this if sv_pure is set
  931. // If it's not singleplayer_only
  932. if ( V_stricmp( modinfo->GetString("type", "singleplayer_only"), "singleplayer_only") == 0 )
  933. {
  934. DevMsg( "Disabling whitelist file tracking in filesystem...\n" );
  935. g_pFileSystem->EnableWhitelistFileTracking( false, false, false );
  936. }
  937. else
  938. {
  939. DevMsg( "Enabling whitelist file tracking in filesystem...\n" );
  940. g_pFileSystem->EnableWhitelistFileTracking( true, false, false );
  941. }
  942. m_bSupportsVR = modinfo->GetInt( "supportsvr" ) > 0 && CommandLine()->CheckParm( "-vr" );
  943. if ( m_bSupportsVR )
  944. {
  945. // This also has to happen before CreateGameWindow to know where to put
  946. // the window and how big to make it
  947. if ( InitVR() )
  948. {
  949. if ( Steam3Client().SteamUtils() )
  950. {
  951. if ( Steam3Client().SteamUtils()->IsSteamRunningInVR() && g_pSourceVR->IsHmdConnected() )
  952. {
  953. int nForceVRAdapterIndex = g_pSourceVR->GetVRModeAdapter();
  954. materials->SetAdapter( nForceVRAdapterIndex, 0 );
  955. g_pSourceVR->SetShouldForceVRMode();
  956. }
  957. }
  958. }
  959. }
  960. }
  961. modinfo->deleteThis();
  962. }
  963. }
  964. //-----------------------------------------------------------------------------
  965. // Init, shutdown
  966. //-----------------------------------------------------------------------------
  967. InitReturnVal_t CEngineAPI::Init()
  968. {
  969. if ( CommandLine()->FindParm( "-sv_benchmark" ) != 0 )
  970. {
  971. Plat_SetBenchmarkMode( true );
  972. }
  973. InitReturnVal_t nRetVal = BaseClass::Init();
  974. if ( nRetVal != INIT_OK )
  975. return nRetVal;
  976. m_bRunningSimulation = false;
  977. // Initialize the FPU control word
  978. #if defined(WIN32) && !defined( SWDS ) && !defined( _X360 )
  979. _asm
  980. {
  981. fninit
  982. }
  983. #endif
  984. SetupFPUControlWord();
  985. // This creates the videomode singleton object, it doesn't depend on the registry
  986. VideoMode_Create();
  987. // Initialize the editor hwnd to render into
  988. m_hEditorHWnd = NULL;
  989. // One-time setup
  990. // FIXME: OnStartup + OnShutdown should be removed + moved into the launcher
  991. // or the launcher code should be merged into the engine into the code in OnStartup/OnShutdown
  992. if ( !OnStartup( m_StartupInfo.m_pInstance, m_StartupInfo.m_pInitialMod ) )
  993. {
  994. return HandleSetModeError();
  995. }
  996. return INIT_OK;
  997. }
  998. void CEngineAPI::Shutdown()
  999. {
  1000. VideoMode_Destroy();
  1001. BaseClass::Shutdown();
  1002. }
  1003. //-----------------------------------------------------------------------------
  1004. // Sets the engine to run in a particular editor window
  1005. //-----------------------------------------------------------------------------
  1006. void CEngineAPI::SetEngineWindow( void *hWnd )
  1007. {
  1008. if ( !InEditMode() )
  1009. return;
  1010. // Detach input from the previous editor window
  1011. game->InputDetachFromGameWindow();
  1012. m_hEditorHWnd = hWnd;
  1013. videomode->SetGameWindow( m_hEditorHWnd );
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Posts a console command
  1017. //-----------------------------------------------------------------------------
  1018. void CEngineAPI::PostConsoleCommand( const char *pCommand )
  1019. {
  1020. Cbuf_AddText( pCommand );
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Is the engine currently rinning?
  1024. //-----------------------------------------------------------------------------
  1025. bool CEngineAPI::IsRunningSimulation() const
  1026. {
  1027. return (eng->GetState() == IEngine::DLL_ACTIVE);
  1028. }
  1029. //-----------------------------------------------------------------------------
  1030. // Reset the map we're on
  1031. //-----------------------------------------------------------------------------
  1032. void CEngineAPI::SetMap( const char *pMapName )
  1033. {
  1034. // if ( !Q_stricmp( sv.mapname, pMapName ) )
  1035. // return;
  1036. char buf[MAX_PATH];
  1037. Q_snprintf( buf, MAX_PATH, "map %s", pMapName );
  1038. Cbuf_AddText( buf );
  1039. }
  1040. //-----------------------------------------------------------------------------
  1041. // Start/stop running the simulation
  1042. //-----------------------------------------------------------------------------
  1043. void CEngineAPI::ActivateSimulation( bool bActive )
  1044. {
  1045. // FIXME: Not sure what will happen in this case
  1046. if ( ( eng->GetState() != IEngine::DLL_ACTIVE ) &&
  1047. ( eng->GetState() != IEngine::DLL_PAUSED ) )
  1048. {
  1049. return;
  1050. }
  1051. bool bCurrentlyActive = (eng->GetState() != IEngine::DLL_PAUSED);
  1052. if ( bActive == bCurrentlyActive )
  1053. return;
  1054. // FIXME: Should attachment/detachment be part of the state machine in IEngine?
  1055. if ( !bActive )
  1056. {
  1057. eng->SetNextState( IEngine::DLL_PAUSED );
  1058. // Detach input from the previous editor window
  1059. game->InputDetachFromGameWindow();
  1060. }
  1061. else
  1062. {
  1063. eng->SetNextState( IEngine::DLL_ACTIVE );
  1064. // Start accepting input from the new window
  1065. // FIXME: What if the attachment fails?
  1066. game->InputAttachToGameWindow();
  1067. }
  1068. }
  1069. static void MoveConsoleWindowToFront()
  1070. {
  1071. #ifdef _WIN32
  1072. // Move the window to the front.
  1073. HINSTANCE hInst = LoadLibrary( "kernel32.dll" );
  1074. if ( hInst )
  1075. {
  1076. typedef HWND (*GetConsoleWindowFn)();
  1077. GetConsoleWindowFn fn = (GetConsoleWindowFn)GetProcAddress( hInst, "GetConsoleWindow" );
  1078. if ( fn )
  1079. {
  1080. HWND hwnd = fn();
  1081. ShowWindow( hwnd, SW_SHOW );
  1082. UpdateWindow( hwnd );
  1083. SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW );
  1084. }
  1085. FreeLibrary( hInst );
  1086. }
  1087. #endif
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose: Message pump when running stand-alone
  1091. //-----------------------------------------------------------------------------
  1092. void CEngineAPI::PumpMessages()
  1093. {
  1094. // This message pumping happens in SDL if SDL is enabled.
  1095. #if defined( PLATFORM_WINDOWS ) && !defined( USE_SDL )
  1096. MSG msg;
  1097. while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
  1098. {
  1099. TranslateMessage( &msg );
  1100. DispatchMessage( &msg );
  1101. }
  1102. #endif
  1103. #if defined( USE_SDL )
  1104. g_pLauncherMgr->PumpWindowsMessageLoop();
  1105. #endif
  1106. // Get input from attached devices
  1107. g_pInputSystem->PollInputState();
  1108. if ( IsX360() )
  1109. {
  1110. // handle Xbox system messages
  1111. XBX_ProcessEvents();
  1112. }
  1113. // NOTE: Under some implementations of Win9x,
  1114. // dispatching messages can cause the FPU control word to change
  1115. if ( IsPC() )
  1116. {
  1117. SetupFPUControlWord();
  1118. }
  1119. game->DispatchAllStoredGameMessages();
  1120. if ( IsPC() )
  1121. {
  1122. static bool s_bFirstRun = true;
  1123. if ( s_bFirstRun )
  1124. {
  1125. s_bFirstRun = false;
  1126. MoveConsoleWindowToFront();
  1127. }
  1128. }
  1129. }
  1130. //-----------------------------------------------------------------------------
  1131. // Purpose: Message pump when running stand-alone
  1132. //-----------------------------------------------------------------------------
  1133. void CEngineAPI::PumpMessagesEditMode( bool &bIdle, long &lIdleCount )
  1134. {
  1135. if ( bIdle && !g_pHammer->HammerOnIdle( lIdleCount++ ) )
  1136. {
  1137. bIdle = false;
  1138. }
  1139. // Get input from attached devices
  1140. g_pInputSystem->PollInputState();
  1141. #ifdef WIN32
  1142. MSG msg;
  1143. while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
  1144. {
  1145. if ( msg.message == WM_QUIT )
  1146. {
  1147. eng->SetQuitting( IEngine::QUIT_TODESKTOP );
  1148. break;
  1149. }
  1150. if ( !g_pHammer->HammerPreTranslateMessage(&msg) )
  1151. {
  1152. TranslateMessage(&msg);
  1153. DispatchMessage(&msg);
  1154. }
  1155. // Reset idle state after pumping idle message.
  1156. if ( g_pHammer->HammerIsIdleMessage(&msg) )
  1157. {
  1158. bIdle = true;
  1159. lIdleCount = 0;
  1160. }
  1161. }
  1162. #elif defined( USE_SDL )
  1163. Error( "Not supported" );
  1164. #else
  1165. #error
  1166. #endif
  1167. // NOTE: Under some implementations of Win9x,
  1168. // dispatching messages can cause the FPU control word to change
  1169. SetupFPUControlWord();
  1170. game->DispatchAllStoredGameMessages();
  1171. }
  1172. //-----------------------------------------------------------------------------
  1173. // Activate/deactivates edit mode shaders
  1174. //-----------------------------------------------------------------------------
  1175. void CEngineAPI::ActivateEditModeShaders( bool bActive )
  1176. {
  1177. if ( InEditMode() && ( g_pMaterialSystemConfig->bEditMode != bActive ) )
  1178. {
  1179. MaterialSystem_Config_t config = *g_pMaterialSystemConfig;
  1180. config.bEditMode = bActive;
  1181. OverrideMaterialSystemConfig( config );
  1182. }
  1183. }
  1184. #ifdef GPROFILER
  1185. static bool g_gprofiling = false;
  1186. CON_COMMAND( gprofilerstart, "Starts the gperftools profiler recording to the specified file." )
  1187. {
  1188. if ( g_gprofiling )
  1189. {
  1190. Msg( "Profiling is already started.\n" );
  1191. return;
  1192. }
  1193. char buffer[500];
  1194. const char* profname = buffer;
  1195. if ( args.ArgC() < 2 )
  1196. {
  1197. static const char *s_pszHomeDir = getenv("HOME");
  1198. if ( !s_pszHomeDir )
  1199. {
  1200. Msg( "Syntax: gprofile <outputfilename>\n" );
  1201. return;
  1202. }
  1203. // Use the current date and time to create a unique file name.time_t t = time(NULL);
  1204. time_t t = time(NULL);
  1205. struct tm tm = *localtime(&t);
  1206. V_sprintf_safe( buffer, "%s/valveprofile_%4d_%02d_%02d_%02d.%02d.%02d.prof", s_pszHomeDir,
  1207. tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec );
  1208. // profname already points to buffer.
  1209. }
  1210. else
  1211. {
  1212. profname = args[1];
  1213. }
  1214. int result = ProfilerStart( profname );
  1215. if ( result )
  1216. {
  1217. Msg( "Profiling started successfully. Recording to %s. Stop profiling with gprofilerstop.\n", profname );
  1218. g_gprofiling = true;
  1219. }
  1220. else
  1221. {
  1222. Msg( "Profiling to %s failed to start - errno = %d.\n", profname, errno );
  1223. }
  1224. }
  1225. CON_COMMAND( gprofilerstop, "Stops the gperftools profiler." )
  1226. {
  1227. if ( g_gprofiling )
  1228. {
  1229. ProfilerStop();
  1230. Msg( "Stopped profiling.\n" );
  1231. g_gprofiling = false;
  1232. }
  1233. }
  1234. #endif
  1235. void StopGProfiler()
  1236. {
  1237. #ifdef GPROFILER
  1238. gprofilerstop( CCommand() );
  1239. #endif
  1240. }
  1241. //-----------------------------------------------------------------------------
  1242. // Purpose: Message pump
  1243. //-----------------------------------------------------------------------------
  1244. bool CEngineAPI::MainLoop()
  1245. {
  1246. bool bIdle = true;
  1247. long lIdleCount = 0;
  1248. // Main message pump
  1249. while ( true )
  1250. {
  1251. // Pump messages unless someone wants to quit
  1252. if ( eng->GetQuitting() != IEngine::QUIT_NOTQUITTING )
  1253. {
  1254. // We have to explicitly stop the profiler since otherwise symbol
  1255. // resolution doesn't work correctly.
  1256. StopGProfiler();
  1257. if ( eng->GetQuitting() != IEngine::QUIT_TODESKTOP )
  1258. return true;
  1259. return false;
  1260. }
  1261. // Pump the message loop
  1262. if ( !InEditMode() )
  1263. {
  1264. PumpMessages();
  1265. }
  1266. else
  1267. {
  1268. PumpMessagesEditMode( bIdle, lIdleCount );
  1269. }
  1270. // Run engine frame + hammer frame
  1271. if ( !InEditMode() || m_hEditorHWnd )
  1272. {
  1273. VCRSyncToken( "Frame" );
  1274. // Deactivate edit mode shaders
  1275. ActivateEditModeShaders( false );
  1276. eng->Frame();
  1277. // Reactivate edit mode shaders (in Edit mode only...)
  1278. ActivateEditModeShaders( true );
  1279. }
  1280. if ( InEditMode() )
  1281. {
  1282. g_pHammer->RunFrame();
  1283. }
  1284. }
  1285. return false;
  1286. }
  1287. //-----------------------------------------------------------------------------
  1288. // Initializes, shuts down the registry
  1289. //-----------------------------------------------------------------------------
  1290. bool CEngineAPI::InitRegistry( const char *pModName )
  1291. {
  1292. if ( IsPC() )
  1293. {
  1294. char szRegSubPath[MAX_PATH];
  1295. Q_snprintf( szRegSubPath, sizeof(szRegSubPath), "%s\\%s", "Source", pModName );
  1296. return registry->Init( szRegSubPath );
  1297. }
  1298. return true;
  1299. }
  1300. void CEngineAPI::ShutdownRegistry( )
  1301. {
  1302. if ( IsPC() )
  1303. {
  1304. registry->Shutdown( );
  1305. }
  1306. }
  1307. //-----------------------------------------------------------------------------
  1308. // Initializes, shuts down VR (via sourcevr.dll)
  1309. //-----------------------------------------------------------------------------
  1310. bool CEngineAPI::InitVR()
  1311. {
  1312. if ( m_bSupportsVR )
  1313. {
  1314. g_pSourceVR = (ISourceVirtualReality *)g_AppSystemFactory( SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, NULL );
  1315. if ( g_pSourceVR )
  1316. {
  1317. // make sure that the sourcevr DLL we loaded is secure. If not, don't
  1318. // let this client connect to secure servers.
  1319. if ( !Host_AllowLoadModule( "sourcevr" DLL_EXT_STRING, "EXECUTABLE_PATH", false ) )
  1320. {
  1321. Warning( "Preventing connections to secure servers because sourcevr.dll is not signed.\n" );
  1322. Host_DisallowSecureServers();
  1323. }
  1324. }
  1325. }
  1326. return true;
  1327. }
  1328. void CEngineAPI::ShutdownVR()
  1329. {
  1330. }
  1331. //-----------------------------------------------------------------------------
  1332. // One-time setup, based on the initially selected mod
  1333. // FIXME: This should move into the launcher!
  1334. //-----------------------------------------------------------------------------
  1335. bool CEngineAPI::OnStartup( void *pInstance, const char *pStartupModName )
  1336. {
  1337. // This fixes a bug on certain machines where the input will
  1338. // stop coming in for about 1 second when someone hits a key.
  1339. // (true means to disable priority boost)
  1340. #ifdef WIN32
  1341. if ( IsPC() )
  1342. {
  1343. SetThreadPriorityBoost( GetCurrentThread(), true );
  1344. }
  1345. #endif
  1346. // FIXME: Turn videomode + game into IAppSystems?
  1347. // Try to create the window
  1348. COM_TimestampedLog( "game->Init" );
  1349. // This has to happen before CreateGameWindow to set up the instance
  1350. // for use by the code that creates the window
  1351. if ( !game->Init( pInstance ) )
  1352. {
  1353. goto onStartupError;
  1354. }
  1355. // Try to create the window
  1356. COM_TimestampedLog( "videomode->Init" );
  1357. // This needs to be after Shader_Init and registry->Init
  1358. // This way mods can have different default video settings
  1359. if ( !videomode->Init( ) )
  1360. {
  1361. goto onStartupShutdownGame;
  1362. }
  1363. // We need to access the registry to get various settings (specifically,
  1364. // InitMaterialSystemConfig requires it).
  1365. if ( !InitRegistry( pStartupModName ) )
  1366. {
  1367. goto onStartupShutdownVideoMode;
  1368. }
  1369. materials->ModInit();
  1370. // Setup the material system config record, CreateGameWindow depends on it
  1371. // (when we're running stand-alone)
  1372. InitMaterialSystemConfig( InEditMode() );
  1373. #if defined( _X360 )
  1374. XBX_NotifyCreateListener( XNOTIFY_SYSTEM|XNOTIFY_LIVE|XNOTIFY_XMP );
  1375. #endif
  1376. ShutdownRegistry();
  1377. return true;
  1378. // Various error conditions
  1379. onStartupShutdownVideoMode:
  1380. videomode->Shutdown();
  1381. onStartupShutdownGame:
  1382. game->Shutdown();
  1383. onStartupError:
  1384. return false;
  1385. }
  1386. //-----------------------------------------------------------------------------
  1387. // One-time shutdown (shuts down stuff set up in OnStartup)
  1388. // FIXME: This should move into the launcher!
  1389. //-----------------------------------------------------------------------------
  1390. void CEngineAPI::OnShutdown()
  1391. {
  1392. if ( videomode )
  1393. {
  1394. videomode->Shutdown();
  1395. }
  1396. ShutdownVR();
  1397. // Shut down the game
  1398. game->Shutdown();
  1399. materials->ModShutdown();
  1400. TRACESHUTDOWN( COM_ShutdownFileSystem() );
  1401. }
  1402. static bool IsValveMod( const char *pModName )
  1403. {
  1404. // Figure out if we're running a Valve mod or not.
  1405. return ( Q_stricmp( GetCurrentMod(), "cstrike" ) == 0 ||
  1406. Q_stricmp( GetCurrentMod(), "dod" ) == 0 ||
  1407. Q_stricmp( GetCurrentMod(), "hl1mp" ) == 0 ||
  1408. Q_stricmp( GetCurrentMod(), "tf" ) == 0 ||
  1409. Q_stricmp( GetCurrentMod(), "tf_beta" ) == 0 ||
  1410. Q_stricmp( GetCurrentMod(), "hl2mp" ) == 0 );
  1411. }
  1412. //-----------------------------------------------------------------------------
  1413. // Initialization, shutdown of a mod.
  1414. //-----------------------------------------------------------------------------
  1415. bool CEngineAPI::ModInit( const char *pModName, const char *pGameDir )
  1416. {
  1417. // Set up the engineparms_t which contains global information about the mod
  1418. host_parms.mod = COM_StringCopy( GetModDirFromPath( pModName ) );
  1419. host_parms.game = COM_StringCopy( pGameDir );
  1420. // By default, restrict server commands in Valve games and don't restrict them in mods.
  1421. cl.m_bRestrictServerCommands = IsValveMod( host_parms.mod );
  1422. cl.m_bRestrictClientCommands = cl.m_bRestrictServerCommands;
  1423. // build the registry path we're going to use for this mod
  1424. InitRegistry( pModName );
  1425. // This sets up the game search path, depends on host_parms
  1426. TRACEINIT( MapReslistGenerator_Init(), MapReslistGenerator_Shutdown() );
  1427. #if !defined( _X360 )
  1428. TRACEINIT( DevShotGenerator_Init(), DevShotGenerator_Shutdown() );
  1429. #endif
  1430. // Slam cvars based on mod/config.cfg
  1431. Host_ReadPreStartupConfiguration();
  1432. bool bWindowed = g_pMaterialSystemConfig->Windowed();
  1433. if( g_pMaterialSystemConfig->m_nVRModeAdapter != -1 )
  1434. {
  1435. // at init time we never want to start up full screen
  1436. bWindowed = true;
  1437. }
  1438. // Create the game window now that we have a search path
  1439. // FIXME: Deal with initial window width + height better
  1440. if ( !videomode || !videomode->CreateGameWindow( g_pMaterialSystemConfig->m_VideoMode.m_Width, g_pMaterialSystemConfig->m_VideoMode.m_Height, bWindowed ) )
  1441. {
  1442. return false;
  1443. }
  1444. return true;
  1445. }
  1446. void CEngineAPI::ModShutdown()
  1447. {
  1448. COM_StringFree(host_parms.mod);
  1449. COM_StringFree(host_parms.game);
  1450. // Stop accepting input from the window
  1451. game->InputDetachFromGameWindow();
  1452. #if !defined( _X360 )
  1453. TRACESHUTDOWN( DevShotGenerator_Shutdown() );
  1454. #endif
  1455. TRACESHUTDOWN( MapReslistGenerator_Shutdown() );
  1456. ShutdownRegistry();
  1457. }
  1458. //-----------------------------------------------------------------------------
  1459. // Purpose: Handles there being an error setting up the video mode
  1460. // Output : Returns true on if the engine should restart, false if it should quit
  1461. //-----------------------------------------------------------------------------
  1462. InitReturnVal_t CEngineAPI::HandleSetModeError()
  1463. {
  1464. // show an error, see if the user wants to restart
  1465. if ( CommandLine()->FindParm( "-safe" ) )
  1466. {
  1467. Sys_MessageBox( "Failed to set video mode.\n\nThis game has a minimum requirement of DirectX 7.0 compatible hardware.\n", "Video mode error", false );
  1468. return INIT_FAILED;
  1469. }
  1470. if ( CommandLine()->FindParm( "-autoconfig" ) )
  1471. {
  1472. if ( Sys_MessageBox( "Failed to set video mode - falling back to safe mode settings.\n\nGame will now restart with the new video settings.", "Video - safe mode fallback", true ))
  1473. {
  1474. CommandLine()->AppendParm( "-safe", NULL );
  1475. return (InitReturnVal_t)INIT_RESTART;
  1476. }
  1477. return INIT_FAILED;
  1478. }
  1479. if ( Sys_MessageBox( "Failed to set video mode - resetting to defaults.\n\nGame will now restart with the new video settings.", "Video mode warning", true ) )
  1480. {
  1481. CommandLine()->AppendParm( "-autoconfig", NULL );
  1482. return (InitReturnVal_t)INIT_RESTART;
  1483. }
  1484. return INIT_FAILED;
  1485. }
  1486. //-----------------------------------------------------------------------------
  1487. // Purpose: Main loop for non-dedicated servers
  1488. //-----------------------------------------------------------------------------
  1489. int CEngineAPI::RunListenServer()
  1490. {
  1491. //
  1492. // NOTE: Systems set up here should depend on the mod
  1493. // Systems which are mod-independent should be set up in the launcher or Init()
  1494. //
  1495. // Innocent until proven guilty
  1496. int nRunResult = RUN_OK;
  1497. // Happens every time we start up and shut down a mod
  1498. if ( ModInit( m_StartupInfo.m_pInitialMod, m_StartupInfo.m_pInitialGame ) )
  1499. {
  1500. CModAppSystemGroup modAppSystemGroup( false, m_StartupInfo.m_pParentAppSystemGroup );
  1501. // Store off the app system factory...
  1502. g_AppSystemFactory = modAppSystemGroup.GetFactory();
  1503. nRunResult = modAppSystemGroup.Run();
  1504. g_AppSystemFactory = NULL;
  1505. // Shuts down the mod
  1506. ModShutdown();
  1507. // Disconnects from the editor window
  1508. videomode->SetGameWindow( NULL );
  1509. }
  1510. // Closes down things that were set up in OnStartup
  1511. // FIXME: OnStartup + OnShutdown should be removed + moved into the launcher
  1512. // or the launcher code should be merged into the engine into the code in OnStartup/OnShutdown
  1513. OnShutdown();
  1514. return nRunResult;
  1515. }
  1516. static void StaticRunListenServer( void *arg )
  1517. {
  1518. *(int *)arg = s_EngineAPI.RunListenServer();
  1519. }
  1520. // This function is set as the crash handler for unhandled exceptions and as the minidump
  1521. // handler for to be used by all of tier0's crash recording. This function
  1522. // adds a game-specific minidump comment and ensures that the SteamAPI function is
  1523. // used to save the minidump so that crashes are uploaded. SteamAPI has previously
  1524. // been configured to use breakpad by calling SteamAPI_UseBreakpadCrashHandler.
  1525. extern "C" void __cdecl WriteSteamMiniDumpWithComment( unsigned int uStructuredExceptionCode,
  1526. struct _EXCEPTION_POINTERS * pExceptionInfo,
  1527. const char *pszFilenameSuffix )
  1528. {
  1529. // TODO: dynamically set the minidump comment from contextual info about the crash (i.e current VPROF node)?
  1530. #if !defined( NO_STEAM )
  1531. if ( g_bUpdateMinidumpComment )
  1532. {
  1533. BuildMinidumpComment( NULL, true );
  1534. }
  1535. SteamAPI_WriteMiniDump( uStructuredExceptionCode, pExceptionInfo, build_number() );
  1536. // Clear DSound Buffers so the sound doesn't loop while the game shuts down
  1537. try
  1538. {
  1539. S_ClearBuffer();
  1540. }
  1541. catch ( ... )
  1542. {
  1543. }
  1544. #endif
  1545. }
  1546. //-----------------------------------------------------------------------------
  1547. // Purpose: Main
  1548. //-----------------------------------------------------------------------------
  1549. int CEngineAPI::Run()
  1550. {
  1551. if ( CommandLine()->FindParm( "-insecure" ) || CommandLine()->FindParm( "-textmode" ) )
  1552. {
  1553. Host_DisallowSecureServers();
  1554. }
  1555. #ifdef _X360
  1556. return RunListenServer(); // don't handle exceptions on 360 (because if we do then minidumps won't work at all)
  1557. #elif defined ( _WIN32 )
  1558. // Ensure that we crash when we do something naughty in a callback
  1559. // such as a window proc. Otherwise on a 64-bit OS the crashes will be
  1560. // silently swallowed.
  1561. EnableCrashingOnCrashes();
  1562. // Set the default minidump handling function. This is necessary so that Steam
  1563. // will upload crashes, with comments.
  1564. SetMiniDumpFunction( WriteSteamMiniDumpWithComment );
  1565. // Catch unhandled crashes. A normal __try/__except block will not work across
  1566. // the kernel callback boundary, but this does. To be clear, __try/__except
  1567. // and try/catch will usually not catch exceptions in a WindowProc or other
  1568. // callback that is called from kernel mode because 64-bit Windows cannot handle
  1569. // throwing exceptions across that boundary. See this article for details:
  1570. // http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
  1571. // Note that the unhandled exception function is not called when running
  1572. // under a debugger, but that's fine because in that case we don't care about
  1573. // recording minidumps.
  1574. // The try/catch block still makes sense because it is a more reliable way
  1575. // of catching exceptions that aren't in callbacks.
  1576. // The unhandled exception filter will also catch crashes in threads that
  1577. // don't have a try/catch or __try/__except block.
  1578. bool noMinidumps = CommandLine()->FindParm( "-nominidumps");
  1579. if ( !noMinidumps )
  1580. MinidumpSetUnhandledExceptionFunction( WriteSteamMiniDumpWithComment );
  1581. if ( !Plat_IsInDebugSession() && !noMinidumps )
  1582. {
  1583. int nRetVal = RUN_OK;
  1584. CatchAndWriteMiniDumpForVoidPtrFn( StaticRunListenServer, &nRetVal, true );
  1585. return nRetVal;
  1586. }
  1587. else
  1588. {
  1589. return RunListenServer();
  1590. }
  1591. #else
  1592. return RunListenServer();
  1593. #endif
  1594. }
  1595. #endif // SWDS
  1596. bool g_bUsingLegacyAppSystems = false;
  1597. bool CModAppSystemGroup::AddLegacySystems()
  1598. {
  1599. g_bUsingLegacyAppSystems = true;
  1600. AppSystemInfo_t appSystems[] =
  1601. {
  1602. { "soundemittersystem", SOUNDEMITTERSYSTEM_INTERFACE_VERSION },
  1603. { "", "" } // Required to terminate the list
  1604. };
  1605. if ( !AddSystems( appSystems ) )
  1606. return false;
  1607. #if !defined( DEDICATED )
  1608. // if ( CommandLine()->FindParm( "-tools" ) )
  1609. {
  1610. AppModule_t toolFrameworkModule = LoadModule( "engine" DLL_EXT_STRING );
  1611. if ( !AddSystem( toolFrameworkModule, VTOOLFRAMEWORK_INTERFACE_VERSION ) )
  1612. return false;
  1613. }
  1614. #endif
  1615. return true;
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. // Instantiate all main libraries
  1619. //-----------------------------------------------------------------------------
  1620. bool CModAppSystemGroup::Create()
  1621. {
  1622. #ifndef SWDS
  1623. if ( !IsServerOnly() )
  1624. {
  1625. if ( !ClientDLL_Load() )
  1626. return false;
  1627. }
  1628. #endif
  1629. if ( !ServerDLL_Load( IsServerOnly() ) )
  1630. return false;
  1631. IClientDLLSharedAppSystems *clientSharedSystems = 0;
  1632. #ifndef SWDS
  1633. if ( !IsServerOnly() )
  1634. {
  1635. clientSharedSystems = ( IClientDLLSharedAppSystems * )g_ClientFactory( CLIENT_DLL_SHARED_APPSYSTEMS, NULL );
  1636. if ( !clientSharedSystems )
  1637. return AddLegacySystems();
  1638. }
  1639. #endif
  1640. IServerDLLSharedAppSystems *serverSharedSystems = ( IServerDLLSharedAppSystems * )g_ServerFactory( SERVER_DLL_SHARED_APPSYSTEMS, NULL );
  1641. if ( !serverSharedSystems )
  1642. {
  1643. Assert( !"Expected both game and client .dlls to have or not have shared app systems interfaces!!!" );
  1644. return AddLegacySystems();
  1645. }
  1646. // Load game and client .dlls and build list then
  1647. CUtlVector< AppSystemInfo_t > systems;
  1648. int i;
  1649. int serverCount = serverSharedSystems->Count();
  1650. for ( i = 0 ; i < serverCount; ++i )
  1651. {
  1652. const char *dllName = serverSharedSystems->GetDllName( i );
  1653. const char *interfaceName = serverSharedSystems->GetInterfaceName( i );
  1654. AppSystemInfo_t info;
  1655. info.m_pModuleName = dllName;
  1656. info.m_pInterfaceName = interfaceName;
  1657. systems.AddToTail( info );
  1658. }
  1659. if ( !IsServerOnly() )
  1660. {
  1661. int clientCount = clientSharedSystems->Count();
  1662. for ( i = 0 ; i < clientCount; ++i )
  1663. {
  1664. const char *dllName = clientSharedSystems->GetDllName( i );
  1665. const char *interfaceName = clientSharedSystems->GetInterfaceName( i );
  1666. if ( ModuleAlreadyInList( systems, dllName, interfaceName ) )
  1667. continue;
  1668. AppSystemInfo_t info;
  1669. info.m_pModuleName = dllName;
  1670. info.m_pInterfaceName = interfaceName;
  1671. systems.AddToTail( info );
  1672. }
  1673. }
  1674. AppSystemInfo_t info;
  1675. info.m_pModuleName = "";
  1676. info.m_pInterfaceName = "";
  1677. systems.AddToTail( info );
  1678. if ( !AddSystems( systems.Base() ) )
  1679. return false;
  1680. #if !defined( DEDICATED )
  1681. // if ( CommandLine()->FindParm( "-tools" ) )
  1682. {
  1683. AppModule_t toolFrameworkModule = LoadModule( "engine" DLL_EXT_STRING );
  1684. if ( !AddSystem( toolFrameworkModule, VTOOLFRAMEWORK_INTERFACE_VERSION ) )
  1685. return false;
  1686. }
  1687. #endif
  1688. return true;
  1689. }
  1690. //-----------------------------------------------------------------------------
  1691. // Purpose: Fixme, we might need to verify if the interface names differ for the client versus the server
  1692. // Input : list -
  1693. // *moduleName -
  1694. // Output : Returns true on success, false on failure.
  1695. //-----------------------------------------------------------------------------
  1696. bool CModAppSystemGroup::ModuleAlreadyInList( CUtlVector< AppSystemInfo_t >& list, const char *moduleName, const char *interfaceName )
  1697. {
  1698. for ( int i = 0; i < list.Count(); ++i )
  1699. {
  1700. if ( !Q_stricmp( list[ i ].m_pModuleName, moduleName ) )
  1701. {
  1702. if ( Q_stricmp( list[ i ].m_pInterfaceName, interfaceName ) )
  1703. {
  1704. Error( "Game and client .dlls requesting different versions '%s' vs. '%s' from '%s'\n",
  1705. list[ i ].m_pInterfaceName, interfaceName, moduleName );
  1706. }
  1707. return true;
  1708. }
  1709. }
  1710. return false;
  1711. }
  1712. bool CModAppSystemGroup::PreInit()
  1713. {
  1714. return true;
  1715. }
  1716. void SV_ShutdownGameDLL();
  1717. int CModAppSystemGroup::Main()
  1718. {
  1719. int nRunResult = RUN_OK;
  1720. if ( IsServerOnly() )
  1721. {
  1722. // Start up the game engine
  1723. if ( eng->Load( true, host_parms.basedir ) )
  1724. {
  1725. // If we're using STEAM, pass the map cycle list as resource hints...
  1726. // Dedicated server drives frame loop manually
  1727. dedicated->RunServer();
  1728. SV_ShutdownGameDLL();
  1729. }
  1730. }
  1731. else
  1732. {
  1733. eng->SetQuitting( IEngine::QUIT_NOTQUITTING );
  1734. COM_TimestampedLog( "eng->Load" );
  1735. // Start up the game engine
  1736. static const char engineLoadMessage[] = "Calling CEngine::Load";
  1737. int64 nStartTime = ETWBegin( engineLoadMessage );
  1738. if ( eng->Load( false, host_parms.basedir ) )
  1739. {
  1740. #if !defined(SWDS)
  1741. ETWEnd( engineLoadMessage, nStartTime );
  1742. toolframework->ServerInit( g_ServerFactory );
  1743. if ( s_EngineAPI.MainLoop() )
  1744. {
  1745. nRunResult = RUN_RESTART;
  1746. }
  1747. // unload systems
  1748. eng->Unload();
  1749. toolframework->ServerShutdown();
  1750. #endif
  1751. SV_ShutdownGameDLL();
  1752. }
  1753. }
  1754. return nRunResult;
  1755. }
  1756. void CModAppSystemGroup::PostShutdown()
  1757. {
  1758. }
  1759. void CModAppSystemGroup::Destroy()
  1760. {
  1761. // unload game and client .dlls
  1762. ServerDLL_Unload();
  1763. #ifndef SWDS
  1764. if ( !IsServerOnly() )
  1765. {
  1766. ClientDLL_Unload();
  1767. }
  1768. #endif
  1769. }
  1770. //-----------------------------------------------------------------------------
  1771. //
  1772. // Purpose: Expose engine interface to launcher for dedicated servers
  1773. //
  1774. //-----------------------------------------------------------------------------
  1775. class CDedicatedServerAPI : public CTier3AppSystem< IDedicatedServerAPI >
  1776. {
  1777. typedef CTier3AppSystem< IDedicatedServerAPI > BaseClass;
  1778. public:
  1779. CDedicatedServerAPI() :
  1780. m_pDedicatedServer( 0 )
  1781. {
  1782. }
  1783. virtual bool Connect( CreateInterfaceFn factory );
  1784. virtual void Disconnect();
  1785. virtual void *QueryInterface( const char *pInterfaceName );
  1786. virtual bool ModInit( ModInfo_t &info );
  1787. virtual void ModShutdown( void );
  1788. virtual bool RunFrame( void );
  1789. virtual void AddConsoleText( char *text );
  1790. virtual void UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen );
  1791. virtual void UpdateHostname(char *pszHostname, int maxlen);
  1792. CModAppSystemGroup *m_pDedicatedServer;
  1793. };
  1794. //-----------------------------------------------------------------------------
  1795. // Singleton
  1796. //-----------------------------------------------------------------------------
  1797. EXPOSE_SINGLE_INTERFACE( CDedicatedServerAPI, IDedicatedServerAPI, VENGINE_HLDS_API_VERSION );
  1798. #define LONG_TICK_TIME 0.12f // about 8/66ths of a second
  1799. #define MIN_TIME_BETWEEN_DUMPED_TICKS 5.0f;
  1800. #define MAX_DUMPS_PER_LONG_TICK 10
  1801. void Sys_Sleep ( int msec );
  1802. bool g_bLongTickWatcherThreadEnabled = false;
  1803. bool g_bQuitLongTickWatcherThread = false;
  1804. int g_bTotalDumps = 0;
  1805. DWORD __stdcall LongTickWatcherThread( void *voidPtr )
  1806. {
  1807. int nLastTick = 0;
  1808. double flWarnTickTime = 0.0f;
  1809. double flNextPossibleDumpTime = Plat_FloatTime() + MIN_TIME_BETWEEN_DUMPED_TICKS;
  1810. int nNumDumpsThisTick = 0;
  1811. while ( eng->GetQuitting() == IEngine::QUIT_NOTQUITTING && !g_bQuitLongTickWatcherThread )
  1812. {
  1813. if ( sv.m_State == ss_active && sv.m_bSimulatingTicks )
  1814. {
  1815. int curTick = sv.m_nTickCount;
  1816. double curTime = Plat_FloatTime();
  1817. if ( nLastTick > 0 && nLastTick == curTick )
  1818. {
  1819. if ( curTime > flNextPossibleDumpTime && curTime > flWarnTickTime && nNumDumpsThisTick < MAX_DUMPS_PER_LONG_TICK )
  1820. {
  1821. nNumDumpsThisTick++;
  1822. g_bTotalDumps++;
  1823. Warning( "Long tick after tick %i. Writing minidump #%i (%i total).\n", nLastTick, nNumDumpsThisTick, g_bTotalDumps );
  1824. if ( nNumDumpsThisTick == MAX_DUMPS_PER_LONG_TICK )
  1825. {
  1826. Msg( "Not writing any more minidumps for this tick.\n" );
  1827. }
  1828. // If you're debugging a minidump and you ended up here, you probably want to switch to the main thread.
  1829. WriteMiniDump( "longtick" );
  1830. }
  1831. }
  1832. if ( nLastTick != curTick )
  1833. {
  1834. if ( nNumDumpsThisTick )
  1835. {
  1836. Msg( "Long tick lasted about %.1f seconds.\n", curTime - (flWarnTickTime - LONG_TICK_TIME) );
  1837. nNumDumpsThisTick = 0;
  1838. flNextPossibleDumpTime = curTime + MIN_TIME_BETWEEN_DUMPED_TICKS;
  1839. }
  1840. nLastTick = curTick;
  1841. flWarnTickTime = curTime + LONG_TICK_TIME;
  1842. }
  1843. }
  1844. else
  1845. {
  1846. nLastTick = 0;
  1847. }
  1848. if ( nNumDumpsThisTick )
  1849. {
  1850. // We'll write the next minidump 0.06 seconds from now.
  1851. Sys_Sleep( 60 );
  1852. }
  1853. else
  1854. {
  1855. // Check tick progress every 1/100th of a second.
  1856. Sys_Sleep( 10 );
  1857. }
  1858. }
  1859. g_bLongTickWatcherThreadEnabled = false;
  1860. g_bQuitLongTickWatcherThread = false;
  1861. return 0;
  1862. }
  1863. bool EnableLongTickWatcher()
  1864. {
  1865. bool bRet = false;
  1866. if ( !g_bLongTickWatcherThreadEnabled )
  1867. {
  1868. g_bQuitLongTickWatcherThread = false;
  1869. g_bLongTickWatcherThreadEnabled = true;
  1870. DWORD nThreadID;
  1871. VCRHook_CreateThread(NULL, 0,
  1872. #ifdef POSIX
  1873. (void*)
  1874. #endif
  1875. LongTickWatcherThread, NULL, 0, (unsigned long int *)&nThreadID );
  1876. bRet = true;
  1877. }
  1878. else if ( g_bQuitLongTickWatcherThread )
  1879. {
  1880. Msg( "Cannot create a new long tick watcher while waiting for an old one to terminate.\n" );
  1881. }
  1882. else
  1883. {
  1884. Msg( "The long tick watcher thread is already running.\n" );
  1885. }
  1886. return bRet;
  1887. }
  1888. //-----------------------------------------------------------------------------
  1889. // Dedicated server entrypoint
  1890. //-----------------------------------------------------------------------------
  1891. bool CDedicatedServerAPI::Connect( CreateInterfaceFn factory )
  1892. {
  1893. if ( CommandLine()->FindParm( "-sv_benchmark" ) != 0 )
  1894. {
  1895. Plat_SetBenchmarkMode( true );
  1896. }
  1897. if ( CommandLine()->FindParm( "-dumplongticks" ) )
  1898. {
  1899. Msg( "-dumplongticks found on command line. Activating long tick watcher thread.\n" );
  1900. EnableLongTickWatcher();
  1901. }
  1902. // Store off the app system factory...
  1903. g_AppSystemFactory = factory;
  1904. if ( !BaseClass::Connect( factory ) )
  1905. return false;
  1906. dedicated = ( IDedicatedExports * )factory( VENGINE_DEDICATEDEXPORTS_API_VERSION, NULL );
  1907. if ( !dedicated )
  1908. return false;
  1909. g_pFileSystem = g_pFullFileSystem;
  1910. g_pFileSystem->SetWarningFunc( Warning );
  1911. if ( !Shader_Connect( false ) )
  1912. return false;
  1913. if ( !g_pStudioRender )
  1914. {
  1915. Sys_Error( "Unable to init studio render system version %s\n", STUDIO_RENDER_INTERFACE_VERSION );
  1916. return false;
  1917. }
  1918. g_pPhysics = (IPhysics*)factory( VPHYSICS_INTERFACE_VERSION, NULL );
  1919. if ( !g_pDataCache || !g_pPhysics || !g_pMDLCache )
  1920. {
  1921. Warning( "Engine wasn't able to acquire required interfaces!\n" );
  1922. return false;
  1923. }
  1924. ConnectMDLCacheNotify();
  1925. return true;
  1926. }
  1927. void CDedicatedServerAPI::Disconnect()
  1928. {
  1929. DisconnectMDLCacheNotify();
  1930. g_pPhysics = NULL;
  1931. Shader_Disconnect();
  1932. g_pFileSystem = NULL;
  1933. ConVar_Unregister();
  1934. dedicated = NULL;
  1935. BaseClass::Disconnect();
  1936. g_AppSystemFactory = NULL;
  1937. }
  1938. //-----------------------------------------------------------------------------
  1939. // Query interface
  1940. //-----------------------------------------------------------------------------
  1941. void *CDedicatedServerAPI::QueryInterface( const char *pInterfaceName )
  1942. {
  1943. // Loading the engine DLL mounts *all* engine interfaces
  1944. CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary
  1945. return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing.
  1946. }
  1947. //-----------------------------------------------------------------------------
  1948. // Purpose:
  1949. // Input : type - 0 == normal, 1 == dedicated server
  1950. // *instance -
  1951. // *basedir -
  1952. // *cmdline -
  1953. // launcherFactory -
  1954. //-----------------------------------------------------------------------------
  1955. bool CDedicatedServerAPI::ModInit( ModInfo_t &info )
  1956. {
  1957. // Setup and write out steam_appid.txt before we launch
  1958. bool bDedicated = true;
  1959. eSteamInfoInit steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );
  1960. eng->SetQuitting( IEngine::QUIT_NOTQUITTING );
  1961. // Set up the engineparms_t which contains global information about the mod
  1962. host_parms.basedir = const_cast<char*>(info.m_pBaseDirectory);
  1963. host_parms.mod = const_cast<char*>(GetModDirFromPath(info.m_pInitialMod));
  1964. host_parms.game = const_cast<char*>(info.m_pInitialGame);
  1965. g_bTextMode = info.m_bTextMode;
  1966. TRACEINIT( COM_InitFilesystem( info.m_pInitialMod ), COM_ShutdownFileSystem() );
  1967. if ( steamInfo != eSteamInfo_Initialized )
  1968. {
  1969. // Try again with filesystem available. This is commonly needed for SDK mods which need the filesystem to find
  1970. // their steam.inf, due to mounting SDK search paths.
  1971. steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );
  1972. Assert( steamInfo == eSteamInfo_Initialized );
  1973. if ( steamInfo != eSteamInfo_Initialized )
  1974. {
  1975. Warning( "Failed to find steam.inf or equivalent steam info. May not have proper information to connect to Steam.\n" );
  1976. }
  1977. }
  1978. // set this up as early as possible, if the server isn't going to run pure, stop CRCing bits as we load them
  1979. // this happens even before the ConCommand's are processed, but we need to be sure to either CRC every file
  1980. // that is loaded, or not bother doing any
  1981. // Note that this mirrors g_sv_pure_mode from sv_main.cpp
  1982. int pure_mode = 1; // default to on, +sv_pure 0 or -sv_pure 0 will turn it off
  1983. if ( CommandLine()->CheckParm("+sv_pure") )
  1984. pure_mode = CommandLine()->ParmValue( "+sv_pure", 1 );
  1985. else if ( CommandLine()->CheckParm("-sv_pure") )
  1986. pure_mode = CommandLine()->ParmValue( "-sv_pure", 1 );
  1987. if ( pure_mode )
  1988. g_pFullFileSystem->EnableWhitelistFileTracking( true, true, CommandLine()->FindParm( "-sv_pure_verify_hashes" ) ? true : false );
  1989. else
  1990. g_pFullFileSystem->EnableWhitelistFileTracking( false, false, false );
  1991. materials->ModInit();
  1992. // Setup the material system config record, CreateGameWindow depends on it
  1993. // (when we're running stand-alone)
  1994. #ifndef SWDS
  1995. InitMaterialSystemConfig( true ); // !!should this be called standalone or not?
  1996. #endif
  1997. // Initialize general game stuff and create the main window
  1998. if ( game->Init( NULL ) )
  1999. {
  2000. m_pDedicatedServer = new CModAppSystemGroup( true, info.m_pParentAppSystemGroup );
  2001. // Store off the app system factory...
  2002. g_AppSystemFactory = m_pDedicatedServer->GetFactory();
  2003. m_pDedicatedServer->Run();
  2004. return true;
  2005. }
  2006. return false;
  2007. }
  2008. void CDedicatedServerAPI::ModShutdown( void )
  2009. {
  2010. if ( m_pDedicatedServer )
  2011. {
  2012. delete m_pDedicatedServer;
  2013. m_pDedicatedServer = NULL;
  2014. }
  2015. g_AppSystemFactory = NULL;
  2016. // Unload GL, Sound, etc.
  2017. eng->Unload();
  2018. // Shut down memory, etc.
  2019. game->Shutdown();
  2020. materials->ModShutdown();
  2021. TRACESHUTDOWN( COM_ShutdownFileSystem() );
  2022. }
  2023. bool CDedicatedServerAPI::RunFrame( void )
  2024. {
  2025. // Bail if someone wants to quit.
  2026. if ( eng->GetQuitting() != IEngine::QUIT_NOTQUITTING )
  2027. {
  2028. return false;
  2029. }
  2030. // Run engine frame
  2031. eng->Frame();
  2032. return true;
  2033. }
  2034. void CDedicatedServerAPI::AddConsoleText( char *text )
  2035. {
  2036. Cbuf_AddText( text );
  2037. }
  2038. void CDedicatedServerAPI::UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen )
  2039. {
  2040. Host_GetHostInfo( fps, nActive, nMaxPlayers, pszMap, maxlen );
  2041. }
  2042. void CDedicatedServerAPI::UpdateHostname(char *pszHostname, int maxlen)
  2043. {
  2044. if ( pszHostname && ( maxlen > 0 ) )
  2045. {
  2046. Q_strncpy( pszHostname, sv.GetName(), maxlen );
  2047. }
  2048. }
  2049. #ifndef SWDS
  2050. class CGameUIFuncs : public IGameUIFuncs
  2051. {
  2052. public:
  2053. bool IsKeyDown( const char *keyname, bool& isdown )
  2054. {
  2055. isdown = false;
  2056. if ( !g_ClientDLL )
  2057. return false;
  2058. return g_ClientDLL->IN_IsKeyDown( keyname, isdown );
  2059. }
  2060. const char *GetBindingForButtonCode( ButtonCode_t code )
  2061. {
  2062. return ::Key_BindingForKey( code );
  2063. }
  2064. virtual ButtonCode_t GetButtonCodeForBind( const char *bind )
  2065. {
  2066. const char *pKeyName = Key_NameForBinding( bind );
  2067. if ( !pKeyName )
  2068. return KEY_NONE;
  2069. return g_pInputSystem->StringToButtonCode( pKeyName ) ;
  2070. }
  2071. void GetVideoModes( struct vmode_s **ppListStart, int *pCount )
  2072. {
  2073. if ( videomode )
  2074. {
  2075. *pCount = videomode->GetModeCount();
  2076. *ppListStart = videomode->GetMode( 0 );
  2077. }
  2078. else
  2079. {
  2080. *pCount = 0;
  2081. *ppListStart = NULL;
  2082. }
  2083. }
  2084. void GetDesktopResolution( int &width, int &height )
  2085. {
  2086. int refreshrate;
  2087. game->GetDesktopInfo( width, height, refreshrate );
  2088. }
  2089. virtual void SetFriendsID( uint friendsID, const char *friendsName )
  2090. {
  2091. cl.SetFriendsID( friendsID, friendsName );
  2092. }
  2093. bool IsConnectedToVACSecureServer()
  2094. {
  2095. if ( cl.IsConnected() )
  2096. return Steam3Client().BGSSecure();
  2097. return false;
  2098. }
  2099. };
  2100. EXPOSE_SINGLE_INTERFACE( CGameUIFuncs, IGameUIFuncs, VENGINE_GAMEUIFUNCS_VERSION );
  2101. #endif
  2102. CON_COMMAND( dumplongticks, "Enables generating minidumps on long ticks." )
  2103. {
  2104. int enable = atoi( args[1] );
  2105. if ( args.ArgC() == 1 || enable )
  2106. {
  2107. if ( EnableLongTickWatcher() )
  2108. {
  2109. Msg( "Long tick watcher thread created. Use \"dumplongticks 0\" to disable.\n" );
  2110. }
  2111. }
  2112. else
  2113. {
  2114. // disable watcher thread if enabled
  2115. if ( g_bLongTickWatcherThreadEnabled && !g_bQuitLongTickWatcherThread )
  2116. {
  2117. Msg( "Disabling the long tick watcher.\n" );
  2118. g_bQuitLongTickWatcherThread = true;
  2119. }
  2120. else
  2121. {
  2122. Msg( "The long tick watcher is already disabled.\n" );
  2123. }
  2124. }
  2125. }