Counter Strike : Global Offensive Source Code
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.

667 lines
15 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include "isys.h"
  10. #include "conproc.h"
  11. #include "dedicated.h"
  12. #include "engine_hlds_api.h"
  13. #include "checksum_md5.h"
  14. #include "mathlib/mathlib.h"
  15. #include "tier0/dbg.h"
  16. #include "tier1/strtools.h"
  17. #include "tier0/icommandline.h"
  18. #include "idedicatedexports.h"
  19. #include "vgui/vguihelpers.h"
  20. #include "appframework/AppFramework.h"
  21. #include "filesystem_init.h"
  22. #include "tier2/tier2.h"
  23. #include "dedicated.h"
  24. #include "vstdlib/cvar.h"
  25. #ifdef LINUX
  26. #include <mcheck.h>
  27. #endif
  28. #ifdef _WIN32
  29. #include <windows.h>
  30. #include <direct.h>
  31. #include "keyvalues.h"
  32. // filesystem_steam.cpp implements this useful function - mount all the caches for a given app ID.
  33. extern void MountDependencies( int iAppId, CUtlVector<unsigned int> &depList );
  34. #else
  35. #define _chdir chdir
  36. #include <unistd.h>
  37. #endif
  38. void* FileSystemFactory( const char *pName, int *pReturnCode );
  39. bool InitInstance( );
  40. void ProcessConsoleInput( void );
  41. const char *UTIL_GetExecutableDir( );
  42. bool NET_Init( void );
  43. void NET_Shutdown( void );
  44. const char *UTIL_GetBaseDir( void );
  45. bool g_bVGui = false;
  46. #if defined( CSTRIKE15 )
  47. const char *g_gameName = "csgo";
  48. #else
  49. const char *g_gameName = "hl2";
  50. #endif
  51. #if defined ( _WIN32 )
  52. #include "console/TextConsoleWin32.h"
  53. CTextConsoleWin32 console;
  54. #else
  55. #include "console/TextConsoleUnix.h"
  56. CTextConsoleUnix console;
  57. #endif
  58. extern char *gpszCvars;
  59. IDedicatedServerAPI *engine = NULL;
  60. int g_nSubProcessId = 0;
  61. #ifdef POSIX
  62. extern char g_szEXEName[ 256 ];
  63. #endif
  64. class CDedicatedServerLoggingListener : public ILoggingListener
  65. {
  66. public:
  67. virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
  68. {
  69. if ( sys )
  70. {
  71. if ( g_nSubProcessId )
  72. {
  73. sys->Printf( " #%0x2d:%s", g_nSubProcessId, pMessage );
  74. }
  75. else
  76. {
  77. sys->Printf( "#%s", pMessage );
  78. }
  79. }
  80. #ifdef _WIN32
  81. Plat_DebugString( pMessage );
  82. #endif
  83. if ( pContext->m_Severity == LS_ERROR )
  84. {
  85. // In Windows vgui mode, make a message box or they won't ever see the error.
  86. #ifdef _WIN32
  87. if ( g_bVGui )
  88. {
  89. MessageBox( NULL, pMessage, "Error", MB_OK | MB_TASKMODAL );
  90. }
  91. TerminateProcess( GetCurrentProcess(), 1 );
  92. #elif POSIX
  93. fflush(stdout);
  94. _exit(1);
  95. #else
  96. #error "Implement me"
  97. #endif
  98. }
  99. }
  100. };
  101. #if defined(POSIX) && !defined(_PS3)
  102. #define MAX_LINUX_CMDLINE 2048
  103. static char linuxCmdline[ MAX_LINUX_CMDLINE +7 ]; // room for -steam
  104. void BuildCmdLine( int argc, char **argv )
  105. {
  106. int len;
  107. int i;
  108. for (len = 0, i = 0; i < argc; i++)
  109. {
  110. len += strlen(argv[i]);
  111. }
  112. if ( len > MAX_LINUX_CMDLINE )
  113. {
  114. printf( "command line too long, %i max\n", MAX_LINUX_CMDLINE );
  115. exit(-1);
  116. return;
  117. }
  118. linuxCmdline[0] = '\0';
  119. for ( i = 0; i < argc; i++ )
  120. {
  121. if ( i > 0 )
  122. {
  123. strcat( linuxCmdline, " " );
  124. }
  125. strcat( linuxCmdline, argv[ i ] );
  126. }
  127. strcat( linuxCmdline, " -steam" );
  128. }
  129. char *GetCommandLine()
  130. {
  131. return linuxCmdline;
  132. }
  133. #endif
  134. static CNonFatalLoggingResponsePolicy s_NonFatalLoggingResponsePolicy;
  135. static CDedicatedServerLoggingListener s_DedicatedServerLoggingListener;
  136. bool RunServerIteration( bool bSupressStdIOBecauseWeAreAForkedChild )
  137. {
  138. bool bDone = false;
  139. #if defined ( _WIN32 )
  140. MSG msg;
  141. while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
  142. {
  143. //if (!GetMessage( &msg, NULL, 0, 0))
  144. if ( msg.message == WM_QUIT )
  145. {
  146. bDone = true;
  147. break;
  148. }
  149. TranslateMessage( &msg );
  150. DispatchMessage( &msg );
  151. }
  152. if ( IsPC() )
  153. {
  154. // NOTE: Under some implementations of Win9x,
  155. // dispatching messages can cause the FPU control word to change
  156. SetupFPUControlWord();
  157. }
  158. if ( bDone /*|| gbAppHasBeenTerminated*/ )
  159. return bDone;
  160. #endif // _WIN32
  161. if ( g_bVGui )
  162. {
  163. #ifdef _WIN32
  164. RunVGUIFrame();
  165. #endif
  166. }
  167. else
  168. {
  169. if (! bSupressStdIOBecauseWeAreAForkedChild )
  170. {
  171. // Calling ProcessConsoleInput can cost about a tenth of a millisecond.
  172. // We used to call it up to 1,000 times a second. Even calling it once
  173. // a frame is wasteful since the console hardly needs that level of
  174. // responsiveness, and calling it too frequently is a waste of CPU time
  175. // and power.
  176. static int s_nProcessCount;
  177. // Don't set this too high since the users keystrokes are not reflected
  178. // until this ProcessConsoleInput is called.
  179. const int nConsoleInputFrames = 5;
  180. ++s_nProcessCount;
  181. if ( s_nProcessCount > nConsoleInputFrames )
  182. {
  183. s_nProcessCount = 0;
  184. ProcessConsoleInput();
  185. }
  186. }
  187. }
  188. if ( !engine->RunFrame() )
  189. {
  190. bDone = true;
  191. }
  192. sys->UpdateStatus( 0 /* don't force */ );
  193. return bDone;
  194. }
  195. //-----------------------------------------------------------------------------
  196. //
  197. // Server loop
  198. //
  199. //-----------------------------------------------------------------------------
  200. void RunServer( bool bSupressStdIOBecauseWeAreAForkedChild )
  201. {
  202. #ifdef _WIN32
  203. if(gpszCvars)
  204. {
  205. engine->AddConsoleText(gpszCvars);
  206. }
  207. #endif
  208. // run 2 engine frames first to get the engine to load its resources
  209. if (g_bVGui)
  210. {
  211. #ifdef _WIN32
  212. RunVGUIFrame();
  213. #endif
  214. }
  215. if ( !engine->RunFrame() )
  216. {
  217. return;
  218. }
  219. if (g_bVGui)
  220. {
  221. #ifdef _WIN32
  222. RunVGUIFrame();
  223. #endif
  224. }
  225. if ( !engine->RunFrame() )
  226. {
  227. return;
  228. }
  229. if (g_bVGui)
  230. {
  231. #ifdef _WIN32
  232. VGUIFinishedConfig();
  233. RunVGUIFrame();
  234. #endif
  235. }
  236. bool bDone = false;
  237. while ( ! bDone )
  238. {
  239. bDone = RunServerIteration( bSupressStdIOBecauseWeAreAForkedChild );
  240. }
  241. }
  242. //-----------------------------------------------------------------------------
  243. //
  244. // initialize the console or wait for vgui to start the server
  245. //
  246. //-----------------------------------------------------------------------------
  247. bool ConsoleStartup( CreateInterfaceFn dedicatedFactory )
  248. {
  249. #ifdef _WIN32
  250. if ( g_bVGui )
  251. {
  252. StartVGUI( dedicatedFactory );
  253. RunVGUIFrame();
  254. // Run the config screen
  255. while (VGUIIsInConfig() && VGUIIsRunning())
  256. {
  257. RunVGUIFrame();
  258. }
  259. if ( VGUIIsStopping() )
  260. {
  261. return false;
  262. }
  263. }
  264. else
  265. #endif // _WIN32
  266. {
  267. if ( !console.Init() )
  268. {
  269. return false;
  270. }
  271. }
  272. return true;
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Instantiate all main libraries
  276. //-----------------------------------------------------------------------------
  277. bool CDedicatedAppSystemGroup::Create( )
  278. {
  279. // Hook the debug output stuff (override the spew func in the appframework)
  280. LoggingSystem_PushLoggingState();
  281. LoggingSystem_SetLoggingResponsePolicy( &s_NonFatalLoggingResponsePolicy );
  282. LoggingSystem_RegisterLoggingListener( &s_DedicatedServerLoggingListener );
  283. // Added the dedicated exports module for the engine to grab
  284. AppModule_t dedicatedModule = LoadModule( Sys_GetFactoryThis() );
  285. IAppSystem *pSystem = AddSystem( dedicatedModule, VENGINE_DEDICATEDEXPORTS_API_VERSION );
  286. if ( !pSystem )
  287. return false;
  288. return sys->LoadModules( this );
  289. }
  290. bool CDedicatedAppSystemGroup::PreInit( )
  291. {
  292. // A little hack needed because dedicated links directly to filesystem .cpp files
  293. g_pFullFileSystem = NULL;
  294. if ( !BaseClass::PreInit() )
  295. return false;
  296. CFSSteamSetupInfo steamInfo;
  297. steamInfo.m_pDirectoryName = NULL;
  298. steamInfo.m_bOnlyUseDirectoryName = false;
  299. steamInfo.m_bToolsMode = false;
  300. steamInfo.m_bSetSteamDLLPath = false;
  301. steamInfo.m_bSteam = g_pFullFileSystem->IsSteam();
  302. steamInfo.m_bNoGameInfo = steamInfo.m_bSteam;
  303. if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
  304. return false;
  305. CFSMountContentInfo fsInfo;
  306. fsInfo.m_pFileSystem = g_pFullFileSystem;
  307. fsInfo.m_bToolsMode = false;
  308. fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
  309. if ( FileSystem_MountContent( fsInfo ) != FS_OK )
  310. return false;
  311. if ( !NET_Init() )
  312. return false;
  313. // Needs to be done prior to init material system config
  314. CFSSearchPathsInit initInfo;
  315. initInfo.m_pFileSystem = g_pFullFileSystem;
  316. initInfo.m_pDirectoryName = CommandLine()->ParmValue( "-game" );
  317. // Load gameinfo.txt and setup all the search paths, just like the tools do.
  318. FileSystem_LoadSearchPaths( initInfo );
  319. #ifdef _WIN32
  320. if ( CommandLine()->CheckParm( "-console" ) )
  321. {
  322. g_bVGui = false;
  323. }
  324. else
  325. {
  326. g_bVGui = true;
  327. }
  328. #else
  329. // no VGUI under linux
  330. g_bVGui = false;
  331. #endif
  332. if ( !g_bVGui )
  333. {
  334. if ( !sys->CreateConsoleWindow() )
  335. return false;
  336. }
  337. return true;
  338. }
  339. int CDedicatedAppSystemGroup::Main( )
  340. {
  341. if ( !ConsoleStartup( GetFactory() ) )
  342. return -1;
  343. #ifdef _WIN32
  344. if ( g_bVGui )
  345. {
  346. RunVGUIFrame();
  347. }
  348. else
  349. {
  350. // mount the caches
  351. if (CommandLine()->CheckParm("-steam"))
  352. {
  353. // Add a search path for the base dir
  354. char fullLocationPath[MAX_PATH];
  355. if ( _getcwd( fullLocationPath, MAX_PATH ) )
  356. {
  357. g_pFullFileSystem->AddSearchPath( fullLocationPath, "MAIN" );
  358. }
  359. // Find the gameinfo.txt for our mod and mount it's caches
  360. char gameInfoFilename[MAX_PATH];
  361. Q_snprintf( gameInfoFilename, sizeof(gameInfoFilename) - 1, "%s\\gameinfo.txt", CommandLine()->ParmValue( "-game", g_gameName ) );
  362. KeyValues *gameData = new KeyValues( "GameInfo" );
  363. if ( gameData->LoadFromFile( g_pFullFileSystem, gameInfoFilename ) )
  364. {
  365. KeyValues *pFileSystem = gameData->FindKey( "FileSystem" );
  366. int iAppId = pFileSystem->GetInt( "SteamAppId" );
  367. if ( iAppId )
  368. {
  369. CUtlVector<unsigned int> depList;
  370. MountDependencies( iAppId, depList );
  371. }
  372. }
  373. gameData->deleteThis();
  374. // remove our base search path
  375. g_pFullFileSystem->RemoveSearchPaths( "MAIN" );
  376. }
  377. }
  378. #endif
  379. // Set up mod information
  380. ModInfo_t info;
  381. info.m_pInstance = GetAppInstance();
  382. info.m_pBaseDirectory = UTIL_GetBaseDir();
  383. info.m_pInitialMod = CommandLine()->ParmValue( "-game", g_gameName );
  384. info.m_pInitialGame = CommandLine()->ParmValue( "-defaultgamedir", g_gameName );
  385. info.m_pParentAppSystemGroup = this;
  386. info.m_bTextMode = CommandLine()->CheckParm( "-textmode" ) ? true : false;
  387. if ( engine->ModInit( info ) )
  388. {
  389. engine->ModShutdown();
  390. } // if engine->ModInit
  391. return 0;
  392. }
  393. void CDedicatedAppSystemGroup::PostShutdown()
  394. {
  395. #ifdef _WIN32
  396. if ( g_bVGui )
  397. {
  398. StopVGUI();
  399. }
  400. #endif
  401. sys->DestroyConsoleWindow();
  402. console.ShutDown();
  403. NET_Shutdown();
  404. BaseClass::PostShutdown();
  405. }
  406. void CDedicatedAppSystemGroup::Destroy()
  407. {
  408. LoggingSystem_PopLoggingState();
  409. }
  410. //-----------------------------------------------------------------------------
  411. // Gets the executable name
  412. //-----------------------------------------------------------------------------
  413. bool GetExecutableName( char *out, int nMaxLen )
  414. {
  415. #ifdef _WIN32
  416. if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, nMaxLen ) )
  417. {
  418. return false;
  419. }
  420. return true;
  421. #elif POSIX
  422. Q_strncpy( out, g_szEXEName, nMaxLen );
  423. return true;
  424. #endif
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose: Return the directory where this .exe is running from
  428. // Output : char
  429. //-----------------------------------------------------------------------------
  430. void UTIL_ComputeBaseDir( char *pBaseDir, int nMaxLen )
  431. {
  432. int j;
  433. char *pBuffer = NULL;
  434. pBaseDir[ 0 ] = 0;
  435. if ( GetExecutableName( pBaseDir, nMaxLen ) )
  436. {
  437. pBuffer = strrchr( pBaseDir, CORRECT_PATH_SEPARATOR );
  438. if ( pBuffer && *pBuffer )
  439. {
  440. *(pBuffer+1) = '\0';
  441. }
  442. j = strlen( pBaseDir );
  443. if (j > 0)
  444. {
  445. if ( ( pBaseDir[ j-1 ] == '\\' ) ||
  446. ( pBaseDir[ j-1 ] == '/' ) )
  447. {
  448. pBaseDir[ j-1 ] = 0;
  449. }
  450. }
  451. }
  452. char const *pOverrideDir = CommandLine()->CheckParm( "-basedir" );
  453. if ( pOverrideDir )
  454. {
  455. strcpy( pBaseDir, pOverrideDir );
  456. }
  457. Q_strlower( pBaseDir );
  458. Q_FixSlashes( pBaseDir );
  459. }
  460. //-----------------------------------------------------------------------------
  461. // This class is a helper class used for steam-based applications.
  462. // It loads up the file system in preparation for using it to load other
  463. // required modules from steam.
  464. //
  465. // I couldn't use the one in appframework because the dedicated server
  466. // inlines all the filesystem code.
  467. //-----------------------------------------------------------------------------
  468. class CDedicatedSteamApplication : public CSteamApplication
  469. {
  470. public:
  471. CDedicatedSteamApplication( CSteamAppSystemGroup *pAppSystemGroup );
  472. virtual bool Create( );
  473. };
  474. //-----------------------------------------------------------------------------
  475. // This class is a helper class used for steam-based applications.
  476. // It loads up the file system in preparation for using it to load other
  477. // required modules from steam.
  478. //
  479. // I couldn't use the one in appframework because the dedicated server
  480. // inlines all the filesystem code.
  481. //-----------------------------------------------------------------------------
  482. CDedicatedSteamApplication::CDedicatedSteamApplication( CSteamAppSystemGroup *pAppSystemGroup ) : CSteamApplication( pAppSystemGroup )
  483. {
  484. }
  485. //-----------------------------------------------------------------------------
  486. // Implementation of IAppSystemGroup
  487. //-----------------------------------------------------------------------------
  488. bool CDedicatedSteamApplication::Create( )
  489. {
  490. // Add in the cvar factory
  491. AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
  492. AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
  493. AppModule_t fileSystemModule = LoadModule( FileSystemFactory );
  494. m_pFileSystem = (IFileSystem*)AddSystem( fileSystemModule, FILESYSTEM_INTERFACE_VERSION );
  495. if ( !m_pFileSystem )
  496. {
  497. Warning( "Unable to load the file system!\n" );
  498. return false;
  499. }
  500. return true;
  501. }
  502. static bool s_GameInfoSuggestFN( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
  503. {
  504. V_strncpy( pchPathBuffer, "left4dead", nBufferLength );
  505. return true;
  506. }
  507. //-----------------------------------------------------------------------------
  508. //
  509. // Main entry point for dedicated server, shared between win32 and linux
  510. //
  511. //-----------------------------------------------------------------------------
  512. int main(int argc, char **argv)
  513. {
  514. #if !defined( POSIX ) && !defined( _WIN64 )
  515. _asm
  516. {
  517. fninit
  518. }
  519. #endif
  520. SetupFPUControlWord();
  521. #ifdef POSIX
  522. strcpy(g_szEXEName, *argv);
  523. // Store off command line for argument searching
  524. BuildCmdLine(argc, argv);
  525. #endif
  526. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
  527. // Store off command line for argument searching
  528. CommandLine()->CreateCmdLine( GetCommandLine() );
  529. #ifndef _WIN32
  530. Plat_SetCommandLine( CommandLine()->GetCmdLine() );
  531. #endif
  532. #ifdef LINUX
  533. if ( CommandLine()->CheckParm( "-mtrace" ) )
  534. {
  535. mtrace();
  536. }
  537. #ifndef DEDICATED
  538. if ( CommandLine()->CheckParm( "-logmem" ) )
  539. {
  540. EnableMemoryLogging( true );
  541. }
  542. #endif
  543. #endif
  544. // Figure out the directory the executable is running from
  545. // and make that be the current working directory
  546. char pBasedir[ MAX_PATH ];
  547. UTIL_ComputeBaseDir( pBasedir, MAX_PATH );
  548. _chdir( pBasedir );
  549. // Rehook the command line.
  550. CommandLine()->CreateCmdLine( GetCommandLine() );
  551. if ( !InitInstance() )
  552. return -1;
  553. SetSuggestGameInfoDirFn( s_GameInfoSuggestFN );
  554. CDedicatedAppSystemGroup dedicatedSystems;
  555. CDedicatedSteamApplication steamApplication( &dedicatedSystems );
  556. int nRet = steamApplication.Run( );
  557. #ifdef LINUX
  558. #ifndef DEDICATED
  559. EnableMemoryLogging( false );
  560. if ( CommandLine()->CheckParm( "-mtrace" ) )
  561. {
  562. muntrace();
  563. }
  564. #endif
  565. #endif
  566. return nRet;
  567. }