diff options
Diffstat (limited to 'engine/sys_dll.cpp')
| -rw-r--r-- | engine/sys_dll.cpp | 1600 |
1 files changed, 1600 insertions, 0 deletions
diff --git a/engine/sys_dll.cpp b/engine/sys_dll.cpp new file mode 100644 index 0000000..a7795f4 --- /dev/null +++ b/engine/sys_dll.cpp @@ -0,0 +1,1600 @@ +//========= 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 +} |