Skip to content

Commit

Permalink
cmd: fixed nested commands offset after command execution
Browse files Browse the repository at this point in the history
cmd: fixed handling of truncated command buffer leftover
cmd: handle 'wait' command(s) much more correctly and made its timing really 1-frame-precise
  • Loading branch information
ec- committed Mar 7, 2024
1 parent d90d628 commit 51b6ae4
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 45 deletions.
8 changes: 3 additions & 5 deletions code/client/cl_cgame.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -829,7 +827,7 @@ void CL_InitCGame( void ) {
int t1, t2;
vmInterpret_t interpret;

nestedCmdOffset = 0;
Cbuf_NestedReset();

t1 = Sys_Milliseconds();

Expand Down Expand Up @@ -910,7 +908,7 @@ qboolean CL_GameCommand( void ) {

bRes = (qboolean)VM_Call( cgvm, 0, CG_CONSOLE_COMMAND );

nestedCmdOffset = 0;
Cbuf_NestedReset();

return bRes;
}
Expand Down
124 changes: 88 additions & 36 deletions code/qcommon/cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -152,7 +177,7 @@ int Cbuf_Add( const char *text, int pos ) {

cmd_text.cursize += len;

return pos + len;
nestedCmdOffset = cmd_text.cursize;
}


Expand Down Expand Up @@ -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");
Expand All @@ -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 ) {

This comment has been minimized.

Copy link
@ensiform

ensiform Mar 7, 2024

Contributor

I have not tested this at all yet, but from glance, won't this change the behavior of multiple waits in a single bind or script?

This comment has been minimized.

Copy link
@ec-

ec- Mar 8, 2024

Author Owner

Sure it will change because multiple wait commands like 'echo 1; wait 125; echo 2; wait 125; echo 3' - will now work properly unlike it was before

This comment has been minimized.

Copy link
@ec-

ec- Mar 8, 2024

Author Owner

this change in particular will force wait command to expire not depending from other commands present in buffer - which leads to more logical and predictable behavior

// 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;

Expand Down Expand Up @@ -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;
}


/*
==============================================================================
Expand Down
7 changes: 5 additions & 2 deletions code/qcommon/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -4369,7 +4369,10 @@ void Com_Frame( qboolean noDelay ) {
timeBeforeEvents = Sys_Milliseconds();
}
Com_EventLoop();
Cbuf_Execute();

if ( !Cbuf_Wait() ) {
Cbuf_Execute();
}

//
// client side
Expand Down
10 changes: 8 additions & 2 deletions code/qcommon/qcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

//===========================================================================

/*
Expand Down

0 comments on commit 51b6ae4

Please sign in to comment.