summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2009-04-22 23:40:03 +0000
committerAndrey Nazarov <skuller@skuller.net>2009-04-22 23:40:03 +0000
commit275f310df767a57b8cf409acdcbd14c63cb90df9 (patch)
tree3b59e2751c32b2f15fcfabb1af8b03e702cd5230
parent385ccd0e9a518933019a8c6f1fecad2ae660b766 (diff)
Don't execute ‘cl_beginmapcmd’, ‘cl_changemapcmd’ and ‘cl_disconnectcmd’ triggers when playing back demos.
Fixed automatic restart of sound subsystem after cvar change not working randomly. Don't crash in CL_Disconnect() on dedicated server exit. Added ‘sv_redirect_address’ variable. Fixed ‘\r’ char not stripped properly from filename when processing ‘\include’ directive in AC config files. Made AC config parser compliant with reference format as defined by R1Q2 implementation. Updated documentation.
-rw-r--r--source/cl_demo.c6
-rw-r--r--source/cl_local.h2
-rw-r--r--source/cl_main.c19
-rw-r--r--source/cl_parse.c4
-rw-r--r--source/cmodel.h4
-rw-r--r--source/common.c3
-rw-r--r--source/mvd_client.c63
-rw-r--r--source/mvd_local.h1
-rw-r--r--source/mvd_parse.c2
-rw-r--r--source/q_shared.c71
-rw-r--r--source/q_shared.h2
-rw-r--r--source/snd_main.c1
-rw-r--r--source/sv_ac.c55
-rw-r--r--source/sv_local.h4
-rw-r--r--source/sv_main.c22
-rw-r--r--source/sv_mvd.c6
-rw-r--r--source/sv_world.c4
-rw-r--r--source/ui_demos.c8
-rw-r--r--wiki/doc.mdwn4
-rw-r--r--wiki/doc/client.mdwn2
-rw-r--r--wiki/doc/server.mdwn116
21 files changed, 245 insertions, 154 deletions
diff --git a/source/cl_demo.c b/source/cl_demo.c
index eec20e9..dff6041 100644
--- a/source/cl_demo.c
+++ b/source/cl_demo.c
@@ -241,7 +241,11 @@ void CL_Stop_f( void ) {
cls.demo.recording = 0;
cls.demo.paused = qfalse;
- Com_Printf( "Stopped demo (%u bytes written).\n", msglen );
+ if( msglen == INVALID_LENGTH ) {
+ Com_Printf( "Stopped demo.\n" );
+ } else {
+ Com_Printf( "Stopped demo (%u bytes written).\n", msglen );
+ }
}
/*
diff --git a/source/cl_local.h b/source/cl_local.h
index 2ef0e80..1c81af2 100644
--- a/source/cl_local.h
+++ b/source/cl_local.h
@@ -81,7 +81,7 @@ typedef struct {
int number;
int delta;
- byte areabits[MAX_MAP_AREAS / 8];
+ byte areabits[MAX_MAP_AREA_BYTES];
int areabytes;
player_state_t ps;
diff --git a/source/cl_main.c b/source/cl_main.c
index ed28ba0..cc04be8 100644
--- a/source/cl_main.c
+++ b/source/cl_main.c
@@ -610,7 +610,7 @@ This is also called on Com_Error, so it shouldn't cause any errors
=====================
*/
void CL_Disconnect( comErrorType_t type, const char *text ) {
- if( cls.state > ca_disconnected ) {
+ if( cls.state > ca_disconnected && !cls.demo.playback ) {
EXEC_TRIGGER( cl_disconnectcmd );
}
@@ -975,7 +975,9 @@ static void CL_Changing_f( void ) {
Com_Printf( "Changing map...\n" );
- EXEC_TRIGGER( cl_changemapcmd );
+ if( !cls.demo.playback ) {
+ EXEC_TRIGGER( cl_changemapcmd );
+ }
SCR_BeginLoadingPlaque();
@@ -2340,7 +2342,6 @@ CL_LocalConnect
*/
void CL_LocalConnect( void ) {
if ( FS_NeedRestart() ) {
- cls.state = ca_challenging;
CL_RestartFilesystem();
}
}
@@ -2908,11 +2909,11 @@ CL_Init
====================
*/
void CL_Init( void ) {
- if ( dedicated->integer ) {
+ if( dedicated->integer ) {
return; // nothing running on the client
}
- if ( cl_running->integer ) {
+ if( cl_running->integer ) {
return;
}
@@ -2982,13 +2983,17 @@ to run quit through here before the final handoff to the sys code.
*/
void CL_Shutdown( void ) {
static qboolean isdown = qfalse;
-
- if ( isdown ) {
+
+ if( isdown ) {
Com_Printf( "CL_Shutdown: recursive shutdown\n" );
return;
}
isdown = qtrue;
+ if( !cl_running || !cl_running->integer ) {
+ return;
+ }
+
CL_Disconnect( ERR_SILENT, NULL );
#if USE_ZLIB
diff --git a/source/cl_parse.c b/source/cl_parse.c
index a98f468..23fa50f 100644
--- a/source/cl_parse.c
+++ b/source/cl_parse.c
@@ -438,7 +438,9 @@ static void CL_SetActiveState( void ) {
SCR_EndLoadingPlaque (); // get rid of loading plaque
Con_Close(); // close console
- EXEC_TRIGGER( cl_beginmapcmd );
+ if( !cls.demo.playback ) {
+ EXEC_TRIGGER( cl_beginmapcmd );
+ }
Cvar_Set( "cl_paused", "0" );
}
diff --git a/source/cmodel.h b/source/cmodel.h
index c1523a7..22a7f6f 100644
--- a/source/cmodel.h
+++ b/source/cmodel.h
@@ -22,6 +22,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// cmodel.h
//
+// bitmasks communicated by server
+#define MAX_MAP_AREA_BYTES ( MAX_MAP_AREAS / 8 )
+#define MAX_MAP_PORTAL_BYTES MAX_MAP_AREA_BYTES
+
typedef struct {
bsp_t *cache;
int *floodnums; // if two areas have equal floodnums,
diff --git a/source/common.c b/source/common.c
index dbb9b0b..7e47f4c 100644
--- a/source/common.c
+++ b/source/common.c
@@ -1687,9 +1687,8 @@ void Qcommon_Frame( void ) {
if( cvar_modified & CVAR_FILES ) {
Cbuf_ExecuteText( EXEC_NOW, "fs_restart" );
+ cvar_modified &= ~CVAR_FILES;
}
-
- cvar_modified = 0;
com_localTime += msec;
com_framenum++;
diff --git a/source/mvd_client.c b/source/mvd_client.c
index 7d9e314..64d491a 100644
--- a/source/mvd_client.c
+++ b/source/mvd_client.c
@@ -47,13 +47,17 @@ typedef enum {
typedef struct gtv_s {
list_t entry;
+ // common stuff
int id;
char name[MAX_MVD_NAME];
gtv_state_t state;
mvd_t *mvd;
- char *username, *password;
+ void (*drop)( struct gtv_s * );
+ void (*destroy)( struct gtv_s * );
+ void (*run)( struct gtv_s * );
// connection variables
+ char *username, *password;
netstream_t stream;
char address[MAX_QPATH];
byte *data;
@@ -66,9 +70,6 @@ typedef struct gtv_s {
#endif
unsigned last_rcvd;
unsigned last_sent;
- void (*drop)( struct gtv_s * );
- void (*destroy)( struct gtv_s * );
- void (*run)( struct gtv_s * );
unsigned retry_time;
unsigned retry_backoff;
@@ -130,6 +131,8 @@ static void MVD_Free( mvd_t *mvd ) {
FS_Write( &msglen, 2, mvd->demorecording );
FS_FCloseFile( mvd->demorecording );
mvd->demorecording = 0;
+ Z_Free( mvd->demoname );
+ mvd->demoname = NULL;
}
for( i = 0; i < mvd->maxclients; i++ ) {
@@ -1420,14 +1423,9 @@ void MVD_Spawn_f( void ) {
sv.state = ss_broadcast;
}
-static void MVD_ListChannels_f( void ) {
+static void list_generic( void ) {
mvd_t *mvd;
- if( LIST_EMPTY( &mvd_channel_list ) ) {
- Com_Printf( "No MVD channels.\n" );
- return;
- }
-
Com_Printf(
"id name map spc plr stat buf pckt address \n"
"-- ------------ -------- --- --- ---- --- ---- --------------\n" );
@@ -1442,6 +1440,48 @@ static void MVD_ListChannels_f( void ) {
}
}
+static void list_recordings( void ) {
+ mvd_t *mvd;
+ char buffer[8];
+ size_t bytes;
+
+ Com_Printf(
+ "id name map size name\n"
+ "-- ------------ -------- ---- --------------\n" );
+
+ LIST_FOR_EACH( mvd_t, mvd, &mvd_channel_list, entry ) {
+ if( mvd->demorecording ) {
+ bytes = FS_Tell( mvd->demorecording );
+ if( bytes == INVALID_LENGTH ) {
+ strcpy( buffer, "???" );
+ } else {
+ Q_FormatFileSize( buffer, bytes, sizeof( buffer ) );
+ }
+ } else {
+ strcpy( buffer, "-" );
+ }
+ Com_Printf( "%2d %-12.12s %-8.8s %-4s %s\n",
+ mvd->id, mvd->name, mvd->mapname,
+ buffer, mvd->demoname ? mvd->demoname : "-" );
+ }
+}
+
+static void MVD_ListChannels_f( void ) {
+ char *s;
+
+ if( LIST_EMPTY( &mvd_channel_list ) ) {
+ Com_Printf( "No MVD channels.\n" );
+ return;
+ }
+
+ s = Cmd_Argv( 1 );
+ if( *s == 'r' ) {
+ list_recordings();
+ } else {
+ list_generic();
+ }
+}
+
static void MVD_ListServers_f( void ) {
gtv_t *gtv;
unsigned ratio;
@@ -1501,7 +1541,7 @@ static void MVD_EmitGamestate( mvd_t *mvd ) {
mvd_player_t *player;
size_t length;
int flags, extra, portalbytes;
- byte portalbits[MAX_MAP_AREAS/8];
+ byte portalbits[MAX_MAP_PORTAL_BYTES];
// pack MVD stream flags into extra bits
extra = mvd->flags << SVCMD_BITS;
@@ -1631,6 +1671,7 @@ void MVD_StreamedRecord_f( void ) {
}
mvd->demorecording = f;
+ mvd->demoname = MVD_CopyString( buffer );
MVD_EmitGamestate( mvd );
diff --git a/source/mvd_local.h b/source/mvd_local.h
index 29af571..264572c 100644
--- a/source/mvd_local.h
+++ b/source/mvd_local.h
@@ -117,6 +117,7 @@ typedef struct mvd_s {
// demo related variables
fileHandle_t demorecording;
+ char *demoname;
// delay buffer
fifo_t delay;
diff --git a/source/mvd_parse.c b/source/mvd_parse.c
index dc8b52a..1ac3339 100644
--- a/source/mvd_parse.c
+++ b/source/mvd_parse.c
@@ -871,7 +871,7 @@ static void MVD_ParseFrame( mvd_t *mvd ) {
if( length < 0 || msg_read.readcount + length > msg_read.cursize ) {
MVD_Destroyf( mvd, "%s: read past end of message", __func__ );
}
- if( length > MAX_MAP_AREAS/8 ) {
+ if( length > MAX_MAP_PORTAL_BYTES ) {
MVD_Destroyf( mvd, "%s: bad portalbits length: %d", __func__, length );
}
CM_SetPortalStates( &mvd->cm, msg_read.data +
diff --git a/source/q_shared.c b/source/q_shared.c
index 32a1707..67772ef 100644
--- a/source/q_shared.c
+++ b/source/q_shared.c
@@ -1038,6 +1038,16 @@ size_t Q_UnescapeString( char *out, const char *in, size_t bufsize, escape_t fla
}
#endif
+size_t Q_FormatFileSize( char *dest, size_t bytes, size_t size ) {
+ if( bytes >= 1000000 ) {
+ return Q_snprintf( dest, size, "%2.1fM", ( float )bytes / 1000000 );
+ }
+ if( bytes >= 1000 ) {
+ return Q_snprintf( dest, size, "%3"PRIz"K", bytes / 1000 );
+ }
+ return Q_snprintf( dest, size, "%3"PRIz, bytes );
+}
+
/*
============
va
@@ -1061,68 +1071,9 @@ char *va( const char *format, ... ) {
return buffers[index];
}
-
static char com_token[4][MAX_TOKEN_CHARS];
static int com_tokidx;
-static char *COM_GetToken( void ) {
- return com_token[com_tokidx++ & 3];
-}
-
-/*
-==============
-COM_SimpleParse
-
-Parse a token out of a string.
-==============
-*/
-char *COM_SimpleParse( const char **data_p, int *length ) {
- int c;
- int len;
- const char *data;
- char *s = COM_GetToken();
-
- data = *data_p;
- len = 0;
- s[0] = 0;
- if( length ) {
- *length = 0;
- }
-
- if( !data ) {
- *data_p = NULL;
- return s;
- }
-
-// skip whitespace
- while( ( c = *data ) <= ' ' ) {
- if( c == 0 ) {
- *data_p = NULL;
- return s;
- }
- data++;
- }
-
-// parse a regular word
- do {
- if( len < MAX_TOKEN_CHARS - 1 ) {
- s[len++] = c;
- }
- data++;
- c = *data;
- } while( c > 32 );
-
- s[len] = 0;
-
- if( length ) {
- *length = len;
- }
-
- *data_p = data;
- return s;
-}
-
-
/*
==============
COM_Parse
@@ -1135,7 +1086,7 @@ char *COM_Parse( const char **data_p ) {
int c;
int len;
const char *data;
- char *s = COM_GetToken();
+ char *s = com_token[com_tokidx++ & 3];
data = *data_p;
len = 0;
diff --git a/source/q_shared.h b/source/q_shared.h
index 8a41a21..a69c5a3 100644
--- a/source/q_shared.h
+++ b/source/q_shared.h
@@ -406,6 +406,7 @@ int Q_DrawStrlenTo( const char *string, int maxChars );
qboolean Q_IsWhiteSpace( const char *string );
char *Q_FormatString( const char *string );
char *Q_UnescapeString( const char *string );
+size_t Q_FormatFileSize( char *dest, size_t bytes, size_t size );
int Q_ClearColorStr( char *out, const char *in, int bufsize );
int Q_ClearStr( char *out, const char *in, int bufsize );
int Q_HighlightStr( char *out, const char *in, int bufsize );
@@ -504,7 +505,6 @@ qboolean COM_IsFloat( const char *s );
qboolean COM_IsUint( const char *s );
qboolean COM_HasSpaces( const char *s );
-char *COM_SimpleParse( const char **data_p, int *length );
char *COM_Parse( const char **data_p );
// data is an in/out parm, returns a parsed out token
int COM_Compress( char *data );
diff --git a/source/snd_main.c b/source/snd_main.c
index e155fec..e5de4e8 100644
--- a/source/snd_main.c
+++ b/source/snd_main.c
@@ -999,6 +999,7 @@ void S_Update( void ) {
if( cvar_modified & CVAR_SOUND ) {
Cbuf_AddText( "snd_restart\n" );
+ cvar_modified &= ~CVAR_SOUND;
return;
}
diff --git a/source/sv_ac.c b/source/sv_ac.c
index 8f60243..cfdae8b 100644
--- a/source/sv_ac.c
+++ b/source/sv_ac.c
@@ -176,7 +176,7 @@ FILE PARSING
#define AC_MAX_INCLUDES 16
-typedef void (*ac_parse_t)( const char *, int, const char * );
+typedef void (*ac_parse_t)( char *, int, const char * );
typedef struct {
char str[4];
@@ -198,10 +198,35 @@ static const ac_cvarop_t ac_cvarops[] = {
{ "" }
};
+static char *AC_SimpleParse( char **data_p, size_t *len_p ) {
+ char *data, *p;
-static void AC_ParseHash( const char *data, int linenum, const char *path ) {
+ data = *data_p;
+ if( !data ) {
+ if( len_p ) {
+ *len_p = 0;
+ }
+ return NULL;
+ }
+
+ p = Q_strchrnul( data, '\t' );
+ if( *p ) {
+ *p = 0;
+ *data_p = p + 1;
+ } else {
+ *data_p = NULL;
+ }
+
+ if( len_p ) {
+ *len_p = p - data;
+ }
+
+ return data;
+}
+
+static void AC_ParseHash( char *data, int linenum, const char *path ) {
char *pstr, *hstr;
- int pathlen, hashlen;
+ size_t pathlen, hashlen;
int flags;
byte hash[20];
ac_file_t *file;
@@ -212,11 +237,11 @@ static void AC_ParseHash( const char *data, int linenum, const char *path ) {
return;
}
- pstr = COM_SimpleParse( &data, &pathlen );
+ pstr = AC_SimpleParse( &data, &pathlen );
if( !pstr[0] ) {
return;
}
- hstr = COM_SimpleParse( &data, &hashlen );
+ hstr = AC_SimpleParse( &data, &hashlen );
if( !hstr[0] ) {
Com_WPrintf( "ANTICHEAT: Incomplete line %d in %s\n", linenum, path );
return;
@@ -266,23 +291,22 @@ badhash:
acs.num_files++;
}
-static void AC_ParseCvar( const char *data, int linenum, const char *path ) {
+static void AC_ParseCvar( char *data, int linenum, const char *path ) {
char *values[256], *p;
byte lengths[256];
char *name, *opstr, *val, *def;
- int num_values, namelen, vallen, deflen;
+ size_t len, namelen, vallen, deflen;
ac_cvar_t *cvar;
const ac_cvarop_t *op;
- int i;
- size_t len;
+ int i, num_values;
- name = COM_SimpleParse( &data, &namelen );
+ name = AC_SimpleParse( &data, &namelen );
if( !name[0] ) {
return;
}
- opstr = COM_SimpleParse( &data, NULL );
- val = COM_SimpleParse( &data, &vallen );
- def = COM_SimpleParse( &data, &deflen );
+ opstr = AC_SimpleParse( &data, NULL );
+ val = AC_SimpleParse( &data, &vallen );
+ def = AC_SimpleParse( &data, &deflen );
if( !opstr[0] || !val[0] || !def[0] ) {
Com_WPrintf( "ANTICHEAT: Incomplete line %d in %s\n", linenum, path );
return;
@@ -353,7 +377,7 @@ static void AC_ParseCvar( const char *data, int linenum, const char *path ) {
acs.num_cvars++;
}
-static void AC_ParseToken( const char *data, int linenum, const char *path ) {
+static void AC_ParseToken( char *data, int linenum, const char *path ) {
string_entry_t *tok;
size_t len = strlen( data );
@@ -376,6 +400,9 @@ static qboolean AC_ParseFile( const char *path, ac_parse_t parse, int depth ) {
while( *data ) {
p = strchr( data, '\n' );
if( p ) {
+ if( p > data && *( p - 1 ) == '\r' ) {
+ *( p - 1 ) = 0;
+ }
*p = 0;
}
diff --git a/source/sv_local.h b/source/sv_local.h
index f4cfaf9..9d14238 100644
--- a/source/sv_local.h
+++ b/source/sv_local.h
@@ -68,7 +68,7 @@ typedef struct {
unsigned firstEntity;
player_state_t ps;
int areabytes;
- byte areabits[MAX_MAP_AREAS/8]; // portalarea visibility bits
+ byte areabits[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
unsigned sentTime; // for ping calculations
int clientNum;
} client_frame_t;
@@ -104,7 +104,7 @@ typedef struct {
#define EDICT_POOL(c,n) ((edict_t *)((byte *)(c)->pool->edicts + (c)->pool->edict_size*(n)))
#define EDICT_NUM(n) ((edict_t *)((byte *)ge->edicts + ge->edict_size*(n)))
-#define NUM_FOR_EDICT(e) ( ((byte *)(e)-(byte *)ge->edicts ) / ge->edict_size)
+#define NUM_FOR_EDICT(e) ((int)(((byte *)(e)-(byte *)ge->edicts ) / ge->edict_size))
#define MAX_TOTAL_ENT_LEAFS 128
diff --git a/source/sv_main.c b/source/sv_main.c
index ce0036e..22890b6 100644
--- a/source/sv_main.c
+++ b/source/sv_main.c
@@ -55,6 +55,7 @@ cvar_t *sv_reserved_slots;
cvar_t *sv_showclamp;
cvar_t *sv_locked;
cvar_t *sv_downloadserver;
+cvar_t *sv_redirect_address;
cvar_t *sv_hostname;
cvar_t *sv_public; // should heartbeats be sent
@@ -475,6 +476,21 @@ static void SVC_GetChallenge( void ) {
"challenge %u p=34,35,36", challenge );
}
+static void send_redirect_hack( const char *addr ) {
+ Netchan_OutOfBand( NS_SERVER, &net_from, "client_connect" );
+
+ MSG_WriteLong( 1 );
+ MSG_WriteLong( 0 );
+ MSG_WriteByte( svc_print );
+ MSG_WriteByte( PRINT_HIGH );
+ MSG_WriteString( va( "Server is full.\nRedirecting you to %s...\n", addr ) );
+ MSG_WriteByte( svc_stufftext );
+ MSG_WriteString( va( "connect %s\n", addr ) );
+
+ NET_SendPacket( NS_SERVER, &net_from, msg_write.cursize, msg_write.data );
+ SZ_Clear( &msg_write );
+}
+
#define SV_OobPrintf(...) \
Netchan_OutOfBand( NS_SERVER, &net_from, "print\n" __VA_ARGS__ )
@@ -778,6 +794,11 @@ static void SVC_DirectConnect( void ) {
} else {
SV_OobPrintf( "Server is full.\n" );
Com_DPrintf( " rejected - server is full.\n" );
+
+ // optionally redirect them to a different address
+ if( sv_redirect_address->string[0] ) {
+ send_redirect_hack( sv_redirect_address->string );
+ }
}
return;
}
@@ -1753,6 +1774,7 @@ void SV_Init( void ) {
sv_locked = Cvar_Get( "sv_locked", "0", 0 );
sv_novis = Cvar_Get ("sv_novis", "0", 0);
sv_downloadserver = Cvar_Get( "sv_downloadserver", "", 0 );
+ sv_redirect_address = Cvar_Get( "sv_redirect_address", "", 0 );
#if USE_CLIENT
sv_debug_send = Cvar_Get( "sv_debug_send", "0", 0 );
diff --git a/source/sv_mvd.c b/source/sv_mvd.c
index 2bd373b..12fe860 100644
--- a/source/sv_mvd.c
+++ b/source/sv_mvd.c
@@ -570,7 +570,7 @@ static void emit_gamestate( void ) {
entity_state_t *es;
size_t length;
int flags, extra, portalbytes;
- byte portalbits[MAX_MAP_AREAS/8];
+ byte portalbits[MAX_MAP_PORTAL_BYTES];
// pack MVD stream flags into extra bits
extra = 0;
@@ -679,7 +679,7 @@ static void emit_frame( void ) {
entity_state_t *oldes, *newes;
edict_t *ent;
int flags, portalbytes;
- byte portalbits[MAX_MAP_AREAS/8];
+ byte portalbits[MAX_MAP_PORTAL_BYTES];
int i;
MSG_WriteByte( mvd_frame );
@@ -2075,7 +2075,7 @@ static void SV_MvdRecord_f( void ) {
while( ( c = Cmd_ParseOptions( o_record ) ) != -1 ) {
switch( c ) {
case 'h':
- Cmd_PrintUsage( o_record, "[/]<filename>" );
+ Cmd_PrintUsage( o_record, "<filename>" );
Com_Printf( "Begin local MVD recording.\n" );
Cmd_PrintHelp( o_record );
return;
diff --git a/source/sv_world.c b/source/sv_world.c
index 8a19d52..83d77bb 100644
--- a/source/sv_world.c
+++ b/source/sv_world.c
@@ -290,8 +290,10 @@ void PF_LinkEdict (edict_t *ent) {
if (ent == ge->edicts)
return; // don't add the world
- if (!ent->inuse)
+ if (!ent->inuse) {
+ Com_DPrintf( "%s: entity %d is not in use\n", __func__, NUM_FOR_EDICT( ent ) );
return;
+ }
if( !sv.cm.cache ) {
return;
diff --git a/source/ui_demos.c b/source/ui_demos.c
index 7fc6dd8..5596e29 100644
--- a/source/ui_demos.c
+++ b/source/ui_demos.c
@@ -85,13 +85,7 @@ static void BuildName( fsFileInfo_t *info, char **cache ) {
CL_GetDemoInfo( buffer, &demo );
}
- if( info->size >= 1000000 ) {
- sprintf( buffer, "%2.1fM", ( float )info->size / 1000000 );
- } else if( info->size >= 1000 ) {
- sprintf( buffer, "%3"PRIz"K", info->size / 1000 );
- } else {
- sprintf( buffer, "%3"PRIz"b", info->size );
- }
+ Q_FormatFileSize( buffer, info->size, sizeof( buffer ) );
e = UI_FormatColumns( DEMO_EXTRASIZE,
info->name, buffer, demo.map, demo.pov, NULL );
diff --git a/wiki/doc.mdwn b/wiki/doc.mdwn
index 53e412e..6e3136d 100644
--- a/wiki/doc.mdwn
+++ b/wiki/doc.mdwn
@@ -1,7 +1,7 @@
Documentation for Q2PRO.
-* [[Server_commands_and_cvars|server]].
-* [[Client_commands_and_cvars|client]].
+* [[Server]] commands and cvars.
+* [[Client]] commands and cvars.
* Building instructions for Win32 hosted [[MinGW]].
* Some technical [[netcode]] details.
* Online Q2PRO [configurator](http://skuller.ath.cx/q2pro/config.cgi).
diff --git a/wiki/doc/client.mdwn b/wiki/doc/client.mdwn
index ee176bf..990f63d 100644
--- a/wiki/doc/client.mdwn
+++ b/wiki/doc/client.mdwn
@@ -1,4 +1,4 @@
-[[toc ]]
+[[!toc levels=2]]
Variables
==========
diff --git a/wiki/doc/server.mdwn b/wiki/doc/server.mdwn
index 213e01a..09207fb 100644
--- a/wiki/doc/server.mdwn
+++ b/wiki/doc/server.mdwn
@@ -1,4 +1,4 @@
-[[toc ]]
+[[!toc levels=2 ]]
Variables
==========
@@ -6,15 +6,11 @@ Variables
Network
----------
-All network related cvars below can be changed at runtime and will
-take effect immediately.
-
- `set net_ip ""` (string)
Specifies network interface address server should listen on
for UDP connections. The same interface is also used for outgoing
TCP connections (these include MVD/GTV and anticheat connections).
-Empty string means listening on all interfaces. *The latter is not
-supported on multi IP systems if you plan to use anticheat!*
+Empty string means listening on all interfaces.
- `set net_port 27910` (integer)
Specifies port number server should listen on for UDP connections.
@@ -30,6 +26,12 @@ Value of this cvar mirrors `net_ip` unless modified by user.
Specifies port number server should listen on for TCP connections.
Value of this cvar mirrors `net_port` unless modified by user.
+*NOTE*: There is a limitation preventing anticheat to work correctly on
+servers accessible from multiple IP addresses. If you are running the server
+on multi IP system and plan to use anticheat, you need to explicitly bind
+the server to one of your network interfaces using the `net_ip` cvar, otherwise
+expect any kinds of problems.
+
Misc
----------
@@ -93,7 +95,7 @@ unless game mod already shows name changes.
Display uptime string in server info.
-MVD server
+MVD/GTV server
----------
- `set sv_mvd_enable 0` (integer)
@@ -105,6 +107,11 @@ Enables MVD/GTV server functionality.
- `set sv_mvd_maxclients 8` (integer)
Total number of MVD/GTV client slots on the server.
+- `set sv_mvd_password ""` (string)
+Password used to authenticate MVD/GTV clients. If password is not set, only those clients
+can connect, whose IP address is added to the list of trusted MVD/GTV hosts using ‘addgtvhos
+command.
+
- `set sv_mvd_nogun 1` (boolean)
Reduce bandwidth usage by filtering on-screen gun updates out of MVD stream.
@@ -147,7 +154,7 @@ 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
+MVD/GTV client
----------
- `set mvd_username "unnamed"` (string)
@@ -203,6 +210,9 @@ clients, regardless of their protocol version.
Commands
==========
+Misc
+----------
+
- `stuff <userid> <text ...>`
Stuff the given _text_ into command buffer of the client identified by _userid_.
@@ -216,57 +226,86 @@ connections. Remote client must support passive connections (R1Q2 and Q2PRO
clients do), must be in passive connection mode and the specified _port_
must be reachable. See `passive` [[client#index3h1]] command for more details.
-- `addban <address[/mask]> [comment ...]`
+- `addban <address[/mask]> [comment ...]`
Adds specified address to the ban list. Specify _mask_ to ban entire subnetwork.
If specified, _comment_ will be printed to banned user(s) when they attempt to connect.
-- `delban <address[/mask]|id|all>`
+- `delban <address[/mask]|id|all>`
Deletes single matching address from the ban list. You can also specify
-numeric ID of the address/mask pair to delete, or `all` to clear
-the whole ban list.
+numeric ID of the address/mask pair to delete, or use special keyword `all`
+to clear the entire list.
-- `listbans`
-Enumerates all registered address/mask pairs in the ban list.
+- `listbans`
+Displays all address/mask pairs added to the ban list along with their IDs.
-- `kickban <userid>`
+- `kickban <userid>`
Kick the specified client and add his IP address to the
ban list (with a default mask of 32).
-- `addstuffcmd <connect|begin> <command> [...]`
+- `addstuffcmd <connect|begin> <command> [...]`
Adds command to be automatically stuffed to every client as they initially
connect or each time they enter new map.
-- `delstuffcmd <connect|begin> <id|all>`
+- `delstuffcmd <connect|begin> <id|all>`
Deletes command identified by numeric ID from the specified list.
You can also specify `all` to clear the whole stuffcmd list.
-- `liststuffcmds <connect|begin>`
+- `liststuffcmds <connect|begin>`
Enumerates all registered commands in the specified stuffcmd list.
-- `addfiltercmd <command> [ignore|print|stuff|kick] [comment ...]`
+- `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>`
+- `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`
+- `listfiltercmds`
Enumerates all filtered commands along with appropriate actions and comments.
+MVD/GTV server
+----------
+
+- `mvdrecord [-hz] <filename>`
+Start local MVD recording into `demos/<filename>.mvd2`.
+
+- `mvdstop`
+Stop local MVD recording.
+
+- `mvdstuff <text>`
+Execute the given _text_ on behaf of dummy MVD observer.
+
+- `addgtvhost <address[/mask]>`
+Adds specified address to the list of trusted MVD/GTV hosts allowed to connect
+to this server without password. Specify _mask_ to allow entire subnetwork.
+
+- `delgtvhost <address[/mask]|id|all>`
+Deletes single matching address from the list of trusted MVD/GTV hosts.
+You can also specify numeric ID of the address/mask pair to delete,
+or use special keyword `all` to clear the entire list.
+
+- `listgtvhosts`
+Displays all address/mask pairs added to the list of trusted MVD/GTV hosts
+along with their IDs.
+
+MVD/GTV client
+----------
+
- `mvdplay [-hl:n:] <filename> [...]`
Play the local MVD identified by _filename_.
- `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.
+automatically created for this connection as soon as there is some data available,
+and MVD channel will be destroyed after the server suspends the connection.
- `mvdisconnect [connection_id]`
-Disconnect the specified channel from the server.
-There is no need to specify `chanid` if there is only one active channel.
+Disconnect the specified channel from the server (allowing it to replay any remaining
+data to spectators). 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).
@@ -279,28 +318,27 @@ when the very first GTV connection is established or local MVD file
is replayed.
- `mvdchannels`
-List all MVD channels.
+List all MVD channels (there may be none, if all GTV connections are suspended).
- `mvdservers`
List all GTV connections.
-- `mvdstuff <text>`
-Stuff the specified text into command buffer of the dummy MVD observer.
-Should be issued on _game_ server, not on MVD client.
-
Anticheat
==========
-Q2PRO server supports r1ch.net anticheat server interface, which means it is
-capable of serving clients supported by the proprietary client side anticheat
-module (currently, these include Windows builds of R1Q2, EGL, AprQ2 and Q2PRO).
+Q2PRO server supports r1ch.net anticheat server interface, and is capable of serving
+Win32 clients supported by the proprietary anticheat module.
+
+Please see this [forum thread][1] for more information on how to setup your server
+to use the anticheat interface.
+
+Q2PRO anticheat server interface is based on R1Q2 reference implementation.
+All R1Q2 variables and commands related to anticheat are supported, with the
+following exceptions:
-See this [thread](http://www.r1ch.net/forum/index.php?topic=944.0) on r1ch.net
-forums for more information on how to setup your server to use anticheat
-interface.
+- `sv_anticheat_nag_*`
+- `sv_anticheat_client_restrictions`
+- `sv_anticheat_force_protocol35`
-Q2PRO anticheat server interface is implemented on top of R1Q2 reference
-implementation. All R1Q2 variables and commands related to anticheat are
-supported, except of `sv_anticheat_nag_*`, `sv_anticheat_client_restrictions`
-and `sv_anticheat_force_protocol35` variables.
+[1]: http://www.r1ch.net/forum/index.php?topic=944.0