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.
1600 lines
43 KiB
1600 lines
43 KiB
//========= 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> |
|
#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 |
|
} |
|
#elif defined( OSX ) |
|
// 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 ); |
|
|
|
int *p = 0; |
|
*p = 0xdeadbeef; |
|
#elif defined( LINUX ) |
|
// 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() ); |
|
int *p = 0; |
|
*p = 0xdeadbeef; |
|
#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; |
|
|
|
#if defined(OSX) |
|
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 |
|
//----------------------------------------------------------------------------- |
|
CThreadLocalInt<> g_bInSpew; |
|
|
|
#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 ); |
|
#else |
|
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 |
|
}
|
|
|