summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2008-08-24 16:10:31 +0000
committerAndrey Nazarov <skuller@skuller.net>2008-08-24 16:10:31 +0000
commit0d18a13a94d2d3b9bf3ac6ca160c49f7199a0c91 (patch)
tree4e053139df20582497ad39e9446afc6e5b54aee9
parent0257c5df02687ff9a97c7ef87f464838d4df32aa (diff)
Game DLL expects gi.Args() return raw arguments, give it what it wants
for compatibility reasons (preserve non-ascii chars and double quotes in team chat, etc). Added `addfiltercmd', `delfiltercmd' and `listfiltercmds' commands designed to prevent game DLL from interpreting certain client commands.
-rw-r--r--source/cmd.c15
-rw-r--r--source/sv_ccmds.c155
-rw-r--r--source/sv_game.c7
-rw-r--r--source/sv_local.h18
-rw-r--r--source/sv_main.c2
-rw-r--r--source/sv_user.c34
-rw-r--r--wiki/doc/server.mdwn14
7 files changed, 232 insertions, 13 deletions
diff --git a/source/cmd.c b/source/cmd.c
index b725526..43a93b0 100644
--- a/source/cmd.c
+++ b/source/cmd.c
@@ -508,23 +508,24 @@ typedef struct cmd_function_s {
char *name;
} cmd_function_t;
-static list_t cmd_functions; /* possible commands to execute */
+static list_t cmd_functions; // possible commands to execute
static list_t cmd_hash[CMD_HASH_SIZE];
static int cmd_argc;
-static char *cmd_argv[MAX_STRING_TOKENS];
+static char *cmd_argv[MAX_STRING_TOKENS]; // pointers to cmd_data[]
static char *cmd_null_string = "";
-/* complete command string, quotes preserved */
+// complete command string, left untouched
static char cmd_string[MAX_STRING_CHARS];
static size_t cmd_string_len;
-/* offsets of individual tokens in cmd_string */
+// offsets of individual tokens into cmd_string
static size_t cmd_offsets[MAX_STRING_TOKENS];
-/* sequence of NULL-terminated tokens, each cmd_argv[] points here */
+// sequence of NULL-terminated, normalized tokens
static char cmd_data[MAX_STRING_CHARS];
+// normalized command arguments
static char cmd_args[MAX_STRING_CHARS];
int cmd_optind;
@@ -1035,7 +1036,7 @@ void Cmd_TokenizeString( const char *text, qboolean macroExpand ) {
start = data = cmd_string;
while( cmd_argc < MAX_STRING_TOKENS ) {
// skip whitespace up to a /n
- while( *data <= 32 ) {
+ while( *data <= ' ' ) {
if( *data == 0 ) {
return; // end of text
}
@@ -1072,7 +1073,7 @@ void Cmd_TokenizeString( const char *text, qboolean macroExpand ) {
}
// parse reqular token
- while( *data > 32 ) {
+ while( *data > ' ' ) {
if( *data == '\"' ) {
break;
}
diff --git a/source/sv_ccmds.c b/source/sv_ccmds.c
index 530a1e5..6cdb54d 100644
--- a/source/sv_ccmds.c
+++ b/source/sv_ccmds.c
@@ -870,7 +870,7 @@ static void SV_DelStuffCmd_f( void ) {
int i;
if( Cmd_Argc() < 3 ) {
- Com_Printf( "Usage: %s <list> <index>\n", Cmd_Argv( 0 ) );
+ Com_Printf( "Usage: %s <list> <id|all>\n", Cmd_Argv( 0 ) );
return;
}
@@ -925,9 +925,11 @@ static void SV_ListStuffCmds_f( void ) {
return;
}
+ Com_Printf( "id command\n"
+ "-- -------\n" );
count = 1;
LIST_FOR_EACH( stuffcmd_t, stuff, list, entry ) {
- Com_Printf( "(%d) %s\n", count, stuff->string );
+ Com_Printf( "%-2d %s\n", count, stuff->string );
count++;
}
}
@@ -939,6 +941,152 @@ static void SV_StuffCmd_c( genctx_t *ctx, int argnum ) {
}
}
+static const char filteractions[FA_MAX][8] = {
+ "ignore", "print", "stuff", "kick"
+};
+
+static void SV_AddFilterCmd_f( void ) {
+ char *s, *comment;
+ filtercmd_t *filter;
+ filteraction_t action;
+ size_t len;
+
+ if( Cmd_Argc() < 2 ) {
+usage:
+ Com_Printf( "Usage: %s <command> [ignore|print|stuff|kick] [comment]\n", Cmd_Argv( 0 ) );
+ return;
+ }
+
+ if( Cmd_Argc() > 2 ) {
+ s = Cmd_Argv( 2 );
+ for( action = 0; action < FA_MAX; action++ ) {
+ if( !strcmp( s, filteractions[action] ) ) {
+ break;
+ }
+ }
+ if( action == FA_MAX ) {
+ goto usage;
+ }
+ comment = Cmd_ArgsFrom( 3 );
+ } else {
+ action = FA_IGNORE;
+ comment = NULL;
+ }
+
+
+ s = Cmd_Argv( 1 );
+ LIST_FOR_EACH( filtercmd_t, filter, &sv_filterlist, entry ) {
+ if( !Q_stricmp( filter->string, s ) ) {
+ Com_Printf( "Filtercmd already exists: %s\n", s );
+ return;
+ }
+ }
+ len = strlen( s );
+ filter = Z_Malloc( sizeof( *filter ) + len );
+ memcpy( filter->string, s, len + 1 );
+ filter->action = action;
+ filter->comment = Z_CopyString( comment );
+ List_Append( &sv_filterlist, &filter->entry );
+}
+
+static void SV_AddFilterCmd_c( genctx_t *ctx, int argnum ) {
+ filteraction_t action;
+
+ if( argnum == 2 ) {
+ for( action = 0; action < FA_MAX; action++ ) {
+ Prompt_AddMatch( ctx, filteractions[action] );
+ }
+ }
+}
+
+static void SV_DelFilterCmd_f( void ) {
+ filtercmd_t *filter, *next;
+ char *s;
+ int i;
+
+ if( Cmd_Argc() < 2 ) {
+ Com_Printf( "Usage: %s <id|cmd|all>\n", Cmd_Argv( 0 ) );
+ return;
+ }
+
+ if( LIST_EMPTY( &sv_filterlist ) ) {
+ Com_Printf( "No filtercmds registered.\n" );
+ return;
+ }
+
+ s = Cmd_Argv( 1 );
+ if( !strcmp( s, "all" ) ) {
+ LIST_FOR_EACH_SAFE( filtercmd_t, filter, next, &sv_filterlist, entry ) {
+ Z_Free( filter->comment );
+ Z_Free( filter );
+ }
+ List_Init( &sv_filterlist );
+ return;
+ }
+ if( COM_IsUint( s ) ) {
+ i = atoi( s );
+ if( i < 1 ) {
+ Com_Printf( "Bad filtercmd index: %d\n", i );
+ return;
+ }
+ filter = LIST_INDEX( filtercmd_t, i - 1, &sv_filterlist, entry );
+ if( !filter ) {
+ Com_Printf( "No such filtercmd index: %d\n", i );
+ return;
+ }
+ } else {
+ LIST_FOR_EACH( filtercmd_t, filter, &sv_filterlist, entry ) {
+ if( !Q_stricmp( filter->string, s ) ) {
+ goto remove;
+ }
+ }
+ Com_Printf( "No such filtercmd string: %s\n", s );
+ return;
+ }
+
+remove:
+ List_Remove( &filter->entry );
+ Z_Free( filter->comment );
+ Z_Free( filter );
+}
+
+static void SV_DelFilterCmd_c( genctx_t *ctx, int argnum ) {
+ filtercmd_t *filter;
+
+ if( argnum == 1 ) {
+ if( LIST_EMPTY( &sv_filterlist ) ) {
+ return;
+ }
+ ctx->ignorecase = qtrue;
+ Prompt_AddMatch( ctx, "all" );
+ LIST_FOR_EACH( filtercmd_t, filter, &sv_filterlist, entry ) {
+ if( !Prompt_AddMatch( ctx, filter->string ) ) {
+ break;
+ }
+ }
+ }
+}
+
+static void SV_ListFilterCmds_f( void ) {
+ filtercmd_t *filter;
+ int count;
+
+ if( LIST_EMPTY( &sv_filterlist ) ) {
+ Com_Printf( "No filtercmds registered.\n" );
+ return;
+ }
+
+ Com_Printf( "id command action comment\n"
+ "-- ---------------- ------ -------\n" );
+ count = 1;
+ LIST_FOR_EACH( filtercmd_t, filter, &sv_filterlist, entry ) {
+ Com_Printf( "%-2d %-16s %-6s %s\n", count,
+ filter->string, filteractions[filter->action],
+ filter->comment ? filter->comment : "" );
+ count++;
+ }
+}
+
static size_t SV_Client_m( char *buffer, size_t size ) {
if( !sv_client ) {
return Q_strncpyz( buffer, "unknown", size );
@@ -977,6 +1125,9 @@ static const cmdreg_t c_server[] = {
{ "addstuffcmd", SV_AddStuffCmd_f, SV_StuffCmd_c },
{ "delstuffcmd", SV_DelStuffCmd_f, SV_StuffCmd_c },
{ "liststuffcmds", SV_ListStuffCmds_f, SV_StuffCmd_c },
+ { "addfiltercmd", SV_AddFilterCmd_f, SV_AddFilterCmd_c },
+ { "delfiltercmd", SV_DelFilterCmd_f, SV_DelFilterCmd_c },
+ { "listfiltercmds", SV_ListFilterCmds_f },
{ NULL }
};
diff --git a/source/sv_game.c b/source/sv_game.c
index fa0aefe..b277040 100644
--- a/source/sv_game.c
+++ b/source/sv_game.c
@@ -835,13 +835,13 @@ void SV_InitGameProgs ( void ) {
#else
import.trace = SV_Trace_Native;
#endif
-#else /* _WIN32 */
+#else // _WIN32
if( sv_oldgame_hack->integer ) {
import.trace = ( sv_trace_t )SV_Trace;
} else {
import.trace = SV_Trace_Native;
}
-#endif /* !_WIN32 */
+#endif // !_WIN32
import.pointcontents = SV_PointContents;
import.setmodel = PF_setmodel;
import.inPVS = PF_inPVS;
@@ -876,7 +876,8 @@ void SV_InitGameProgs ( void ) {
import.argc = Cmd_Argc;
import.argv = Cmd_Argv;
- import.args = Cmd_Args;
+ // original Cmd_Args() did actually return raw arguments
+ import.args = Cmd_RawArgs;
import.AddCommandString = PF_AddCommandString;
import.DebugGraph = SCR_DebugGraph;
diff --git a/source/sv_local.h b/source/sv_local.h
index 1211546..a44235e 100644
--- a/source/sv_local.h
+++ b/source/sv_local.h
@@ -334,6 +334,22 @@ typedef struct {
char string[1];
} stuffcmd_t;
+typedef enum {
+ FA_IGNORE,
+ FA_PRINT,
+ FA_STUFF,
+ FA_KICK,
+
+ FA_MAX
+} filteraction_t;
+
+typedef struct {
+ list_t entry;
+ filteraction_t action;
+ char *comment;
+ char string[1];
+} filtercmd_t;
+
typedef struct server_static_s {
qboolean initialized; // sv_init has completed
unsigned realtime, time; // always increasing, no clamping, etc
@@ -388,6 +404,8 @@ extern list_t sv_blacklist;
extern list_t sv_cmdlist_connect;
extern list_t sv_cmdlist_begin;
+extern list_t sv_filterlist;
+
extern server_static_t svs; // persistant server info
extern server_t sv; // local server
diff --git a/source/sv_main.c b/source/sv_main.c
index ffc7512..8c8113b 100644
--- a/source/sv_main.c
+++ b/source/sv_main.c
@@ -26,9 +26,9 @@ netadr_t master_adr[MAX_MASTERS]; // address of group servers
pmoveParams_t sv_pmp;
LIST_DECL( sv_banlist );
-LIST_DECL( sv_blacklist );
LIST_DECL( sv_cmdlist_connect );
LIST_DECL( sv_cmdlist_begin );
+LIST_DECL( sv_filterlist );
client_t *sv_client; // current client
diff --git a/source/sv_user.c b/source/sv_user.c
index 0c29983..b1396ed 100644
--- a/source/sv_user.c
+++ b/source/sv_user.c
@@ -801,6 +801,33 @@ static const ucmd_t ucmds[] = {
{ NULL, NULL }
};
+static void handle_filtercmd( filtercmd_t *filter ) {
+ size_t len;
+
+ switch( filter->action ) {
+ case FA_PRINT:
+ MSG_WriteByte( svc_print );
+ MSG_WriteByte( PRINT_HIGH );
+ break;
+ case FA_STUFF:
+ MSG_WriteByte( svc_stufftext );
+ break;
+ case FA_KICK:
+ SV_DropClient( sv_client, filter->comment[0] ?
+ filter->comment : "issued banned command" );
+ // fall through
+ default:
+ return;
+ }
+
+ len = strlen( filter->comment );
+ MSG_WriteData( filter->comment, len );
+ MSG_WriteByte( '\n' );
+ MSG_WriteByte( 0 );
+
+ SV_ClientAddMessage( sv_client, MSG_RELIABLE|MSG_CLEAR );
+}
+
/*
==================
SV_ExecuteUserCommand
@@ -808,6 +835,7 @@ SV_ExecuteUserCommand
*/
static void SV_ExecuteUserCommand( const char *s ) {
const ucmd_t *u;
+ filtercmd_t *filter;
char *c;
Cmd_TokenizeString( s, qfalse );
@@ -827,6 +855,12 @@ static void SV_ExecuteUserCommand( const char *s ) {
if( sv.state < ss_game ) {
return;
}
+ LIST_FOR_EACH( filtercmd_t, filter, &sv_filterlist, entry ) {
+ if( !Q_stricmp( filter->string, c ) ) {
+ handle_filtercmd( filter );
+ return;
+ }
+ }
ge->ClientCommand( sv_player );
}
diff --git a/wiki/doc/server.mdwn b/wiki/doc/server.mdwn
index cd459bd..4d40950 100644
--- a/wiki/doc/server.mdwn
+++ b/wiki/doc/server.mdwn
@@ -204,6 +204,20 @@ You can also specify `all` to clear the whole stuffcmd list.
- `liststuffcmds <connect|begin>`
Enumerates all registered commands in the specified stuffcmd list.
+- `addfiltercmd <command> [ignore|print|stuff|kick] [comment ...]`
+Prevents client command otherwise unknown to the server from being
+interpreted by the game mod, and takes the specified action instead.
+For _print_ and _stuff_ actions _comment_ argument is mandatory.
+Default action is _ignore_. Commands are matched in a case-insensitive way.
+
+- `delfiltercmd <id|cmd|all>`
+Deletes command identified by numeric ID or by name from the list of
+filtered commands. You can also specify `all` to clear the whole
+filtercmd list.
+
+- `listfiltercmds`
+Enumerates all filtered commands along with appropriate actions and comments.
+
- `mvdplay [-hl:n:] <filename> [...]`
Play the local MVD identified by _filename_.