diff options
Diffstat (limited to 'dedicated')
25 files changed, 5284 insertions, 0 deletions
diff --git a/dedicated/console/TextConsoleUnix.cpp b/dedicated/console/TextConsoleUnix.cpp new file mode 100644 index 0000000..4f71845 --- /dev/null +++ b/dedicated/console/TextConsoleUnix.cpp @@ -0,0 +1,419 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// CTextConsoleUnix.cpp: Unix implementation of the TextConsole class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef _WIN32 + +#include <sys/ioctl.h> + +#include "TextConsoleUnix.h" +#include "tier0/icommandline.h" +#include "tier1/utllinkedlist.h" +#include "filesystem.h" +#include "../thirdparty/libedit-3.1/src/histedit.h" +#include "tier0/vprof.h" + +#define CONSOLE_LOG_FILE "console.log" + +static pthread_mutex_t g_lock; +static pthread_t g_threadid = (pthread_t)-1; +CUtlLinkedList< CUtlString > g_Commands; +static volatile int g_ProcessingCommands = false; + +#if defined( LINUX ) + +// Dynamically load the libtinfo stuff. On servers without a tty attached, +// we get to skip all of these dependencies on servers without ttys. +#define TINFO_SYM(rc, fn, params, args, ret) \ + typedef rc (*DYNTINFOFN_##fn) params; \ + static DYNTINFOFN_##fn g_TINFO_##fn; \ + extern "C" rc fn params \ + { \ + ret g_TINFO_##fn args; \ + } + +TINFO_SYM(char *, tgoto, (char *string, int x, int y), (string, x, y), return); +TINFO_SYM(int, tputs, (const char *str, int affcnt, int (*putc)(int)), (str, affcnt, putc), return); +TINFO_SYM(int, tgetflag, (char *id), (id), return); +TINFO_SYM(int, tgetnum, (char *id), (id), return); +TINFO_SYM(int, tgetent, (char *bufp, const char *name), (bufp, name), return); +TINFO_SYM(char *, tgetstr, (char *id, char **area), (id, area), return); + +#endif // LINUX + +//---------------------------------------------------------------------------------------------------------------------- +// init_tinfo_functions +//---------------------------------------------------------------------------------------------------------------------- +static bool init_tinfo_functions() +{ +#if !defined( LINUX ) + return true; +#else + static void *s_ncurses_handle = NULL; + + if ( !s_ncurses_handle ) + { + // Long time ago, ncurses was two libraries. So if libtinfo fails, try libncurses. + static const char *names[] = { "libtinfo.so.5", "libncurses.so.5" }; + + for ( int i = 0; !s_ncurses_handle && ( i < ARRAYSIZE( names ) ); i++ ) + { + bool bFailed = true; + s_ncurses_handle = dlopen( names[i], RTLD_NOW ); + + if ( s_ncurses_handle ) + { + bFailed = false; +#define LOADTINFOFUNC(_handle, _func, _failed) \ + do { \ + g_TINFO_##_func = ( DYNTINFOFN_##_func )dlsym(_handle, #_func); \ + if ( !g_TINFO_##_func) \ + _failed = true; \ + } while (0) + + LOADTINFOFUNC( s_ncurses_handle, tgoto, bFailed ); + LOADTINFOFUNC( s_ncurses_handle, tputs, bFailed ); + LOADTINFOFUNC( s_ncurses_handle, tgetflag, bFailed ); + LOADTINFOFUNC( s_ncurses_handle, tgetnum, bFailed ); + LOADTINFOFUNC( s_ncurses_handle, tgetent, bFailed ); + LOADTINFOFUNC( s_ncurses_handle, tgetstr, bFailed ); +#undef LOADTINFOFUNC + } + + if ( bFailed ) + s_ncurses_handle = NULL; + } + + if ( !s_ncurses_handle ) + { + fprintf( stderr, "\nWARNING: Failed to load 32-bit libtinfo.so.5 or libncurses.so.5.\n" + " Please install (lib32tinfo5 / ncurses-libs.i686 / equivalent) to enable readline.\n\n"); + } + } + + return !!s_ncurses_handle; +#endif // LINUX +} + +static unsigned char editline_complete( EditLine *el, int ch __attribute__((__unused__)) ) +{ + static const char *s_cmds[] = + { + "cvarlist ", + "find ", + "help ", + "maps ", + "nextlevel", + "quit", + "status", + "sv_cheats ", + "tf_bot_quota ", + "toggle ", + "sv_dump_edicts", +#ifdef STAGING_ONLY + "tf_bot_use_items ", +#endif + }; + + const LineInfo *lf = el_line(el); + const char *cmd = lf->buffer; + size_t len = lf->cursor - cmd; + + if ( len > 0 ) + { + for (int i = 0; i < ARRAYSIZE(s_cmds); i++) + { + if ( len > strlen( s_cmds[i] ) ) + continue; + + if ( !Q_strncmp( cmd, s_cmds[i], len ) ) + { + if ( el_insertstr( el, s_cmds[i] + len ) == -1 ) + return CC_ERROR; + else + return CC_REFRESH; + } + } + } + + return CC_ERROR; +} + +static const char *editline_prompt( EditLine *e ) +{ + // Something like: "\1\033[7m\1Srcds$\1\033[0m\1 " + static const char *szPrompt = getenv( "SRCDS_PROMPT" ); + return szPrompt ? szPrompt : ""; +} + +static void editline_cleanup_handler( void *arg ) +{ + if ( arg ) + { + EditLine *el = (EditLine *)arg; + el_end( el ); + } +} + +static bool add_command( const char *cmd, int cmd_len ) +{ + if ( cmd ) + { + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + + // Trim trailing whitespace. + while ( ( cmd_len > 0 ) && isspace( cmd[ cmd_len - 1 ] ) ) + cmd_len--; + + if ( cmd_len > 0 ) + { + pthread_mutex_lock( &g_lock ); + if ( g_Commands.Count() < 32 ) + { + CUtlString szCommand( cmd, cmd_len ); + g_Commands.AddToTail( szCommand ); + + g_ProcessingCommands = true; + } + pthread_mutex_unlock( &g_lock ); + + // Wait a bit until we've processed the command we added. + for ( int i = 0; i < 6; i++ ) + { + while ( g_ProcessingCommands ) + usleep( 500 ); + } + + return true; + } + } + + return false; +} + +static void *editline_threadproc( void *arg ) +{ + HistEvent ev; + EditLine *el; + History *myhistory; + FILE *tty = (FILE *)arg; + + ThreadSetDebugName( "libedit" ); + + // Set up state + el = el_init( "srcds_linux", stdin, tty, stderr ); + el_set( el, EL_PROMPT, &editline_prompt ); + el_set( el, EL_EDITOR, "emacs" ); // or "vi" + + // Hitting Ctrl+R will reset prompt. + el_set( el, EL_BIND, "^R", "ed-redisplay", NULL ); + + /* Add a user-defined function */ + el_set( el, EL_ADDFN, "ed-complete", "Complete argument", editline_complete ); + /* Bind tab to it */ + el_set( el, EL_BIND, "^I", "ed-complete", NULL ); + + // Init history. + myhistory = history_init(); + if (myhistory == 0) + { + fprintf( stderr, "history could not be initialized\n" ); + g_threadid = (pthread_t)-1; + return (void *)-1; + } + + // History size. + history( myhistory, &ev, H_SETSIZE, 800 ); + + // History callback. + el_set( el, EL_HIST, history, myhistory ); + + // Source user's defaults. + el_source( el, NULL ); + + pthread_cleanup_push( editline_cleanup_handler, el ); + + while ( g_threadid != (pthread_t)-1 ) + { + // count is the number of characters read. + // line is a const char* of our command line with the tailing \n + int count; + const char *line = el_gets( el, &count ); + + if ( add_command( line, count ) ) + { + // Add command to history. + history( myhistory, &ev, H_ENTER, line ); + } + } + + pthread_cleanup_pop( 0 ); + + // Clean up... + history_end( myhistory ); + el_end( el ); + return NULL; +} + +static void *fgets_threadproc( void *arg ) +{ + pthread_cleanup_push( editline_cleanup_handler, NULL ); + + while ( g_threadid != (pthread_t)-1 ) + { + char cmd[ 512 ]; + + if ( fgets( cmd, sizeof( cmd ), stdin ) ) + { + cmd[ sizeof(cmd) - 1 ] = 0; + add_command( cmd, strlen( cmd ) ); + } + } + + pthread_cleanup_pop( 0 ); + return NULL; +} + +bool CTextConsoleUnix::Init() +{ + if( g_threadid != (pthread_t)-1 ) + { + Assert( !"CTextConsoleUnix can only handle a single thread!" ); + return false; + } + + pthread_mutex_init( &g_lock, NULL ); + + // This code is for echo-ing key presses to the connected tty + // (which is != STDOUT) + if ( isatty( STDIN_FILENO ) ) + { + const char *termid_str = ctermid( NULL ); + + m_tty = fopen( termid_str, "w+" ); + if ( !m_tty ) + { + fprintf( stderr, "WARNING: Unable to open tty(%s) for output.\n", termid_str ); + m_tty = stdout; + } + + void *(*terminal_threadproc) (void *) = editline_threadproc; + if ( !init_tinfo_functions() ) + terminal_threadproc = fgets_threadproc; + + if ( pthread_create( &g_threadid, NULL, terminal_threadproc, (void *)m_tty ) != 0 ) + { + g_threadid = (pthread_t)-1; + fprintf( stderr, "WARNING: pthread_create failed: %s.\n", strerror(errno) ); + } + } + else + { + m_tty = fopen( "/dev/null", "w+" ); + if ( !m_tty ) + m_tty = stdout; + } + + m_bConDebug = CommandLine()->FindParm( "-condebug" ) != 0; + if ( m_bConDebug && CommandLine()->FindParm( "-conclearlog" ) ) + g_pFullFileSystem->RemoveFile( CONSOLE_LOG_FILE, "GAME" ); + + return CTextConsole::Init(); +} + +void CTextConsoleUnix::ShutDown() +{ + if ( g_threadid != (pthread_t)-1 ) + { + void *status = NULL; + pthread_t tid = g_threadid; + + g_threadid = (pthread_t)-1; + + pthread_cancel( tid ); + pthread_join( tid, &status ); + } + + pthread_mutex_destroy( &g_lock ); +} + +void CTextConsoleUnix::Print( char * pszMsg ) +{ + int nChars = strlen( pszMsg ); + + if ( nChars > 0 ) + { + if ( m_bConDebug ) + { + FileHandle_t fh = g_pFullFileSystem->Open( CONSOLE_LOG_FILE, "a" ); + if ( fh != FILESYSTEM_INVALID_HANDLE ) + { + g_pFullFileSystem->Write( pszMsg, nChars, fh ); + g_pFullFileSystem->Close( fh ); + } + } + + fwrite( pszMsg, 1, nChars, m_tty ); + } +} + +void CTextConsoleUnix::SetTitle( char *pszTitle ) +{ +} + +void CTextConsoleUnix::SetStatusLine( char *pszStatus ) +{ +} + +void CTextConsoleUnix::UpdateStatus() +{ +} + +char *CTextConsoleUnix::GetLine( int index, char *buf, int buflen ) +{ + if ( g_threadid != (pthread_t)-1 ) + { + if ( g_Commands.Count() > 0 ) + { + pthread_mutex_lock( &g_lock ); + + const CUtlString& psCommand = g_Commands[ g_Commands.Head() ]; + V_strncpy( buf, psCommand.Get(), buflen ); + g_Commands.Remove( g_Commands.Head() ); + + pthread_mutex_unlock( &g_lock ); + return buf; + } + else if ( index == 0 ) + { + // We're being asked for the first command. Must be a new frame. + // Reset the processed commands global. + g_ProcessingCommands = false; + } + } + + return NULL; +} + +int CTextConsoleUnix::GetWidth() +{ + int nWidth = 0; + struct winsize ws; + + if ( ioctl( STDOUT_FILENO, TIOCGWINSZ, &ws ) == 0 ) + nWidth = (int)ws.ws_col; + + if ( nWidth <= 1 ) + nWidth = 80; + + return nWidth; +} + +#endif // !_WIN32 diff --git a/dedicated/console/TextConsoleUnix.h b/dedicated/console/TextConsoleUnix.h new file mode 100644 index 0000000..b32fe12 --- /dev/null +++ b/dedicated/console/TextConsoleUnix.h @@ -0,0 +1,44 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// TextConsoleUnix.h: Unix interface for the TextConsole class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined TEXTCONSOLE_UNIX_H +#define TEXTCONSOLE_UNIX_H + +#ifndef _WIN32 + +#include <stdio.h> +#include "textconsole.h" + +class CTextConsoleUnix : public CTextConsole +{ +public: + virtual ~CTextConsoleUnix() { } + + // CTextConsole + bool Init(); + void ShutDown(); + void Print( char * pszMsg ); + + void SetTitle( char *pszTitle ); + void SetStatusLine( char *pszStatus ); + void UpdateStatus(); + + char * GetLine( int index, char *buf, int buflen ); + int GetWidth(); + +private: + bool m_bConDebug; + FILE *m_tty; +}; + +#endif // _ndef WIN32 + +#endif // !defined TEXTCONSOLE_UNIX_H diff --git a/dedicated/console/TextConsoleWin32.cpp b/dedicated/console/TextConsoleWin32.cpp new file mode 100644 index 0000000..ece1eff --- /dev/null +++ b/dedicated/console/TextConsoleWin32.cpp @@ -0,0 +1,644 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// CTextConsoleWin32.cpp: Win32 implementation of the TextConsole class. +// +////////////////////////////////////////////////////////////////////// + +#include "TextConsoleWin32.h" +#include "tier0/dbg.h" +#include "utlvector.h" + +// Could possibly switch all this code over to using readline. This: +// http://mingweditline.sourceforge.net/?Description +// readline() / add_history(char *) + +#ifdef _WIN32 + +BOOL WINAPI ConsoleHandlerRoutine( DWORD CtrlType ) +{ + NOTE_UNUSED( CtrlType ); + /* TODO ? + if ( CtrlType != CTRL_C_EVENT && CtrlType != CTRL_BREAK_EVENT ) + m_System->Stop(); // don't quit on break or ctrl+c + */ + + return TRUE; +} + +// GetConsoleHwnd() helper function from MSDN Knowledge Base Article Q124103 +// needed, because HWND GetConsoleWindow(VOID) is not avaliable under Win95/98/ME + +HWND GetConsoleHwnd(void) +{ + typedef HWND (WINAPI *PFNGETCONSOLEWINDOW)( VOID ); + static PFNGETCONSOLEWINDOW s_pfnGetConsoleWindow = (PFNGETCONSOLEWINDOW) GetProcAddress( GetModuleHandle("kernel32"), "GetConsoleWindow" ); + if ( s_pfnGetConsoleWindow ) + return s_pfnGetConsoleWindow(); + + HWND hwndFound; // This is what is returned to the caller. + char pszNewWindowTitle[1024]; // Contains fabricated WindowTitle + char pszOldWindowTitle[1024]; // Contains original WindowTitle + + // Fetch current window title. + GetConsoleTitle( pszOldWindowTitle, 1024 ); + + // Format a "unique" NewWindowTitle. + wsprintf( pszNewWindowTitle,"%d/%d", GetTickCount(), GetCurrentProcessId() ); + + // Change current window title. + SetConsoleTitle(pszNewWindowTitle); + + // Ensure window title has been updated. + Sleep(40); + + // Look for NewWindowTitle. + hwndFound = FindWindow( NULL, pszNewWindowTitle ); + + // Restore original window title. + + SetConsoleTitle( pszOldWindowTitle ); + + return hwndFound; +} + +CTextConsoleWin32::CTextConsoleWin32() +{ + hinput = NULL; + houtput = NULL; + Attrib = 0; + statusline[0] = '\0'; +} + +bool CTextConsoleWin32::Init() +{ + (void) AllocConsole(); + SetTitle( "SOURCE DEDICATED SERVER" ); + + hinput = GetStdHandle ( STD_INPUT_HANDLE ); + houtput = GetStdHandle ( STD_OUTPUT_HANDLE ); + + if ( !SetConsoleCtrlHandler( &ConsoleHandlerRoutine, TRUE) ) + { + Print( "WARNING! TextConsole::Init: Could not attach console hook.\n" ); + } + + Attrib = FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY ; + + SetWindowPos( GetConsoleHwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW ); + + memset( m_szConsoleText, 0, sizeof( m_szConsoleText ) ); + m_nConsoleTextLen = 0; + m_nCursorPosition = 0; + + memset( m_szSavedConsoleText, 0, sizeof( m_szSavedConsoleText ) ); + m_nSavedConsoleTextLen = 0; + + memset( m_aszLineBuffer, 0, sizeof( m_aszLineBuffer ) ); + m_nTotalLines = 0; + m_nInputLine = 0; + m_nBrowseLine = 0; + + // these are log messages, not related to console + Msg( "\n" ); + Msg( "Console initialized.\n" ); + + return CTextConsole::Init(); +} + +void CTextConsoleWin32::ShutDown( void ) +{ + FreeConsole(); +} + +void CTextConsoleWin32::SetVisible( bool visible ) +{ + ShowWindow ( GetConsoleHwnd(), visible ? SW_SHOW : SW_HIDE ); + m_ConsoleVisible = visible; +} + +char * CTextConsoleWin32::GetLine( int index, char *buf, int buflen ) +{ + while ( 1 ) + { + INPUT_RECORD recs[ 1024 ]; + unsigned long numread; + unsigned long numevents; + + if ( !GetNumberOfConsoleInputEvents( hinput, &numevents ) ) + { + Error("CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents"); + return NULL; + } + + if ( numevents <= 0 ) + break; + + if ( !ReadConsoleInput( hinput, recs, ARRAYSIZE( recs ), &numread ) ) + { + Error("CTextConsoleWin32::GetLine: !ReadConsoleInput"); + return NULL; + } + + if ( numread == 0 ) + return NULL; + + for ( int i=0; i < (int)numread; i++ ) + { + INPUT_RECORD *pRec = &recs[i]; + if ( pRec->EventType != KEY_EVENT ) + continue; + + if ( pRec->Event.KeyEvent.bKeyDown ) + { + // check for cursor keys + if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_UP ) + { + ReceiveUpArrow(); + } + else if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_DOWN ) + { + ReceiveDownArrow(); + } + else if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_LEFT ) + { + ReceiveLeftArrow(); + } + else if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_RIGHT ) + { + ReceiveRightArrow(); + } + else + { + char ch; + int nLen; + + ch = pRec->Event.KeyEvent.uChar.AsciiChar; + switch ( ch ) + { + case '\r': // Enter + nLen = ReceiveNewline(); + if ( nLen ) + { + strncpy( buf, m_szConsoleText, buflen ); + buf[ buflen - 1 ] = 0; + return buf; + } + break; + + case '\b': // Backspace + ReceiveBackspace(); + break; + + case '\t': // TAB + ReceiveTab(); + break; + + default: + if ( ( ch >= ' ') && ( ch <= '~' ) ) // dont' accept nonprintable chars + { + ReceiveStandardChar( ch ); + } + break; + } + } + } + } + } + + return NULL; +} + +void CTextConsoleWin32::Print( char * pszMsg ) +{ + if ( m_nConsoleTextLen ) + { + int nLen; + + nLen = m_nConsoleTextLen; + + while ( nLen-- ) + { + PrintRaw( "\b \b" ); + } + } + + PrintRaw( pszMsg ); + + if ( m_nConsoleTextLen ) + { + PrintRaw( m_szConsoleText, m_nConsoleTextLen ); + } + + UpdateStatus(); +} + +void CTextConsoleWin32::PrintRaw( const char * pszMsg, int nChars ) +{ + unsigned long dummy; + + if ( houtput == NULL ) + { + houtput = GetStdHandle ( STD_OUTPUT_HANDLE ); + if ( houtput == NULL ) + return; + } + + if ( nChars <= 0 ) + { + nChars = strlen( pszMsg ); + if ( nChars <= 0 ) + return; + } + + // filter out ASCII BEL characters because windows actually plays a + // bell sound, which can be used to lag the server in a DOS attack. + char * pTempBuf = NULL; + for ( int i = 0; i < nChars; ++i ) + { + if ( pszMsg[i] == 0x07 /*BEL*/ ) + { + if ( !pTempBuf ) + { + pTempBuf = ( char * ) malloc( nChars ); + memcpy( pTempBuf, pszMsg, nChars ); + } + pTempBuf[i] = '.'; + } + } + + WriteFile( houtput, pTempBuf ? pTempBuf : pszMsg, nChars, &dummy, NULL ); + + free( pTempBuf ); // usually NULL +} + +int CTextConsoleWin32::GetWidth( void ) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + int nWidth; + + nWidth = 0; + + if ( GetConsoleScreenBufferInfo( houtput, &csbi ) ) + { + nWidth = csbi.dwSize.X; + } + + if ( nWidth <= 1 ) + nWidth = 80; + + return nWidth; +} + +void CTextConsoleWin32::SetStatusLine( char * pszStatus ) +{ + strncpy( statusline, pszStatus, 80 ); + statusline[ 79 ] = '\0'; + UpdateStatus(); +} + +void CTextConsoleWin32::UpdateStatus( void ) +{ + COORD coord; + DWORD dwWritten = 0; + WORD wAttrib[ 80 ]; + + for ( int i = 0; i < 80; i++ ) + { + wAttrib[i] = Attrib; //FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY ; + } + + coord.X = coord.Y = 0; + + WriteConsoleOutputAttribute( houtput, wAttrib, 80, coord, &dwWritten ); + WriteConsoleOutputCharacter( houtput, statusline, 80, coord, &dwWritten ); +} + + +void CTextConsoleWin32::SetTitle( char * pszTitle ) +{ + SetConsoleTitle( pszTitle ); +} + +void CTextConsoleWin32::SetColor(WORD attrib) +{ + Attrib = attrib; +} + +int CTextConsoleWin32::ReceiveNewline( void ) +{ + int nLen = 0; + + PrintRaw( "\n" ); + + if ( m_nConsoleTextLen ) + { + nLen = m_nConsoleTextLen; + + m_szConsoleText[ m_nConsoleTextLen ] = 0; + m_nConsoleTextLen = 0; + m_nCursorPosition = 0; + + // cache line in buffer, but only if it's not a duplicate of the previous line + if ( ( m_nInputLine == 0 ) || ( strcmp( m_aszLineBuffer[ m_nInputLine - 1 ], m_szConsoleText ) ) ) + { + strncpy( m_aszLineBuffer[ m_nInputLine ], m_szConsoleText, MAX_CONSOLE_TEXTLEN ); + + m_nInputLine++; + + if ( m_nInputLine > m_nTotalLines ) + m_nTotalLines = m_nInputLine; + + if ( m_nInputLine >= MAX_BUFFER_LINES ) + m_nInputLine = 0; + + } + + m_nBrowseLine = m_nInputLine; + } + + return nLen; +} + + +void CTextConsoleWin32::ReceiveBackspace( void ) +{ + int nCount; + + if ( m_nCursorPosition == 0 ) + { + return; + } + + m_nConsoleTextLen--; + m_nCursorPosition--; + + PrintRaw( "\b" ); + + for ( nCount = m_nCursorPosition; nCount < m_nConsoleTextLen; nCount++ ) + { + m_szConsoleText[ nCount ] = m_szConsoleText[ nCount + 1 ]; + PrintRaw( m_szConsoleText + nCount, 1 ); + } + + PrintRaw( " " ); + + nCount = m_nConsoleTextLen; + while ( nCount >= m_nCursorPosition ) + { + PrintRaw( "\b" ); + nCount--; + } + + m_nBrowseLine = m_nInputLine; +} + + +void CTextConsoleWin32::ReceiveTab( void ) +{ + CUtlVector<char *> matches; + + m_szConsoleText[ m_nConsoleTextLen ] = 0; + + if ( matches.Count() == 0 ) + { + return; + } + + if ( matches.Count() == 1 ) + { + char * pszCmdName; + char * pszRest; + + pszCmdName = matches[0]; + pszRest = pszCmdName + strlen( m_szConsoleText ); + + if ( pszRest ) + { + PrintRaw( pszRest ); + strcat( m_szConsoleText, pszRest ); + m_nConsoleTextLen += strlen( pszRest ); + + PrintRaw( " " ); + strcat( m_szConsoleText, " " ); + m_nConsoleTextLen++; + } + } + else + { + int nLongestCmd; + int nTotalColumns; + int nCurrentColumn; + char * pszCurrentCmd; + int i = 0; + + nLongestCmd = 0; + + pszCurrentCmd = matches[0]; + while ( pszCurrentCmd ) + { + if ( (int)strlen( pszCurrentCmd) > nLongestCmd ) + { + nLongestCmd = strlen( pszCurrentCmd); + } + + i++; + pszCurrentCmd = (char *)matches[i]; + } + + nTotalColumns = ( GetWidth() - 1 ) / ( nLongestCmd + 1 ); + nCurrentColumn = 0; + + PrintRaw( "\n" ); + + // Would be nice if these were sorted, but not that big a deal + pszCurrentCmd = matches[0]; + i = 0; + while ( pszCurrentCmd ) + { + char szFormatCmd[ 256 ]; + + nCurrentColumn++; + + if ( nCurrentColumn > nTotalColumns ) + { + PrintRaw( "\n" ); + nCurrentColumn = 1; + } + + Q_snprintf( szFormatCmd, sizeof(szFormatCmd), "%-*s ", nLongestCmd, pszCurrentCmd ); + PrintRaw( szFormatCmd ); + + i++; + pszCurrentCmd = matches[i]; + } + + PrintRaw( "\n" ); + PrintRaw( m_szConsoleText ); + // TODO: Tack on 'common' chars in all the matches, i.e. if I typed 'con' and all the + // matches begin with 'connect_' then print the matches but also complete the + // command up to that point at least. + } + + m_nCursorPosition = m_nConsoleTextLen; + m_nBrowseLine = m_nInputLine; +} + + + +void CTextConsoleWin32::ReceiveStandardChar( const char ch ) +{ + int nCount; + + // If the line buffer is maxed out, ignore this char + if ( m_nConsoleTextLen >= ( sizeof( m_szConsoleText ) - 2 ) ) + { + return; + } + + nCount = m_nConsoleTextLen; + while ( nCount > m_nCursorPosition ) + { + m_szConsoleText[ nCount ] = m_szConsoleText[ nCount - 1 ]; + nCount--; + } + + m_szConsoleText[ m_nCursorPosition ] = ch; + + PrintRaw( m_szConsoleText + m_nCursorPosition, m_nConsoleTextLen - m_nCursorPosition + 1 ); + + m_nConsoleTextLen++; + m_nCursorPosition++; + + nCount = m_nConsoleTextLen; + while ( nCount > m_nCursorPosition ) + { + PrintRaw( "\b" ); + nCount--; + } + + m_nBrowseLine = m_nInputLine; +} + + +void CTextConsoleWin32::ReceiveUpArrow( void ) +{ + int nLastCommandInHistory; + + nLastCommandInHistory = m_nInputLine + 1; + if ( nLastCommandInHistory > m_nTotalLines ) + { + nLastCommandInHistory = 0; + } + + if ( m_nBrowseLine == nLastCommandInHistory ) + { + return; + } + + if ( m_nBrowseLine == m_nInputLine ) + { + if ( m_nConsoleTextLen > 0 ) + { + // Save off current text + strncpy( m_szSavedConsoleText, m_szConsoleText, m_nConsoleTextLen ); + // No terminator, it's a raw buffer we always know the length of + } + + m_nSavedConsoleTextLen = m_nConsoleTextLen; + } + + m_nBrowseLine--; + if ( m_nBrowseLine < 0 ) + { + m_nBrowseLine = m_nTotalLines - 1; + } + + while ( m_nConsoleTextLen-- ) // delete old line + { + PrintRaw( "\b \b" ); + } + + // copy buffered line + PrintRaw( m_aszLineBuffer[ m_nBrowseLine ] ); + + strncpy( m_szConsoleText, m_aszLineBuffer[ m_nBrowseLine ], MAX_CONSOLE_TEXTLEN ); + m_nConsoleTextLen = strlen( m_aszLineBuffer[ m_nBrowseLine ] ); + + m_nCursorPosition = m_nConsoleTextLen; +} + + +void CTextConsoleWin32::ReceiveDownArrow( void ) +{ + if ( m_nBrowseLine == m_nInputLine ) + { + return; + } + + m_nBrowseLine++; + if ( m_nBrowseLine > m_nTotalLines ) + { + m_nBrowseLine = 0; + } + + while ( m_nConsoleTextLen-- ) // delete old line + { + PrintRaw( "\b \b" ); + } + + if ( m_nBrowseLine == m_nInputLine ) + { + if ( m_nSavedConsoleTextLen > 0 ) + { + // Restore current text + strncpy( m_szConsoleText, m_szSavedConsoleText, m_nSavedConsoleTextLen ); + // No terminator, it's a raw buffer we always know the length of + + PrintRaw( m_szConsoleText, m_nSavedConsoleTextLen ); + } + + m_nConsoleTextLen = m_nSavedConsoleTextLen; + } + else + { + // copy buffered line + PrintRaw( m_aszLineBuffer[ m_nBrowseLine ] ); + + strncpy( m_szConsoleText, m_aszLineBuffer[ m_nBrowseLine ], MAX_CONSOLE_TEXTLEN ); + + m_nConsoleTextLen = strlen( m_aszLineBuffer[ m_nBrowseLine ] ); + } + + m_nCursorPosition = m_nConsoleTextLen; +} + + +void CTextConsoleWin32::ReceiveLeftArrow( void ) +{ + if ( m_nCursorPosition == 0 ) + { + return; + } + + PrintRaw( "\b" ); + m_nCursorPosition--; +} + + +void CTextConsoleWin32::ReceiveRightArrow( void ) +{ + if ( m_nCursorPosition == m_nConsoleTextLen ) + { + return; + } + + PrintRaw( m_szConsoleText + m_nCursorPosition, 1 ); + m_nCursorPosition++; +} + +#endif // _WIN32 diff --git a/dedicated/console/TextConsoleWin32.h b/dedicated/console/TextConsoleWin32.h new file mode 100644 index 0000000..4c762ec --- /dev/null +++ b/dedicated/console/TextConsoleWin32.h @@ -0,0 +1,82 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// TextConsoleWin32.h: Win32 interface for the TextConsole class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined TEXTCONSOLE_WIN32_H +#define TEXTCONSOLE_WIN32_H +#pragma once + +#ifdef _WIN32 + +#include <windows.h> +#include "TextConsole.h" + +#define MAX_CONSOLE_TEXTLEN 256 +#define MAX_BUFFER_LINES 30 + +class CTextConsoleWin32 : public CTextConsole +{ +public: + CTextConsoleWin32(); + virtual ~CTextConsoleWin32() { }; + + // CTextConsole + bool Init(); + void ShutDown( void ); + void Print( char *pszMsg ); + + void SetTitle( char * pszTitle ); + void SetStatusLine( char * pszStatus ); + void UpdateStatus( void ); + + char * GetLine( int index, char *buf, int buflen ); + int GetWidth( void ); + + void SetVisible( bool visible ); + +protected: + // CTextConsoleWin32 + void SetColor( WORD ); + void PrintRaw( const char * pszMsg, int nChars = -1 ); + +private: + char m_szConsoleText[ MAX_CONSOLE_TEXTLEN ]; // console text buffer + int m_nConsoleTextLen; // console textbuffer length + int m_nCursorPosition; // position in the current input line + + // Saved input data when scrolling back through command history + char m_szSavedConsoleText[ MAX_CONSOLE_TEXTLEN ]; // console text buffer + int m_nSavedConsoleTextLen; // console textbuffer length + + char m_aszLineBuffer[ MAX_BUFFER_LINES ][ MAX_CONSOLE_TEXTLEN ]; // command buffer last MAX_BUFFER_LINES commands + int m_nInputLine; // Current line being entered + int m_nBrowseLine; // current buffer line for up/down arrow + int m_nTotalLines; // # of nonempty lines in the buffer + + int ReceiveNewline( void ); + void ReceiveBackspace( void ); + void ReceiveTab( void ); + void ReceiveStandardChar( const char ch ); + void ReceiveUpArrow( void ); + void ReceiveDownArrow( void ); + void ReceiveLeftArrow( void ); + void ReceiveRightArrow( void ); + +private: + HANDLE hinput; // standard input handle + HANDLE houtput; // standard output handle + WORD Attrib; // attrib colours for status bar + + char statusline[81]; // first line in console is status line +}; + +#endif // _WIN32 + +#endif // !defined TEXTCONSOLE_WIN32_H diff --git a/dedicated/console/conproc.cpp b/dedicated/console/conproc.cpp new file mode 100644 index 0000000..2156450 --- /dev/null +++ b/dedicated/console/conproc.cpp @@ -0,0 +1,428 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifdef _WIN32 +// conproc.c -- support for qhost +#include <stdio.h> +#include <process.h> +#include <windows.h> +#include "conproc.h" +#include "isys.h" +#include "tier0/icommandline.h" +#include "tier1/strtools.h" + +static HANDLE heventDone; +static HANDLE hfileBuffer; +static HANDLE heventChildSend; +static HANDLE heventParentSend; +static HANDLE hStdout; +static HANDLE hStdin; + +/* +============== +SetConsoleCXCY + +============== +*/ +BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD coordMax; + + coordMax = GetLargestConsoleWindowSize(hStdout); + + if (cy > coordMax.Y) + cy = coordMax.Y; + + if (cx > coordMax.X) + cx = coordMax.X; + + if (!GetConsoleScreenBufferInfo(hStdout, &info)) + return FALSE; + +// height + info.srWindow.Left = 0; + info.srWindow.Right = info.dwSize.X - 1; + info.srWindow.Top = 0; + info.srWindow.Bottom = cy - 1; + + if (cy < info.dwSize.Y) + { + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + + info.dwSize.Y = cy; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + } + else if (cy > info.dwSize.Y) + { + info.dwSize.Y = cy; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + } + + if (!GetConsoleScreenBufferInfo(hStdout, &info)) + return FALSE; + +// width + info.srWindow.Left = 0; + info.srWindow.Right = cx - 1; + info.srWindow.Top = 0; + info.srWindow.Bottom = info.dwSize.Y - 1; + + if (cx < info.dwSize.X) + { + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + + info.dwSize.X = cx; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + } + else if (cx > info.dwSize.X) + { + info.dwSize.X = cx; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + } + + return TRUE; +} + +/* +============== +GetMappedBuffer + +============== +*/ +LPVOID GetMappedBuffer (HANDLE hfileBuffer) +{ + LPVOID pBuffer; + + pBuffer = MapViewOfFile (hfileBuffer, + FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + + return pBuffer; +} + +/* +============== +ReleaseMappedBuffer + +============== +*/ +void ReleaseMappedBuffer (LPVOID pBuffer) +{ + UnmapViewOfFile (pBuffer); +} + +/* +============== +GetScreenBufferLines + +============== +*/ +BOOL GetScreenBufferLines (int *piLines) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + BOOL bRet; + + bRet = GetConsoleScreenBufferInfo (hStdout, &info); + + if (bRet) + *piLines = info.dwSize.Y; + + return bRet; +} + +/* +============== +SetScreenBufferLines + +============== +*/ +BOOL SetScreenBufferLines (int iLines) +{ + return SetConsoleCXCY (hStdout, 80, iLines); +} + +/* +============== +ReadText + +============== +*/ +BOOL ReadText (LPTSTR pszText, int iBeginLine, int iEndLine) +{ + COORD coord; + DWORD dwRead; + BOOL bRet; + + coord.X = 0; + coord.Y = iBeginLine; + + bRet = ReadConsoleOutputCharacter( + hStdout, + pszText, + 80 * (iEndLine - iBeginLine + 1), + coord, + &dwRead); + + // Make sure it's null terminated. + if (bRet) + pszText[dwRead] = '\0'; + + return bRet; +} + +/* +============== +CharToCode + +============== +*/ +int CharToCode (char c) +{ + char upper; + + upper = toupper(c); + + switch (c) + { + case 13: + return 28; + + default: + break; + } + + if (V_isalpha(c)) + return (30 + upper - 65); + + if (V_isdigit(c)) + return (1 + upper - 47); + + return c; +} + +/* +============== +WriteText + +============== +*/ +BOOL WriteText (LPCTSTR szText) +{ + DWORD dwWritten; + INPUT_RECORD rec; + char upper, *sz; + + sz = (LPTSTR) szText; + + while (*sz) + { + // 13 is the code for a carriage return (\n) instead of 10. + if (*sz == 10) + *sz = 13; + + upper = toupper(*sz); + + rec.EventType = KEY_EVENT; + rec.Event.KeyEvent.bKeyDown = TRUE; + rec.Event.KeyEvent.wRepeatCount = 1; + rec.Event.KeyEvent.wVirtualKeyCode = upper; + rec.Event.KeyEvent.wVirtualScanCode = CharToCode (*sz); + rec.Event.KeyEvent.uChar.AsciiChar = *sz; + rec.Event.KeyEvent.uChar.UnicodeChar = *sz; + rec.Event.KeyEvent.dwControlKeyState = V_isupper(*sz) ? 0x80 : 0x0; + + WriteConsoleInput( + hStdin, + &rec, + 1, + &dwWritten); + + rec.Event.KeyEvent.bKeyDown = FALSE; + + WriteConsoleInput( + hStdin, + &rec, + 1, + &dwWritten); + + sz++; + } + + return TRUE; +} + +/* +============== +RequestProc + +============== +*/ +unsigned _stdcall RequestProc (void *arg) +{ + int *pBuffer; + DWORD dwRet; + HANDLE heventWait[2]; + int iBeginLine, iEndLine; + + heventWait[0] = heventParentSend; + heventWait[1] = heventDone; + + while (1) + { + dwRet = WaitForMultipleObjects (2, heventWait, FALSE, INFINITE); + + // heventDone fired, so we're exiting. + if (dwRet == WAIT_OBJECT_0 + 1) + break; + + pBuffer = (int *) GetMappedBuffer (hfileBuffer); + + // hfileBuffer is invalid. Just leave. + if (!pBuffer) + { + sys->Printf ("Request Proc: Invalid -HFILE handle\n"); + break; + } + + switch (pBuffer[0]) + { + case CCOM_WRITE_TEXT: + // Param1 : Text + pBuffer[0] = WriteText ((LPCTSTR) (pBuffer + 1)); + break; + + case CCOM_GET_TEXT: + // Param1 : Begin line + // Param2 : End line + iBeginLine = pBuffer[1]; + iEndLine = pBuffer[2]; + pBuffer[0] = ReadText ((LPTSTR) (pBuffer + 1), iBeginLine, + iEndLine); + break; + + case CCOM_GET_SCR_LINES: + // No params + pBuffer[0] = GetScreenBufferLines (&pBuffer[1]); + break; + + case CCOM_SET_SCR_LINES: + // Param1 : Number of lines + pBuffer[0] = SetScreenBufferLines (pBuffer[1]); + break; + } + + ReleaseMappedBuffer (pBuffer); + SetEvent (heventChildSend); + } + + _endthreadex (0); + return 0; +} + +/* +============== +DeinitConProc + +============== +*/ +void DeinitConProc (void) +{ + if ( heventDone ) + { + SetEvent ( heventDone ); + } +} + +/* +============== +InitConProc + +============== +*/ +void InitConProc ( void ) +{ + unsigned threadAddr; + HANDLE hFile = (HANDLE)0; + HANDLE heventParent = (HANDLE)0; + HANDLE heventChild = (HANDLE)0; + int WantHeight = 50; + const char *p; + + // give external front ends a chance to hook into the console + if ( CommandLine()->CheckParm ( "-HFILE", &p ) && p ) + { + hFile = (HANDLE)atoi ( p ); + } + + if ( CommandLine()->CheckParm ( "-HPARENT", &p ) && p ) + { + heventParent = (HANDLE)atoi ( p ); + } + + if ( CommandLine()->CheckParm ( "-HCHILD", &p ) && p ) + { + heventChild = (HANDLE)atoi ( p ); + } + + // ignore if we don't have all the events. + if ( !hFile || !heventParent || !heventChild ) + { + //sys->Printf ("\n\nNo external front end present.\n" ); + return; + } + + sys->Printf( "\n\nInitConProc: Setting up external control.\n" ); + + hfileBuffer = hFile; + heventParentSend = heventParent; + heventChildSend = heventChild; + + // So we'll know when to go away. + heventDone = CreateEvent (NULL, FALSE, FALSE, NULL); + if (!heventDone) + { + sys->Printf ("InitConProc: Couldn't create heventDone\n"); + return; + } + + if (!_beginthreadex (NULL, 0, RequestProc, NULL, 0, &threadAddr)) + { + CloseHandle (heventDone); + sys->Printf ("InitConProc: Couldn't create third party thread\n"); + return; + } + + // save off the input/output handles. + hStdout = GetStdHandle (STD_OUTPUT_HANDLE); + hStdin = GetStdHandle (STD_INPUT_HANDLE); + + if ( CommandLine()->CheckParm( "-conheight", &p ) && p ) + { + WantHeight = atoi( p ); + } + + // Force 80 character width, at least 25 character height + SetConsoleCXCY( hStdout, 80, WantHeight ); +} +#endif // _WIN32 + diff --git a/dedicated/console/conproc.h b/dedicated/console/conproc.h new file mode 100644 index 0000000..ef84c42 --- /dev/null +++ b/dedicated/console/conproc.h @@ -0,0 +1,30 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// conproc.h -- support for external server monitoring programs +#ifndef INC_CONPROCH +#define INC_CONPROCH + +#define CCOM_WRITE_TEXT 0x2 +// Param1 : Text + +#define CCOM_GET_TEXT 0x3 +// Param1 : Begin line +// Param2 : End line + +#define CCOM_GET_SCR_LINES 0x4 +// No params + +#define CCOM_SET_SCR_LINES 0x5 +// Param1 : Number of lines + +void InitConProc ( void ); +void DeinitConProc ( void ); + +void WriteStatusText( char *psz ); + +#endif // !INC_CONPROCH
\ No newline at end of file diff --git a/dedicated/console/textconsole.cpp b/dedicated/console/textconsole.cpp new file mode 100644 index 0000000..c52ae23 --- /dev/null +++ b/dedicated/console/textconsole.cpp @@ -0,0 +1,22 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// TextConsole.cpp: implementation of the TextConsole class. +// +////////////////////////////////////////////////////////////////////// + +#include "textconsole.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +bool CTextConsole::Init() +{ + m_ConsoleVisible = true; + return true; +} diff --git a/dedicated/console/textconsole.h b/dedicated/console/textconsole.h new file mode 100644 index 0000000..69cf366 --- /dev/null +++ b/dedicated/console/textconsole.h @@ -0,0 +1,38 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#if !defined TEXTCONSOLE_H +#define TEXTCONSOLE_H +#pragma once + +class CTextConsole +{ +public: + CTextConsole() : m_ConsoleVisible( true ) {} + virtual ~CTextConsole() {}; + + virtual bool Init (); + virtual void ShutDown() = 0; + virtual void Print( char * pszMsg ) = 0; + + virtual void SetTitle( char * pszTitle ) = 0; + virtual void SetStatusLine( char * pszStatus ) = 0; + virtual void UpdateStatus() = 0; + + // Must be provided by children + virtual char * GetLine( int index, char *buf, int buflen ) = 0; + virtual int GetWidth() = 0; + + virtual void SetVisible( bool visible ) { m_ConsoleVisible = visible; } + virtual bool IsVisible() { return m_ConsoleVisible; } + +protected: + bool m_ConsoleVisible; +}; + +#endif // !defined TEXTCONSOLE_H diff --git a/dedicated/dedicated.h b/dedicated/dedicated.h new file mode 100644 index 0000000..1dbe25f --- /dev/null +++ b/dedicated/dedicated.h @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// Purpose: Defines a group of app systems that all have the same lifetime +// that need to be connected/initialized, etc. in a well-defined order +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef DEDICATED_H +#define DEDICATED_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "appframework/tier3app.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class IDedicatedServerAPI; + + +//----------------------------------------------------------------------------- +// Singleton interfaces +//----------------------------------------------------------------------------- +extern IDedicatedServerAPI *engine; + + +extern char g_szEXEName[ MAX_PATH ]; + + +//----------------------------------------------------------------------------- +// Inner loop: initialize, shutdown main systems, load steam to +//----------------------------------------------------------------------------- +#ifdef POSIX +#define DEDICATED_BASECLASS CTier2SteamApp +#else +#define DEDICATED_BASECLASS CVguiSteamApp +#endif + +class CDedicatedAppSystemGroup : public DEDICATED_BASECLASS +{ + typedef DEDICATED_BASECLASS BaseClass; + +public: + // Methods of IApplication + virtual bool Create( ); + virtual bool PreInit( ); + virtual int Main( ); + virtual void PostShutdown(); + virtual void Destroy(); + + // Used to chain to base class + AppModule_t LoadModule( CreateInterfaceFn factory ) + { + return CSteamAppSystemGroup::LoadModule( factory ); + } + + // Method to add various global singleton systems + bool AddSystems( AppSystemInfo_t *pSystems ) + { + return CSteamAppSystemGroup::AddSystems( pSystems ); + } + + void *FindSystem( const char *pInterfaceName ) + { + return CSteamAppSystemGroup::FindSystem( pInterfaceName ); + } +}; + + + +#endif // DEDICATED_H diff --git a/dedicated/dedicated.rc b/dedicated/dedicated.rc new file mode 100644 index 0000000..0427b0a --- /dev/null +++ b/dedicated/dedicated.rc @@ -0,0 +1,72 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_HALFLIFE ICON DISCARDABLE "..\\launcher\\res\\launcher.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/dedicated/dedicated.vpc b/dedicated/dedicated.vpc new file mode 100644 index 0000000..949ba09 --- /dev/null +++ b/dedicated/dedicated.vpc @@ -0,0 +1,149 @@ +//----------------------------------------------------------------------------- +// DEDICATED.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" +$include "$SRCDIR\vpc_scripts\source_cryptlib_include.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE,.\,$SRCDIR\engine" + $PreprocessorDefinitions "$BASE;DEDICATED;LAUNCHERONLY;SUPPORT_PACKED_STORE" + } + + $Linker + { + $AdditionalDependencies "$BASE wsock32.lib odbc32.lib odbccp32.lib winmm.lib" + $SystemLibraries "iconv;edit;curses" [$OSXALL] + $SystemFrameworks "AppKit" [$OSXALL] + } +} + +$Project "Dedicated" +{ + $Folder "Source Files" + { + $File "dedicated.rc" + $File "sys_linux.cpp" + { + $Configuration + { + $ExcludedFromBuild "Yes" + } + } + $File "filesystem.cpp" + $File "$SRCDIR\public\filesystem_init.cpp" + $File "$SRCDIR\common\netapi.cpp" + $File "$SRCDIR\common\SteamAppStartup.cpp" + $File "sys_common.cpp" + $File "sys_ded.cpp" + $File "sys_windows.cpp" [$WINDOWS] + $File "sys_linux.cpp" [$POSIX] + { + $Configuration + { + $Compiler + { + $AdditionalOptions "/EHa" + $EnableC++Exceptions "Yes (/EHsc)" + } + } + } + + $Folder "Console" + { + $File "console\conproc.cpp" + $File "console\textconsole.cpp" + $File "console\TextConsoleUnix.cpp" [$POSIX] + $File "console\TextConsoleWin32.cpp" [$WINDOWS] + } + + $Folder "VGUI" [$WINDOWS] + { + $File "vgui\CreateMultiplayerGameServerPage.cpp" + $File "vgui\MainPanel.cpp" + $File "$SRCDIR\public\vgui_controls\vgui_controls.cpp" + $File "vgui\vguihelpers.cpp" + } + + $Folder "FileSystem" + { + $File "$SRCDIR\filesystem\filetracker.cpp" + $File "$SRCDIR\filesystem\basefilesystem.cpp" + $File "$SRCDIR\filesystem\packfile.cpp" + $File "$SRCDIR\filesystem\filesystem_async.cpp" + $File "$SRCDIR\filesystem\filesystem_stdio.cpp" + $File "$SRCDIR\filesystem\QueuedLoader.cpp" + $File "$SRCDIR\public\zip_utils.cpp" + $File "$SRCDIR\filesystem\linux_support.cpp" [$POSIX] + } + } + + $Folder "Header Files" + { + $File "$SRCDIR\filesystem\filetracker.h" + $File "$SRCDIR\filesystem\threadsaferefcountedobject.h" + $File "$SRCDIR\public\ifilelist.h" + $File "$SRCDIR\public\tier0\basetypes.h" + $File "$SRCDIR\public\tier0\commonmacros.h" + $File "$SRCDIR\public\tier0\dbg.h" + $File "dedicated.h" + $File "$SRCDIR\public\engine_hlds_api.h" + $File "$SRCDIR\public\tier0\fasttimer.h" + $File "$SRCDIR\public\filesystem.h" + $File "$SRCDIR\common\IAdminServer.h" + $File "$SRCDIR\public\appframework\IAppSystem.h" + $File "$SRCDIR\public\tier0\icommandline.h" + $File "$SRCDIR\public\idedicatedexports.h" + $File "$SRCDIR\common\IManageServer.h" + $File "$SRCDIR\public\tier1\interface.h" + $File "isys.h" + $File "$SRCDIR\public\mathlib\mathlib.h" + $File "$SRCDIR\common\netapi.h" + $File "$SRCDIR\common\GameUI\ObjectList.h" + $File "$SRCDIR\public\tier0\platform.h" + $File "$SRCDIR\public\tier0\protected_things.h" + $File "$SRCDIR\common\SteamAppStartup.h" + $File "$SRCDIR\public\string_t.h" + $File "$SRCDIR\public\tier1\strtools.h" + $File "$SRCDIR\public\tier0\vcr_shared.h" + $File "$SRCDIR\public\tier0\vcrmode.h" + $File "$SRCDIR\public\mathlib\vector.h" + $File "$SRCDIR\public\mathlib\vector2d.h" + $File "$SRCDIR\public\vstdlib\vstdlib.h" + + $Folder "Console Headers" + { + $File "console\conproc.h" + $File "$SRCDIR\common\IObjectContainer.h" + $File "console\textconsole.h" + $File "console\TextConsoleWin32.h" + } + + $Folder "VGUI Headers" + { + $File "vgui\CreateMultiplayerGameServerPage.h" + $File "vgui\MainPanel.h" + $File "vgui\vguihelpers.h" + } + } + + $Folder "Link Libraries" + { + $Lib appframework + $Lib mathlib + $Lib tier2 + $Lib tier3 + $Lib vgui_controls [$WIN32] + $LibExternal "$SRCDIR/thirdparty/libedit-3.1/src/.libs/libedit" [$LINUXALL] + $ImpLibExternal steam_api + $ImpLib SDL2 [$SDL] + } +} diff --git a/dedicated/filesystem.cpp b/dedicated/filesystem.cpp new file mode 100644 index 0000000..6d908a6 --- /dev/null +++ b/dedicated/filesystem.cpp @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#include "filesystem.h" +#include "dedicated.h" +#include <stdio.h> +#include <stdlib.h> +#include "interface.h" +#include <string.h> +#include <malloc.h> +#include "tier1/strtools.h" +#include "tier0/icommandline.h" +#include "tier0/dbg.h" +#include "../filesystem/basefilesystem.h" +#include "appframework/AppFramework.h" +#include "tier2/tier2.h" + + +extern IFileSystem *g_pFileSystem; +extern IBaseFileSystem *g_pBaseFileSystem; + +// implement our own special factory that we don't export outside of the DLL, to stop +// people being able to get a pointer to a FILESYSTEM_INTERFACE_VERSION stdio interface +void* FileSystemFactory(const char *pName, int *pReturnCode) +{ + { + if ( !Q_stricmp(pName, FILESYSTEM_INTERFACE_VERSION ) ) + { + if ( pReturnCode ) + { + *pReturnCode = IFACE_OK; + } + return g_pFileSystem; + } + if ( !Q_stricmp(pName, BASEFILESYSTEM_INTERFACE_VERSION ) ) + { + if ( pReturnCode ) + { + *pReturnCode = IFACE_OK; + } + return g_pBaseFileSystem; + } + } + + if ( pReturnCode ) + { + *pReturnCode = IFACE_FAILED; + } + return NULL; +} diff --git a/dedicated/isys.h b/dedicated/isys.h new file mode 100644 index 0000000..d86769a --- /dev/null +++ b/dedicated/isys.h @@ -0,0 +1,45 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#if !defined( ISYS_H ) +#define ISYS_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + +class CDedicatedAppSystemGroup; + + +abstract_class ISys +{ +public: + virtual ~ISys( void ) { } + + virtual bool LoadModules( CDedicatedAppSystemGroup *pAppSystemGroup ) = 0; + + virtual void Sleep( int msec ) = 0; + virtual bool GetExecutableName( char *out ) = 0; + virtual void ErrorMessage( int level, const char *msg ) = 0; + + virtual void WriteStatusText( char *szText ) = 0; + virtual void UpdateStatus( int force ) = 0; + + virtual long LoadLibrary( char *lib ) = 0; + virtual void FreeLibrary( long library ) = 0; + + virtual bool CreateConsoleWindow( void ) = 0; + virtual void DestroyConsoleWindow( void ) = 0; + + virtual void ConsoleOutput ( char *string ) = 0; + virtual char *ConsoleInput ( int index, char *buf, int buflen ) = 0; + virtual void Printf( PRINTF_FORMAT_STRING const char *fmt, ...) FMTFUNCTION( 2, 3 ) = 0; +}; + +extern ISys *sys; + +#endif // ISYS_H
\ No newline at end of file diff --git a/dedicated/resource.h b/dedicated/resource.h new file mode 100644 index 0000000..3447d84 --- /dev/null +++ b/dedicated/resource.h @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Dedicated.rc +// +#define IDI_HALFLIFE 101 +#define IDD_CDKEY 102 +#define IDC_KEY 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/dedicated/sys_common.cpp b/dedicated/sys_common.cpp new file mode 100644 index 0000000..5138bd3 --- /dev/null +++ b/dedicated/sys_common.cpp @@ -0,0 +1,300 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// +#ifdef _WIN32 +#include <windows.h> +#elif POSIX +#include <unistd.h> +#else +#error +#endif +#include <stdio.h> +#include <stdlib.h> +#include "isys.h" +#include "dedicated.h" +#include "engine_hlds_api.h" +#include "filesystem.h" +#include "tier0/vcrmode.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include "tier0/icommandline.h" +#include "idedicatedexports.h" +#include "vgui/vguihelpers.h" + +static long hDLLThirdParty = 0L; + +//----------------------------------------------------------------------------- +// Modules... +//----------------------------------------------------------------------------- +CSysModule *s_hMatSystemModule = NULL; +CSysModule *s_hEngineModule = NULL; +CSysModule *s_hSoundEmitterModule = NULL; + +CreateInterfaceFn s_MaterialSystemFactory; +CreateInterfaceFn s_EngineFactory; +CreateInterfaceFn s_SoundEmitterFactory; + +/* +============== +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 ( CommandLine()->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) +{ +} + +/* +============== +InitInstance + +============== +*/ +bool InitInstance( ) +{ + Load3rdParty(); + + return true; +} + +/* +============== +ProcessConsoleInput + +============== +*/ +int ProcessConsoleInput(void) +{ + char *s; + int count = 0; + + if ( engine ) + { + do + { + char szBuf[ 256 ]; + s = sys->ConsoleInput( count++, szBuf, sizeof( szBuf ) ); + if (s && s[0] ) + { + V_strcat_safe( szBuf, "\n" ); + engine->AddConsoleText ( szBuf ); + } + } while (s); + } + + return count; +} + +void RunServer( void ); + +class CDedicatedExports : public CBaseAppSystem<IDedicatedExports> +{ +public: + virtual void Sys_Printf( char *text ) + { + if ( sys ) + { + sys->Printf( "%s", text ); + } + } + + virtual void RunServer() + { + void RunServer( void ); + ::RunServer(); + } +}; + +EXPOSE_SINGLE_INTERFACE( CDedicatedExports, IDedicatedExports, VENGINE_DEDICATEDEXPORTS_API_VERSION ); + +static const char *get_consolelog_filename() +{ + static bool s_bInited = false; + static char s_consolelog[ MAX_PATH ]; + + if ( !s_bInited ) + { + s_bInited = true; + + // Don't do the -consolelog thing if -consoledebug is present. + // CTextConsoleUnix::Print() looks for -consoledebug. + const char *filename = NULL; + if ( !CommandLine()->FindParm( "-consoledebug" ) && + CommandLine()->CheckParm( "-consolelog", &filename ) && + filename ) + { + V_strcpy_safe( s_consolelog, filename ); + } + } + + return s_consolelog; +} + +SpewRetval_t DedicatedSpewOutputFunc( SpewType_t spewType, char const *pMsg ) +{ + if ( sys ) + { + sys->Printf( "%s", pMsg ); + + // If they have specified -consolelog, log this message there. Otherwise these + // wind up being lost because Sys_InitGame hasn't been called yet, and + // Sys_SpewFunc is the thing that logs stuff to -consolelog, etc. + const char *filename = get_consolelog_filename(); + if ( filename[ 0 ] && pMsg[ 0 ] ) + { + FileHandle_t fh = g_pFullFileSystem->Open( filename, "a" ); + if ( fh != FILESYSTEM_INVALID_HANDLE ) + { + g_pFullFileSystem->Write( pMsg, V_strlen( pMsg ), fh ); + g_pFullFileSystem->Close( fh ); + } + } + } +#ifdef _WIN32 + Plat_DebugString( pMsg ); +#endif + + if (spewType == SPEW_ERROR) + { + // In Windows vgui mode, make a message box or they won't ever see the error. +#ifdef _WIN32 + extern bool g_bVGui; + if ( g_bVGui ) + { + MessageBox( NULL, pMsg, "Error", MB_OK | MB_TASKMODAL ); + } + TerminateProcess( GetCurrentProcess(), 1 ); +#elif POSIX + fflush(stdout); + _exit(1); +#else +#error "Implement me" +#endif + + return SPEW_ABORT; + } + if (spewType == SPEW_ASSERT) + { + if ( CommandLine()->FindParm( "-noassert" ) == 0 ) + return SPEW_DEBUGGER; + else + return SPEW_CONTINUE; + } + return SPEW_CONTINUE; +} + +int Sys_GetExecutableName( char *out ) +{ +#ifdef _WIN32 + if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, 256 ) ) + { + return 0; + } +#else + strcpy( out, g_szEXEName ); +#endif + return 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Return the directory where this .exe is running from +// Output : char +//----------------------------------------------------------------------------- +const char *UTIL_GetExecutableDir( ) +{ + static char exedir[ MAX_PATH ]; + + exedir[ 0 ] = 0; + if ( !Sys_GetExecutableName(exedir) ) + return NULL; + + char *pSlash; + char *pSlash2; + pSlash = strrchr( exedir,'\\' ); + pSlash2 = strrchr( exedir,'/' ); + if ( pSlash2 > pSlash ) + { + pSlash = pSlash2; + } + if (pSlash) + { + *pSlash = 0; + } + + // Return the bin directory as the executable dir if it's not in there + // because that's really where we're running from... + int exeLen = strlen(exedir); + if ( exedir[exeLen-4] != CORRECT_PATH_SEPARATOR || + exedir[exeLen-3] != 'b' || + exedir[exeLen-2] != 'i' || + exedir[exeLen-1] != 'n' ) + { + Q_strncat( exedir, "\\bin", sizeof( exedir ), COPY_ALL_CHARACTERS ); + Q_FixSlashes( exedir ); + } + + return exedir; +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the directory where this .exe is running from +// Output : char +//----------------------------------------------------------------------------- +const char *UTIL_GetBaseDir( void ) +{ + static char basedir[ MAX_PATH ]; + + char const *pOverrideDir = CommandLine()->CheckParm( "-basedir" ); + if ( pOverrideDir ) + return pOverrideDir; + + basedir[ 0 ] = 0; + const char *pExeDir = UTIL_GetExecutableDir( ); + if ( pExeDir ) + { + strcpy( basedir, pExeDir ); + int dirlen = strlen( basedir ); + if ( basedir[ dirlen - 3 ] == 'b' && + basedir[ dirlen - 2 ] == 'i' && + basedir[ dirlen - 1 ] == 'n' ) + { + basedir[ dirlen - 4 ] = 0; + } + } + + return basedir; +} diff --git a/dedicated/sys_ded.cpp b/dedicated/sys_ded.cpp new file mode 100644 index 0000000..c7a9be9 --- /dev/null +++ b/dedicated/sys_ded.cpp @@ -0,0 +1,530 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include <stdio.h> +#include <stdlib.h> +#include "isys.h" +#include "console/conproc.h" +#include "dedicated.h" +#include "engine_hlds_api.h" +#include "checksum_md5.h" +#include "mathlib/mathlib.h" +#include "tier0/vcrmode.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include "tier0/icommandline.h" +#include "idedicatedexports.h" +#include "vgui/vguihelpers.h" +#include "appframework/AppFramework.h" +#include "filesystem_init.h" +#include "tier2/tier2.h" +#include "dedicated.h" +#include "vstdlib/cvar.h" +#include "inputsystem/iinputsystem.h" + +#ifdef _WIN32 +#include <windows.h> +#include <direct.h> +#include "KeyValues.h" +#else +#define _chdir chdir +#include <unistd.h> +#endif + +void* FileSystemFactory( const char *pName, int *pReturnCode ); +bool InitInstance( ); +int ProcessConsoleInput( void ); +bool NET_Init( void ); +void NET_Shutdown( void ); +const char *UTIL_GetBaseDir( void ); +#ifdef _WIN32 +bool g_bVGui = false; +#endif + +#if defined ( _WIN32 ) +#include "console/TextConsoleWin32.h" +CTextConsoleWin32 console; +#else +#include "console/TextConsoleUnix.h" +CTextConsoleUnix console; +#endif + +#ifdef _WIN32 +extern char *gpszCvars; +#endif + +IDedicatedServerAPI *engine = NULL; + +//----------------------------------------------------------------------------- +// Implementation of IVCRHelpers. +//----------------------------------------------------------------------------- +class CVCRHelpers : public IVCRHelpers +{ +public: + virtual void ErrorMessage( const char *pMsg ) + { + printf( "ERROR: %s\n", pMsg ); + } + + virtual void* GetMainWindow() + { + return 0; + } +}; +CVCRHelpers g_VCRHelpers; + +SpewRetval_t DedicatedSpewOutputFunc( SpewType_t spewType, char const *pMsg ); // in sys_common.cpp + +//----------------------------------------------------------------------------- +// Run a single VGUI frame. if bFinished is true, run VGUIFinishedConfig() first. +//----------------------------------------------------------------------------- +static bool DoRunVGUIFrame( bool bFinished = false ) +{ +#ifdef _WIN32 + if ( g_bVGui ) + { + if ( bFinished ) + VGUIFinishedConfig(); + RunVGUIFrame(); + return true; + } +#endif + + return false; +} + +//----------------------------------------------------------------------------- +// Handle the VCRHook PeekMessage loop. +// Return true if WM_QUIT received. +//----------------------------------------------------------------------------- +static bool HandleVCRHook() +{ +#if defined ( _WIN32 ) + MSG msg; + + bool bDone = false; + while( VCRHook_PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + { + //if (!GetMessage( &msg, NULL, 0, 0)) + if ( msg.message == WM_QUIT ) + { + bDone = true; + break; + } + + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + if ( IsPC() ) + { + // NOTE: Under some implementations of Win9x, + // dispatching messages can cause the FPU control word to change + SetupFPUControlWord(); + } + + if ( bDone /*|| gbAppHasBeenTerminated*/ ) + return true; +#endif // _WIN32 + + return false; +} + +//----------------------------------------------------------------------------- +// +// Server loop +// +//----------------------------------------------------------------------------- +void RunServer( void ) +{ +#ifdef _WIN32 + if(gpszCvars) + { + engine->AddConsoleText(gpszCvars); + } +#endif + + // Run 2 engine frames first to get the engine to load its resources. + for ( int i = 0; i < 2; i++ ) + { + DoRunVGUIFrame(); + if ( !engine->RunFrame() ) + return; + } + + // Run final VGUI frame. + DoRunVGUIFrame( true ); + + int bDone = false; + while ( !bDone ) + { + // Check on VCRHook_PeekMessage... + if ( HandleVCRHook() ) + break; + + if ( !DoRunVGUIFrame() ) + ProcessConsoleInput(); + + if ( !engine->RunFrame() ) + bDone = true; + + sys->UpdateStatus( 0 /* don't force */ ); + } +} + +//----------------------------------------------------------------------------- +// +// initialize the console or wait for vgui to start the server +// +//----------------------------------------------------------------------------- +static bool ConsoleStartup( ) +{ +#ifdef _WIN32 + if ( g_bVGui ) + { + RunVGUIFrame(); + + // Run the config screen + while (VGUIIsInConfig() && VGUIIsRunning()) + RunVGUIFrame(); + + if ( VGUIIsStopping() ) + return false; + + return true; + } + else + { + if ( !console.Init() ) + { + return false; + } + } +#endif // _WIN32 + + return true; +} + + +//----------------------------------------------------------------------------- +// Instantiate all main libraries +//----------------------------------------------------------------------------- +bool CDedicatedAppSystemGroup::Create( ) +{ +#ifndef _WIN32 + if ( !console.Init() ) + return false; +#endif + + // Hook the debug output stuff (override the spew func in the appframework) + SpewOutputFunc( DedicatedSpewOutputFunc ); + + // Added the dedicated exports module for the engine to grab + AppModule_t dedicatedModule = LoadModule( Sys_GetFactoryThis() ); + IAppSystem *pSystem = AddSystem( dedicatedModule, VENGINE_DEDICATEDEXPORTS_API_VERSION ); + if ( !pSystem ) + return false; + + if ( sys->LoadModules( this ) ) + { + // Find the input system and tell it to skip Steam Controller initialization (we have to set this flag before Init gets called on the + // input system). Dedicated server should skip controller initialization to avoid initializing Steam, because we don't want the user to be + // flagged as "playing" the game. + auto inputsystem = ( IInputSystem* )FindSystem( INPUTSYSTEM_INTERFACE_VERSION ); + if ( inputsystem ) + { + inputsystem->SetSkipControllerInitialization( true ); + } + + return true; + } + else + { + return false; + } +} + +bool CDedicatedAppSystemGroup::PreInit( ) +{ + // A little hack needed because dedicated links directly to filesystem .cpp files + g_pFullFileSystem = NULL; + + if ( !BaseClass::PreInit() ) + return false; + + CFSSteamSetupInfo steamInfo; + steamInfo.m_pDirectoryName = NULL; + steamInfo.m_bOnlyUseDirectoryName = false; + steamInfo.m_bToolsMode = false; + steamInfo.m_bSetSteamDLLPath = false; + steamInfo.m_bSteam = g_pFullFileSystem->IsSteam(); + steamInfo.m_bNoGameInfo = steamInfo.m_bSteam; + if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK ) + return false; + + CFSMountContentInfo fsInfo; + fsInfo.m_pFileSystem = g_pFullFileSystem; + fsInfo.m_bToolsMode = false; + fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath; + + if ( FileSystem_MountContent( fsInfo ) != FS_OK ) + return false; + + if ( !NET_Init() ) + return false; + +#ifdef _WIN32 + g_bVGui = !CommandLine()->CheckParm( "-console" ); +#endif + + CreateInterfaceFn factory = GetFactory(); + IInputSystem *inputsystem = (IInputSystem *)factory( INPUTSYSTEM_INTERFACE_VERSION, NULL ); + if ( inputsystem ) + { + inputsystem->SetConsoleTextMode( true ); + } + +#ifdef _WIN32 + if ( g_bVGui ) + { + StartVGUI( GetFactory() ); + } + else +#endif + { + if ( !sys->CreateConsoleWindow() ) + return false; + } + + return true; +} + +int CDedicatedAppSystemGroup::Main( ) +{ + if ( !ConsoleStartup() ) + return -1; + +#ifdef _WIN32 + if ( g_bVGui ) + RunVGUIFrame(); +#endif + + // Set up mod information + ModInfo_t info; + info.m_pInstance = GetAppInstance(); + info.m_pBaseDirectory = UTIL_GetBaseDir(); + info.m_pInitialMod = CommandLine()->ParmValue( "-game", "hl2" ); + info.m_pInitialGame = CommandLine()->ParmValue( "-defaultgamedir", "hl2" ); + info.m_pParentAppSystemGroup = this; + info.m_bTextMode = CommandLine()->CheckParm( "-textmode" ); + + if ( engine->ModInit( info ) ) + { + engine->ModShutdown(); + } // if engine->ModInit + + return 0; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDedicatedAppSystemGroup::PostShutdown() +{ +#ifdef _WIN32 + if ( g_bVGui ) + StopVGUI(); +#endif + + sys->DestroyConsoleWindow(); + console.ShutDown(); + NET_Shutdown(); + BaseClass::PostShutdown(); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDedicatedAppSystemGroup::Destroy() +{ +} + + +//----------------------------------------------------------------------------- +// Gets the executable name +//----------------------------------------------------------------------------- +bool GetExecutableName( char *out, int nMaxLen ) +{ +#ifdef _WIN32 + if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, nMaxLen ) ) + return false; + return true; +#elif POSIX + Q_strncpy( out, g_szEXEName, nMaxLen ); + return true; +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the directory where this .exe is running from +// Output : char +//----------------------------------------------------------------------------- +void UTIL_ComputeBaseDir( char *pBaseDir, int nMaxLen ) +{ + int j; + char *pBuffer = NULL; + + pBaseDir[ 0 ] = 0; + + if ( GetExecutableName( pBaseDir, nMaxLen ) ) + { + pBuffer = strrchr( pBaseDir, CORRECT_PATH_SEPARATOR ); + if ( pBuffer && *pBuffer ) + { + *(pBuffer+1) = '\0'; + } + + j = strlen( pBaseDir ); + if (j > 0) + { + if ( ( pBaseDir[ j-1 ] == '\\' ) || + ( pBaseDir[ j-1 ] == '/' ) ) + { + pBaseDir[ j-1 ] = 0; + } + } + } + + char const *pOverrideDir = CommandLine()->CheckParm( "-basedir" ); + if ( pOverrideDir ) + { + strcpy( pBaseDir, pOverrideDir ); + } + + Q_strlower( pBaseDir ); + Q_FixSlashes( pBaseDir ); +} + + +//----------------------------------------------------------------------------- +// This class is a helper class used for steam-based applications. +// It loads up the file system in preparation for using it to load other +// required modules from steam. +// +// I couldn't use the one in appframework because the dedicated server +// inlines all the filesystem code. +//----------------------------------------------------------------------------- +class CDedicatedSteamApplication : public CSteamApplication +{ +public: + CDedicatedSteamApplication( CSteamAppSystemGroup *pAppSystemGroup ); + virtual bool Create( ); +}; + + +//----------------------------------------------------------------------------- +// This class is a helper class used for steam-based applications. +// It loads up the file system in preparation for using it to load other +// required modules from steam. +// +// I couldn't use the one in appframework because the dedicated server +// inlines all the filesystem code. +//----------------------------------------------------------------------------- +CDedicatedSteamApplication::CDedicatedSteamApplication( CSteamAppSystemGroup *pAppSystemGroup ) : CSteamApplication( pAppSystemGroup ) +{ +} + + +//----------------------------------------------------------------------------- +// Implementation of IAppSystemGroup +//----------------------------------------------------------------------------- +bool CDedicatedSteamApplication::Create( ) +{ + // Add in the cvar factory + AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() ); + AddSystem( cvarModule, CVAR_INTERFACE_VERSION ); + + AppModule_t fileSystemModule = LoadModule( FileSystemFactory ); + m_pFileSystem = (IFileSystem*)AddSystem( fileSystemModule, FILESYSTEM_INTERFACE_VERSION ); + + if ( !m_pFileSystem ) + { + Warning( "Unable to load the file system!\n" ); + return false; + } + + return true; +} + + + +//----------------------------------------------------------------------------- +// +// Main entry point for dedicated server, shared between win32 and linux +// +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ +#ifndef POSIX + _asm + { + fninit + } +#endif + + SetupFPUControlWord(); + +#ifdef POSIX + Q_strncpy( g_szEXEName, *argv, ARRAYSIZE( g_szEXEName ) ); + // Store off command line for argument searching + BuildCmdLine( argc, argv ); +#endif + + MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f ); + + // Store off command line for argument searching + CommandLine()->CreateCmdLine( VCRHook_GetCommandLine() ); +#ifndef _WIN32 + Plat_SetCommandLine( CommandLine()->GetCmdLine() ); +#endif + + // Start VCR mode? + const char *filename; + if( CommandLine()->CheckParm( "-vcrrecord", &filename ) ) + { + if ( !VCRStart( filename, true, &g_VCRHelpers ) ) + { + Error( "-vcrrecord: can't open '%s' for writing.\n", filename ); + return -1; + } + } + else if( CommandLine()->CheckParm( "-vcrplayback", &filename ) ) + { + if ( !VCRStart( filename, false, &g_VCRHelpers ) ) + { + Error( "-vcrplayback: can't open '%s' for reading.\n", filename ); + return -1; + } + } + + // Figure out the directory the executable is running from + // and make that be the current working directory + char pBasedir[ MAX_PATH ]; + UTIL_ComputeBaseDir( pBasedir, MAX_PATH ); + _chdir( pBasedir ); + + // Rehook the command line through VCR mode. + CommandLine()->CreateCmdLine( VCRHook_GetCommandLine() ); + + if ( !InitInstance() ) + return -1; + + CDedicatedAppSystemGroup dedicatedSystems; + CDedicatedSteamApplication steamApplication( &dedicatedSystems ); + return steamApplication.Run( ); +} diff --git a/dedicated/sys_linux.cpp b/dedicated/sys_linux.cpp new file mode 100644 index 0000000..28e9b6c --- /dev/null +++ b/dedicated/sys_linux.cpp @@ -0,0 +1,298 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + + +#include <unistd.h> +#include <string.h> +#include <dlfcn.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/time.h> +#include <malloc.h> +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include "isys.h" +#include "console/conproc.h" +#include "dedicated.h" +#include "engine_hlds_api.h" +#include "checksum_md5.h" +#include "idedicatedexports.h" +#include "tier0/vcrmode.h" +#include "tier0/dbg.h" +#include "mathlib/mathlib.h" +#include "interface.h" +#include "tier1/strtools.h" +#include "tier0/icommandline.h" +#include "materialsystem/imaterialsystem.h" +#include "istudiorender.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" +#include "datacache/idatacache.h" +#include "datacache/imdlcache.h" +#include "vphysics_interface.h" +#include "icvar.h" +#include "filesystem/IQueuedLoader.h" +#include "console/TextConsoleUnix.h" + +bool InitInstance( ); + +char g_szEXEName[ MAX_PATH ]; + +extern CTextConsoleUnix console; + +//----------------------------------------------------------------------------- +// Purpose: Implements OS Specific layer ( loosely ) +//----------------------------------------------------------------------------- +class CSys : public ISys +{ +public: + virtual ~CSys(); + + virtual bool LoadModules( CDedicatedAppSystemGroup *pAppSystemGroup ); + + void Sleep( int msec ); + bool GetExecutableName( char *out ); + void ErrorMessage( int level, const char *msg ); + + void WriteStatusText( char *szText ); + void UpdateStatus( int force ); + + long LoadLibrary( char *lib ); + void FreeLibrary( long library ); + void *GetProcAddress( long library, const char *name ); + + bool CreateConsoleWindow( void ); + void DestroyConsoleWindow( void ); + + void ConsoleOutput ( char *string ); + char *ConsoleInput ( int index, char *buf, int buflen ); + void Printf( const char *fmt, ...); +}; + +static CSys g_Sys; +ISys *sys = &g_Sys; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSys::~CSys() +{ + sys = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msec +// Output : +//----------------------------------------------------------------------------- +void CSys::Sleep( int msec ) +{ + usleep(msec * 1000); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : handle, function name- +// Output : void * +//----------------------------------------------------------------------------- +void *CSys::GetProcAddress( long library, const char *name ) +{ + return dlsym( library, name ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *lib - +// Output : long +//----------------------------------------------------------------------------- +long CSys::LoadLibrary( char *lib ) +{ + void *hDll = NULL; + + char cwd[1024]; + char absolute_lib[1024]; + + if (!getcwd(cwd, sizeof(cwd))) + ErrorMessage(1, "Sys_LoadLibrary: Couldn't determine current directory."); + + if (cwd[strlen(cwd)-1] == '/') + cwd[strlen(cwd)-1] = 0; + + Q_snprintf(absolute_lib, sizeof( absolute_lib ), "%s/%s", cwd, lib); + + hDll = dlopen( absolute_lib, RTLD_NOW ); + if ( !hDll ) + { + ErrorMessage( 1, dlerror() ); + } + return (long)hDll; +} + +void CSys::FreeLibrary( long library ) +{ + if ( !library ) + return; + + dlclose( (void *)library ); +} + +bool CSys::GetExecutableName( char *out ) +{ + char *name = strrchr(g_szEXEName, '/' ); + if ( name ) + { + strcpy( out, name + 1); + return true; + } + else + { + return false; + } +} + +/* +============== +ErrorMessage + +Engine is erroring out, display error in message box +============== +*/ +void CSys::ErrorMessage( int level, const char *msg ) +{ + Error( "%s\n", msg ); + exit( -1 ); +} + +void CSys::UpdateStatus( int force ) +{ +} + +/* +================ +ConsoleOutput + +Print text to the dedicated console +================ +*/ +void CSys::ConsoleOutput (char *string) +{ + console.Print(string); +} + +/* +============== +Printf + +Engine is printing to console +============== +*/ +void CSys::Printf( const char *fmt, ...) +{ + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start (argptr, fmt); + Q_vsnprintf (szText, sizeof( szText ), fmt, argptr); + va_end (argptr); + + // Get Current text and append it. + ConsoleOutput( szText ); +} + +/* +================ +ConsoleInput + +================ +*/ +char *CSys::ConsoleInput( int index, char *buf, int buflen ) +{ + return console.GetLine( index, buf, buflen ); +} + +/* +============== +WriteStatusText + +============== +*/ +void CSys::WriteStatusText( char *szText ) +{ +} + +/* +============== +CreateConsoleWindow + +Create console window ( overridable? ) +============== +*/ +bool CSys::CreateConsoleWindow( void ) +{ + return true; +} + +/* +============== +DestroyConsoleWindow + +============== +*/ +void CSys::DestroyConsoleWindow( void ) +{ +} + +/* +================ +GameInit +================ +*/ +bool CSys::LoadModules( CDedicatedAppSystemGroup *pAppSystemGroup ) +{ + AppSystemInfo_t appSystems[] = + { + { "engine" DLL_EXT_STRING, CVAR_QUERY_INTERFACE_VERSION }, + { "soundemittersystem" DLL_EXT_STRING, SOUNDEMITTERSYSTEM_INTERFACE_VERSION }, // loaded for backwards compatability, prevents crash on exit for old game dlls + { "materialsystem" DLL_EXT_STRING, MATERIAL_SYSTEM_INTERFACE_VERSION }, + { "studiorender" DLL_EXT_STRING, STUDIO_RENDER_INTERFACE_VERSION }, + { "vphysics" DLL_EXT_STRING, VPHYSICS_INTERFACE_VERSION }, + { "datacache" DLL_EXT_STRING, DATACACHE_INTERFACE_VERSION }, + { "datacache" DLL_EXT_STRING, MDLCACHE_INTERFACE_VERSION }, + { "datacache" DLL_EXT_STRING, STUDIO_DATA_CACHE_INTERFACE_VERSION }, + { "dedicated" DLL_EXT_STRING, QUEUEDLOADER_INTERFACE_VERSION }, + { "engine" DLL_EXT_STRING, VENGINE_HLDS_API_VERSION }, + { "", "" } // Required to terminate the list + }; + + if ( !pAppSystemGroup->AddSystems( appSystems ) ) + return false; + + engine = (IDedicatedServerAPI *)pAppSystemGroup->FindSystem( VENGINE_HLDS_API_VERSION ); + // obsolete i think SetCVarIF( (ICvar*)pAppSystemGroup->FindSystem( VENGINE_CVAR_INTERFACE_VERSION ) ); + + IMaterialSystem* pMaterialSystem = (IMaterialSystem*)pAppSystemGroup->FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION ); + pMaterialSystem->SetShaderAPI( "shaderapiempty" DLL_EXT_STRING ); + return true; +} + +bool NET_Init() +{ + return true; +} + +void NET_Shutdown() +{ +} + +extern int main(int argc, char *argv[]); +DLL_EXPORT int DedicatedMain( int argc, char *argv[] ); + +int DedicatedMain( int argc, char *argv[] ) +{ + return main(argc,argv); +} diff --git a/dedicated/sys_windows.cpp b/dedicated/sys_windows.cpp new file mode 100644 index 0000000..aacf0aa --- /dev/null +++ b/dedicated/sys_windows.cpp @@ -0,0 +1,411 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <eh.h> +#include "isys.h" +#include "console/conproc.h" +#include "dedicated.h" +#include "engine_hlds_api.h" +#include "checksum_md5.h" +#include "tier0/vcrmode.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" +#include "tier0/icommandline.h" +#include "inputsystem/iinputsystem.h" +#include "SteamAppStartup.h" +#include "console/textconsole.h" +#include "vgui/vguihelpers.h" +#include "appframework/appframework.h" +#include "materialsystem/imaterialsystem.h" +#include "istudiorender.h" +#include "vgui/ivgui.h" +#include "console/TextConsoleWin32.h" +#include "icvar.h" +#include "datacache/idatacache.h" +#include "datacache/imdlcache.h" +#include "vphysics_interface.h" +#include "filesystem.h" +#include "steam/steam_api.h" +#include "filesystem/IQueuedLoader.h" + +extern CTextConsoleWin32 console; +extern bool g_bVGui; + +//----------------------------------------------------------------------------- +// Purpose: Implements OS Specific layer ( loosely ) +//----------------------------------------------------------------------------- +class CSys : public ISys +{ +public: + virtual ~CSys( void ); + + virtual bool LoadModules( CDedicatedAppSystemGroup *pAppSystemGroup ); + + void Sleep( int msec ); + bool GetExecutableName( char *out ); + void ErrorMessage( int level, const char *msg ); + + void WriteStatusText( char *szText ); + void UpdateStatus( int force ); + + long LoadLibrary( char *lib ); + void FreeLibrary( long library ); + + bool CreateConsoleWindow( void ); + void DestroyConsoleWindow( void ); + + void ConsoleOutput ( char *string ); + char *ConsoleInput ( int index, char *buf, int buflen ); + void Printf( PRINTF_FORMAT_STRING const char *fmt, ... ); +}; + +static CSys g_Sys; +ISys *sys = &g_Sys; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSys::~CSys() +{ + sys = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : msec - +//----------------------------------------------------------------------------- +void CSys::Sleep( int msec ) +{ + // Call ThreadSleep because it has the necessary magic to set the system + // timer resolution so that Sleep( 1 ) will sleep for one millisecond + // instead of for 10-16 ms. + ThreadSleep( msec ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *lib - +// Output : long +//----------------------------------------------------------------------------- +long CSys::LoadLibrary( char *lib ) +{ + void *hDll = ::LoadLibrary( lib ); + return (long)hDll; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : library - +//----------------------------------------------------------------------------- +void CSys::FreeLibrary( long library ) +{ + if ( !library ) + return; + + ::FreeLibrary( (HMODULE)library ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *out - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSys::GetExecutableName( char *out ) +{ + if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, 256 ) ) + { + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : level - +// *msg - +//----------------------------------------------------------------------------- +void CSys::ErrorMessage( int level, const char *msg ) +{ + MessageBox( NULL, msg, "Half-Life", MB_OK ); + PostQuitMessage(0); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : force - +//----------------------------------------------------------------------------- +void CSys::UpdateStatus( int force ) +{ + static double tLast = 0.0; + double tCurrent; + char szPrompt[256]; + int n, nMax; + char szMap[64]; + char szHostname[128]; + float fps; + + if ( !engine ) + return; + + tCurrent = Sys_FloatTime(); + + if ( !force ) + { + if ( ( tCurrent - tLast ) < 0.5f ) + return; + } + + tLast = tCurrent; + + engine->UpdateStatus( &fps, &n, &nMax, szMap, sizeof( szMap ) ); + engine->UpdateHostname( szHostname, sizeof( szHostname ) ); + + console.SetTitle( szHostname ); + + Q_snprintf( szPrompt, sizeof( szPrompt ), "%.1f fps %2i/%2i on map %16s", (float)fps, n, nMax, szMap); + + console.SetStatusLine(szPrompt); + console.UpdateStatus(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *string - +// Output : void CSys::ConsoleOutput +//----------------------------------------------------------------------------- +void CSys::ConsoleOutput (char *string) +{ + if ( g_bVGui ) + { + VGUIPrintf( string ); + } + else + { + console.Print(string); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *fmt - +// ... - +//----------------------------------------------------------------------------- +void CSys::Printf( PRINTF_FORMAT_STRING const char *fmt, ... ) +{ + // Dump text to debugging console. + va_list argptr; + char szText[1024]; + + va_start (argptr, fmt); + Q_vsnprintf (szText, sizeof( szText ), fmt, argptr); + va_end (argptr); + + // Get Current text and append it. + ConsoleOutput( szText ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : char * +//----------------------------------------------------------------------------- +char *CSys::ConsoleInput ( int index, char *buf, int buflen ) +{ + return console.GetLine( index, buf, buflen ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szText - +//----------------------------------------------------------------------------- +void CSys::WriteStatusText( char *szText ) +{ + SetConsoleTitle( szText ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSys::CreateConsoleWindow( void ) +{ + if ( !AllocConsole () ) + { + return false; + } + + InitConProc(); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSys::DestroyConsoleWindow( void ) +{ + FreeConsole (); + + // shut down QHOST hooks if necessary + DeinitConProc (); +} + + +//----------------------------------------------------------------------------- +// Loading modules used by the dedicated server. +//----------------------------------------------------------------------------- +bool CSys::LoadModules( CDedicatedAppSystemGroup *pAppSystemGroup ) +{ + AppSystemInfo_t appSystems[] = + { + { "engine.dll", CVAR_QUERY_INTERFACE_VERSION }, // NOTE: This one must be first!! + { "inputsystem.dll", INPUTSYSTEM_INTERFACE_VERSION }, + { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION }, + { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION }, + { "vphysics.dll", VPHYSICS_INTERFACE_VERSION }, + { "datacache.dll", DATACACHE_INTERFACE_VERSION }, + { "datacache.dll", MDLCACHE_INTERFACE_VERSION }, + { "datacache.dll", STUDIO_DATA_CACHE_INTERFACE_VERSION }, + { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION }, + { "engine.dll", VENGINE_HLDS_API_VERSION }, + { "dedicated.dll", QUEUEDLOADER_INTERFACE_VERSION }, + { "", "" } // Required to terminate the list + }; + + if ( !pAppSystemGroup->AddSystems( appSystems ) ) + return false; + + engine = (IDedicatedServerAPI *)pAppSystemGroup->FindSystem( VENGINE_HLDS_API_VERSION ); + + IMaterialSystem* pMaterialSystem = (IMaterialSystem*)pAppSystemGroup->FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION ); + pMaterialSystem->SetShaderAPI( "shaderapiempty.dll" ); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool NET_Init( void ) +{ + // Startup winock + WORD version = MAKEWORD( 1, 1 ); + WSADATA wsaData; + + int err = WSAStartup( version, &wsaData ); + if ( err != 0 ) + { + char msg[ 256 ]; + Q_snprintf( msg, sizeof( msg ), "Winsock 1.1 unavailable...\n" ); + sys->Printf( "%s", msg ); + Plat_DebugString( msg ); + return false; + } + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void NET_Shutdown( void ) +{ + // Kill winsock + WSACleanup(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : hInstance - +// hPrevInstance - +// lpszCmdLine - +// nCmdShow - +// Output : int PASCAL +//----------------------------------------------------------------------------- +int main(int argc, char **argv); // in sys_ded.cpp +static char *GetBaseDir( const char *pszBuffer ) +{ + static char basedir[ MAX_PATH ]; + char szBuffer[ MAX_PATH ]; + int j; + char *pBuffer = NULL; + + V_strcpy_safe( szBuffer, pszBuffer ); + + pBuffer = strrchr( szBuffer,'\\' ); + if ( pBuffer ) + { + *(pBuffer+1) = '\0'; + } + + strcpy( basedir, szBuffer ); + + j = strlen( basedir ); + if (j > 0) + { + if ( ( basedir[ j-1 ] == '\\' ) || + ( basedir[ j-1 ] == '/' ) ) + { + basedir[ j-1 ] = 0; + } + } + + return basedir; +} + +void MiniDumpFunction( unsigned int nExceptionCode, EXCEPTION_POINTERS *pException ) +{ + SteamAPI_WriteMiniDump( nExceptionCode, pException, 0 ); +} + +extern "C" __declspec(dllexport) int DedicatedMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) +{ + SetAppInstance( hInstance ); + + // Check that we are running on Win32 + OSVERSIONINFO vinfo; + vinfo.dwOSVersionInfoSize = sizeof(vinfo); + + if ( !GetVersionEx ( &vinfo ) ) + return -1; + + if ( vinfo.dwPlatformId == VER_PLATFORM_WIN32s ) + return -1; + + int argc, iret = -1; + LPWSTR * argv= CommandLineToArgvW(GetCommandLineW(),&argc); + CommandLine()->CreateCmdLine( VCRHook_GetCommandLine() ); + + if ( !Plat_IsInDebugSession() && !CommandLine()->FindParm( "-nominidumps") ) + { + // This warning only applies if you want to catch structured exceptions (crashes) + // using C++ exceptions. We do not want to do that so we can build with C++ exceptions + // completely disabled, and just suppress this warning. + // warning C4535: calling _set_se_translator() requires /EHa + #pragma warning( suppress : 4535 ) + _set_se_translator( MiniDumpFunction ); + + try // this try block allows the SE translator to work + { + iret = main(argc,(char **)argv); + } + catch( ... ) + { + return -1; + } + } + else + { + iret = main(argc,(char **)argv); + } + + GlobalFree( argv ); + return iret; +} + diff --git a/dedicated/vgui/CreateMultiplayerGameServerPage.cpp b/dedicated/vgui/CreateMultiplayerGameServerPage.cpp new file mode 100644 index 0000000..d4e6308 --- /dev/null +++ b/dedicated/vgui/CreateMultiplayerGameServerPage.cpp @@ -0,0 +1,872 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifdef _WIN32 +#include <stdio.h> + +#include "CreateMultiplayerGameServerPage.h" +#include <Winsock2.h> + +using namespace vgui; + +#include <vgui_controls/Controls.h> +#include <KeyValues.h> +#include <vgui_controls/ListPanel.h> +#include <vgui_controls/ComboBox.h> +#include <vgui_controls/MessageBox.h> +#include <vgui_controls/CheckButton.h> +#include <vgui/IVGui.h> + +#include <OfflineMode.h> + +#include "filesystem.h" +#include "mainpanel.h" +#include "tier0/icommandline.h" +#include "netapi.h" +// for SRC +#include <vstdlib/random.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//#define ALLOW_OLD_ENGINE_GAMES + + +bool IsEp1EraAppID( int iSteamAppId ) +{ + return iSteamAppId == 211 || iSteamAppId == 215; +} + +// Checks the liblist.gam file for a "fallback_dir" +const char *GetLiblistFallbackDir( const char *pszGameDir ) +{ + static char szFallback[512]; + char szTemp[512]; + + szFallback[0] = 0; + + V_sprintf_safe( szTemp, "%s\\liblist.gam", pszGameDir ); + g_pFullFileSystem->GetLocalCopy( szTemp ); + + FileHandle_t hFile = g_pFullFileSystem->Open( szTemp, "rt" ); + + if ( hFile ) + { + char szLine[512]; + + // look for the line starting with 'fallback_dir' + while ( !g_pFullFileSystem->EndOfFile( hFile ) ) + { + // get a single line + szLine[0] = 0; + g_pFullFileSystem->ReadLine( szLine, sizeof(szLine) - 1, hFile ); + szLine[sizeof(szLine) - 1] = 0; + + if ( !strnicmp( szLine, "fallback_dir", 12 ) ) + { + // we got the line, get the value between the quotes + char *start = strchr( szLine, '\"' ); + + if ( !start ) + { + break; + } + + char *end = strchr( start + 1, '\"' ); + if ( !end ) + { + break; + } + + // copy out between start and end + int bytesToCopy = end - start - 1; + if ( bytesToCopy >= sizeof(szFallback) - 1 ) + { + bytesToCopy = sizeof(szFallback) - 1; + break; + } + + if ( bytesToCopy > 0 ) + { + strncpy( szFallback, start + 1, bytesToCopy ); + szFallback[bytesToCopy] = 0; + } + } + } + + g_pFullFileSystem->Close( hFile ); + } + + return szFallback; +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameServerPage::CCreateMultiplayerGameServerPage(vgui::Panel *parent, const char *name) : Frame(parent, name) +{ + memset(&m_iServer,0x0,sizeof(serveritem_t)); + + m_MainPanel = parent; // as we are a popup frame we need to store this seperately + m_pSavedData = NULL; + m_pGameInfo = NULL; + + SetMinimumSize(310, 350); + SetSize(310, 350); + SetSizeable(false); + + + SetTitle("#Start_Server_Title",true); + + m_pMapList = new ComboBox(this, "MapList",10,false); + m_pMapList->SetEnabled(false); // a mod needs to be chosen first to populate the map list + m_pMapList->SetEditable(false); + + m_pNetworkCombo = new ComboBox(this, "NetworkCombo",10,false); + int defaultItem = m_pNetworkCombo->AddItem("#Internet", NULL); + int lanItem = m_pNetworkCombo->AddItem("#LAN", NULL); + if ( CommandLine()->CheckParm("-steam") && IsSteamInOfflineMode() ) + { + defaultItem = lanItem; + } + m_pNetworkCombo->ActivateItem(defaultItem); + + m_pNumPlayers = new ComboBox(this, "NumPlayers",10,false); + char num[3]; + int i; + for( i = 1 ; i <= MAX_PLAYERS ; i++ ) + { + V_sprintf_safe(num, "%i", i); + m_pNumPlayers->AddItem(num, NULL); + } + m_pNumPlayers->ActivateItemByRow(23); // 24 players by default + + m_pGameCombo = new ComboBox(this,"MODCombo", 10, false); + + m_pStartServerButton = new Button(this, "StartButton", "#Start_Server_Button"); + m_pStartServerButton->SetCommand("start"); + + m_pCancelButton = new Button(this, "CancelButton", "#Start_Server_Cancel"); + m_pCancelButton->SetCommand("cancel"); + + m_pSecureCheck = new CheckButton(this, "SecureCheck", "#Start_Server_Secure"); + m_pSecureCheck->SetSelected(true); + + LoadControlSettingsAndUserConfig("Admin/CreateMultiplayerGameServerPage.res"); + + // load some defaults into the controls + SetControlString("ServerNameEdit", "Half-Life dedicated server"); + V_strcpy_safe(m_szGameName, "Half-Life"); + + LoadMODList(); + + m_pGameCombo->RequestFocus(); + + // get default port from commandline if possible + m_iPort = 27015; + const char *portVal = NULL; + if (CommandLine()->CheckParm("-port", &portVal) && portVal && atoi(portVal) > 0) + { + m_iPort = atoi(portVal); + } + SetControlInt("PortEdit", m_iPort); + + LoadConfig(); + + m_szMapName[0] = 0; + m_szHostName[0] = 0; + m_szPassword[0] = 0; + m_iMaxPlayers = 24; + + if ( CommandLine()->CheckParm("-steam") && IsSteamInOfflineMode() ) + { + m_pNetworkCombo->SetEnabled( false ); + } + + SetVisible(true); + + if ( CommandLine()->CheckParm("-steam") && IsSteamInOfflineMode() ) + { + MessageBox *box = new vgui::MessageBox( "#Start_Server_Offline_Title", "#Start_Server_Offline_Warning" ); + box->DoModal(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CCreateMultiplayerGameServerPage::~CCreateMultiplayerGameServerPage() +{ + SaveConfig(); + if (m_pSavedData) + { + m_pSavedData->deleteThis(); + m_pSavedData = NULL; + } + if ( m_pGameInfo ) + { + m_pGameInfo->deleteThis(); + m_pGameInfo = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::OnResetData() +{ + m_pGameCombo->SetEnabled(true); +} + +//----------------------------------------------------------------------------- +// Purpose: loads settings from a config file +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::LoadConfig() +{ + // free any old filters + if (m_pSavedData) + { + m_pSavedData->deleteThis(); + } + m_pSavedData = new KeyValues ("Server"); + + if (!m_pSavedData->LoadFromFile(g_pFullFileSystem, "Server.vdf", "CONFIG")) + { + // file not successfully loaded + } + else + { + if (m_pSavedData->FindKey("RconPassword", false)) + { + const char *password = m_pSavedData->GetString("RconPassword", ""); + if (strlen(password)>0) + { + SetControlString("RCONPasswordEdit", password); + } + } + if (m_pSavedData->FindKey("MaxPlayers", false)) + { + int maxPlayers = m_pSavedData->GetInt("MaxPlayers", -1); + if (maxPlayers > 0 && maxPlayers <= 32) + { + m_pNumPlayers->ActivateItemByRow(maxPlayers - 1); + } + } + if (m_pSavedData->FindKey("MOD", false)) + { + const char *mod = m_pSavedData->GetString("MOD", ""); + if (strlen(mod) > 0) + { + // look for the item in the dropdown + m_szMod[0] = 0; + for (int i = 0; i < m_pGameCombo->GetItemCount(); i++) + { + if (!m_pGameCombo->IsItemIDValid(i)) + continue; + + if (!stricmp(m_pGameCombo->GetItemUserData(i)->GetString("gamedir"), mod)) + { + // item found in list, activate + m_pGameCombo->ActivateItem(i); + break; + } + } + } + } + if (m_pSavedData->FindKey("Map", false)) + { + const char *map = m_pSavedData->GetString("Map", ""); + if (strlen(map) > 0) + { + SetControlString("MapList", map); + } + } + + if (m_pSavedData->FindKey("Network", false)) + { + int nwIndex = m_pSavedData->GetInt("Network"); + if (nwIndex > 0 && nwIndex < 2) + { + m_pNetworkCombo->ActivateItemByRow(nwIndex); + } + } + if (m_pSavedData->FindKey("Secure", false)) + { + int secure = m_pSavedData->GetInt("Secure"); + m_pSecureCheck->SetSelected(secure); + } + if (m_pSavedData->FindKey("ServerName", false)) + { + const char *serverName = m_pSavedData->GetString("ServerName",""); + if (strlen(serverName) > 0) + { + SetControlString("ServerNameEdit", serverName); + } + } + m_iPort = m_pSavedData->GetInt("Port", m_iPort); + SetControlInt("PortEdit", m_iPort); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::SetConfig(const char *serverName, const char *rconPassword, int maxPlayers, const char *mod, const char *map, int network, int secure, int port) +{ + m_pSavedData->SetInt("MaxPlayers", maxPlayers); + m_pSavedData->SetString("RconPassword", rconPassword); + m_pSavedData->SetString("ServerName", serverName); + m_pSavedData->SetString("MOD", mod); + m_pSavedData->SetString("Map", map); + m_pSavedData->SetInt("Secure", secure); + m_pSavedData->SetInt("Network", network); + m_pSavedData->SetInt("Port", port); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::SaveConfig() +{ + m_pSavedData->SaveToFile(g_pFullFileSystem, "Server.vdf", "CONFIG"); +} + +//----------------------------------------------------------------------------- +// Purpose: returns true if one of the characters in the string is not a valid alpha numeric character +//----------------------------------------------------------------------------- +bool CCreateMultiplayerGameServerPage::BadRconChars(const char *pass) +{ + bool bad = false; + + for(unsigned int i=0;i<strlen(pass);i++) + { + bad |= !( V_isalnum(pass[i]) ? true : false ); + } + return bad; +} + +const char *ToString( int val ) +{ + static char text[256]; + Q_snprintf( text, sizeof(text), "%i", val ); + return text; +} + + +//----------------------------------------------------------------------------- +// Purpose: called to get the info from the dialog +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::OnCommand(const char *cmd) +{ + char cvars[1024]; + int secure = GetControlInt("SecureCheck", 1); + m_pNumPlayers->GetText(cvars, 1024); + m_iMaxPlayers = atoi(cvars); + V_strcpy_safe(m_szHostName, GetControlString("ServerNameEdit", "")); + V_strcpy_safe(m_szPassword, GetControlString("RCONPasswordEdit", "")); + m_iPort = GetControlInt("PortEdit", 27015); + + if (!stricmp(cmd, "cancel")) + { + vgui::ivgui()->PostMessage( m_MainPanel->GetVPanel(), new KeyValues("Quit"), NULL); + Close(); + } + else if (!stricmp(cmd, "start")) + { + // save our current settings + SetConfig(m_szHostName, m_szPassword, m_iMaxPlayers, m_szMod, GetMapName(), m_pNetworkCombo->GetActiveItem() != 0, secure, m_iPort); + SaveConfig(); + + // create the command to execute + bool isLanOnly = (m_pNetworkCombo->GetActiveItem() != 0); + CommandLine()->AppendParm("-game", m_szMod); + CommandLine()->AppendParm("-maxplayers", ToString(m_iMaxPlayers)); + CommandLine()->AppendParm("+sv_lan", ToString(isLanOnly)); + CommandLine()->AppendParm("+map", GetMapName()); + CommandLine()->AppendParm("-port", ToString(m_iPort)); + if (!secure) // if they don't want it secure... + { + CommandLine()->AppendParm("-insecure", ""); + } + + + // V_strcpy_safe(m_szPassword, GetControlString("RCONPasswordEdit", "")); + if (strlen(m_szPassword) < 3 || BadRconChars(m_szPassword)) + { + MessageBox *dlg = new MessageBox("#Start_Server_RCON_Error_Title", "#Start_Server_RCON_Error"); + dlg->DoModal(); + } + else + { + V_sprintf_safe(cvars, "rcon_password \"%s\"\nsetmaster enable\nhostname \"%s\"\n", m_szPassword, m_szHostName); + + m_pGameCombo->SetEnabled(false); + m_pNumPlayers->SetEnabled(false); + + netadr_t local; + net->GetLocalIP(&local); + local.port = ::htons(m_iPort); + + for (int i = 0; i < 4; i++) + { + m_iServer.ip[i] = local.ip[i]; + } + m_iServer.port = (local.port & 0xff) << 8 | (local.port & 0xff00) >> 8;; + + V_strcpy_safe(m_iServer.name, m_szHostName); + V_strcpy_safe(m_iServer.map, GetMapName()); + V_strcpy_safe(m_iServer.gameDir, m_szMod); + m_iServer.maxPlayers = m_iMaxPlayers; + + SetVisible(false); + + KeyValues *gameData = m_pGameCombo->GetActiveItemUserData(); + +// // mount the caches +// if (CommandLine()->CheckParm("-steam")) +// { +// if (gameData) +// { +// KeyValues *pFileSystem = gameData->FindKey( "FileSystem" ); +// if ( !pFileSystem ) +// Error( "Game %s missing FileSystem key.", gameData->GetString( "game" ) ); +// +// // Mods just specify their app ID (CS, HL2, HL2MP, etc), and it mounts all the necessary caches. +// int iAppId = pFileSystem->GetInt( "SteamAppId" ); +// if ( iAppId ) +// { +// CUtlVector<unsigned int> depList; +// MountDependencies( iAppId, depList ); +// +// char gameinfoFilename[MAX_PATH]; +// Q_snprintf( gameinfoFilename, sizeof( gameinfoFilename ), "%s\\gameinfo.txt", m_iServer.gameDir ); +// g_pFullFileSystem->GetLocalCopy( gameinfoFilename ); +// } +// } +// } + + // Launch the old dedicated server if necessary. + if ( LaunchOldDedicatedServer( gameData ) ) + { + vgui::ivgui()->PostMessage( m_MainPanel->GetVPanel(), new KeyValues("Quit"), NULL); + Close(); + } + + CMainPanel::GetInstance()->StartServer(cvars); + } + } +} + +bool CCreateMultiplayerGameServerPage::LaunchOldDedicatedServer( KeyValues *pGameInfo ) +{ +#if defined( ALLOW_OLD_ENGINE_GAMES ) + // Validate the gameinfo.txt format.. + KeyValues *pSub = pGameInfo->FindKey( "FileSystem" ); + if ( pSub ) + { + int iSteamAppId = pSub->GetInt( "SteamAppId", -1 ); + if ( iSteamAppId != -1 ) + { + if ( IsEp1EraAppID( iSteamAppId ) ) + { + // Old-skool app. Launch the old dedicated server. + char steamDir[MAX_PATH]; + if ( !system()->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\SteamPath", steamDir, sizeof( steamDir ) ) ) + Error( "LaunchOldDedicatedServer: can't get SteamPath." ); + + V_FixSlashes( steamDir ); + V_AppendSlash( steamDir, sizeof( steamDir ) ); + + char commandLine[1024 * 4]; + commandLine[0] = 0; + V_snprintf( commandLine, sizeof( commandLine ), "\"%ssteam.exe\" -applaunch 205 -HiddenLaunch", steamDir ); + + // Feed it all the parameters chosen in the UI so it doesn't redisplay the UI. + STARTUPINFO si; + memset( &si, 0, sizeof( si ) ); + si.cb = sizeof( si ); + + PROCESS_INFORMATION pi; + if ( !CreateProcess( NULL, commandLine, NULL, NULL, false, 0, NULL, steamDir, &si, &pi ) ) + { + Error( "LaunchOldDedicatedServer: Unable to launch old srcds." ); + } + + return true; + } + } + } +#endif + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: loads the list of available maps into the map list +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::LoadMODList() +{ + m_pGameCombo->DeleteAllItems(); + + // add steam games + if (CommandLine()->CheckParm("-steam")) + { + const char *pSteamGamesFilename = "hlds_steamgames.vdf"; + + KeyValues *gamesFile = new KeyValues( pSteamGamesFilename ); + + if ( gamesFile->LoadFromFile( g_pFullFileSystem, pSteamGamesFilename, NULL ) ) + { + for ( KeyValues *kv = gamesFile->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey() ) + { + const char *pGameDir = kv->GetString( "gamedir", NULL ); + if ( !pGameDir ) + Error( "Mod %s in %s missing 'gamedir'.", kv->GetName(), pSteamGamesFilename ); + + AddMod( pGameDir, pSteamGamesFilename, kv ); + } + } + gamesFile->deleteThis(); + gamesFile = NULL; + } + + + // For backward compatibility, check inside the dedicated server's own directory for mods. + LoadModListInDirectory( "." ); + + // Also, check in SourceMods. + char sourceModsDir[MAX_PATH]; + if ( system()->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\SourceModInstallPath", sourceModsDir, sizeof( sourceModsDir ) ) ) + LoadModListInDirectory( sourceModsDir ); + + m_pGameCombo->ActivateItem(0); +} + + +void CCreateMultiplayerGameServerPage::LoadModListInDirectory( const char *pDirectoryName ) +{ + char searchString[MAX_PATH*2]; + V_strcpy_safe( searchString, pDirectoryName ); + Q_AppendSlash( searchString, sizeof( searchString ) ); + Q_strncat( searchString, "*.*", sizeof( searchString ), COPY_ALL_CHARACTERS ); + + FileFindHandle_t findHandle = NULL; + const char *filename = g_pFullFileSystem->FindFirst( searchString, &findHandle ); + while ( filename ) + { + // add to the mod list + if (filename[0] != '.' && g_pFullFileSystem->FindIsDirectory(findHandle)) + { + char fullFilename[MAX_PATH]; + if ( Q_stricmp( pDirectoryName, "." ) == 0 ) + { + // If we don't do this, then the games in hlds_steamgames.vdf will get listed twice + // since their gamedir is listed as "cstrike" and "hl2mp", not ".\cstrike" or ".\hl2mp". + V_strcpy_safe( fullFilename, filename ); + } + else + { + V_strcpy_safe( fullFilename, pDirectoryName ); + Q_AppendSlash( fullFilename, sizeof( fullFilename ) ); + Q_strncat( fullFilename, filename, sizeof( fullFilename ), COPY_ALL_CHARACTERS ); + } + + LoadPossibleMod( fullFilename ); + } + + filename = g_pFullFileSystem->FindNext(findHandle); + } + g_pFullFileSystem->FindClose(findHandle); +} + + +void CCreateMultiplayerGameServerPage::LoadPossibleMod( const char *pGameDirName ) +{ + char gameInfoFilename[1024]; + Q_snprintf(gameInfoFilename, sizeof(gameInfoFilename) - 1, "%s\\gameinfo.txt", pGameDirName); + if ( !g_pFullFileSystem->FileExists(gameInfoFilename) ) + return; + + // don't want to add single player games to the list + KeyValues *pGameInfo = new KeyValues( "GameInfo" ); + bool loadedFile = pGameInfo->LoadFromFile( g_pFullFileSystem, gameInfoFilename ); + if ( !loadedFile ) + return; + + AddMod( pGameDirName, gameInfoFilename, pGameInfo ); + + pGameInfo->deleteThis(); + pGameInfo = NULL; +} + + +void CCreateMultiplayerGameServerPage::AddMod( const char *pGameDirName, const char *pGameInfoFilename, KeyValues *pGameInfo ) +{ + // Don't re-add something with the same gamedir name (this can happen with games listed in hlds_steamgames.vdf, + // since after the first time a game is run, it'll also have a gameinfo.txt that will be found). + for ( int i=0; i < m_pGameCombo->GetItemCount(); i++ ) + { + if ( !m_pGameCombo->IsItemIDValid(i) ) + continue; + + if ( Q_stricmp( m_pGameCombo->GetItemUserData(i)->GetString("gamedir"), pGameDirName ) == 0 ) + return; + } + + // If this mod supports multiplayer, then we'll add it. + const char *gameType = pGameInfo->GetString( "type", "singleplayer_only" ); + if ( Q_stricmp(gameType, "singleplayer_only") != 0 ) + { + // Validate the gameinfo.txt format.. + KeyValues *pSub = pGameInfo->FindKey( "FileSystem" ); + if ( !pSub ) + Error( "%s missing FileSystem key.", pGameInfoFilename ); + + int iSteamAppId = pSub->GetInt( "SteamAppId", -1 ); + if ( iSteamAppId == -1 ) + Error( "%s missing FileSystem\\SteamAppId key.", pGameInfoFilename ); + +#if !defined( ALLOW_OLD_ENGINE_GAMES ) + // If we're not supporting old games in here and its appid is old, forget about it. + if ( IsEp1EraAppID( iSteamAppId ) ) + return; +#endif + + const char *gameName = pGameInfo->GetString( "game", NULL ); + if ( !gameName ) + Error( "%s missing 'game' key.", pGameInfoFilename ); + + + // add to drop-down combo and mod list + KeyValues *kv = pGameInfo->MakeCopy(); + kv->SetString( "gamedir", pGameDirName ); + + m_pGameCombo->AddItem( gameName, kv ); + + kv->deleteThis(); + kv = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: loads the list of available maps for the given path into the map list +// Returns: number of maps loaded into the list +//----------------------------------------------------------------------------- +int CCreateMultiplayerGameServerPage::LoadMaps( const char *pszMod ) +{ + // iterate the filesystem getting the list of all the files + // UNDONE: steam wants this done in a special way, need to support that + FileFindHandle_t findHandle = NULL; + char szSearch[256]; + sprintf( szSearch, "%s/maps/*.bsp", pszMod ); + + int iMapsFound = 0; + + const char *pszFilename = g_pFullFileSystem->FindFirst( szSearch, &findHandle ); + + KeyValues *hiddenMaps = NULL; + if ( m_pGameInfo ) + { + hiddenMaps = m_pGameInfo->FindKey( "hidden_maps" ); + } + + while ( pszFilename ) + { + // remove the text 'maps/' and '.bsp' from the file name to get the map name + char mapname[256]; + const char *str = strstr( pszFilename, "maps" ); + if ( str ) + { + V_strcpy_safe( mapname, str + 5 ); // maps + \\ = 5 + } + else + { + V_strcpy_safe( mapname, pszFilename ); + } + + char *ext = strstr( mapname, ".bsp" ); + if ( ext ) + { + *ext = 0; + } + + //!! hack: strip out single player HL maps + // this needs to be specified in a seperate file + if ( ( mapname[0] == 'c' || mapname[0] == 't' ) && mapname[2] == 'a' && mapname[1] >= '0' && mapname[1] <= '5' ) + { + goto nextFile; + } + + // strip out maps that shouldn't be displayed + if ( hiddenMaps ) + { + if ( hiddenMaps->GetInt( mapname, 0 ) ) + { + goto nextFile; + } + } + + iMapsFound++; + + // add to the map list + m_pMapList->AddItem( mapname, NULL ); + + // get the next file + nextFile: + pszFilename = g_pFullFileSystem->FindNext( findHandle ); + } + + g_pFullFileSystem->FindClose( findHandle ); + + return iMapsFound; +} + +//----------------------------------------------------------------------------- +// Purpose: loads the list of available maps into the map list +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::LoadMapList() +{ + int iMapsFound = 0; + + // clear the current list (if any) + m_pMapList->DeleteAllItems(); + + Assert( strlen(m_szMod ) > 0); + if ( strlen( m_szMod ) < 1) + { + m_pMapList->SetEnabled( false ); + return; + } + + m_pMapList->SetEnabled( true ); + m_pStartServerButton->SetEnabled( true ); + + if ( CommandLine()->CheckParm( "-steam" ) ) + { + KeyValues *userData = m_pGameCombo->GetActiveItemUserData(); + if ( userData && userData->GetString( "DedicatedServerStartMap", NULL ) ) + { + // set only + m_pMapList->AddItem( userData->GetString( "DedicatedServerStartMap" ), NULL ); + m_pMapList->ActivateItemByRow( 0 ); + m_pMapList->SetEnabled( false ); + return; + } + } + + // Load the maps for the GameDir + iMapsFound += LoadMaps( m_szMod ); + + // If we're using a "fallback_dir" in liblist.gam then include those maps... + const char *pszFallback = GetLiblistFallbackDir( m_szMod ); + if ( pszFallback[0] ) + { + iMapsFound += LoadMaps( pszFallback ); + } + + if ( iMapsFound < 1 ) + { + m_pMapList->SetEnabled( false ); + } + + // set the first item to be selected + m_pMapList->ActivateItemByRow( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: returns the name of the map selected from the map combo +//----------------------------------------------------------------------------- +const char *CCreateMultiplayerGameServerPage::GetMapName() +{ + m_pMapList->GetText(m_szMapName, DATA_STR_LENGTH); + return m_szMapName; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +const char *CCreateMultiplayerGameServerPage::GetRconPassword() +{ + return m_szPassword; +} + +//----------------------------------------------------------------------------- +// Purpose: updates "s" with the details of the chosen server to run +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::GetServer(serveritem_t &s) +{ + s=m_iServer; + V_strcpy_safe(s.name,m_iServer.name); + V_strcpy_safe(s.rconPassword,m_iServer.rconPassword); + memcpy(s.ip,m_iServer.ip,sizeof(m_iServer.ip)); + memcpy(s.pings,m_iServer.pings,3*sizeof(int)); + V_strcpy_safe(s.gameDir,m_iServer.gameDir); + V_strcpy_safe(s.map,m_iServer.map); + V_strcpy_safe(s.gameDescription,m_iServer.gameDescription); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles changes to combo boxes +//----------------------------------------------------------------------------- +void CCreateMultiplayerGameServerPage::OnTextChanged(Panel *panel) +{ + if (panel == m_pGameCombo) + { + // see if we should update the game name + bool updateHostname = false; + char hostname[256]; + GetControlString("ServerNameEdit", m_szHostName, sizeof(m_szHostName)); + V_sprintf_safe(hostname, "%s dedicated server", m_szGameName); + if (!stricmp(m_szHostName, hostname)) + { + updateHostname = true; + } + + // update the game name + m_pGameCombo->GetText( m_szGameName, sizeof(m_szGameName) ); + + + // Copy the gamedir into m_szMod. + KeyValues *gameData = m_pGameCombo->GetActiveItemUserData(); + if ( !gameData ) + Error( "Missing gameData for active item." ); + + const char *pGameDir = gameData->GetString( "gamedir", NULL ); + if ( !pGameDir ) + Error( "Game %s missing 'gamedir' key.", m_szGameName ); + + V_strcpy_safe( m_szMod, pGameDir ); + + + // re-load the GameInfo KeyValues + if ( m_pGameInfo ) + { + m_pGameInfo->deleteThis(); + } + char liblist[1024]; + Q_snprintf(liblist, sizeof(liblist) - 1, "%s\\gameinfo.txt", m_szMod); + m_pGameInfo = new KeyValues( "GameInfo" ); + m_pGameInfo->LoadFromFile( g_pFullFileSystem, liblist ); + + // redo the hostname with the new game name + if (updateHostname) + { + V_sprintf_safe(hostname, "%s dedicated server", m_szGameName); + SetControlString("ServerNameEdit", hostname); + } + + // reload the list of maps we display + LoadMapList(); + } +} + +#endif // _WIN32 + diff --git a/dedicated/vgui/CreateMultiplayerGameServerPage.h b/dedicated/vgui/CreateMultiplayerGameServerPage.h new file mode 100644 index 0000000..6cabef4 --- /dev/null +++ b/dedicated/vgui/CreateMultiplayerGameServerPage.h @@ -0,0 +1,126 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef CREATEMULTIPLAYERGAMESERVERPAGE_H +#define CREATEMULTIPLAYERGAMESERVERPAGE_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include <vgui_controls/Button.h> +#include <vgui_controls/ComboBox.h> + +//----------------------------------------------------------------------------- +// Purpose: Data describing a single server +//----------------------------------------------------------------------------- +struct serveritem_t +{ + serveritem_t() + { + pings[0] = 0; + pings[1] = 0; + pings[2] = 0; + } + + unsigned char ip[4]; + int port; + int received; + float time; + int ping; // current ping time, derived from pings[] + int pings[3]; // last 3 ping times + bool hadSuccessfulResponse; // server has responded successfully in the past + bool doNotRefresh; // server is marked as not responding and should no longer be refreshed + char gameDir[32]; // current game directory + char map[32]; // current map + char gameDescription[64]; // game description + char name[64]; // server name + int players; + int maxPlayers; + int botPlayers; + bool proxy; + bool password; + bool secure; + bool loadedFromFile; // true if this entry was loaded from file rather than comming from the master + unsigned int serverID; + int listEntryID; + char rconPassword[64]; // the rcon password for this server +}; + +//----------------------------------------------------------------------------- +// Purpose: server options page of the create game server dialog +//----------------------------------------------------------------------------- +class CCreateMultiplayerGameServerPage : public vgui::Frame +{ + DECLARE_CLASS_SIMPLE( CCreateMultiplayerGameServerPage, vgui::Frame ); +public: + CCreateMultiplayerGameServerPage(vgui::Panel *parent, const char *name); + ~CCreateMultiplayerGameServerPage(); + + // returns currently entered information about the server + int GetMaxPlayers() { return m_iMaxPlayers; } + const char *GetPassword() { return m_szPassword; } + const char *GetHostName() { return m_szHostName; } + const char *GetMapName(); + const char *GetModName() { return m_szMod; } + const char *GetGameName() { return m_szGameName; } + void LoadMapList(); + int LoadMaps( const char *pszMod ); + + virtual void OnCommand(const char *cmd); + + virtual void OnResetData(); + + void GetServer(serveritem_t &s); + const char *GetRconPassword(); + +private: + enum { MAX_PLAYERS = 32 }; + enum { DATA_STR_LENGTH = 64 }; + + void LoadConfig(); + void SaveConfig(); + void SetConfig(const char *serverName, const char *rconPassword, int maxPlayers, const char *map, const char *mod, int network, int secure, int port); + + bool LaunchOldDedicatedServer( KeyValues *pGameInfo ); + + //void OnCommand(const char *text); + void LoadMODList(); + void LoadModListInDirectory( const char *pDirectoryName ); + void LoadPossibleMod( const char *pGameDirName ); + void AddMod( const char *pGameDirName, const char *pGameInfoFilename, KeyValues *pGameInfo ); + + bool BadRconChars(const char *pass); + + vgui::ComboBox *m_pGameCombo; + vgui::ComboBox *m_pMapList; + vgui::ComboBox *m_pNumPlayers; + vgui::ComboBox *m_pNetworkCombo; + vgui::Button *m_pStartServerButton; + vgui::Button *m_pCancelButton; + vgui::CheckButton *m_pSecureCheck; + + MESSAGE_FUNC_PTR( OnTextChanged, "TextChanged", panel ); + + serveritem_t m_iServer; + + char m_szHostName[DATA_STR_LENGTH]; + char m_szPassword[DATA_STR_LENGTH]; + char m_szMapName[DATA_STR_LENGTH]; + char m_szMod[DATA_STR_LENGTH]; + char m_szGameName[DATA_STR_LENGTH]; + char m_szExtra[DATA_STR_LENGTH*2]; + int m_iMaxPlayers; + int m_iPort; + + vgui::Panel *m_MainPanel; + KeyValues *m_pSavedData; // data to save away + KeyValues *m_pGameInfo; +}; + + +#endif // CREATEMULTIPLAYERGAMESERVERPAGE_H diff --git a/dedicated/vgui/MainPanel.cpp b/dedicated/vgui/MainPanel.cpp new file mode 100644 index 0000000..f7fc51b --- /dev/null +++ b/dedicated/vgui/MainPanel.cpp @@ -0,0 +1,274 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#ifdef _WIN32 +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#include <windows.h> +// base vgui interfaces +#include <vgui/vgui.h> +#include <vgui_controls/Panel.h> +#include <vgui/IVGui.h> +#include <vgui/ISurface.h> +#include <vgui/Cursor.h> +#include <vgui_controls/ProgressBox.h> + +#include "filesystem.h" +#include "IAdminServer.h" + +#include "MainPanel.h" +#include <imanageserver.h> +#include "ivguimodule.h" +#include <vgui/IVGui.h> + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +static CMainPanel *s_InternetDlg = NULL; +CSysModule *g_hAdminServerModule = NULL; +extern IAdminServer *g_pAdminServer; +char *gpszCvars = NULL; + + +void Sys_Sleep_Old( int msec ); + +extern BOOL gbAppHasBeenTerminated; // used to signal the server thread + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CMainPanel::CMainPanel( ) : Panel(NULL, "CMainPanel") +{ + SetPaintBackgroundEnabled( false ); + SetFgColor( Color( 0,0,0,0 ) ); + m_bStarting = false; + m_flPreviousSteamProgress = 0.0f; + m_pGameServer= NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CMainPanel::~CMainPanel() +{ + if (gpszCvars) + { + free(gpszCvars); + } + +} + +//----------------------------------------------------------------------------- +// Purpose: Called once to set up +//----------------------------------------------------------------------------- +void CMainPanel::Initialize( ) +{ + s_InternetDlg = this; + m_pGameServer = NULL; + + m_bStarted = false; + m_bIsInConfig = true; + m_bClosing = false; + m_pProgressBox = NULL; + m_hShutdown = NULL; + + MoveToFront(); + + m_pConfigPage = new CCreateMultiplayerGameServerPage(this, "Config"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainPanel::Open( void ) +{ + m_pConfigPage->SetVisible(true); + m_pConfigPage->MoveToFront(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMainPanel::OnClose() +{ + DoStop(); +} + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to a static instance of this dialog +//----------------------------------------------------------------------------- +CMainPanel *CMainPanel::GetInstance() +{ + return s_InternetDlg; +} + +//----------------------------------------------------------------------------- +// Purpose: Changes to the console page and starts up the actual server +//----------------------------------------------------------------------------- +void CMainPanel::StartServer(const char *cvars) +{ + surface()->SetCursor(dc_hourglass); + m_pConfigPage->GetServer(s1); + + // hide the config page and close it + m_pConfigPage->SetVisible(false); + m_pConfigPage->Close(); + + gpszCvars = strdup(cvars); + + // show the basic progress box immediately + m_pProgressBox = new ProgressBox("#Start_Server_Loading_Title", "#Server_UpdatingSteamResources", "Starting dedicated server..."); + m_pProgressBox->SetCancelButtonVisible(true); + m_pProgressBox->ShowWindow(); + + // make sure we have all the steam content for this mod + char reslist[_MAX_PATH]; + _snprintf(reslist, sizeof(reslist), "reslists/%s/preload.lst", m_pConfigPage->GetGameName()); + m_hResourceWaitHandle = g_pFullFileSystem->WaitForResources(reslist); + if (!m_hResourceWaitHandle) + { + Assert( 0 ); + } + + m_pProgressBox->SetCancelButtonEnabled(false); + + m_hShutdown = CreateEvent( NULL, TRUE, FALSE, NULL ); + ivgui()->AddTickSignal(GetVPanel()); +} + +//----------------------------------------------------------------------------- +// Purpose: lets us delay the loading of the management screen until the server has started +//----------------------------------------------------------------------------- +void CMainPanel::OnTick() +{ + if (m_hResourceWaitHandle) + { + // see if we've been cancelled + if (!m_pProgressBox.Get() || !m_pProgressBox->IsVisible()) + { + // cancel out + g_pFullFileSystem->CancelWaitForResources(m_hResourceWaitHandle); + m_hResourceWaitHandle = NULL; + DoStop(); + return; + } + + // update resource waiting + bool complete; + float progress; + if (g_pFullFileSystem->GetWaitForResourcesProgress(m_hResourceWaitHandle, &progress, &complete)) + { + vgui::ivgui()->DPrintf2( "progress %.2f %s\n", progress, complete ? "not complete" : "complete" ); + + // don't set the progress if we've jumped straight from 0 to 100% complete + if (!(progress == 1.0f && m_flPreviousSteamProgress == 0.0f)) + { + m_pProgressBox->SetProgress(progress); + m_flPreviousSteamProgress = progress; + } + } + + // This is here because without it, the dedicated server will consume a lot of CPU and it will slow Steam down + // so much that it'll download at 64k instead of 6M. + Sleep( 200 ); + + // see if we're done + if (complete) + { + m_hResourceWaitHandle = NULL; + m_bStarting = true; + m_bIsInConfig = false; + // at this stage in the process the user can no longer cancel + m_pProgressBox->SetCancelButtonEnabled(false); + } + } + + if (m_bStarting) // if we are actively launching the app + { + static int count = 0; + if (WAIT_OBJECT_0 == WaitForSingleObject(m_hShutdown, 10) || count > 5000) + { + if (!m_bStarted) + { + serveritem_t server; + m_pConfigPage->GetServer(server); + ManageServerUIHandle_t managePage = g_pAdminServer->OpenManageServerDialog(server.name, server.gameDir); + m_pGameServer = g_pAdminServer->GetManageServerInterface(managePage); + m_bStarted = true; + + if (m_pProgressBox) + { + m_pProgressBox->Close(); + m_pProgressBox = NULL; + } + } + else // must be stopping the server + { + DoStop(); + } + + surface()->SetCursor(dc_user); + m_bStarting = false; + ResetEvent(m_hShutdown); + } + else + { + count++; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: stops VGUI and kills any progress dialog we may have been displaying +//----------------------------------------------------------------------------- +void CMainPanel::DoStop() +{ + surface()->SetCursor(dc_user); + + m_bStarted = false; + m_bClosing = true; + + if (m_pProgressBox) + { + m_pProgressBox->Close(); + m_pProgressBox = NULL; + } + + ivgui()->Stop(); +} + +//----------------------------------------------------------------------------- +// Purpose: Pushes text into the console +//----------------------------------------------------------------------------- +void CMainPanel::AddConsoleText(const char *msg) +{ + if (m_pGameServer) + { + m_pGameServer->AddToConsole(msg); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Message map +//----------------------------------------------------------------------------- +MessageMapItem_t CMainPanel::m_MessageMap[] = +{ + MAP_MESSAGE( CMainPanel, "Quit", OnClose ), +}; + +IMPLEMENT_PANELMAP(CMainPanel, BaseClass); + +#endif // _WIN32 diff --git a/dedicated/vgui/MainPanel.h b/dedicated/vgui/MainPanel.h new file mode 100644 index 0000000..04f558e --- /dev/null +++ b/dedicated/vgui/MainPanel.h @@ -0,0 +1,89 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// +#ifndef CMAINPANEL_H +#define CMAINPANEL_H +#ifdef _WIN32 +#pragma once +#endif + +#include <vgui_controls/Frame.h> +#include <vgui_controls/Panel.h> +#include <vgui_controls/ListPanel.h> +#include <vgui_controls/PHandle.h> +#include "utlvector.h" + +//#include <GamePanelInfo.h> + +#include "imanageserver.h" +//#include "gameserver.h" +#include "CreateMultiplayerGameServerPage.h" + +class IAdminServer; + +//----------------------------------------------------------------------------- +// Purpose: Root panel for dedicated server GUI +//----------------------------------------------------------------------------- +class CMainPanel : public vgui::Panel +{ +public: + // Construction/destruction + CMainPanel( ); + virtual ~CMainPanel(); + + virtual void Initialize( ); + + // displays the dialog, moves it into focus, updates if it has to + virtual void Open( void ); + + // returns a pointer to a static instance of this dialog + // valid for use only in sort functions + static CMainPanel *GetInstance(); + virtual void StartServer(const char *cvars); + + void ActivateBuildMode(); + + void *GetShutdownHandle() { return m_hShutdown; } + + void AddConsoleText(const char *msg); + + bool Stopping() { return m_bClosing; } + + bool IsInConfig() { return m_bIsInConfig; } + +private: + + // called when dialog is shut down + virtual void OnClose(); + virtual void OnTick(); + void DoStop(); + + // GUI elements + IManageServer *m_pGameServer; + + // the popup menu + vgui::DHANDLE<vgui::ProgressBox> m_pProgressBox; + CCreateMultiplayerGameServerPage *m_pConfigPage; + + // Event that lets the thread tell the main window it shutdown + void *m_hShutdown; + + bool m_bStarting; // whether the server is currently starting + bool m_bStarted; // whether the server has been started or not + bool m_bClosing; // whether we are shutting down + bool m_bIsInConfig; + serveritem_t s1; + int m_hResourceWaitHandle; + float m_flPreviousSteamProgress; + + typedef vgui::Panel BaseClass; + DECLARE_PANELMAP(); + +}; + +#endif // CMAINPANEL_H
\ No newline at end of file diff --git a/dedicated/vgui/vguihelpers.cpp b/dedicated/vgui/vguihelpers.cpp new file mode 100644 index 0000000..11cf33e --- /dev/null +++ b/dedicated/vgui/vguihelpers.cpp @@ -0,0 +1,187 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// +#ifdef _WIN32 +#include <windows.h> +#include <direct.h> + +// includes for the VGUI version +#include <vgui_controls/Panel.h> +#include <vgui_controls/Controls.h> +#include <vgui/ISystem.h> +#include <vgui/IVGui.h> +#include <vgui/IPanel.h> +#include "filesystem.h" +#include <vgui/ILocalize.h> +#include <vgui/IScheme.h> +#include <vgui/ISurface.h> +#include <IVGuiModule.h> + +#include "vgui/MainPanel.h" +#include "IAdminServer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +static CMainPanel *g_pMainPanel = NULL; // the main panel to show +static CSysModule *g_hAdminServerModule; +IAdminServer *g_pAdminServer = NULL; +static IVGuiModule *g_pAdminVGuiModule = NULL; + +void* DedicatedFactory(const char *pName, int *pReturnCode); + + +//----------------------------------------------------------------------------- +// Purpose: Starts up the VGUI system and loads the base panel +//----------------------------------------------------------------------------- +int StartVGUI( CreateInterfaceFn dedicatedFactory ) +{ + // the "base dir" so we can scan mod name + g_pFullFileSystem->AddSearchPath(".", "MAIN"); + // the main platform dir + g_pFullFileSystem->AddSearchPath( "platform", "PLATFORM", PATH_ADD_TO_HEAD); + + vgui::ivgui()->SetSleep(false); + + // find our configuration directory + char szConfigDir[512]; + const char *steamPath = getenv("SteamInstallPath"); + if (steamPath) + { + // put the config dir directly under steam + Q_snprintf(szConfigDir, sizeof(szConfigDir), "%s/config", steamPath); + } + else + { + // we're not running steam, so just put the config dir under the platform + Q_strncpy( szConfigDir, "platform/config", sizeof(szConfigDir)); + } + g_pFullFileSystem->CreateDirHierarchy("config", "PLATFORM"); + g_pFullFileSystem->AddSearchPath(szConfigDir, "CONFIG", PATH_ADD_TO_HEAD); + + // initialize the user configuration file + vgui::system()->SetUserConfigFile("DedicatedServerDialogConfig.vdf", "CONFIG"); + + // Init the surface + g_pMainPanel = new CMainPanel( ); + g_pMainPanel->SetVisible(true); + + vgui::surface()->SetEmbeddedPanel(g_pMainPanel->GetVPanel()); + + // load the scheme + vgui::scheme()->LoadSchemeFromFile("Resource/SourceScheme.res", NULL); + + // localization + g_pVGuiLocalize->AddFile( "Resource/platform_%language%.txt" ); + g_pVGuiLocalize->AddFile( "Resource/vgui_%language%.txt" ); + g_pVGuiLocalize->AddFile( "Admin/server_%language%.txt" ); + + // Start vgui + vgui::ivgui()->Start(); + + // load the module + g_pFullFileSystem->GetLocalCopy("bin/AdminServer.dll"); + g_hAdminServerModule = g_pFullFileSystem->LoadModule("AdminServer"); + Assert(g_hAdminServerModule != NULL); + CreateInterfaceFn adminFactory = NULL; + + if (!g_hAdminServerModule) + { + vgui::ivgui()->DPrintf2("Admin Error: module version (Admin/AdminServer.dll, %s) invalid, not loading\n", IMANAGESERVER_INTERFACE_VERSION ); + } + else + { + // make sure we get the right version + adminFactory = Sys_GetFactory(g_hAdminServerModule); + g_pAdminServer = (IAdminServer *)adminFactory(ADMINSERVER_INTERFACE_VERSION, NULL); + g_pAdminVGuiModule = (IVGuiModule *)adminFactory("VGuiModuleAdminServer001", NULL); + Assert(g_pAdminServer != NULL); + Assert(g_pAdminVGuiModule != NULL); + if (!g_pAdminServer || !g_pAdminVGuiModule) + { + vgui::ivgui()->DPrintf2("Admin Error: module version (Admin/AdminServer.dll, %s) invalid, not loading\n", IMANAGESERVER_INTERFACE_VERSION ); + } + } + + // finish initializing admin module + g_pAdminVGuiModule->Initialize( &dedicatedFactory, 1 ); + g_pAdminVGuiModule->PostInitialize(&adminFactory, 1); + g_pAdminVGuiModule->SetParent( g_pMainPanel->GetVPanel() ); + + // finish setting up main panel + g_pMainPanel->Initialize( ); + g_pMainPanel->Open(); + + return 0; +} + + + +//----------------------------------------------------------------------------- +// Purpose: Shuts down the VGUI system +//----------------------------------------------------------------------------- +void StopVGUI() +{ + SetEvent(g_pMainPanel->GetShutdownHandle()); + + delete g_pMainPanel; + g_pMainPanel = NULL; + + if (g_hAdminServerModule) + { + g_pAdminVGuiModule->Shutdown( ); + Sys_UnloadModule(g_hAdminServerModule); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Run a single VGUI frame +//----------------------------------------------------------------------------- +void RunVGUIFrame() +{ + vgui::ivgui()->RunFrame(); +} + + +bool VGUIIsStopping() +{ + return g_pMainPanel->Stopping(); +} + + +bool VGUIIsRunning() +{ + return vgui::ivgui()->IsRunning(); +} + +bool VGUIIsInConfig() +{ + return g_pMainPanel->IsInConfig(); +} + +void VGUIFinishedConfig() +{ + Assert( g_pMainPanel ); + if(g_pMainPanel) // engine is loaded, pass the message on + { + SetEvent(g_pMainPanel->GetShutdownHandle()); + } +} + +void VGUIPrintf( const char *msg ) +{ + if ( !g_pMainPanel || VGUIIsInConfig() || VGUIIsStopping() ) + { + OutputDebugStringA( msg ); + } + else if ( g_pMainPanel ) + { + g_pMainPanel->AddConsoleText( msg ); + } +} + +#endif // _WIN32 + diff --git a/dedicated/vgui/vguihelpers.h b/dedicated/vgui/vguihelpers.h new file mode 100644 index 0000000..e29203e --- /dev/null +++ b/dedicated/vgui/vguihelpers.h @@ -0,0 +1,26 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#ifndef VGUIHELPERS_H +#define VGUIHELPERS_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/interface.h" + +int StartVGUI( CreateInterfaceFn dedicatedFactory ); +void StopVGUI(); +void RunVGUIFrame(); +bool VGUIIsRunning(); +bool VGUIIsStopping(); +bool VGUIIsInConfig(); +void VGUIFinishedConfig(); +void VGUIPrintf( const char *msg ); + +#endif // VGUIHELPERS_H + diff --git a/dedicated/vgui/vguihelpers_linux.cpp b/dedicated/vgui/vguihelpers_linux.cpp new file mode 100644 index 0000000..4b8f474 --- /dev/null +++ b/dedicated/vgui/vguihelpers_linux.cpp @@ -0,0 +1,39 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// +#include "interface.h" + +bool InitializeVGui(CreateInterfaceFn *factorylist, int factorycount) +{ +} + +int StartVGUI() +{ +} + +void StopVGUI() +{ +} + +void RunVGUIFrame() +{ +} + +bool VGUIIsRunning() +{ +} + +bool VGUIIsStopping() +{ +} + +bool VGUIIsInConfig() +{ +} + +void VGUIFinishedConfig() +{ +} + |