diff options
-rw-r--r-- | source/common.c | 7 | ||||
-rw-r--r-- | source/gtv.h | 8 | ||||
-rw-r--r-- | source/mvd_client.c | 165 | ||||
-rw-r--r-- | source/mvd_game.c | 50 | ||||
-rw-r--r-- | source/mvd_local.h | 1 | ||||
-rw-r--r-- | source/sv_mvd.c | 115 | ||||
-rw-r--r-- | source/sys_unix.c | 20 | ||||
-rw-r--r-- | wiki/doc/server.mdwn | 76 |
8 files changed, 309 insertions, 133 deletions
diff --git a/source/common.c b/source/common.c index e3bf26d..16b276d 100644 --- a/source/common.c +++ b/source/common.c @@ -241,7 +241,12 @@ static void logfile_write( const char *string ) { } c = *string++; - c &= 127; + if( c & 128 ) { + c &= 127; + if( c < 32 ) { + continue; + } + } if( c == '\n' ) { com_logNewline = qtrue; } diff --git a/source/gtv.h b/source/gtv.h index 545eec1..cac7567 100644 --- a/source/gtv.h +++ b/source/gtv.h @@ -18,13 +18,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#define GTV_PROTOCOL_VERSION 0xED03 +#define GTV_PROTOCOL_VERSION 0xED04 #define MAX_GTS_MSGLEN MAX_MSGLEN // maximum GTV server message length #define MAX_GTC_MSGLEN 256 // maximum GTV client message length // flags used in hello packet -#define GTF_DEFLATE 1 +#define GTF_DEFLATE 1 +#define GTF_STRINGCMDS 2 typedef enum { GTS_HELLO, @@ -43,6 +44,7 @@ typedef enum { GTC_HELLO, GTC_PING, GTC_STREAM_START, - GTC_STREAM_STOP + GTC_STREAM_STOP, + GTC_STRINGCMD } gtv_clientop_t; 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; diff --git a/source/mvd_game.c b/source/mvd_game.c index 240979a..44125ed 100644 --- a/source/mvd_game.c +++ b/source/mvd_game.c @@ -708,6 +708,24 @@ static void MVD_Admin_f( mvd_client_t *client ) { SV_ClientPrintf( client->cl, PRINT_HIGH, "[MVD] Granted admin status.\n" ); } +static void MVD_Forward_f( mvd_client_t *client ) { + mvd_t *mvd = client->mvd; + + if( !client->admin ) { + SV_ClientPrintf( client->cl, PRINT_HIGH, + "[MVD] You don't have admin status.\n" ); + return; + } + + if( !mvd->forward_cmd ) { + SV_ClientPrintf( client->cl, PRINT_HIGH, + "[MVD] This channel does not support command forwarding.\n" ); + return; + } + + mvd->forward_cmd( client ); +} + static void MVD_Say_f( mvd_client_t *client, int argnum ) { mvd_t *mvd = client->mvd; unsigned delta, delay = mvd_flood_waitdelay->value * 1000; @@ -1045,6 +1063,10 @@ static void MVD_GameClientCommand( edict_t *ent ) { MVD_Admin_f( client ); return; } + if( !strcmp( cmd, "fwd" ) ) { + MVD_Forward_f( client ); + return; + } if( !strcmp( cmd, "say" ) || !strcmp( cmd, "say_team" ) ) { MVD_Say_f( client, 1 ); return; @@ -1296,10 +1318,6 @@ static void MVD_GameClientBegin( edict_t *ent ) { if( !client->begin_time ) { MVD_BroadcastPrintf( mvd, PRINT_MEDIUM, UF_MUTE_MISC, "[MVD] %s entered the channel\n", client->cl->name ); - if( Com_IsDedicated() && mvd->state == MVD_WAITING ) { - SV_ClientPrintf( client->cl, PRINT_HIGH, - "[MVD] Buffering data, please wait...\n" ); - } target = MVD_MostFollowed( mvd ); } else { target = client->target; @@ -1439,30 +1457,6 @@ static void MVD_GameClientThink( edict_t *ent, usercmd_t *cmd ) { *old = *cmd; } -void MVD_CheckActive( mvd_t *mvd ) { - mvd_t *cur; - - // demo channels are always marked as active - if( mvd->numplayers /*|| mvd->demoplayback*/ ) { - 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( !mvd->intermission ) { - if( !LIST_EMPTY( &mvd->active ) ) { - // delete this one from the list of active channels - List_Delete( &mvd->active ); - mvd_dirty = qtrue; - } - } -} - static void MVD_IntermissionStart( mvd_t *mvd ) { mvd_client_t *client; diff --git a/source/mvd_local.h b/source/mvd_local.h index 685539c..db84548 100644 --- a/source/mvd_local.h +++ b/source/mvd_local.h @@ -106,6 +106,7 @@ typedef struct mvd_s { char name[MAX_MVD_NAME]; struct gtv_s *gtv; qboolean (*read_frame)( struct mvd_s * ); + qboolean (*forward_cmd)( mvd_client_t * ); // demo related variables fileHandle_t demorecording; diff --git a/source/sv_mvd.c b/source/sv_mvd.c index 0bf663e..6a106d0 100644 --- a/source/sv_mvd.c +++ b/source/sv_mvd.c @@ -90,7 +90,7 @@ static LIST_DECL( gtv_host_list ); static cvar_t *sv_mvd_enable; static cvar_t *sv_mvd_maxclients; static cvar_t *sv_mvd_bufsize; -static cvar_t *sv_mvd_auth; +static cvar_t *sv_mvd_password; static cvar_t *sv_mvd_noblend; static cvar_t *sv_mvd_nogun; static cvar_t *sv_mvd_nomsgs; @@ -103,6 +103,7 @@ static cvar_t *sv_mvd_autorecord; static cvar_t *sv_mvd_capture_flags; static cvar_t *sv_mvd_disconnect_time; static cvar_t *sv_mvd_suspend_time; +static cvar_t *sv_mvd_allow_stufftext; static qboolean mvd_enable( void ); static void mvd_disable( void ); @@ -144,10 +145,7 @@ static void dummy_wait_f( void ) { dummy_buffer.waitCount = count; } -static void dummy_forward_f( void ) { - Cmd_Shift(); - Com_DPrintf( "dummy cmd: %s %s\n", Cmd_Argv( 0 ), Cmd_Args() ); - +static void dummy_command( void ) { sv_client = mvd.dummy; sv_player = sv_client->edict; ge->ClientCommand( sv_player ); @@ -155,6 +153,12 @@ static void dummy_forward_f( void ) { sv_player = NULL; } +static void dummy_forward_f( void ) { + Cmd_Shift(); + Com_DPrintf( "dummy cmd: %s %s\n", Cmd_Argv( 0 ), Cmd_Args() ); + dummy_command(); +} + static void dummy_record_f( void ) { char buffer[MAX_OSPATH]; fileHandle_t demofile; @@ -214,7 +218,9 @@ static const ucmd_t dummy_cmds[] = { { "set", Cvar_Set_f }, { "alias", Cmd_Alias_f }, { "play", NULL }, + { "stopsound", NULL }, { "exec", NULL }, + { "screenshot", NULL }, { "wait", dummy_wait_f }, { "record", dummy_record_f }, { "stop", dummy_stop_f }, @@ -236,19 +242,17 @@ static void dummy_exec_string( const char *line ) { if( !cmd[0] ) { return; } - for( u = dummy_cmds; u->name; u++ ) { - if( !strcmp( cmd, u->name ) ) { - if( u->func ) { - u->func(); - } - return; + if( ( u = Com_Find( dummy_cmds, cmd ) ) != NULL ) { + if( u->func ) { + u->func(); } + return; } alias = Cmd_AliasCommand( cmd ); if( alias ) { if( ++dummy_buffer.aliasCount == ALIAS_LOOP_COUNT ) { - Com_WPrintf( "dummy_exec_string: runaway alias loop\n" ); + Com_WPrintf( "%s: runaway alias loop\n", __func__ ); return; } Cbuf_InsertTextEx( &dummy_buffer, alias ); @@ -261,26 +265,27 @@ static void dummy_exec_string( const char *line ) { return; } - Com_DPrintf( "dummy stufftext: %s\n", line ); - sv_client = mvd.dummy; - sv_player = mvd.dummy->edict; - ge->ClientCommand( sv_player ); - sv_client = NULL; - sv_player = NULL; + Com_DPrintf( "dummy forward: %s\n", line ); + dummy_command(); } static void dummy_add_message( client_t *client, byte *data, size_t length, qboolean reliable ) { - if( !length || !reliable ) { - return; + char *text; + + if( !length || !reliable || data[0] != svc_stufftext ) { + return; // not interesting } - if( data[0] == svc_stufftext ) { - data[length] = 0; - Cbuf_AddTextEx( &dummy_buffer, ( char * )( data + 1 ) ); - return; + if( sv_mvd_allow_stufftext->integer <= 0 ) { + return; // not allowed } + + data[length] = 0; + text = ( char * )( data + 1 ); + Com_DPrintf( "dummy stufftext: %s\n", text ); + Cbuf_AddTextEx( &dummy_buffer, text ); } static void dummy_spawn( void ) { @@ -1285,7 +1290,21 @@ static void write_message( gtv_client_t *client, gtv_serverop_t op ) { write_stream( client, msg_write.data, msg_write.cursize ); } +static qboolean auth_client( gtv_client_t *client, const char *password ) { + if( SV_MatchAddress( >v_host_list, &client->stream.address ) ) { + return qtrue; // dedicated GTV hosts don't need password + } + if( !sv_mvd_password->string[0] ) { + return qfalse; // no password set on the server + } + if( strcmp( sv_mvd_password->string, password ) ) { + return qfalse; // password doesn't match + } + return qtrue; +} + static void parse_hello( gtv_client_t *client ) { + char password[MAX_QPATH]; int protocol, flags; size_t size; byte *data; @@ -1311,15 +1330,20 @@ static void parse_hello( gtv_client_t *client ) { flags = MSG_ReadLong(); MSG_ReadLong(); MSG_ReadString( client->name, sizeof( client->name ) ); - MSG_ReadString( NULL, 0 ); + MSG_ReadString( password, sizeof( password ) ); MSG_ReadString( client->version, sizeof( client->version ) ); - if( !SV_MatchAddress( >v_host_list, &client->stream.address ) ) { + // authorize access + if( !auth_client( client, password ) ) { write_message( client, GTS_NOACCESS ); drop_client( client, "not authorized" ); return; } + if( sv_mvd_allow_stufftext->integer >= 0 ) { + flags &= ~GTF_STRINGCMDS; + } + #if !USE_ZLIB flags &= ~GTF_DEFLATE; #endif @@ -1427,6 +1451,29 @@ static void parse_stream_stop( gtv_client_t *client ) { #endif } +static void parse_stringcmd( gtv_client_t *client ) { + char string[MAX_GTC_MSGLEN]; + + if( client->state < cs_primed ) { + drop_client( client, "unexpected stringcmd message" ); + return; + } + + if( !mvd.dummy || !( client->flags & GTF_STRINGCMDS ) ) { + Com_DPrintf( "ignored stringcmd from %s[%s]\n", client->name, + NET_AdrToString( &client->stream.address ) ); + return; + } + + MSG_ReadString( string, sizeof( string ) ); + + Cmd_TokenizeString( string, qfalse ); + + Com_DPrintf( "dummy stringcmd from %s[%s]: %s\n", client->name, + NET_AdrToString( &client->stream.address ), string ); + dummy_command(); +} + static qboolean parse_message( gtv_client_t *client ) { uint32_t magic; uint16_t msglen; @@ -1490,6 +1537,9 @@ static qboolean parse_message( gtv_client_t *client ) { case GTC_STREAM_STOP: parse_stream_stop( client ); break; + case GTC_STRINGCMD: + parse_stringcmd( client ); + break; default: drop_client( client, "unknown command byte" ); return qfalse; @@ -1875,6 +1925,10 @@ void SV_MvdInit( void ) { Cvar_Set( "sv_mvd_enable", "1" ); } } + + dummy_buffer.text = dummy_buffer_text; + dummy_buffer.maxsize = sizeof( dummy_buffer_text ); + dummy_buffer.exec = dummy_exec_string; } /* @@ -1896,6 +1950,8 @@ void SV_MvdShutdown( killtype_t type ) { NET_Listen( qfalse ); memset( &mvd, 0, sizeof( mvd ) ); + + memset( &dummy_buffer, 0, sizeof( dummy_buffer ) ); } @@ -2121,7 +2177,7 @@ void SV_MvdRegister( void ) { sv_mvd_enable = Cvar_Get( "sv_mvd_enable", "0", CVAR_LATCH ); sv_mvd_maxclients = Cvar_Get( "sv_mvd_maxclients", "8", CVAR_LATCH ); sv_mvd_bufsize = Cvar_Get( "sv_mvd_bufsize", "2", CVAR_LATCH ); - sv_mvd_auth = Cvar_Get( "sv_mvd_auth", "", CVAR_PRIVATE ); + sv_mvd_password = Cvar_Get( "sv_mvd_password", "", CVAR_PRIVATE ); sv_mvd_maxsize = Cvar_Get( "sv_mvd_maxsize", "0", 0 ); sv_mvd_maxtime = Cvar_Get( "sv_mvd_maxtime", "0", 0 ); sv_mvd_maxmaps = Cvar_Get( "sv_mvd_maxmaps", "1", 0 ); @@ -2136,10 +2192,7 @@ void SV_MvdRegister( void ) { sv_mvd_capture_flags = Cvar_Get( "sv_mvd_capture_flags", "5", 0 ); sv_mvd_disconnect_time = Cvar_Get( "sv_mvd_disconnect_time", "15", 0 ); sv_mvd_suspend_time = Cvar_Get( "sv_mvd_suspend_time", "5", 0 ); - - dummy_buffer.text = dummy_buffer_text; - dummy_buffer.maxsize = sizeof( dummy_buffer_text ); - dummy_buffer.exec = dummy_exec_string; + sv_mvd_allow_stufftext = Cvar_Get( "sv_mvd_allow_stufftext", "0", CVAR_LATCH ); Cmd_Register( c_svmvd ); } diff --git a/source/sys_unix.c b/source/sys_unix.c index 316cee0..2128b56 100644 --- a/source/sys_unix.c +++ b/source/sys_unix.c @@ -177,7 +177,7 @@ Sys_ConsoleOutput void Sys_ConsoleOutput( const char *string ) { char buffer[MAXPRINTMSG*2]; char *m, *p; - int color = 0; + int c, color = 0; p = buffer; m = buffer + sizeof( buffer ); @@ -191,7 +191,14 @@ void Sys_ConsoleOutput( const char *string ) { if( p + 1 > m ) { break; } - *p++ = *string++ & 127; + c = *string++; + if( c & 128 ) { + c &= 127; + if( c < 32 ) { + continue; + } + } + *p++ = c; } Sys_ConsoleWrite( buffer, p - buffer ); @@ -229,7 +236,14 @@ void Sys_ConsoleOutput( const char *string ) { if( p + 1 > m ) { break; } - *p++ = *string++ & 127; + c = *string++; + if( c & 128 ) { + c &= 127; + if( c < 32 ) { + continue; + } + } + *p++ = c; } if( color ) { diff --git a/wiki/doc/server.mdwn b/wiki/doc/server.mdwn index becf0c7..8854fa2 100644 --- a/wiki/doc/server.mdwn +++ b/wiki/doc/server.mdwn @@ -19,11 +19,11 @@ Specifies UDP port number server should listen on. - `set net_tcp_ip $net_ip` (string) Specifies network interface address server TCP socket should be bound to. Empty string means listening on all interfaces. -This cvar mirrors `net_ip` unless manually overridden. +Value of this cvar mirrors `net_ip` unless modified by user. - `set net_tcp_port $net_port` (integer) Specifies TCP port number server should listen on. -This cvar mirrors `net_port` unless manually overridden. +Value of this cvar mirrors `net_port` unless modified by user. - `set net_tcp_clientip ""` (string) Specifies network interface address server should use for outgoing TCP @@ -90,7 +90,7 @@ Broadcast player name changes to everyone. You should probably enable this unless game mod already shows name changes. - `set sv_uptime 0` (boolean) -Display uptime in server info. +Display uptime string in server info. MVD server @@ -114,7 +114,7 @@ Reduce bandwidth usage by filtering on-screen blend effects out of MVD stream. - `set sv_mvd_nomsgs 1` (boolean) When enabled, MVD/GTV spectators in chasecam mode will not receive any text messages routed to their chase targets (which are normally on a team), but will -receive messages routed to the dummy MVD observer only. +receive messages routed to the dummy MVD observer. - `set sv_mvd_maxtime 0` (float) Maximum duration in minutes of the locally recorded MVD. @@ -129,31 +129,48 @@ Specifies number of map changes local MVD recording is stopped after. Setting this to zero disables the limit. - `set sv_mvd_begincmd "wait 50; putaway; wait 10; help;"` (string) -Issue this command on behaf of dummy MVD observer as soon as it -enters the game. Do any game mod dependant preparations here to make sure -MVD observer enters an appropriate spectator team, opens the scoreboard, etc. -Each `wait` cycle lasts 0.1 second here. +This command is issued on behaf of dummy MVD observer as soon as it +enters the game. Do whatever preparations are needed here to make sure +MVD observer enters an appropriate observing mode, opens the scoreboard, etc. +MVD observer has it's own command buffer and each `wait` cycle +lasts 100 ms there. - `set sv_mvd_scorecmd "putaway; wait 10; help;"` (string) -Issue this command on behaf of dummy MVD observer each time no layout +This command is issued on behaf of dummy MVD observer each time no layout updates are detected for more than 9 seconds. Useful for reopening the scoreboard if the game mod closes it for some reason. -Each `wait` cycle lasts 0.1 second here. +MVD observer has it's own command buffer and each `wait` cycle +lasts 100 ms there. +- `set sv_mvd_suspend_time 5` (float) +GTV connections are suspended after this period of time, in minutes, +counted from the moment last active player disconnects or becomes inactive. +Setting this to zero disables server side suspending entirely. MVD client ---------- -- `set mvd_timeout 120` (float) +- `set mvd_username "unnamed"` (string) +Default username to use for outgoing GTV connections. + +- `set mvd_password ""` (string) +Default password to use for outgoing GTV connections. + +- `set mvd_timeout 90` (float) Specifies MVD connection timeout value, in seconds. +- `set mvd_suspend_time 5` (float) +GTV connections are suspended after this period of time, in minutes, +counted from the moment last MVD spectator disconnects. +Setting this to zero disables client side suspending entirely. + - `set mvd_wait_delay 20` (float) -Time, in seconds, for MVD client to buffer data right after initial -connection. This effectively specifies MVD stream delay seen by observers. +Time, in seconds, for MVD channel to buffer data initially. +This effectively specifies MVD stream delay seen by observers. - `set mvd_wait_percent 50` (float) -Maximum inuse percentage of the input buffer before MVD stops buffering -phase to prevent overrun, regardless of `mvd_wait_delay` value. +Maximum inuse percentage of the delay buffer when MVD channel stops +buffering data to prevent overrun, ignoring `mvd_wait_delay` value. - `set mvd_default_map "q2dm1"` (string) Specifies default map used for the Waiting Room channel. @@ -239,24 +256,29 @@ Enumerates all filtered commands along with appropriate actions and comments. - `mvdplay [-hl:n:] <filename> [...]` Play the local MVD identified by _filename_. -- `mvdconnect [-hi:n:] [http://][user:pass@]<host>[:port][/resource]` -Connect to the MVD resource identified by _URI_. If _resource_ part is given, -default port is 80. Otherwise, default port is 27910 and _streamID_ identifies -the live MVD stream on server. +- `mvdconnect [-hi:n:] <address[:port]>` +Connect to the specified GTV server. Separate MVD channel will be +created for this connection as soon as there is any MVD stream data available. -- `mvdisconnect [chanid]` -Disconnect the specified channel from the server. There is no need to -specify `chanid` if there is only one active channel. +- `mvdisconnect [connection_id]` +Disconnect the specified channel from the server. +There is no need to specify `chanid` if there is only one active channel. -- `mvdkill [chanid]` -Kill the specified channel. There is no need to specify `chanid` if -there is only one active channel. +- `mvdkill [channel_id]` +Kill the specified MVD channel (and any parent GTV connection). +There is no need to specify `chanid` if there is only one active channel. - `mvdspawn` -(Re)spawn MVD client instance (that is, Waiting Room channel). +Put the server into MVD/GTV mode, creating the Waiting Room channel. +This is equivalent to a full server restart. Done automatically +when the very first GTV connection is established or local MVD file +is replayed. - `mvdchannels` -Display status of all MVD channels. +List all MVD channels. + +- `mvdservers` +List all GTV connections. - `mvdstuff <text>` Stuff the specified text into command buffer of the dummy MVD observer. |