diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/tier1/commandbuffer.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/tier1/commandbuffer.cpp')
| -rw-r--r-- | sp/src/tier1/commandbuffer.cpp | 1272 |
1 files changed, 636 insertions, 636 deletions
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 |