diff options
author | Andrey Nazarov <skuller@skuller.net> | 2008-08-24 16:10:31 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2008-08-24 16:10:31 +0000 |
commit | 0d18a13a94d2d3b9bf3ac6ca160c49f7199a0c91 (patch) | |
tree | 4e053139df20582497ad39e9446afc6e5b54aee9 | |
parent | 0257c5df02687ff9a97c7ef87f464838d4df32aa (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.c | 15 | ||||
-rw-r--r-- | source/sv_ccmds.c | 155 | ||||
-rw-r--r-- | source/sv_game.c | 7 | ||||
-rw-r--r-- | source/sv_local.h | 18 | ||||
-rw-r--r-- | source/sv_main.c | 2 | ||||
-rw-r--r-- | source/sv_user.c | 34 | ||||
-rw-r--r-- | wiki/doc/server.mdwn | 14 |
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_. |