halflife-thewastes-sdk/dedicated/sys_ded.cpp

1058 lines
18 KiB
C++

//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/time.h>
#include <malloc.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "sys_ded.h"
#include "conproc.h"
#include "dedicated.h"
#include "exefuncs.h"
#include "crc.h"
#include "dll_state.h"
#include "enginecallback.h"
#include "md5.h"
#if defined( _WIN32 )
#include "../utils/procinfo/procinfo.h"
#endif
#ifdef _WIN32
static const char *g_pszengine = "sw.dll";
#else
static const char *g_pszengine = "engine_i386.so";
static char g_szEXEName[ 256 ];
#endif
#define MINIMUM_WIN_MEMORY 0x0c00000
#define MAXIMUM_WIN_MEMORY 0x2000000 // max 32 mb
#define FIFTEEN_MEGS ( 15 * 1024 * 1024 )
#ifdef _WIN32
static HANDLE hinput;
static HANDLE houtput;
#endif
// System Memory & Size
static unsigned char *gpMemBase = NULL;
static int giMemSize = 0x1800000; // 24 Mb Linux heapsize
exefuncs_t ef;
static char console_text[256];
static int console_textlen;
static long hDLLThirdParty = 0L;
char *gpszCmdLine = NULL;
void Sys_Sleep( int msec )
{
#ifdef _WIN32
Sleep( msec );
#else
usleep(msec * 1000);
#endif
}
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;
sprintf(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, "The Wastes", 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;
sprintf( 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);
vsprintf (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" );
}
}
/*
============
StripExtension
Strips the extension off a filename. Works backward to insure stripping
of extensions only, and not parts of the path that might contain a
period (i.e. './hlds_run').
============
*/
void StripExtension (char *in, char *out)
{
char * in_current = in + strlen(in);
char * out_current = out + strlen(in);
int found_extension = 0;
while (in_current >= in) {
if ((found_extension == 0) && (*in_current == '.')) {
*out_current = 0;
found_extension = 1;
}
else {
if ((*in_current == '/') || (*in_current == '\\'))
found_extension = 1;
*out_current = *in_current;
}
in_current--;
out_current--;
}
}
/*
==============
CheckExeChecksum
Simple self-crc check
==============
*/
int CheckExeChecksum( void )
{
unsigned char g_MD5[16];
char szFileName[ 256 ];
unsigned int newdat = 0;
unsigned int olddat;
char datfile[ 256 ];
// Get our filename
if ( !Sys_GetExecutableName( szFileName ) )
{
return 0;
}
// compute raw 16 byte hash value
if ( !MD5_Hash_File( g_MD5, szFileName ) )
{
return 0;
}
StripExtension( szFileName, datfile );
strcat( datfile, ".dat" );
// Check .dat file ( or write a new one if running with -newdat )
FILE *fp = fopen( datfile, "rb" );
if ( !fp || CheckParm( "-newdat" ) ) // No existing file, or we are asked to create a new one
{
if ( fp )
{
fclose( fp );
}
newdat = *(unsigned int *)&g_MD5[0];
fp = fopen ( datfile, "wb" );
if ( fp )
{
fwrite( &newdat, sizeof( unsigned int ), 1, fp );
fclose( fp );
}
}
else
{
int bOk = 0;
if ( fread( &newdat, sizeof( unsigned int ), 1, fp ) == 1 )
bOk = 1;
fclose( fp );
if ( bOk )
{
olddat = *(unsigned int *)&g_MD5[0];
if ( olddat != newdat )
{
const char *pmsg = "Your Half-Life executable appears to have been modified. Please check your system for viruses and then re-install Half-Life.";
#ifdef _WIN32
MessageBox( NULL, pmsg, "The Wastes Dedicated Server", MB_OK );
#else
printf( "%s\n", pmsg );
#endif
return 0;
}
}
}
return 1;
}
/*
==============
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();
#if !defined( _DEBUG )
if ( !CheckExeChecksum() )
return 0;
#endif
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)
{
char ch;
int len;
fd_set fdset;
struct timeval timeout;
char szInput[256];
char iInput = 0;
FD_ZERO(&fdset);
FD_SET(0, &fdset); // stdin
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
return NULL;
while (read(0,&ch,1))
{
if (iInput >= 255)
continue;
if (ch == 10)
{
char *pszret = NULL;
//Null terminate string and return if we have anything
szInput[iInput] = 0;
if (iInput > 0)
pszret = szInput;
iInput = 0;
return pszret;
}
szInput[iInput++] = 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, 81 );
strcpy( szFullLine, 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 ];
sprintf( szBuf, "%s\n", s );
engineapi.Cbuf_AddText ( szBuf );
}
} while (s);
}
/*
================
GameInit
================
*/
int GameInit(void)
{
char *p;
#ifdef _WIN32
MEMORYSTATUS Buffer;
memset( &Buffer, 0, sizeof( Buffer ) );
Buffer.dwLength = sizeof( MEMORYSTATUS );
GlobalMemoryStatus ( &Buffer );
// take the greater of all the available memory or half the total memory,
// but at least 10 Mb and no more than 32 Mb, unless they explicitly
// request otherwise
giMemSize = Buffer.dwTotalPhys;
if ( giMemSize < FIFTEEN_MEGS )
{
return 0;
}
if ( giMemSize < (int)( Buffer.dwTotalPhys >> 1 ) )
{
giMemSize = (int)( Buffer.dwTotalPhys >> 1 );
}
// At least 10 mb, even if we have to swap a lot.
if (giMemSize <= MINIMUM_WIN_MEMORY)
{
giMemSize = MINIMUM_WIN_MEMORY;
}
else if (giMemSize > MAXIMUM_WIN_MEMORY)
{
giMemSize = MAXIMUM_WIN_MEMORY;
}
#endif
// Command line override
if ( (CheckParm ("-heapsize", &p ) ) && p )
{
giMemSize = atoi( p ) * 1024;
}
// Command line to force running with minimal memory.
if (CheckParm ("-minmemory", NULL))
{
giMemSize = MINIMUM_WIN_MEMORY;
}
// 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;
}
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
/*
==============
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() );
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;
// 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 ( 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 (!::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
strcpy(g_szEXEName, "hlds_run.dbg" );
#else
strcpy(g_szEXEName, *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 )
{
char *p;
static double oldtime = 0.0;
double newtime;
double dtime;
// 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 ( dtime > 0.001 )
break;
// Running really fast, yield some time to other apps
Sys_Sleep( 1 );
}
Eng_Frame( 0, dtime );
p = Sys_ConsoleInput();
if ( p )
{
engineapi.Cbuf_AddText( p );
engineapi.Cbuf_AddText( "\n" );
}
oldtime = newtime;
}
GameShutdown();
DestroyConsoleWindow();
iret = 1;
cleanup:
if ( gpszCmdLine )
{
free( gpszCmdLine );
}
if ( hDLLThirdParty )
{
Sys_FreeLibrary( hDLLThirdParty );
hDLLThirdParty = 0L;
}
return iret;
}
#endif