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.
2572 lines
71 KiB
2572 lines
71 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
#define DISABLE_PROTECTED_THINGS |
|
|
|
#if defined( USE_SDL ) |
|
#include "appframework/ilaunchermgr.h" |
|
#endif |
|
|
|
#if defined( _WIN32 ) && !defined( _X360 ) |
|
#include "winlite.h" |
|
#include <Psapi.h> |
|
#endif |
|
|
|
#if defined( OSX ) |
|
#include <sys/sysctl.h> |
|
#endif |
|
|
|
#if defined( POSIX ) |
|
#include <setjmp.h> |
|
#include <signal.h> |
|
#endif |
|
|
|
#include <stdarg.h> |
|
#include "quakedef.h" |
|
#include "idedicatedexports.h" |
|
#include "engine_launcher_api.h" |
|
#include "ivideomode.h" |
|
#include "common.h" |
|
#include "iregistry.h" |
|
#include "keys.h" |
|
#include "cdll_engine_int.h" |
|
#include "traceinit.h" |
|
#include "iengine.h" |
|
#include "igame.h" |
|
#include "tier0/etwprof.h" |
|
#include "tier0/vcrmode.h" |
|
#include "tier0/icommandline.h" |
|
#include "tier0/minidump.h" |
|
#include "engine_hlds_api.h" |
|
#include "filesystem_engine.h" |
|
#include "cl_main.h" |
|
#include "client.h" |
|
#include "tier3/tier3.h" |
|
#include "MapReslistGenerator.h" |
|
#include "toolframework/itoolframework.h" |
|
#include "sourcevr/isourcevirtualreality.h" |
|
#include "DevShotGenerator.h" |
|
#include "gl_shader.h" |
|
#include "l_studio.h" |
|
#include "IHammer.h" |
|
#include "sys_dll.h" |
|
#include "materialsystem/materialsystem_config.h" |
|
#include "server.h" |
|
#include "video/ivideoservices.h" |
|
#include "datacache/idatacache.h" |
|
#include "vphysics_interface.h" |
|
#include "inputsystem/iinputsystem.h" |
|
#include "appframework/IAppSystemGroup.h" |
|
#include "tier0/systeminformation.h" |
|
#include "host_cmd.h" |
|
#ifdef _WIN32 |
|
#include "VGuiMatSurface/IMatSystemSurface.h" |
|
#endif |
|
|
|
#ifdef GPROFILER |
|
#include "gperftools/profiler.h" |
|
#endif |
|
|
|
// This is here just for legacy support of older .dlls!!! |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
#include "eiface.h" |
|
#include "tier1/fmtstr.h" |
|
#include "steam/steam_api.h" |
|
|
|
#ifndef SWDS |
|
#include "sys_mainwind.h" |
|
#include "vgui/ISystem.h" |
|
#include "vgui_controls/Controls.h" |
|
#include "IGameUIFuncs.h" |
|
#include "cl_steamauth.h" |
|
#endif // SWDS |
|
|
|
#if defined(_WIN32) |
|
#include <eh.h> |
|
#endif |
|
|
|
#if POSIX |
|
#include <dlfcn.h> |
|
#endif |
|
|
|
#if defined( _X360 ) |
|
#include "xbox/xbox_win32stubs.h" |
|
#else |
|
#include "xbox/xboxstubs.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
#include "tier0/memalloc.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Globals |
|
//----------------------------------------------------------------------------- |
|
IDedicatedExports *dedicated = NULL; |
|
extern CreateInterfaceFn g_AppSystemFactory; |
|
IHammer *g_pHammer = NULL; |
|
IPhysics *g_pPhysics = NULL; |
|
ISourceVirtualReality *g_pSourceVR = NULL; |
|
#if defined( USE_SDL ) |
|
ILauncherMgr *g_pLauncherMgr = NULL; |
|
#endif |
|
|
|
#ifndef SWDS |
|
extern CreateInterfaceFn g_ClientFactory; |
|
#endif |
|
|
|
static SteamInfVersionInfo_t g_SteamInfIDVersionInfo; |
|
const SteamInfVersionInfo_t& GetSteamInfIDVersionInfo() |
|
{ |
|
Assert( g_SteamInfIDVersionInfo.AppID != k_uAppIdInvalid ); |
|
return g_SteamInfIDVersionInfo; |
|
} |
|
|
|
int build_number( void ) |
|
{ |
|
return GetSteamInfIDVersionInfo().ServerVersion; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Forward declarations |
|
//----------------------------------------------------------------------------- |
|
void Host_GetHostInfo(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen ); |
|
const char *Key_BindingForKey( int keynum ); |
|
void COM_ShutdownFileSystem( void ); |
|
void COM_InitFilesystem( const char *pFullModPath ); |
|
void Host_ReadPreStartupConfiguration(); |
|
|
|
//----------------------------------------------------------------------------- |
|
// ConVars and console commands |
|
//----------------------------------------------------------------------------- |
|
#ifndef SWDS |
|
//----------------------------------------------------------------------------- |
|
// Purpose: exports an interface that can be used by the launcher to run the engine |
|
// this is the exported function when compiled as a blob |
|
//----------------------------------------------------------------------------- |
|
void EXPORT F( IEngineAPI **api ) |
|
{ |
|
CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary to prevent the LTCG compiler from crashing. |
|
*api = ( IEngineAPI * )(factory(VENGINE_LAUNCHER_API_VERSION, NULL)); |
|
} |
|
#endif // SWDS |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void ClearIOStates( void ) |
|
{ |
|
#ifndef SWDS |
|
if ( g_ClientDLL ) |
|
{ |
|
g_ClientDLL->IN_ClearStates(); |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// The SDK launches the game with the full path to gameinfo.txt, so we need |
|
// to strip off the path. |
|
//----------------------------------------------------------------------------- |
|
const char *GetModDirFromPath( const char *pszPath ) |
|
{ |
|
char *pszSlash = Q_strrchr( pszPath, '\\' ); |
|
if ( pszSlash ) |
|
{ |
|
return pszSlash + 1; |
|
} |
|
else if ( ( pszSlash = Q_strrchr( pszPath, '/' ) ) != NULL ) |
|
{ |
|
return pszSlash + 1; |
|
} |
|
|
|
// Must just be a mod directory already. |
|
return pszPath; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main entry |
|
//----------------------------------------------------------------------------- |
|
#ifndef SWDS |
|
#include "gl_matsysiface.h" |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Inner loop: initialize, shutdown main systems, load steam to |
|
//----------------------------------------------------------------------------- |
|
class CModAppSystemGroup : public CAppSystemGroup |
|
{ |
|
typedef CAppSystemGroup BaseClass; |
|
public: |
|
// constructor |
|
CModAppSystemGroup( bool bServerOnly, CAppSystemGroup *pParentAppSystem = NULL ) |
|
: BaseClass( pParentAppSystem ), |
|
m_bServerOnly( bServerOnly ) |
|
{ |
|
} |
|
|
|
CreateInterfaceFn GetFactory() |
|
{ |
|
return CAppSystemGroup::GetFactory(); |
|
} |
|
|
|
// Methods of IApplication |
|
virtual bool Create(); |
|
virtual bool PreInit(); |
|
virtual int Main(); |
|
virtual void PostShutdown(); |
|
virtual void Destroy(); |
|
|
|
private: |
|
|
|
bool IsServerOnly() const |
|
{ |
|
return m_bServerOnly; |
|
} |
|
bool ModuleAlreadyInList( CUtlVector< AppSystemInfo_t >& list, const char *moduleName, const char *interfaceName ); |
|
|
|
bool AddLegacySystems(); |
|
bool m_bServerOnly; |
|
}; |
|
|
|
#if defined( STAGING_ONLY ) |
|
CON_COMMAND( bigalloc, "huge alloc crash" ) |
|
{ |
|
Msg( "pre-crash %d\n", MemAlloc_MemoryAllocFailed() ); |
|
// Alloc a bit less than UINT_MAX so there is room for heap headers in the malloc functions. |
|
void *buf = malloc( UINT_MAX - 0x4000 ); |
|
Msg( "post-alloc %d. buf: %p\n", MemAlloc_MemoryAllocFailed(), buf ); |
|
*(int *)buf = 0; |
|
} |
|
#endif |
|
|
|
extern void S_ClearBuffer(); |
|
extern char g_minidumpinfo[ 4096 ]; |
|
extern PAGED_POOL_INFO_t g_pagedpoolinfo; |
|
extern bool g_bUpdateMinidumpComment; |
|
void GetSpew( char *buf, size_t buflen ); |
|
|
|
extern int gHostSpawnCount; |
|
extern int g_nMapLoadCount; |
|
extern int g_HostServerAbortCount; |
|
extern int g_HostErrorCount; |
|
extern int g_HostEndDemo; |
|
|
|
// Turn this to 1 to allow for expanded spew in minidump comments. |
|
static ConVar sys_minidumpexpandedspew( "sys_minidumpexpandedspew", "1" ); |
|
|
|
#ifdef IS_WINDOWS_PC |
|
|
|
extern "C" void __cdecl FailSafe( unsigned int uStructuredExceptionCode, struct _EXCEPTION_POINTERS * pExceptionInfo ) |
|
{ |
|
// Nothing, this just catches a crash when creating the comment block |
|
} |
|
|
|
#endif |
|
|
|
#if defined( POSIX ) |
|
|
|
static sigjmp_buf g_mark; |
|
static void posix_signal_handler( int i ) |
|
{ |
|
siglongjmp( g_mark, -1 ); |
|
} |
|
|
|
#define DO_TRY if ( sigsetjmp( g_mark, 1 ) == 0 ) |
|
#define DO_CATCH else |
|
|
|
#if defined( OSX ) |
|
#define __sighandler_t sig_t |
|
#endif |
|
|
|
#else |
|
|
|
#define DO_TRY try |
|
#define DO_CATCH catch ( ... ) |
|
|
|
#endif // POSIX |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Check whether any mods are loaded. |
|
// Currently looks for metamod and sourcemod. |
|
//----------------------------------------------------------------------------- |
|
static bool IsSourceModLoaded() |
|
{ |
|
#if defined( _WIN32 ) |
|
static const char *s_pFileNames[] = { "metamod.2.tf2.dll", "sourcemod.2.tf2.dll", "sdkhooks.ext.2.ep2v.dll", "sdkhooks.ext.2.tf2.dll" }; |
|
|
|
for ( size_t i = 0; i < Q_ARRAYSIZE( s_pFileNames ); i++ ) |
|
{ |
|
// GetModuleHandle function returns a handle to a mapped module |
|
// without incrementing its reference count. |
|
if ( GetModuleHandleA( s_pFileNames[ i ] ) ) |
|
return true; |
|
} |
|
#else |
|
FILE *fh = fopen( "/proc/self/maps", "r" ); |
|
|
|
if ( fh ) |
|
{ |
|
char buf[ 1024 ]; |
|
static const char *s_pFileNames[] = { "metamod.2.tf2.so", "sourcemod.2.tf2.so", "sdkhooks.ext.2.ep2v.so", "sdkhooks.ext.2.tf2.so" }; |
|
|
|
while ( fgets( buf, sizeof( buf ), fh ) ) |
|
{ |
|
for ( size_t i = 0; i < Q_ARRAYSIZE( s_pFileNames ); i++ ) |
|
{ |
|
if ( strstr( buf, s_pFileNames[ i ] ) ) |
|
{ |
|
fclose( fh ); |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
fclose( fh ); |
|
} |
|
#endif |
|
|
|
return false; |
|
} |
|
|
|
template< int _SIZE > |
|
class CErrorText |
|
{ |
|
public: |
|
CErrorText() : m_bIsDedicatedServer( false ) {} |
|
~CErrorText() {} |
|
|
|
void Steam_SetMiniDumpComment() |
|
{ |
|
#if !defined( NO_STEAM ) |
|
SteamAPI_SetMiniDumpComment( m_errorText ); |
|
#endif |
|
} |
|
|
|
void CommentCat( const char * str ) |
|
{ |
|
V_strcat_safe( m_errorText, str ); |
|
} |
|
|
|
void CommentPrintf( const char *fmt, ... ) |
|
{ |
|
va_list args; |
|
va_start( args, fmt ); |
|
|
|
size_t len = strlen( m_errorText ); |
|
vsnprintf( m_errorText + len, sizeof( m_errorText ) - len - 1, fmt, args ); |
|
m_errorText[ sizeof( m_errorText ) - 1 ] = 0; |
|
|
|
va_end( args ); |
|
} |
|
|
|
void BuildComment( char const *pchSysErrorText, bool bRealCrash ) |
|
{ |
|
// Try and detect whether this |
|
bool bSourceModLoaded = false; |
|
if ( m_bIsDedicatedServer ) |
|
{ |
|
bSourceModLoaded = IsSourceModLoaded(); |
|
if ( bSourceModLoaded ) |
|
{ |
|
AppId_t AppId = GetSteamInfIDVersionInfo().ServerAppID; |
|
// Bump up the number and report the crash. This should be something |
|
// like 232251 (instead of 232250). 232251 is for the TF2 Windows client, |
|
// but we actually report those crashes under ID 440, so this should be ok. |
|
SteamAPI_SetBreakpadAppID( AppId + 1 ); |
|
} |
|
} |
|
|
|
#ifdef IS_WINDOWS_PC |
|
// This warning only applies if you want to catch structured exceptions (crashes) |
|
// using C++ exceptions. We do not want to do that so we can build with C++ exceptions |
|
// completely disabled, and just suppress this warning. |
|
// warning C4535: calling _set_se_translator() requires /EHa |
|
#pragma warning( suppress : 4535 ) |
|
_se_translator_function curfilter = _set_se_translator( &FailSafe ); |
|
#elif defined( POSIX ) |
|
// Only need to worry about this function crashing when we're dealing with a real crash. |
|
__sighandler_t curfilter = bRealCrash ? signal( SIGSEGV, posix_signal_handler ) : 0; |
|
#endif |
|
|
|
DO_TRY |
|
{ |
|
Q_memset( m_errorText, 0x00, sizeof( m_errorText ) ); |
|
|
|
if ( pchSysErrorText ) |
|
{ |
|
CommentCat( "Sys_Error( " ); |
|
CommentCat( pchSysErrorText ); |
|
|
|
// Trim trailing \n. |
|
int len = V_strlen( m_errorText ); |
|
if ( len > 0 && m_errorText[ len - 1 ] == '\n' ) |
|
m_errorText[ len - 1 ] = 0; |
|
|
|
CommentCat( " )\n" ); |
|
} |
|
else |
|
{ |
|
CommentCat( "Crash\n" ); |
|
} |
|
CommentPrintf( "Uptime( %f )\n", Plat_FloatTime() ); |
|
CommentPrintf( "SourceMod:%d,DS:%d,Crash:%d\n\n", bSourceModLoaded, m_bIsDedicatedServer, bRealCrash ); |
|
|
|
// Add g_minidumpinfo from CL_SetSteamCrashComment(). |
|
CommentCat( g_minidumpinfo ); |
|
|
|
// Latch in case extended stuff below crashes |
|
Steam_SetMiniDumpComment(); |
|
|
|
// Add Memory Status |
|
BuildCommentMemStatus(); |
|
|
|
// Spew out paged pool stuff, etc. |
|
PAGED_POOL_INFO_t ppi_info; |
|
if ( Plat_GetPagedPoolInfo( &ppi_info ) != SYSCALL_UNSUPPORTED ) |
|
{ |
|
CommentPrintf( "\nPaged Pool\nprev PP PAGES: used: %lu, free %lu\nfinal PP PAGES: used: %lu, free %lu\n", |
|
g_pagedpoolinfo.numPagesUsed, g_pagedpoolinfo.numPagesFree, |
|
ppi_info.numPagesUsed, ppi_info.numPagesFree ); |
|
} |
|
|
|
CommentPrintf( "memallocfail? = %u\nActive: %s\nSpawnCount %d MapLoad Count %d\nError count %d, end demo %d, abort count %d\n", |
|
MemAlloc_MemoryAllocFailed(), |
|
( game && game->IsActiveApp() ) ? "active" : "inactive", |
|
gHostSpawnCount, |
|
g_nMapLoadCount, |
|
g_HostErrorCount, |
|
g_HostEndDemo, |
|
g_HostServerAbortCount ); |
|
|
|
// Latch in case extended stuff below crashes |
|
Steam_SetMiniDumpComment(); |
|
|
|
// Add user comment strings. 4096 is just a large sanity number we should |
|
// never ever reach (currently our minidump supports 32 of these.) |
|
for( int i = 0; i < 4096; i++ ) |
|
{ |
|
const char *pUserStreamInfo = MinidumpUserStreamInfoGet( i ); |
|
if( !pUserStreamInfo ) |
|
break; |
|
|
|
if ( pUserStreamInfo[ 0 ] ) |
|
CommentPrintf( "%s", pUserStreamInfo ); |
|
} |
|
|
|
bool bExtendedSpew = sys_minidumpexpandedspew.GetBool(); |
|
if ( bExtendedSpew ) |
|
{ |
|
BuildCommentExtended(); |
|
Steam_SetMiniDumpComment(); |
|
|
|
#if defined( LINUX ) |
|
if ( bRealCrash ) |
|
{ |
|
// bRealCrash is set when we're actually making a comment for a dump or error. |
|
AddFileToComment( "/proc/meminfo" ); |
|
AddFileToComment( "/proc/self/status" ); |
|
Steam_SetMiniDumpComment(); |
|
|
|
// Useful, but really big, so disable for now. |
|
//$ AddFileToComment( "/proc/self/maps" ); |
|
} |
|
#endif |
|
} |
|
} |
|
DO_CATCH |
|
{ |
|
// Oh oh |
|
} |
|
|
|
#ifdef IS_WINDOWS_PC |
|
_set_se_translator( curfilter ); |
|
#elif defined( POSIX ) |
|
if ( bRealCrash ) |
|
signal( SIGSEGV, curfilter ); |
|
#endif |
|
} |
|
|
|
void BuildCommentMemStatus() |
|
{ |
|
#ifdef _WIN32 |
|
const double MbDiv = 1024.0 * 1024.0; |
|
|
|
MEMORYSTATUSEX memStat; |
|
ZeroMemory( &memStat, sizeof( MEMORYSTATUSEX ) ); |
|
memStat.dwLength = sizeof( MEMORYSTATUSEX ); |
|
|
|
if ( GlobalMemoryStatusEx( &memStat ) ) |
|
{ |
|
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", |
|
memStat.dwMemoryLoad, |
|
(double)memStat.ullTotalPhys / MbDiv, |
|
(double)memStat.ullAvailPhys / MbDiv, |
|
(double)memStat.ullTotalPageFile / MbDiv, |
|
(double)memStat.ullAvailPageFile / MbDiv, |
|
(double)memStat.ullTotalVirtual / MbDiv, |
|
(double)memStat.ullAvailVirtual / MbDiv, |
|
(double)memStat.ullAvailExtendedVirtual / MbDiv); |
|
} |
|
|
|
HINSTANCE hInst = LoadLibrary( "Psapi.dll" ); |
|
if ( hInst ) |
|
{ |
|
typedef BOOL (WINAPI *GetProcessMemoryInfoFn)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD); |
|
GetProcessMemoryInfoFn fn = (GetProcessMemoryInfoFn)GetProcAddress( hInst, "GetProcessMemoryInfo" ); |
|
if ( fn ) |
|
{ |
|
PROCESS_MEMORY_COUNTERS counters; |
|
|
|
ZeroMemory( &counters, sizeof( PROCESS_MEMORY_COUNTERS ) ); |
|
counters.cb = sizeof( PROCESS_MEMORY_COUNTERS ); |
|
|
|
if ( fn( GetCurrentProcess(), &counters, sizeof( PROCESS_MEMORY_COUNTERS ) ) ) |
|
{ |
|
CommentPrintf( "\nProcess Memory\nWorkingSetSize Mb(%.2f)\nQuotaPagedPoolUsage Mb(%.2f)\nQuotaNonPagedPoolUsage: Mb(%.2f)\nPagefileUsage: Mb(%.2f)\n", |
|
(double)counters.WorkingSetSize / MbDiv, |
|
(double)counters.QuotaPagedPoolUsage / MbDiv, |
|
(double)counters.QuotaNonPagedPoolUsage / MbDiv, |
|
(double)counters.PagefileUsage / MbDiv ); |
|
} |
|
} |
|
|
|
FreeLibrary( hInst ); |
|
} |
|
|
|
#elif defined( OSX ) |
|
|
|
static const struct |
|
{ |
|
int ctl; |
|
const char *name; |
|
} s_ctl_names[] = |
|
{ |
|
#define _XTAG( _x ) { _x, #_x } |
|
_XTAG( HW_PHYSMEM ), |
|
_XTAG( HW_USERMEM ), |
|
_XTAG( HW_MEMSIZE ), |
|
_XTAG( HW_AVAILCPU ), |
|
#undef _XTAG |
|
}; |
|
|
|
for ( size_t i = 0; i < Q_ARRAYSIZE( s_ctl_names ); i++ ) |
|
{ |
|
uint64_t val = 0; |
|
size_t len = sizeof( val ); |
|
int mib[] = { CTL_HW, s_ctl_names[ i ].ctl }; |
|
|
|
if ( sysctl( mib, Q_ARRAYSIZE( mib ), &val, &len, NULL, 0 ) == 0 ) |
|
{ |
|
CommentPrintf( " %s: %" PRIu64 "\n", s_ctl_names[ i ].name, val ); |
|
} |
|
} |
|
|
|
#endif |
|
} |
|
|
|
void BuildCommentExtended() |
|
{ |
|
try |
|
{ |
|
CommentCat( "\nConVars (non-default)\n\n" ); |
|
CommentPrintf( "%s %s %s\n", "var", "value", "default" ); |
|
|
|
for ( const ConCommandBase *var = g_pCVar->GetCommands() ; var ; var = var->GetNext()) |
|
{ |
|
if ( var->IsCommand() ) |
|
continue; |
|
|
|
ConVar *pCvar = ( ConVar * )var; |
|
if ( pCvar->IsFlagSet( FCVAR_SERVER_CANNOT_QUERY | FCVAR_PROTECTED ) ) |
|
continue; |
|
|
|
if ( !(pCvar->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) ) |
|
{ |
|
char var1[ MAX_OSPATH ]; |
|
char var2[ MAX_OSPATH ]; |
|
|
|
Q_strncpy( var1, Host_CleanupConVarStringValue( pCvar->GetString() ), sizeof( var1 ) ); |
|
Q_strncpy( var2, Host_CleanupConVarStringValue( pCvar->GetDefault() ), sizeof( var2 ) ); |
|
|
|
if ( !Q_stricmp( var1, var2 ) ) |
|
continue; |
|
} |
|
else |
|
{ |
|
if ( pCvar->GetFloat() == Q_atof( pCvar->GetDefault() ) ) |
|
continue; |
|
} |
|
|
|
if ( !(pCvar->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) ) |
|
CommentPrintf( "%s '%s' '%s'\n", pCvar->GetName(), Host_CleanupConVarStringValue( pCvar->GetString() ), pCvar->GetDefault() ); |
|
else |
|
CommentPrintf( "%s '%f' '%f'\n", pCvar->GetName(), pCvar->GetFloat(), Q_atof( pCvar->GetDefault() ) ); |
|
} |
|
|
|
CommentCat( "\nConsole History (reversed)\n\n" ); |
|
|
|
// Get console |
|
int len = V_strlen( m_errorText ); |
|
if ( len < sizeof( m_errorText ) ) |
|
{ |
|
GetSpew( m_errorText + len, sizeof( m_errorText ) - len - 1 ); |
|
m_errorText[ sizeof( m_errorText ) - 1 ] = 0; |
|
} |
|
} |
|
catch ( ... ) |
|
{ |
|
CommentCat( "Exception thrown building console/convar history.\n" ); |
|
} |
|
} |
|
|
|
#if defined( LINUX ) |
|
|
|
void AddFileToComment( const char *filename ) |
|
{ |
|
CommentPrintf( "\n%s:\n", filename ); |
|
|
|
int nStart = Q_strlen( m_errorText ); |
|
int nMaxLen = sizeof( m_errorText ) - nStart - 1; |
|
|
|
if ( nMaxLen > 0 ) |
|
{ |
|
FILE *fh = fopen( filename, "r" ); |
|
|
|
if ( fh ) |
|
{ |
|
size_t ret = fread( m_errorText + nStart, 1, nMaxLen, fh ); |
|
fclose( fh ); |
|
|
|
// Replace tab characters with spaces. |
|
for ( size_t i = 0; i < ret; i++ ) |
|
{ |
|
if ( m_errorText[ nStart + i ] == '\t' ) |
|
m_errorText[ nStart + i ] = ' '; |
|
} |
|
} |
|
|
|
// Entire buffer should have been zeroed out, but just super sure... |
|
m_errorText[ sizeof( m_errorText ) - 1 ] = 0; |
|
} |
|
} |
|
|
|
#endif // LINUX |
|
|
|
public: |
|
char m_errorText[ _SIZE ]; |
|
bool m_bIsDedicatedServer; |
|
}; |
|
|
|
#if defined( _X360 ) |
|
static CErrorText<3500> errorText; |
|
#else |
|
static CErrorText<95000> errorText; |
|
#endif |
|
|
|
void BuildMinidumpComment( char const *pchSysErrorText, bool bRealCrash ) |
|
{ |
|
#if !defined(NO_STEAM) |
|
/* |
|
// Uncomment this code if you are testing max minidump comment length issues |
|
// It allows you to asked for a dummy comment of a certain length |
|
int nCommentLength = CommandLine()->ParmValue( "-commentlen", 0 ); |
|
if ( nCommentLength > 0 ) |
|
{ |
|
nCommentLength = MIN( nCommentLength, 128*1024 ); |
|
char *cbuf = new char[ nCommentLength + 1 ]; |
|
for ( int i = 0; i < nCommentLength; ++i ) |
|
{ |
|
cbuf[ i ] = (char)('0' + (i % 10)); |
|
} |
|
cbuf[ nCommentLength ] = 0; |
|
SteamAPI_SetMiniDumpComment( cbuf ); |
|
delete[] cbuf; |
|
return; |
|
} |
|
*/ |
|
errorText.BuildComment( pchSysErrorText, bRealCrash ); |
|
#endif |
|
} |
|
|
|
#if defined( POSIX ) |
|
|
|
static void PosixPreMinidumpCallback( void *context ) |
|
{ |
|
BuildMinidumpComment( NULL, true ); |
|
} |
|
|
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Attempt to initialize appid/steam.inf/minidump information. May only return partial information if called |
|
// before Filesystem is ready. |
|
// |
|
// The desire is to be able to call this ASAP to init basic minidump and AppID info, then re-call later on when |
|
// the filesystem is setup, so full version # information and such can be propagated to the minidump system. |
|
// (Currently, SDK mods will generally only have partial information prior to filesystem init) |
|
//----------------------------------------------------------------------------- |
|
// steam.inf keys. |
|
#define VERSION_KEY "PatchVersion=" |
|
#define PRODUCT_KEY "ProductName=" |
|
#define SERVER_VERSION_KEY "ServerVersion=" |
|
#define APPID_KEY "AppID=" |
|
#define SERVER_APPID_KEY "ServerAppID=" |
|
enum eSteamInfoInit |
|
{ |
|
eSteamInfo_Uninitialized, |
|
eSteamInfo_Partial, |
|
eSteamInfo_Initialized |
|
}; |
|
static eSteamInfoInit Sys_TryInitSteamInfo( void *pvAPI, SteamInfVersionInfo_t& VerInfo, const char *pchMod, const char *pchBaseDir, bool bDedicated ) |
|
{ |
|
static eSteamInfoInit initState = eSteamInfo_Uninitialized; |
|
|
|
eSteamInfoInit previousInitState = initState; |
|
|
|
// |
|
// |
|
// Initialize with some defaults. |
|
VerInfo.ClientVersion = 0; |
|
VerInfo.ServerVersion = 0; |
|
V_strcpy_safe( VerInfo.szVersionString, "valve" ); |
|
V_strcpy_safe( VerInfo.szProductString, "1.0.1.0" ); |
|
VerInfo.AppID = k_uAppIdInvalid; |
|
VerInfo.ServerAppID = k_uAppIdInvalid; |
|
|
|
// Filesystem may or may not be up |
|
CUtlBuffer infBuf; |
|
bool bFoundInf = false; |
|
if ( g_pFileSystem ) |
|
{ |
|
FileHandle_t fh; |
|
fh = g_pFileSystem->Open( "steam.inf", "rb", "GAME" ); |
|
bFoundInf = fh && g_pFileSystem->ReadToBuffer( fh, infBuf ); |
|
} |
|
|
|
if ( !bFoundInf ) |
|
{ |
|
// We may try to load the steam.inf BEFORE we turn on the filesystem, so use raw filesystem API's here. |
|
char szFullPath[ MAX_PATH ] = { 0 }; |
|
char szModSteamInfPath[ MAX_PATH ] = { 0 }; |
|
V_ComposeFileName( pchMod, "steam.inf", szModSteamInfPath, sizeof( szModSteamInfPath ) ); |
|
V_MakeAbsolutePath( szFullPath, sizeof( szFullPath ), szModSteamInfPath, pchBaseDir ); |
|
|
|
// Try opening steam.inf |
|
FILE *fp = fopen( szFullPath, "rb" ); |
|
if ( fp ) |
|
{ |
|
// Read steam.inf data. |
|
fseek( fp, 0, SEEK_END ); |
|
size_t bufsize = ftell( fp ); |
|
fseek( fp, 0, SEEK_SET ); |
|
|
|
infBuf.EnsureCapacity( bufsize + 1 ); |
|
|
|
size_t iBytesRead = fread( infBuf.Base(), 1, bufsize, fp ); |
|
((char *)infBuf.Base())[iBytesRead] = 0; |
|
infBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, iBytesRead + 1 ); |
|
fclose( fp ); |
|
|
|
bFoundInf = ( iBytesRead == bufsize ); |
|
} |
|
} |
|
|
|
if ( bFoundInf ) |
|
{ |
|
const char *pbuf = (const char*)infBuf.Base(); |
|
while ( 1 ) |
|
{ |
|
pbuf = COM_Parse( pbuf ); |
|
if ( !pbuf || !com_token[ 0 ] ) |
|
break; |
|
|
|
if ( !Q_strnicmp( com_token, VERSION_KEY, Q_strlen( VERSION_KEY ) ) ) |
|
{ |
|
V_strcpy_safe( VerInfo.szVersionString, com_token + Q_strlen( VERSION_KEY ) ); |
|
VerInfo.ClientVersion = atoi( VerInfo.szVersionString ); |
|
} |
|
else if ( !Q_strnicmp( com_token, PRODUCT_KEY, Q_strlen( PRODUCT_KEY ) ) ) |
|
{ |
|
V_strcpy_safe( VerInfo.szProductString, com_token + Q_strlen( PRODUCT_KEY ) ); |
|
} |
|
else if ( !Q_strnicmp( com_token, SERVER_VERSION_KEY, Q_strlen( SERVER_VERSION_KEY ) ) ) |
|
{ |
|
VerInfo.ServerVersion = atoi( com_token + Q_strlen( SERVER_VERSION_KEY ) ); |
|
} |
|
else if ( !Q_strnicmp( com_token, APPID_KEY, Q_strlen( APPID_KEY ) ) ) |
|
{ |
|
VerInfo.AppID = atoi( com_token + Q_strlen( APPID_KEY ) ); |
|
} |
|
else if ( !Q_strnicmp( com_token, SERVER_APPID_KEY, Q_strlen( SERVER_APPID_KEY ) ) ) |
|
{ |
|
VerInfo.ServerAppID = atoi( com_token + Q_strlen( SERVER_APPID_KEY ) ); |
|
} |
|
} |
|
|
|
// 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 |
|
// if it doesn't at least have an AppID |
|
initState = ( VerInfo.AppID != k_uAppIdInvalid ) ? eSteamInfo_Initialized : eSteamInfo_Partial; |
|
} |
|
else if ( !bDedicated ) |
|
{ |
|
// Opening steam.inf failed - try to open gameinfo.txt and read in just SteamAppId from that. |
|
// (gameinfo.txt lacks the dedicated server steamid, so we'll just have to live until filesystem init to setup |
|
// breakpad there when we hit this case) |
|
char szModGameinfoPath[ MAX_PATH ] = { 0 }; |
|
char szFullPath[ MAX_PATH ] = { 0 }; |
|
V_ComposeFileName( pchMod, "gameinfo.txt", szModGameinfoPath, sizeof( szModGameinfoPath ) ); |
|
V_MakeAbsolutePath( szFullPath, sizeof( szFullPath ), szModGameinfoPath, pchBaseDir ); |
|
|
|
// Try opening gameinfo.txt |
|
FILE *fp = fopen( szFullPath, "rb" ); |
|
if( fp ) |
|
{ |
|
fseek( fp, 0, SEEK_END ); |
|
size_t bufsize = ftell( fp ); |
|
fseek( fp, 0, SEEK_SET ); |
|
|
|
char *buffer = ( char * )_alloca( bufsize + 1 ); |
|
|
|
size_t iBytesRead = fread( buffer, 1, bufsize, fp ); |
|
buffer[ iBytesRead ] = 0; |
|
fclose( fp ); |
|
|
|
KeyValuesAD pkvGameInfo( "gameinfo" ); |
|
if ( pkvGameInfo->LoadFromBuffer( "gameinfo.txt", buffer ) ) |
|
{ |
|
VerInfo.AppID = (AppId_t)pkvGameInfo->GetInt( "FileSystem/SteamAppId", k_uAppIdInvalid ); |
|
} |
|
} |
|
|
|
initState = eSteamInfo_Partial; |
|
} |
|
|
|
// In partial state the ServerAppID might be unknown, but if we found the full steam.inf and it's not set, it shares AppID. |
|
if ( initState == eSteamInfo_Initialized && VerInfo.ServerAppID == k_uAppIdInvalid ) |
|
VerInfo.ServerAppID = VerInfo.AppID; |
|
|
|
#if !defined(_X360) |
|
if ( VerInfo.AppID ) |
|
{ |
|
// steamclient.dll doesn't know about steam.inf files in mod folder, |
|
// it accepts a steam_appid.txt in the root directory if the game is |
|
// not started through Steam. So we create one there containing the |
|
// current AppID |
|
FILE *fh = fopen( "steam_appid.txt", "wb" ); |
|
if ( fh ) |
|
{ |
|
CFmtStrN< 128 > strAppID( "%u\n", VerInfo.AppID ); |
|
|
|
fwrite( strAppID.Get(), strAppID.Length() + 1, 1, fh ); |
|
fclose( fh ); |
|
} |
|
} |
|
#endif // !_X360 |
|
|
|
// |
|
// Update minidump info if we have more information than before |
|
// |
|
|
|
#ifndef NO_STEAM |
|
// If -nobreakpad was specified or we found metamod or sourcemod, don't register breakpad. |
|
bool bUseBreakpad = !CommandLine()->FindParm( "-nobreakpad" ) && ( !bDedicated || !IsSourceModLoaded() ); |
|
AppId_t BreakpadAppId = bDedicated ? VerInfo.ServerAppID : VerInfo.AppID; |
|
Assert( BreakpadAppId != k_uAppIdInvalid || initState < eSteamInfo_Initialized ); |
|
if ( BreakpadAppId != k_uAppIdInvalid && initState > previousInitState && bUseBreakpad ) |
|
{ |
|
void *pvMiniDumpContext = NULL; |
|
PFNPreMinidumpCallback pfnPreMinidumpCallback = NULL; |
|
bool bFullMemoryDump = !bDedicated && IsWindows() && CommandLine()->FindParm( "-full_memory_dumps" ); |
|
|
|
#if defined( POSIX ) |
|
// On Windows we're relying on the try/except to build the minidump comment. On Linux, we don't have that |
|
// so we need to register the minidumpcallback handler here. |
|
pvMiniDumpContext = pvAPI; |
|
pfnPreMinidumpCallback = PosixPreMinidumpCallback; |
|
#endif |
|
|
|
CFmtStrN<128> pchVersion( "%d", build_number() ); |
|
Msg( "Using Breakpad minidump system. Version: %s AppID: %u\n", pchVersion.Get(), BreakpadAppId ); |
|
|
|
// We can filter various crash dumps differently in the Socorro backend code: |
|
// Steam/min/web/crash_reporter/socorro/scripts/config/collectorconfig.py |
|
SteamAPI_SetBreakpadAppID( BreakpadAppId ); |
|
SteamAPI_UseBreakpadCrashHandler( pchVersion, __DATE__, __TIME__, bFullMemoryDump, pvMiniDumpContext, pfnPreMinidumpCallback ); |
|
|
|
// Tell errorText class if this is dedicated server. |
|
errorText.m_bIsDedicatedServer = bDedicated; |
|
} |
|
#endif // NO_STEAM |
|
|
|
MinidumpUserStreamInfoSetHeader( "%sLaunching \"%s\"\n", ( bDedicated ? "DedicatedServerAPI " : "" ), CommandLine()->GetCmdLine() ); |
|
|
|
|
|
return initState; |
|
} |
|
|
|
#ifndef SWDS |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Main engine interface exposed to launcher |
|
// |
|
//----------------------------------------------------------------------------- |
|
class CEngineAPI : public CTier3AppSystem< IEngineAPI > |
|
{ |
|
typedef CTier3AppSystem< IEngineAPI > BaseClass; |
|
|
|
public: |
|
virtual bool Connect( CreateInterfaceFn factory ); |
|
virtual void Disconnect(); |
|
virtual void *QueryInterface( const char *pInterfaceName ); |
|
virtual InitReturnVal_t Init(); |
|
virtual void Shutdown(); |
|
|
|
// This function must be called before init |
|
virtual void SetStartupInfo( StartupInfo_t &info ); |
|
|
|
virtual int Run( ); |
|
|
|
// Sets the engine to run in a particular editor window |
|
virtual void SetEngineWindow( void *hWnd ); |
|
|
|
// Posts a console command |
|
virtual void PostConsoleCommand( const char *pConsoleCommand ); |
|
|
|
// Are we running the simulation? |
|
virtual bool IsRunningSimulation( ) const; |
|
|
|
// Start/stop running the simulation |
|
virtual void ActivateSimulation( bool bActive ); |
|
|
|
// Reset the map we're on |
|
virtual void SetMap( const char *pMapName ); |
|
|
|
bool MainLoop(); |
|
|
|
int RunListenServer(); |
|
|
|
private: |
|
|
|
// Hooks a particular mod up to the registry |
|
void SetRegistryMod( const char *pModName ); |
|
|
|
// One-time setup, based on the initially selected mod |
|
// FIXME: This should move into the launcher! |
|
bool OnStartup( void *pInstance, const char *pStartupModName ); |
|
void OnShutdown(); |
|
|
|
// Initialization, shutdown of a mod. |
|
bool ModInit( const char *pModName, const char *pGameDir ); |
|
void ModShutdown(); |
|
|
|
// Initializes, shuts down the registry |
|
bool InitRegistry( const char *pModName ); |
|
void ShutdownRegistry(); |
|
|
|
// Handles there being an error setting up the video mode |
|
InitReturnVal_t HandleSetModeError(); |
|
|
|
// Initializes, shuts down VR |
|
bool InitVR(); |
|
void ShutdownVR(); |
|
|
|
// Purpose: Message pump when running stand-alone |
|
void PumpMessages(); |
|
|
|
// Purpose: Message pump when running with the editor |
|
void PumpMessagesEditMode( bool &bIdle, long &lIdleCount ); |
|
|
|
// Activate/deactivates edit mode shaders |
|
void ActivateEditModeShaders( bool bActive ); |
|
|
|
private: |
|
void *m_hEditorHWnd; |
|
bool m_bRunningSimulation; |
|
bool m_bSupportsVR; |
|
StartupInfo_t m_StartupInfo; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton interface |
|
//----------------------------------------------------------------------------- |
|
static CEngineAPI s_EngineAPI; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineAPI, IEngineAPI, VENGINE_LAUNCHER_API_VERSION, s_EngineAPI ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Connect, disconnect |
|
//----------------------------------------------------------------------------- |
|
bool CEngineAPI::Connect( CreateInterfaceFn factory ) |
|
{ |
|
// Store off the app system factory... |
|
g_AppSystemFactory = factory; |
|
|
|
if ( !BaseClass::Connect( factory ) ) |
|
return false; |
|
|
|
g_pFileSystem = g_pFullFileSystem; |
|
if ( !g_pFileSystem ) |
|
return false; |
|
|
|
g_pFileSystem->SetWarningFunc( Warning ); |
|
|
|
if ( !Shader_Connect( true ) ) |
|
return false; |
|
|
|
g_pPhysics = (IPhysics*)factory( VPHYSICS_INTERFACE_VERSION, NULL ); |
|
|
|
if ( !g_pStudioRender || !g_pDataCache || !g_pPhysics || !g_pMDLCache || !g_pMatSystemSurface || !g_pInputSystem /* || !g_pVideo */ ) |
|
{ |
|
Warning( "Engine wasn't able to acquire required interfaces!\n" ); |
|
return false; |
|
} |
|
|
|
if (!g_pStudioRender) |
|
{ |
|
Sys_Error( "Unable to init studio render system version %s\n", STUDIO_RENDER_INTERFACE_VERSION ); |
|
return false; |
|
} |
|
|
|
g_pHammer = (IHammer*)factory( INTERFACEVERSION_HAMMER, NULL ); |
|
|
|
#if defined( USE_SDL ) |
|
g_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL ); |
|
#endif |
|
|
|
ConnectMDLCacheNotify(); |
|
|
|
return true; |
|
} |
|
|
|
void CEngineAPI::Disconnect() |
|
{ |
|
DisconnectMDLCacheNotify(); |
|
|
|
#if !defined( SWDS ) |
|
TRACESHUTDOWN( Steam3Client().Shutdown() ); |
|
#endif |
|
|
|
g_pHammer = NULL; |
|
g_pPhysics = NULL; |
|
|
|
Shader_Disconnect(); |
|
|
|
g_pFileSystem = NULL; |
|
|
|
BaseClass::Disconnect(); |
|
|
|
g_AppSystemFactory = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Query interface |
|
//----------------------------------------------------------------------------- |
|
void *CEngineAPI::QueryInterface( const char *pInterfaceName ) |
|
{ |
|
// Loading the engine DLL mounts *all* engine interfaces |
|
CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary |
|
return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing. |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets startup info |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::SetStartupInfo( StartupInfo_t &info ) |
|
{ |
|
// Setup and write out steam_appid.txt before we launch |
|
bool bDedicated = false; // Dedicated comes through CDedicatedServerAPI |
|
eSteamInfoInit steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated ); |
|
|
|
g_bTextMode = info.m_bTextMode; |
|
|
|
// Set up the engineparms_t which contains global information about the mod |
|
host_parms.basedir = const_cast<char*>( info.m_pBaseDirectory ); |
|
|
|
// Copy off all the startup info |
|
m_StartupInfo = info; |
|
|
|
#if !defined( SWDS ) |
|
// turn on the Steam3 API early so we can query app data up front |
|
TRACEINIT( Steam3Client().Activate(), Steam3Client().Shutdown() ); |
|
#endif |
|
|
|
// Needs to be done prior to init material system config |
|
TRACEINIT( COM_InitFilesystem( m_StartupInfo.m_pInitialMod ), COM_ShutdownFileSystem() ); |
|
|
|
if ( steamInfo != eSteamInfo_Initialized ) |
|
{ |
|
// Try again with filesystem available. This is commonly needed for SDK mods which need the filesystem to find |
|
// their steam.inf, due to mounting SDK search paths. |
|
steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated ); |
|
Assert( steamInfo == eSteamInfo_Initialized ); |
|
if ( steamInfo != eSteamInfo_Initialized ) |
|
{ |
|
Warning( "Failed to find steam.inf or equivalent steam info. May not have proper information to connect to Steam.\n" ); |
|
} |
|
} |
|
|
|
m_bSupportsVR = false; |
|
if ( IsPC() ) |
|
{ |
|
KeyValues *modinfo = new KeyValues("ModInfo"); |
|
if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) ) |
|
{ |
|
// Enable file tracking - client always does this in case it connects to a pure server. |
|
// server only does this if sv_pure is set |
|
// If it's not singleplayer_only |
|
if ( V_stricmp( modinfo->GetString("type", "singleplayer_only"), "singleplayer_only") == 0 ) |
|
{ |
|
DevMsg( "Disabling whitelist file tracking in filesystem...\n" ); |
|
g_pFileSystem->EnableWhitelistFileTracking( false, false, false ); |
|
} |
|
else |
|
{ |
|
DevMsg( "Enabling whitelist file tracking in filesystem...\n" ); |
|
g_pFileSystem->EnableWhitelistFileTracking( true, false, false ); |
|
} |
|
|
|
m_bSupportsVR = modinfo->GetInt( "supportsvr" ) > 0 && CommandLine()->CheckParm( "-vr" ); |
|
if ( m_bSupportsVR ) |
|
{ |
|
// This also has to happen before CreateGameWindow to know where to put |
|
// the window and how big to make it |
|
if ( InitVR() ) |
|
{ |
|
if ( Steam3Client().SteamUtils() ) |
|
{ |
|
if ( Steam3Client().SteamUtils()->IsSteamRunningInVR() && g_pSourceVR->IsHmdConnected() ) |
|
{ |
|
int nForceVRAdapterIndex = g_pSourceVR->GetVRModeAdapter(); |
|
materials->SetAdapter( nForceVRAdapterIndex, 0 ); |
|
|
|
g_pSourceVR->SetShouldForceVRMode(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
} |
|
modinfo->deleteThis(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Init, shutdown |
|
//----------------------------------------------------------------------------- |
|
InitReturnVal_t CEngineAPI::Init() |
|
{ |
|
if ( CommandLine()->FindParm( "-sv_benchmark" ) != 0 ) |
|
{ |
|
Plat_SetBenchmarkMode( true ); |
|
} |
|
|
|
InitReturnVal_t nRetVal = BaseClass::Init(); |
|
if ( nRetVal != INIT_OK ) |
|
return nRetVal; |
|
|
|
m_bRunningSimulation = false; |
|
|
|
// Initialize the FPU control word |
|
#if defined(WIN32) && !defined( SWDS ) && !defined( _X360 ) && !defined (__arm__) && !defined(PLATFORM_WINDOWS_PC64) |
|
_asm |
|
{ |
|
fninit |
|
} |
|
#endif |
|
|
|
SetupFPUControlWord(); |
|
|
|
// This creates the videomode singleton object, it doesn't depend on the registry |
|
VideoMode_Create(); |
|
|
|
// Initialize the editor hwnd to render into |
|
m_hEditorHWnd = NULL; |
|
|
|
// One-time setup |
|
// FIXME: OnStartup + OnShutdown should be removed + moved into the launcher |
|
// or the launcher code should be merged into the engine into the code in OnStartup/OnShutdown |
|
if ( !OnStartup( m_StartupInfo.m_pInstance, m_StartupInfo.m_pInitialMod ) ) |
|
{ |
|
return HandleSetModeError(); |
|
} |
|
|
|
return INIT_OK; |
|
} |
|
|
|
void CEngineAPI::Shutdown() |
|
{ |
|
VideoMode_Destroy(); |
|
BaseClass::Shutdown(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the engine to run in a particular editor window |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::SetEngineWindow( void *hWnd ) |
|
{ |
|
if ( !InEditMode() ) |
|
return; |
|
|
|
// Detach input from the previous editor window |
|
game->InputDetachFromGameWindow(); |
|
|
|
m_hEditorHWnd = hWnd; |
|
videomode->SetGameWindow( m_hEditorHWnd ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Posts a console command |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::PostConsoleCommand( const char *pCommand ) |
|
{ |
|
Cbuf_AddText( pCommand ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Is the engine currently rinning? |
|
//----------------------------------------------------------------------------- |
|
bool CEngineAPI::IsRunningSimulation() const |
|
{ |
|
return (eng->GetState() == IEngine::DLL_ACTIVE); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Reset the map we're on |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::SetMap( const char *pMapName ) |
|
{ |
|
// if ( !Q_stricmp( sv.mapname, pMapName ) ) |
|
// return; |
|
|
|
char buf[MAX_PATH]; |
|
Q_snprintf( buf, MAX_PATH, "map %s", pMapName ); |
|
Cbuf_AddText( buf ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Start/stop running the simulation |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::ActivateSimulation( bool bActive ) |
|
{ |
|
// FIXME: Not sure what will happen in this case |
|
if ( ( eng->GetState() != IEngine::DLL_ACTIVE ) && |
|
( eng->GetState() != IEngine::DLL_PAUSED ) ) |
|
{ |
|
return; |
|
} |
|
|
|
bool bCurrentlyActive = (eng->GetState() != IEngine::DLL_PAUSED); |
|
if ( bActive == bCurrentlyActive ) |
|
return; |
|
|
|
// FIXME: Should attachment/detachment be part of the state machine in IEngine? |
|
if ( !bActive ) |
|
{ |
|
eng->SetNextState( IEngine::DLL_PAUSED ); |
|
|
|
// Detach input from the previous editor window |
|
game->InputDetachFromGameWindow(); |
|
} |
|
else |
|
{ |
|
eng->SetNextState( IEngine::DLL_ACTIVE ); |
|
|
|
// Start accepting input from the new window |
|
// FIXME: What if the attachment fails? |
|
game->InputAttachToGameWindow(); |
|
} |
|
} |
|
|
|
static void MoveConsoleWindowToFront() |
|
{ |
|
#ifdef _WIN32 |
|
// Move the window to the front. |
|
HINSTANCE hInst = LoadLibrary( "kernel32.dll" ); |
|
if ( hInst ) |
|
{ |
|
typedef HWND (*GetConsoleWindowFn)(); |
|
GetConsoleWindowFn fn = (GetConsoleWindowFn)GetProcAddress( hInst, "GetConsoleWindow" ); |
|
if ( fn ) |
|
{ |
|
HWND hwnd = fn(); |
|
ShowWindow( hwnd, SW_SHOW ); |
|
UpdateWindow( hwnd ); |
|
SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW ); |
|
} |
|
FreeLibrary( hInst ); |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Message pump when running stand-alone |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::PumpMessages() |
|
{ |
|
// This message pumping happens in SDL if SDL is enabled. |
|
#if defined( PLATFORM_WINDOWS ) && !defined( USE_SDL ) |
|
MSG msg; |
|
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) |
|
{ |
|
TranslateMessage( &msg ); |
|
DispatchMessage( &msg ); |
|
} |
|
#endif |
|
|
|
#if defined( USE_SDL ) |
|
g_pLauncherMgr->PumpWindowsMessageLoop(); |
|
#endif |
|
|
|
// Get input from attached devices |
|
g_pInputSystem->PollInputState(); |
|
|
|
if ( IsX360() ) |
|
{ |
|
// handle Xbox system messages |
|
XBX_ProcessEvents(); |
|
} |
|
|
|
// NOTE: Under some implementations of Win9x, |
|
// dispatching messages can cause the FPU control word to change |
|
if ( IsPC() ) |
|
{ |
|
SetupFPUControlWord(); |
|
} |
|
|
|
game->DispatchAllStoredGameMessages(); |
|
|
|
if ( IsPC() ) |
|
{ |
|
static bool s_bFirstRun = true; |
|
if ( s_bFirstRun ) |
|
{ |
|
s_bFirstRun = false; |
|
MoveConsoleWindowToFront(); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Message pump when running stand-alone |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::PumpMessagesEditMode( bool &bIdle, long &lIdleCount ) |
|
{ |
|
|
|
if ( bIdle && !g_pHammer->HammerOnIdle( lIdleCount++ ) ) |
|
{ |
|
bIdle = false; |
|
} |
|
|
|
// Get input from attached devices |
|
g_pInputSystem->PollInputState(); |
|
|
|
#ifdef WIN32 |
|
MSG msg; |
|
while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) |
|
{ |
|
if ( msg.message == WM_QUIT ) |
|
{ |
|
eng->SetQuitting( IEngine::QUIT_TODESKTOP ); |
|
break; |
|
} |
|
|
|
if ( !g_pHammer->HammerPreTranslateMessage(&msg) ) |
|
{ |
|
TranslateMessage(&msg); |
|
DispatchMessage(&msg); |
|
} |
|
|
|
// Reset idle state after pumping idle message. |
|
if ( g_pHammer->HammerIsIdleMessage(&msg) ) |
|
{ |
|
bIdle = true; |
|
lIdleCount = 0; |
|
} |
|
} |
|
#elif defined( USE_SDL ) |
|
Error( "Not supported" ); |
|
#else |
|
#error |
|
#endif |
|
|
|
|
|
// NOTE: Under some implementations of Win9x, |
|
// dispatching messages can cause the FPU control word to change |
|
SetupFPUControlWord(); |
|
|
|
game->DispatchAllStoredGameMessages(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Activate/deactivates edit mode shaders |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::ActivateEditModeShaders( bool bActive ) |
|
{ |
|
if ( InEditMode() && ( g_pMaterialSystemConfig->bEditMode != bActive ) ) |
|
{ |
|
MaterialSystem_Config_t config = *g_pMaterialSystemConfig; |
|
config.bEditMode = bActive; |
|
OverrideMaterialSystemConfig( config ); |
|
} |
|
} |
|
|
|
|
|
#ifdef GPROFILER |
|
static bool g_gprofiling = false; |
|
|
|
CON_COMMAND( gprofilerstart, "Starts the gperftools profiler recording to the specified file." ) |
|
{ |
|
if ( g_gprofiling ) |
|
{ |
|
Msg( "Profiling is already started.\n" ); |
|
return; |
|
} |
|
|
|
char buffer[500]; |
|
const char* profname = buffer; |
|
if ( args.ArgC() < 2 ) |
|
{ |
|
static const char *s_pszHomeDir = getenv("HOME"); |
|
if ( !s_pszHomeDir ) |
|
{ |
|
Msg( "Syntax: gprofile <outputfilename>\n" ); |
|
return; |
|
} |
|
|
|
// Use the current date and time to create a unique file name.time_t t = time(NULL); |
|
time_t t = time(NULL); |
|
struct tm tm = *localtime(&t); |
|
|
|
V_sprintf_safe( buffer, "%s/valveprofile_%4d_%02d_%02d_%02d.%02d.%02d.prof", s_pszHomeDir, |
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ); |
|
// profname already points to buffer. |
|
} |
|
else |
|
{ |
|
profname = args[1]; |
|
} |
|
|
|
int result = ProfilerStart( profname ); |
|
if ( result ) |
|
{ |
|
Msg( "Profiling started successfully. Recording to %s. Stop profiling with gprofilerstop.\n", profname ); |
|
g_gprofiling = true; |
|
} |
|
else |
|
{ |
|
Msg( "Profiling to %s failed to start - errno = %d.\n", profname, errno ); |
|
} |
|
} |
|
|
|
CON_COMMAND( gprofilerstop, "Stops the gperftools profiler." ) |
|
{ |
|
if ( g_gprofiling ) |
|
{ |
|
ProfilerStop(); |
|
Msg( "Stopped profiling.\n" ); |
|
g_gprofiling = false; |
|
} |
|
} |
|
#endif |
|
|
|
|
|
void StopGProfiler() |
|
{ |
|
#ifdef GPROFILER |
|
gprofilerstop( CCommand() ); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Message pump |
|
//----------------------------------------------------------------------------- |
|
bool CEngineAPI::MainLoop() |
|
{ |
|
bool bIdle = true; |
|
long lIdleCount = 0; |
|
|
|
// Main message pump |
|
while ( true ) |
|
{ |
|
// Pump messages unless someone wants to quit |
|
if ( eng->GetQuitting() != IEngine::QUIT_NOTQUITTING ) |
|
{ |
|
// We have to explicitly stop the profiler since otherwise symbol |
|
// resolution doesn't work correctly. |
|
StopGProfiler(); |
|
if ( eng->GetQuitting() != IEngine::QUIT_TODESKTOP ) |
|
return true; |
|
return false; |
|
} |
|
|
|
// Pump the message loop |
|
if ( !InEditMode() ) |
|
{ |
|
PumpMessages(); |
|
} |
|
else |
|
{ |
|
PumpMessagesEditMode( bIdle, lIdleCount ); |
|
} |
|
|
|
// Run engine frame + hammer frame |
|
if ( !InEditMode() || m_hEditorHWnd ) |
|
{ |
|
VCRSyncToken( "Frame" ); |
|
|
|
// Deactivate edit mode shaders |
|
ActivateEditModeShaders( false ); |
|
|
|
eng->Frame(); |
|
|
|
// Reactivate edit mode shaders (in Edit mode only...) |
|
ActivateEditModeShaders( true ); |
|
} |
|
|
|
if ( InEditMode() ) |
|
{ |
|
g_pHammer->RunFrame(); |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes, shuts down the registry |
|
//----------------------------------------------------------------------------- |
|
bool CEngineAPI::InitRegistry( const char *pModName ) |
|
{ |
|
if ( IsPC() ) |
|
{ |
|
char szRegSubPath[MAX_PATH]; |
|
Q_snprintf( szRegSubPath, sizeof(szRegSubPath), "%s\\%s", "Source", pModName ); |
|
return registry->Init( szRegSubPath ); |
|
} |
|
return true; |
|
} |
|
|
|
void CEngineAPI::ShutdownRegistry( ) |
|
{ |
|
if ( IsPC() ) |
|
{ |
|
registry->Shutdown( ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes, shuts down VR (via sourcevr.dll) |
|
//----------------------------------------------------------------------------- |
|
bool CEngineAPI::InitVR() |
|
{ |
|
if ( m_bSupportsVR ) |
|
{ |
|
g_pSourceVR = (ISourceVirtualReality *)g_AppSystemFactory( SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, NULL ); |
|
if ( g_pSourceVR ) |
|
{ |
|
// make sure that the sourcevr DLL we loaded is secure. If not, don't |
|
// let this client connect to secure servers. |
|
if ( !Host_AllowLoadModule( "sourcevr" DLL_EXT_STRING, "EXECUTABLE_PATH", false ) ) |
|
{ |
|
Warning( "Preventing connections to secure servers because sourcevr.dll is not signed.\n" ); |
|
Host_DisallowSecureServers(); |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
void CEngineAPI::ShutdownVR() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// One-time setup, based on the initially selected mod |
|
// FIXME: This should move into the launcher! |
|
//----------------------------------------------------------------------------- |
|
bool CEngineAPI::OnStartup( void *pInstance, const char *pStartupModName ) |
|
{ |
|
// This fixes a bug on certain machines where the input will |
|
// stop coming in for about 1 second when someone hits a key. |
|
// (true means to disable priority boost) |
|
#ifdef WIN32 |
|
if ( IsPC() ) |
|
{ |
|
SetThreadPriorityBoost( GetCurrentThread(), true ); |
|
} |
|
#endif |
|
|
|
// FIXME: Turn videomode + game into IAppSystems? |
|
|
|
// Try to create the window |
|
COM_TimestampedLog( "game->Init" ); |
|
|
|
// This has to happen before CreateGameWindow to set up the instance |
|
// for use by the code that creates the window |
|
if ( !game->Init( pInstance ) ) |
|
{ |
|
goto onStartupError; |
|
} |
|
|
|
// Try to create the window |
|
COM_TimestampedLog( "videomode->Init" ); |
|
|
|
// This needs to be after Shader_Init and registry->Init |
|
// This way mods can have different default video settings |
|
if ( !videomode->Init( ) ) |
|
{ |
|
goto onStartupShutdownGame; |
|
} |
|
|
|
// We need to access the registry to get various settings (specifically, |
|
// InitMaterialSystemConfig requires it). |
|
if ( !InitRegistry( pStartupModName ) ) |
|
{ |
|
goto onStartupShutdownVideoMode; |
|
} |
|
|
|
materials->ModInit(); |
|
|
|
// Setup the material system config record, CreateGameWindow depends on it |
|
// (when we're running stand-alone) |
|
InitMaterialSystemConfig( InEditMode() ); |
|
|
|
#if defined( _X360 ) |
|
XBX_NotifyCreateListener( XNOTIFY_SYSTEM|XNOTIFY_LIVE|XNOTIFY_XMP ); |
|
#endif |
|
|
|
ShutdownRegistry(); |
|
return true; |
|
|
|
// Various error conditions |
|
onStartupShutdownVideoMode: |
|
videomode->Shutdown(); |
|
|
|
onStartupShutdownGame: |
|
game->Shutdown(); |
|
|
|
onStartupError: |
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// One-time shutdown (shuts down stuff set up in OnStartup) |
|
// FIXME: This should move into the launcher! |
|
//----------------------------------------------------------------------------- |
|
void CEngineAPI::OnShutdown() |
|
{ |
|
if ( videomode ) |
|
{ |
|
videomode->Shutdown(); |
|
} |
|
|
|
ShutdownVR(); |
|
|
|
// Shut down the game |
|
game->Shutdown(); |
|
|
|
materials->ModShutdown(); |
|
TRACESHUTDOWN( COM_ShutdownFileSystem() ); |
|
} |
|
|
|
static bool IsValveMod( const char *pModName ) |
|
{ |
|
// Figure out if we're running a Valve mod or not. |
|
return ( Q_stricmp( GetCurrentMod(), "cstrike" ) == 0 || |
|
Q_stricmp( GetCurrentMod(), "dod" ) == 0 || |
|
Q_stricmp( GetCurrentMod(), "hl1mp" ) == 0 || |
|
Q_stricmp( GetCurrentMod(), "tf" ) == 0 || |
|
Q_stricmp( GetCurrentMod(), "tf_beta" ) == 0 || |
|
Q_stricmp( GetCurrentMod(), "hl2mp" ) == 0 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Initialization, shutdown of a mod. |
|
//----------------------------------------------------------------------------- |
|
bool CEngineAPI::ModInit( const char *pModName, const char *pGameDir ) |
|
{ |
|
// Set up the engineparms_t which contains global information about the mod |
|
host_parms.mod = COM_StringCopy( GetModDirFromPath( pModName ) ); |
|
host_parms.game = COM_StringCopy( pGameDir ); |
|
|
|
// By default, restrict server commands in Valve games and don't restrict them in mods. |
|
cl.m_bRestrictServerCommands = IsValveMod( host_parms.mod ); |
|
cl.m_bRestrictClientCommands = cl.m_bRestrictServerCommands; |
|
|
|
// build the registry path we're going to use for this mod |
|
InitRegistry( pModName ); |
|
|
|
// This sets up the game search path, depends on host_parms |
|
TRACEINIT( MapReslistGenerator_Init(), MapReslistGenerator_Shutdown() ); |
|
#if !defined( _X360 ) |
|
TRACEINIT( DevShotGenerator_Init(), DevShotGenerator_Shutdown() ); |
|
#endif |
|
|
|
// Slam cvars based on mod/config.cfg |
|
Host_ReadPreStartupConfiguration(); |
|
|
|
bool bWindowed = g_pMaterialSystemConfig->Windowed(); |
|
if( g_pMaterialSystemConfig->m_nVRModeAdapter != -1 ) |
|
{ |
|
// at init time we never want to start up full screen |
|
bWindowed = true; |
|
} |
|
|
|
// Create the game window now that we have a search path |
|
// FIXME: Deal with initial window width + height better |
|
if ( !videomode || !videomode->CreateGameWindow( g_pMaterialSystemConfig->m_VideoMode.m_Width, g_pMaterialSystemConfig->m_VideoMode.m_Height, bWindowed ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CEngineAPI::ModShutdown() |
|
{ |
|
COM_StringFree(host_parms.mod); |
|
COM_StringFree(host_parms.game); |
|
|
|
// Stop accepting input from the window |
|
game->InputDetachFromGameWindow(); |
|
|
|
#if !defined( _X360 ) |
|
TRACESHUTDOWN( DevShotGenerator_Shutdown() ); |
|
#endif |
|
TRACESHUTDOWN( MapReslistGenerator_Shutdown() ); |
|
|
|
ShutdownRegistry(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles there being an error setting up the video mode |
|
// Output : Returns true on if the engine should restart, false if it should quit |
|
//----------------------------------------------------------------------------- |
|
InitReturnVal_t CEngineAPI::HandleSetModeError() |
|
{ |
|
// show an error, see if the user wants to restart |
|
if ( CommandLine()->FindParm( "-safe" ) ) |
|
{ |
|
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 ); |
|
return INIT_FAILED; |
|
} |
|
|
|
if ( CommandLine()->FindParm( "-autoconfig" ) ) |
|
{ |
|
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 )) |
|
{ |
|
CommandLine()->AppendParm( "-safe", NULL ); |
|
return (InitReturnVal_t)INIT_RESTART; |
|
} |
|
return INIT_FAILED; |
|
} |
|
|
|
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 ) ) |
|
{ |
|
CommandLine()->AppendParm( "-autoconfig", NULL ); |
|
return (InitReturnVal_t)INIT_RESTART; |
|
} |
|
|
|
return INIT_FAILED; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main loop for non-dedicated servers |
|
//----------------------------------------------------------------------------- |
|
int CEngineAPI::RunListenServer() |
|
{ |
|
// |
|
// NOTE: Systems set up here should depend on the mod |
|
// Systems which are mod-independent should be set up in the launcher or Init() |
|
// |
|
|
|
// Innocent until proven guilty |
|
int nRunResult = RUN_OK; |
|
|
|
// Happens every time we start up and shut down a mod |
|
if ( ModInit( m_StartupInfo.m_pInitialMod, m_StartupInfo.m_pInitialGame ) ) |
|
{ |
|
CModAppSystemGroup modAppSystemGroup( false, m_StartupInfo.m_pParentAppSystemGroup ); |
|
|
|
// Store off the app system factory... |
|
g_AppSystemFactory = modAppSystemGroup.GetFactory(); |
|
|
|
nRunResult = modAppSystemGroup.Run(); |
|
|
|
g_AppSystemFactory = NULL; |
|
|
|
// Shuts down the mod |
|
ModShutdown(); |
|
|
|
// Disconnects from the editor window |
|
videomode->SetGameWindow( NULL ); |
|
} |
|
|
|
// Closes down things that were set up in OnStartup |
|
// FIXME: OnStartup + OnShutdown should be removed + moved into the launcher |
|
// or the launcher code should be merged into the engine into the code in OnStartup/OnShutdown |
|
OnShutdown(); |
|
|
|
return nRunResult; |
|
} |
|
|
|
static void StaticRunListenServer( void *arg ) |
|
{ |
|
*(int *)arg = s_EngineAPI.RunListenServer(); |
|
} |
|
|
|
|
|
|
|
// This function is set as the crash handler for unhandled exceptions and as the minidump |
|
// handler for to be used by all of tier0's crash recording. This function |
|
// adds a game-specific minidump comment and ensures that the SteamAPI function is |
|
// used to save the minidump so that crashes are uploaded. SteamAPI has previously |
|
// been configured to use breakpad by calling SteamAPI_UseBreakpadCrashHandler. |
|
extern "C" void __cdecl WriteSteamMiniDumpWithComment( unsigned int uStructuredExceptionCode, |
|
struct _EXCEPTION_POINTERS * pExceptionInfo, |
|
const char *pszFilenameSuffix ) |
|
{ |
|
// TODO: dynamically set the minidump comment from contextual info about the crash (i.e current VPROF node)? |
|
#if !defined( NO_STEAM ) |
|
|
|
if ( g_bUpdateMinidumpComment ) |
|
{ |
|
BuildMinidumpComment( NULL, true ); |
|
} |
|
|
|
SteamAPI_WriteMiniDump( uStructuredExceptionCode, pExceptionInfo, build_number() ); |
|
// Clear DSound Buffers so the sound doesn't loop while the game shuts down |
|
try |
|
{ |
|
S_ClearBuffer(); |
|
} |
|
catch ( ... ) |
|
{ |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main |
|
//----------------------------------------------------------------------------- |
|
int CEngineAPI::Run() |
|
{ |
|
if ( CommandLine()->FindParm( "-insecure" ) || CommandLine()->FindParm( "-textmode" ) ) |
|
{ |
|
Host_DisallowSecureServers(); |
|
} |
|
|
|
#ifdef _X360 |
|
return RunListenServer(); // don't handle exceptions on 360 (because if we do then minidumps won't work at all) |
|
#elif defined ( _WIN32 ) |
|
// Ensure that we crash when we do something naughty in a callback |
|
// such as a window proc. Otherwise on a 64-bit OS the crashes will be |
|
// silently swallowed. |
|
EnableCrashingOnCrashes(); |
|
|
|
// Set the default minidump handling function. This is necessary so that Steam |
|
// will upload crashes, with comments. |
|
SetMiniDumpFunction( WriteSteamMiniDumpWithComment ); |
|
|
|
// Catch unhandled crashes. A normal __try/__except block will not work across |
|
// the kernel callback boundary, but this does. To be clear, __try/__except |
|
// and try/catch will usually not catch exceptions in a WindowProc or other |
|
// callback that is called from kernel mode because 64-bit Windows cannot handle |
|
// throwing exceptions across that boundary. See this article for details: |
|
// http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/ |
|
// Note that the unhandled exception function is not called when running |
|
// under a debugger, but that's fine because in that case we don't care about |
|
// recording minidumps. |
|
// The try/catch block still makes sense because it is a more reliable way |
|
// of catching exceptions that aren't in callbacks. |
|
// The unhandled exception filter will also catch crashes in threads that |
|
// don't have a try/catch or __try/__except block. |
|
bool noMinidumps = CommandLine()->FindParm( "-nominidumps"); |
|
if ( !noMinidumps ) |
|
MinidumpSetUnhandledExceptionFunction( WriteSteamMiniDumpWithComment ); |
|
|
|
if ( !Plat_IsInDebugSession() && !noMinidumps ) |
|
{ |
|
int nRetVal = RUN_OK; |
|
CatchAndWriteMiniDumpForVoidPtrFn( StaticRunListenServer, &nRetVal, true ); |
|
return nRetVal; |
|
} |
|
else |
|
{ |
|
return RunListenServer(); |
|
} |
|
#else |
|
return RunListenServer(); |
|
#endif |
|
} |
|
#endif // SWDS |
|
|
|
bool g_bUsingLegacyAppSystems = false; |
|
|
|
bool CModAppSystemGroup::AddLegacySystems() |
|
{ |
|
g_bUsingLegacyAppSystems = true; |
|
|
|
AppSystemInfo_t appSystems[] = |
|
{ |
|
{ "soundemittersystem", SOUNDEMITTERSYSTEM_INTERFACE_VERSION }, |
|
{ "", "" } // Required to terminate the list |
|
}; |
|
|
|
if ( !AddSystems( appSystems ) ) |
|
return false; |
|
|
|
#if !defined( DEDICATED ) |
|
// if ( CommandLine()->FindParm( "-tools" ) ) |
|
{ |
|
AppModule_t toolFrameworkModule = LoadModule( "engine" DLL_EXT_STRING ); |
|
|
|
if ( !AddSystem( toolFrameworkModule, VTOOLFRAMEWORK_INTERFACE_VERSION ) ) |
|
return false; |
|
} |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Instantiate all main libraries |
|
//----------------------------------------------------------------------------- |
|
bool CModAppSystemGroup::Create() |
|
{ |
|
#ifndef SWDS |
|
if ( !IsServerOnly() ) |
|
{ |
|
if ( !ClientDLL_Load() ) |
|
return false; |
|
} |
|
#endif |
|
|
|
if ( !ServerDLL_Load( IsServerOnly() ) ) |
|
return false; |
|
|
|
IClientDLLSharedAppSystems *clientSharedSystems = 0; |
|
|
|
#ifndef SWDS |
|
if ( !IsServerOnly() ) |
|
{ |
|
clientSharedSystems = ( IClientDLLSharedAppSystems * )g_ClientFactory( CLIENT_DLL_SHARED_APPSYSTEMS, NULL ); |
|
if ( !clientSharedSystems ) |
|
return AddLegacySystems(); |
|
} |
|
#endif |
|
|
|
IServerDLLSharedAppSystems *serverSharedSystems = ( IServerDLLSharedAppSystems * )g_ServerFactory( SERVER_DLL_SHARED_APPSYSTEMS, NULL ); |
|
if ( !serverSharedSystems ) |
|
{ |
|
Assert( !"Expected both game and client .dlls to have or not have shared app systems interfaces!!!" ); |
|
return AddLegacySystems(); |
|
} |
|
|
|
// Load game and client .dlls and build list then |
|
CUtlVector< AppSystemInfo_t > systems; |
|
|
|
int i; |
|
int serverCount = serverSharedSystems->Count(); |
|
for ( i = 0 ; i < serverCount; ++i ) |
|
{ |
|
const char *dllName = serverSharedSystems->GetDllName( i ); |
|
const char *interfaceName = serverSharedSystems->GetInterfaceName( i ); |
|
|
|
AppSystemInfo_t info; |
|
info.m_pModuleName = dllName; |
|
info.m_pInterfaceName = interfaceName; |
|
|
|
systems.AddToTail( info ); |
|
} |
|
|
|
if ( !IsServerOnly() ) |
|
{ |
|
int clientCount = clientSharedSystems->Count(); |
|
for ( i = 0 ; i < clientCount; ++i ) |
|
{ |
|
const char *dllName = clientSharedSystems->GetDllName( i ); |
|
const char *interfaceName = clientSharedSystems->GetInterfaceName( i ); |
|
|
|
if ( ModuleAlreadyInList( systems, dllName, interfaceName ) ) |
|
continue; |
|
|
|
AppSystemInfo_t info; |
|
info.m_pModuleName = dllName; |
|
info.m_pInterfaceName = interfaceName; |
|
|
|
systems.AddToTail( info ); |
|
} |
|
} |
|
|
|
AppSystemInfo_t info; |
|
info.m_pModuleName = ""; |
|
info.m_pInterfaceName = ""; |
|
systems.AddToTail( info ); |
|
|
|
if ( !AddSystems( systems.Base() ) ) |
|
return false; |
|
|
|
#if !defined( DEDICATED ) |
|
// if ( CommandLine()->FindParm( "-tools" ) ) |
|
{ |
|
AppModule_t toolFrameworkModule = LoadModule( "engine" DLL_EXT_STRING ); |
|
|
|
if ( !AddSystem( toolFrameworkModule, VTOOLFRAMEWORK_INTERFACE_VERSION ) ) |
|
return false; |
|
} |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Fixme, we might need to verify if the interface names differ for the client versus the server |
|
// Input : list - |
|
// *moduleName - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CModAppSystemGroup::ModuleAlreadyInList( CUtlVector< AppSystemInfo_t >& list, const char *moduleName, const char *interfaceName ) |
|
{ |
|
for ( int i = 0; i < list.Count(); ++i ) |
|
{ |
|
if ( !Q_stricmp( list[ i ].m_pModuleName, moduleName ) ) |
|
{ |
|
if ( Q_stricmp( list[ i ].m_pInterfaceName, interfaceName ) ) |
|
{ |
|
Error( "Game and client .dlls requesting different versions '%s' vs. '%s' from '%s'\n", |
|
list[ i ].m_pInterfaceName, interfaceName, moduleName ); |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool CModAppSystemGroup::PreInit() |
|
{ |
|
return true; |
|
} |
|
|
|
void SV_ShutdownGameDLL(); |
|
int CModAppSystemGroup::Main() |
|
{ |
|
int nRunResult = RUN_OK; |
|
|
|
if ( IsServerOnly() ) |
|
{ |
|
// Start up the game engine |
|
if ( eng->Load( true, host_parms.basedir ) ) |
|
{ |
|
// If we're using STEAM, pass the map cycle list as resource hints... |
|
// Dedicated server drives frame loop manually |
|
dedicated->RunServer(); |
|
|
|
SV_ShutdownGameDLL(); |
|
} |
|
} |
|
else |
|
{ |
|
eng->SetQuitting( IEngine::QUIT_NOTQUITTING ); |
|
|
|
COM_TimestampedLog( "eng->Load" ); |
|
|
|
// Start up the game engine |
|
static const char engineLoadMessage[] = "Calling CEngine::Load"; |
|
int64 nStartTime = ETWBegin( engineLoadMessage ); |
|
if ( eng->Load( false, host_parms.basedir ) ) |
|
{ |
|
#if !defined(SWDS) |
|
ETWEnd( engineLoadMessage, nStartTime ); |
|
toolframework->ServerInit( g_ServerFactory ); |
|
|
|
if ( s_EngineAPI.MainLoop() ) |
|
{ |
|
nRunResult = RUN_RESTART; |
|
} |
|
|
|
// unload systems |
|
eng->Unload(); |
|
|
|
toolframework->ServerShutdown(); |
|
#endif |
|
SV_ShutdownGameDLL(); |
|
} |
|
} |
|
|
|
return nRunResult; |
|
} |
|
|
|
void CModAppSystemGroup::PostShutdown() |
|
{ |
|
} |
|
|
|
void CModAppSystemGroup::Destroy() |
|
{ |
|
// unload game and client .dlls |
|
ServerDLL_Unload(); |
|
#ifndef SWDS |
|
if ( !IsServerOnly() ) |
|
{ |
|
ClientDLL_Unload(); |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Purpose: Expose engine interface to launcher for dedicated servers |
|
// |
|
//----------------------------------------------------------------------------- |
|
class CDedicatedServerAPI : public CTier3AppSystem< IDedicatedServerAPI > |
|
{ |
|
typedef CTier3AppSystem< IDedicatedServerAPI > BaseClass; |
|
|
|
public: |
|
CDedicatedServerAPI() : |
|
m_pDedicatedServer( 0 ) |
|
{ |
|
} |
|
virtual bool Connect( CreateInterfaceFn factory ); |
|
virtual void Disconnect(); |
|
virtual void *QueryInterface( const char *pInterfaceName ); |
|
|
|
virtual bool ModInit( ModInfo_t &info ); |
|
virtual void ModShutdown( void ); |
|
|
|
virtual bool RunFrame( void ); |
|
|
|
virtual void AddConsoleText( char *text ); |
|
virtual void UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen ); |
|
virtual void UpdateHostname(char *pszHostname, int maxlen); |
|
|
|
CModAppSystemGroup *m_pDedicatedServer; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton |
|
//----------------------------------------------------------------------------- |
|
EXPOSE_SINGLE_INTERFACE( CDedicatedServerAPI, IDedicatedServerAPI, VENGINE_HLDS_API_VERSION ); |
|
|
|
#define LONG_TICK_TIME 0.12f // about 8/66ths of a second |
|
#define MIN_TIME_BETWEEN_DUMPED_TICKS 5.0f; |
|
#define MAX_DUMPS_PER_LONG_TICK 10 |
|
void Sys_Sleep ( int msec ); |
|
|
|
bool g_bLongTickWatcherThreadEnabled = false; |
|
bool g_bQuitLongTickWatcherThread = false; |
|
int g_bTotalDumps = 0; |
|
|
|
DWORD __stdcall LongTickWatcherThread( void *voidPtr ) |
|
{ |
|
int nLastTick = 0; |
|
double flWarnTickTime = 0.0f; |
|
double flNextPossibleDumpTime = Plat_FloatTime() + MIN_TIME_BETWEEN_DUMPED_TICKS; |
|
int nNumDumpsThisTick = 0; |
|
|
|
while ( eng->GetQuitting() == IEngine::QUIT_NOTQUITTING && !g_bQuitLongTickWatcherThread ) |
|
{ |
|
if ( sv.m_State == ss_active && sv.m_bSimulatingTicks ) |
|
{ |
|
int curTick = sv.m_nTickCount; |
|
double curTime = Plat_FloatTime(); |
|
if ( nLastTick > 0 && nLastTick == curTick ) |
|
{ |
|
if ( curTime > flNextPossibleDumpTime && curTime > flWarnTickTime && nNumDumpsThisTick < MAX_DUMPS_PER_LONG_TICK ) |
|
{ |
|
nNumDumpsThisTick++; |
|
g_bTotalDumps++; |
|
Warning( "Long tick after tick %i. Writing minidump #%i (%i total).\n", nLastTick, nNumDumpsThisTick, g_bTotalDumps ); |
|
|
|
if ( nNumDumpsThisTick == MAX_DUMPS_PER_LONG_TICK ) |
|
{ |
|
Msg( "Not writing any more minidumps for this tick.\n" ); |
|
} |
|
|
|
// If you're debugging a minidump and you ended up here, you probably want to switch to the main thread. |
|
WriteMiniDump( "longtick" ); |
|
} |
|
} |
|
|
|
if ( nLastTick != curTick ) |
|
{ |
|
if ( nNumDumpsThisTick ) |
|
{ |
|
Msg( "Long tick lasted about %.1f seconds.\n", curTime - (flWarnTickTime - LONG_TICK_TIME) ); |
|
nNumDumpsThisTick = 0; |
|
flNextPossibleDumpTime = curTime + MIN_TIME_BETWEEN_DUMPED_TICKS; |
|
} |
|
|
|
nLastTick = curTick; |
|
flWarnTickTime = curTime + LONG_TICK_TIME; |
|
} |
|
} |
|
else |
|
{ |
|
nLastTick = 0; |
|
} |
|
|
|
if ( nNumDumpsThisTick ) |
|
{ |
|
// We'll write the next minidump 0.06 seconds from now. |
|
Sys_Sleep( 60 ); |
|
} |
|
else |
|
{ |
|
// Check tick progress every 1/100th of a second. |
|
Sys_Sleep( 10 ); |
|
} |
|
} |
|
|
|
g_bLongTickWatcherThreadEnabled = false; |
|
g_bQuitLongTickWatcherThread = false; |
|
|
|
return 0; |
|
} |
|
|
|
bool EnableLongTickWatcher() |
|
{ |
|
bool bRet = false; |
|
if ( !g_bLongTickWatcherThreadEnabled ) |
|
{ |
|
g_bQuitLongTickWatcherThread = false; |
|
g_bLongTickWatcherThreadEnabled = true; |
|
|
|
DWORD nThreadID; |
|
VCRHook_CreateThread(NULL, 0, |
|
#ifdef POSIX |
|
(void*) |
|
#endif |
|
LongTickWatcherThread, NULL, 0, (uintp *)&nThreadID ); |
|
|
|
bRet = true; |
|
} |
|
else if ( g_bQuitLongTickWatcherThread ) |
|
{ |
|
Msg( "Cannot create a new long tick watcher while waiting for an old one to terminate.\n" ); |
|
} |
|
else |
|
{ |
|
Msg( "The long tick watcher thread is already running.\n" ); |
|
} |
|
|
|
return bRet; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Dedicated server entrypoint |
|
//----------------------------------------------------------------------------- |
|
bool CDedicatedServerAPI::Connect( CreateInterfaceFn factory ) |
|
{ |
|
if ( CommandLine()->FindParm( "-sv_benchmark" ) != 0 ) |
|
{ |
|
Plat_SetBenchmarkMode( true ); |
|
} |
|
|
|
if ( CommandLine()->FindParm( "-dumplongticks" ) ) |
|
{ |
|
Msg( "-dumplongticks found on command line. Activating long tick watcher thread.\n" ); |
|
EnableLongTickWatcher(); |
|
} |
|
|
|
// Store off the app system factory... |
|
g_AppSystemFactory = factory; |
|
|
|
if ( !BaseClass::Connect( factory ) ) |
|
return false; |
|
|
|
dedicated = ( IDedicatedExports * )factory( VENGINE_DEDICATEDEXPORTS_API_VERSION, NULL ); |
|
if ( !dedicated ) |
|
return false; |
|
|
|
g_pFileSystem = g_pFullFileSystem; |
|
g_pFileSystem->SetWarningFunc( Warning ); |
|
|
|
if ( !Shader_Connect( false ) ) |
|
return false; |
|
|
|
if ( !g_pStudioRender ) |
|
{ |
|
Sys_Error( "Unable to init studio render system version %s\n", STUDIO_RENDER_INTERFACE_VERSION ); |
|
return false; |
|
} |
|
|
|
g_pPhysics = (IPhysics*)factory( VPHYSICS_INTERFACE_VERSION, NULL ); |
|
|
|
if ( !g_pDataCache || !g_pPhysics || !g_pMDLCache ) |
|
{ |
|
Warning( "Engine wasn't able to acquire required interfaces!\n" ); |
|
return false; |
|
} |
|
|
|
ConnectMDLCacheNotify(); |
|
return true; |
|
} |
|
|
|
void CDedicatedServerAPI::Disconnect() |
|
{ |
|
DisconnectMDLCacheNotify(); |
|
|
|
g_pPhysics = NULL; |
|
|
|
Shader_Disconnect(); |
|
|
|
g_pFileSystem = NULL; |
|
|
|
ConVar_Unregister(); |
|
|
|
dedicated = NULL; |
|
|
|
BaseClass::Disconnect(); |
|
|
|
g_AppSystemFactory = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Query interface |
|
//----------------------------------------------------------------------------- |
|
void *CDedicatedServerAPI::QueryInterface( const char *pInterfaceName ) |
|
{ |
|
// Loading the engine DLL mounts *all* engine interfaces |
|
CreateInterfaceFn factory = Sys_GetFactoryThis(); // This silly construction is necessary |
|
return factory( pInterfaceName, NULL ); // to prevent the LTCG compiler from crashing. |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : type - 0 == normal, 1 == dedicated server |
|
// *instance - |
|
// *basedir - |
|
// *cmdline - |
|
// launcherFactory - |
|
//----------------------------------------------------------------------------- |
|
bool CDedicatedServerAPI::ModInit( ModInfo_t &info ) |
|
{ |
|
// Setup and write out steam_appid.txt before we launch |
|
bool bDedicated = true; |
|
eSteamInfoInit steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated ); |
|
|
|
eng->SetQuitting( IEngine::QUIT_NOTQUITTING ); |
|
|
|
// Set up the engineparms_t which contains global information about the mod |
|
host_parms.basedir = const_cast<char*>(info.m_pBaseDirectory); |
|
host_parms.mod = const_cast<char*>(GetModDirFromPath(info.m_pInitialMod)); |
|
host_parms.game = const_cast<char*>(info.m_pInitialGame); |
|
|
|
g_bTextMode = info.m_bTextMode; |
|
|
|
TRACEINIT( COM_InitFilesystem( info.m_pInitialMod ), COM_ShutdownFileSystem() ); |
|
|
|
if ( steamInfo != eSteamInfo_Initialized ) |
|
{ |
|
// Try again with filesystem available. This is commonly needed for SDK mods which need the filesystem to find |
|
// their steam.inf, due to mounting SDK search paths. |
|
steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated ); |
|
Assert( steamInfo == eSteamInfo_Initialized ); |
|
if ( steamInfo != eSteamInfo_Initialized ) |
|
{ |
|
Warning( "Failed to find steam.inf or equivalent steam info. May not have proper information to connect to Steam.\n" ); |
|
} |
|
} |
|
|
|
// set this up as early as possible, if the server isn't going to run pure, stop CRCing bits as we load them |
|
// this happens even before the ConCommand's are processed, but we need to be sure to either CRC every file |
|
// that is loaded, or not bother doing any |
|
// Note that this mirrors g_sv_pure_mode from sv_main.cpp |
|
int pure_mode = 1; // default to on, +sv_pure 0 or -sv_pure 0 will turn it off |
|
if ( CommandLine()->CheckParm("+sv_pure") ) |
|
pure_mode = CommandLine()->ParmValue( "+sv_pure", 1 ); |
|
else if ( CommandLine()->CheckParm("-sv_pure") ) |
|
pure_mode = CommandLine()->ParmValue( "-sv_pure", 1 ); |
|
if ( pure_mode ) |
|
g_pFullFileSystem->EnableWhitelistFileTracking( true, true, CommandLine()->FindParm( "-sv_pure_verify_hashes" ) ? true : false ); |
|
else |
|
g_pFullFileSystem->EnableWhitelistFileTracking( false, false, false ); |
|
|
|
materials->ModInit(); |
|
|
|
// Setup the material system config record, CreateGameWindow depends on it |
|
// (when we're running stand-alone) |
|
#ifndef SWDS |
|
InitMaterialSystemConfig( true ); // !!should this be called standalone or not? |
|
#endif |
|
|
|
// Initialize general game stuff and create the main window |
|
if ( game->Init( NULL ) ) |
|
{ |
|
m_pDedicatedServer = new CModAppSystemGroup( true, info.m_pParentAppSystemGroup ); |
|
|
|
// Store off the app system factory... |
|
g_AppSystemFactory = m_pDedicatedServer->GetFactory(); |
|
|
|
m_pDedicatedServer->Run(); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void CDedicatedServerAPI::ModShutdown( void ) |
|
{ |
|
if ( m_pDedicatedServer ) |
|
{ |
|
delete m_pDedicatedServer; |
|
m_pDedicatedServer = NULL; |
|
} |
|
|
|
g_AppSystemFactory = NULL; |
|
|
|
// Unload GL, Sound, etc. |
|
eng->Unload(); |
|
|
|
// Shut down memory, etc. |
|
game->Shutdown(); |
|
|
|
materials->ModShutdown(); |
|
TRACESHUTDOWN( COM_ShutdownFileSystem() ); |
|
} |
|
|
|
bool CDedicatedServerAPI::RunFrame( void ) |
|
{ |
|
// Bail if someone wants to quit. |
|
if ( eng->GetQuitting() != IEngine::QUIT_NOTQUITTING ) |
|
{ |
|
return false; |
|
} |
|
|
|
// Run engine frame |
|
eng->Frame(); |
|
return true; |
|
} |
|
|
|
void CDedicatedServerAPI::AddConsoleText( char *text ) |
|
{ |
|
Cbuf_AddText( text ); |
|
} |
|
|
|
void CDedicatedServerAPI::UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen ) |
|
{ |
|
Host_GetHostInfo( fps, nActive, nMaxPlayers, pszMap, maxlen ); |
|
} |
|
|
|
void CDedicatedServerAPI::UpdateHostname(char *pszHostname, int maxlen) |
|
{ |
|
if ( pszHostname && ( maxlen > 0 ) ) |
|
{ |
|
Q_strncpy( pszHostname, sv.GetName(), maxlen ); |
|
} |
|
} |
|
|
|
#ifndef SWDS |
|
|
|
class CGameUIFuncs : public IGameUIFuncs |
|
{ |
|
public: |
|
bool IsKeyDown( const char *keyname, bool& isdown ) |
|
{ |
|
isdown = false; |
|
if ( !g_ClientDLL ) |
|
return false; |
|
|
|
return g_ClientDLL->IN_IsKeyDown( keyname, isdown ); |
|
} |
|
|
|
const char *GetBindingForButtonCode( ButtonCode_t code ) |
|
{ |
|
return ::Key_BindingForKey( code ); |
|
} |
|
|
|
virtual ButtonCode_t GetButtonCodeForBind( const char *bind ) |
|
{ |
|
const char *pKeyName = Key_NameForBinding( bind ); |
|
if ( !pKeyName ) |
|
return KEY_NONE; |
|
return g_pInputSystem->StringToButtonCode( pKeyName ) ; |
|
} |
|
|
|
void GetVideoModes( struct vmode_s **ppListStart, int *pCount ) |
|
{ |
|
if ( videomode ) |
|
{ |
|
*pCount = videomode->GetModeCount(); |
|
*ppListStart = videomode->GetMode( 0 ); |
|
} |
|
else |
|
{ |
|
*pCount = 0; |
|
*ppListStart = NULL; |
|
} |
|
} |
|
|
|
void GetDesktopResolution( int &width, int &height ) |
|
{ |
|
int refreshrate; |
|
game->GetDesktopInfo( width, height, refreshrate ); |
|
} |
|
|
|
virtual void SetFriendsID( uint friendsID, const char *friendsName ) |
|
{ |
|
cl.SetFriendsID( friendsID, friendsName ); |
|
} |
|
|
|
bool IsConnectedToVACSecureServer() |
|
{ |
|
if ( cl.IsConnected() ) |
|
return Steam3Client().BGSSecure(); |
|
return false; |
|
} |
|
}; |
|
|
|
EXPOSE_SINGLE_INTERFACE( CGameUIFuncs, IGameUIFuncs, VENGINE_GAMEUIFUNCS_VERSION ); |
|
|
|
#endif |
|
|
|
CON_COMMAND( dumplongticks, "Enables generating minidumps on long ticks." ) |
|
{ |
|
int enable = atoi( args[1] ); |
|
if ( args.ArgC() == 1 || enable ) |
|
{ |
|
if ( EnableLongTickWatcher() ) |
|
{ |
|
Msg( "Long tick watcher thread created. Use \"dumplongticks 0\" to disable.\n" ); |
|
} |
|
} |
|
else |
|
{ |
|
// disable watcher thread if enabled |
|
if ( g_bLongTickWatcherThreadEnabled && !g_bQuitLongTickWatcherThread ) |
|
{ |
|
Msg( "Disabling the long tick watcher.\n" ); |
|
g_bQuitLongTickWatcherThread = true; |
|
} |
|
else |
|
{ |
|
Msg( "The long tick watcher is already disabled.\n" ); |
|
} |
|
} |
|
} |
|
|
|
|