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

//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include <stdio.h>
#include <stdlib.h>
#include "isys.h"
#include "conproc.h"
#include "dedicated.h"
#include "engine_hlds_api.h"
#include "checksum_md5.h"
#include "mathlib/mathlib.h"
#include "tier0/dbg.h"
#include "tier1/strtools.h"
#include "tier0/icommandline.h"
#include "idedicatedexports.h"
#include "vgui/vguihelpers.h"
#include "appframework/AppFramework.h"
#include "filesystem_init.h"
#include "tier2/tier2.h"
#include "dedicated.h"
#include "vstdlib/cvar.h"
#ifdef LINUX
#include <mcheck.h>
#endif
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#include "keyvalues.h"
// filesystem_steam.cpp implements this useful function - mount all the caches for a given app ID.
extern void MountDependencies( int iAppId, CUtlVector<unsigned int> &depList );
#else
#define _chdir chdir
#include <unistd.h>
#endif
void* FileSystemFactory( const char *pName, int *pReturnCode );
bool InitInstance( );
void ProcessConsoleInput( void );
const char *UTIL_GetExecutableDir( );
bool NET_Init( void );
void NET_Shutdown( void );
const char *UTIL_GetBaseDir( void );
bool g_bVGui = false;
#if defined( CSTRIKE15 )
const char *g_gameName = "csgo";
#else
const char *g_gameName = "hl2";
#endif
#if defined ( _WIN32 )
#include "console/TextConsoleWin32.h"
CTextConsoleWin32 console;
#else
#include "console/TextConsoleUnix.h"
CTextConsoleUnix console;
#endif
extern char *gpszCvars;
IDedicatedServerAPI *engine = NULL;
int g_nSubProcessId = 0;
#ifdef POSIX
extern char g_szEXEName[ 256 ];
#endif
class CDedicatedServerLoggingListener : public ILoggingListener
{
public:
virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
{
if ( sys )
{
if ( g_nSubProcessId )
{
sys->Printf( " #%0x2d:%s", g_nSubProcessId, pMessage );
}
else
{
sys->Printf( "#%s", pMessage );
}
}
#ifdef _WIN32
Plat_DebugString( pMessage );
#endif
if ( pContext->m_Severity == LS_ERROR )
{
// In Windows vgui mode, make a message box or they won't ever see the error.
#ifdef _WIN32
if ( g_bVGui )
{
MessageBox( NULL, pMessage, "Error", MB_OK | MB_TASKMODAL );
}
TerminateProcess( GetCurrentProcess(), 1 );
#elif POSIX
fflush(stdout);
_exit(1);
#else
#error "Implement me"
#endif
}
}
};
#if defined(POSIX) && !defined(_PS3)
#define MAX_LINUX_CMDLINE 2048
static char linuxCmdline[ MAX_LINUX_CMDLINE +7 ]; // room for -steam
void BuildCmdLine( int argc, char **argv )
{
int len;
int i;
for (len = 0, i = 0; i < argc; i++)
{
len += strlen(argv[i]);
}
if ( len > MAX_LINUX_CMDLINE )
{
printf( "command line too long, %i max\n", MAX_LINUX_CMDLINE );
exit(-1);
return;
}
linuxCmdline[0] = '\0';
for ( i = 0; i < argc; i++ )
{
if ( i > 0 )
{
strcat( linuxCmdline, " " );
}
strcat( linuxCmdline, argv[ i ] );
}
strcat( linuxCmdline, " -steam" );
}
char *GetCommandLine()
{
return linuxCmdline;
}
#endif
static CNonFatalLoggingResponsePolicy s_NonFatalLoggingResponsePolicy;
static CDedicatedServerLoggingListener s_DedicatedServerLoggingListener;
bool RunServerIteration( bool bSupressStdIOBecauseWeAreAForkedChild )
{
bool bDone = false;
#if defined ( _WIN32 )
MSG msg;
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
//if (!GetMessage( &msg, NULL, 0, 0))
if ( msg.message == WM_QUIT )
{
bDone = true;
break;
}
TranslateMessage( &msg );
DispatchMessage( &msg );
}
if ( IsPC() )
{
// NOTE: Under some implementations of Win9x,
// dispatching messages can cause the FPU control word to change
SetupFPUControlWord();
}
if ( bDone /*|| gbAppHasBeenTerminated*/ )
return bDone;
#endif // _WIN32
if ( g_bVGui )
{
#ifdef _WIN32
RunVGUIFrame();
#endif
}
else
{
if (! bSupressStdIOBecauseWeAreAForkedChild )
{
// Calling ProcessConsoleInput can cost about a tenth of a millisecond.
// We used to call it up to 1,000 times a second. Even calling it once
// a frame is wasteful since the console hardly needs that level of
// responsiveness, and calling it too frequently is a waste of CPU time
// and power.
static int s_nProcessCount;
// Don't set this too high since the users keystrokes are not reflected
// until this ProcessConsoleInput is called.
const int nConsoleInputFrames = 5;
++s_nProcessCount;
if ( s_nProcessCount > nConsoleInputFrames )
{
s_nProcessCount = 0;
ProcessConsoleInput();
}
}
}
if ( !engine->RunFrame() )
{
bDone = true;
}
sys->UpdateStatus( 0 /* don't force */ );
return bDone;
}
//-----------------------------------------------------------------------------
//
// Server loop
//
//-----------------------------------------------------------------------------
void RunServer( bool bSupressStdIOBecauseWeAreAForkedChild )
{
#ifdef _WIN32
if(gpszCvars)
{
engine->AddConsoleText(gpszCvars);
}
#endif
// run 2 engine frames first to get the engine to load its resources
if (g_bVGui)
{
#ifdef _WIN32
RunVGUIFrame();
#endif
}
if ( !engine->RunFrame() )
{
return;
}
if (g_bVGui)
{
#ifdef _WIN32
RunVGUIFrame();
#endif
}
if ( !engine->RunFrame() )
{
return;
}
if (g_bVGui)
{
#ifdef _WIN32
VGUIFinishedConfig();
RunVGUIFrame();
#endif
}
bool bDone = false;
while ( ! bDone )
{
bDone = RunServerIteration( bSupressStdIOBecauseWeAreAForkedChild );
}
}
//-----------------------------------------------------------------------------
//
// initialize the console or wait for vgui to start the server
//
//-----------------------------------------------------------------------------
bool ConsoleStartup( CreateInterfaceFn dedicatedFactory )
{
#ifdef _WIN32
if ( g_bVGui )
{
StartVGUI( dedicatedFactory );
RunVGUIFrame();
// Run the config screen
while (VGUIIsInConfig() && VGUIIsRunning())
{
RunVGUIFrame();
}
if ( VGUIIsStopping() )
{
return false;
}
}
else
#endif // _WIN32
{
if ( !console.Init() )
{
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
// Instantiate all main libraries
//-----------------------------------------------------------------------------
bool CDedicatedAppSystemGroup::Create( )
{
// Hook the debug output stuff (override the spew func in the appframework)
LoggingSystem_PushLoggingState();
LoggingSystem_SetLoggingResponsePolicy( &s_NonFatalLoggingResponsePolicy );
LoggingSystem_RegisterLoggingListener( &s_DedicatedServerLoggingListener );
// Added the dedicated exports module for the engine to grab
AppModule_t dedicatedModule = LoadModule( Sys_GetFactoryThis() );
IAppSystem *pSystem = AddSystem( dedicatedModule, VENGINE_DEDICATEDEXPORTS_API_VERSION );
if ( !pSystem )
return false;
return sys->LoadModules( this );
}
bool CDedicatedAppSystemGroup::PreInit( )
{
// A little hack needed because dedicated links directly to filesystem .cpp files
g_pFullFileSystem = NULL;
if ( !BaseClass::PreInit() )
return false;
CFSSteamSetupInfo steamInfo;
steamInfo.m_pDirectoryName = NULL;
steamInfo.m_bOnlyUseDirectoryName = false;
steamInfo.m_bToolsMode = false;
steamInfo.m_bSetSteamDLLPath = false;
steamInfo.m_bSteam = g_pFullFileSystem->IsSteam();
steamInfo.m_bNoGameInfo = steamInfo.m_bSteam;
if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
return false;
CFSMountContentInfo fsInfo;
fsInfo.m_pFileSystem = g_pFullFileSystem;
fsInfo.m_bToolsMode = false;
fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
if ( FileSystem_MountContent( fsInfo ) != FS_OK )
return false;
if ( !NET_Init() )
return false;
// Needs to be done prior to init material system config
CFSSearchPathsInit initInfo;
initInfo.m_pFileSystem = g_pFullFileSystem;
initInfo.m_pDirectoryName = CommandLine()->ParmValue( "-game" );
// Load gameinfo.txt and setup all the search paths, just like the tools do.
FileSystem_LoadSearchPaths( initInfo );
#ifdef _WIN32
if ( CommandLine()->CheckParm( "-console" ) )
{
g_bVGui = false;
}
else
{
g_bVGui = true;
}
#else
// no VGUI under linux
g_bVGui = false;
#endif
if ( !g_bVGui )
{
if ( !sys->CreateConsoleWindow() )
return false;
}
return true;
}
int CDedicatedAppSystemGroup::Main( )
{
if ( !ConsoleStartup( GetFactory() ) )
return -1;
#ifdef _WIN32
if ( g_bVGui )
{
RunVGUIFrame();
}
else
{
// mount the caches
if (CommandLine()->CheckParm("-steam"))
{
// Add a search path for the base dir
char fullLocationPath[MAX_PATH];
if ( _getcwd( fullLocationPath, MAX_PATH ) )
{
g_pFullFileSystem->AddSearchPath( fullLocationPath, "MAIN" );
}
// Find the gameinfo.txt for our mod and mount it's caches
char gameInfoFilename[MAX_PATH];
Q_snprintf( gameInfoFilename, sizeof(gameInfoFilename) - 1, "%s\\gameinfo.txt", CommandLine()->ParmValue( "-game", g_gameName ) );
KeyValues *gameData = new KeyValues( "GameInfo" );
if ( gameData->LoadFromFile( g_pFullFileSystem, gameInfoFilename ) )
{
KeyValues *pFileSystem = gameData->FindKey( "FileSystem" );
int iAppId = pFileSystem->GetInt( "SteamAppId" );
if ( iAppId )
{
CUtlVector<unsigned int> depList;
MountDependencies( iAppId, depList );
}
}
gameData->deleteThis();
// remove our base search path
g_pFullFileSystem->RemoveSearchPaths( "MAIN" );
}
}
#endif
// Set up mod information
ModInfo_t info;
info.m_pInstance = GetAppInstance();
info.m_pBaseDirectory = UTIL_GetBaseDir();
info.m_pInitialMod = CommandLine()->ParmValue( "-game", g_gameName );
info.m_pInitialGame = CommandLine()->ParmValue( "-defaultgamedir", g_gameName );
info.m_pParentAppSystemGroup = this;
info.m_bTextMode = CommandLine()->CheckParm( "-textmode" ) ? true : false;
if ( engine->ModInit( info ) )
{
engine->ModShutdown();
} // if engine->ModInit
return 0;
}
void CDedicatedAppSystemGroup::PostShutdown()
{
#ifdef _WIN32
if ( g_bVGui )
{
StopVGUI();
}
#endif
sys->DestroyConsoleWindow();
console.ShutDown();
NET_Shutdown();
BaseClass::PostShutdown();
}
void CDedicatedAppSystemGroup::Destroy()
{
LoggingSystem_PopLoggingState();
}
//-----------------------------------------------------------------------------
// Gets the executable name
//-----------------------------------------------------------------------------
bool GetExecutableName( char *out, int nMaxLen )
{
#ifdef _WIN32
if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, nMaxLen ) )
{
return false;
}
return true;
#elif POSIX
Q_strncpy( out, g_szEXEName, nMaxLen );
return true;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Return the directory where this .exe is running from
// Output : char
//-----------------------------------------------------------------------------
void UTIL_ComputeBaseDir( char *pBaseDir, int nMaxLen )
{
int j;
char *pBuffer = NULL;
pBaseDir[ 0 ] = 0;
if ( GetExecutableName( pBaseDir, nMaxLen ) )
{
pBuffer = strrchr( pBaseDir, CORRECT_PATH_SEPARATOR );
if ( pBuffer && *pBuffer )
{
*(pBuffer+1) = '\0';
}
j = strlen( pBaseDir );
if (j > 0)
{
if ( ( pBaseDir[ j-1 ] == '\\' ) ||
( pBaseDir[ j-1 ] == '/' ) )
{
pBaseDir[ j-1 ] = 0;
}
}
}
char const *pOverrideDir = CommandLine()->CheckParm( "-basedir" );
if ( pOverrideDir )
{
strcpy( pBaseDir, pOverrideDir );
}
Q_strlower( pBaseDir );
Q_FixSlashes( pBaseDir );
}
//-----------------------------------------------------------------------------
// This class is a helper class used for steam-based applications.
// It loads up the file system in preparation for using it to load other
// required modules from steam.
//
// I couldn't use the one in appframework because the dedicated server
// inlines all the filesystem code.
//-----------------------------------------------------------------------------
class CDedicatedSteamApplication : public CSteamApplication
{
public:
CDedicatedSteamApplication( CSteamAppSystemGroup *pAppSystemGroup );
virtual bool Create( );
};
//-----------------------------------------------------------------------------
// This class is a helper class used for steam-based applications.
// It loads up the file system in preparation for using it to load other
// required modules from steam.
//
// I couldn't use the one in appframework because the dedicated server
// inlines all the filesystem code.
//-----------------------------------------------------------------------------
CDedicatedSteamApplication::CDedicatedSteamApplication( CSteamAppSystemGroup *pAppSystemGroup ) : CSteamApplication( pAppSystemGroup )
{
}
//-----------------------------------------------------------------------------
// Implementation of IAppSystemGroup
//-----------------------------------------------------------------------------
bool CDedicatedSteamApplication::Create( )
{
// Add in the cvar factory
AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() );
AddSystem( cvarModule, CVAR_INTERFACE_VERSION );
AppModule_t fileSystemModule = LoadModule( FileSystemFactory );
m_pFileSystem = (IFileSystem*)AddSystem( fileSystemModule, FILESYSTEM_INTERFACE_VERSION );
if ( !m_pFileSystem )
{
Warning( "Unable to load the file system!\n" );
return false;
}
return true;
}
static bool s_GameInfoSuggestFN( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
{
V_strncpy( pchPathBuffer, "left4dead", nBufferLength );
return true;
}
//-----------------------------------------------------------------------------
//
// Main entry point for dedicated server, shared between win32 and linux
//
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
#if !defined( POSIX ) && !defined( _WIN64 )
_asm
{
fninit
}
#endif
SetupFPUControlWord();
#ifdef POSIX
strcpy(g_szEXEName, *argv);
// Store off command line for argument searching
BuildCmdLine(argc, argv);
#endif
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
// Store off command line for argument searching
CommandLine()->CreateCmdLine( GetCommandLine() );
#ifndef _WIN32
Plat_SetCommandLine( CommandLine()->GetCmdLine() );
#endif
#ifdef LINUX
if ( CommandLine()->CheckParm( "-mtrace" ) )
{
mtrace();
}
#ifndef DEDICATED
if ( CommandLine()->CheckParm( "-logmem" ) )
{
EnableMemoryLogging( true );
}
#endif
#endif
// Figure out the directory the executable is running from
// and make that be the current working directory
char pBasedir[ MAX_PATH ];
UTIL_ComputeBaseDir( pBasedir, MAX_PATH );
_chdir( pBasedir );
// Rehook the command line.
CommandLine()->CreateCmdLine( GetCommandLine() );
if ( !InitInstance() )
return -1;
SetSuggestGameInfoDirFn( s_GameInfoSuggestFN );
CDedicatedAppSystemGroup dedicatedSystems;
CDedicatedSteamApplication steamApplication( &dedicatedSystems );
int nRet = steamApplication.Run( );
#ifdef LINUX
#ifndef DEDICATED
EnableMemoryLogging( false );
if ( CommandLine()->CheckParm( "-mtrace" ) )
{
muntrace();
}
#endif
#endif
return nRet;
}