source-engine/dedicated/sys_ded.cpp

531 lines
13 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include <stdio.h>
#include <stdlib.h>
#include "isys.h"
#include "console/conproc.h"
#include "dedicated.h"
#include "engine_hlds_api.h"
#include "checksum_md5.h"
#include "mathlib/mathlib.h"
#include "tier0/vcrmode.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"
#include "inputsystem/iinputsystem.h"
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#include "KeyValues.h"
#else
#define _chdir chdir
#include <unistd.h>
#endif
void* FileSystemFactory( const char *pName, int *pReturnCode );
bool InitInstance( );
int ProcessConsoleInput( void );
bool NET_Init( void );
void NET_Shutdown( void );
const char *UTIL_GetBaseDir( void );
#ifdef _WIN32
bool g_bVGui = false;
#endif
#if defined ( _WIN32 )
#include "console/TextConsoleWin32.h"
CTextConsoleWin32 console;
#else
#include "console/TextConsoleUnix.h"
CTextConsoleUnix console;
#endif
#ifdef _WIN32
extern char *gpszCvars;
#endif
IDedicatedServerAPI *engine = NULL;
//-----------------------------------------------------------------------------
// Implementation of IVCRHelpers.
//-----------------------------------------------------------------------------
class CVCRHelpers : public IVCRHelpers
{
public:
virtual void ErrorMessage( const char *pMsg )
{
printf( "ERROR: %s\n", pMsg );
}
virtual void* GetMainWindow()
{
return 0;
}
};
CVCRHelpers g_VCRHelpers;
SpewRetval_t DedicatedSpewOutputFunc( SpewType_t spewType, char const *pMsg ); // in sys_common.cpp
//-----------------------------------------------------------------------------
// Run a single VGUI frame. if bFinished is true, run VGUIFinishedConfig() first.
//-----------------------------------------------------------------------------
static bool DoRunVGUIFrame( bool bFinished = false )
{
#ifdef _WIN32
if ( g_bVGui )
{
if ( bFinished )
VGUIFinishedConfig();
RunVGUIFrame();
return true;
}
#endif
return false;
}
//-----------------------------------------------------------------------------
// Handle the VCRHook PeekMessage loop.
// Return true if WM_QUIT received.
//-----------------------------------------------------------------------------
static bool HandleVCRHook()
{
#if defined ( _WIN32 )
MSG msg;
bool bDone = false;
while( VCRHook_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 true;
#endif // _WIN32
return false;
}
//-----------------------------------------------------------------------------
//
// Server loop
//
//-----------------------------------------------------------------------------
void RunServer( void )
{
#ifdef _WIN32
if(gpszCvars)
{
engine->AddConsoleText(gpszCvars);
}
#endif
// Run 2 engine frames first to get the engine to load its resources.
for ( int i = 0; i < 2; i++ )
{
DoRunVGUIFrame();
if ( !engine->RunFrame() )
return;
}
// Run final VGUI frame.
DoRunVGUIFrame( true );
int bDone = false;
while ( !bDone )
{
// Check on VCRHook_PeekMessage...
if ( HandleVCRHook() )
break;
if ( !DoRunVGUIFrame() )
ProcessConsoleInput();
if ( !engine->RunFrame() )
bDone = true;
sys->UpdateStatus( 0 /* don't force */ );
}
}
//-----------------------------------------------------------------------------
//
// initialize the console or wait for vgui to start the server
//
//-----------------------------------------------------------------------------
static bool ConsoleStartup( )
{
#ifdef _WIN32
if ( g_bVGui )
{
RunVGUIFrame();
// Run the config screen
while (VGUIIsInConfig() && VGUIIsRunning())
RunVGUIFrame();
if ( VGUIIsStopping() )
return false;
return true;
}
else
{
if ( !console.Init() )
{
return false;
}
}
#endif // _WIN32
return true;
}
//-----------------------------------------------------------------------------
// Instantiate all main libraries
//-----------------------------------------------------------------------------
bool CDedicatedAppSystemGroup::Create( )
{
#ifndef _WIN32
if ( !console.Init() )
return false;
#endif
// Hook the debug output stuff (override the spew func in the appframework)
SpewOutputFunc( DedicatedSpewOutputFunc );
// 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;
if ( sys->LoadModules( this ) )
{
// Find the input system and tell it to skip Steam Controller initialization (we have to set this flag before Init gets called on the
// input system). Dedicated server should skip controller initialization to avoid initializing Steam, because we don't want the user to be
// flagged as "playing" the game.
auto inputsystem = ( IInputSystem* )FindSystem( INPUTSYSTEM_INTERFACE_VERSION );
if ( inputsystem )
{
inputsystem->SetSkipControllerInitialization( true );
}
return true;
}
else
{
return false;
}
}
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;
#ifdef _WIN32
g_bVGui = CommandLine()->CheckParm( "-vgui" );
#endif
CreateInterfaceFn factory = GetFactory();
IInputSystem *inputsystem = (IInputSystem *)factory( INPUTSYSTEM_INTERFACE_VERSION, NULL );
if ( inputsystem )
{
inputsystem->SetConsoleTextMode( true );
}
#ifdef _WIN32
if ( g_bVGui )
{
StartVGUI( GetFactory() );
}
else
#endif
{
if ( !sys->CreateConsoleWindow() )
return false;
}
return true;
}
int CDedicatedAppSystemGroup::Main( )
{
if ( !ConsoleStartup() )
return -1;
#ifdef _WIN32
if ( g_bVGui )
RunVGUIFrame();
#endif
// Set up mod information
ModInfo_t info;
info.m_pInstance = GetAppInstance();
info.m_pBaseDirectory = UTIL_GetBaseDir();
info.m_pInitialMod = CommandLine()->ParmValue( "-game", "hl2" );
info.m_pInitialGame = CommandLine()->ParmValue( "-defaultgamedir", "hl2" );
info.m_pParentAppSystemGroup = this;
info.m_bTextMode = CommandLine()->CheckParm( "-textmode" );
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()
{
}
//-----------------------------------------------------------------------------
// 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;
}
//-----------------------------------------------------------------------------
//
// Main entry point for dedicated server, shared between win32 and linux
//
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
#if !defined( POSIX ) && !defined( PLATFORM_64BITS )
_asm
{
fninit
}
#endif
SetupFPUControlWord();
#ifdef POSIX
Q_strncpy( g_szEXEName, *argv, ARRAYSIZE( g_szEXEName ) );
// Store off command line for argument searching
BuildCmdLine( argc, argv );
#endif
MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f );
// Store off command line for argument searching
CommandLine()->CreateCmdLine( VCRHook_GetCommandLine() );
#ifndef _WIN32
Plat_SetCommandLine( CommandLine()->GetCmdLine() );
#endif
// Start VCR mode?
const char *filename;
if( CommandLine()->CheckParm( "-vcrrecord", &filename ) )
{
if ( !VCRStart( filename, true, &g_VCRHelpers ) )
{
Error( "-vcrrecord: can't open '%s' for writing.\n", filename );
return -1;
}
}
else if( CommandLine()->CheckParm( "-vcrplayback", &filename ) )
{
if ( !VCRStart( filename, false, &g_VCRHelpers ) )
{
Error( "-vcrplayback: can't open '%s' for reading.\n", filename );
return -1;
}
}
// 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 through VCR mode.
CommandLine()->CreateCmdLine( VCRHook_GetCommandLine() );
if ( !InitInstance() )
return -1;
CDedicatedAppSystemGroup dedicatedSystems;
CDedicatedSteamApplication steamApplication( &dedicatedSystems );
return steamApplication.Run( );
}