aboutsummaryrefslogtreecommitdiff
path: root/src/parsec_server/con_ext_sv.cpp
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
committerFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
commitc068f22329d5cc722622a2183bbb22eef2093df7 (patch)
tree12d56c1aede67988a55e241364606bfbb4dba933 /src/parsec_server/con_ext_sv.cpp
downloadopenparsec-c068f22329d5cc722622a2183bbb22eef2093df7.tar.xz
openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.zip
initial importHEADmain
Diffstat (limited to 'src/parsec_server/con_ext_sv.cpp')
-rwxr-xr-xsrc/parsec_server/con_ext_sv.cpp1193
1 files changed, 1193 insertions, 0 deletions
diff --git a/src/parsec_server/con_ext_sv.cpp b/src/parsec_server/con_ext_sv.cpp
new file mode 100755
index 0000000..c3c556d
--- /dev/null
+++ b/src/parsec_server/con_ext_sv.cpp
@@ -0,0 +1,1193 @@
+/*
+ * PARSEC - Server External Commands
+ *
+ * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:44 $
+ *
+ * Orginally written by:
+ * Copyright (c) Clemens Beer <cbx@parsec.org> 2001
+ * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// C library
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// compilation flags/debug support
+#include "config.h"
+#include "debug.h"
+
+// general definitions
+#include "general.h"
+#include "objstruc.h"
+
+// global externals
+#include "globals.h"
+
+// local module header
+#include "con_ext_sv.h"
+
+// proprietary module headers
+//#include "con_act.h"
+#include "con_aux_sv.h"
+#include "con_list_sv.h"
+#include "con_main_sv.h"
+#include "con_std_sv.h"
+#include "con_vald.h" // shared !
+//#include "e_demo.h"
+//#include "e_record.h"
+//#include "e_replay.h"
+#include "sys_file.h"
+#include "sys_path.h"
+
+
+
+// maximum depth of recursion in command batch files
+#define MAX_BATCH_REC_DEPTH 16
+
+// special eof needed in package console scripts
+#define PACKAGE_CON_EOF ";eof"
+#define PACKAGE_CON_EOF_LEN 4
+
+// generic string paste area
+#define PASTE_STR_LEN 255
+static char paste_str[ PASTE_STR_LEN + 1 ];
+
+// string constants
+static char con_extension[] = CON_FILE_EXTENSION;
+static char invalid_batch_name[]= "invalid name specifier.";
+static char recursion_canceled[]= "command canceled due to recursion.";
+static char recursion_maxdepth[]= "maximum recursion depth reached.";
+static char replaying_script[] = "replay in progress (script).";
+static char replaying_binary[] = "replay in progress (binary).";
+static char no_replay[] = "no replay in progress.";
+static char stopped_replay[] = "replay has been stopped.";
+static char record_disallowed[] = "recording not allowed (object camera is active).";
+
+static char con_file_wldcard1[] = "*" CON_FILE_EXTENSION;
+static char con_file_wldcard2[] = REFCON_COMMANDS_DIR "*" CON_FILE_EXTENSION;
+static char con_file_wldcard3[] = STDCON_COMMANDS_DIR "*" CON_FILE_EXTENSION;
+static char con_file_wldcard4[] = RECORD_COMMANDS_DIR "*" CON_FILE_EXTENSION;
+
+// name of batch command file to execute on every gameloop start
+char gameloop_start_script[ PATH_MAX + 1 ] = "";
+
+// list of external commands
+char external_commands[ MAX_EXTERNAL_COMMANDS + 1 ][ COMMAND_NAME_ALLOC_LEN + 1 ];
+int external_command_types[ MAX_EXTERNAL_COMMANDS + 1 ];
+int num_external_commands = 0;
+
+// external batch execution vars
+int processing_batch = 0; // flag that commands are called from batch
+int idle_reclevel = 0; // level of recursion idle wait stalled execution on
+int rec_depth = 0; // current level of recursion
+
+// action commands not allowed?
+int preempt_action_commands = FALSE;
+
+// batch execution recursive call stack
+static int extcom_on_level[ MAX_BATCH_REC_DEPTH + 1 ];
+static FILE *fp_for_level[ MAX_BATCH_REC_DEPTH + 1 ];
+
+// log file (created command batch)
+FILE *log_fp;
+char log_name[ COMMAND_NAME_ALLOC_LEN + 1 ];
+int log_commands = 0;
+
+// recording file
+static char record_name[ COMMAND_NAME_ALLOC_LEN + 1 ];
+
+// packet recording/replay file names
+char packet_record_name[ COMMAND_NAME_ALLOC_LEN + 1 ];
+char packet_replay_name[ COMMAND_NAME_ALLOC_LEN + 1 ];
+
+// single command line
+static char com_line[ MAX_CONSOLE_LINE_LENGTH + 1 ];
+
+// cat feature variables
+static char cat_name[ MAX_CONSOLE_LINE_LENGTH + 1 ];
+static int cat_pos;
+
+// current active mod name
+PUBLIC char* mod_names[ MAX_REGISTERED_MODS ];
+PUBLIC int mod_numnames = 0;
+PUBLIC int mod_override = FALSE;
+
+
+// acquire scripts located in mod directories ---------------------------------
+//
+PRIVATE
+void AcquireModScripts()
+{
+ for ( int mod = 0; mod < mod_numnames; mod++ ) {
+
+ char *path = (char *) ALLOCMEM( strlen( mod_names[ mod ] ) + strlen( con_file_wldcard1 ) + 2 );
+ if ( path == NULL ) {
+ OUTOFMEM( 0 );
+ }
+
+ strcpy( path, mod_names[ mod ] );
+ strcat( path, "/" );
+ strcat( path, con_file_wldcard1 );
+
+ char *npath = SYSs_ProcessPathString( path );
+ SYSs_AcquireScriptPath( npath, COMTYPE_MODIFICATION, mod_names[ mod ] );
+
+ FREEMEM( path );
+ }
+}
+
+
+// build list of externally available commands --------------------------------
+//
+void BuildExternalCommandList()
+{
+ //NOTE:
+ // the sequence of paths scanned in this function mostly determines the
+ // display order of console scripts (ls command, tab completion, ...).
+ // the actual precedence of scripts is determined by the search order
+ // in OpenScript(). for consistency, the two orders should be the same.
+
+ num_external_commands = 0;
+
+ SYS_AcquirePackageScripts( COMTYPE_PACKAGE );
+
+ if ( mod_override ) {
+ AcquireModScripts();
+ }
+
+ SYSs_AcquireScriptPath( con_file_wldcard1, COMTYPE_ROOT, NULL );
+ SYSs_AcquireScriptPath( con_file_wldcard2, COMTYPE_REFERENCE, NULL );
+ SYSs_AcquireScriptPath( con_file_wldcard3, COMTYPE_STANDARD, NULL );
+ SYSs_AcquireScriptPath( con_file_wldcard4, COMTYPE_RECORDING, NULL );
+
+ if ( !mod_override ) {
+ AcquireModScripts();
+ }
+}
+
+
+// check if string is valid external command ----------------------------------
+//
+int CheckExternalCommand( char *com )
+{
+ ASSERT( com != NULL );
+
+ //NOTE:
+ // command script directories are only scanned once. therefore,
+ // new commands copied into a directory while the game is still
+ // running will not be noticed until next startup or explicit
+ // rescanning via the "rescan" command.
+
+ // to ensure scripts with explicitly specified paths work
+ com = SYSs_ProcessPathString( com );
+
+ // scan list in memory (no directory rescanning!!)
+ int cid = -1;
+ for ( int eid = 0; eid < num_external_commands; eid++ ) {
+ ASSERT( external_commands[ eid ] != NULL );
+ if ( stricmp( external_commands[ eid ], com ) == 0 ) {
+ cid = eid;
+ break;
+ }
+ }
+
+ // return command number + 1 (thus 0 means command is invalid)
+ return ( cid + 1 );
+}
+
+
+// insert command into log file (command batch) -------------------------------
+//
+int InsertCommandLog( char *command )
+{
+ ASSERT( command != NULL );
+
+ if ( log_commands && ( strcmp( command, CMSTR( CM_CLOSE ) ) != 0 ) ) {
+ // append command to currently open logfile
+ fprintf( log_fp, "%s\n", command );
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+// search all console script paths and open batch if it exists ----------------
+//
+PRIVATE
+FILE *OpenScript( char *script )
+{
+ ASSERT( script != NULL );
+
+ //NOTE:
+ // the sequence of paths scanned in this function determines the
+ // precedence of console scripts of the same name but located in
+ // different directories.
+
+ //NOTE:
+ // the path search sequence is:
+ // 1. data package
+ // 2. root (local) directory
+ // 3. reference directory (refs)
+ // 4. standard directory (cons)
+ // 5. recording directory (recs)
+
+ //NOTE:
+ // to avoid inconsistencies the search order in E_RECORD::LoadPacket()
+ // and E_DEMO::DEMO_BinaryOpenDemo() should be consistent
+ // with the search order used here.
+
+ FILE *fp = NULL;
+
+ strcpy( paste_str, script );
+ strcat( paste_str, con_extension );
+
+ // to ensure scripts with explicitly specified paths work
+ char *path = SYSs_ProcessPathString( paste_str );
+
+ if ( SV_PACKAGE_SCRIPTS ) {
+
+ // try to read from package first
+ if ( ( fp = SYS_fopen( path, "r" ) ) != NULL ) {
+ return fp;
+ }
+ }
+
+ // search root (local) directory
+ if ( ( fp = fopen( path, "r" ) ) != NULL ) {
+ return fp;
+ }
+
+ strcpy( paste_str, REFCON_COMMANDS_DIR );
+ strcat( paste_str, script );
+ strcat( paste_str, con_extension );
+
+ // search reference directory (refs)
+ if ( ( fp = fopen( paste_str, "r" ) ) != NULL ) {
+ return fp;
+ }
+
+ strcpy( paste_str, STDCON_COMMANDS_DIR );
+ strcat( paste_str, script );
+ strcat( paste_str, con_extension );
+
+ // search standard directory (cons)
+ if ( ( fp = fopen( paste_str, "r" ) ) != NULL ) {
+ return fp;
+ }
+
+ strcpy( paste_str, RECORD_COMMANDS_DIR );
+ strcat( paste_str, script );
+ strcat( paste_str, con_extension );
+
+ // search recording directory (recs)
+ return fopen( paste_str, "r" );
+}
+
+
+// display content of command batch file --------------------------------------
+//
+PRIVATE
+void CatExternalCommands_ctd()
+{
+ int i = 0;
+
+ FILE *fp = OpenScript( cat_name );
+
+ if ( fp != NULL ) {
+
+ SYS_fseek( fp, cat_pos, SEEK_SET );
+
+ int packeof = 0;
+ while ( SYS_fgets( com_line, MAX_CONSOLE_LINE_LENGTH, fp ) != NULL ) {
+
+ // check special package eof
+ if ( strncmp( com_line, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) {
+ packeof = 1;
+ break;
+ }
+
+ ProcessExternalLine( com_line );
+ CON_AddLine( com_line );
+ if ( ++i >= cur_vis_conlines - 1 ) {
+ break;
+ }
+ }
+
+ if ( packeof || SYS_feof( fp ) ) {
+ CON_ListEndPrompt();
+ } else {
+ CON_ListCtdPrompt();
+ cat_pos = SYS_ftell( fp );
+ }
+
+ SYS_fclose( fp );
+ }
+}
+
+
+// display content of command batch file (command "cat") ----------------------
+//
+int Cmd_CatExternalCommands( char *cstr )
+{
+ ASSERT( cstr != NULL );
+ HANDLE_COMMAND_DOMAIN_SEP( cstr );
+
+ char *name = strtok( cstr, " " );
+
+ if ( ( name == NULL ) || ( strtok( NULL, " " ) != NULL ) ) {
+ CON_AddLine( "syntax: cat <script>." );
+ return TRUE;
+ }
+
+ if ( !CheckExternalCommand( name ) ) {
+ CON_AddLine( "script not found." );
+ return TRUE;
+ }
+
+ strcpy( cat_name, name );
+ FILE *fp = OpenScript( cat_name );
+
+ if ( fp != NULL ) {
+
+ int i = 0;
+ int packeof = 0;
+ while ( SYS_fgets( com_line, MAX_CONSOLE_LINE_LENGTH, fp ) != NULL ) {
+
+ // check special package eof
+ if ( strncmp( com_line, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) {
+ packeof = 1;
+ break;
+ }
+
+ ProcessExternalLine( com_line );
+ CON_AddLine( com_line );
+ if ( ++i >= cur_vis_conlines - 1 ) {
+ break;
+ }
+ }
+
+ if ( !packeof && !SYS_feof( fp ) ) {
+ cat_pos = SYS_ftell( fp );
+ await_keypress = TRUE;
+ com_cont_func = CatExternalCommands_ctd;
+ }
+
+ SYS_fclose( fp );
+ }
+
+ return TRUE;
+}
+
+
+// forward declaration --------------------------------------------------------
+//
+PRIVATE
+int ExecExternalLine( char *line, int echo );
+
+
+// execute console command file automatically on startup ----------------------
+//
+void ExecConsoleFile( char *fname, int echo )
+{
+ //NOTE:
+ // the scripts recognized by this function NEED NOT be
+ // contained in external_commands[] since files are opened
+ // directly and no error will result if they are not found.
+
+ //NOTE:
+ // this function sets the preempt_action_commands flag
+ // to prevent execution of all (also direct!) action
+ // commands. the processing_batch flag also will not be
+ // set by this function.
+
+ ASSERT( fname != NULL );
+ ASSERT( ( echo == FALSE ) || ( echo == TRUE ) );
+
+ FILE *fp = SYS_fopen( fname, "r" );
+
+ if ( fp != NULL ) {
+
+ // used to determine if invoked from CON_MAIN::InitConsole()
+ extern int console_init_done;
+
+ // put together console message
+ sprintf( paste_str, "executing %s", fname );
+
+ if ( !console_init_done ) {
+ // invocation from InitConsole() (exec init script)
+ MSGPUT( "Executing console script (%s)...", fname );
+ CON_AddLine( paste_str );
+ } else {
+ if ( !SV_CONSOLE_LEVEL_MESSAGES ) {
+ // not invoked from InitConsole()
+ CON_AddMessage( paste_str );
+ }
+ }
+
+ // disallow action commands
+ preempt_action_commands = TRUE;
+
+ // execute script line for line
+ strcpy( com_line, con_prompt );
+ while ( SYS_fgets( com_line + 2, MAX_CONSOLE_LINE_LENGTH - 2, fp ) != NULL ) {
+
+ // check special package eof
+ if ( strncmp( com_line + 2, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) {
+ break;
+ }
+
+ ExecExternalLine( com_line, echo );
+ }
+
+ // allow action commands once again
+ preempt_action_commands = FALSE;
+
+ // insert new prompt
+ if ( console_init_done && echo )
+ CON_AddLine( con_prompt );
+
+ MSGOUT( "\nScript execution done." );
+
+ SYS_fclose( fp );
+ }
+}
+
+
+// scan external line, overwrite invalid characters with space ----------------
+//
+int ProcessExternalLine( char *scan )
+{
+ ASSERT( scan != NULL );
+
+ int exec = 0;
+
+ while ( *scan != 0 ) {
+ if ( ( *scan == '\r' ) || ( *scan == '\n' ) ) {
+ // strip '\r' and '\n' at end of line
+ *scan-- = 0;
+ } else if ( !ConsoleASCIIValid[ (byte) *scan ] ) {
+ if ( ( *scan == '\\' ) && ConsoleASCIIValid[ (byte) '/' ] ) {
+ // replace backslashes with slashes
+ *scan = '/';
+ } else {
+ // replace invalid characters with spaces
+ *scan = ' ';
+ }
+ } else {
+ // convert valid characters to lower-case
+ *scan = tolower( *scan );
+ exec = 1;
+ }
+ scan++;
+ }
+
+ return exec;
+}
+
+
+// execute externally obtained console line -----------------------------------
+//
+PRIVATE
+int ExecExternalLine( char *line, int echo )
+{
+ ASSERT( line != NULL );
+ ASSERT( ( echo == FALSE ) || ( echo == TRUE ) );
+
+ //NOTE:
+ // a ';' in the first column starts a comment
+
+ int exec = FALSE;
+ if ( *( line + 2 ) != ';' ) {
+
+ if ( ProcessExternalLine( line + 2 ) ) {
+ if ( echo )
+ CON_AddLine( line );
+ ExecConsoleCommand( line + 2, echo );
+ exec = TRUE;
+ }
+ }
+
+/*
+ // count executed commands
+ if ( !DEMO_BinaryReplayActive() ) {
+ demoinfo_curline++;
+ }
+*/
+
+ return exec;
+}
+
+
+// execute all lines of external command batch --------------------------------
+//
+PRIVATE
+void ExecLines( int echo )
+{
+ //NOTE:
+ // this function is used by ExecExternalCommand(),
+ // RestartExternalCommand(), and ExecExternalFile()
+ // to execute all lines of a single console file.
+ // it will be called recursively if a script invokes
+ // another script.
+
+ ASSERT( ( echo == FALSE ) || ( echo == TRUE ) );
+
+ // go one recursion level down
+ rec_depth++;
+
+ strcpy( com_line, con_prompt );
+ while ( SYS_fgets( com_line + 2, MAX_CONSOLE_LINE_LENGTH - 2, fp_for_level[ rec_depth - 1 ] ) != NULL ) {
+
+ // check special package eof
+ if ( strncmp( com_line + 2, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) {
+ break;
+ }
+
+ // execute current line
+ ExecExternalLine( com_line, echo );
+
+ // stall recursion if idlewait specified
+ // FIXME: do we need the AC.WAIT command in the server ??
+ /*if ( CurActionWait > 0 ) {
+ break;
+ }*/
+ }
+
+ // go one recursion level up
+ rec_depth--;
+}
+
+
+// execute external command (command batch file) ------------------------------
+//
+PRIVATE
+int ExecExternalCommand( int extcom, int echo )
+{
+ //NOTE:
+ // this function simply assumes that the current
+ // recursion level in rec_depth is valid and can
+ // be used. it is called by CallExternalCommand()
+ // which performs recursion level management.
+
+ ASSERT( extcom >= 0 );
+ ASSERT( ( echo == FALSE ) || ( echo == TRUE ) );
+
+ // store number of command on this level
+ extcom_on_level[ rec_depth ] = extcom;
+
+ // try to open command file
+ fp_for_level[ rec_depth ] = OpenScript( external_commands[ extcom ] );
+
+ // exec command file
+ if ( fp_for_level[ rec_depth ] != NULL ) {
+
+ // store name to use as packet replay file
+ ASSERT( strlen( external_commands[ extcom ] ) <= COMMAND_NAME_ALLOC_LEN );
+ strcpy( packet_replay_name, external_commands[ extcom ] );
+
+ // exec all lines of this batch
+ ExecLines( echo );
+
+ // keep file open if recursion stalled
+ // FIXME: do we need the AC.WAIT command in the server ??
+ //if ( CurActionWait == 0 ) {
+ SYS_fclose( fp_for_level[ rec_depth ] );
+ fp_for_level[ rec_depth ] = NULL;
+ //}
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+// execute external command (command batch file) via filename -----------------
+//
+int ExecExternalFile( char *command )
+{
+ //NOTE:
+ // this is the main internal interface to execute
+ // a specified console script. (used to automatically
+ // execute scripts on game loop entry and video
+ // subsystem switches, for instance.) the scripts
+ // recognized by this function need to be contained
+ // in external_commands[].
+
+ //NOTE:
+ // the processing_batch flag will not be set by this
+ // function. this means that most action commands
+ // will not be allowed to execute.
+
+ //CAVEAT:
+ // no checks are performed whether any replay is
+ // currently in progress and therefore execution
+ // should be disallowed.
+
+ if ( ( command == NULL ) || ( *command == 0 ) )
+ return FALSE;
+
+ int extcom = CheckExternalCommand( command );
+
+ if ( extcom-- == 0 )
+ return FALSE;
+
+ // store number of command on this level
+ extcom_on_level[ rec_depth ] = extcom;
+
+ // try to open command file
+ fp_for_level[ rec_depth ] = OpenScript( external_commands[ extcom ] );
+
+ // exec command file
+ if ( fp_for_level[ rec_depth ] != NULL ) {
+
+ // exec all lines of this batch
+ ExecLines( FALSE );
+
+ // keep file open if recursion stalled
+ // FIXME: do we need the AC.WAIT command in the server ??
+ //if ( CurActionWait == 0 ) {
+ SYS_fclose( fp_for_level[ rec_depth ] );
+ fp_for_level[ rec_depth ] = NULL;
+ //}
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+// compile the state header script necessary for demo cutting -----------------
+//
+int CompileDemoCut( FILE *fp, char *script )
+{
+ ASSERT( fp != NULL );
+ ASSERT( script != NULL );
+
+ ASSERT( rec_depth == 0 );
+
+ if ( num_external_commands == MAX_EXTERNAL_COMMANDS ) {
+ return FALSE;
+ }
+
+ // make sure the script is known
+ int extcom = CheckExternalCommand( script );
+ if ( extcom == 0 ) {
+ // if enough space append to list and set type to root
+ ASSERT( strlen( script ) <= COMMAND_NAME_ALLOC_LEN );
+ strcpy( external_commands[ num_external_commands ], script );
+ external_command_types[ num_external_commands ] = COMTYPE_ROOT;
+ extcom = ++num_external_commands;
+ }
+
+ // compile the script to already open file
+ compile_fp = fp;
+ compile_script = TRUE;
+ processing_batch = TRUE;
+
+ ExecExternalCommand( extcom - 1, FALSE );
+
+ compile_fp = NULL;
+ processing_batch = FALSE;
+ compile_script = FALSE;
+
+ return TRUE;
+}
+
+
+// determine if script replay currently in progress ---------------------------
+//
+int ScriptReplayActive()
+{
+ // if no recursion is stalled then no
+ // replay is currently in progress
+ return ( idle_reclevel > 0 );
+}
+
+
+// restart ExecExternalCommand() ----------------------------------------------
+//
+int RestartExternalCommand( int echo )
+{
+ //NOTE:
+ // this function is called by E_REPLAY::FetchNewActions()
+ // to recommence script execution. it is quite similar
+ // to ExecExternalCommand() and that function will
+ // actually be called by ExecLines() if further command
+ // invocations are encountered.
+
+ ASSERT( ( echo == FALSE ) || ( echo == TRUE ) );
+
+ // check if command batch execution has been stalled
+ if ( !ScriptReplayActive() )
+ return FALSE;
+
+ // set batch execution flag
+ processing_batch = TRUE;
+
+ // non-interactive console mode
+ con_non_interactive = TRUE;
+
+ // start in bottom level of recursion
+ rec_depth = idle_reclevel;
+
+ // restart stalled execution
+ idle_reclevel = 0;
+
+ do {
+
+ // set correct recursion level
+ rec_depth--;
+
+ // exec all lines of this batch
+ ExecLines( echo );
+
+ // keep file open if recursion stalled
+ // FIXME: do we need the AC.WAIT command in the server ??
+ //if ( CurActionWait == 0 ) {
+ SYS_fclose( fp_for_level[ rec_depth ] );
+ fp_for_level[ rec_depth ] = NULL;
+ /*} else {
+ // simulate unraveling of
+ // recursion to highest level
+ rec_depth = 0;
+ break;
+ }*/
+
+ } while ( rec_depth > 0 );
+
+ // clear batch execution flag
+ processing_batch = FALSE;
+
+ // back to interactive console mode
+ con_non_interactive = FALSE;
+
+ // ensure no dangling automatic actions
+ // if execution finished (not stalled)
+ if ( idle_reclevel == 0 ) {
+ //REPLAY_StopAutomaticActions();
+ }
+
+ return TRUE;
+}
+
+
+// check validity of batchname ------------------------------------------------
+//
+PRIVATE
+int CheckBatchName( char *name )
+{
+ ASSERT( name != NULL );
+
+ int fnlen = strlen( name );
+
+ if ( ( fnlen < 1 ) || ( fnlen > COMMAND_NAME_VALID_LEN ) ||
+ ( strtok( NULL, " " ) != NULL ) ) {
+ CON_AddLine( invalid_batch_name );
+ return FALSE;
+ }
+
+ for ( int pos = 0; pos < fnlen; pos++ ) {
+ // only alphanumeric characters and underscores are allowed
+ if ( !isalnum( name[ pos ] ) && ( name[ pos ] != '_' ) ) {
+ CON_AddLine( invalid_batch_name );
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+// open output script for writing ---------------------------------------------
+//
+int OpenOutputBatch( char *cstr, int type )
+{
+ ASSERT( cstr != NULL );
+ ASSERT( ( type == CM_CREATE ) || ( type == CM_OPEN ) || ( type == CM_APPEND ) );
+ HANDLE_COMMAND_DOMAIN_SEP( cstr );
+
+ if ( log_commands ) {
+ CON_AddLine( "there is another script open." );
+ return TRUE;
+ }
+
+ char *name = strtok( cstr, " " );
+ if ( name == NULL ) {
+ CON_AddLine( arg_missing );
+ return TRUE;
+ }
+
+ if ( !CheckBatchName( name ) )
+ return TRUE;
+ strcpy( log_name, name );
+
+ if ( CheckExternalCommand( name ) && ( type == CM_CREATE ) ) {
+ CON_AddLine( "there is already a script with this name." );
+ return TRUE;
+ }
+ strcpy( paste_str, name );
+ strcat( paste_str, con_extension );
+
+ const char *mode = ( type == CM_APPEND ) ? "a" : "w";
+ if ( ( log_fp = fopen( paste_str, mode ) ) != NULL ) {
+ log_commands = 1;
+ } else {
+ CON_AddLine( "cannot open script with this name." );
+ }
+
+ return TRUE;
+}
+
+
+// close output batch ---------------------------------------------------------
+//
+void CloseOutputBatch()
+{
+ if ( log_commands ) {
+
+ fclose( log_fp );
+ log_commands = 0;
+ sprintf( paste_str, "script %s closed.", log_name );
+ CON_AddLine( paste_str );
+
+ // if command not already in list: append
+ if ( !CheckExternalCommand( log_name ) &&
+ ( num_external_commands < MAX_EXTERNAL_COMMANDS ) ) {
+
+ // if enough space append to list and set type to root
+ ASSERT( strlen( log_name ) <= COMMAND_NAME_ALLOC_LEN );
+ strcpy( external_commands[ num_external_commands ], log_name );
+ external_command_types[ num_external_commands ] = COMTYPE_ROOT;
+ num_external_commands++;
+ }
+
+ } else {
+ CON_AddLine( "there is no script open." );
+ }
+}
+
+
+// set file name of script automatically executed on start of game loop -------
+//
+int Cmd_SetGameLoopBatchName( char *cstr )
+{
+ ASSERT( cstr != NULL );
+ HANDLE_COMMAND_DOMAIN_SEP( cstr );
+
+ char *name = strtok( cstr, " " );
+
+ if ( name == NULL ) {
+ if ( *gameloop_start_script != 0 ) {
+ // display name of currently selected
+ // gameloop start-script
+ CON_AddLine( gameloop_start_script );
+ } else {
+ CON_AddLine( "no gameloop start-script set." );
+ }
+ return TRUE;
+ }
+
+ if ( strtok( NULL, " " ) != NULL ) {
+ CON_AddLine( "syntax: glbatch <script>." );
+ return TRUE;
+ }
+
+ if ( CheckBatchName( name ) ) {
+ // set new name for gameloop start-script
+ strcpy( gameloop_start_script, name );
+ sprintf( paste_str, "gameloop start-script set to: %s", gameloop_start_script );
+ CON_AddLine( paste_str );
+ } else {
+ CON_AddLine( "invalid script name." );
+ }
+
+ return TRUE;
+}
+
+
+// start recording into command batch -----------------------------------------
+//
+int StartRecording( char *cstr, int savestate )
+{
+ ASSERT( cstr != NULL );
+ HANDLE_COMMAND_DOMAIN_SEP( cstr );
+
+ if ( !RecordingActive ) {
+
+ // fetch name for recording
+ char *name = strtok( cstr, " " );
+ if ( name == NULL ) {
+ CON_AddLine( arg_missing );
+ return TRUE;
+ }
+ if ( !CheckBatchName( name ) )
+ return TRUE;
+
+ // store name (needed by CloseRecording())
+ strcpy( record_name, name );
+
+ // store name to use as packet record file
+ strcpy( packet_record_name, name );
+
+ // create actual file name
+ strcpy( paste_str, name );
+ strcat( paste_str, con_extension );
+
+ if ( ( RecordingFp = fopen( paste_str, "w" ) ) != NULL ) {
+
+ RemoteRecPacketId = 0;
+ RecordingActive = TRUE;
+
+ // warn user if packet recording is off
+ if ( !RecordRemotePackets ) {
+ CON_AddLine( "[warning]: remote packet recording is off." );
+ }
+
+ // save game state (data screenshot) if desired
+ if ( savestate ) {
+/*
+ REC_InitMatrices();
+ SaveGameState();
+*/
+ CON_AddLine( "game state saved." );
+ }
+
+ } else {
+
+ CON_AddLine( "cannot start a recording with this name." );
+ }
+
+ } else {
+
+ CON_AddLine( "recording already in progress." );
+ }
+
+ return TRUE;
+}
+
+
+// close recording batch ------------------------------------------------------
+//
+void CloseRecording()
+{
+ fclose( RecordingFp );
+
+ RecordingActive = 0;
+ RemoteRecSessionId++;
+
+ sprintf( paste_str, "recording %s closed.", record_name );
+ CON_AddLine( paste_str );
+
+ // if command not already in list: append
+ if ( !CheckExternalCommand( record_name ) &&
+ ( num_external_commands < MAX_EXTERNAL_COMMANDS ) ) {
+
+ // if enough space append to list and set type to
+ // root (not recording since it will be placed
+ // into the root directory!)
+ ASSERT( strlen( record_name ) <= COMMAND_NAME_ALLOC_LEN );
+ strcpy( external_commands[ num_external_commands ], record_name );
+ external_command_types[ num_external_commands ] = COMTYPE_ROOT;
+ num_external_commands++;
+ }
+}
+
+
+// call external command and check recursion; called by ExecConsoleCommand() --
+//
+void CallExternalCommand( int extcom, int echo )
+{
+ //NOTE:
+ // no new command can be started if replay in progress.
+ // unfortunately, this means that no script at all
+ // can be executed while any other script is stalled.
+ // alas, this includes utility scripts.
+
+ ASSERT( extcom >= 0 );
+ ASSERT( ( echo == FALSE ) || ( echo == TRUE ) );
+
+/*
+ // init demo info
+ if ( !DEMO_BinaryReplayActive() ) {
+ DEMO_InitInfo();
+ }
+*/
+
+ // assume action commands will be allowed
+ int actionflag = TRUE;
+
+ // remember action wait
+ // FIXME: do we need the AC.WAIT command in the server ??
+ //int curactionwait = CurActionWait;
+
+ // handle non-recursive call
+ if ( rec_depth == 0 ) {
+
+ if ( ScriptReplayActive() ) {
+ CON_AddLine( "script replay in progress. command not executed." );
+ return;
+ }
+
+ /*if ( DEMO_BinaryReplayActive() ) {
+
+ // disallow action commands to allow
+ // simultaneous playback of binary demo
+ actionflag = FALSE;
+
+ //NOTE:
+ // this ensures that no other ac.wait will be
+ // executed, conflicting with the current one.
+
+ // prevent batch execution from stalling
+ // FIXME: do we need the AC.WAIT command in the server ??
+ CurActionWait = 0;
+ }*/
+ }
+
+ // check if maximum recursion depth reached
+ if ( rec_depth <= MAX_BATCH_REC_DEPTH ) {
+
+ // set batch execution flag
+ processing_batch = actionflag;
+
+ // check all recursion levels above this one
+ int lvl;
+ for ( lvl = 0; lvl < rec_depth; lvl++ )
+ if ( extcom_on_level[ lvl ] == extcom )
+ break;
+
+ // open contained batch if no endless recursion detected
+ if ( lvl == rec_depth ) {
+ ExecExternalCommand( extcom, echo );
+ } else {
+ CON_AddLine( recursion_canceled );
+ }
+
+ } else {
+ // output error message if no more recursion levels available
+ CON_AddLine( recursion_maxdepth );
+ }
+
+ // handle non-recursive call
+ if ( rec_depth == 0 ) {
+
+ // clear batch execution flag
+ processing_batch = FALSE;
+
+ // distinguish script only/simultaneous binary replay
+ if ( actionflag == FALSE ) {
+
+ // restore wait interval if binary demo
+ // replay simultaneously in progress
+ // FIXME: do we need the AC.WAIT command in the server ??
+ //CurActionWait = curactionwait;
+
+ } else {
+
+ // ensure no dangling automatic actions
+ // (only if no simultaneous binary replay)
+ //REPLAY_StopAutomaticActions();
+ }
+ }
+}
+
+
+// kill batch execution that is currently in progress -------------------------
+//
+void ScriptStopReplay()
+{
+ // immediately stop idle waiting
+ // FIXME: do we need the AC.WAIT command in the server ??
+ //CurActionWait = 0;
+
+ // shut down recursive call stack
+ while ( idle_reclevel > 0 ) {
+ // step one level up
+ idle_reclevel--;
+ // close files kept open to maintain
+ // state of recursion for script files
+ if ( fp_for_level[ idle_reclevel ] != NULL ) {
+ SYS_fclose( fp_for_level[ idle_reclevel ] );
+ fp_for_level[ idle_reclevel ] = NULL;
+ }
+ }
+ ASSERT( idle_reclevel == 0 );
+
+ // kill dangling automatic actions
+ //REPLAY_StopAutomaticActions();
+}
+
+
+// close either recording or output batch -------------------------------------
+//
+void Cmd_CloseOutputBatch()
+{
+ if ( RecordingActive )
+ CloseRecording();
+ else
+ CloseOutputBatch();
+}
+
+
+// stop replay of command batch (command "repstop") ---------------------------
+//
+void Cmd_StopBatchReplay()
+{
+// if ( !DEMO_ReplayActive() ) {
+ CON_AddLine( no_replay );
+/* } else {
+ // stop both binary replay and command batch replay
+ DEMO_StopReplay();
+ CON_AddLine( stopped_replay );
+ }
+*/
+}
+
+
+// query status of command batch replay (command "repinfo") -------------------
+//
+void Cmd_QueryReplayInfo()
+{
+ /*if ( ScriptReplayActive() )
+ CON_AddLine( replaying_script );
+ else if ( DEMO_BinaryReplayActive() )
+ CON_AddLine( replaying_binary );
+ else*/
+ CON_AddLine( no_replay );
+}
+
+
+// rescan list of external commands (command "rescan") ------------------------
+//
+void Cmd_RescanExternalCommands()
+{
+ // rebuild demo info
+ //DEMO_RegisterInitialDemos();
+
+ // rebuild script cache
+ BuildExternalCommandList();
+ CON_AddLine( "script cache rebuilt." );
+}
+
+
+