2004-08-23 00:15:46 +00:00
/*
Copyright ( C ) 1996 - 1997 Id Software , Inc .
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
2005-07-03 15:16:20 +00:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
2004-08-23 00:15:46 +00:00
See the included ( GNU . txt ) GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include "qwsvdef.h"
2004-11-29 01:21:00 +00:00
# ifndef CLIENTONLY
2005-11-29 13:22:58 +00:00
2004-08-23 00:15:46 +00:00
# include "winquake.h"
2005-11-29 13:22:58 +00:00
# include "netinc.h"
2005-05-21 01:28:51 +00:00
2004-08-23 00:15:46 +00:00
void SV_MVDStop_f ( void ) ;
2004-12-10 21:34:42 +00:00
# define demo_size_padding 0x1000
2011-08-16 04:12:15 +00:00
# define MIN_MVD_MEMORY 0x100000
# define MAXSIZE (demobuffer->end < demobuffer->last ? \
demobuffer - > start - demobuffer - > end : \
demobuffer - > maxsize - demobuffer - > end )
static void SV_DemoDir_Callback ( struct cvar_s * var , char * oldvalue ) ;
cvar_t sv_demoUseCache = CVARD ( " sv_demoUseCache " , " 0 " , " If set, demo data will be flushed only periodically " ) ;
cvar_t sv_demoCacheSize = CVAR ( " sv_demoCacheSize " , " 0x80000 " ) ; //half a meg
cvar_t sv_demoMaxDirSize = CVAR ( " sv_demoMaxDirSize " , " 102400 " ) ; //so ktpro autorecords.
cvar_t sv_demoDir = CVARC ( " sv_demoDir " , " demos " , SV_DemoDir_Callback ) ;
cvar_t sv_demofps = CVAR ( " sv_demofps " , " 30 " ) ;
cvar_t sv_demoPings = CVARD ( " sv_demoPings " , " 10 " , " Interval between ping updates in mvds " ) ;
cvar_t sv_demoMaxSize = CVARD ( " sv_demoMaxSize " , " " , " Demos will be truncated to be no larger than this size. " ) ;
cvar_t sv_demoExtraNames = CVAR ( " sv_demoExtraNames " , " " ) ;
cvar_t sv_demoExtensions = CVARD ( " sv_demoExtensions " , " 0 " , " Enables protocol extensions within MVDs. This will cause older/non-fte clients to error upon playback " ) ;
cvar_t qtv_password = CVAR ( " qtv_password " , " " ) ;
cvar_t qtv_streamport = CVARAF ( " qtv_streamport " , " 0 " ,
" mvd_streamport " , 0 ) ;
cvar_t qtv_maxstreams = CVARAF ( " qtv_maxstreams " , " 1 " ,
" mvd_maxstreams " , 0 ) ;
cvar_t sv_demoPrefix = CVAR ( " sv_demoPrefix " , " " ) ;
cvar_t sv_demoSuffix = CVAR ( " sv_demoSuffix " , " " ) ;
cvar_t sv_demotxt = CVAR ( " sv_demotxt " , " 1 " ) ;
void SV_WriteMVDMessage ( sizebuf_t * msg , int type , int to , float time ) ;
demo_t demo ;
static dbuffer_t * demobuffer ;
static int header = ( char * ) & ( ( header_t * ) 0 ) - > data - ( char * ) NULL ;
entity_state_t demo_entities [ UPDATE_MASK + 1 ] [ MAX_MVDPACKET_ENTITIES ] ;
client_frame_t demo_frames [ UPDATE_MASK + 1 ] ;
2004-12-10 21:34:42 +00:00
mvddest_t * singledest ;
2006-09-17 00:59:22 +00:00
mvddest_t * SV_InitStream ( int socket ) ;
static qboolean SV_MVD_Record ( mvddest_t * dest ) ;
2009-04-01 22:03:56 +00:00
char * SV_MVDName2Txt ( char * name ) ;
2006-09-17 00:59:22 +00:00
extern cvar_t qtv_password ;
2004-12-10 21:34:42 +00:00
void DestClose ( mvddest_t * d , qboolean destroyfiles )
{
char path [ MAX_OSPATH ] ;
if ( d - > cache )
BZ_Free ( d - > cache ) ;
if ( d - > file )
2009-04-01 22:03:56 +00:00
VFS_CLOSE ( d - > file ) ;
2012-04-09 19:12:12 +00:00
# ifdef HAVE_TCP
2004-12-10 21:34:42 +00:00
if ( d - > socket )
UDP_CloseSocket ( d - > socket ) ;
2012-04-09 19:12:12 +00:00
# endif
2004-12-10 21:34:42 +00:00
if ( destroyfiles )
{
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , d - > path , d - > name ) ;
FS_Remove ( path , FS_GAMEONLY ) ;
2004-12-10 21:34:42 +00:00
2009-04-01 22:03:56 +00:00
FS_Remove ( SV_MVDName2Txt ( path ) , FS_GAMEONLY ) ;
2004-12-10 21:34:42 +00:00
}
Z_Free ( d ) ;
}
void DestFlush ( qboolean compleate )
{
int len ;
mvddest_t * d , * t ;
if ( ! demo . dest )
return ;
while ( demo . dest - > error )
{
d = demo . dest ;
demo . dest = d - > nextdest ;
DestClose ( d , false ) ;
2005-09-06 01:19:26 +00:00
if ( ! demo . dest )
{
2006-11-03 16:03:15 +00:00
SV_MVDStop ( 3 , false ) ;
2005-09-06 01:19:26 +00:00
return ;
}
2004-12-10 21:34:42 +00:00
}
for ( d = demo . dest ; d ; d = d - > nextdest )
{
switch ( d - > desttype )
{
case DEST_FILE :
2009-04-01 22:03:56 +00:00
VFS_FLUSH ( d - > file ) ;
2004-12-10 21:34:42 +00:00
break ;
case DEST_BUFFEREDFILE :
if ( d - > cacheused + demo_size_padding > d - > maxcachesize | | compleate )
{
2009-04-01 22:03:56 +00:00
len = VFS_WRITE ( d - > file , d - > cache , d - > cacheused ) ;
2004-12-10 21:34:42 +00:00
if ( len < d - > cacheused )
d - > error = true ;
2009-04-01 22:03:56 +00:00
VFS_FLUSH ( d - > file ) ;
2004-12-10 21:34:42 +00:00
d - > cacheused = 0 ;
}
break ;
case DEST_STREAM :
2012-04-09 19:12:12 +00:00
# ifndef HAVE_TCP
d - > error = true ;
# else
2006-03-23 19:22:12 +00:00
if ( d - > cacheused & & ! d - > error )
2004-12-10 21:34:42 +00:00
{
2005-10-07 02:03:17 +00:00
len = send ( d - > socket , d - > cache , d - > cacheused , 0 ) ;
if ( len = = 0 ) //client died
2005-09-06 01:19:26 +00:00
d - > error = true ;
2006-03-23 19:22:12 +00:00
else if ( len > 0 ) //we put some data through
{ //move up the buffer
2005-10-07 02:03:17 +00:00
d - > cacheused - = len ;
memmove ( d - > cache , d - > cache + len , d - > cacheused ) ;
}
else
{ //error of some kind. would block or something
int e ;
e = qerrno ;
if ( e ! = EWOULDBLOCK )
d - > error = true ;
}
2004-12-10 21:34:42 +00:00
}
2012-04-09 19:12:12 +00:00
# endif
2004-12-10 21:34:42 +00:00
break ;
case DEST_NONE :
Sys_Error ( " DestFlush encoundered bad dest. " ) ;
}
if ( sv_demoMaxSize . value & & d - > totalsize > sv_demoMaxSize . value * 1024 )
d - > error = 2 ; //abort, but don't kill it.
while ( d - > nextdest & & d - > nextdest - > error )
{
t = d - > nextdest ;
d - > nextdest = t - > nextdest ;
DestClose ( t , false ) ;
}
}
}
2006-09-17 00:59:22 +00:00
void SV_MVD_RunPendingConnections ( void )
{
2012-04-09 19:12:12 +00:00
# ifndef HAVE_TCP
if ( demo . pendingdest )
Sys_Error ( " demo.pendingdest not null " ) ;
# else
2006-09-17 00:59:22 +00:00
unsigned short ushort_result ;
char * e ;
int len ;
mvdpendingdest_t * p ;
mvdpendingdest_t * np ;
if ( ! demo . pendingdest )
return ;
while ( demo . pendingdest & & demo . pendingdest - > error )
{
np = demo . pendingdest - > nextdest ;
2007-11-04 08:16:23 +00:00
if ( demo . pendingdest - > socket ! = INVALID_SOCKET )
2006-09-17 00:59:22 +00:00
closesocket ( demo . pendingdest - > socket ) ;
Z_Free ( demo . pendingdest ) ;
demo . pendingdest = np ;
}
for ( p = demo . pendingdest ; p & & p - > nextdest ; p = p - > nextdest )
{
if ( p - > nextdest - > error )
{
np = p - > nextdest - > nextdest ;
2007-11-04 08:21:19 +00:00
if ( p - > nextdest - > socket ! = INVALID_SOCKET )
2006-09-17 00:59:22 +00:00
closesocket ( p - > nextdest - > socket ) ;
Z_Free ( p - > nextdest ) ;
p - > nextdest = np ;
}
}
for ( p = demo . pendingdest ; p ; p = p - > nextdest )
{
if ( p - > outsize & & ! p - > error )
{
len = send ( p - > socket , p - > outbuffer , p - > outsize , 0 ) ;
if ( len = = 0 ) //client died
p - > error = true ;
else if ( len > 0 ) //we put some data through
{ //move up the buffer
p - > outsize - = len ;
memmove ( p - > outbuffer , p - > outbuffer + len , p - > outsize ) ;
}
else
{ //error of some kind. would block or something
int e ;
e = qerrno ;
if ( e ! = EWOULDBLOCK )
p - > error = true ;
}
}
if ( ! p - > error )
{
len = recv ( p - > socket , p - > inbuffer + p - > insize , sizeof ( p - > inbuffer ) - p - > insize - 1 , 0 ) ;
if ( len > 0 )
{ //fixme: cope with extra \rs
char * end ;
p - > insize + = len ;
p - > inbuffer [ p - > insize ] = 0 ;
for ( end = p - > inbuffer ; ; end + + )
{
if ( * end = = ' \0 ' )
{
end = NULL ;
break ; //not enough data
}
if ( end [ 0 ] = = ' \n ' )
{
if ( end [ 1 ] = = ' \n ' )
{
end [ 1 ] = ' \0 ' ;
break ;
}
}
}
if ( end )
{ //we found the end of the header
2007-08-07 17:49:35 +00:00
qboolean server = false ;
2006-09-17 00:59:22 +00:00
char * start , * lineend ;
int versiontouse = 0 ;
int raw = 0 ;
char password [ 256 ] = " " ;
enum {
QTVAM_NONE ,
QTVAM_PLAIN ,
QTVAM_CCITT ,
QTVAM_MD4 ,
QTVAM_MD5 ,
} authmethod = QTVAM_NONE ;
start = p - > inbuffer ;
lineend = strchr ( start , ' \n ' ) ;
if ( ! lineend )
{
// char *e;
// e = "This is a QTV server.";
// send(p->socket, e, strlen(e), 0);
p - > error = true ;
continue ;
}
* lineend = ' \0 ' ;
COM_ParseToken ( start , NULL ) ;
start = lineend + 1 ;
if ( strcmp ( com_token , " QTV " ) )
{ //it's an error if it's not qtv.
2007-05-28 02:25:32 +00:00
if ( ! strcmp ( com_token , " QTVSV " ) )
server = true ;
else
{
p - > error = true ;
lineend = strchr ( start , ' \n ' ) ;
continue ;
}
}
if ( server ! = p - > isreverse )
{ //just a small check
2006-09-17 00:59:22 +00:00
p - > error = true ;
2007-05-28 02:25:32 +00:00
return ;
2006-09-17 00:59:22 +00:00
}
for ( ; ; )
{
lineend = strchr ( start , ' \n ' ) ;
if ( ! lineend )
break ;
* lineend = ' \0 ' ;
start = COM_ParseToken ( start , NULL ) ;
if ( * start = = ' : ' )
{
//VERSION: a list of the different qtv protocols supported. Multiple versions can be specified. The first is assumed to be the prefered version.
//RAW: if non-zero, send only a raw mvd with no additional markup anywhere (for telnet use). Doesn't work with challenge-based auth, so will only be accepted when proxy passwords are not required.
//AUTH: specifies an auth method, the exact specs varies based on the method
// PLAIN: the password is sent as a PASSWORD line
// MD4: the server responds with an "AUTH: MD4\n" line as well as a "CHALLENGE: somerandomchallengestring\n" line, the client sends a new 'initial' request with CHALLENGE: MD4\nRESPONSE: hexbasedmd4checksumhere\n"
// MD5: same as md4
// CCITT: same as md4, but using the CRC stuff common to all quake engines.
// if the supported/allowed auth methods don't match, the connection is silently dropped.
//SOURCE: which stream to play from, DEFAULT is special. Without qualifiers, it's assumed to be a tcp address.
//COMPRESSION: Suggests a compression method (multiple are allowed). You'll get a COMPRESSION response, and compression will begin with the binary data.
start = start + 1 ;
Con_Printf ( " qtv, got (%s) (%s) \n " , com_token , start ) ;
if ( ! strcmp ( com_token , " VERSION " ) )
{
start = COM_ParseToken ( start , NULL ) ;
if ( atoi ( com_token ) = = 1 )
versiontouse = 1 ;
}
else if ( ! strcmp ( com_token , " RAW " ) )
{
start = COM_ParseToken ( start , NULL ) ;
raw = atoi ( com_token ) ;
}
else if ( ! strcmp ( com_token , " PASSWORD " ) )
{
start = COM_ParseToken ( start , NULL ) ;
Q_strncpyz ( password , com_token , sizeof ( password ) ) ;
}
else if ( ! strcmp ( com_token , " AUTH " ) )
{
int thisauth ;
start = COM_ParseToken ( start , NULL ) ;
if ( ! strcmp ( com_token , " NONE " ) )
thisauth = QTVAM_PLAIN ;
else if ( ! strcmp ( com_token , " PLAIN " ) )
thisauth = QTVAM_PLAIN ;
else if ( ! strcmp ( com_token , " CCIT " ) )
thisauth = QTVAM_CCITT ;
else if ( ! strcmp ( com_token , " MD4 " ) )
thisauth = QTVAM_MD4 ;
// else if (!strcmp(com_token, "MD5"))
// thisauth = QTVAM_MD5;
else
{
thisauth = QTVAM_NONE ;
Con_DPrintf ( " qtv: received unrecognised auth method (%s) \n " , com_token ) ;
}
if ( authmethod < thisauth )
authmethod = thisauth ;
}
else if ( ! strcmp ( com_token , " SOURCE " ) )
{
//servers don't support source, and ignore it.
//source is only useful for qtv proxy servers.
}
else if ( ! strcmp ( com_token , " COMPRESSION " ) )
{
//compression not supported yet
}
else
{
//not recognised.
}
}
start = lineend + 1 ;
}
len = ( end - p - > inbuffer ) + 2 ;
p - > insize - = len ;
memmove ( p - > inbuffer , p - > inbuffer + len , p - > insize ) ;
p - > inbuffer [ p - > insize ] = 0 ;
e = NULL ;
if ( p - > hasauthed )
{
}
2007-05-28 02:25:32 +00:00
else if ( p - > isreverse )
p - > hasauthed = true ; //reverse connections do not need to auth.
2006-09-17 00:59:22 +00:00
else if ( ! * qtv_password . string )
p - > hasauthed = true ; //no password, no need to auth.
else if ( * password )
{
switch ( authmethod )
{
case QTVAM_NONE :
e = ( " QTVSV 1 \n "
" PERROR: You need to provide a common auth method. \n \n " ) ;
break ;
case QTVAM_PLAIN :
p - > hasauthed = ! strcmp ( qtv_password . string , password ) ;
break ;
case QTVAM_CCITT :
QCRC_Init ( & ushort_result ) ;
QCRC_AddBlock ( & ushort_result , p - > challenge , strlen ( p - > challenge ) ) ;
QCRC_AddBlock ( & ushort_result , qtv_password . string , strlen ( qtv_password . string ) ) ;
2007-11-09 21:21:21 +00:00
p - > hasauthed = ( ushort_result = = strtoul ( password , NULL , 0 ) ) ;
2006-09-17 00:59:22 +00:00
break ;
case QTVAM_MD4 :
{
char hash [ 512 ] ;
int md4sum [ 4 ] ;
2011-05-19 13:34:07 +00:00
2006-09-17 00:59:22 +00:00
snprintf ( hash , sizeof ( hash ) , " %s%s " , p - > challenge , qtv_password . string ) ;
Com_BlockFullChecksum ( hash , strlen ( hash ) , ( unsigned char * ) md4sum ) ;
sprintf ( hash , " %X%X%X%X " , md4sum [ 0 ] , md4sum [ 1 ] , md4sum [ 2 ] , md4sum [ 3 ] ) ;
p - > hasauthed = ! strcmp ( password , hash ) ;
}
break ;
case QTVAM_MD5 :
default :
e = ( " QTVSV 1 \n "
" PERROR: FTEQWSV bug detected. \n \n " ) ;
break ;
}
if ( ! p - > hasauthed & & ! e )
{
if ( raw )
e = " " ;
else
e = ( " QTVSV 1 \n "
" PERROR: Bad password. \n \n " ) ;
}
}
else
{
//no password, and not automagically authed
switch ( authmethod )
{
case QTVAM_NONE :
if ( raw )
e = " " ;
else
e = ( " QTVSV 1 \n "
" PERROR: You need to provide a common auth method. \n \n " ) ;
break ;
case QTVAM_PLAIN :
p - > hasauthed = ! strcmp ( qtv_password . string , password ) ;
break ;
if ( 0 )
{
case QTVAM_CCITT :
e = ( " QTVSV 1 \n "
" AUTH: CCITT \n "
" CHALLENGE: " ) ;
}
else if ( 0 )
{
case QTVAM_MD4 :
e = ( " QTVSV 1 \n "
" AUTH: MD4 \n "
" CHALLENGE: " ) ;
}
else
{
case QTVAM_MD5 :
e = ( " QTVSV 1 \n "
" AUTH: MD5 \n "
" CHALLENGE: " ) ;
}
send ( p - > socket , e , strlen ( e ) , 0 ) ;
send ( p - > socket , p - > challenge , strlen ( p - > challenge ) , 0 ) ;
e = " \n \n " ;
send ( p - > socket , e , strlen ( e ) , 0 ) ;
continue ;
default :
e = ( " QTVSV 1 \n "
" PERROR: FTEQWSV bug detected. \n \n " ) ;
break ;
}
}
if ( e )
{
}
else if ( ! versiontouse )
{
e = ( " QTVSV 1 \n "
2007-10-05 18:08:47 +00:00
" PERROR: Incompatible version (valid version is v1) \n \n " ) ;
2006-09-17 00:59:22 +00:00
}
else if ( raw )
{
if ( p - > hasauthed = = false )
{
e = " " ;
}
else
{
SV_MVD_Record ( SV_InitStream ( p - > socket ) ) ;
2007-11-04 08:21:19 +00:00
p - > socket = INVALID_SOCKET ; //so it's not cleared wrongly.
2006-09-17 00:59:22 +00:00
}
p - > error = true ;
}
else
{
if ( p - > hasauthed = = true )
{
2007-05-28 02:25:32 +00:00
mvddest_t * dst ;
2006-09-17 00:59:22 +00:00
e = ( " QTVSV 1 \n "
2006-11-03 15:53:04 +00:00
" BEGIN \n "
2006-09-17 00:59:22 +00:00
" \n " ) ;
send ( p - > socket , e , strlen ( e ) , 0 ) ;
e = NULL ;
2007-05-28 02:25:32 +00:00
dst = SV_InitStream ( p - > socket ) ;
dst - > droponmapchange = p - > isreverse ;
SV_MVD_Record ( dst ) ;
2007-11-04 08:21:19 +00:00
p - > socket = INVALID_SOCKET ; //so it's not cleared wrongly.
2006-09-17 00:59:22 +00:00
}
else
{
e = ( " QTVSV 1 \n "
" PERROR: You need to provide a password. \n \n " ) ;
}
p - > error = true ;
}
if ( e )
{
send ( p - > socket , e , strlen ( e ) , 0 ) ;
p - > error = true ;
}
}
}
else if ( len = = 0 )
p - > error = true ;
else
{ //error of some kind. would block or something
int e ;
e = qerrno ;
if ( e ! = EWOULDBLOCK )
p - > error = true ;
}
}
}
2012-04-09 19:12:12 +00:00
# endif
2006-09-17 00:59:22 +00:00
}
2006-11-03 15:53:04 +00:00
int DestCloseAllFlush ( qboolean destroyfiles , qboolean mvdonly )
2004-12-10 21:34:42 +00:00
{
2006-11-03 15:53:04 +00:00
int numclosed = 0 ;
2007-01-06 09:35:16 +00:00
mvddest_t * d , * * prev , * next ;
2004-12-10 21:34:42 +00:00
DestFlush ( true ) ; //make sure it's all written.
2006-11-03 15:53:04 +00:00
prev = & demo . dest ;
d = demo . dest ;
while ( d )
2004-12-10 21:34:42 +00:00
{
2007-01-06 09:35:16 +00:00
next = d - > nextdest ;
2007-05-28 02:25:32 +00:00
if ( ! mvdonly | | d - > droponmapchange )
2006-11-03 15:53:04 +00:00
{
* prev = d - > nextdest ;
DestClose ( d , destroyfiles ) ;
numclosed + + ;
}
else
2007-01-06 09:35:16 +00:00
prev = & d - > nextdest ;
2004-12-10 21:34:42 +00:00
2007-01-06 09:35:16 +00:00
d = next ;
2004-12-10 21:34:42 +00:00
}
2006-11-03 15:53:04 +00:00
return numclosed ;
2004-12-10 21:34:42 +00:00
}
int DemoWriteDest ( void * data , int len , mvddest_t * d )
{
if ( d - > error )
return 0 ;
d - > totalsize + = len ;
switch ( d - > desttype )
{
case DEST_FILE :
2009-04-01 22:03:56 +00:00
VFS_WRITE ( d - > file , data , len ) ;
2004-12-10 21:34:42 +00:00
break ;
case DEST_BUFFEREDFILE : //these write to a cache, which is flushed later
case DEST_STREAM :
if ( d - > cacheused + len > d - > maxcachesize )
{
d - > error = true ;
return 0 ;
}
memcpy ( d - > cache + d - > cacheused , data , len ) ;
d - > cacheused + = len ;
break ;
case DEST_NONE :
Sys_Error ( " DemoWriteDest encoundered bad dest. " ) ;
}
return len ;
}
2006-09-17 00:59:22 +00:00
int DemoWrite ( void * data , int len ) //broadcast to all proxies/mvds
2004-12-10 21:34:42 +00:00
{
mvddest_t * d ;
for ( d = demo . dest ; d ; d = d - > nextdest )
{
if ( singledest & & singledest ! = d )
continue ;
DemoWriteDest ( data , len , d ) ;
}
return len ;
}
2004-08-23 00:15:46 +00:00
2006-09-17 00:59:22 +00:00
void DemoWriteQTVTimePad ( int msecs ) //broadcast to all proxies
{
mvddest_t * d ;
unsigned char buffer [ 6 ] ;
while ( msecs > 0 )
{
//duration
if ( msecs > 255 )
buffer [ 0 ] = 255 ;
else
buffer [ 0 ] = msecs ;
msecs - = buffer [ 0 ] ;
//message type
buffer [ 1 ] = dem_read ;
//length
buffer [ 2 ] = 0 ;
buffer [ 3 ] = 0 ;
buffer [ 4 ] = 0 ;
buffer [ 5 ] = 0 ;
for ( d = demo . dest ; d ; d = d - > nextdest )
{
if ( d - > desttype = = DEST_STREAM )
{
DemoWriteDest ( buffer , sizeof ( buffer ) , d ) ;
}
}
}
}
2004-08-23 00:15:46 +00:00
2011-12-24 04:05:28 +00:00
2004-08-23 00:15:46 +00:00
// returns the file size
// return -1 if file is not present
// the file should be in BINARY mode for stupid OSs that care
# define MAX_DIRFILES 1000
# define MAX_MVD_NAME 64
typedef struct
{
char name [ MAX_MVD_NAME ] ;
int size ;
} file_t ;
typedef struct
{
file_t * files ;
int size ;
int numfiles ;
int numdirs ;
2008-06-01 22:06:22 +00:00
int maxfiles ;
2004-08-23 00:15:46 +00:00
} dir_t ;
# define SORT_NO 0
# define SORT_BY_DATE 1
2009-04-01 22:03:56 +00:00
int Sys_listdirFound ( const char * fname , int fsize , void * uptr )
2004-08-23 00:15:46 +00:00
{
2009-04-01 22:03:56 +00:00
file_t * f ;
dir_t * dir = uptr ;
fname = COM_SkipPath ( fname ) ;
if ( ! * fname )
2004-08-23 00:15:46 +00:00
{
2009-04-01 22:03:56 +00:00
dir - > numdirs + + ;
return true ;
2004-08-23 00:15:46 +00:00
}
2009-04-01 22:03:56 +00:00
if ( dir - > numfiles = = dir - > maxfiles )
return true ;
f = & dir - > files [ dir - > numfiles + + ] ;
Q_strncpyz ( f - > name , fname , sizeof ( f - > name ) ) ;
f - > size = fsize ;
dir - > size + = fsize ;
2005-07-03 15:16:20 +00:00
2009-04-01 22:03:56 +00:00
return true ;
2008-06-01 22:06:22 +00:00
}
dir_t * Sys_listdir ( char * path , char * ext , qboolean usesorting )
2004-08-23 00:15:46 +00:00
{
2009-04-01 22:03:56 +00:00
char searchterm [ MAX_QPATH ] ;
2011-05-19 13:34:07 +00:00
2008-06-01 22:06:22 +00:00
unsigned int maxfiles = MAX_DIRFILES ;
2009-04-01 22:03:56 +00:00
dir_t * dir = malloc ( sizeof ( * dir ) + sizeof ( * dir - > files ) * maxfiles ) ;
memset ( dir , 0 , sizeof ( * dir ) ) ;
dir - > files = ( file_t * ) ( dir + 1 ) ;
dir - > maxfiles = maxfiles ;
2004-08-23 00:15:46 +00:00
2010-01-11 17:39:56 +00:00
Q_strncpyz ( searchterm , va ( " %s/*%s " , path , ext ) , sizeof ( searchterm ) ) ;
2009-04-01 22:03:56 +00:00
COM_EnumerateFiles ( searchterm , Sys_listdirFound , dir ) ;
2004-08-23 00:15:46 +00:00
2009-04-01 22:03:56 +00:00
return dir ;
2004-08-23 00:15:46 +00:00
}
2008-06-01 22:06:22 +00:00
void Sys_freedir ( dir_t * dir )
{
free ( dir ) ;
}
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
// only one .. is allowed (so we can get to the same dir as the quake exe)
2007-09-01 01:51:34 +00:00
static void SV_DemoDir_Callback ( struct cvar_s * var , char * oldvalue )
2004-08-23 00:15:46 +00:00
{
2004-12-10 21:34:42 +00:00
char * value ;
2007-09-01 01:51:34 +00:00
value = var - > string ;
if ( ! value [ 0 ] | | value [ 0 ] = = ' / ' | | ( value [ 0 ] = = ' \\ ' & & value [ 1 ] = = ' \\ ' ) )
2004-12-10 21:34:42 +00:00
{
Cvar_ForceSet ( & sv_demoDir , " demos " ) ;
return ;
}
2004-08-23 00:15:46 +00:00
if ( value [ 0 ] = = ' . ' & & value [ 1 ] = = ' . ' )
value + = 2 ;
2004-12-10 21:34:42 +00:00
if ( strstr ( value , " .. " ) )
{
Cvar_ForceSet ( & sv_demoDir , " demos " ) ;
2007-09-01 01:51:34 +00:00
return ;
2004-12-10 21:34:42 +00:00
}
2004-08-23 00:15:46 +00:00
}
void SV_MVDPings ( void )
{
client_t * client ;
int j ;
for ( j = 0 , client = svs . clients ; j < MAX_CLIENTS ; j + + , client + + )
{
if ( client - > state ! = cs_spawned )
continue ;
MVDWrite_Begin ( dem_all , 0 , 7 ) ;
2010-11-20 22:01:16 +00:00
MSG_WriteByte ( & demo . dbuf - > sb , svc_updateping ) ;
MSG_WriteByte ( & demo . dbuf - > sb , j ) ;
MSG_WriteShort ( & demo . dbuf - > sb , SV_CalcPing ( client , false ) ) ;
MSG_WriteByte ( & demo . dbuf - > sb , svc_updatepl ) ;
MSG_WriteByte ( & demo . dbuf - > sb , j ) ;
MSG_WriteByte ( & demo . dbuf - > sb , client - > lossage ) ;
2004-08-23 00:15:46 +00:00
}
}
2011-06-05 01:36:14 +00:00
void SV_MVD_FullClientUpdate ( sizebuf_t * msg , client_t * player )
{
char info [ MAX_INFO_STRING ] ;
qboolean dosizes ;
if ( ! sv . mvdrecording )
return ;
if ( msg )
dosizes = false ;
else
{
dosizes = true ;
msg = & demo . dbuf - > sb ;
}
if ( dosizes )
MVDWrite_Begin ( dem_all , 0 , 4 ) ;
MSG_WriteByte ( msg , svc_updatefrags ) ;
MSG_WriteByte ( msg , player - svs . clients ) ;
MSG_WriteShort ( msg , player - > old_frags ) ;
if ( dosizes )
MVDWrite_Begin ( dem_all , 0 , 4 ) ;
MSG_WriteByte ( msg , svc_updateping ) ;
MSG_WriteByte ( msg , player - svs . clients ) ;
MSG_WriteShort ( msg , SV_CalcPing ( player , false ) ) ;
if ( dosizes )
MVDWrite_Begin ( dem_all , 0 , 3 ) ;
MSG_WriteByte ( msg , svc_updatepl ) ;
MSG_WriteByte ( msg , player - svs . clients ) ;
MSG_WriteByte ( msg , player - > lossage ) ;
if ( dosizes )
MVDWrite_Begin ( dem_all , 0 , 6 ) ;
MSG_WriteByte ( msg , svc_updateentertime ) ;
MSG_WriteByte ( msg , player - svs . clients ) ;
MSG_WriteFloat ( msg , realtime - player - > connection_started ) ;
Q_strncpyz ( info , player - > userinfo , MAX_INFO_STRING ) ;
Info_RemovePrefixedKeys ( info , ' _ ' ) ; // server passwords, etc
Info_RemoveKey ( info , " password " ) ;
Info_RemoveKey ( info , " *ip " ) ;
if ( dosizes )
MVDWrite_Begin ( dem_all , 0 , 7 + strlen ( info ) ) ;
MSG_WriteByte ( msg , svc_updateuserinfo ) ;
MSG_WriteByte ( msg , player - svs . clients ) ;
MSG_WriteLong ( msg , player - > userid ) ;
MSG_WriteString ( msg , info ) ;
}
2004-08-23 00:15:46 +00:00
void MVDBuffer_Init ( dbuffer_t * dbuffer , qbyte * buf , size_t size )
{
demobuffer = dbuffer ;
demobuffer - > data = buf ;
demobuffer - > maxsize = size ;
demobuffer - > start = 0 ;
demobuffer - > end = 0 ;
demobuffer - > last = 0 ;
}
/*
= = = = = = = = = = = = = =
MVD_SetMsgBuf
Sets the frame message buffer
= = = = = = = = = = = = = =
*/
void MVDSetMsgBuf ( demobuf_t * prev , demobuf_t * cur )
{
// fix the maxsize of previous msg buffer,
// we won't be able to write there anymore
if ( prev ! = NULL )
2010-11-20 22:01:16 +00:00
prev - > sb . maxsize = prev - > bufsize ;
2004-08-23 00:15:46 +00:00
demo . dbuf = cur ;
memset ( demo . dbuf , 0 , sizeof ( * demo . dbuf ) ) ;
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . data = demobuffer - > data + demobuffer - > end ;
demo . dbuf - > sb . maxsize = MAXSIZE ;
demo . dbuf - > sb . prim = demo . recorder . netchan . netprim ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = =
DemoWriteToDisk
Writes to disk a message meant for specifc client
or all messages if type = = 0
Message is cleared from demobuf after that
= = = = = = = = = = = = = =
*/
void SV_MVDWriteToDisk ( int type , int to , float time )
{
int pos = 0 , oldm , oldd ;
header_t * p ;
int size ;
sizebuf_t msg ;
2010-11-20 22:01:16 +00:00
p = ( header_t * ) demo . dbuf - > sb . data ;
2004-08-23 00:15:46 +00:00
demo . dbuf - > h = NULL ;
oldm = demo . dbuf - > bufsize ;
oldd = demobuffer - > start ;
while ( pos < demo . dbuf - > bufsize )
{
size = p - > size ;
pos + = header + size ;
// no type means we are writing to disk everything
if ( ! type | | ( p - > type = = type & & p - > to = = to ) )
{
2004-12-10 21:34:42 +00:00
if ( size )
{
2004-08-23 00:15:46 +00:00
msg . data = p - > data ;
msg . cursize = size ;
SV_WriteMVDMessage ( & msg , p - > type , p - > to , time ) ;
}
// data is written so it need to be cleard from demobuf
2010-11-20 22:01:16 +00:00
if ( demo . dbuf - > sb . data ! = ( qbyte * ) p )
memmove ( demo . dbuf - > sb . data + size + header , demo . dbuf - > sb . data , ( qbyte * ) p - demo . dbuf - > sb . data ) ;
2004-08-23 00:15:46 +00:00
demo . dbuf - > bufsize - = size + header ;
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . data + = size + header ;
2004-08-23 00:15:46 +00:00
pos - = size + header ;
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . maxsize - = size + header ;
2004-08-23 00:15:46 +00:00
demobuffer - > start + = size + header ;
}
// move along
2004-09-15 03:07:18 +00:00
p = ( header_t * ) ( p - > data + size ) ;
2004-08-23 00:15:46 +00:00
}
2004-12-10 21:34:42 +00:00
if ( demobuffer - > start = = demobuffer - > last )
{
if ( demobuffer - > start = = demobuffer - > end )
{
2004-08-23 00:15:46 +00:00
demobuffer - > end = 0 ; // demobuffer is empty
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . data = demobuffer - > data ;
2004-08-23 00:15:46 +00:00
}
// go back to begining of the buffer
demobuffer - > last = demobuffer - > end ;
demobuffer - > start = 0 ;
}
}
/*
= = = = = = = = = = = = = =
MVDSetBuf
Sets position in the buf for writing to specific client
= = = = = = = = = = = = = =
*/
static void MVDSetBuf ( qbyte type , int to )
{
header_t * p ;
int pos = 0 ;
2010-11-20 22:01:16 +00:00
p = ( header_t * ) demo . dbuf - > sb . data ;
2004-08-23 00:15:46 +00:00
while ( pos < demo . dbuf - > bufsize )
{
pos + = header + p - > size ;
if ( type = = p - > type & & to = = p - > to & & ! p - > full )
{
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . cursize = pos ;
2004-08-23 00:15:46 +00:00
demo . dbuf - > h = p ;
return ;
}
2004-09-15 03:07:18 +00:00
p = ( header_t * ) ( p - > data + p - > size ) ;
2004-08-23 00:15:46 +00:00
}
// type&&to not exist in the buf, so add it
p - > type = type ;
p - > to = to ;
p - > size = 0 ;
p - > full = 0 ;
demo . dbuf - > bufsize + = header ;
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . cursize = demo . dbuf - > bufsize ;
2004-08-23 00:15:46 +00:00
demobuffer - > end + = header ;
demo . dbuf - > h = p ;
}
void MVDMoveBuf ( void )
{
// set the last message mark to the previous frame (i/e begining of this one)
demobuffer - > last = demobuffer - > end - demo . dbuf - > bufsize ;
// move buffer to the begining of demo buffer
2010-11-20 22:01:16 +00:00
memmove ( demobuffer - > data , demo . dbuf - > sb . data , demo . dbuf - > bufsize ) ;
demo . dbuf - > sb . data = demobuffer - > data ;
2004-08-23 00:15:46 +00:00
demobuffer - > end = demo . dbuf - > bufsize ;
demo . dbuf - > h = NULL ; // it will be setup again
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . maxsize = MAXSIZE + demo . dbuf - > bufsize ;
2004-08-23 00:15:46 +00:00
}
2007-09-10 14:55:36 +00:00
qboolean MVDWrite_Begin ( qbyte type , int to , int size )
2004-08-23 00:15:46 +00:00
{
qbyte * p ;
qboolean move = false ;
// will it fit?
2010-11-20 22:01:16 +00:00
while ( demo . dbuf - > bufsize + size + header > demo . dbuf - > sb . maxsize )
2004-08-23 00:15:46 +00:00
{
// if we reached the end of buffer move msgbuf to the begining
if ( ! move & & demobuffer - > end > demobuffer - > start )
move = true ;
2007-09-10 14:39:08 +00:00
if ( ! SV_MVDWritePackets ( 1 ) )
2007-09-10 14:55:36 +00:00
return false ;
2007-09-10 14:39:08 +00:00
2004-08-23 00:15:46 +00:00
if ( move & & demobuffer - > start > demo . dbuf - > bufsize + header + size )
MVDMoveBuf ( ) ;
}
if ( demo . dbuf - > h = = NULL | | demo . dbuf - > h - > type ! = type | | demo . dbuf - > h - > to ! = to | | demo . dbuf - > h - > full ) {
MVDSetBuf ( type , to ) ;
}
if ( demo . dbuf - > h - > size + size > MAX_QWMSGLEN )
{
demo . dbuf - > h - > full = 1 ;
MVDSetBuf ( type , to ) ;
}
// we have to make room for new data
2010-11-20 22:01:16 +00:00
if ( demo . dbuf - > sb . cursize ! = demo . dbuf - > bufsize ) {
p = demo . dbuf - > sb . data + demo . dbuf - > sb . cursize ;
memmove ( p + size , p , demo . dbuf - > bufsize - demo . dbuf - > sb . cursize ) ;
2004-08-23 00:15:46 +00:00
}
demo . dbuf - > bufsize + = size ;
demo . dbuf - > h - > size + = size ;
if ( ( demobuffer - > end + = size ) > demobuffer - > last )
demobuffer - > last = demobuffer - > end ;
2007-09-10 14:55:36 +00:00
return true ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_WriteMVDMessage
Dumps the current net message , prefixed by the length and view angles
= = = = = = = = = = = = = = = = = = = =
*/
void SV_WriteMVDMessage ( sizebuf_t * msg , int type , int to , float time )
{
int len , i , msec ;
qbyte c ;
static double prevtime ;
if ( ! sv . mvdrecording )
return ;
msec = ( time - prevtime ) * 1000 ;
prevtime + = msec * 0.001 ;
if ( msec > 255 ) msec = 255 ;
if ( msec < 2 ) msec = 0 ;
c = msec ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
if ( demo . lasttype ! = type | | demo . lastto ! = to )
{
demo . lasttype = type ;
demo . lastto = to ;
switch ( demo . lasttype )
{
case dem_all :
c = dem_all ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
break ;
case dem_multiple :
c = dem_multiple ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
i = LittleLong ( demo . lastto ) ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & i , sizeof ( i ) ) ;
2004-08-23 00:15:46 +00:00
break ;
case dem_single :
case dem_stats :
c = demo . lasttype + ( demo . lastto < < 3 ) ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
break ;
default :
SV_MVDStop_f ( ) ;
Con_Printf ( " bad demo message type:%d " , type ) ;
return ;
}
} else {
c = dem_read ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
}
len = LittleLong ( msg - > cursize ) ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & len , 4 ) ;
DemoWrite ( msg - > data , msg - > cursize ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
DestFlush ( false ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_MVDWritePackets
Interpolates to get exact players position for current frame
and writes packets to the disk / memory
= = = = = = = = = = = = = = = = = = = =
*/
float adjustangle ( float current , float ideal , float fraction )
{
float move ;
move = ideal - current ;
if ( ideal > current )
{
if ( move > = 180 )
move = move - 360 ;
}
else
{
if ( move < = - 180 )
move = move + 360 ;
}
move * = fraction ;
return ( current + move ) ;
}
# define DF_ORIGIN 1
# define DF_ANGLES (1<<3)
# define DF_EFFECTS (1<<6)
# define DF_SKINNUM (1<<7)
# define DF_DEAD (1<<8)
# define DF_GIB (1<<9)
# define DF_WEAPONFRAME (1<<10)
# define DF_MODEL (1<<11)
2007-09-10 14:39:08 +00:00
qboolean SV_MVDWritePackets ( int num )
2004-08-23 00:15:46 +00:00
{
demo_frame_t * frame , * nextframe ;
demo_client_t * cl , * nextcl = NULL ;
int i , j , flags ;
qboolean valid ;
double time , playertime , nexttime ;
float f ;
vec3_t origin , angles ;
sizebuf_t msg ;
qbyte msg_buf [ MAX_QWMSGLEN ] ;
demoinfo_t * demoinfo ;
if ( ! sv . mvdrecording )
2007-09-10 14:39:08 +00:00
return false ;
2004-08-23 00:15:46 +00:00
2010-11-20 22:01:16 +00:00
msg . prim = svs . netprim ;
2004-08-23 00:15:46 +00:00
msg . data = msg_buf ;
msg . maxsize = sizeof ( msg_buf ) ;
if ( num > demo . parsecount - demo . lastwritten + 1 )
num = demo . parsecount - demo . lastwritten + 1 ;
// 'num' frames to write
for ( ; num ; num - - , demo . lastwritten + + )
{
frame = & demo . frames [ demo . lastwritten & DEMO_FRAMES_MASK ] ;
time = frame - > time ;
nextframe = frame ;
msg . cursize = 0 ;
demo . dbuf = & frame - > buf ;
// find two frames
// one before the exact time (time - msec) and one after,
// then we can interpolte exact position for current frame
for ( i = 0 , cl = frame - > clients , demoinfo = demo . info ; i < MAX_CLIENTS ; i + + , cl + + , demoinfo + + )
{
if ( cl - > parsecount ! = demo . lastwritten )
continue ; // not valid
nexttime = playertime = time - cl - > sec ;
for ( j = demo . lastwritten + 1 , valid = false ; nexttime < time & & j < demo . parsecount ; j + + )
{
nextframe = & demo . frames [ j & DEMO_FRAMES_MASK ] ;
nextcl = & nextframe - > clients [ i ] ;
if ( nextcl - > parsecount ! = j )
break ; // disconnected?
if ( nextcl - > fixangle )
break ; // respawned, or walked into teleport, do not interpolate!
if ( ! ( nextcl - > flags & DF_DEAD ) & & ( cl - > flags & DF_DEAD ) )
break ; // respawned, do not interpolate
nexttime = nextframe - > time - nextcl - > sec ;
if ( nexttime > = time )
{
// good, found what we were looking for
valid = true ;
break ;
}
}
if ( valid )
{
f = ( time - nexttime ) / ( nexttime - playertime ) ;
for ( j = 0 ; j < 3 ; j + + ) {
angles [ j ] = adjustangle ( cl - > info . angles [ j ] , nextcl - > info . angles [ j ] , 1.0 + f ) ;
origin [ j ] = nextcl - > info . origin [ j ] + f * ( nextcl - > info . origin [ j ] - cl - > info . origin [ j ] ) ;
}
} else {
VectorCopy ( cl - > info . origin , origin ) ;
VectorCopy ( cl - > info . angles , angles ) ;
}
// now write it to buf
flags = cl - > flags ;
2010-12-05 02:46:07 +00:00
if ( cl - > fixangle )
{
2004-08-23 00:15:46 +00:00
demo . fixangletime [ i ] = cl - > cmdtime ;
}
for ( j = 0 ; j < 3 ; j + + )
if ( origin [ j ] ! = demoinfo - > origin [ i ] )
flags | = DF_ORIGIN < < j ;
if ( cl - > fixangle | | demo . fixangletime [ i ] ! = cl - > cmdtime )
{
for ( j = 0 ; j < 3 ; j + + )
if ( angles [ j ] ! = demoinfo - > angles [ j ] )
flags | = DF_ANGLES < < j ;
}
if ( cl - > info . model ! = demoinfo - > model )
flags | = DF_MODEL ;
if ( cl - > info . effects ! = demoinfo - > effects )
flags | = DF_EFFECTS ;
if ( cl - > info . skinnum ! = demoinfo - > skinnum )
flags | = DF_SKINNUM ;
if ( cl - > info . weaponframe ! = demoinfo - > weaponframe )
flags | = DF_WEAPONFRAME ;
MSG_WriteByte ( & msg , svc_playerinfo ) ;
MSG_WriteByte ( & msg , i ) ;
MSG_WriteShort ( & msg , flags ) ;
MSG_WriteByte ( & msg , cl - > frame ) ;
for ( j = 0 ; j < 3 ; j + + )
if ( flags & ( DF_ORIGIN < < j ) )
MSG_WriteCoord ( & msg , origin [ j ] ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
for ( j = 0 ; j < 3 ; j + + )
if ( flags & ( DF_ANGLES < < j ) )
MSG_WriteAngle16 ( & msg , angles [ j ] ) ;
if ( flags & DF_MODEL )
MSG_WriteByte ( & msg , cl - > info . model ) ;
if ( flags & DF_SKINNUM )
MSG_WriteByte ( & msg , cl - > info . skinnum ) ;
if ( flags & DF_EFFECTS )
MSG_WriteByte ( & msg , cl - > info . effects ) ;
if ( flags & DF_WEAPONFRAME )
MSG_WriteByte ( & msg , cl - > info . weaponframe ) ;
VectorCopy ( cl - > info . origin , demoinfo - > origin ) ;
VectorCopy ( cl - > info . angles , demoinfo - > angles ) ;
demoinfo - > skinnum = cl - > info . skinnum ;
demoinfo - > effects = cl - > info . effects ;
demoinfo - > weaponframe = cl - > info . weaponframe ;
demoinfo - > model = cl - > info . model ;
}
SV_MVDWriteToDisk ( demo . lasttype , demo . lastto , ( float ) time ) ; // this goes first to reduce demo size a bit
SV_MVDWriteToDisk ( 0 , 0 , ( float ) time ) ; // now goes the rest
if ( msg . cursize )
SV_WriteMVDMessage ( & msg , dem_all , 0 , ( float ) time ) ;
2007-09-10 14:39:08 +00:00
/* The above functions can set this variable to false, but that's a really bad thing. Let's try to fix it. */
if ( ! sv . mvdrecording )
return false ;
2004-08-23 00:15:46 +00:00
}
if ( demo . lastwritten > demo . parsecount )
demo . lastwritten = demo . parsecount ;
demo . dbuf = & demo . frames [ demo . parsecount & DEMO_FRAMES_MASK ] . buf ;
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . maxsize = MAXSIZE + demo . dbuf - > bufsize ;
2007-09-10 14:39:08 +00:00
return true ;
2004-08-23 00:15:46 +00:00
}
2006-01-01 09:01:15 +00:00
extern char readable [ 256 ] ;
# define chartbl readable
2004-08-23 00:15:46 +00:00
void MVD_Init ( void )
{
# define MVDVARGROUP "Server MVD cvars"
2004-12-08 04:14:52 +00:00
Cvar_Register ( & sv_demofps , MVDVARGROUP ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & sv_demoPings , MVDVARGROUP ) ;
Cvar_Register ( & sv_demoUseCache , MVDVARGROUP ) ;
Cvar_Register ( & sv_demoCacheSize , MVDVARGROUP ) ;
Cvar_Register ( & sv_demoMaxSize , MVDVARGROUP ) ;
Cvar_Register ( & sv_demoMaxDirSize , MVDVARGROUP ) ;
2004-12-08 04:14:52 +00:00
Cvar_Register ( & sv_demoDir , MVDVARGROUP ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & sv_demoPrefix , MVDVARGROUP ) ;
Cvar_Register ( & sv_demoSuffix , MVDVARGROUP ) ;
2004-12-08 04:14:52 +00:00
Cvar_Register ( & sv_demotxt , MVDVARGROUP ) ;
2004-08-23 00:15:46 +00:00
Cvar_Register ( & sv_demoExtraNames , MVDVARGROUP ) ;
2011-08-16 04:12:15 +00:00
Cvar_Register ( & sv_demoExtensions , MVDVARGROUP ) ;
2004-12-10 21:34:42 +00:00
}
static char * SV_PrintTeams ( void )
{
char * teams [ MAX_CLIENTS ] ;
// char *p;
int i , j , numcl = 0 , numt = 0 ;
client_t * clients [ MAX_CLIENTS ] ;
char buf [ 2048 ] = { 0 } ;
extern cvar_t teamplay ;
// extern char chartbl2[];
// count teams and players
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
2004-08-23 00:15:46 +00:00
{
2004-12-10 21:34:42 +00:00
if ( svs . clients [ i ] . state ! = cs_spawned )
continue ;
if ( svs . clients [ i ] . spectator )
continue ;
clients [ numcl + + ] = & svs . clients [ i ] ;
for ( j = 0 ; j < numt ; j + + )
if ( ! strcmp ( Info_ValueForKey ( svs . clients [ i ] . userinfo , " team " ) , teams [ j ] ) )
break ;
if ( j ! = numt )
continue ;
teams [ numt + + ] = Info_ValueForKey ( svs . clients [ i ] . userinfo , " team " ) ;
2004-08-23 00:15:46 +00:00
}
2004-12-10 21:34:42 +00:00
// create output
2005-07-03 15:16:20 +00:00
2004-12-10 21:34:42 +00:00
if ( numcl = = 2 ) // duel
2004-08-23 00:15:46 +00:00
{
2006-03-06 01:41:09 +00:00
snprintf ( buf , sizeof ( buf ) , " team1 %s \n team2 %s \n " , clients [ 0 ] - > name , clients [ 1 ] - > name ) ;
2005-07-03 15:16:20 +00:00
}
2004-12-10 21:34:42 +00:00
else if ( ! teamplay . value ) // ffa
2005-07-03 15:16:20 +00:00
{
2006-03-06 01:41:09 +00:00
snprintf ( buf , sizeof ( buf ) , " players: \n " ) ;
2004-12-10 21:34:42 +00:00
for ( i = 0 ; i < numcl ; i + + )
2006-03-06 01:41:09 +00:00
snprintf ( buf + strlen ( buf ) , sizeof ( buf ) - strlen ( buf ) , " %s \n " , clients [ i ] - > name ) ;
2005-07-03 15:16:20 +00:00
}
else
2004-12-10 21:34:42 +00:00
{ // teamplay
2005-07-03 15:16:20 +00:00
for ( j = 0 ; j < numt ; j + + )
2004-12-10 21:34:42 +00:00
{
2006-03-06 01:41:09 +00:00
snprintf ( buf + strlen ( buf ) , sizeof ( buf ) - strlen ( buf ) , " team %s: \n " , teams [ j ] ) ;
2004-12-10 21:34:42 +00:00
for ( i = 0 ; i < numcl ; i + + )
if ( ! strcmp ( Info_ValueForKey ( clients [ i ] - > userinfo , " team " ) , teams [ j ] ) )
2006-03-06 01:41:09 +00:00
snprintf ( buf + strlen ( buf ) , sizeof ( buf ) - strlen ( buf ) , " %s \n " , clients [ i ] - > name ) ;
2004-12-10 21:34:42 +00:00
}
2004-08-23 00:15:46 +00:00
}
2004-12-10 21:34:42 +00:00
if ( ! numcl )
return " \n " ;
// for (p = buf; *p; p++) *p = chartbl2[(qbyte)*p];
return va ( " %s " , buf ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_InitRecord
= = = = = = = = = = = = = = = = = = = =
*/
2004-12-10 21:34:42 +00:00
mvddest_t * SV_InitRecordFile ( char * name )
2004-08-23 00:15:46 +00:00
{
2004-12-10 21:34:42 +00:00
char * s ;
mvddest_t * dst ;
2009-04-01 22:03:56 +00:00
vfsfile_t * file ;
2004-12-10 21:34:42 +00:00
char path [ MAX_OSPATH ] ;
2009-04-01 22:03:56 +00:00
file = FS_OpenVFS ( name , " wb " , FS_GAMEONLY ) ;
2004-12-10 21:34:42 +00:00
if ( ! file )
{
Con_Printf ( " ERROR: couldn't open \" %s \" \n " , name ) ;
return NULL ;
}
dst = Z_Malloc ( sizeof ( mvddest_t ) ) ;
if ( ! sv_demoUseCache . value )
2004-08-23 00:15:46 +00:00
{
2004-12-10 21:34:42 +00:00
dst - > desttype = DEST_FILE ;
dst - > file = file ;
dst - > maxcachesize = 0 ;
}
2005-07-03 15:16:20 +00:00
else
2004-08-23 00:15:46 +00:00
{
2004-12-10 21:34:42 +00:00
dst - > desttype = DEST_BUFFEREDFILE ;
dst - > file = file ;
2011-08-16 04:12:15 +00:00
if ( sv_demoCacheSize . ival < 0x8000 )
dst - > maxcachesize = 0x8000 ;
else
dst - > maxcachesize = sv_demoCacheSize . ival ;
2004-12-10 21:34:42 +00:00
dst - > cache = BZ_Malloc ( dst - > maxcachesize ) ;
2004-08-23 00:15:46 +00:00
}
2007-05-28 02:25:32 +00:00
dst - > droponmapchange = true ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
s = name + strlen ( name ) ;
while ( * s ! = ' / ' ) s - - ;
Q_strncpyz ( dst - > name , s + 1 , sizeof ( dst - > name ) ) ;
Q_strncpyz ( dst - > path , sv_demoDir . string , sizeof ( dst - > path ) ) ;
2005-07-03 15:16:20 +00:00
2004-12-10 21:34:42 +00:00
if ( ! * demo . path )
Q_strncpyz ( demo . path , " . " , MAX_OSPATH ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
SV_BroadcastPrintf ( PRINT_CHAT , " Server starts recording (%s): \n %s \n " , ( dst - > desttype = = DEST_BUFFEREDFILE ) ? " memory " : " disk " , name ) ;
2011-09-03 03:49:43 +00:00
Cvar_ForceSet ( Cvar_Get ( " serverdemo " , " " , CVAR_NOSET , " " ) , SV_Demo_CurrentOutput ( ) ) ;
2004-12-10 21:34:42 +00:00
Q_strncpyz ( path , name , MAX_OSPATH ) ;
Q_strncpyz ( path + strlen ( path ) - 3 , " txt " , MAX_OSPATH - strlen ( path ) + 3 ) ;
if ( sv_demotxt . value )
2004-12-08 04:14:52 +00:00
{
2009-04-01 22:03:56 +00:00
vfsfile_t * f ;
2004-12-10 21:34:42 +00:00
2009-04-01 22:03:56 +00:00
f = FS_OpenVFS ( path , " wt " , FS_GAMEONLY ) ;
2004-12-10 21:34:42 +00:00
if ( f ! = NULL )
{
char buf [ 2000 ] ;
date_t date ;
2011-12-24 04:05:28 +00:00
COM_TimeOfDay ( & date ) ;
2004-12-10 21:34:42 +00:00
2006-03-06 01:41:09 +00:00
snprintf ( buf , sizeof ( buf ) , " date %s \n map %s \n teamplay %d \n deathmatch %d \n timelimit %d \n %s " , date . str , sv . name , ( int ) teamplay . value , ( int ) deathmatch . value , ( int ) timelimit . value , SV_PrintTeams ( ) ) ;
2009-04-01 22:03:56 +00:00
VFS_WRITE ( f , buf , strlen ( buf ) ) ;
VFS_FLUSH ( f ) ;
VFS_CLOSE ( f ) ;
2004-12-10 21:34:42 +00:00
}
2004-12-08 04:14:52 +00:00
}
2005-07-03 15:16:20 +00:00
else
2009-04-01 22:03:56 +00:00
FS_Remove ( path , FS_GAMEONLY ) ;
2004-12-08 04:14:52 +00:00
2010-11-20 22:01:16 +00:00
FS_FlushFSHash ( ) ;
2004-12-08 04:14:52 +00:00
2004-12-10 21:34:42 +00:00
return dst ;
}
2011-09-03 03:49:43 +00:00
char * SV_Demo_CurrentOutput ( void )
{
mvddest_t * d ;
for ( d = demo . dest ; d ; d = d - > nextdest )
{
if ( d - > desttype = = DEST_FILE | | d - > desttype = = DEST_BUFFEREDFILE )
return d - > name ;
}
return " QTV " ;
}
2004-12-10 21:34:42 +00:00
mvddest_t * SV_InitStream ( int socket )
{
mvddest_t * dst ;
dst = Z_Malloc ( sizeof ( mvddest_t ) ) ;
dst - > desttype = DEST_STREAM ;
dst - > socket = socket ;
2005-09-06 01:19:26 +00:00
dst - > maxcachesize = 0x8000 ; //is this too small?
2004-12-10 21:34:42 +00:00
dst - > cache = BZ_Malloc ( dst - > maxcachesize ) ;
2007-05-28 02:25:32 +00:00
dst - > droponmapchange = false ;
2004-12-10 21:34:42 +00:00
2005-10-07 02:03:17 +00:00
SV_BroadcastPrintf ( PRINT_CHAT , " Smile, you're on QTV! \n " ) ;
2004-12-10 21:34:42 +00:00
return dst ;
2004-12-08 04:14:52 +00:00
}
2007-05-28 02:25:32 +00:00
mvdpendingdest_t * SV_MVD_InitPendingStream ( int socket , char * ip )
2006-09-17 00:59:22 +00:00
{
mvdpendingdest_t * dst ;
int i ;
dst = Z_Malloc ( sizeof ( mvdpendingdest_t ) ) ;
dst - > socket = socket ;
Q_strncpyz ( dst - > challenge , ip , sizeof ( dst - > challenge ) ) ;
for ( i = strlen ( dst - > challenge ) ; i < sizeof ( dst - > challenge ) - 1 ; i + + )
dst - > challenge [ i ] = rand ( ) % ( 127 - 33 ) + 33 ; //generate a random challenge
dst - > nextdest = demo . pendingdest ;
demo . pendingdest = dst ;
2007-05-28 02:25:32 +00:00
return dst ;
2006-09-17 00:59:22 +00:00
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = = =
SV_Stop
stop recording a demo
= = = = = = = = = = = = = = = = = = = =
*/
2006-11-03 15:53:04 +00:00
void SV_MVDStop ( int reason , qboolean mvdonly )
2004-08-23 00:15:46 +00:00
{
2006-11-03 15:53:04 +00:00
int numclosed ;
2004-08-23 00:15:46 +00:00
if ( ! sv . mvdrecording )
{
Con_Printf ( " Not recording a demo. \n " ) ;
return ;
}
2006-11-03 16:03:15 +00:00
if ( reason = = 2 | | reason = = 3 )
2004-08-23 00:15:46 +00:00
{
2006-11-03 15:53:04 +00:00
DestCloseAllFlush ( true , mvdonly ) ;
2004-08-23 00:15:46 +00:00
// stop and remove
2004-12-10 21:34:42 +00:00
if ( ! demo . dest )
sv . mvdrecording = false ;
2004-08-23 00:15:46 +00:00
2006-11-03 16:03:15 +00:00
if ( reason = = 3 )
SV_BroadcastPrintf ( PRINT_CHAT , " QTV disconnected \n " ) ;
else
SV_BroadcastPrintf ( PRINT_CHAT , " Server recording canceled, demo removed \n " ) ;
2004-08-23 00:15:46 +00:00
2004-11-29 01:21:00 +00:00
Cvar_ForceSet ( Cvar_Get ( " serverdemo " , " " , CVAR_NOSET , " " ) , " " ) ;
2004-08-23 00:15:46 +00:00
return ;
}
// write a disconnect message to the demo file
// clearup to be sure message will fit
2010-11-20 22:01:16 +00:00
demo . dbuf - > sb . cursize = 0 ;
2004-08-23 00:15:46 +00:00
demo . dbuf - > h = NULL ;
demo . dbuf - > bufsize = 0 ;
MVDWrite_Begin ( dem_all , 0 , 2 + strlen ( " EndOfDemo " ) ) ;
2010-11-20 22:01:16 +00:00
MSG_WriteByte ( & demo . dbuf - > sb , svc_disconnect ) ;
MSG_WriteString ( & demo . dbuf - > sb , " EndOfDemo " ) ;
2004-08-23 00:15:46 +00:00
SV_MVDWritePackets ( demo . parsecount - demo . lastwritten + 1 ) ;
// finish up
2004-11-29 01:21:00 +00:00
2006-11-03 15:53:04 +00:00
numclosed = DestCloseAllFlush ( false , mvdonly ) ;
2004-11-29 01:21:00 +00:00
2006-11-03 15:53:04 +00:00
if ( ! demo . dest )
sv . mvdrecording = false ;
if ( numclosed )
{
if ( ! reason )
SV_BroadcastPrintf ( PRINT_CHAT , " Server recording completed \n " ) ;
else
SV_BroadcastPrintf ( PRINT_CHAT , " Server recording stoped \n Max demo size exceeded \n " ) ;
}
2004-08-23 00:15:46 +00:00
2004-11-29 01:21:00 +00:00
Cvar_ForceSet ( Cvar_Get ( " serverdemo " , " " , CVAR_NOSET , " " ) , " " ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_Stop_f
= = = = = = = = = = = = = = = = = = = =
*/
void SV_MVDStop_f ( void )
{
2006-11-03 15:53:04 +00:00
SV_MVDStop ( 0 , true ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_Cancel_f
Stops recording , and removes the demo
= = = = = = = = = = = = = = = = = = = =
*/
void SV_MVD_Cancel_f ( void )
{
2006-11-03 15:53:04 +00:00
SV_MVDStop ( 2 , true ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_WriteMVDMessage
Dumps the current net message , prefixed by the length and view angles
= = = = = = = = = = = = = = = = = = = =
*/
void SV_WriteRecordMVDMessage ( sizebuf_t * msg , int seq )
{
int len ;
qbyte c ;
if ( ! sv . mvdrecording )
return ;
2004-12-10 21:34:42 +00:00
if ( ! msg - > cursize )
return ;
2004-08-23 00:15:46 +00:00
c = 0 ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
c = dem_read ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
len = LittleLong ( msg - > cursize ) ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & len , 4 ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
DemoWrite ( msg - > data , msg - > cursize ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
DestFlush ( false ) ;
2004-08-23 00:15:46 +00:00
}
void SV_WriteSetMVDMessage ( void )
{
int len ;
qbyte c ;
//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
if ( ! sv . mvdrecording )
return ;
c = 0 ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
c = dem_set ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & c , sizeof ( c ) ) ;
2004-08-23 00:15:46 +00:00
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
len = LittleLong ( 0 ) ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & len , 4 ) ;
2004-08-23 00:15:46 +00:00
len = LittleLong ( 0 ) ;
2004-12-10 21:34:42 +00:00
DemoWrite ( & len , 4 ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
DestFlush ( false ) ;
2004-08-23 00:15:46 +00:00
}
2006-11-03 15:53:04 +00:00
void SV_MVD_SendInitialGamestate ( mvddest_t * dest ) ;
2004-12-10 21:34:42 +00:00
static qboolean SV_MVD_Record ( mvddest_t * dest )
2004-08-23 00:15:46 +00:00
{
2006-11-03 15:53:04 +00:00
/* sizebuf_t buf;
2004-08-23 00:15:46 +00:00
char buf_data [ MAX_QWMSGLEN ] ;
int n , i ;
2004-12-10 21:34:42 +00:00
char * s , info [ MAX_INFO_STRING ] ;
2004-11-29 01:21:00 +00:00
2004-08-23 00:15:46 +00:00
client_t * player ;
char * gamedir ;
int seq = 1 ;
2006-11-03 15:53:04 +00:00
*/
int i ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
if ( ! dest )
return false ;
2004-11-29 01:21:00 +00:00
2004-12-10 21:34:42 +00:00
DestFlush ( true ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
if ( ! sv . mvdrecording )
2004-08-23 00:15:46 +00:00
{
2004-12-10 21:34:42 +00:00
memset ( & demo , 0 , sizeof ( demo ) ) ;
2006-02-17 02:51:59 +00:00
demo . recorder . frameunion . frames = demo_frames ;
2005-09-08 01:44:31 +00:00
demo . recorder . protocol = SCP_QUAKEWORLD ;
2010-11-20 22:01:16 +00:00
demo . recorder . netchan . netprim = sv . datagram . prim ;
2004-12-10 21:34:42 +00:00
for ( i = 0 ; i < UPDATE_BACKUP ; i + + )
2004-11-29 01:21:00 +00:00
{
2006-02-17 02:51:59 +00:00
demo . recorder . frameunion . frames [ i ] . entities . max_entities = MAX_MVDPACKET_ENTITIES ;
demo . recorder . frameunion . frames [ i ] . entities . entities = demo_entities [ i ] ;
2004-11-29 01:21:00 +00:00
}
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
MVDBuffer_Init ( & demo . dbuffer , demo . buffer , sizeof ( demo . buffer ) ) ;
MVDSetMsgBuf ( NULL , & demo . frames [ 0 ] . buf ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
demo . datagram . maxsize = sizeof ( demo . datagram_data ) ;
demo . datagram . data = demo . datagram_data ;
2010-11-20 22:01:16 +00:00
demo . datagram . prim = demo . recorder . netchan . netprim ;
2011-08-16 04:12:15 +00:00
if ( sv_demoExtensions . ival )
{
demo . recorder . fteprotocolextensions = PEXT_CSQC | PEXT_COLOURMOD | PEXT_DPFLAGS | PEXT_CUSTOMTEMPEFFECTS | PEXT_ENTITYDBL | PEXT_ENTITYDBL2 | PEXT_FATNESS | PEXT_HEXEN2 | PEXT_HULLSIZE | PEXT_LIGHTSTYLECOL | PEXT_MODELDBL | PEXT_SCALE | PEXT_SETATTACHMENT | PEXT_SETVIEW | PEXT_SOUNDDBL | PEXT_SPAWNSTATIC2 | PEXT_TRANS | PEXT_VIEW2 ;
demo . recorder . fteprotocolextensions2 = PEXT2_VOICECHAT | PEXT2_SETANGLEDELTA | PEXT2_PRYDONCURSOR ;
/*assume that all playback will be done with a valid csprogs that can correctly decode*/
demo . recorder . csqcactive = true ;
/*enable these, because we might as well (stat ones are always useful)*/
demo . recorder . zquake_extensions = Z_EXT_PM_TYPE | Z_EXT_PM_TYPE_NEW | Z_EXT_VIEWHEIGHT | Z_EXT_SERVERTIME | Z_EXT_PITCHLIMITS | Z_EXT_JOIN_OBSERVE | Z_EXT_VWEP ;
}
2011-09-21 06:42:50 +00:00
demo . recorder . max_net_ents = 512 ;
if ( demo . recorder . fteprotocolextensions & PEXT_ENTITYDBL )
demo . recorder . max_net_ents + = 512 ;
if ( demo . recorder . fteprotocolextensions & PEXT_ENTITYDBL2 )
demo . recorder . max_net_ents + = 1024 ;
if ( demo . recorder . fteprotocolextensions & PEXT_MODELDBL )
demo . recorder . maxmodels = MAX_MODELS ;
else
demo . recorder . maxmodels = 256 ;
2004-11-29 01:21:00 +00:00
}
2005-10-07 02:03:17 +00:00
// else
// SV_WriteRecordMVDMessage(&buf, dem_read);
2004-12-08 04:14:52 +00:00
2004-12-10 21:34:42 +00:00
dest - > nextdest = demo . dest ;
demo . dest = dest ;
2004-08-23 00:15:46 +00:00
2006-11-03 15:53:04 +00:00
SV_MVD_SendInitialGamestate ( dest ) ;
return true ;
}
void SV_MVD_SendInitialGamestate ( mvddest_t * dest )
{
sizebuf_t buf ;
char buf_data [ MAX_QWMSGLEN ] ;
int n , i ;
2011-06-05 01:36:14 +00:00
char * s ;
2006-11-03 15:53:04 +00:00
client_t * player ;
char * gamedir ;
int seq = 1 ;
if ( ! demo . dest )
return ;
sv . mvdrecording = true ;
demo . pingtime = demo . time = sv . time ;
2004-12-10 21:34:42 +00:00
singledest = dest ;
2004-08-23 00:15:46 +00:00
/*-------------------------------------------------*/
// serverdata
// send the info about the new client to all connected clients
memset ( & buf , 0 , sizeof ( buf ) ) ;
buf . data = buf_data ;
buf . maxsize = sizeof ( buf_data ) ;
2010-11-20 22:01:16 +00:00
buf . prim = svs . netprim ;
2004-08-23 00:15:46 +00:00
// send the serverdata
gamedir = Info_ValueForKey ( svs . info , " *gamedir " ) ;
if ( ! gamedir [ 0 ] )
2011-08-16 04:12:15 +00:00
gamedir = FS_GetGamedir ( ) ;
/*the gamedir shouldn't be fte - that should be hidden from clients*/
if ( ! strncmp ( gamedir , " fte " , 3 ) )
2004-08-23 00:15:46 +00:00
gamedir = " qw " ;
MSG_WriteByte ( & buf , svc_serverdata ) ;
2011-08-16 04:12:15 +00:00
//fix up extensions to match sv_bigcoords correctly. sorry for old clients not working.
if ( buf . prim . coordsize = = 4 )
demo . recorder . fteprotocolextensions | = PEXT_FLOATCOORDS ;
else
demo . recorder . fteprotocolextensions & = ~ PEXT_FLOATCOORDS ;
if ( demo . recorder . fteprotocolextensions )
2004-12-10 21:34:42 +00:00
{
2011-08-16 04:12:15 +00:00
MSG_WriteLong ( & buf , PROTOCOL_VERSION_FTE ) ;
MSG_WriteLong ( & buf , demo . recorder . fteprotocolextensions ) ;
}
if ( demo . recorder . fteprotocolextensions2 )
{
MSG_WriteLong ( & buf , PROTOCOL_VERSION_FTE2 ) ;
MSG_WriteLong ( & buf , demo . recorder . fteprotocolextensions2 ) ;
2004-12-10 21:34:42 +00:00
}
2008-04-12 23:24:19 +00:00
MSG_WriteLong ( & buf , PROTOCOL_VERSION_QW ) ;
2004-08-23 00:15:46 +00:00
MSG_WriteLong ( & buf , svs . spawncount ) ;
MSG_WriteString ( & buf , gamedir ) ;
2012-02-12 05:18:31 +00:00
if ( demo . recorder . fteprotocolextensions2 & PEXT2_MAXPLAYERS )
MSG_WriteByte ( & buf , MAX_CLIENTS ) ;
2004-08-23 00:15:46 +00:00
MSG_WriteFloat ( & buf , sv . time ) ;
// send full levelname
MSG_WriteString ( & buf , sv . mapname ) ;
// send the movevars
MSG_WriteFloat ( & buf , movevars . gravity ) ;
MSG_WriteFloat ( & buf , movevars . stopspeed ) ;
MSG_WriteFloat ( & buf , movevars . maxspeed ) ;
MSG_WriteFloat ( & buf , movevars . spectatormaxspeed ) ;
MSG_WriteFloat ( & buf , movevars . accelerate ) ;
MSG_WriteFloat ( & buf , movevars . airaccelerate ) ;
MSG_WriteFloat ( & buf , movevars . wateraccelerate ) ;
MSG_WriteFloat ( & buf , movevars . friction ) ;
MSG_WriteFloat ( & buf , movevars . waterfriction ) ;
MSG_WriteFloat ( & buf , movevars . entgravity ) ;
// send music
MSG_WriteByte ( & buf , svc_cdtrack ) ;
MSG_WriteByte ( & buf , 0 ) ; // none in demos
// send server info string
MSG_WriteByte ( & buf , svc_stufftext ) ;
MSG_WriteString ( & buf , va ( " fullserverinfo \" %s \" \n " , svs . info ) ) ;
// flush packet
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
SZ_Clear ( & buf ) ;
// soundlist
2011-08-16 04:12:15 +00:00
MSG_WriteByte ( & buf , svc_soundlist ) ; /*FIXME: soundlist2*/
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( & buf , 0 ) ;
n = 0 ;
2006-02-17 02:51:59 +00:00
s = sv . strings . sound_precache [ n + 1 ] ;
2004-08-23 00:15:46 +00:00
while ( * s )
{
MSG_WriteString ( & buf , s ) ;
if ( buf . cursize > MAX_QWMSGLEN / 2 )
{
MSG_WriteByte ( & buf , 0 ) ;
MSG_WriteByte ( & buf , n ) ;
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( & buf , svc_soundlist ) ;
MSG_WriteByte ( & buf , n + 1 ) ;
}
n + + ;
2006-02-17 02:51:59 +00:00
s = sv . strings . sound_precache [ n + 1 ] ;
2004-08-23 00:15:46 +00:00
}
if ( buf . cursize )
{
MSG_WriteByte ( & buf , 0 ) ;
MSG_WriteByte ( & buf , 0 ) ;
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
}
// modellist
2011-08-16 04:12:15 +00:00
MSG_WriteByte ( & buf , svc_modellist ) ; /*FIXME: modellist2*/
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( & buf , 0 ) ;
n = 0 ;
2006-02-17 02:51:59 +00:00
s = sv . strings . model_precache [ n + 1 ] ;
2005-07-01 19:23:00 +00:00
while ( s )
2004-08-23 00:15:46 +00:00
{
MSG_WriteString ( & buf , s ) ;
if ( buf . cursize > MAX_QWMSGLEN / 2 )
{
MSG_WriteByte ( & buf , 0 ) ;
MSG_WriteByte ( & buf , n ) ;
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( & buf , svc_modellist ) ;
MSG_WriteByte ( & buf , n + 1 ) ;
}
n + + ;
2006-02-17 02:51:59 +00:00
s = sv . strings . model_precache [ n + 1 ] ;
2004-08-23 00:15:46 +00:00
}
if ( buf . cursize )
{
MSG_WriteByte ( & buf , 0 ) ;
MSG_WriteByte ( & buf , 0 ) ;
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
}
// baselines
{
entity_state_t from ;
edict_t * ent ;
entity_state_t * state ;
memset ( & from , 0 , sizeof ( from ) ) ;
2009-11-04 21:16:50 +00:00
for ( n = 0 ; n < sv . world . num_edicts ; n + + )
2004-08-23 00:15:46 +00:00
{
ent = EDICT_NUM ( svprogfuncs , n ) ;
state = & ent - > baseline ;
if ( ! state - > number | | ! state - > modelindex )
{ //ent doesn't have a baseline
continue ;
}
if ( ! ent )
{
MSG_WriteByte ( & buf , svc_spawnbaseline ) ;
MSG_WriteShort ( & buf , n ) ;
MSG_WriteByte ( & buf , 0 ) ;
MSG_WriteByte ( & buf , 0 ) ;
MSG_WriteByte ( & buf , 0 ) ;
MSG_WriteByte ( & buf , 0 ) ;
for ( i = 0 ; i < 3 ; i + + )
{
MSG_WriteCoord ( & buf , 0 ) ;
MSG_WriteAngle ( & buf , 0 ) ;
}
}
2011-08-16 04:12:15 +00:00
else if ( demo . recorder . fteprotocolextensions & PEXT_SPAWNSTATIC2 )
2004-08-23 00:15:46 +00:00
{
2011-08-16 04:12:15 +00:00
MSG_WriteByte ( & buf , svcfte_spawnbaseline2 ) ;
2012-02-12 05:18:31 +00:00
SVQW_WriteDelta ( & from , state , & buf , true , demo . recorder . fteprotocolextensions ) ;
2011-08-16 04:12:15 +00:00
}
2004-08-23 00:15:46 +00:00
else
{
MSG_WriteByte ( & buf , svc_spawnbaseline ) ;
MSG_WriteShort ( & buf , n ) ;
MSG_WriteByte ( & buf , state - > modelindex & 255 ) ;
MSG_WriteByte ( & buf , state - > frame ) ;
MSG_WriteByte ( & buf , ( int ) state - > colormap ) ;
MSG_WriteByte ( & buf , ( int ) state - > skinnum ) ;
for ( i = 0 ; i < 3 ; i + + )
{
MSG_WriteCoord ( & buf , state - > origin [ i ] ) ;
MSG_WriteAngle ( & buf , state - > angles [ i ] ) ;
}
}
if ( buf . cursize > MAX_QWMSGLEN / 2 )
{
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
}
}
}
//prespawn
for ( n = 0 ; n < sv . num_signon_buffers ; n + + )
{
2005-09-06 01:19:26 +00:00
if ( buf . cursize + sv . signon_buffer_size [ n ] > MAX_QWMSGLEN / 2 )
2004-08-23 00:15:46 +00:00
{
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
}
2005-09-06 01:19:26 +00:00
SZ_Write ( & buf ,
sv . signon_buffers [ n ] ,
sv . signon_buffer_size [ n ] ) ;
}
if ( buf . cursize > MAX_QWMSGLEN / 2 )
{
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
}
MSG_WriteByte ( & buf , svc_stufftext ) ;
MSG_WriteString ( & buf , va ( " cmd spawn %i \n " , svs . spawncount ) ) ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
if ( buf . cursize )
{
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
}
// send current status of all other players
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
{
player = svs . clients + i ;
2011-06-05 01:36:14 +00:00
SV_MVD_FullClientUpdate ( & buf , player ) ;
2004-08-23 00:15:46 +00:00
if ( buf . cursize > MAX_QWMSGLEN / 2 )
{
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
2005-07-03 15:16:20 +00:00
SZ_Clear ( & buf ) ;
2004-08-23 00:15:46 +00:00
}
}
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
// send all current light styles
2011-08-16 04:12:15 +00:00
for ( i = 0 ; i < MAX_LIGHTSTYLES ; i + + )
2004-08-23 00:15:46 +00:00
{
2011-08-16 04:12:15 +00:00
if ( i > = MAX_STANDARDLIGHTSTYLES )
if ( ! sv . strings . lightstyles [ i ] )
continue ;
# ifdef PEXT_LIGHTSTYLECOL
if ( ( demo . recorder . fteprotocolextensions & PEXT_LIGHTSTYLECOL ) & & sv . strings . lightstylecolours [ i ] ! = 7 )
{
MSG_WriteByte ( & buf , svcfte_lightstylecol ) ;
MSG_WriteByte ( & buf , ( unsigned char ) i ) ;
MSG_WriteByte ( & buf , sv . strings . lightstylecolours [ i ] ) ;
MSG_WriteString ( & buf , sv . strings . lightstyles [ i ] ) ;
}
else
# endif
{
MSG_WriteByte ( & buf , svc_lightstyle ) ;
MSG_WriteByte ( & buf , ( unsigned char ) i ) ;
MSG_WriteString ( & buf , sv . strings . lightstyles [ i ] ) ;
}
2004-08-23 00:15:46 +00:00
}
// get the client to check and download skins
// when that is completed, a begin command will be issued
MSG_WriteByte ( & buf , svc_stufftext ) ;
2005-09-06 01:19:26 +00:00
MSG_WriteString ( & buf , " skins \n " ) ;
2004-08-23 00:15:46 +00:00
SV_WriteRecordMVDMessage ( & buf , seq + + ) ;
SV_WriteSetMVDMessage ( ) ;
2004-12-10 21:34:42 +00:00
singledest = NULL ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_CleanName
Cleans the demo name , removes restricted chars , makes name lowercase
= = = = = = = = = = = = = = = = = = = =
*/
char * SV_CleanName ( unsigned char * name )
{
static char text [ 1024 ] ;
char * out = text ;
* out = chartbl [ * name + + ] ;
while ( * name & & out - text < sizeof ( text ) )
if ( * out = = ' _ ' & & chartbl [ * name ] = = ' _ ' )
name + + ;
else * + + out = chartbl [ * name + + ] ;
* + + out = 0 ;
return text ;
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_Record_f
record < demoname >
= = = = = = = = = = = = = = = = = = = =
*/
void SV_MVD_Record_f ( void )
{
int c ;
char name [ MAX_OSPATH + MAX_MVD_NAME ] ;
char newname [ MAX_MVD_NAME ] ;
2008-06-01 22:06:22 +00:00
dir_t * dir ;
2004-08-23 00:15:46 +00:00
c = Cmd_Argc ( ) ;
if ( c ! = 2 )
{
2005-07-25 13:36:42 +00:00
Con_Printf ( " mvdrecord <demoname> \n " ) ;
2004-08-23 00:15:46 +00:00
return ;
}
2004-12-10 21:34:42 +00:00
if ( sv . state ! = ss_active ) {
2004-08-23 00:15:46 +00:00
Con_Printf ( " Not active yet. \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
dir = Sys_listdir ( sv_demoDir . string , " .* " , SORT_NO ) ;
2008-06-01 22:06:22 +00:00
if ( sv_demoMaxDirSize . value & & dir - > size > sv_demoMaxDirSize . value * 1024 )
2004-08-23 00:15:46 +00:00
{
Con_Printf ( " insufficient directory space, increase sv_demoMaxDirSize \n " ) ;
2008-06-01 22:06:22 +00:00
Sys_freedir ( dir ) ;
2004-08-23 00:15:46 +00:00
return ;
}
2008-06-01 22:06:22 +00:00
Sys_freedir ( dir ) ;
dir = NULL ;
2004-08-23 00:15:46 +00:00
Q_strncpyz ( newname , va ( " %s%s " , sv_demoPrefix . string , SV_CleanName ( Cmd_Argv ( 1 ) ) ) ,
sizeof ( newname ) - strlen ( sv_demoSuffix . string ) - 5 ) ;
Q_strncatz ( newname , sv_demoSuffix . string , MAX_MVD_NAME ) ;
2009-04-01 22:03:56 +00:00
snprintf ( name , MAX_OSPATH + MAX_MVD_NAME , " %s/%s " , sv_demoDir . string , newname ) ;
2004-08-23 00:15:46 +00:00
2006-03-11 03:12:10 +00:00
COM_StripExtension ( name , name , sizeof ( name ) ) ;
COM_DefaultExtension ( name , " .mvd " , sizeof ( name ) ) ;
2010-07-18 08:42:59 +00:00
FS_CreatePath ( name , FS_GAMEONLY ) ;
2004-08-23 00:15:46 +00:00
2005-08-03 23:14:59 +00:00
//
// open the demo file and start recording
//
2004-12-10 21:34:42 +00:00
SV_MVD_Record ( SV_InitRecordFile ( name ) ) ;
2004-08-23 00:15:46 +00:00
}
2007-05-28 02:25:32 +00:00
void SV_MVD_QTVReverse_f ( void )
{
2012-04-09 19:12:12 +00:00
# ifndef HAVE_TCP
Con_Printf ( " %s is not supported in this build \n " , Cmd_Argv ( 0 ) ) ;
# else
2007-05-28 02:25:32 +00:00
char * ip ;
if ( sv . state ! = ss_active )
{
Con_Printf ( " Server is not running \n " ) ;
return ;
}
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( " %s ip:port \n " , Cmd_Argv ( 0 ) ) ;
return ;
}
ip = Cmd_Argv ( 1 ) ;
{
char * data ;
int sock ;
struct sockaddr_in local ;
struct sockaddr_qstorage remote ;
// int fromlen;
unsigned int nonblocking = true ;
if ( ! NET_StringToSockaddr ( ip , & remote ) )
{
Con_Printf ( " qtvreverse: failed to resolve address \n " ) ;
return ;
}
local . sin_family = AF_INET ;
local . sin_addr . s_addr = INADDR_ANY ;
local . sin_port = 0 ;
2007-11-04 08:21:19 +00:00
if ( ( sock = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ) ) = = INVALID_SOCKET )
2007-05-28 02:25:32 +00:00
{
Con_Printf ( " qtvreverse: socket: %s \n " , strerror ( qerrno ) ) ;
return ;
}
2007-11-04 08:21:19 +00:00
if ( bind ( sock , ( void * ) & local , sizeof ( local ) ) = = INVALID_SOCKET )
2007-05-28 02:25:32 +00:00
{
closesocket ( sock ) ;
Con_Printf ( " qtvreverse: bind: %s \n " , strerror ( qerrno ) ) ;
return ;
}
2007-11-04 08:21:19 +00:00
if ( connect ( sock , ( void * ) & remote , sizeof ( remote ) ) = = INVALID_SOCKET )
2007-05-28 02:25:32 +00:00
{
closesocket ( sock ) ;
Con_Printf ( " qtvreverse: connect: %s \n " , strerror ( qerrno ) ) ;
return ;
}
2011-05-19 13:34:07 +00:00
if ( ioctlsocket ( sock , FIONBIO , ( u_long * ) & nonblocking ) = = INVALID_SOCKET )
2007-05-28 02:25:32 +00:00
{
closesocket ( sock ) ;
Con_Printf ( " qtvreverse: ioctl FIONBIO: %s \n " , strerror ( qerrno ) ) ;
return ;
}
data = " QTV \n "
" REVERSE \n "
" \n " ;
2007-11-04 08:21:19 +00:00
if ( send ( sock , data , strlen ( data ) , 0 ) = = INVALID_SOCKET )
2007-05-28 02:25:32 +00:00
{
closesocket ( sock ) ;
Con_Printf ( " qtvreverse: send: %s \n " , strerror ( qerrno ) ) ;
return ;
}
SV_MVD_InitPendingStream ( sock , ip ) - > isreverse = true ;
}
//SV_MVD_Record (dest);
2012-04-09 19:12:12 +00:00
# endif
2007-05-28 02:25:32 +00:00
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = = =
SV_EasyRecord_f
easyrecord [ demoname ]
= = = = = = = = = = = = = = = = = = = =
*/
int Dem_CountPlayers ( )
{
int i , count ;
count = 0 ;
for ( i = 0 ; i < MAX_CLIENTS ; i + + ) {
if ( svs . clients [ i ] . name [ 0 ] & & ! svs . clients [ i ] . spectator )
count + + ;
}
return count ;
}
char * Dem_Team ( int num )
{
int i ;
static char * lastteam [ 2 ] ;
qboolean first = true ;
client_t * client ;
static int index = 0 ;
index = 1 - index ;
for ( i = 0 , client = svs . clients ; num & & i < MAX_CLIENTS ; i + + , client + + )
{
if ( ! client - > name [ 0 ] | | client - > spectator )
continue ;
if ( first | | strcmp ( lastteam [ index ] , Info_ValueForKey ( client - > userinfo , " team " ) ) )
{
first = false ;
num - - ;
lastteam [ index ] = Info_ValueForKey ( client - > userinfo , " team " ) ;
}
}
if ( num )
return " " ;
return lastteam [ index ] ;
}
char * Dem_PlayerName ( int num )
{
int i ;
client_t * client ;
for ( i = 0 , client = svs . clients ; i < MAX_CLIENTS ; i + + , client + + )
{
if ( ! client - > name [ 0 ] | | client - > spectator )
continue ;
if ( ! - - num )
return client - > name ;
}
return " " ;
}
// -> scream
char * Dem_PlayerNameTeam ( char * t )
{
int i ;
client_t * client ;
static char n [ 1024 ] ;
int sep ;
n [ 0 ] = 0 ;
sep = 0 ;
for ( i = 0 , client = svs . clients ; i < MAX_CLIENTS ; i + + , client + + )
{
if ( ! client - > name [ 0 ] | | client - > spectator )
continue ;
2005-07-03 15:16:20 +00:00
if ( strcmp ( t , Info_ValueForKey ( client - > userinfo , " team " ) ) = = 0 )
2004-08-23 00:15:46 +00:00
{
if ( sep > = 1 )
Q_strncatz ( n , " _ " , sizeof ( n ) ) ;
// snprintf (n, sizeof(n), "%s_", n);
Q_strncatz ( n , client - > name , sizeof ( n ) ) ;
// snprintf (n, sizeof(n),"%s%s", n, client->name);
sep + + ;
}
}
return n ;
}
int Dem_CountTeamPlayers ( char * t )
{
int i , count ;
count = 0 ;
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
{
if ( svs . clients [ i ] . name [ 0 ] & & ! svs . clients [ i ] . spectator )
if ( strcmp ( Info_ValueForKey ( svs . clients [ i ] . userinfo , " team " ) , t ) = = 0 )
count + + ;
}
return count ;
}
// <-
void SV_MVDEasyRecord_f ( void )
{
int c ;
2008-06-01 22:06:22 +00:00
dir_t * dir ;
2004-08-23 00:15:46 +00:00
char name [ 1024 ] ;
char name2 [ MAX_OSPATH * 7 ] ; // scream
//char name2[MAX_OSPATH*2];
int i ;
2009-04-01 22:03:56 +00:00
vfsfile_t * f ;
2004-08-23 00:15:46 +00:00
c = Cmd_Argc ( ) ;
2006-05-28 23:32:16 +00:00
if ( c > 2 )
2004-08-23 00:15:46 +00:00
{
Con_Printf ( " easyrecord [demoname] \n " ) ;
return ;
}
2006-05-28 23:31:30 +00:00
if ( sv . state < ss_active )
{
Con_Printf ( " Server isn't running or is still loading \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
dir = Sys_listdir ( sv_demoDir . string , " .* " , SORT_NO ) ;
2008-06-01 22:06:22 +00:00
if ( sv_demoMaxDirSize . value & & dir - > size > sv_demoMaxDirSize . value * 1024 )
2004-08-23 00:15:46 +00:00
{
Con_Printf ( " insufficient directory space, increase sv_demoMaxDirSize \n " ) ;
2008-06-01 22:06:22 +00:00
Sys_freedir ( dir ) ;
2004-08-23 00:15:46 +00:00
return ;
}
2008-06-01 22:06:22 +00:00
Sys_freedir ( dir ) ;
2004-08-23 00:15:46 +00:00
if ( c = = 2 )
2012-04-20 14:09:04 +00:00
{
char * c ;
2004-08-23 00:15:46 +00:00
Q_strncpyz ( name , Cmd_Argv ( 1 ) , sizeof ( name ) ) ;
2012-04-20 14:09:04 +00:00
while ( c = strchr ( name , ' : ' ) )
* c = ' - ' ;
}
2004-08-23 00:15:46 +00:00
else
{
i = Dem_CountPlayers ( ) ;
if ( teamplay . value > = 1 & & i > 2 )
{
// Teamplay
2006-03-06 01:41:09 +00:00
snprintf ( name , sizeof ( name ) , " %don%d_ " , Dem_CountTeamPlayers ( Dem_Team ( 1 ) ) , Dem_CountTeamPlayers ( Dem_Team ( 2 ) ) ) ;
2004-08-23 00:15:46 +00:00
if ( sv_demoExtraNames . value > 0 )
{
2005-07-03 15:16:20 +00:00
Q_strncatz ( name , va ( " [%s]_%s_vs_[%s]_%s_%s " ,
Dem_Team ( 1 ) , Dem_PlayerNameTeam ( Dem_Team ( 1 ) ) ,
2004-08-23 00:15:46 +00:00
Dem_Team ( 2 ) , Dem_PlayerNameTeam ( Dem_Team ( 2 ) ) ,
sv . name ) , sizeof ( name ) ) ;
} else
Q_strncatz ( name , va ( " %s_vs_%s_%s " , Dem_Team ( 1 ) , Dem_Team ( 2 ) , sv . name ) , sizeof ( name ) ) ;
} else {
if ( i = = 2 ) {
// Duel
2006-03-06 01:41:09 +00:00
snprintf ( name , sizeof ( name ) , " duel_%s_vs_%s_%s " ,
2004-08-23 00:15:46 +00:00
Dem_PlayerName ( 1 ) ,
Dem_PlayerName ( 2 ) ,
sv . name ) ;
} else {
// FFA
2006-03-06 01:41:09 +00:00
snprintf ( name , sizeof ( name ) , " ffa_%s(%d) " , sv . name , i ) ;
2004-08-23 00:15:46 +00:00
}
}
}
// <-
// Make sure the filename doesn't contain illegal characters
Q_strncpyz ( name , va ( " %s%s " , sv_demoPrefix . string , SV_CleanName ( name ) ) ,
MAX_MVD_NAME - strlen ( sv_demoSuffix . string ) - 7 ) ;
Q_strncatz ( name , sv_demoSuffix . string , sizeof ( name ) ) ;
2009-04-01 22:03:56 +00:00
Q_strncpyz ( name , va ( " %s/%s " , sv_demoDir . string , name ) , sizeof ( name ) ) ;
2004-08-23 00:15:46 +00:00
// find a filename that doesn't exist yet
Q_strncpyz ( name2 , name , sizeof ( name2 ) ) ;
// COM_StripExtension(name2, name2);
2010-07-18 08:42:59 +00:00
FS_CreatePath ( name2 , FS_GAMEONLY ) ;
2004-08-23 00:15:46 +00:00
strcat ( name2 , " .mvd " ) ;
2009-04-01 22:03:56 +00:00
if ( ( f = FS_OpenVFS ( name2 , " rb " , FS_GAMEONLY ) ) = = 0 )
f = FS_OpenVFS ( va ( " %s.gz " , name2 ) , " rb " , FS_GAMEONLY ) ;
2005-07-03 15:16:20 +00:00
2004-12-10 21:34:42 +00:00
if ( f )
{
2004-08-23 00:15:46 +00:00
i = 1 ;
do {
2009-04-01 22:03:56 +00:00
VFS_CLOSE ( f ) ;
2006-03-06 01:41:09 +00:00
snprintf ( name2 , sizeof ( name2 ) , " %s_%02i " , name , i ) ;
2004-08-23 00:15:46 +00:00
// COM_StripExtension(name2, name2);
strcat ( name2 , " .mvd " ) ;
2009-04-01 22:03:56 +00:00
if ( ( f = FS_OpenVFS ( name2 , " rb " , FS_GAMEONLY ) ) = = 0 )
f = FS_OpenVFS ( va ( " %s.gz " , name2 ) , " rb " , FS_GAMEONLY ) ;
2004-08-23 00:15:46 +00:00
i + + ;
} while ( f ) ;
}
2004-12-10 21:34:42 +00:00
SV_MVD_Record ( SV_InitRecordFile ( name2 ) ) ;
2004-08-23 00:15:46 +00:00
}
2012-04-09 19:12:12 +00:00
# ifdef HAVE_TCP
static int MVD_StreamStartListening ( int port )
2004-12-08 04:14:52 +00:00
{
int sock ;
2005-07-03 15:16:20 +00:00
2004-12-08 04:14:52 +00:00
struct sockaddr_in address ;
// int fromlen;
unsigned int nonblocking = true ;
2005-09-06 01:19:26 +00:00
address . sin_family = AF_INET ;
2005-04-18 17:12:18 +00:00
address . sin_addr . s_addr = INADDR_ANY ;
2004-12-08 04:14:52 +00:00
address . sin_port = htons ( ( u_short ) port ) ;
2007-11-04 08:21:19 +00:00
if ( ( sock = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ) ) = = INVALID_SOCKET )
2004-12-08 04:14:52 +00:00
{
2009-10-06 00:39:35 +00:00
Sys_Error ( " MVD_StreamStartListening: socket: %s " , strerror ( qerrno ) ) ;
2004-12-08 04:14:52 +00:00
}
2011-05-19 13:34:07 +00:00
if ( ioctlsocket ( sock , FIONBIO , ( u_long * ) & nonblocking ) = = INVALID_SOCKET )
2004-12-08 04:14:52 +00:00
{
2009-10-06 00:39:35 +00:00
Sys_Error ( " FTP_TCP_OpenSocket: ioctl FIONBIO: %s " , strerror ( qerrno ) ) ;
2004-12-08 04:14:52 +00:00
}
2005-07-03 15:16:20 +00:00
2007-11-04 08:21:19 +00:00
if ( bind ( sock , ( void * ) & address , sizeof ( address ) ) = = INVALID_SOCKET )
2004-12-08 04:14:52 +00:00
{
closesocket ( sock ) ;
return INVALID_SOCKET ;
}
2005-07-03 15:16:20 +00:00
2004-12-08 04:14:52 +00:00
listen ( sock , 2 ) ;
return sock ;
}
2012-04-09 19:12:12 +00:00
# endif
2004-12-08 04:14:52 +00:00
void SV_MVDStream_Poll ( void )
{
2012-04-09 19:12:12 +00:00
# ifdef HAVE_TCP
2004-12-08 04:14:52 +00:00
static int listensocket = INVALID_SOCKET ;
static int listenport ;
2007-09-04 20:54:24 +00:00
int _true = true ;
2005-10-07 02:03:17 +00:00
2004-12-08 04:14:52 +00:00
int client ;
netadr_t na ;
struct sockaddr_qstorage addr ;
int addrlen ;
2005-10-07 02:03:17 +00:00
int count ;
2004-12-08 04:14:52 +00:00
qboolean wanted ;
2005-10-07 02:03:17 +00:00
mvddest_t * dest ;
2006-09-17 00:59:22 +00:00
char * ip ;
2008-06-08 14:37:57 +00:00
char adrbuf [ MAX_ADR_SIZE ] ;
2005-10-07 02:03:17 +00:00
2009-11-04 21:16:50 +00:00
if ( ! sv . state | | ! qtv_streamport . ival )
2004-12-08 04:14:52 +00:00
wanted = false ;
2009-11-04 21:16:50 +00:00
else if ( listenport & & qtv_streamport . ival ! = listenport ) //easy way to switch... disable for a frame. :)
2004-12-08 04:14:52 +00:00
{
2009-11-04 21:16:50 +00:00
listenport = qtv_streamport . ival ;
2004-12-08 04:14:52 +00:00
wanted = false ;
}
else
{
2009-11-04 21:16:50 +00:00
listenport = qtv_streamport . ival ;
2004-12-08 04:14:52 +00:00
wanted = true ;
}
if ( wanted & & listensocket = = INVALID_SOCKET )
2006-09-17 00:59:22 +00:00
{
2004-12-08 04:14:52 +00:00
listensocket = MVD_StreamStartListening ( listenport ) ;
2006-09-17 00:59:22 +00:00
if ( listensocket = = INVALID_SOCKET & & qtv_streamport . modified )
{
Con_Printf ( " Cannot open TCP port %i for QTV \n " , listenport ) ;
qtv_streamport . modified = false ;
}
}
2004-12-08 04:14:52 +00:00
else if ( ! wanted & & listensocket ! = INVALID_SOCKET )
{
closesocket ( listensocket ) ;
listensocket = INVALID_SOCKET ;
return ;
}
if ( listensocket = = INVALID_SOCKET )
return ;
addrlen = sizeof ( addr ) ;
client = accept ( listensocket , ( struct sockaddr * ) & addr , & addrlen ) ;
if ( client = = INVALID_SOCKET )
return ;
2005-07-03 15:16:20 +00:00
2011-05-19 13:34:07 +00:00
ioctlsocket ( client , FIONBIO , ( u_long * ) & _true ) ;
2007-09-04 20:54:24 +00:00
2006-09-17 00:59:22 +00:00
if ( qtv_maxstreams . value > 0 )
2005-10-07 02:03:17 +00:00
{
count = 0 ;
for ( dest = demo . dest ; dest ; dest = dest - > nextdest )
{
if ( dest - > desttype = = DEST_STREAM )
{
count + + ;
}
}
2006-09-17 00:59:22 +00:00
if ( count > qtv_maxstreams . value )
2005-10-07 02:03:17 +00:00
{ //sorry
2008-02-05 02:41:46 +00:00
char * goawaymessage = " QTVSV 1 \n TERROR: This server enforces a limit on the number of proxies connected at any one time. Please try again later \n \n " ;
2006-09-17 00:59:22 +00:00
send ( client , goawaymessage , strlen ( goawaymessage ) , 0 ) ;
2005-10-07 02:03:17 +00:00
closesocket ( client ) ;
return ;
}
2004-12-08 04:14:52 +00:00
}
SockadrToNetadr ( & addr , & na ) ;
2008-06-08 14:37:57 +00:00
ip = NET_AdrToString ( adrbuf , sizeof ( adrbuf ) , na ) ;
2006-09-17 00:59:22 +00:00
Con_Printf ( " MVD streaming client attempting to connect from %s \n " , ip ) ;
SV_MVD_InitPendingStream ( client , ip ) ;
2004-12-08 04:14:52 +00:00
2006-09-17 00:59:22 +00:00
// SV_MVD_Record (SV_InitStream(client));
2012-04-09 19:12:12 +00:00
# endif
2004-12-08 04:14:52 +00:00
}
2004-08-23 00:15:46 +00:00
void SV_MVDList_f ( void )
{
2004-12-10 21:34:42 +00:00
mvddest_t * d ;
2008-06-01 22:06:22 +00:00
dir_t * dir ;
2004-08-23 00:15:46 +00:00
file_t * list ;
float f ;
int i , j , show ;
2009-04-01 22:03:56 +00:00
Con_Printf ( " content of %s/*.mvd \n " , sv_demoDir . string ) ;
dir = Sys_listdir ( sv_demoDir . string , " .mvd " , SORT_BY_DATE ) ;
2008-06-01 22:06:22 +00:00
list = dir - > files ;
2004-08-23 00:15:46 +00:00
if ( ! list - > name [ 0 ] )
{
Con_Printf ( " no demos \n " ) ;
}
2008-12-03 02:23:53 +00:00
for ( i = 1 ; i < = dir - > numfiles ; i + + , list + + )
2004-08-23 00:15:46 +00:00
{
for ( j = 1 ; j < Cmd_Argc ( ) ; j + + )
if ( strstr ( list - > name , Cmd_Argv ( j ) ) = = NULL )
break ;
show = Cmd_Argc ( ) = = j ;
2004-12-10 21:34:42 +00:00
if ( show )
{
for ( d = demo . dest ; d ; d = d - > nextdest )
{
if ( ! strcmp ( list - > name , d - > name ) )
Con_Printf ( " *%d: %s %dk \n " , i , list - > name , d - > totalsize / 1024 ) ;
}
if ( ! d )
2004-08-23 00:15:46 +00:00
Con_Printf ( " %d: %s %dk \n " , i , list - > name , list - > size / 1024 ) ;
}
}
2004-12-10 21:34:42 +00:00
for ( d = demo . dest ; d ; d = d - > nextdest )
2008-06-01 22:06:22 +00:00
dir - > size + = d - > totalsize ;
2004-08-23 00:15:46 +00:00
2008-06-01 22:06:22 +00:00
Con_Printf ( " \n directory size: %.1fMB \n " , ( float ) dir - > size / ( 1024 * 1024 ) ) ;
2004-12-10 21:34:42 +00:00
if ( sv_demoMaxDirSize . value )
{
2008-06-01 22:06:22 +00:00
f = ( sv_demoMaxDirSize . value * 1024 - dir - > size ) / ( 1024 * 1024 ) ;
2004-08-23 00:15:46 +00:00
if ( f < 0 )
f = 0 ;
Con_Printf ( " space available: %.1fMB \n " , f ) ;
}
2008-06-01 22:06:22 +00:00
Sys_freedir ( dir ) ;
2004-08-23 00:15:46 +00:00
}
2008-06-01 22:06:22 +00:00
void SV_UserCmdMVDList_f ( void )
2004-08-23 00:15:46 +00:00
{
2008-06-01 22:06:22 +00:00
mvddest_t * d ;
dir_t * dir ;
2004-08-23 00:15:46 +00:00
file_t * list ;
2008-06-01 22:06:22 +00:00
float f ;
int i , j , show ;
2010-01-11 17:39:56 +00:00
SV_ClientPrintf ( host_client , PRINT_HIGH , " available demos: \n " ) ;
2009-04-01 22:03:56 +00:00
dir = Sys_listdir ( sv_demoDir . string , " .mvd " , SORT_BY_DATE ) ;
2008-06-01 22:06:22 +00:00
list = dir - > files ;
if ( ! list - > name [ 0 ] )
{
2010-01-11 17:39:56 +00:00
SV_ClientPrintf ( host_client , PRINT_HIGH , " no demos \n " ) ;
2008-06-01 22:06:22 +00:00
}
2008-12-03 02:23:53 +00:00
for ( i = 1 ; i < = dir - > numfiles ; i + + , list + + )
2008-06-01 22:06:22 +00:00
{
for ( j = 1 ; j < Cmd_Argc ( ) ; j + + )
if ( strstr ( list - > name , Cmd_Argv ( j ) ) = = NULL )
break ;
show = Cmd_Argc ( ) = = j ;
if ( show )
{
for ( d = demo . dest ; d ; d = d - > nextdest )
{
if ( ! strcmp ( list - > name , d - > name ) )
2010-01-11 17:39:56 +00:00
SV_ClientPrintf ( host_client , PRINT_HIGH , " *%d: %s %dk \n " , i , list - > name , d - > totalsize / 1024 ) ;
2008-06-01 22:06:22 +00:00
}
if ( ! d )
2010-01-11 17:39:56 +00:00
SV_ClientPrintf ( host_client , PRINT_HIGH , " %d: %s %dk \n " , i , list - > name , list - > size / 1024 ) ;
2008-06-01 22:06:22 +00:00
}
}
2011-05-19 13:34:07 +00:00
2008-06-01 22:06:22 +00:00
for ( d = demo . dest ; d ; d = d - > nextdest )
dir - > size + = d - > totalsize ;
2010-01-11 17:39:56 +00:00
SV_ClientPrintf ( host_client , PRINT_HIGH , " \n directory size: %.1fMB \n " , ( float ) dir - > size / ( 1024 * 1024 ) ) ;
2008-06-01 22:06:22 +00:00
if ( sv_demoMaxDirSize . value )
{
f = ( sv_demoMaxDirSize . value * 1024 - dir - > size ) / ( 1024 * 1024 ) ;
if ( f < 0 )
f = 0 ;
2010-01-11 17:39:56 +00:00
SV_ClientPrintf ( host_client , PRINT_HIGH , " space available: %.1fMB \n " , f ) ;
2008-06-01 22:06:22 +00:00
}
Sys_freedir ( dir ) ;
}
char * SV_MVDNum ( char * buffer , int bufferlen , int num )
{
file_t * list ;
dir_t * dir ;
2004-08-23 00:15:46 +00:00
2009-04-01 22:03:56 +00:00
dir = Sys_listdir ( sv_demoDir . string , " .mvd " , SORT_BY_DATE ) ;
2008-06-01 22:06:22 +00:00
list = dir - > files ;
2004-08-23 00:15:46 +00:00
2008-12-03 02:23:53 +00:00
if ( num > dir - > numfiles | | num < = 0 )
2008-06-01 22:06:22 +00:00
{
Sys_freedir ( dir ) ;
2004-08-23 00:15:46 +00:00
return NULL ;
2008-06-01 22:06:22 +00:00
}
2004-08-23 00:15:46 +00:00
num - - ;
2008-12-03 02:23:53 +00:00
list + = num ;
2004-08-23 00:15:46 +00:00
2008-12-03 02:23:53 +00:00
Q_strncpyz ( buffer , list - > name , bufferlen ) ;
2008-06-01 22:06:22 +00:00
Sys_freedir ( dir ) ;
return buffer ;
2004-08-23 00:15:46 +00:00
}
char * SV_MVDName2Txt ( char * name )
{
char s [ MAX_OSPATH ] ;
if ( ! name )
return NULL ;
Q_strncpyz ( s , name , MAX_OSPATH ) ;
if ( strstr ( s , " .mvd.gz " ) ! = NULL )
Q_strncpyz ( s + strlen ( s ) - 6 , " txt " , MAX_OSPATH - strlen ( s ) + 6 ) ;
else
Q_strncpyz ( s + strlen ( s ) - 3 , " txt " , MAX_OSPATH - strlen ( s ) + 3 ) ;
return va ( " %s " , s ) ;
}
2008-06-01 22:06:22 +00:00
char * SV_MVDTxTNum ( char * buffer , int bufferlen , int num )
2004-08-23 00:15:46 +00:00
{
2008-06-01 22:06:22 +00:00
return SV_MVDName2Txt ( SV_MVDNum ( buffer , bufferlen , num ) ) ;
2004-08-23 00:15:46 +00:00
}
void SV_MVDRemove_f ( void )
{
char name [ MAX_MVD_NAME ] , * ptr ;
char path [ MAX_OSPATH ] ;
int i ;
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( " rmdemo <demoname> - removes the demo \n rmdemo *<token> - removes demo with <token> in the name \n rmdemo * - removes all demos \n " ) ;
return ;
}
ptr = Cmd_Argv ( 1 ) ;
if ( * ptr = = ' * ' )
{
2008-06-01 22:06:22 +00:00
dir_t * dir ;
2004-08-23 00:15:46 +00:00
file_t * list ;
// remove all demos with specified token
ptr + + ;
2009-04-01 22:03:56 +00:00
dir = Sys_listdir ( sv_demoDir . string , " .mvd " , SORT_BY_DATE ) ;
2008-06-01 22:06:22 +00:00
list = dir - > files ;
2008-12-03 02:23:53 +00:00
for ( i = 0 ; i < dir - > numfiles ; list + + )
2004-08-23 00:15:46 +00:00
{
if ( strstr ( list - > name , ptr ) )
{
if ( sv . mvdrecording & & ! strcmp ( list - > name , demo . name ) )
SV_MVDStop_f ( ) ;
// stop recording first;
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , sv_demoDir . string , list - > name ) ;
if ( ! FS_Remove ( path , FS_GAMEONLY ) )
2004-12-10 21:34:42 +00:00
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " removing %s... \n " , list - > name ) ;
i + + ;
}
2005-07-03 15:16:20 +00:00
2009-04-01 22:03:56 +00:00
FS_Remove ( SV_MVDName2Txt ( path ) , FS_GAMEONLY ) ;
2004-08-23 00:15:46 +00:00
}
}
2008-06-01 22:06:22 +00:00
Sys_freedir ( dir ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
if ( i )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " %d demos removed \n " , i ) ;
2004-12-10 21:34:42 +00:00
}
else
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " no matching found \n " ) ;
}
return ;
}
Q_strncpyz ( name , Cmd_Argv ( 1 ) , MAX_MVD_NAME ) ;
2006-03-11 03:12:10 +00:00
COM_DefaultExtension ( name , " .mvd " , sizeof ( name ) ) ;
2004-08-23 00:15:46 +00:00
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
if ( sv . mvdrecording & & ! strcmp ( name , demo . name ) )
SV_MVDStop_f ( ) ;
2009-04-01 22:03:56 +00:00
if ( ! FS_Remove ( path , FS_GAMEONLY ) )
2004-08-23 00:15:46 +00:00
{
Con_Printf ( " demo %s successfully removed \n " , name ) ;
}
2005-07-03 15:16:20 +00:00
else
2004-08-23 00:15:46 +00:00
Con_Printf ( " unable to remove demo %s \n " , name ) ;
2009-04-01 22:03:56 +00:00
FS_Remove ( SV_MVDName2Txt ( path ) , FS_GAMEONLY ) ;
2004-08-23 00:15:46 +00:00
}
void SV_MVDRemoveNum_f ( void )
{
int num ;
2008-06-01 22:06:22 +00:00
char namebuf [ MAX_QPATH ] ;
2004-08-23 00:15:46 +00:00
char * val , * name ;
char path [ MAX_OSPATH ] ;
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( " rmdemonum <#> \n " ) ;
return ;
}
val = Cmd_Argv ( 1 ) ;
if ( ( num = atoi ( val ) ) = = 0 & & val [ 0 ] ! = ' 0 ' )
{
Con_Printf ( " rmdemonum <#> \n " ) ;
return ;
}
2008-06-01 22:06:22 +00:00
name = SV_MVDNum ( namebuf , sizeof ( namebuf ) , num ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
if ( name ! = NULL )
{
2004-08-23 00:15:46 +00:00
if ( sv . mvdrecording & & ! strcmp ( name , demo . name ) )
SV_MVDStop_f ( ) ;
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , sv_demoDir . string , name ) ;
if ( ! FS_Remove ( path , FS_GAMEONLY ) )
2004-08-23 00:15:46 +00:00
{
Con_Printf ( " demo %s succesfully removed \n " , name ) ;
}
2005-07-03 15:16:20 +00:00
else
2004-08-23 00:15:46 +00:00
Con_Printf ( " unable to remove demo %s \n " , name ) ;
2009-04-01 22:03:56 +00:00
FS_Remove ( SV_MVDName2Txt ( path ) , FS_GAMEONLY ) ;
2004-12-10 21:34:42 +00:00
}
else
2004-08-23 00:15:46 +00:00
Con_Printf ( " invalid demo num \n " ) ;
}
void SV_MVDInfoAdd_f ( void )
{
2008-06-01 22:06:22 +00:00
char namebuf [ MAX_QPATH ] ;
2004-08-23 00:15:46 +00:00
char * name , * args , path [ MAX_OSPATH ] ;
2009-04-01 22:03:56 +00:00
vfsfile_t * f ;
2004-08-23 00:15:46 +00:00
if ( Cmd_Argc ( ) < 3 ) {
Con_Printf ( " usage:MVDInfoAdd <demonum> <info string> \n <demonum> = * for currently recorded demo \n " ) ;
return ;
}
if ( ! strcmp ( Cmd_Argv ( 1 ) , " * " ) )
{
2004-12-10 21:34:42 +00:00
if ( ! sv . mvdrecording )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " Not recording demo! \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , demo . path , SV_MVDName2Txt ( demo . name ) ) ;
2004-12-10 21:34:42 +00:00
}
else
{
2008-06-01 22:06:22 +00:00
name = SV_MVDTxTNum ( namebuf , sizeof ( namebuf ) , atoi ( Cmd_Argv ( 1 ) ) ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
if ( ! name )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " invalid demo num \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
}
2009-04-01 22:03:56 +00:00
if ( ( f = FS_OpenVFS ( path , " a+t " , FS_GAMEONLY ) ) = = NULL )
2004-08-23 00:15:46 +00:00
{
Con_Printf ( " failed to open the file \n " ) ;
return ;
}
// skip demonum
args = Cmd_Args ( ) ;
while ( * args > 32 ) args + + ;
while ( * args & & * args < = 32 ) args + + ;
2009-04-01 22:03:56 +00:00
VFS_WRITE ( f , args , strlen ( args ) ) ;
VFS_WRITE ( f , " \n " , 1 ) ;
VFS_FLUSH ( f ) ;
VFS_CLOSE ( f ) ;
2004-08-23 00:15:46 +00:00
}
void SV_MVDInfoRemove_f ( void )
{
2008-06-01 22:06:22 +00:00
char namebuf [ MAX_QPATH ] ;
2004-08-23 00:15:46 +00:00
char * name , path [ MAX_OSPATH ] ;
2004-12-10 21:34:42 +00:00
if ( Cmd_Argc ( ) < 2 )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " usage:demoInfoRemove <demonum> \n <demonum> = * for currently recorded demo \n " ) ;
return ;
}
if ( ! strcmp ( Cmd_Argv ( 1 ) , " * " ) )
{
if ( ! sv . mvdrecording )
{
Con_Printf ( " Not recording demo! \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , demo . path , SV_MVDName2Txt ( demo . name ) ) ;
2004-08-23 00:15:46 +00:00
}
else
{
2008-06-01 22:06:22 +00:00
name = SV_MVDTxTNum ( namebuf , sizeof ( namebuf ) , atoi ( Cmd_Argv ( 1 ) ) ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
if ( ! name )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " invalid demo num \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
}
2009-04-01 22:03:56 +00:00
if ( FS_Remove ( path , FS_GAMEONLY ) )
2004-08-23 00:15:46 +00:00
Con_Printf ( " failed to remove the file \n " ) ;
else Con_Printf ( " file removed \n " ) ;
}
void SV_MVDInfo_f ( void )
{
2008-06-01 22:06:22 +00:00
int len ;
2004-08-23 00:15:46 +00:00
char buf [ 64 ] ;
2009-04-01 22:03:56 +00:00
vfsfile_t * f = NULL ;
2004-08-23 00:15:46 +00:00
char * name , path [ MAX_OSPATH ] ;
2004-12-10 21:34:42 +00:00
if ( Cmd_Argc ( ) < 2 )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " usage:demoinfo <demonum> \n <demonum> = * for currently recorded demo \n " ) ;
return ;
}
if ( ! strcmp ( Cmd_Argv ( 1 ) , " * " ) )
{
2004-12-10 21:34:42 +00:00
if ( ! sv . mvdrecording )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " Not recording demo! \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , demo . path , SV_MVDName2Txt ( demo . name ) ) ;
2004-12-10 21:34:42 +00:00
}
else
{
2008-06-01 22:06:22 +00:00
name = SV_MVDTxTNum ( buf , sizeof ( buf ) , atoi ( Cmd_Argv ( 1 ) ) ) ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
if ( ! name )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " invalid demo num \n " ) ;
return ;
}
2009-04-01 22:03:56 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s " , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
}
2009-04-01 22:03:56 +00:00
if ( ( f = FS_OpenVFS ( path , " rt " , FS_GAMEONLY ) ) = = NULL )
2004-08-23 00:15:46 +00:00
{
Con_Printf ( " (empty) \n " ) ;
return ;
}
2008-06-01 22:06:22 +00:00
for ( ; ; )
2004-08-23 00:15:46 +00:00
{
2009-04-01 22:03:56 +00:00
len = VFS_READ ( f , buf , sizeof ( buf ) - 1 ) ;
2008-06-01 22:06:22 +00:00
if ( len < 0 )
break ;
buf [ len ] = 0 ;
2004-08-23 00:15:46 +00:00
Con_Printf ( " %s " , buf ) ;
}
2009-04-01 22:03:56 +00:00
VFS_CLOSE ( f ) ;
2004-08-23 00:15:46 +00:00
}
void SV_MVDPlayNum_f ( void )
{
2008-06-01 22:06:22 +00:00
char namebuf [ MAX_QPATH ] ;
2004-08-23 00:15:46 +00:00
char * name ;
int num ;
char * val ;
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( " mvdplaynum <#> \n " ) ;
return ;
}
val = Cmd_Argv ( 1 ) ;
if ( ( num = atoi ( val ) ) = = 0 & & val [ 0 ] ! = ' 0 ' )
{
Con_Printf ( " mvdplaynum <#> \n " ) ;
return ;
}
2008-06-01 22:06:22 +00:00
name = SV_MVDNum ( namebuf , sizeof ( namebuf ) , atoi ( val ) ) ;
2004-08-23 00:15:46 +00:00
if ( name )
Cbuf_AddText ( va ( " mvdplay %s \n " , name ) , Cmd_ExecLevel ) ;
else
Con_Printf ( " invalid demo num \n " ) ;
}
void SV_MVDInit ( void )
{
MVD_Init ( ) ;
2004-10-19 16:10:14 +00:00
# ifdef SERVERONLY //client command would conflict otherwise.
Cmd_AddCommand ( " record " , SV_MVD_Record_f ) ;
Cmd_AddCommand ( " stop " , SV_MVDStop_f ) ;
# endif
2011-09-03 03:49:43 +00:00
Cmd_AddCommand ( " cancel " , SV_MVD_Cancel_f ) ;
2007-05-28 02:25:32 +00:00
Cmd_AddCommand ( " qtvreverse " , SV_MVD_QTVReverse_f ) ;
2004-08-23 00:15:46 +00:00
Cmd_AddCommand ( " mvdrecord " , SV_MVD_Record_f ) ;
Cmd_AddCommand ( " easyrecord " , SV_MVDEasyRecord_f ) ;
Cmd_AddCommand ( " mvdstop " , SV_MVDStop_f ) ;
Cmd_AddCommand ( " mvdcancel " , SV_MVD_Cancel_f ) ;
2006-03-14 01:12:17 +00:00
//Cmd_AddCommand ("mvdplaynum", SV_MVDPlayNum_f);
2004-08-23 00:15:46 +00:00
Cmd_AddCommand ( " mvdlist " , SV_MVDList_f ) ;
Cmd_AddCommand ( " demolist " , SV_MVDList_f ) ;
Cmd_AddCommand ( " rmdemo " , SV_MVDRemove_f ) ;
Cmd_AddCommand ( " rmdemonum " , SV_MVDRemoveNum_f ) ;
2004-12-08 04:14:52 +00:00
2006-09-17 00:59:22 +00:00
Cvar_Register ( & qtv_streamport , " MVD Streaming " ) ;
Cvar_Register ( & qtv_maxstreams , " MVD Streaming " ) ;
Cvar_Register ( & qtv_password , " MVD Streaming " ) ;
2004-08-23 00:15:46 +00:00
}
2004-11-29 01:21:00 +00:00
# endif