//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // // $NoKeywords: $ //============================================================================= #ifdef _WIN32 #include #define snprintf _snprintf #define vsnprintf _vsnprintf #else #include #include #include #include #include #include #include #include #include #include #endif //#define FRAMERATE // define me to have hlds print out what it thinks the framerate is #include #include #include "sys_ded.h" #include "conproc.h" #include "dedicated.h" #include "exefuncs.h" #include "dll_state.h" #include "enginecallback.h" #if defined( _WIN32 ) #include "../utils/procinfo/procinfo.h" #endif #ifdef _WIN32 static HANDLE hinput; static HANDLE houtput; static const char *g_pszengine = "swds.dll"; #else static const char *g_pszengine = "engine_i386.so"; static char g_szEXEName[ 256 ]; #endif // System Memory & Size static unsigned char *gpMemBase = NULL; static int giMemSize = 0x2000000; // 32 Mb default heapsize exefuncs_t ef; static char console_text[256]; static int console_textlen; static long hDLLThirdParty = 0L; char *gpszCmdLine = NULL; SleepType Sys_Sleep; void Sys_Sleep_Old( int msec ) { #ifdef _WIN32 Sleep( msec ); #else usleep(msec * 1000); #endif } #if !defined ( _WIN32 ) typedef int (*NET_Sleep_t)( void ); NET_Sleep_t NET_Sleep =NULL; extern long ghMod; // from engine.cpp void Sys_Sleep_Net( int msec ) { if ( NET_Sleep != NULL) { NET_Sleep(); } else { Sys_Sleep_Old(msec); // NET_Sleep isn't hooked yet, fallback to the old method } } volatile char paused=0; // this checks if pause has run yet, tell the compiler it can change at any time // for Sys_Sleep_Timer() void alarmFunc(int num) { signal( SIGALRM, alarmFunc ); // reset the signal handler if(!paused) { // paused is 0, the timer has fired before the pause was called... Lets queue it again struct itimerval itim; itim.it_interval.tv_sec = 0; itim.it_interval.tv_usec = 0; itim.it_value.tv_sec = 0; itim.it_value.tv_usec = 1000; // get it to run again real soon setitimer( ITIMER_REAL, &itim, 0 ); } } #endif void Sys_Sleep_Timer( int msec ) { #ifdef _WIN32 Sleep( msec ); #else // linux runs on a 100Hz scheduling clock, so the minimum latency from // usleep is 10msec. However, people want lower latency than this.. // // There are a few solutions, one is to use the realtime scheduler in the // kernel BUT this needs root privelleges to start. It also can play // unfriendly with other programs. // Another solution is to use software timers, they use the RTC of the // system and are accurate to microseconds (or so). // timers, via setitimer() are used here struct itimerval tm; tm.it_value.tv_sec=msec/1000; // convert msec to seconds tm.it_value.tv_usec=static_cast<__suseconds_t>((msec%1000)*1E3); // get the number of msecs and change to micros tm.it_interval.tv_sec = 0; tm.it_interval.tv_usec = 0; paused=0; if( setitimer(ITIMER_REAL,&tm,NULL)==0) { // set the timer to trigger pause(); // wait for the signal } paused=1; #endif } void Sys_Sleep_Select( int msec ) { #ifdef _WIN32 Sleep( msec ); #else // _WIN32 struct timeval tv; // Assumes msec < 1000 tv.tv_sec = 0; tv.tv_usec = 1000 * msec; select( 1, NULL, NULL, NULL, &tv ); #endif // _WIN32 } void *Sys_GetProcAddress( long library, const char *name ) { #ifdef _WIN32 return ( void * )GetProcAddress( (HMODULE)library, name ); #else // LINUX return dlsym( (void *)library, name ); #endif } long Sys_LoadLibrary( char *lib ) { void *hDll = NULL; #ifdef _WIN32 hDll = ::LoadLibrary( lib ); #else char cwd[1024]; char absolute_lib[1024]; if (!getcwd(cwd, sizeof(cwd))) Sys_ErrorMessage(1, "Sys_LoadLibrary: Couldn't determine current directory."); if (cwd[strlen(cwd)-1] == '/') cwd[strlen(cwd)-1] = 0; snprintf(absolute_lib, sizeof(absolute_lib), "%s/%s", cwd, lib); hDll = dlopen( absolute_lib, RTLD_NOW ); if ( !hDll ) { Sys_ErrorMessage( 1, dlerror() ); } #endif return (long)hDll; } void Sys_FreeLibrary( long library ) { if ( !library ) return; #ifdef _WIN32 ::FreeLibrary( (HMODULE)library ); #else dlclose( (void *)library ); #endif } int Sys_GetExecutableName( char *out ) { #ifdef _WIN32 if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, 256 ) ) { return 0; } #else strcpy( out, g_szEXEName ); #endif return 1; } /* ============== Sys_ErrorMessage Engine is erroring out, display error in message box ============== */ void Sys_ErrorMessage( int level, const char *msg ) { #ifdef _WIN32 MessageBox( NULL, msg, "Half-Life", MB_OK ); PostQuitMessage(0); #else printf( "%s\n", msg ); exit( -1 ); #endif } #ifdef _WIN32 /* ============== UpdateStatus Update status line at top of console if engine is running ============== */ void UpdateStatus( int force ) { static double tLast = 0.0; double tCurrent; char szPrompt[256]; int n, spec, nMax; char szMap[32]; float fps; if ( !engineapi.Host_GetHostInfo ) return; tCurrent = (float)( timeGetTime() / 1000.0f ); engineapi.Host_GetHostInfo( &fps, &n, &spec, &nMax, szMap ); if ( !force ) { if ( ( tCurrent - tLast ) < 0.5f ) return; } tLast = tCurrent; snprintf( szPrompt, sizeof(szPrompt), "%.1f fps %2i(%2i spec)/%2i on %16s", (float)fps, n, spec, nMax, szMap); WriteStatusText( szPrompt ); } #endif /* ================ Sys_ConsoleOutput Print text to the dedicated console ================ */ void Sys_ConsoleOutput (char *string) { #ifdef _WIN32 unsigned long dummy; char text[256]; if (console_textlen) { text[0] = '\r'; memset(&text[1], ' ', console_textlen); text[console_textlen+1] = '\r'; text[console_textlen+2] = 0; WriteFile(houtput, text, console_textlen+2, &dummy, NULL); } WriteFile(houtput, string, strlen(string), &dummy, NULL); if (console_textlen) { WriteFile(houtput, console_text, console_textlen, &dummy, NULL); } UpdateStatus( 1 /* force */ ); #else printf( "%s", string ); fflush( stdout ); #endif } /* ============== Sys_Printf Engine is printing to console ============== */ void Sys_Printf(char *fmt, ...) { // Dump text to debugging console. va_list argptr; char szText[1024]; va_start (argptr, fmt); vsnprintf (szText, sizeof(szText), fmt, argptr); va_end (argptr); // Get Current text and append it. Sys_ConsoleOutput( szText ); } /* ============== Load3rdParty Load support for third party .dlls ( gamehost ) ============== */ void Load3rdParty( void ) { // Only do this if the server operator wants the support. // ( In case of malicious code, too ) if ( CheckParm( "-usegh" ) ) { hDLLThirdParty = Sys_LoadLibrary( "ghostinj.dll" ); } } /* ============== EF_VID_ForceUnlockedAndReturnState Dummy funcion called by engine ============== */ int EF_VID_ForceUnlockedAndReturnState(void) { return 0; } /* ============== EF_VID_ForceLockState Dummy funcion called by engine ============== */ void EF_VID_ForceLockState(int) { } /* ============== CheckParm Search for psz in command line to .exe, if **ppszValue is set, then the pointer is directed at the NEXT argument in the command line ============== */ char *CheckParm(const char *psz, char **ppszValue) { int i; static char sz[128]; char *pret; if (!gpszCmdLine) return NULL; pret = strstr( gpszCmdLine, psz ); // should we return a pointer to the value? if (pret && ppszValue) { char *p1 = pret; *ppszValue = NULL; while ( *p1 && (*p1 != 32)) p1++; if (p1 != 0) { char *p2 = ++p1; for ( i = 0; i < 128; i++ ) { if ( !*p2 || (*p2 == 32)) break; sz[i] = *p2++; } sz[i] = 0; *ppszValue = &sz[0]; } } return pret; } /* ============== InitInstance ============== */ int InitInstance( void ) { Load3rdParty(); Eng_SetState( DLL_INACTIVE ); memset( &ef, 0, sizeof( ef ) ); // Function pointers used by dedicated server ef.Sys_Printf = Sys_Printf; ef.ErrorMessage = Sys_ErrorMessage; ef.VID_ForceLockState = EF_VID_ForceLockState; ef.VID_ForceUnlockedAndReturnState = EF_VID_ForceUnlockedAndReturnState; #ifdef _WIN32 // Data ef.fMMX = PROC_IsMMX(); ef.iCPUMhz = PROC_GetSpeed(); // in MHz #endif return 1; } /* ================ Sys_ConsoleInput ================ */ #ifdef _WIN32 char *Sys_ConsoleInput (void) { INPUT_RECORD recs[1024]; unsigned long dummy; int ch; unsigned long numread, numevents; while ( 1 ) { if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) { exit( -1 ); } if (numevents <= 0) break; if ( !ReadConsoleInput(hinput, recs, 1, &numread) ) { exit( -1 ); } if (numread != 1) { exit( -1 ); } if ( recs[0].EventType == KEY_EVENT ) { if ( !recs[0].Event.KeyEvent.bKeyDown ) { ch = recs[0].Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (console_textlen) { console_text[console_textlen] = 0; console_textlen = 0; return console_text; } break; case '\b': if (console_textlen) { console_textlen--; WriteFile(houtput, "\b \b", 3, &dummy, NULL); } break; default: if (ch >= ' ') { if (console_textlen < sizeof(console_text)-2) { WriteFile(houtput, &ch, 1, &dummy, NULL); console_text[console_textlen] = ch; console_textlen++; } } break; } } } } return NULL; } #else char *Sys_ConsoleInput(void) { struct timeval tvTimeout; fd_set fdSet; unsigned char ch; FD_ZERO( &fdSet ); FD_SET( STDIN_FILENO, &fdSet ); tvTimeout.tv_sec = 0; tvTimeout.tv_usec = 0; if ( select( 1, &fdSet, NULL, NULL, &tvTimeout ) == -1 || !FD_ISSET( STDIN_FILENO, &fdSet ) ) return NULL; console_textlen = 0; // We're going to shove a newline onto the end of the input later in // ProcessConsoleInput(), so only accept 254 chars instead of 255. -LH while ( read( STDIN_FILENO, &ch, 1 ) ) { if ( ( ch == 10 ) || ( console_textlen == 254 ) ) { // For commands longer than we can accept, consume the remainder of the input buffer if ( ( console_textlen == 254 ) && ( ch != 10 ) ) { while ( read( STDIN_FILENO, &ch, 1 ) ) { if ( ch == 10 ) break; } } //Null terminate string and return console_text[ console_textlen ] = 0; console_textlen = 0; return console_text; } console_text[ console_textlen++ ] = ch; } return NULL; } #endif #ifdef _WIN32 /* ============== WriteStatusText ============== */ void WriteStatusText( char *szText ) { char szFullLine[81]; COORD coord; DWORD dwWritten = 0; WORD wAttrib[80]; int i; for ( i = 0; i < 80; i++ ) { wAttrib[i] = FOREGROUND_RED | FOREGROUND_INTENSITY; } memset( szFullLine, 0, sizeof(szFullLine) ); snprintf( szFullLine, sizeof( szFullLine ), "%s", szText ); coord.X = 0; coord.Y = 0; WriteConsoleOutputAttribute( houtput, wAttrib, 80, coord, &dwWritten ); WriteConsoleOutputCharacter( houtput, szFullLine, 80, coord, &dwWritten ); } #endif /* ============== CreateConsoleWindow Create console window ( overridable? ) ============== */ int CreateConsoleWindow( void ) { #ifdef _WIN32 if ( !AllocConsole () ) { return 0; } hinput = GetStdHandle (STD_INPUT_HANDLE); houtput = GetStdHandle (STD_OUTPUT_HANDLE); InitConProc(); #endif return 1; } /* ============== DestroyConsoleWindow ============== */ void DestroyConsoleWindow( void ) { #ifdef _WIN32 FreeConsole (); // shut down QHOST hooks if necessary DeinitConProc (); #endif } /* ============== ProcessConsoleInput ============== */ void ProcessConsoleInput( void ) { char *s; if ( !engineapi.Cbuf_AddText ) return; do { s = Sys_ConsoleInput (); if (s) { char szBuf[ 256 ]; snprintf( szBuf, sizeof(szBuf), "%s\n", s ); engineapi.Cbuf_AddText ( szBuf ); } } while (s); } /* ================ GameInit ================ */ int GameInit(void) { char *p; // Command line override if ( (CheckParm ("-heapsize", &p ) ) && p ) { giMemSize = atoi( p ) * 1024; } Sys_Sleep=Sys_Sleep_Old; #if !defined ( _WIN32 ) char *pPingType; int type; if ( (CheckParm ("-pingboost", &pPingType)) && pPingType ) { type=atoi( pPingType ); switch( type ) { case 1: //printf("Using timer method\n"); signal(SIGALRM,alarmFunc); Sys_Sleep=Sys_Sleep_Timer; break; case 2: Sys_Sleep = Sys_Sleep_Select; break; case 3: Sys_Sleep = Sys_Sleep_Net; // we Sys_GetProcAddress NET_Sleep() from //engine_i386.so later in this function break; default: // just in case Sys_Sleep=Sys_Sleep_Old; break; } } #endif // Try and allocated it #ifdef _WIN32 gpMemBase = (unsigned char *)::GlobalAlloc( GMEM_FIXED, giMemSize ); #else gpMemBase = (unsigned char *)malloc( giMemSize ); #endif if (!gpMemBase) { return 0; } #ifdef _WIN32 // Check that we are running on Win32 OSVERSIONINFO vinfo; vinfo.dwOSVersionInfoSize = sizeof(vinfo); if ( !GetVersionEx ( &vinfo ) ) { return 0; } if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32s ) { return 0; } #endif if ( !Eng_Load( gpszCmdLine, &ef, giMemSize, gpMemBase, g_pszengine, DLL_NORMAL ) ) { return 0; } #if !defined ( _WIN32 ) if ( type == 3 ) { NET_Sleep=(NET_Sleep_t)Sys_GetProcAddress(ghMod,"NET_Sleep_Timeout"); //printf("Net_Sleep:%p\n",NET_Sleep); } #endif Eng_SetState( DLL_ACTIVE ); return 1; } /* ============== GameShutdown ============== */ void GameShutdown( void ) { Eng_Unload(); if ( gpMemBase ) { #ifdef _WIN32 ::GlobalFree( gpMemBase ); #else free( gpMemBase ); #endif gpMemBase = NULL; } } #ifdef _WIN32 BOOL gbAppHasBeenTerminated = FALSE; BOOL CtrlHandler(DWORD fdwCtrlType) { switch ( fdwCtrlType ) { case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: gbAppHasBeenTerminated = TRUE; return TRUE; default: return FALSE; } } /* ============== WinMain EXE entry point ============== */ int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow ) { int iret = -1; // Store off command line for argument searching gpszCmdLine = strdup( GetCommandLine() ); Sys_Sleep=Sys_Sleep_Old; // win32 doesn't have pingbooster options :) if ( !InitInstance() ) { goto cleanup; } if ( !CreateConsoleWindow() ) { goto cleanup; } if ( !GameInit() ) { goto cleanup; } if ( engineapi.SetStartupMode ) { engineapi.SetStartupMode( 1 ); } while ( 1 ) { int bDone = 0; static double oldtime = 0.0; MSG msg; double newtime; double dtime; if ( gbAppHasBeenTerminated ) break; // Try to allow other apps to get some CPU Sys_Sleep( 1 ); if ( !engineapi.Sys_FloatTime ) break; while ( 1 ) { newtime = engineapi.Sys_FloatTime(); if ( newtime < oldtime ) { oldtime = newtime - 0.05; } dtime = newtime - oldtime; if ( gbAppHasBeenTerminated ) break; if ( dtime > 0.001 ) break; // Running really fast, yield some time to other apps Sys_Sleep( 1 ); } while ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) { if ( gbAppHasBeenTerminated ) break; if (!::GetMessage( &msg, NULL, 0, 0)) { bDone = 1; break; } ::TranslateMessage( &msg ); ::DispatchMessage( &msg ); } if ( bDone ) break; ProcessConsoleInput(); if ( engineapi.Host_Frame ) { Eng_Frame( 0, dtime ); } UpdateStatus( 0 /* don't force */ ); oldtime = newtime; } GameShutdown(); DestroyConsoleWindow(); iret = 1; cleanup: if ( gpszCmdLine ) { free( gpszCmdLine ); } return iret; } #else #define MAX_LINUX_CMDLINE 512 static char cmdline[ MAX_LINUX_CMDLINE ]; void BuildCmdLine( int argc, char **argv ) { int len; int i; for (len = 0, i = 1; i < argc; i++) { len += strlen(argv[i]) + 1; } if ( len > MAX_LINUX_CMDLINE ) { printf( "command line too long, %i max\n", MAX_LINUX_CMDLINE ); exit(-1); return; } cmdline[0] = '\0'; for ( i = 1; i < argc; i++ ) { if ( i > 1 ) { strcat( cmdline, " " ); } strcat( cmdline, argv[ i ] ); } } char *GetCommandLine( void ) { return cmdline; } int main(int argc, char **argv) { int iret = -1; #ifdef _DEBUG snprintf(g_szEXEName, sizeof( g_szEXEName ), "hlds_run.dbg" ); #else snprintf(g_szEXEName, sizeof( g_szEXEName ), "%s", *argv ); #endif // Store off command line for argument searching BuildCmdLine(argc, argv); gpszCmdLine = strdup( GetCommandLine() ); if ( !InitInstance() ) { goto cleanup; } if ( !CreateConsoleWindow() ) { goto cleanup; } if ( !GameInit() ) { goto cleanup; } if ( engineapi.SetStartupMode ) { engineapi.SetStartupMode( 1 ); } while ( 1 ) { static double oldtime = 0.0; double newtime; double dtime; #ifdef FRAMERATE static int frameNumber=0,frameRate=0; static struct timeval beforeTime; if(frameRate==0) { gettimeofday(&beforeTime,NULL); } frameRate++; if(frameRate%1000==0) { struct timeval afterTime; float timediff; gettimeofday(&afterTime,NULL); timediff= (float)(afterTime.tv_sec-beforeTime.tv_sec)+ ((float)(afterTime.tv_usec-beforeTime.tv_usec))/1E6; printf("Frame Rate:%f\n",(float)frameRate/timediff); frameRate=0; } #endif Sys_Sleep( 1 ); if ( !engineapi.Sys_FloatTime ) break; while ( 1 ) { newtime = engineapi.Sys_FloatTime(); if ( newtime < oldtime ) { oldtime = newtime - 0.05; } dtime = newtime - oldtime; if ( dtime > 0.001 ) break; // Running really fast, yield some time to other apps Sys_Sleep( 1 ); } ProcessConsoleInput(); Eng_Frame( 0, dtime ); oldtime = newtime; } GameShutdown(); DestroyConsoleWindow(); iret = 1; cleanup: if ( gpszCmdLine ) { free( gpszCmdLine ); } if ( hDLLThirdParty ) { Sys_FreeLibrary( hDLLThirdParty ); hDLLThirdParty = 0L; } return iret; } #endif