summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/common.c7
-rw-r--r--source/gtv.h8
-rw-r--r--source/mvd_client.c165
-rw-r--r--source/mvd_game.c50
-rw-r--r--source/mvd_local.h1
-rw-r--r--source/sv_mvd.c115
-rw-r--r--source/sys_unix.c20
-rw-r--r--wiki/doc/server.mdwn76
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( &gtv->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( &gtv->entry );
// free all memory buffers
+ Z_Free( gtv->username );
+ Z_Free( gtv->password );
#if USE_ZLIB
inflateEnd( &gtv->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, &gtv->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( &gtv_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( &gtv_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.