2020-04-23 00:56:21 +08:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
# if defined(_WIN32) && !defined(_X360)
# include "winlite.h"
# elif defined(OSX)
# include <Carbon/Carbon.h>
# include <sys/sysctl.h>
2022-11-27 21:48:27 +08:00
# elif defined(PLATFORM_BSD)
2022-11-25 03:04:29 +08:00
# include <sys/types.h>
# include <sys/sysctl.h>
# define HW_MEMSIZE HW_PHYSMEM
2020-04-23 00:56:21 +08:00
# endif
# if defined(LINUX)
# include <unistd.h>
# include <fcntl.h>
# endif
# if defined( USE_SDL )
# include "SDL.h"
# endif
# include "quakedef.h"
# include "igame.h"
# include "errno.h"
# include "host.h"
# include "profiling.h"
# include "server.h"
# include "vengineserver_impl.h"
# include "filesystem_engine.h"
# include "sys.h"
# include "sys_dll.h"
# include "ivideomode.h"
# include "host_cmd.h"
# include "crtmemdebug.h"
# include "sv_log.h"
# include "sv_main.h"
# include "traceinit.h"
# include "dt_test.h"
# include "keys.h"
# include "gl_matsysiface.h"
# include "tier0/vcrmode.h"
# include "tier0/icommandline.h"
# include "cmd.h"
# include <ihltvdirector.h>
# if defined( REPLAY_ENABLED )
# include "replay/ireplaysystem.h"
# endif
# include "MapReslistGenerator.h"
# include "DevShotGenerator.h"
# include "cdll_engine_int.h"
# include "dt_send.h"
# include "idedicatedexports.h"
# include "eifacev21.h"
# include "cl_steamauth.h"
# include "tier0/etwprof.h"
# include "vgui_baseui_interface.h"
# include "tier0/systeminformation.h"
# ifdef _WIN32
# if !defined( _X360 )
# include <io.h>
# endif
# endif
# include "toolframework/itoolframework.h"
# if defined( _X360 )
# include "xbox/xbox_win32stubs.h"
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# define ONE_HUNDRED_TWENTY_EIGHT_MB (128 * 1024 * 1024)
ConVar mem_min_heapsize ( " mem_min_heapsize " , " 48 " , FCVAR_INTERNAL_USE , " Minimum amount of memory to dedicate to engine hunk and datacache (in mb) " ) ;
ConVar mem_max_heapsize ( " mem_max_heapsize " , " 256 " , FCVAR_INTERNAL_USE , " Maximum amount of memory to dedicate to engine hunk and datacache (in mb) " ) ;
ConVar mem_max_heapsize_dedicated ( " mem_max_heapsize_dedicated " , " 64 " , FCVAR_INTERNAL_USE , " Maximum amount of memory to dedicate to engine hunk and datacache, for dedicated server (in mb) " ) ;
# define MINIMUM_WIN_MEMORY (unsigned)(mem_min_heapsize.GetInt()*1024*1024)
# define MAXIMUM_WIN_MEMORY max( (unsigned)(mem_max_heapsize.GetInt()*1024*1024), MINIMUM_WIN_MEMORY )
# define MAXIMUM_DEDICATED_MEMORY (unsigned)(mem_max_heapsize_dedicated.GetInt()*1024*1024)
char * CheckParm ( const char * psz , char * * ppszValue = NULL ) ;
void SeedRandomNumberGenerator ( bool random_invariant ) ;
void Con_ColorPrintf ( const Color & clr , PRINTF_FORMAT_STRING const char * fmt , . . . ) FMTFUNCTION ( 2 , 3 ) ;
void COM_ShutdownFileSystem ( void ) ;
void COM_InitFilesystem ( const char * pFullModPath ) ;
modinfo_t gmodinfo ;
# ifdef PLATFORM_WINDOWS
HWND * pmainwindow = NULL ;
# endif
char gszDisconnectReason [ 256 ] ;
char gszExtendedDisconnectReason [ 256 ] ;
bool gfExtendedError = false ;
uint8 g_eSteamLoginFailure = 0 ;
bool g_bV3SteamInterface = false ;
CreateInterfaceFn g_AppSystemFactory = NULL ;
static bool s_bIsDedicated = false ;
ConVar * sv_noclipduringpause = NULL ;
// Special mode where the client uses a console window and has no graphics. Useful for stress-testing a server
// without having to round up 32 people.
bool g_bTextMode = false ;
// Set to true when we exit from an error.
bool g_bInErrorExit = false ;
static FileFindHandle_t g_hfind = FILESYSTEM_INVALID_FIND_HANDLE ;
// The extension DLL directory--one entry per loaded DLL
CSysModule * g_GameDLL = NULL ;
// Prototype of an global method function
typedef void ( DLLEXPORT * PFN_GlobalMethod ) ( edict_t * pEntity ) ;
IServerGameDLL * serverGameDLL = NULL ;
int g_iServerGameDLLVersion = 0 ;
IServerGameEnts * serverGameEnts = NULL ;
IServerGameClients * serverGameClients = NULL ;
int g_iServerGameClientsVersion = 0 ; // This matches the number at the end of the interface name (so for "ServerGameClients004", this would be 4).
IHLTVDirector * serverGameDirector = NULL ;
IServerGameTags * serverGameTags = NULL ;
void Sys_InitArgv ( char * lpCmdLine ) ;
void Sys_ShutdownArgv ( void ) ;
//-----------------------------------------------------------------------------
// Purpose: Compare file times
// Input : ft1 -
// ft2 -
// Output : int
//-----------------------------------------------------------------------------
int Sys_CompareFileTime ( long ft1 , long ft2 )
{
if ( ft1 < ft2 )
{
return - 1 ;
}
else if ( ft1 > ft2 )
{
return 1 ;
}
return 0 ;
}
//-----------------------------------------------------------------------------
// Is slash?
//-----------------------------------------------------------------------------
inline bool IsSlash ( char c )
{
return ( c = = ' \\ ' ) | | ( c = = ' / ' ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Create specified directory
// Input : *path -
// Output : void Sys_mkdir
//-----------------------------------------------------------------------------
void Sys_mkdir ( const char * path )
{
char testpath [ MAX_OSPATH ] ;
// Remove any terminal backslash or /
Q_strncpy ( testpath , path , sizeof ( testpath ) ) ;
int nLen = Q_strlen ( testpath ) ;
if ( ( nLen > 0 ) & & IsSlash ( testpath [ nLen - 1 ] ) )
{
testpath [ nLen - 1 ] = 0 ;
}
// Look for URL
const char * pPathID = " MOD " ;
if ( IsSlash ( testpath [ 0 ] ) & & IsSlash ( testpath [ 1 ] ) )
{
pPathID = NULL ;
}
if ( g_pFileSystem - > FileExists ( testpath , pPathID ) )
{
// if there is a file of the same name as the directory we want to make, just kill it
if ( ! g_pFileSystem - > IsDirectory ( testpath , pPathID ) )
{
g_pFileSystem - > RemoveFile ( testpath , pPathID ) ;
}
}
g_pFileSystem - > CreateDirHierarchy ( path , pPathID ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *path -
// *basename -
// Output : char *Sys_FindFirst
//-----------------------------------------------------------------------------
const char * Sys_FindFirst ( const char * path , char * basename , int namelength )
{
if ( g_hfind ! = FILESYSTEM_INVALID_FIND_HANDLE )
{
Sys_Error ( " Sys_FindFirst without close " ) ;
g_pFileSystem - > FindClose ( g_hfind ) ;
}
const char * psz = g_pFileSystem - > FindFirst ( path , & g_hfind ) ;
if ( basename & & psz )
{
Q_FileBase ( psz , basename , namelength ) ;
}
return psz ;
}
//-----------------------------------------------------------------------------
// Purpose: Sys_FindFirst with a path ID filter.
//-----------------------------------------------------------------------------
const char * Sys_FindFirstEx ( const char * pWildcard , const char * pPathID , char * basename , int namelength )
{
if ( g_hfind ! = FILESYSTEM_INVALID_FIND_HANDLE )
{
Sys_Error ( " Sys_FindFirst without close " ) ;
g_pFileSystem - > FindClose ( g_hfind ) ;
}
const char * psz = g_pFileSystem - > FindFirstEx ( pWildcard , pPathID , & g_hfind ) ;
if ( basename & & psz )
{
Q_FileBase ( psz , basename , namelength ) ;
}
return psz ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *basename -
// Output : char *Sys_FindNext
//-----------------------------------------------------------------------------
const char * Sys_FindNext ( char * basename , int namelength )
{
const char * psz = g_pFileSystem - > FindNext ( g_hfind ) ;
if ( basename & & psz )
{
Q_FileBase ( psz , basename , namelength ) ;
}
return psz ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : void Sys_FindClose
//-----------------------------------------------------------------------------
void Sys_FindClose ( void )
{
if ( FILESYSTEM_INVALID_FIND_HANDLE ! = g_hfind )
{
g_pFileSystem - > FindClose ( g_hfind ) ;
g_hfind = FILESYSTEM_INVALID_FIND_HANDLE ;
}
}
//-----------------------------------------------------------------------------
// Purpose: OS Specific initializations
//-----------------------------------------------------------------------------
void Sys_Init ( void )
{
// Set default FPU control word to truncate (chop) mode for optimized _ftol()
// This does not "stick", the mode is restored somewhere down the line.
// Sys_TruncateFPU();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Sys_Shutdown ( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Print to system console
// Input : *fmt -
// ... -
// Output : void Sys_Printf
//-----------------------------------------------------------------------------
void Sys_Printf ( char * fmt , . . . )
{
va_list argptr ;
char text [ 1024 ] ;
va_start ( argptr , fmt ) ;
Q_vsnprintf ( text , sizeof ( text ) , fmt , argptr ) ;
va_end ( argptr ) ;
if ( developer . GetInt ( ) )
{
# ifdef _WIN32
wchar_t unicode [ 2048 ] ;
: : MultiByteToWideChar ( CP_UTF8 , 0 , text , - 1 , unicode , sizeof ( unicode ) / sizeof ( wchar_t ) ) ;
unicode [ ( sizeof ( unicode ) / sizeof ( wchar_t ) ) - 1 ] = L ' \0 ' ;
OutputDebugStringW ( unicode ) ;
Sleep ( 0 ) ;
# else
fprintf ( stderr , " %s " , text ) ;
# endif
}
if ( s_bIsDedicated )
{
printf ( " %s " , text ) ;
}
}
bool Sys_MessageBox ( const char * title , const char * info , bool bShowOkAndCancel )
{
# ifdef _WIN32
if ( IDOK = = : : MessageBox ( NULL , title , info , MB_ICONEXCLAMATION | ( bShowOkAndCancel ? MB_OKCANCEL : MB_OK ) ) )
{
return true ;
}
return false ;
# elif defined( USE_SDL )
int buttonid = 0 ;
SDL_MessageBoxData messageboxdata = { 0 } ;
SDL_MessageBoxButtonData buttondata [ ] =
{
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT , 1 , " OK " } ,
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT , 0 , " Cancel " } ,
} ;
messageboxdata . window = GetAssertDialogParent ( ) ;
messageboxdata . title = title ;
messageboxdata . message = info ;
messageboxdata . numbuttons = bShowOkAndCancel ? 2 : 1 ;
messageboxdata . buttons = buttondata ;
SDL_ShowMessageBox ( & messageboxdata , & buttonid ) ;
return ( buttonid = = 1 ) ;
# elif defined( POSIX )
Warning ( " %s \n " , info ) ;
return true ;
# else
# error "implement me"
# endif
}
bool g_bUpdateMinidumpComment = true ;
void BuildMinidumpComment ( char const * pchSysErrorText , bool bRealCrash ) ;
void Sys_Error_Internal ( bool bMinidump , const char * error , va_list argsList )
{
char text [ 1024 ] ;
static bool bReentry = false ; // Don't meltdown
Q_vsnprintf ( text , sizeof ( text ) , error , argsList ) ;
if ( bReentry )
{
fprintf ( stderr , " %s \n " , text ) ;
return ;
}
bReentry = true ;
if ( s_bIsDedicated )
{
printf ( " %s \n " , text ) ;
}
else
{
Sys_Printf ( " %s \n " , text ) ;
}
// Write the error to the log and ensure the log contents get written to disk
g_Log . Printf ( " Engine error: %s \n " , text ) ;
g_Log . Flush ( ) ;
g_bInErrorExit = true ;
# if !defined( SWDS )
if ( IsPC ( ) & & videomode )
videomode - > Shutdown ( ) ;
# endif
if ( IsPC ( ) & &
! CommandLine ( ) - > FindParm ( " -makereslists " ) & &
! CommandLine ( ) - > FindParm ( " -nomessagebox " ) & &
! CommandLine ( ) - > FindParm ( " -nocrashdialog " ) )
{
# ifdef _WIN32
: : MessageBox ( NULL , text , " Engine Error " , MB_OK | MB_TOPMOST ) ;
# elif defined( USE_SDL )
Sys_MessageBox ( " Engine Error " , text , false ) ;
# endif
}
if ( IsPC ( ) )
{
DebuggerBreakIfDebugging ( ) ;
}
else if ( ! IsRetail ( ) )
{
DebuggerBreak ( ) ;
}
# if !defined( _X360 )
BuildMinidumpComment ( text , true ) ;
g_bUpdateMinidumpComment = false ;
if ( bMinidump & & ! Plat_IsInDebugSession ( ) & & ! CommandLine ( ) - > FindParm ( " -nominidumps " ) )
{
# if defined( WIN32 )
// MiniDumpWrite() has problems capturing the calling thread's context
// unless it is called with an exception context. So fake an exception.
__try
{
RaiseException
(
0 , // dwExceptionCode
EXCEPTION_NONCONTINUABLE , // dwExceptionFlags
0 , // nNumberOfArguments,
NULL // const ULONG_PTR* lpArguments
) ;
// Never get here (non-continuable exception)
}
// Write the minidump from inside the filter (GetExceptionInformation() is only
// valid in the filter)
__except ( SteamAPI_WriteMiniDump ( 0 , GetExceptionInformation ( ) , build_number ( ) ) , EXCEPTION_EXECUTE_HANDLER )
{
// We always get here because the above filter evaluates to EXCEPTION_EXECUTE_HANDLER
}
2022-11-25 03:04:29 +08:00
# elif defined(POSIX)
2020-04-23 00:56:21 +08:00
// Doing this doesn't quite work the way we want because there is no "crashing" thread
// and we see "No thread was identified as the cause of the crash; No signature could be created because we do not know which thread crashed" on the back end
//SteamAPI_WriteMiniDump( 0, NULL, build_number() );
printf ( " \n ##### Sys_Error: %s " , text ) ;
fflush ( stdout ) ;
2022-11-25 03:04:29 +08:00
raise ( SIGTRAP ) ;
2020-04-23 00:56:21 +08:00
# else
# warning "need minidump impl on sys_error"
# endif
}
# endif // _X360
host_initialized = false ;
# if defined(_WIN32) && !defined( _X360 )
// We don't want global destructors in our process OR in any DLL to get executed.
// _exit() avoids calling global destructors in our module, but not in other DLLs.
TerminateProcess ( GetCurrentProcess ( ) , 100 ) ;
# else
_exit ( 100 ) ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Exit engine with error
// Input : *error -
// ... -
// Output : void Sys_Error
//-----------------------------------------------------------------------------
void Sys_Error ( const char * error , . . . )
{
va_list argptr ;
va_start ( argptr , error ) ;
Sys_Error_Internal ( true , error , argptr ) ;
va_end ( argptr ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Exit engine with error
// Input : *error -
// ... -
// Output : void Sys_Error
//-----------------------------------------------------------------------------
void Sys_Exit ( const char * error , . . . )
{
va_list argptr ;
va_start ( argptr , error ) ;
Sys_Error_Internal ( false , error , argptr ) ;
va_end ( argptr ) ;
}
bool IsInErrorExit ( )
{
return g_bInErrorExit ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : msec -
// Output : void Sys_Sleep
//-----------------------------------------------------------------------------
void Sys_Sleep ( int msec )
{
# ifdef _WIN32
Sleep ( msec ) ;
# elif POSIX
usleep ( msec * 1000 ) ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : hInst -
// ulInit -
// lpReserved -
// Output : BOOL WINAPI DllMain
//-----------------------------------------------------------------------------
# if defined(_WIN32) && !defined( _X360 )
BOOL WINAPI DllMain ( HANDLE hInst , ULONG ulInit , LPVOID lpReserved )
{
InitCRTMemDebug ( ) ;
if ( ulInit = = DLL_PROCESS_ATTACH )
{
}
else if ( ulInit = = DLL_PROCESS_DETACH )
{
}
return TRUE ;
}
# endif
//-----------------------------------------------------------------------------
// Purpose: Allocate memory for engine hunk
// Input : *parms -
//-----------------------------------------------------------------------------
void Sys_InitMemory ( void )
{
// Allow overrides
if ( CommandLine ( ) - > FindParm ( " -minmemory " ) )
{
host_parms . memsize = MINIMUM_WIN_MEMORY ;
return ;
}
host_parms . memsize = 0 ;
# ifdef _WIN32
# if (_MSC_VER > 1200)
// MSVC 6.0 doesn't support GlobalMemoryStatusEx()
if ( IsPC ( ) )
{
OSVERSIONINFOEX osvi ;
ZeroMemory ( & osvi , sizeof ( OSVERSIONINFOEX ) ) ;
osvi . dwOSVersionInfoSize = sizeof ( OSVERSIONINFOEX ) ;
if ( GetVersionEx ( ( OSVERSIONINFO * ) & osvi ) )
{
if ( osvi . dwPlatformId > = VER_PLATFORM_WIN32_NT & & osvi . dwMajorVersion > = 5 )
{
MEMORYSTATUSEX memStat ;
ZeroMemory ( & memStat , sizeof ( MEMORYSTATUSEX ) ) ;
memStat . dwLength = sizeof ( MEMORYSTATUSEX ) ;
if ( GlobalMemoryStatusEx ( & memStat ) )
{
if ( memStat . ullTotalPhys > 0xFFFFFFFFUL )
{
host_parms . memsize = 0xFFFFFFFFUL ;
}
else
{
host_parms . memsize = memStat . ullTotalPhys ;
}
}
}
}
}
# endif // (_MSC_VER > 1200)
if ( ! IsX360 ( ) )
{
if ( host_parms . memsize = = 0 )
{
MEMORYSTATUS lpBuffer ;
// Get OS Memory status
lpBuffer . dwLength = sizeof ( MEMORYSTATUS ) ;
GlobalMemoryStatus ( & lpBuffer ) ;
if ( lpBuffer . dwTotalPhys < = 0 )
{
host_parms . memsize = MAXIMUM_WIN_MEMORY ;
}
else
{
host_parms . memsize = lpBuffer . dwTotalPhys ;
}
}
if ( host_parms . memsize < ONE_HUNDRED_TWENTY_EIGHT_MB )
{
Sys_Error ( " Available memory less than 128MB!!! %i \n " , host_parms . memsize ) ;
}
// take one quarter the physical memory
if ( host_parms . memsize < = 512 * 1024 * 1024 )
{
host_parms . memsize > > = 2 ;
// Apply cap of 64MB for 512MB systems
// this keeps the code the same as HL2 gold
// but allows us to use more memory on 1GB+ systems
if ( host_parms . memsize > MAXIMUM_DEDICATED_MEMORY )
{
host_parms . memsize = MAXIMUM_DEDICATED_MEMORY ;
}
}
else
{
// just take one quarter, no cap
host_parms . memsize > > = 2 ;
}
// At least MINIMUM_WIN_MEMORY mb, even if we have to swap a lot.
if ( host_parms . memsize < MINIMUM_WIN_MEMORY )
{
host_parms . memsize = MINIMUM_WIN_MEMORY ;
}
// Apply cap
if ( host_parms . memsize > MAXIMUM_WIN_MEMORY )
{
host_parms . memsize = MAXIMUM_WIN_MEMORY ;
}
}
else
{
host_parms . memsize = 128 * 1024 * 1024 ;
}
# elif defined(POSIX)
uint64_t memsize = ONE_HUNDRED_TWENTY_EIGHT_MB ;
2022-11-27 21:48:27 +08:00
# if defined(OSX) || defined(PLATFORM_BSD)
2020-04-23 00:56:21 +08:00
int mib [ 2 ] = { CTL_HW , HW_MEMSIZE } ;
u_int namelen = sizeof ( mib ) / sizeof ( mib [ 0 ] ) ;
size_t len = sizeof ( memsize ) ;
if ( sysctl ( mib , namelen , & memsize , & len , NULL , 0 ) < 0 )
{
memsize = ONE_HUNDRED_TWENTY_EIGHT_MB ;
}
# elif defined(LINUX)
const int fd = open ( " /proc/meminfo " , O_RDONLY ) ;
if ( fd < 0 )
{
Sys_Error ( " Can't open /proc/meminfo (%s)! \n " , strerror ( errno ) ) ;
}
char buf [ 1024 * 16 ] ;
const ssize_t br = read ( fd , buf , sizeof ( buf ) ) ;
close ( fd ) ;
if ( br < 0 )
{
Sys_Error ( " Can't read /proc/meminfo (%s)! \n " , strerror ( errno ) ) ;
}
buf [ br ] = ' \0 ' ;
// Split up the buffer by lines...
char * line = buf ;
for ( char * ptr = buf ; * ptr ; ptr + + )
{
if ( * ptr = = ' \n ' )
{
// we've got a complete line.
* ptr = ' \0 ' ;
unsigned long long ull = 0 ;
if ( sscanf ( line , " MemTotal: %llu kB " , & ull ) = = 1 )
{
// found it!
memsize = ( ( uint64_t ) ull ) * 1024 ;
break ;
}
line = ptr ;
}
}
# else
# error Write me.
# endif
if ( memsize > 0xFFFFFFFFUL )
{
host_parms . memsize = 0xFFFFFFFFUL ;
}
else
{
host_parms . memsize = memsize ;
}
if ( host_parms . memsize < ONE_HUNDRED_TWENTY_EIGHT_MB )
{
Sys_Error ( " Available memory less than 128MB!!! %i \n " , host_parms . memsize ) ;
}
// take one quarter the physical memory
if ( host_parms . memsize < = 512 * 1024 * 1024 )
{
host_parms . memsize > > = 2 ;
// Apply cap of 64MB for 512MB systems
// this keeps the code the same as HL2 gold
// but allows us to use more memory on 1GB+ systems
if ( host_parms . memsize > MAXIMUM_DEDICATED_MEMORY )
{
host_parms . memsize = MAXIMUM_DEDICATED_MEMORY ;
}
}
else
{
// just take one quarter, no cap
host_parms . memsize > > = 2 ;
}
// At least MINIMUM_WIN_MEMORY mb, even if we have to swap a lot.
if ( host_parms . memsize < MINIMUM_WIN_MEMORY )
{
host_parms . memsize = MINIMUM_WIN_MEMORY ;
}
// Apply cap
if ( host_parms . memsize > MAXIMUM_WIN_MEMORY )
{
host_parms . memsize = MAXIMUM_WIN_MEMORY ;
}
# else
# error Write me.
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *parms -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
void Sys_ShutdownMemory ( void )
{
host_parms . memsize = 0 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Sys_InitAuthentication ( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Sys_ShutdownAuthentication ( void )
{
}
//-----------------------------------------------------------------------------
// Debug library spew output
//-----------------------------------------------------------------------------
2022-06-05 06:44:42 +08:00
CTHREADLOCALINT g_bInSpew ;
2020-04-23 00:56:21 +08:00
# include "tier1/fmtstr.h"
static ConVar sys_minidumpspewlines ( " sys_minidumpspewlines " , " 500 " , 0 , " Lines of crash dump console spew to keep. " ) ;
static CUtlLinkedList < CUtlString > g_SpewHistory ;
static int g_nSpewLines = 1 ;
static CThreadFastMutex g_SpewMutex ;
static void AddSpewRecord ( char const * pMsg )
{
# if !defined( _X360 )
AUTO_LOCK ( g_SpewMutex ) ;
static bool s_bReentrancyGuard = false ;
if ( s_bReentrancyGuard )
return ;
s_bReentrancyGuard = true ;
if ( g_SpewHistory . Count ( ) > sys_minidumpspewlines . GetInt ( ) )
{
g_SpewHistory . Remove ( g_SpewHistory . Head ( ) ) ;
}
int i = g_SpewHistory . AddToTail ( ) ;
g_SpewHistory [ i ] . Format ( " %d(%f): %s " , g_nSpewLines + + , Plat_FloatTime ( ) , pMsg ) ;
s_bReentrancyGuard = false ;
# endif
}
void GetSpew ( char * buf , size_t buflen )
{
AUTO_LOCK ( g_SpewMutex ) ;
// Walk list backward
char * pcur = buf ;
int remainder = ( int ) buflen - 1 ;
// Walk backward(
for ( int i = g_SpewHistory . Tail ( ) ; i ! = g_SpewHistory . InvalidIndex ( ) ; i = g_SpewHistory . Previous ( i ) )
{
const CUtlString & rec = g_SpewHistory [ i ] ;
int len = rec . Length ( ) ;
int tocopy = MIN ( len , remainder ) ;
if ( tocopy < = 0 )
break ;
Q_memcpy ( pcur , rec . String ( ) , tocopy ) ;
remainder - = tocopy ;
pcur + = tocopy ;
if ( remainder < = 0 )
break ;
}
* pcur = 0 ;
}
ConVar spew_consolelog_to_debugstring ( " spew_consolelog_to_debugstring " , " 0 " , 0 , " Send console log to PLAT_DebugString() " ) ;
SpewRetval_t Sys_SpewFunc ( SpewType_t spewType , const char * pMsg )
{
bool suppress = g_bInSpew ;
g_bInSpew = true ;
AddSpewRecord ( pMsg ) ;
// Text output shows up on dedicated server profiles, both as consuming CPU
// time and causing IPC delays. Sending the messages to ETW will help us
// understand why, and save us time when server operators are triggering
// excessive spew. Having the output in traces is also generically useful
// for understanding slowdowns.
ETWMark1I ( pMsg , spewType ) ;
if ( ! suppress )
{
// If this is a dedicated server, then we have taken over its spew function, but we still
// want its vgui console to show the spew, so pass it into the dedicated server.
if ( dedicated )
dedicated - > Sys_Printf ( ( char * ) pMsg ) ;
if ( spew_consolelog_to_debugstring . GetBool ( ) )
{
Plat_DebugString ( pMsg ) ;
}
if ( g_bTextMode )
{
printf ( " %s " , pMsg ) ;
}
if ( ( spewType ! = SPEW_LOG ) | | ( sv . GetMaxClients ( ) = = 1 ) )
{
Color color ;
switch ( spewType )
{
# ifndef SWDS
case SPEW_WARNING :
{
color . SetColor ( 255 , 90 , 90 , 255 ) ;
}
break ;
case SPEW_ASSERT :
{
color . SetColor ( 255 , 20 , 20 , 255 ) ;
}
break ;
case SPEW_ERROR :
{
color . SetColor ( 20 , 70 , 255 , 255 ) ;
}
break ;
# endif
default :
{
color = * GetSpewOutputColor ( ) ;
}
break ;
}
Con_ColorPrintf ( color , " %s " , pMsg ) ;
}
else
{
g_Log . Printf ( " %s " , pMsg ) ;
}
}
g_bInSpew = false ;
if ( spewType = = SPEW_ERROR )
{
Sys_Error ( " %s " , pMsg ) ;
return SPEW_ABORT ;
}
if ( spewType = = SPEW_ASSERT )
{
if ( CommandLine ( ) - > FindParm ( " -noassert " ) = = 0 )
return SPEW_DEBUGGER ;
else
return SPEW_CONTINUE ;
}
return SPEW_CONTINUE ;
}
void DeveloperChangeCallback ( IConVar * pConVar , const char * pOldString , float flOldValue )
{
// Set the "developer" spew group to the value...
ConVarRef var ( pConVar ) ;
int val = var . GetInt ( ) ;
SpewActivate ( " developer " , val ) ;
// Activate console spew (spew value 2 == developer console spew)
SpewActivate ( " console " , val ? 2 : 1 ) ;
}
//-----------------------------------------------------------------------------
// Purpose: factory comglomerator, gets the client, server, and gameui dlls together
//-----------------------------------------------------------------------------
void * GameFactory ( const char * pName , int * pReturnCode )
{
void * pRetVal = NULL ;
// first ask the app factory
pRetVal = g_AppSystemFactory ( pName , pReturnCode ) ;
if ( pRetVal )
return pRetVal ;
# ifndef SWDS
// now ask the client dll
if ( ClientDLL_GetFactory ( ) )
{
pRetVal = ClientDLL_GetFactory ( ) ( pName , pReturnCode ) ;
if ( pRetVal )
return pRetVal ;
}
// gameui.dll
if ( EngineVGui ( ) - > GetGameUIFactory ( ) )
{
pRetVal = EngineVGui ( ) - > GetGameUIFactory ( ) ( pName , pReturnCode ) ;
if ( pRetVal )
return pRetVal ;
}
# endif
// server dll factory access would go here when needed
return NULL ;
}
// factory instance
CreateInterfaceFn g_GameSystemFactory = GameFactory ;
//-----------------------------------------------------------------------------
// Purpose:
// Input : *lpOrgCmdLine -
// launcherFactory -
// *pwnd -
// bIsDedicated -
// Output : int
//-----------------------------------------------------------------------------
int Sys_InitGame ( CreateInterfaceFn appSystemFactory , const char * pBaseDir , void * pwnd , int bIsDedicated )
{
# ifdef BENCHMARK
if ( bIsDedicated )
{
Error ( " Dedicated server isn't supported by this benchmark! " ) ;
}
# endif
extern void InitMathlib ( void ) ;
InitMathlib ( ) ;
FileSystem_SetWhitelistSpewFlags ( ) ;
// Activate console spew
// Must happen before developer.InstallChangeCallback because that callback may reset it
SpewActivate ( " console " , 1 ) ;
// Install debug spew output....
developer . InstallChangeCallback ( DeveloperChangeCallback ) ;
SpewOutputFunc ( Sys_SpewFunc ) ;
// Assume failure
host_initialized = false ;
# ifdef PLATFORM_WINDOWS
// Grab main window pointer
pmainwindow = ( HWND * ) pwnd ;
# endif
// Remember that this is a dedicated server
s_bIsDedicated = bIsDedicated ? true : false ;
memset ( & gmodinfo , 0 , sizeof ( modinfo_t ) ) ;
static char s_pBaseDir [ 256 ] ;
Q_strncpy ( s_pBaseDir , pBaseDir , sizeof ( s_pBaseDir ) ) ;
Q_strlower ( s_pBaseDir ) ;
Q_FixSlashes ( s_pBaseDir ) ;
host_parms . basedir = s_pBaseDir ;
# ifndef _X360
if ( CommandLine ( ) - > FindParm ( " -pidfile " ) )
{
FileHandle_t pidFile = g_pFileSystem - > Open ( CommandLine ( ) - > ParmValue ( " -pidfile " , " srcds.pid " ) , " w+ " ) ;
if ( pidFile )
{
g_pFileSystem - > FPrintf ( pidFile , " %i \n " , getpid ( ) ) ;
g_pFileSystem - > Close ( pidFile ) ;
}
else
{
Warning ( " Unable to open pidfile (%s) \n " , CommandLine ( ) - > CheckParm ( " -pidfile " ) ) ;
}
}
# endif
// Initialize clock
TRACEINIT ( Sys_Init ( ) , Sys_Shutdown ( ) ) ;
# if defined(_DEBUG)
if ( IsPC ( ) )
{
if ( ! CommandLine ( ) - > FindParm ( " -nodttest " ) & & ! CommandLine ( ) - > FindParm ( " -dti " ) )
{
RunDataTableTest ( ) ;
}
}
# endif
// NOTE: Can't use COM_CheckParm here because it hasn't been set up yet.
SeedRandomNumberGenerator ( CommandLine ( ) - > FindParm ( " -random_invariant " ) ! = 0 ) ;
TRACEINIT ( Sys_InitMemory ( ) , Sys_ShutdownMemory ( ) ) ;
TRACEINIT ( Host_Init ( s_bIsDedicated ) , Host_Shutdown ( ) ) ;
if ( ! host_initialized )
{
return 0 ;
}
TRACEINIT ( Sys_InitAuthentication ( ) , Sys_ShutdownAuthentication ( ) ) ;
MapReslistGenerator_BuildMapList ( ) ;
BuildMinidumpComment ( NULL , false ) ;
return 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Sys_ShutdownGame ( void )
{
TRACESHUTDOWN ( Sys_ShutdownAuthentication ( ) ) ;
TRACESHUTDOWN ( Host_Shutdown ( ) ) ;
TRACESHUTDOWN ( Sys_ShutdownMemory ( ) ) ;
// TRACESHUTDOWN( Sys_ShutdownArgv() );
TRACESHUTDOWN ( Sys_Shutdown ( ) ) ;
// Remove debug spew output....
developer . InstallChangeCallback ( 0 ) ;
SpewOutputFunc ( 0 ) ;
}
//
// Try to load a single DLL. If it conforms to spec, keep it loaded, and add relevant
// info to the DLL directory. If not, ignore it entirely.
//
CreateInterfaceFn g_ServerFactory ;
# pragma optimize( "g", off )
static bool LoadThisDll ( char * szDllFilename , bool bIsServerOnly )
{
CSysModule * pDLL = NULL ;
// check signature, don't let users with modified binaries connect to secure servers, they will get VAC banned
if ( ! Host_AllowLoadModule ( szDllFilename , " GAMEBIN " , true , bIsServerOnly ) )
{
// not supposed to load this but we will anyway
Host_DisallowSecureServers ( ) ;
Host_AllowLoadModule ( szDllFilename , " GAMEBIN " , true , bIsServerOnly ) ;
}
// Load DLL, ignore if cannot
// ensures that the game.dll is running under Steam
// this will have to be undone when we want mods to be able to run
if ( ( pDLL = g_pFileSystem - > LoadModule ( szDllFilename , " GAMEBIN " , false ) ) = = NULL )
{
ConMsg ( " Failed to load %s \n " , szDllFilename ) ;
goto IgnoreThisDLL ;
}
// Load interface factory and any interfaces exported by the game .dll
g_iServerGameDLLVersion = 0 ;
g_ServerFactory = Sys_GetFactory ( pDLL ) ;
if ( g_ServerFactory )
{
// Figure out latest version we understand
g_iServerGameDLLVersion = INTERFACEVERSION_SERVERGAMEDLL_INT ;
// Scan for most recent version the game DLL understands.
for ( ; ; )
{
char archVersion [ 64 ] ;
V_sprintf_safe ( archVersion , " ServerGameDLL%03d " , g_iServerGameDLLVersion ) ;
serverGameDLL = ( IServerGameDLL * ) g_ServerFactory ( archVersion , NULL ) ;
if ( serverGameDLL )
break ;
- - g_iServerGameDLLVersion ;
if ( g_iServerGameDLLVersion < 4 )
{
g_iServerGameDLLVersion = 0 ;
Msg ( " Could not get IServerGameDLL interface from library %s " , szDllFilename ) ;
goto IgnoreThisDLL ;
}
}
serverGameEnts = ( IServerGameEnts * ) g_ServerFactory ( INTERFACEVERSION_SERVERGAMEENTS , NULL ) ;
if ( ! serverGameEnts )
{
ConMsg ( " Could not get IServerGameEnts interface from library %s " , szDllFilename ) ;
goto IgnoreThisDLL ;
}
serverGameClients = ( IServerGameClients * ) g_ServerFactory ( INTERFACEVERSION_SERVERGAMECLIENTS , NULL ) ;
if ( serverGameClients )
{
g_iServerGameClientsVersion = 4 ;
}
else
{
// Try the previous version.
const char * pINTERFACEVERSION_SERVERGAMECLIENTS_V3 = " ServerGameClients003 " ;
serverGameClients = ( IServerGameClients * ) g_ServerFactory ( pINTERFACEVERSION_SERVERGAMECLIENTS_V3 , NULL ) ;
if ( serverGameClients )
{
g_iServerGameClientsVersion = 3 ;
}
else
{
ConMsg ( " Could not get IServerGameClients interface from library %s " , szDllFilename ) ;
goto IgnoreThisDLL ;
}
}
serverGameDirector = ( IHLTVDirector * ) g_ServerFactory ( INTERFACEVERSION_HLTVDIRECTOR , NULL ) ;
if ( ! serverGameDirector )
{
ConMsg ( " Could not get IHLTVDirector interface from library %s " , szDllFilename ) ;
// this is not a critical
}
serverGameTags = ( IServerGameTags * ) g_ServerFactory ( INTERFACEVERSION_SERVERGAMETAGS , NULL ) ;
// Possible that this is NULL - optional interface
}
else
{
ConMsg ( " Could not find factory interface in library %s " , szDllFilename ) ;
goto IgnoreThisDLL ;
}
g_GameDLL = pDLL ;
return true ;
IgnoreThisDLL :
if ( pDLL ! = NULL )
{
g_pFileSystem - > UnloadModule ( pDLL ) ;
serverGameDLL = NULL ;
serverGameEnts = NULL ;
serverGameClients = NULL ;
}
return false ;
}
# pragma optimize( "", on )
//
// Scan DLL directory, load all DLLs that conform to spec.
//
void LoadEntityDLLs ( const char * szBaseDir , bool bIsServerOnly )
{
memset ( & gmodinfo , 0 , sizeof ( modinfo_t ) ) ;
gmodinfo . version = 1 ;
gmodinfo . svonly = true ;
// Run through all DLLs found in the extension DLL directory
g_GameDLL = NULL ;
sv_noclipduringpause = NULL ;
// Listing file for this game.
KeyValues * modinfo = new KeyValues ( " modinfo " ) ;
MEM_ALLOC_CREDIT ( ) ;
if ( modinfo - > LoadFromFile ( g_pFileSystem , " gameinfo.txt " ) )
{
Q_strncpy ( gmodinfo . szInfo , modinfo - > GetString ( " url_info " ) , sizeof ( gmodinfo . szInfo ) ) ;
Q_strncpy ( gmodinfo . szDL , modinfo - > GetString ( " url_dl " ) , sizeof ( gmodinfo . szDL ) ) ;
gmodinfo . version = modinfo - > GetInt ( " version " ) ;
gmodinfo . size = modinfo - > GetInt ( " size " ) ;
gmodinfo . svonly = modinfo - > GetInt ( " svonly " ) ? true : false ;
gmodinfo . cldll = modinfo - > GetInt ( " cldll " ) ? true : false ;
Q_strncpy ( gmodinfo . szHLVersion , modinfo - > GetString ( " hlversion " ) , sizeof ( gmodinfo . szHLVersion ) ) ;
}
modinfo - > deleteThis ( ) ;
// Load the game .dll
LoadThisDll ( " server " DLL_EXT_STRING , bIsServerOnly ) ;
if ( serverGameDLL )
{
Msg ( " server%s loaded for \" %s \" \n " , DLL_EXT_STRING , ( char * ) serverGameDLL - > GetGameDescription ( ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Retrieves a string value from the registry
//-----------------------------------------------------------------------------
# if defined(_WIN32)
void Sys_GetRegKeyValueUnderRoot ( HKEY rootKey , const char * pszSubKey , const char * pszElement , OUT_Z_CAP ( nReturnLength ) char * pszReturnString , int nReturnLength , const char * pszDefaultValue )
{
LONG lResult ; // Registry function result code
HKEY hKey ; // Handle of opened/created key
char szBuff [ 128 ] ; // Temp. buffer
ULONG dwDisposition ; // Type of key opening event
DWORD dwType ; // Type of key
DWORD dwSize ; // Size of element data
// Copying a string to itself is both unnecessary and illegal.
// Address sanitizer prohibits this so we have to fix this in order
// to continue testing with it.
if ( pszReturnString ! = pszDefaultValue )
{
// Assume the worst
Q_strncpy ( pszReturnString , pszDefaultValue , nReturnLength ) ;
}
// Create it if it doesn't exist. (Create opens the key otherwise)
lResult = VCRHook_RegCreateKeyEx (
rootKey , // handle of open key
pszSubKey , // address of name of subkey to open
0ul , // DWORD ulOptions, // reserved
" String " , // Type of value
REG_OPTION_NON_VOLATILE , // Store permanently in reg.
KEY_ALL_ACCESS , // REGSAM samDesired, // security access mask
NULL ,
& hKey , // Key we are creating
& dwDisposition ) ; // Type of creation
if ( lResult ! = ERROR_SUCCESS ) // Failure
return ;
// First time, just set to Valve default
if ( dwDisposition = = REG_CREATED_NEW_KEY )
{
// Just Set the Values according to the defaults
lResult = VCRHook_RegSetValueEx ( hKey , pszElement , 0 , REG_SZ , ( CONST BYTE * ) pszDefaultValue , Q_strlen ( pszDefaultValue ) + 1 ) ;
}
else
{
// We opened the existing key. Now go ahead and find out how big the key is.
dwSize = nReturnLength ;
lResult = VCRHook_RegQueryValueEx ( hKey , pszElement , 0 , & dwType , ( unsigned char * ) szBuff , & dwSize ) ;
// Success?
if ( lResult = = ERROR_SUCCESS )
{
// Only copy strings, and only copy as much data as requested.
if ( dwType = = REG_SZ )
{
Q_strncpy ( pszReturnString , szBuff , nReturnLength ) ;
pszReturnString [ nReturnLength - 1 ] = ' \0 ' ;
}
}
else
// Didn't find it, so write out new value
{
// Just Set the Values according to the defaults
lResult = VCRHook_RegSetValueEx ( hKey , pszElement , 0 , REG_SZ , ( CONST BYTE * ) pszDefaultValue , Q_strlen ( pszDefaultValue ) + 1 ) ;
}
} ;
// Always close this key before exiting.
VCRHook_RegCloseKey ( hKey ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Retrieves a DWORD value from the registry
//-----------------------------------------------------------------------------
void Sys_GetRegKeyValueUnderRootInt ( HKEY rootKey , const char * pszSubKey , const char * pszElement , long * plReturnValue , const long lDefaultValue )
{
LONG lResult ; // Registry function result code
HKEY hKey ; // Handle of opened/created key
ULONG dwDisposition ; // Type of key opening event
DWORD dwType ; // Type of key
DWORD dwSize ; // Size of element data
// Assume the worst
// Set the return value to the default
* plReturnValue = lDefaultValue ;
// Create it if it doesn't exist. (Create opens the key otherwise)
lResult = VCRHook_RegCreateKeyEx (
rootKey , // handle of open key
pszSubKey , // address of name of subkey to open
0ul , // DWORD ulOptions, // reserved
" String " , // Type of value
REG_OPTION_NON_VOLATILE , // Store permanently in reg.
KEY_ALL_ACCESS , // REGSAM samDesired, // security access mask
NULL ,
& hKey , // Key we are creating
& dwDisposition ) ; // Type of creation
if ( lResult ! = ERROR_SUCCESS ) // Failure
return ;
// First time, just set to Valve default
if ( dwDisposition = = REG_CREATED_NEW_KEY )
{
// Just Set the Values according to the defaults
lResult = VCRHook_RegSetValueEx ( hKey , pszElement , 0 , REG_DWORD , ( CONST BYTE * ) & lDefaultValue , sizeof ( DWORD ) ) ;
}
else
{
// We opened the existing key. Now go ahead and find out how big the key is.
dwSize = sizeof ( DWORD ) ;
lResult = VCRHook_RegQueryValueEx ( hKey , pszElement , 0 , & dwType , ( unsigned char * ) plReturnValue , & dwSize ) ;
// Success?
if ( lResult ! = ERROR_SUCCESS )
// Didn't find it, so write out new value
{
// Just Set the Values according to the defaults
lResult = VCRHook_RegSetValueEx ( hKey , pszElement , 0 , REG_DWORD , ( LPBYTE ) & lDefaultValue , sizeof ( DWORD ) ) ;
}
} ;
// Always close this key before exiting.
VCRHook_RegCloseKey ( hKey ) ;
}
void Sys_SetRegKeyValueUnderRoot ( HKEY rootKey , const char * pszSubKey , const char * pszElement , const char * pszValue )
{
LONG lResult ; // Registry function result code
HKEY hKey ; // Handle of opened/created key
//char szBuff[128]; // Temp. buffer
ULONG dwDisposition ; // Type of key opening event
//DWORD dwType; // Type of key
//DWORD dwSize; // Size of element data
// Create it if it doesn't exist. (Create opens the key otherwise)
lResult = VCRHook_RegCreateKeyEx (
rootKey , // handle of open key
pszSubKey , // address of name of subkey to open
0ul , // DWORD ulOptions, // reserved
" String " , // Type of value
REG_OPTION_NON_VOLATILE , // Store permanently in reg.
KEY_ALL_ACCESS , // REGSAM samDesired, // security access mask
NULL ,
& hKey , // Key we are creating
& dwDisposition ) ; // Type of creation
if ( lResult ! = ERROR_SUCCESS ) // Failure
return ;
// First time, just set to Valve default
if ( dwDisposition = = REG_CREATED_NEW_KEY )
{
// Just Set the Values according to the defaults
lResult = VCRHook_RegSetValueEx ( hKey , pszElement , 0 , REG_SZ , ( CONST BYTE * ) pszValue , Q_strlen ( pszValue ) + 1 ) ;
}
else
{
/*
// FIXE: We might want to support a mode where we only create this key, we don't overwrite values already present
// We opened the existing key. Now go ahead and find out how big the key is.
dwSize = nReturnLength ;
lResult = VCRHook_RegQueryValueEx ( hKey , pszElement , 0 , & dwType , ( unsigned char * ) szBuff , & dwSize ) ;
// Success?
if ( lResult = = ERROR_SUCCESS )
{
// Only copy strings, and only copy as much data as requested.
if ( dwType = = REG_SZ )
{
Q_strncpy ( pszReturnString , szBuff , nReturnLength ) ;
pszReturnString [ nReturnLength - 1 ] = ' \0 ' ;
}
}
else
*/
// Didn't find it, so write out new value
{
// Just Set the Values according to the defaults
lResult = VCRHook_RegSetValueEx ( hKey , pszElement , 0 , REG_SZ , ( CONST BYTE * ) pszValue , Q_strlen ( pszValue ) + 1 ) ;
}
} ;
// Always close this key before exiting.
VCRHook_RegCloseKey ( hKey ) ;
}
# endif
void Sys_GetRegKeyValue ( const char * pszSubKey , const char * pszElement , OUT_Z_CAP ( nReturnLength ) char * pszReturnString , int nReturnLength , const char * pszDefaultValue )
{
# if defined(_WIN32)
Sys_GetRegKeyValueUnderRoot ( HKEY_CURRENT_USER , pszSubKey , pszElement , pszReturnString , nReturnLength , pszDefaultValue ) ;
# else
//hushed Assert( !"Impl me" );
// Copying a string to itself is both unnecessary and illegal.
if ( pszReturnString ! = pszDefaultValue )
{
Q_strncpy ( pszReturnString , pszDefaultValue , nReturnLength ) ;
}
# endif
}
void Sys_GetRegKeyValueInt ( const char * pszSubKey , const char * pszElement , long * plReturnValue , long lDefaultValue )
{
# if defined(_WIN32)
Sys_GetRegKeyValueUnderRootInt ( HKEY_CURRENT_USER , pszSubKey , pszElement , plReturnValue , lDefaultValue ) ;
# else
//hushed Assert( !"Impl me" );
* plReturnValue = lDefaultValue ;
# endif
}
void Sys_SetRegKeyValue ( const char * pszSubKey , const char * pszElement , const char * pszValue )
{
# if defined(_WIN32)
Sys_SetRegKeyValueUnderRoot ( HKEY_CURRENT_USER , pszSubKey , pszElement , pszValue ) ;
# else
//hushed Assert( !"Impl me" );
# endif
}
# define SOURCE_ENGINE_APP_CLASS "Valve.Source"
void Sys_CreateFileAssociations ( int count , FileAssociationInfo * list )
{
# if defined(_WIN32)
if ( IsX360 ( ) )
return ;
char appname [ 512 ] ;
GetModuleFileName ( 0 , appname , sizeof ( appname ) ) ;
Q_FixSlashes ( appname ) ;
Q_strlower ( appname ) ;
char quoted_appname_with_arg [ 512 ] ;
Q_snprintf ( quoted_appname_with_arg , sizeof ( quoted_appname_with_arg ) , " \" %s \" \" %%1 \" " , appname ) ;
char base_exe_name [ 256 ] ;
Q_FileBase ( appname , base_exe_name , sizeof ( base_exe_name ) ) ;
Q_DefaultExtension ( base_exe_name , " .exe " , sizeof ( base_exe_name ) ) ;
// HKEY_CLASSES_ROOT/Valve.Source/shell/open/command == "u:\tf2\hl2.exe" "%1" quoted
Sys_SetRegKeyValueUnderRoot ( HKEY_CLASSES_ROOT , va ( " %s \\ shell \\ open \\ command " , SOURCE_ENGINE_APP_CLASS ) , " " , quoted_appname_with_arg ) ;
// HKEY_CLASSES_ROOT/Applications/hl2.exe/shell/open/command == "u:\tf2\hl2.exe" "%1" quoted
Sys_SetRegKeyValueUnderRoot ( HKEY_CLASSES_ROOT , va ( " Applications \\ %s \\ shell \\ open \\ command " , base_exe_name ) , " " , quoted_appname_with_arg ) ;
for ( int i = 0 ; i < count ; i + + )
{
FileAssociationInfo * fa = & list [ i ] ;
char binding [ 32 ] ;
binding [ 0 ] = 0 ;
// Create file association for our .exe
// HKEY_CLASSES_ROOT/.dem == "Valve.Source"
Sys_GetRegKeyValueUnderRoot ( HKEY_CLASSES_ROOT , fa - > extension , " " , binding , sizeof ( binding ) , " " ) ;
if ( Q_strlen ( binding ) = = 0 )
{
Sys_SetRegKeyValueUnderRoot ( HKEY_CLASSES_ROOT , fa - > extension , " " , SOURCE_ENGINE_APP_CLASS ) ;
}
}
# endif
}
void Sys_NoCrashDialog ( )
{
# if defined(_WIN32)
: : SetErrorMode ( SetErrorMode ( SEM_NOGPFAULTERRORBOX ) | SEM_NOGPFAULTERRORBOX ) ;
# endif
}
void Sys_TestSendKey ( const char * pKey )
{
# if defined(_WIN32) && !defined(USE_SDL) && !defined(_XBOX)
int key = pKey [ 0 ] ;
if ( pKey [ 0 ] = = ' \\ ' & & pKey [ 1 ] = = ' r ' )
{
key = VK_RETURN ;
}
HWND hWnd = ( HWND ) game - > GetMainWindow ( ) ;
PostMessageA ( hWnd , WM_KEYDOWN , key , 0 ) ;
PostMessageA ( hWnd , WM_KEYUP , key , 0 ) ;
//void Key_Event (int key, bool down);
//Key_Event( key, 1 );
//Key_Event( key, 0 );
# endif
}
void Sys_OutputDebugString ( const char * msg )
{
Plat_DebugString ( msg ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void UnloadEntityDLLs ( void )
{
if ( ! g_GameDLL )
return ;
// Unlink the cvars associated with game DLL
FileSystem_UnloadModule ( g_GameDLL ) ;
g_GameDLL = NULL ;
serverGameDLL = NULL ;
serverGameEnts = NULL ;
serverGameClients = NULL ;
sv_noclipduringpause = NULL ;
}
CON_COMMAND ( star_memory , " Dump memory stats " )
{
// get a current stat of available memory
// 32 MB is reserved and fixed by OS, so not reporting to allow memory loggers sync
# ifdef LINUX
struct mallinfo memstats = mallinfo ( ) ;
Msg ( " sbrk size: %.2f MB, Used: %.2f MB, #mallocs = %d \n " ,
memstats . arena / ( 1024.0 * 1024.0 ) , memstats . uordblks / ( 1024.0 * 1024.0 ) , memstats . hblks ) ;
# elif OSX
struct mstats memstats = mstats ( ) ;
Msg ( " Available %.2f MB, Used: %.2f MB, #mallocs = %lu \n " ,
memstats . bytes_free / ( 1024.0 * 1024.0 ) , memstats . bytes_used / ( 1024.0 * 1024.0 ) , memstats . chunks_used ) ;
2022-11-27 21:48:27 +08:00
# elif PLATFORM_BSD
2022-11-25 03:04:29 +08:00
# warning TODO: Implement memory stats (peace of sheet of course)
# else // Win32
2020-04-23 00:56:21 +08:00
MEMORYSTATUS stat ;
GlobalMemoryStatus ( & stat ) ;
Msg ( " Available: %.2f MB, Used: %.2f MB, Free: %.2f MB \n " ,
stat . dwTotalPhys / ( 1024.0f * 1024.0f ) - 32.0f ,
( stat . dwTotalPhys - stat . dwAvailPhys ) / ( 1024.0f * 1024.0f ) - 32.0f ,
stat . dwAvailPhys / ( 1024.0f * 1024.0f ) ) ;
# endif
}