From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- sp/src/tier1/commandbuffer.cpp | 1272 ++++++++++++++++++++-------------------- 1 file changed, 636 insertions(+), 636 deletions(-) (limited to 'sp/src/tier1/commandbuffer.cpp') diff --git a/sp/src/tier1/commandbuffer.cpp b/sp/src/tier1/commandbuffer.cpp index e4968b90..2897d038 100644 --- a/sp/src/tier1/commandbuffer.cpp +++ b/sp/src/tier1/commandbuffer.cpp @@ -1,636 +1,636 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//===========================================================================// - -#include "tier1/CommandBuffer.h" -#include "tier1/utlbuffer.h" -#include "tier1/strtools.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#define MAX_ALIAS_NAME 32 -#define MAX_COMMAND_LENGTH 1024 - -struct cmdalias_t -{ - cmdalias_t *next; - char name[ MAX_ALIAS_NAME ]; - char *value; -}; - - -//----------------------------------------------------------------------------- -// Constructor, destructor -//----------------------------------------------------------------------------- -CCommandBuffer::CCommandBuffer( ) : m_Commands( 32, 32 ) -{ - m_hNextCommand = m_Commands.InvalidIndex(); - m_nWaitDelayTicks = 1; - m_nCurrentTick = 0; - m_nLastTickToProcess = -1; - m_nArgSBufferSize = 0; - m_bIsProcessingCommands = false; - m_nMaxArgSBufferLength = ARGS_BUFFER_LENGTH; -} - -CCommandBuffer::~CCommandBuffer() -{ -} - - -//----------------------------------------------------------------------------- -// Indicates how long to delay when encoutering a 'wait' command -//----------------------------------------------------------------------------- -void CCommandBuffer::SetWaitDelayTime( int nTickDelay ) -{ - Assert( nTickDelay >= 0 ); - m_nWaitDelayTicks = nTickDelay; -} - - -//----------------------------------------------------------------------------- -// Specifies a max limit of the args buffer. For unittesting. Size == 0 means use default -//----------------------------------------------------------------------------- -void CCommandBuffer::LimitArgumentBufferSize( int nSize ) -{ - if ( nSize > ARGS_BUFFER_LENGTH ) - { - nSize = ARGS_BUFFER_LENGTH; - } - - m_nMaxArgSBufferLength = ( nSize == 0 ) ? ARGS_BUFFER_LENGTH : nSize; -} - - -//----------------------------------------------------------------------------- -// Parses argv0 out of the buffer -//----------------------------------------------------------------------------- -bool CCommandBuffer::ParseArgV0( CUtlBuffer &buf, char *pArgV0, int nMaxLen, const char **pArgS ) -{ - pArgV0[0] = 0; - *pArgS = NULL; - - if ( !buf.IsValid() ) - return false; - - int nSize = buf.ParseToken( CCommand::DefaultBreakSet(), pArgV0, nMaxLen ); - if ( ( nSize <= 0 ) || ( nMaxLen == nSize ) ) - return false; - - int nArgSLen = buf.TellMaxPut() - buf.TellGet(); - *pArgS = (nArgSLen > 0) ? (const char*)buf.PeekGet() : NULL; - return true; -} - - -//----------------------------------------------------------------------------- -// Insert a command into the command queue -//----------------------------------------------------------------------------- -void CCommandBuffer::InsertCommandAtAppropriateTime( int hCommand ) -{ - int i; - Command_t &command = m_Commands[hCommand]; - for ( i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) - { - if ( m_Commands[i].m_nTick > command.m_nTick ) - break; - } - m_Commands.LinkBefore( i, hCommand ); -} - - -//----------------------------------------------------------------------------- -// Insert a command into the command queue at the appropriate time -//----------------------------------------------------------------------------- -void CCommandBuffer::InsertImmediateCommand( int hCommand ) -{ - m_Commands.LinkBefore( m_hNextCommand, hCommand ); -} - - -//----------------------------------------------------------------------------- -// Insert a command into the command queue -//----------------------------------------------------------------------------- -bool CCommandBuffer::InsertCommand( const char *pArgS, int nCommandSize, int nTick ) -{ - if ( nCommandSize >= CCommand::MaxCommandLength() ) - { - Warning( "WARNING: Command too long... ignoring!\n%s\n", pArgS ); - return false; - } - - // Add one for null termination - if ( m_nArgSBufferSize + nCommandSize + 1 > m_nMaxArgSBufferLength ) - { - Compact(); - if ( m_nArgSBufferSize + nCommandSize + 1 > m_nMaxArgSBufferLength ) - return false; - } - - memcpy( &m_pArgSBuffer[m_nArgSBufferSize], pArgS, nCommandSize ); - m_pArgSBuffer[m_nArgSBufferSize + nCommandSize] = 0; - ++nCommandSize; - - int hCommand = m_Commands.Alloc(); - Command_t &command = m_Commands[hCommand]; - command.m_nTick = nTick; - command.m_nFirstArgS = m_nArgSBufferSize; - command.m_nBufferSize = nCommandSize; - - m_nArgSBufferSize += nCommandSize; - - if ( !m_bIsProcessingCommands || ( nTick > m_nCurrentTick ) ) - { - InsertCommandAtAppropriateTime( hCommand ); - } - else - { - InsertImmediateCommand( hCommand ); - } - return true; -} - - -//----------------------------------------------------------------------------- -// Returns the length of the next command -//----------------------------------------------------------------------------- -void CCommandBuffer::GetNextCommandLength( const char *pText, int nMaxLen, int *pCommandLength, int *pNextCommandOffset ) -{ - int nCommandLength = 0; - int nNextCommandOffset; - bool bIsQuoted = false; - bool bIsCommented = false; - for ( nNextCommandOffset=0; nNextCommandOffset < nMaxLen; ++nNextCommandOffset, nCommandLength += bIsCommented ? 0 : 1 ) - { - char c = pText[nNextCommandOffset]; - if ( !bIsCommented ) - { - if ( c == '"' ) - { - bIsQuoted = !bIsQuoted; - continue; - } - - // don't break if inside a C++ style comment - if ( !bIsQuoted && c == '/' ) - { - bIsCommented = ( nNextCommandOffset < nMaxLen-1 ) && pText[nNextCommandOffset+1] == '/'; - if ( bIsCommented ) - { - ++nNextCommandOffset; - continue; - } - } - - // don't break if inside a quoted string - if ( !bIsQuoted && c == ';' ) - break; - } - - // FIXME: This is legacy behavior; should we not break if a \n is inside a quoted string? - if ( c == '\n' ) - break; - } - - *pCommandLength = nCommandLength; - *pNextCommandOffset = nNextCommandOffset; -} - - -//----------------------------------------------------------------------------- -// Add text to command buffer, return false if it couldn't owing to overflow -//----------------------------------------------------------------------------- -bool CCommandBuffer::AddText( const char *pText, int nTickDelay ) -{ - Assert( nTickDelay >= 0 ); - - int nLen = Q_strlen( pText ); - int nTick = m_nCurrentTick + nTickDelay; - - // Parse the text into distinct commands - const char *pCurrentCommand = pText; - int nOffsetToNextCommand; - for( ; nLen > 0; nLen -= nOffsetToNextCommand+1, pCurrentCommand += nOffsetToNextCommand+1 ) - { - // find a \n or ; line break - int nCommandLength; - GetNextCommandLength( pCurrentCommand, nLen, &nCommandLength, &nOffsetToNextCommand ); - if ( nCommandLength <= 0 ) - continue; - - const char *pArgS; - char *pArgV0 = (char*)_alloca( nCommandLength+1 ); - CUtlBuffer bufParse( pCurrentCommand, nCommandLength, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY ); - ParseArgV0( bufParse, pArgV0, nCommandLength+1, &pArgS ); - if ( pArgV0[0] == 0 ) - continue; - - // Deal with the special 'wait' command - if ( !Q_stricmp( pArgV0, "wait" ) && IsWaitEnabled() ) - { - int nDelay = pArgS ? atoi( pArgS ) : m_nWaitDelayTicks; - nTick += nDelay; - continue; - } - - if ( !InsertCommand( pCurrentCommand, nCommandLength, nTick ) ) - return false; - } - - return true; -} - - -//----------------------------------------------------------------------------- -// Are we in the middle of processing commands? -//----------------------------------------------------------------------------- -bool CCommandBuffer::IsProcessingCommands() -{ - return m_bIsProcessingCommands; -} - - -//----------------------------------------------------------------------------- -// Delays all queued commands to execute at a later time -//----------------------------------------------------------------------------- -void CCommandBuffer::DelayAllQueuedCommands( int nDelay ) -{ - if ( nDelay <= 0 ) - return; - - for ( int i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) - { - m_Commands[i].m_nTick += nDelay; - } -} - - -//----------------------------------------------------------------------------- -// Call this to begin iterating over all commands up to flCurrentTime -//----------------------------------------------------------------------------- -void CCommandBuffer::BeginProcessingCommands( int nDeltaTicks ) -{ - if ( nDeltaTicks == 0 ) - return; - - Assert( !m_bIsProcessingCommands ); - m_bIsProcessingCommands = true; - m_nLastTickToProcess = m_nCurrentTick + nDeltaTicks - 1; - - // Necessary to insert commands while commands are being processed - m_hNextCommand = m_Commands.Head(); -} - - -//----------------------------------------------------------------------------- -// Returns the next command -//----------------------------------------------------------------------------- -bool CCommandBuffer::DequeueNextCommand( ) -{ - m_CurrentCommand.Reset(); - - Assert( m_bIsProcessingCommands ); - if ( m_Commands.Count() == 0 ) - return false; - - int nHead = m_Commands.Head(); - Command_t &command = m_Commands[ nHead ]; - if ( command.m_nTick > m_nLastTickToProcess ) - return false; - - m_nCurrentTick = command.m_nTick; - - // Copy the current command into a temp buffer - // NOTE: This is here to avoid the pointers returned by DequeueNextCommand - // to become invalid by calling AddText. Is there a way we can avoid the memcpy? - if ( command.m_nBufferSize > 0 ) - { - m_CurrentCommand.Tokenize( &m_pArgSBuffer[command.m_nFirstArgS] ); - } - - m_Commands.Remove( nHead ); - - // Necessary to insert commands while commands are being processed - m_hNextCommand = m_Commands.Head(); - -// Msg("Dequeue : "); -// for ( int i = 0; i < nArgc; ++i ) -// { -// Msg("%s ", m_pCurrentArgv[i] ); -// } -// Msg("\n"); - return true; -} - - -//----------------------------------------------------------------------------- -// Returns the next command -//----------------------------------------------------------------------------- -int CCommandBuffer::DequeueNextCommand( const char **& ppArgv ) -{ - DequeueNextCommand(); - ppArgv = ArgV(); - return ArgC(); -} - - -//----------------------------------------------------------------------------- -// Compacts the command buffer -//----------------------------------------------------------------------------- -void CCommandBuffer::Compact() -{ - // Compress argvbuffer + argv - // NOTE: I'm using this choice instead of calling malloc + free - // per command to allocate arguments because I expect to post a - // bunch of commands but not have many delayed commands; - // avoiding the allocation cost seems more important that the memcpy - // cost here since I expect to not have much to copy. - m_nArgSBufferSize = 0; - - char pTempBuffer[ ARGS_BUFFER_LENGTH ]; - for ( int i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) - { - Command_t &command = m_Commands[ i ]; - - memcpy( &pTempBuffer[m_nArgSBufferSize], &m_pArgSBuffer[command.m_nFirstArgS], command.m_nBufferSize ); - command.m_nFirstArgS = m_nArgSBufferSize; - m_nArgSBufferSize += command.m_nBufferSize; - } - - // NOTE: We could also store 2 buffers in the command buffer and switch - // between the two to avoid the 2nd memcpy; but again I'm guessing the memory - // tradeoff isn't worth it - memcpy( m_pArgSBuffer, pTempBuffer, m_nArgSBufferSize ); -} - - -//----------------------------------------------------------------------------- -// Call this to finish iterating over all commands -//----------------------------------------------------------------------------- -void CCommandBuffer::EndProcessingCommands() -{ - Assert( m_bIsProcessingCommands ); - m_bIsProcessingCommands = false; - m_nCurrentTick = m_nLastTickToProcess + 1; - m_hNextCommand = m_Commands.InvalidIndex(); - - // Extract commands that are before the end time - // NOTE: This is a bug for this to - int i = m_Commands.Head(); - if ( i == m_Commands.InvalidIndex() ) - { - m_nArgSBufferSize = 0; - return; - } - - while ( i != m_Commands.InvalidIndex() ) - { - if ( m_Commands[i].m_nTick >= m_nCurrentTick ) - break; - - AssertMsgOnce( false, "CCommandBuffer::EndProcessingCommands() called before all appropriate commands were dequeued.\n" ); - int nNext = i; - Msg( "Warning: Skipping command %s\n", &m_pArgSBuffer[ m_Commands[i].m_nFirstArgS ] ); - m_Commands.Remove( i ); - i = nNext; - } - - Compact(); -} - - -//----------------------------------------------------------------------------- -// Returns a handle to the next command to process -//----------------------------------------------------------------------------- -CommandHandle_t CCommandBuffer::GetNextCommandHandle() -{ - Assert( m_bIsProcessingCommands ); - return m_Commands.Head(); -} - - -#if 0 -/* -=============== -Cmd_Alias_f - -Creates a new command that executes a command string (possibly ; seperated) -=============== -*/ -void Cmd_Alias_f (void) -{ - cmdalias_t *a; - char cmd[MAX_COMMAND_LENGTH]; - int i, c; - char *s; - - if (Cmd_Argc() == 1) - { - Con_Printf ("Current alias commands:\n"); - for (a = cmd_alias ; a ; a=a->next) - Con_Printf ("%s : %s\n", a->name, a->value); - return; - } - - s = Cmd_Argv(1); - if (strlen(s) >= MAX_ALIAS_NAME) - { - Con_Printf ("Alias name is too long\n"); - return; - } - -// copy the rest of the command line - cmd[0] = 0; // start out with a null string - c = Cmd_Argc(); - for (i=2 ; i< c ; i++) - { - Q_strncat(cmd, Cmd_Argv(i), sizeof( cmd ), COPY_ALL_CHARACTERS); - if (i != c) - { - Q_strncat (cmd, " ", sizeof( cmd ), COPY_ALL_CHARACTERS ); - } - } - Q_strncat (cmd, "\n", sizeof( cmd ), COPY_ALL_CHARACTERS); - - // if the alias already exists, reuse it - for (a = cmd_alias ; a ; a=a->next) - { - if (!strcmp(s, a->name)) - { - if ( !strcmp( a->value, cmd ) ) // Re-alias the same thing - return; - - delete[] a->value; - break; - } - } - - if (!a) - { - a = (cmdalias_t *)new cmdalias_t; - a->next = cmd_alias; - cmd_alias = a; - } - Q_strncpy (a->name, s, sizeof( a->name ) ); - - a->value = COM_StringCopy(cmd); -} - - - -/* -============================================================================= - - COMMAND EXECUTION - -============================================================================= -*/ - -#define MAX_ARGS 80 - -static int cmd_argc; -static char *cmd_argv[MAX_ARGS]; -static char *cmd_null_string = ""; -static const char *cmd_args = NULL; - -cmd_source_t cmd_source; - -//----------------------------------------------------------------------------- -// Purpose: -// Output : void Cmd_Init -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void Cmd_Shutdown( void ) -{ - // TODO, cleanup - while ( cmd_alias ) - { - cmdalias_t *next = cmd_alias->next; - delete cmd_alias->value; // created by StringCopy() - delete cmd_alias; - cmd_alias = next; - } -} - - - -/* -============ -Cmd_ExecuteString - -A complete command line has been parsed, so try to execute it -FIXME: lookupnoadd the token to speed search? -============ -*/ -const ConCommandBase *Cmd_ExecuteString (const char *text, cmd_source_t src) -{ - cmdalias_t *a; - - cmd_source = src; - Cmd_TokenizeString (text); - -// execute the command line - if (!Cmd_Argc()) - return NULL; // no tokens - -// check alias - for (a=cmd_alias ; a ; a=a->next) - { - if (!Q_strcasecmp (cmd_argv[0], a->name)) - { - Cbuf_InsertText (a->value); - return NULL; - } - } - -// check ConCommands - ConCommandBase const *pCommand = ConCommandBase::FindCommand( cmd_argv[ 0 ] ); - if ( pCommand && pCommand->IsCommand() ) - { - bool isServerCommand = ( pCommand->IsBitSet( FCVAR_GAMEDLL ) && - // Typed at console - cmd_source == src_command && - // Not HLDS - !sv.IsDedicated() ); - - // Hook to allow game .dll to figure out who type the message on a listen server - if ( serverGameClients ) - { - // We're actually the server, so set it up locally - if ( sv.IsActive() ) - { - g_pServerPluginHandler->SetCommandClient( -1 ); - -#ifndef SWDS - // Special processing for listen server player - if ( isServerCommand ) - { - g_pServerPluginHandler->SetCommandClient( cl.m_nPlayerSlot ); - } -#endif - } - // We're not the server, but we've been a listen server (game .dll loaded) - // forward this command tot he server instead of running it locally if we're still - // connected - // Otherwise, things like "say" won't work unless you quit and restart - else if ( isServerCommand ) - { - if ( cl.IsConnected() ) - { - Cmd_ForwardToServer(); - return NULL; - } - else - { - // It's a server command, but we're not connected to a server. Don't try to execute it. - return NULL; - } - } - } - - // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on -#ifndef _DEBUG - if ( pCommand->IsBitSet( FCVAR_CHEAT ) ) - { - if ( !Host_IsSinglePlayerGame() && sv_cheats.GetInt() == 0 ) - { - Msg( "Can't use cheat command %s in multiplayer, unless the server has sv_cheats set to 1.\n", pCommand->GetName() ); - return NULL; - } - } -#endif - - (( ConCommand * )pCommand )->Dispatch(); - return pCommand; - } - - // check cvars - if ( cv->IsCommand() ) - { - return pCommand; - } - - // forward the command line to the server, so the entity DLL can parse it - if ( cmd_source == src_command ) - { - if ( cl.IsConnected() ) - { - Cmd_ForwardToServer(); - return NULL; - } - } - - Msg("Unknown command \"%s\"\n", Cmd_Argv(0)); - - return NULL; -} -#endif +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + +#include "tier1/CommandBuffer.h" +#include "tier1/utlbuffer.h" +#include "tier1/strtools.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define MAX_ALIAS_NAME 32 +#define MAX_COMMAND_LENGTH 1024 + +struct cmdalias_t +{ + cmdalias_t *next; + char name[ MAX_ALIAS_NAME ]; + char *value; +}; + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CCommandBuffer::CCommandBuffer( ) : m_Commands( 32, 32 ) +{ + m_hNextCommand = m_Commands.InvalidIndex(); + m_nWaitDelayTicks = 1; + m_nCurrentTick = 0; + m_nLastTickToProcess = -1; + m_nArgSBufferSize = 0; + m_bIsProcessingCommands = false; + m_nMaxArgSBufferLength = ARGS_BUFFER_LENGTH; +} + +CCommandBuffer::~CCommandBuffer() +{ +} + + +//----------------------------------------------------------------------------- +// Indicates how long to delay when encoutering a 'wait' command +//----------------------------------------------------------------------------- +void CCommandBuffer::SetWaitDelayTime( int nTickDelay ) +{ + Assert( nTickDelay >= 0 ); + m_nWaitDelayTicks = nTickDelay; +} + + +//----------------------------------------------------------------------------- +// Specifies a max limit of the args buffer. For unittesting. Size == 0 means use default +//----------------------------------------------------------------------------- +void CCommandBuffer::LimitArgumentBufferSize( int nSize ) +{ + if ( nSize > ARGS_BUFFER_LENGTH ) + { + nSize = ARGS_BUFFER_LENGTH; + } + + m_nMaxArgSBufferLength = ( nSize == 0 ) ? ARGS_BUFFER_LENGTH : nSize; +} + + +//----------------------------------------------------------------------------- +// Parses argv0 out of the buffer +//----------------------------------------------------------------------------- +bool CCommandBuffer::ParseArgV0( CUtlBuffer &buf, char *pArgV0, int nMaxLen, const char **pArgS ) +{ + pArgV0[0] = 0; + *pArgS = NULL; + + if ( !buf.IsValid() ) + return false; + + int nSize = buf.ParseToken( CCommand::DefaultBreakSet(), pArgV0, nMaxLen ); + if ( ( nSize <= 0 ) || ( nMaxLen == nSize ) ) + return false; + + int nArgSLen = buf.TellMaxPut() - buf.TellGet(); + *pArgS = (nArgSLen > 0) ? (const char*)buf.PeekGet() : NULL; + return true; +} + + +//----------------------------------------------------------------------------- +// Insert a command into the command queue +//----------------------------------------------------------------------------- +void CCommandBuffer::InsertCommandAtAppropriateTime( int hCommand ) +{ + int i; + Command_t &command = m_Commands[hCommand]; + for ( i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) + { + if ( m_Commands[i].m_nTick > command.m_nTick ) + break; + } + m_Commands.LinkBefore( i, hCommand ); +} + + +//----------------------------------------------------------------------------- +// Insert a command into the command queue at the appropriate time +//----------------------------------------------------------------------------- +void CCommandBuffer::InsertImmediateCommand( int hCommand ) +{ + m_Commands.LinkBefore( m_hNextCommand, hCommand ); +} + + +//----------------------------------------------------------------------------- +// Insert a command into the command queue +//----------------------------------------------------------------------------- +bool CCommandBuffer::InsertCommand( const char *pArgS, int nCommandSize, int nTick ) +{ + if ( nCommandSize >= CCommand::MaxCommandLength() ) + { + Warning( "WARNING: Command too long... ignoring!\n%s\n", pArgS ); + return false; + } + + // Add one for null termination + if ( m_nArgSBufferSize + nCommandSize + 1 > m_nMaxArgSBufferLength ) + { + Compact(); + if ( m_nArgSBufferSize + nCommandSize + 1 > m_nMaxArgSBufferLength ) + return false; + } + + memcpy( &m_pArgSBuffer[m_nArgSBufferSize], pArgS, nCommandSize ); + m_pArgSBuffer[m_nArgSBufferSize + nCommandSize] = 0; + ++nCommandSize; + + int hCommand = m_Commands.Alloc(); + Command_t &command = m_Commands[hCommand]; + command.m_nTick = nTick; + command.m_nFirstArgS = m_nArgSBufferSize; + command.m_nBufferSize = nCommandSize; + + m_nArgSBufferSize += nCommandSize; + + if ( !m_bIsProcessingCommands || ( nTick > m_nCurrentTick ) ) + { + InsertCommandAtAppropriateTime( hCommand ); + } + else + { + InsertImmediateCommand( hCommand ); + } + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the length of the next command +//----------------------------------------------------------------------------- +void CCommandBuffer::GetNextCommandLength( const char *pText, int nMaxLen, int *pCommandLength, int *pNextCommandOffset ) +{ + int nCommandLength = 0; + int nNextCommandOffset; + bool bIsQuoted = false; + bool bIsCommented = false; + for ( nNextCommandOffset=0; nNextCommandOffset < nMaxLen; ++nNextCommandOffset, nCommandLength += bIsCommented ? 0 : 1 ) + { + char c = pText[nNextCommandOffset]; + if ( !bIsCommented ) + { + if ( c == '"' ) + { + bIsQuoted = !bIsQuoted; + continue; + } + + // don't break if inside a C++ style comment + if ( !bIsQuoted && c == '/' ) + { + bIsCommented = ( nNextCommandOffset < nMaxLen-1 ) && pText[nNextCommandOffset+1] == '/'; + if ( bIsCommented ) + { + ++nNextCommandOffset; + continue; + } + } + + // don't break if inside a quoted string + if ( !bIsQuoted && c == ';' ) + break; + } + + // FIXME: This is legacy behavior; should we not break if a \n is inside a quoted string? + if ( c == '\n' ) + break; + } + + *pCommandLength = nCommandLength; + *pNextCommandOffset = nNextCommandOffset; +} + + +//----------------------------------------------------------------------------- +// Add text to command buffer, return false if it couldn't owing to overflow +//----------------------------------------------------------------------------- +bool CCommandBuffer::AddText( const char *pText, int nTickDelay ) +{ + Assert( nTickDelay >= 0 ); + + int nLen = Q_strlen( pText ); + int nTick = m_nCurrentTick + nTickDelay; + + // Parse the text into distinct commands + const char *pCurrentCommand = pText; + int nOffsetToNextCommand; + for( ; nLen > 0; nLen -= nOffsetToNextCommand+1, pCurrentCommand += nOffsetToNextCommand+1 ) + { + // find a \n or ; line break + int nCommandLength; + GetNextCommandLength( pCurrentCommand, nLen, &nCommandLength, &nOffsetToNextCommand ); + if ( nCommandLength <= 0 ) + continue; + + const char *pArgS; + char *pArgV0 = (char*)_alloca( nCommandLength+1 ); + CUtlBuffer bufParse( pCurrentCommand, nCommandLength, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY ); + ParseArgV0( bufParse, pArgV0, nCommandLength+1, &pArgS ); + if ( pArgV0[0] == 0 ) + continue; + + // Deal with the special 'wait' command + if ( !Q_stricmp( pArgV0, "wait" ) && IsWaitEnabled() ) + { + int nDelay = pArgS ? atoi( pArgS ) : m_nWaitDelayTicks; + nTick += nDelay; + continue; + } + + if ( !InsertCommand( pCurrentCommand, nCommandLength, nTick ) ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Are we in the middle of processing commands? +//----------------------------------------------------------------------------- +bool CCommandBuffer::IsProcessingCommands() +{ + return m_bIsProcessingCommands; +} + + +//----------------------------------------------------------------------------- +// Delays all queued commands to execute at a later time +//----------------------------------------------------------------------------- +void CCommandBuffer::DelayAllQueuedCommands( int nDelay ) +{ + if ( nDelay <= 0 ) + return; + + for ( int i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) + { + m_Commands[i].m_nTick += nDelay; + } +} + + +//----------------------------------------------------------------------------- +// Call this to begin iterating over all commands up to flCurrentTime +//----------------------------------------------------------------------------- +void CCommandBuffer::BeginProcessingCommands( int nDeltaTicks ) +{ + if ( nDeltaTicks == 0 ) + return; + + Assert( !m_bIsProcessingCommands ); + m_bIsProcessingCommands = true; + m_nLastTickToProcess = m_nCurrentTick + nDeltaTicks - 1; + + // Necessary to insert commands while commands are being processed + m_hNextCommand = m_Commands.Head(); +} + + +//----------------------------------------------------------------------------- +// Returns the next command +//----------------------------------------------------------------------------- +bool CCommandBuffer::DequeueNextCommand( ) +{ + m_CurrentCommand.Reset(); + + Assert( m_bIsProcessingCommands ); + if ( m_Commands.Count() == 0 ) + return false; + + int nHead = m_Commands.Head(); + Command_t &command = m_Commands[ nHead ]; + if ( command.m_nTick > m_nLastTickToProcess ) + return false; + + m_nCurrentTick = command.m_nTick; + + // Copy the current command into a temp buffer + // NOTE: This is here to avoid the pointers returned by DequeueNextCommand + // to become invalid by calling AddText. Is there a way we can avoid the memcpy? + if ( command.m_nBufferSize > 0 ) + { + m_CurrentCommand.Tokenize( &m_pArgSBuffer[command.m_nFirstArgS] ); + } + + m_Commands.Remove( nHead ); + + // Necessary to insert commands while commands are being processed + m_hNextCommand = m_Commands.Head(); + +// Msg("Dequeue : "); +// for ( int i = 0; i < nArgc; ++i ) +// { +// Msg("%s ", m_pCurrentArgv[i] ); +// } +// Msg("\n"); + return true; +} + + +//----------------------------------------------------------------------------- +// Returns the next command +//----------------------------------------------------------------------------- +int CCommandBuffer::DequeueNextCommand( const char **& ppArgv ) +{ + DequeueNextCommand(); + ppArgv = ArgV(); + return ArgC(); +} + + +//----------------------------------------------------------------------------- +// Compacts the command buffer +//----------------------------------------------------------------------------- +void CCommandBuffer::Compact() +{ + // Compress argvbuffer + argv + // NOTE: I'm using this choice instead of calling malloc + free + // per command to allocate arguments because I expect to post a + // bunch of commands but not have many delayed commands; + // avoiding the allocation cost seems more important that the memcpy + // cost here since I expect to not have much to copy. + m_nArgSBufferSize = 0; + + char pTempBuffer[ ARGS_BUFFER_LENGTH ]; + for ( int i = m_Commands.Head(); i != m_Commands.InvalidIndex(); i = m_Commands.Next(i) ) + { + Command_t &command = m_Commands[ i ]; + + memcpy( &pTempBuffer[m_nArgSBufferSize], &m_pArgSBuffer[command.m_nFirstArgS], command.m_nBufferSize ); + command.m_nFirstArgS = m_nArgSBufferSize; + m_nArgSBufferSize += command.m_nBufferSize; + } + + // NOTE: We could also store 2 buffers in the command buffer and switch + // between the two to avoid the 2nd memcpy; but again I'm guessing the memory + // tradeoff isn't worth it + memcpy( m_pArgSBuffer, pTempBuffer, m_nArgSBufferSize ); +} + + +//----------------------------------------------------------------------------- +// Call this to finish iterating over all commands +//----------------------------------------------------------------------------- +void CCommandBuffer::EndProcessingCommands() +{ + Assert( m_bIsProcessingCommands ); + m_bIsProcessingCommands = false; + m_nCurrentTick = m_nLastTickToProcess + 1; + m_hNextCommand = m_Commands.InvalidIndex(); + + // Extract commands that are before the end time + // NOTE: This is a bug for this to + int i = m_Commands.Head(); + if ( i == m_Commands.InvalidIndex() ) + { + m_nArgSBufferSize = 0; + return; + } + + while ( i != m_Commands.InvalidIndex() ) + { + if ( m_Commands[i].m_nTick >= m_nCurrentTick ) + break; + + AssertMsgOnce( false, "CCommandBuffer::EndProcessingCommands() called before all appropriate commands were dequeued.\n" ); + int nNext = i; + Msg( "Warning: Skipping command %s\n", &m_pArgSBuffer[ m_Commands[i].m_nFirstArgS ] ); + m_Commands.Remove( i ); + i = nNext; + } + + Compact(); +} + + +//----------------------------------------------------------------------------- +// Returns a handle to the next command to process +//----------------------------------------------------------------------------- +CommandHandle_t CCommandBuffer::GetNextCommandHandle() +{ + Assert( m_bIsProcessingCommands ); + return m_Commands.Head(); +} + + +#if 0 +/* +=============== +Cmd_Alias_f + +Creates a new command that executes a command string (possibly ; seperated) +=============== +*/ +void Cmd_Alias_f (void) +{ + cmdalias_t *a; + char cmd[MAX_COMMAND_LENGTH]; + int i, c; + char *s; + + if (Cmd_Argc() == 1) + { + Con_Printf ("Current alias commands:\n"); + for (a = cmd_alias ; a ; a=a->next) + Con_Printf ("%s : %s\n", a->name, a->value); + return; + } + + s = Cmd_Argv(1); + if (strlen(s) >= MAX_ALIAS_NAME) + { + Con_Printf ("Alias name is too long\n"); + return; + } + +// copy the rest of the command line + cmd[0] = 0; // start out with a null string + c = Cmd_Argc(); + for (i=2 ; i< c ; i++) + { + Q_strncat(cmd, Cmd_Argv(i), sizeof( cmd ), COPY_ALL_CHARACTERS); + if (i != c) + { + Q_strncat (cmd, " ", sizeof( cmd ), COPY_ALL_CHARACTERS ); + } + } + Q_strncat (cmd, "\n", sizeof( cmd ), COPY_ALL_CHARACTERS); + + // if the alias already exists, reuse it + for (a = cmd_alias ; a ; a=a->next) + { + if (!strcmp(s, a->name)) + { + if ( !strcmp( a->value, cmd ) ) // Re-alias the same thing + return; + + delete[] a->value; + break; + } + } + + if (!a) + { + a = (cmdalias_t *)new cmdalias_t; + a->next = cmd_alias; + cmd_alias = a; + } + Q_strncpy (a->name, s, sizeof( a->name ) ); + + a->value = COM_StringCopy(cmd); +} + + + +/* +============================================================================= + + COMMAND EXECUTION + +============================================================================= +*/ + +#define MAX_ARGS 80 + +static int cmd_argc; +static char *cmd_argv[MAX_ARGS]; +static char *cmd_null_string = ""; +static const char *cmd_args = NULL; + +cmd_source_t cmd_source; + +//----------------------------------------------------------------------------- +// Purpose: +// Output : void Cmd_Init +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Cmd_Shutdown( void ) +{ + // TODO, cleanup + while ( cmd_alias ) + { + cmdalias_t *next = cmd_alias->next; + delete cmd_alias->value; // created by StringCopy() + delete cmd_alias; + cmd_alias = next; + } +} + + + +/* +============ +Cmd_ExecuteString + +A complete command line has been parsed, so try to execute it +FIXME: lookupnoadd the token to speed search? +============ +*/ +const ConCommandBase *Cmd_ExecuteString (const char *text, cmd_source_t src) +{ + cmdalias_t *a; + + cmd_source = src; + Cmd_TokenizeString (text); + +// execute the command line + if (!Cmd_Argc()) + return NULL; // no tokens + +// check alias + for (a=cmd_alias ; a ; a=a->next) + { + if (!Q_strcasecmp (cmd_argv[0], a->name)) + { + Cbuf_InsertText (a->value); + return NULL; + } + } + +// check ConCommands + ConCommandBase const *pCommand = ConCommandBase::FindCommand( cmd_argv[ 0 ] ); + if ( pCommand && pCommand->IsCommand() ) + { + bool isServerCommand = ( pCommand->IsBitSet( FCVAR_GAMEDLL ) && + // Typed at console + cmd_source == src_command && + // Not HLDS + !sv.IsDedicated() ); + + // Hook to allow game .dll to figure out who type the message on a listen server + if ( serverGameClients ) + { + // We're actually the server, so set it up locally + if ( sv.IsActive() ) + { + g_pServerPluginHandler->SetCommandClient( -1 ); + +#ifndef SWDS + // Special processing for listen server player + if ( isServerCommand ) + { + g_pServerPluginHandler->SetCommandClient( cl.m_nPlayerSlot ); + } +#endif + } + // We're not the server, but we've been a listen server (game .dll loaded) + // forward this command tot he server instead of running it locally if we're still + // connected + // Otherwise, things like "say" won't work unless you quit and restart + else if ( isServerCommand ) + { + if ( cl.IsConnected() ) + { + Cmd_ForwardToServer(); + return NULL; + } + else + { + // It's a server command, but we're not connected to a server. Don't try to execute it. + return NULL; + } + } + } + + // Allow cheat commands in singleplayer, debug, or multiplayer with sv_cheats on +#ifndef _DEBUG + if ( pCommand->IsBitSet( FCVAR_CHEAT ) ) + { + if ( !Host_IsSinglePlayerGame() && sv_cheats.GetInt() == 0 ) + { + Msg( "Can't use cheat command %s in multiplayer, unless the server has sv_cheats set to 1.\n", pCommand->GetName() ); + return NULL; + } + } +#endif + + (( ConCommand * )pCommand )->Dispatch(); + return pCommand; + } + + // check cvars + if ( cv->IsCommand() ) + { + return pCommand; + } + + // forward the command line to the server, so the entity DLL can parse it + if ( cmd_source == src_command ) + { + if ( cl.IsConnected() ) + { + Cmd_ForwardToServer(); + return NULL; + } + } + + Msg("Unknown command \"%s\"\n", Cmd_Argv(0)); + + return NULL; +} +#endif -- cgit v1.2.3