From 51b6ae414f4324e75cc5248e51803ea7fb0b4a09 Mon Sep 17 00:00:00 2001 From: Eugene Date: Thu, 7 Mar 2024 18:18:19 +0200 Subject: [PATCH] cmd: fixed nested commands offset after command execution cmd: fixed handling of truncated command buffer leftover cmd: handle 'wait' command(s) much more correctly and made its timing really 1-frame-precise --- code/client/cl_cgame.c | 8 +-- code/qcommon/cmd.c | 124 +++++++++++++++++++++++++++++------------ code/qcommon/common.c | 7 ++- code/qcommon/qcommon.h | 10 +++- 4 files changed, 104 insertions(+), 45 deletions(-) diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c index 3ae9b59dc..635c0e9f6 100644 --- a/code/client/cl_cgame.c +++ b/code/client/cl_cgame.c @@ -27,8 +27,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA extern botlib_export_t *botlib_export; -static int nestedCmdOffset; // nested command buffer offset - //extern qboolean loadCamera(const char *name); //extern void startCamera(int time); //extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles); @@ -511,7 +509,7 @@ static intptr_t CL_CgameSystemCalls( intptr_t *args ) { case CG_SENDCONSOLECOMMAND: { const char *cmd = VMA(1); - nestedCmdOffset = Cbuf_Add( cmd, nestedCmdOffset ); + Cbuf_NestedAdd( cmd ); return 0; } case CG_ADDCOMMAND: @@ -829,7 +827,7 @@ void CL_InitCGame( void ) { int t1, t2; vmInterpret_t interpret; - nestedCmdOffset = 0; + Cbuf_NestedReset(); t1 = Sys_Milliseconds(); @@ -910,7 +908,7 @@ qboolean CL_GameCommand( void ) { bRes = (qboolean)VM_Call( cgvm, 0, CG_CONSOLE_COMMAND ); - nestedCmdOffset = 0; + Cbuf_NestedReset(); return bRes; } diff --git a/code/qcommon/cmd.c b/code/qcommon/cmd.c index 430dc3280..f111ee1f1 100644 --- a/code/qcommon/cmd.c +++ b/code/qcommon/cmd.c @@ -102,23 +102,47 @@ void Cbuf_AddText( const char *text ) { } +static int nestedCmdOffset; + +void Cbuf_NestedReset( void ) { + nestedCmdOffset = 0; +} + /* ============ -Cbuf_Add +Cbuf_NestedAdd // Adds command text at the specified position of the buffer, adds \n when needed ============ */ -int Cbuf_Add( const char *text, int pos ) { +void Cbuf_NestedAdd( const char *text ) { int len = (int)strlen( text ); + int pos = nestedCmdOffset; qboolean separate = qfalse; int i; - if ( len == 0 ) { - return cmd_text.cursize; + if ( len <= 0 ) { + nestedCmdOffset = cmd_text.cursize; + return; } +#if 0 + if ( cmd_text.cursize > 0 ) { + const int c = cmd_text.data[cmd_text.cursize - 1]; + // insert separator for already existing command(s) + if ( c != '\n' && c != ';' && text[0] != '\n' && text[0] != ';' ) { + if ( cmd_text.cursize < cmd_text.maxsize ) { + cmd_text.data[cmd_text.cursize++] = ';'; + } else { + Com_Printf( S_COLOR_YELLOW "%s(%i) overflowed\n", __func__, pos ); + nestedCmdOffset = cmd_text.cursize; + return; + } + } + } +#endif + if ( pos > cmd_text.cursize || pos < 0 ) { // insert at the text end pos = cmd_text.cursize; @@ -133,7 +157,8 @@ int Cbuf_Add( const char *text, int pos ) { if ( len + cmd_text.cursize > cmd_text.maxsize ) { Com_Printf( S_COLOR_YELLOW "%s(%i) overflowed\n", __func__, pos ); - return cmd_text.cursize; + nestedCmdOffset = cmd_text.cursize; + return; } // move the existing command text @@ -152,7 +177,7 @@ int Cbuf_Add( const char *text, int pos ) { cmd_text.cursize += len; - return pos + len; + nestedCmdOffset = cmd_text.cursize; } @@ -200,19 +225,20 @@ void Cbuf_ExecuteText( cbufExec_t exec_when, const char *text ) switch (exec_when) { case EXEC_NOW: + cmd_wait = 0; // discard any pending waiting if ( text && text[0] != '\0' ) { Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", text); - Cmd_ExecuteString (text); + Cmd_ExecuteString( text ); } else { Cbuf_Execute(); - Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", cmd_text.data); + Com_DPrintf( S_COLOR_YELLOW "EXEC_NOW %s\n", cmd_text.data ); } break; case EXEC_INSERT: - Cbuf_InsertText (text); + Cbuf_InsertText( text ); break; case EXEC_APPEND: - Cbuf_AddText (text); + Cbuf_AddText( text ); break; default: Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when"); @@ -227,25 +253,23 @@ Cbuf_Execute */ void Cbuf_Execute( void ) { - int i; - char *text; - char line[MAX_CMD_LINE]; - int quotes; + char line[MAX_CMD_LINE], *text; + int i, n, quotes; + + if ( cmd_wait > 0 ) { + // delay command buffer execution + cmd_wait--; + return; + } // This will keep // style comments all on one line by not breaking on // a semicolon. It will keep /* ... */ style comments all on one line by not // breaking it for semicolon or newline. qboolean in_star_comment = qfalse; qboolean in_slash_comment = qfalse; + while ( cmd_text.cursize > 0 ) { - if ( cmd_wait > 0 ) { - // skip out while text still remains in buffer, leaving it - // for next frame - cmd_wait--; - break; - } - // find a \n or ; line break or comment: // or /* */ text = (char *)cmd_text.data; @@ -279,36 +303,64 @@ void Cbuf_Execute( void ) } } - if ( i >= (MAX_CMD_LINE - 1) ) - i = MAX_CMD_LINE - 1; + // copy up to (MAX_CMD_LINE - 1) chars but keep buffer position intact to prevent parsing truncated leftover + if ( i > (MAX_CMD_LINE - 1) ) + n = MAX_CMD_LINE - 1; + else + n = i; - Com_Memcpy( line, text, i ); - line[i] = '\0'; + Com_Memcpy( line, text, n ); + line[n] = '\0'; // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec) can insert data at the // beginning of the text buffer - if ( i == cmd_text.cursize ) - cmd_text.cursize = 0; - else - { - i++; - cmd_text.cursize -= i; - // skip all repeating newlines/semicolons - while ( ( text[i] == '\n' || text[i] == '\r' || text[i] == ';' ) && cmd_text.cursize > 0 ) { - cmd_text.cursize--; - i++; + if ( i == cmd_text.cursize ) { + //cmd_text.cursize = 0; + } else { + ++i; + // skip all repeating newlines/semicolons/whitespaces + while ( i < cmd_text.cursize && (text[i] == '\n' || text[i] == '\r' || text[i] == ';' || ( text[i] != '\0' && text[i] <= ' ' ) ) ) { + ++i; + } + } + + cmd_text.cursize -= i; + + if ( cmd_text.cursize ) { + memmove( text, text + i, cmd_text.cursize ); + } + + if ( nestedCmdOffset > 0 ) { + nestedCmdOffset -= i; + if ( nestedCmdOffset < 0 ) { + nestedCmdOffset = 0; } - memmove( text, text+i, cmd_text.cursize ); } // execute the command line Cmd_ExecuteString( line ); + + // break on wait command + if ( cmd_wait > 0 ) { + break; + } } } +/* +============ +Cbuf_Wait +============ +*/ +qboolean Cbuf_Wait( void ) +{ + return (cmd_wait > 0) ? qtrue : qfalse; +} + + /* ============================================================================== diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 0e06e5e09..646dfd79e 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -2987,7 +2987,7 @@ static void Com_ExecuteCfg( void ) Cbuf_ExecuteText(EXEC_NOW, "exec default.cfg\n"); Cbuf_Execute(); // Always execute after exec to prevent text buffer overflowing - if(!Com_SafeMode()) + if (!Com_SafeMode()) { // skip the q3config.cfg and autoexec.cfg if "safe" is on the command line Cbuf_ExecuteText(EXEC_NOW, "exec " Q3CONFIG_CFG "\n"); @@ -4369,7 +4369,10 @@ void Com_Frame( qboolean noDelay ) { timeBeforeEvents = Sys_Milliseconds(); } Com_EventLoop(); - Cbuf_Execute(); + + if ( !Cbuf_Wait() ) { + Cbuf_Execute(); + } // // client side diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 37954f22d..03f0da113 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -456,8 +456,11 @@ void Cbuf_Init( void ); void Cbuf_AddText( const char *text ); // Adds command text at the end of the buffer, does NOT add a final \n -int Cbuf_Add( const char *text, int pos ); -// Adds command text at the specified position of the buffer, adds \n when needed +void Cbuf_NestedAdd( const char *text ); +// Adds nested command text at the specified position of the buffer, adds \n when needed + +void Cbuf_NestedReset( void ); +// Resets nested cmd offset void Cbuf_InsertText( const char *text ); // Adds command text at the beginning of the buffer, add \n @@ -471,6 +474,9 @@ void Cbuf_Execute( void ); // Normally called once per frame, but may be explicitly invoked. // Do not call inside a command function, or current args will be destroyed. +qboolean Cbuf_Wait( void ); +// Checks if wait command timeout remaining + //=========================================================================== /*