diff options
author | Andrey Nazarov <skuller@skuller.net> | 2008-10-15 18:07:06 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2008-10-15 18:07:06 +0000 |
commit | 58c8e2e0377eddcae7f44d30b78578fbedf0eaa7 (patch) | |
tree | 3643ef5da6e9e397cf4c27ff93b4722843b5c83c /source/mvd_client.c | |
parent | 2ff447b2019b5696375fbc09d87416345418cd9a (diff) |
Added `sv_mvd_allow_stufftext' and `sv_mvd_password' cvars.
MVD admins now can execute arbitrary commands on behaf of the
dummy MVD observer on game server side via `fwd' command, if allowed.
MVD channel is now listed in the menu when it is not suspended, not
just when there are active players.
Made `mvdconnect' command accept --user and --pass options.
When masking off high-bit characters before displaying them on system
console or writing into logfile, ignore any control characters.
Updated documentation.
Diffstat (limited to 'source/mvd_client.c')
-rw-r--r-- | source/mvd_client.c | 165 |
1 files changed, 125 insertions, 40 deletions
diff --git a/source/mvd_client.c b/source/mvd_client.c index 9cadf33..401bbb9 100644 --- a/source/mvd_client.c +++ b/source/mvd_client.c @@ -36,7 +36,8 @@ typedef enum { GTV_PREPARING, // waiting for server hello GTV_CONNECTED, // keeping connection alive GTV_RESUMING, // stream start request sent - GTV_ACTIVE, // actively running MVD stream + GTV_WAITING, // server is suspended + GTV_READING, // server is resumed GTV_SUSPENDING, // stream stop request sent GTV_NUM_STATES } gtv_state_t; @@ -48,12 +49,14 @@ typedef struct gtv_s { char name[MAX_MVD_NAME]; gtv_state_t state; mvd_t *mvd; + char *username, *password; // connection variables netstream_t stream; char address[MAX_QPATH]; byte *data; size_t msglen; + unsigned flags; #if USE_ZLIB qboolean z_act; // true when actively inflating z_stream z_str; @@ -84,9 +87,7 @@ static const char *const gtv_states[GTV_NUM_STATES] = { }; static const char *const mvd_states[MVD_NUM_STATES] = { - "DEAD", - "WAIT", - "READ" + "DEAD", "WAIT", "READ" }; static LIST_DECL( mvd_gtv_list ); @@ -214,6 +215,31 @@ mvd_t *MVD_SetChannel( int arg ) { return NULL; } +void MVD_CheckActive( mvd_t *mvd ) { + mvd_t *cur; + + if( mvd->state == MVD_READING || ( mvd->gtv && + mvd->gtv->state == GTV_READING ) ) + { + if( LIST_EMPTY( &mvd->active ) ) { + // sort this one into the list of active channels + LIST_FOR_EACH( mvd_t, cur, &mvd_active_list, active ) { + if( cur->id > mvd->id ) { + break; + } + } + List_Append( &cur->active, &mvd->active ); + mvd_dirty = qtrue; + } + } else { + if( !LIST_EMPTY( &mvd->active ) ) { + // delete this one from the list of active channels + List_Delete( &mvd->active ); + mvd_dirty = qtrue; + } + } +} + /* ==================================================================== @@ -534,27 +560,40 @@ static qboolean gtv_wait_stop( mvd_t *mvd ) { // ran out of buffers static void gtv_wait_start( mvd_t *mvd ) { + gtv_t *gtv = mvd->gtv; int tr = mvd_wait_delay->value * 10; // if not connected, kill it - if( !mvd->gtv ) { + if( !gtv ) { MVD_Destroyf( mvd, "End of MVD stream reached" ); } Com_Printf( "[%s] -=- Buffering data...\n", mvd->name ); - // notify spectators - if( Com_IsDedicated() ) { - MVD_BroadcastPrintf( mvd, PRINT_HIGH, 0, - "[MVD] Buffering data, please wait...\n" ); - } - mvd->state = MVD_WAITING; - mvd->min_packets = 50 + 10 * mvd->underflows; - if( mvd->min_packets > tr ) { + + if( gtv->state == GTV_READING ) { + // oops, if this happened in the middle of the game, + // resume as quickly as possible after there is some + // data available again + mvd->min_packets = 50 + 5 * mvd->underflows; + if( mvd->min_packets > tr ) { + mvd->min_packets = tr; + } + mvd->underflows++; + + // notify spectators + if( Com_IsDedicated() ) { + MVD_BroadcastPrintf( mvd, PRINT_HIGH, 0, + "[MVD] Buffering data, please wait...\n" ); + } + } else { + // this is a `normal' underflow, reset delay to default mvd->min_packets = tr; + mvd->underflows = 0; } - mvd->underflows++; + + MVD_CheckActive( mvd ); } static qboolean gtv_read_frame( mvd_t *mvd ) { @@ -623,8 +662,44 @@ static void write_message( gtv_t *gtv, gtv_clientop_t op ) { write_stream( gtv, msg_write.data, msg_write.cursize ); } +static qboolean gtv_forward_cmd( mvd_client_t *client ) { + mvd_t *mvd = client->mvd; + gtv_t *gtv = mvd->gtv; + char *text; + size_t len; + + if( !gtv || gtv->state < GTV_CONNECTED ) { + SV_ClientPrintf( client->cl, PRINT_HIGH, + "[MVD] Not connected to the game server.\n" ); + return qfalse; + } + if( !( gtv->flags & GTF_STRINGCMDS ) ) { + SV_ClientPrintf( client->cl, PRINT_HIGH, + "[MVD] Game server does not allow command forwarding.\n" ); + return qfalse; + } + if( FIFO_Usage( >v->stream.send ) ) { + SV_ClientPrintf( client->cl, PRINT_HIGH, + "[MVD] Send buffer not empty, please wait.\n" ); + return qfalse; + } + + text = Cmd_Args(); + len = strlen( text ); + if( len > 150 ) { + len = 150; + } + + // send it + MSG_WriteData( text, len ); + MSG_WriteByte( 0 ); + write_message( gtv, GTC_STRINGCMD ); + SZ_Clear( &msg_write ); + return qtrue; +} + static void send_hello( gtv_t *gtv ) { - int flags = 0; + int flags = GTF_STRINGCMDS; #if USE_ZLIB flags |= GTF_DEFLATE; @@ -633,8 +708,8 @@ static void send_hello( gtv_t *gtv ) { MSG_WriteShort( GTV_PROTOCOL_VERSION ); MSG_WriteLong( flags ); MSG_WriteLong( 0 ); // reserved - MSG_WriteString( mvd_username->string ); - MSG_WriteString( mvd_password->string ); + MSG_WriteString( gtv->username ? gtv->username : mvd_username->string ); + MSG_WriteString( gtv->password ? gtv->password : mvd_password->string ); MSG_WriteString( com_version->string ); write_message( gtv, GTC_HELLO ); SZ_Clear( &msg_write ); @@ -695,8 +770,8 @@ static void parse_hello( gtv_t *gtv ) { flags = MSG_ReadLong(); -#if USE_ZLIB if( flags & GTF_DEFLATE ) { +#if USE_ZLIB if( !gtv->z_str.state ) { gtv->z_str.zalloc = gtv_zalloc; gtv->z_str.zfree = gtv_zfree; @@ -710,8 +785,10 @@ static void parse_hello( gtv_t *gtv ) { gtv->z_buf.size = MAX_GTS_MSGLEN; } gtv->z_act = qtrue; // remaining data is deflated - } +#else + gtv_destroyf( gtv, "Server sending deflated data" ); #endif + } Com_Printf( "[%s] -=- Server hello done.\n", gtv->name ); @@ -719,6 +796,7 @@ static void parse_hello( gtv_t *gtv ) { MVD_Spawn_f(); // the game is just starting } + gtv->flags = flags; gtv->state = GTV_CONNECTED; } @@ -741,6 +819,7 @@ static void parse_stream_start( gtv_t *gtv ) { mvd->delay.data = MVD_Malloc( size ); mvd->delay.size = size; mvd->read_frame = gtv_read_frame; + mvd->forward_cmd = gtv_forward_cmd; gtv->mvd = mvd; } else { @@ -751,7 +830,7 @@ static void parse_stream_start( gtv_t *gtv ) { Com_Printf( "[%s] -=- Stream start ack received.\n", gtv->name ); - gtv->state = GTV_ACTIVE; + gtv->state = GTV_READING; } static void parse_stream_stop( gtv_t *gtv ) { @@ -767,35 +846,31 @@ static void parse_stream_stop( gtv_t *gtv ) { static void parse_stream_data( gtv_t *gtv ) { mvd_t *mvd = gtv->mvd; - if( gtv->state < GTV_ACTIVE ) { + if( gtv->state < GTV_WAITING ) { gtv_destroyf( gtv, "Unexpected stream data packet" ); } // ignore any pending data while suspending - if( gtv->state > GTV_ACTIVE ) { + if( gtv->state == GTV_SUSPENDING ) { msg_read.readcount = msg_read.cursize; return; } // empty data part acts as stream suspend marker if( msg_read.readcount == msg_read.cursize ) { -#if 0 - Com_Printf( "[%s] -=- Stream suspended by server.\n", gtv->name ); - if( gtv->mvd && Com_IsDedicated() ) { - // notify spectators if server suspended us - MVD_BroadcastPrintf( gtv->mvd, PRINT_HIGH, 0, - "[MVD] Stream has been suspended.\n" ); + if( gtv->state == GTV_READING ) { + Com_Printf( "[%s] -=- Stream suspended by server.\n", gtv->name ); + gtv->state = GTV_WAITING; } - - if( Com_IsDedicated() ) { - // notify spectators if server resumed us - MVD_BroadcastPrintf( mvd, PRINT_HIGH, 0, - "[MVD] Stream has been resumed.\n" ); - } -#endif return; } + // non-empty data part act as stream resume marker + if( gtv->state == GTV_WAITING ) { + Com_Printf( "[%s] -=- Stream resumed by server.\n", gtv->name ); + gtv->state = GTV_READING; + } + if( !mvd->state ) { // parse it in place MVD_ParseMessage( mvd ); @@ -1057,7 +1132,7 @@ static void check_timeouts( gtv_t *gtv ) { if( gtv->state == GTV_CONNECTED ) { send_stream_start( gtv ); } - } else if( gtv->state == GTV_ACTIVE ) { + } else if( gtv->state == GTV_WAITING || gtv->state == GTV_READING ) { send_stream_stop( gtv ); } @@ -1159,6 +1234,8 @@ static void gtv_destroy( gtv_t *gtv ) { List_Remove( >v->entry ); // free all memory buffers + Z_Free( gtv->username ); + Z_Free( gtv->password ); #if USE_ZLIB inflateEnd( >v->z_str ); Z_Free( gtv->z_buf.data ); @@ -1455,8 +1532,8 @@ void MVD_StreamedRecord_f( void ) { static const cmd_option_t o_mvdconnect[] = { { "h", "help", "display this message" }, { "n:string", "name", "specify channel name as <string>" }, - //{ "u:string", "user", "specify username as <string>" }, - //{ "p:string", "pass", "specify password as <string>" }, + { "u:string", "user", "specify username as <string>" }, + { "p:string", "pass", "specify password as <string>" }, { NULL } }; @@ -1472,7 +1549,7 @@ MVD_Connect_f static void MVD_Connect_f( void ) { netadr_t adr; netstream_t stream; - char *name = NULL; + char *name = NULL, *username = NULL, *password = NULL; gtv_t *gtv; int c; @@ -1486,6 +1563,12 @@ static void MVD_Connect_f( void ) { case 'n': name = cmd_optarg; break; + case 'u': + username = cmd_optarg; + break; + case 'p': + password = cmd_optarg; + break; default: return; } @@ -1528,6 +1611,8 @@ static void MVD_Connect_f( void ) { gtv->run = gtv_run; gtv->drop = gtv_drop; gtv->destroy = gtv_destroy; + gtv->username = MVD_CopyString( username ); + gtv->password = MVD_CopyString( password ); List_Append( &mvd_gtv_list, >v->entry ); // set channel name @@ -1742,7 +1827,7 @@ static void MVD_Play_f( void ) { // create new connection gtv = MVD_Mallocz( sizeof( *gtv ) ); gtv->id = mvd_chanid++; - gtv->state = GTV_CONNECTED; + gtv->state = GTV_READING; gtv->demohead = head; gtv->demoloop = loop; gtv->drop = demo_destroy; |