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
2006-09-17 00:59:22 +00:00
//qtv proxies are meant to send a small header now, bit like http
//this header gives supported version numbers and stuff
typedef struct mvdpendingdest_s {
qboolean error ; //disables writers, quit ASAP.
int socket ;
char inbuffer [ 2048 ] ;
char outbuffer [ 2048 ] ;
char challenge [ 64 ] ;
int hasauthed ;
int insize ;
int outsize ;
struct mvdpendingdest_s * nextdest ;
} mvdpendingdest_t ;
2004-12-10 21:34:42 +00:00
typedef struct mvddest_s {
qboolean error ; //disables writers, quit ASAP.
enum { DEST_NONE , DEST_FILE , DEST_BUFFEREDFILE , DEST_STREAM } desttype ;
int socket ;
FILE * file ;
char name [ MAX_QPATH ] ;
char path [ MAX_QPATH ] ;
char * cache ;
int cacheused ;
int maxcachesize ;
unsigned int totalsize ;
struct mvddest_s * nextdest ;
} mvddest_t ;
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 ) ;
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 )
fclose ( d - > file ) ;
if ( d - > socket )
UDP_CloseSocket ( d - > socket ) ;
if ( destroyfiles )
{
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , d - > path , d - > name ) ;
2004-12-10 21:34:42 +00:00
Sys_remove ( path ) ;
Q_strncpyz ( path + strlen ( path ) - 3 , " txt " , MAX_OSPATH - strlen ( path ) + 3 ) ;
Sys_remove ( path ) ;
}
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 )
{
SV_MVDStop ( 2 ) ;
return ;
}
2004-12-10 21:34:42 +00:00
}
for ( d = demo . dest ; d ; d = d - > nextdest )
{
switch ( d - > desttype )
{
case DEST_FILE :
fflush ( d - > file ) ;
break ;
case DEST_BUFFEREDFILE :
if ( d - > cacheused + demo_size_padding > d - > maxcachesize | | compleate )
{
len = fwrite ( d - > cache , 1 , d - > cacheused , d - > file ) ;
if ( len < d - > cacheused )
d - > error = true ;
fflush ( d - > file ) ;
d - > cacheused = 0 ;
}
break ;
case DEST_STREAM :
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
}
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 )
{
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 ;
if ( demo . pendingdest - > socket ! = - 1 )
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 ;
if ( p - > nextdest - > socket ! = - 1 )
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
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.
p - > error = true ;
lineend = strchr ( start , ' \n ' ) ;
continue ;
}
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 )
{
}
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 ) ) ;
p - > hasauthed = ( ushort_result = = atoi ( password ) ) ;
break ;
case QTVAM_MD4 :
{
char hash [ 512 ] ;
int md4sum [ 4 ] ;
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 "
" PERROR: Incompatable version (valid version is v1) \n \n " ) ;
}
else if ( raw )
{
if ( p - > hasauthed = = false )
{
e = " " ;
}
else
{
SV_MVD_Record ( SV_InitStream ( p - > socket ) ) ;
p - > socket = - 1 ; //so it's not cleared wrongly.
}
p - > error = true ;
}
else
{
if ( p - > hasauthed = = true )
{
e = ( " QTVSV 1 \n "
" \n " ) ;
send ( p - > socket , e , strlen ( e ) , 0 ) ;
e = NULL ;
SV_MVD_Record ( SV_InitStream ( p - > socket ) ) ;
p - > socket = - 1 ; //so it's not cleared wrongly.
}
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 ;
}
}
}
}
2004-12-10 21:34:42 +00:00
void DestCloseAllFlush ( qboolean destroyfiles )
{
mvddest_t * d ;
DestFlush ( true ) ; //make sure it's all written.
while ( demo . dest )
{
d = demo . dest ;
demo . dest = d - > nextdest ;
DestClose ( d , destroyfiles ) ;
}
}
int DemoWriteDest ( void * data , int len , mvddest_t * d )
{
if ( d - > error )
return 0 ;
d - > totalsize + = len ;
switch ( d - > desttype )
{
case DEST_FILE :
fwrite ( data , len , 1 , d - > file ) ;
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
void SV_TimeOfDay ( date_t * date )
{
struct tm * newtime ;
time_t long_time ;
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
time ( & long_time ) ;
newtime = localtime ( & long_time ) ;
date - > day = newtime - > tm_mday ;
date - > mon = newtime - > tm_mon ;
date - > year = newtime - > tm_year + 1900 ;
date - > hour = newtime - > tm_hour ;
date - > min = newtime - > tm_min ;
date - > sec = newtime - > tm_sec ;
strftime ( date - > str , 128 ,
" %a %b %d, %H:%M:%S %Y " , newtime ) ;
}
// 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 ;
} dir_t ;
# define SORT_NO 0
# define SORT_BY_DATE 1
# ifdef _WIN32
dir_t Sys_listdir ( char * path , char * ext , qboolean usesorting )
{
static file_t list [ MAX_DIRFILES ] ;
dir_t dir ;
HANDLE h ;
WIN32_FIND_DATA fd ;
int i , pos , size ;
char name [ MAX_MVD_NAME ] , * s ;
memset ( list , 0 , sizeof ( list ) ) ;
memset ( & dir , 0 , sizeof ( dir ) ) ;
dir . files = list ;
h = FindFirstFile ( va ( " %s/*.* " , path ) , & fd ) ;
if ( h = = INVALID_HANDLE_VALUE )
{
return dir ;
}
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
do
{
if ( ( fd . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
{
dir . numdirs + + ;
continue ;
}
size = fd . nFileSizeLow ;
Q_strncpyz ( name , fd . cFileName , MAX_MVD_NAME ) ;
dir . size + = size ;
for ( s = fd . cFileName + strlen ( fd . cFileName ) ; s > fd . cFileName ; s - - )
{
if ( * s = = ' . ' )
break ;
}
if ( strcmp ( s , ext ) )
continue ;
// inclusion sort
#if 0
for ( i = 0 ; i < numfiles ; i + + )
{
if ( strcmp ( name , list [ i ] . name ) < 0 )
break ;
}
# endif
i = dir . numfiles ;
pos = i ;
dir . numfiles + + ;
for ( i = dir . numfiles - 1 ; i > pos ; i - - )
list [ i ] = list [ i - 1 ] ;
strcpy ( list [ i ] . name , name ) ;
list [ i ] . size = size ;
if ( dir . numfiles = = MAX_DIRFILES )
break ;
} while ( FindNextFile ( h , & fd ) ) ;
FindClose ( h ) ;
return dir ;
}
# else
# include <dirent.h>
dir_t Sys_listdir ( char * path , char * ext , qboolean usesorting )
{
static file_t list [ MAX_DIRFILES ] ;
dir_t d ;
int i , extsize ;
DIR * dir ;
struct dirent * oneentry ;
char pathname [ MAX_OSPATH ] ;
qboolean all ;
memset ( list , 0 , sizeof ( list ) ) ;
memset ( & d , 0 , sizeof ( d ) ) ;
d . files = list ;
extsize = strlen ( ext ) ;
all = ! strcmp ( ext , " .* " ) ;
dir = opendir ( path ) ;
if ( ! dir )
{
return d ;
}
for ( ; ; )
{
oneentry = readdir ( dir ) ;
2005-07-03 15:16:20 +00:00
if ( ! oneentry )
2004-08-23 00:15:46 +00:00
break ;
# ifndef __CYGWIN__
if ( oneentry - > d_type = = DT_DIR | | oneentry - > d_type = = DT_LNK )
{
d . numdirs + + ;
continue ;
}
# endif
sprintf ( pathname , " %s/%s " , path , oneentry - > d_name ) ;
list [ d . numfiles ] . size = COM_FileSize ( pathname ) ;
d . size + = list [ d . numfiles ] . size ;
i = strlen ( oneentry - > d_name ) ;
if ( ! all & & ( i < extsize | | ( Q_strcasecmp ( oneentry - > d_name + i - extsize , ext ) ) ) )
continue ;
Q_strncpyz ( list [ d . numfiles ] . name , oneentry - > d_name , MAX_MVD_NAME ) ;
if ( + + d . numfiles = = MAX_DIRFILES )
break ;
}
closedir ( dir ) ;
return d ;
}
# endif
# define MIN_MVD_MEMORY 0x100000
# define MAXSIZE (demobuffer->end < demobuffer->last ? \
demobuffer - > start - demobuffer - > end : \
demobuffer - > maxsize - demobuffer - > end )
2006-02-11 02:09:43 +00:00
cvar_t sv_demoUseCache = SCVAR ( " sv_demoUseCache " , " " ) ;
cvar_t sv_demoCacheSize = SCVAR ( " sv_demoCacheSize " , " " ) ;
cvar_t sv_demoMaxDirSize = SCVAR ( " sv_demoMaxDirSize " , " 102400 " ) ; //so ktpro autorecords.
cvar_t sv_demoDir = SCVAR ( " sv_demoDir " , " demos " ) ;
cvar_t sv_demofps = SCVAR ( " sv_demofps " , " " ) ;
cvar_t sv_demoPings = SCVAR ( " sv_demoPings " , " " ) ;
cvar_t sv_demoNoVis = SCVAR ( " sv_demoNoVis " , " " ) ;
cvar_t sv_demoMaxSize = SCVAR ( " sv_demoMaxSize " , " " ) ;
cvar_t sv_demoExtraNames = SCVAR ( " sv_demoExtraNames " , " " ) ;
2006-09-17 00:59:22 +00:00
cvar_t qtv_password = SCVAR ( " qtv_password " , " " ) ;
cvar_t qtv_streamport = FCVAR ( " qtv_streamport " , " mvd_streamport " , " 0 " , 0 ) ;
cvar_t qtv_maxstreams = FCVAR ( " qtv_maxstreams " , " mvd_maxstreams " , " 1 " , 0 ) ;
2006-02-11 02:09:43 +00:00
cvar_t sv_demoPrefix = SCVAR ( " sv_demoPrefix " , " " ) ;
cvar_t sv_demoSuffix = SCVAR ( " sv_demoSuffix " , " " ) ;
cvar_t sv_demotxt = SCVAR ( " sv_demotxt " , " 1 " ) ;
2004-08-23 00:15:46 +00:00
void SV_WriteMVDMessage ( sizebuf_t * msg , int type , int to , float time ) ;
demo_t demo ;
static dbuffer_t * demobuffer ;
2005-02-28 07:16:19 +00:00
static int header = ( char * ) & ( ( header_t * ) 0 ) - > data - ( char * ) NULL ;
2004-08-23 00:15:46 +00:00
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
// only one .. is allowed (so we can get to the same dir as the quake exe)
static void Check_DemoDir ( void )
2004-08-23 00:15:46 +00:00
{
2004-12-10 21:34:42 +00:00
char * value ;
value = sv_demoDir . string ;
2004-08-23 00:15:46 +00:00
if ( ! value [ 0 ] )
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 " ) ;
}
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 ) ;
MSG_WriteByte ( ( sizebuf_t * ) demo . dbuf , svc_updateping ) ;
MSG_WriteByte ( ( sizebuf_t * ) demo . dbuf , j ) ;
MSG_WriteShort ( ( sizebuf_t * ) demo . dbuf , SV_CalcPing ( client ) ) ;
MSG_WriteByte ( ( sizebuf_t * ) demo . dbuf , svc_updatepl ) ;
MSG_WriteByte ( ( sizebuf_t * ) demo . dbuf , j ) ;
MSG_WriteByte ( ( sizebuf_t * ) demo . dbuf , client - > lossage ) ;
}
}
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 )
prev - > maxsize = prev - > bufsize ;
demo . dbuf = cur ;
memset ( demo . dbuf , 0 , sizeof ( * demo . dbuf ) ) ;
demo . dbuf - > data = demobuffer - > data + demobuffer - > end ;
demo . dbuf - > maxsize = MAXSIZE ;
}
/*
= = = = = = = = = = = = = =
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 ;
2004-09-15 03:07:18 +00:00
p = ( header_t * ) demo . dbuf - > 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
if ( demo . dbuf - > data ! = ( qbyte * ) p )
memmove ( demo . dbuf - > data + size + header , demo . dbuf - > data , ( qbyte * ) p - demo . dbuf - > data ) ;
demo . dbuf - > bufsize - = size + header ;
demo . dbuf - > data + = size + header ;
pos - = size + header ;
demo . dbuf - > maxsize - = size + header ;
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
demo . dbuf - > data = demobuffer - > data ;
}
// 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 ;
2004-09-15 03:07:18 +00:00
p = ( header_t * ) demo . dbuf - > 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 )
{
demo . dbuf - > cursize = pos ;
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 ;
demo . dbuf - > cursize = demo . dbuf - > bufsize ;
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
memmove ( demobuffer - > data , demo . dbuf - > data , demo . dbuf - > bufsize ) ;
demo . dbuf - > data = demobuffer - > data ;
demobuffer - > end = demo . dbuf - > bufsize ;
demo . dbuf - > h = NULL ; // it will be setup again
demo . dbuf - > maxsize = MAXSIZE + demo . dbuf - > bufsize ;
}
void MVDWrite_Begin ( qbyte type , int to , int size )
{
qbyte * p ;
qboolean move = false ;
// will it fit?
while ( demo . dbuf - > bufsize + size + header > demo . dbuf - > maxsize )
{
// if we reached the end of buffer move msgbuf to the begining
if ( ! move & & demobuffer - > end > demobuffer - > start )
move = true ;
SV_MVDWritePackets ( 1 ) ;
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
if ( demo . dbuf - > cursize ! = demo . dbuf - > bufsize ) {
p = demo . dbuf - > data + demo . dbuf - > cursize ;
memmove ( p + size , p , demo . dbuf - > bufsize - demo . dbuf - > cursize ) ;
}
demo . dbuf - > bufsize + = size ;
demo . dbuf - > h - > size + = size ;
if ( ( demobuffer - > end + = size ) > demobuffer - > last )
demobuffer - > last = demobuffer - > end ;
}
/*
= = = = = = = = = = = = = = = = = = = =
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)
void SV_MVDWritePackets ( int num )
{
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 )
return ;
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 ;
if ( cl - > fixangle ) {
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 ) ;
}
if ( demo . lastwritten > demo . parsecount )
demo . lastwritten = demo . parsecount ;
demo . dbuf = & demo . frames [ demo . parsecount & DEMO_FRAMES_MASK ] . buf ;
demo . dbuf - > maxsize = MAXSIZE + demo . dbuf - > bufsize ;
}
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_demoNoVis , 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 ) ;
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 ;
FILE * file ;
char path [ MAX_OSPATH ] ;
file = fopen ( name , " wb " ) ;
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 ;
dst - > maxcachesize = 0x81000 ;
dst - > cache = BZ_Malloc ( dst - > maxcachesize ) ;
2004-08-23 00:15:46 +00:00
}
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
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 ) ;
Cvar_ForceSet ( Cvar_Get ( " serverdemo " , " " , CVAR_NOSET , " " ) , demo . name ) ;
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
{
2004-12-10 21:34:42 +00:00
FILE * f ;
f = fopen ( path , " w+t " ) ;
if ( f ! = NULL )
{
char buf [ 2000 ] ;
date_t date ;
SV_TimeOfDay ( & date ) ;
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 ( ) ) ;
2004-12-10 21:34:42 +00:00
fwrite ( buf , strlen ( buf ) , 1 , f ) ;
fflush ( f ) ;
fclose ( f ) ;
}
2004-12-08 04:14:52 +00:00
}
2005-07-03 15:16:20 +00:00
else
2004-12-10 21:34:42 +00:00
Sys_remove ( path ) ;
2004-12-08 04:14:52 +00:00
2004-12-10 21:34:42 +00:00
return dst ;
}
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 ) ;
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
}
2006-09-17 00:59:22 +00:00
void SV_MVD_InitPendingStream ( int socket , char * ip )
{
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 ;
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = = =
SV_Stop
stop recording a demo
= = = = = = = = = = = = = = = = = = = =
*/
void SV_MVDStop ( int reason )
{
if ( ! sv . mvdrecording )
{
Con_Printf ( " Not recording a demo. \n " ) ;
return ;
}
if ( reason = = 2 )
{
2004-12-10 21:34:42 +00:00
DestCloseAllFlush ( true ) ;
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
SV_BroadcastPrintf ( PRINT_CHAT , " Server recording canceled, demo removed \n " ) ;
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
demo . dbuf - > cursize = 0 ;
demo . dbuf - > h = NULL ;
demo . dbuf - > bufsize = 0 ;
MVDWrite_Begin ( dem_all , 0 , 2 + strlen ( " EndOfDemo " ) ) ;
MSG_WriteByte ( ( sizebuf_t * ) demo . dbuf , svc_disconnect ) ;
MSG_WriteString ( ( sizebuf_t * ) demo . dbuf , " EndOfDemo " ) ;
SV_MVDWritePackets ( demo . parsecount - demo . lastwritten + 1 ) ;
// finish up
2004-11-29 01:21:00 +00:00
2004-12-10 21:34:42 +00:00
DestCloseAllFlush ( false ) ;
2004-08-23 00:15:46 +00:00
2004-11-29 01:21:00 +00:00
2004-08-23 00:15:46 +00:00
sv . mvdrecording = false ;
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-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 )
{
SV_MVDStop ( 0 ) ;
}
/*
= = = = = = = = = = = = = = = = = = = =
SV_Cancel_f
Stops recording , and removes the demo
= = = = = = = = = = = = = = = = = = = =
*/
void SV_MVD_Cancel_f ( void )
{
SV_MVDStop ( 2 ) ;
}
/*
= = = = = = = = = = = = = = = = = = = =
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
}
2004-12-10 21:34:42 +00:00
static qboolean SV_MVD_Record ( mvddest_t * dest )
2004-08-23 00:15:46 +00:00
{
sizebuf_t buf ;
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 ;
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 ;
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 ;
2004-08-23 00:15:46 +00:00
2004-12-10 21:34:42 +00:00
sv . mvdrecording = true ;
2004-11-29 01:21:00 +00:00
}
2005-10-07 02:03:17 +00:00
// else
// SV_WriteRecordMVDMessage(&buf, dem_read);
2004-12-10 21:34:42 +00:00
demo . pingtime = demo . time = sv . time ;
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
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 ) ;
// send the serverdata
gamedir = Info_ValueForKey ( svs . info , " *gamedir " ) ;
if ( ! gamedir [ 0 ] )
gamedir = " qw " ;
MSG_WriteByte ( & buf , svc_serverdata ) ;
2004-12-10 21:34:42 +00:00
if ( sizeofcoord = = 4 ) //sorry.
{
MSG_WriteLong ( & buf , PROTOCOL_VERSION_FTE ) ;
MSG_WriteLong ( & buf , PEXT_FLOATCOORDS ) ;
}
2004-08-23 00:15:46 +00:00
MSG_WriteLong ( & buf , PROTOCOL_VERSION ) ;
MSG_WriteLong ( & buf , svs . spawncount ) ;
MSG_WriteString ( & buf , gamedir ) ;
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
MSG_WriteByte ( & buf , svc_soundlist ) ;
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
MSG_WriteByte ( & buf , svc_modellist ) ;
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 ) ) ;
for ( n = 0 ; n < sv . num_edicts ; n + + )
{
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 ) ;
}
}
/* else if (host_client->fteprotocolextensions & PEXT_SPAWNSTATIC2)
{
MSG_WriteByte ( & buf , svc_spawnbaseline2 ) ;
SV_WriteDelta ( & from , state , & buf , true , host_client - > fteprotocolextensions ) ;
} */
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 ;
MSG_WriteByte ( & buf , svc_updatefrags ) ;
MSG_WriteByte ( & buf , i ) ;
MSG_WriteShort ( & buf , player - > old_frags ) ;
2004-12-08 04:14:52 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( & buf , svc_updateping ) ;
MSG_WriteByte ( & buf , i ) ;
MSG_WriteShort ( & buf , SV_CalcPing ( player ) ) ;
2004-12-08 04:14:52 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( & buf , svc_updatepl ) ;
MSG_WriteByte ( & buf , i ) ;
MSG_WriteByte ( & buf , player - > lossage ) ;
2004-12-08 04:14:52 +00:00
2004-08-23 00:15:46 +00:00
MSG_WriteByte ( & buf , svc_updateentertime ) ;
MSG_WriteByte ( & buf , i ) ;
MSG_WriteFloat ( & buf , realtime - player - > connection_started ) ;
Q_strncpyz ( info , player - > userinfo , MAX_INFO_STRING ) ;
Info_RemovePrefixedKeys ( info , ' _ ' ) ; // server passwords, etc
MSG_WriteByte ( & buf , svc_updateuserinfo ) ;
MSG_WriteByte ( & buf , i ) ;
MSG_WriteLong ( & buf , player - > userid ) ;
MSG_WriteString ( & buf , info ) ;
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
2005-10-07 02:03:17 +00:00
for ( i = 0 ; i < MAX_STANDARDLIGHTSTYLES ; i + + )
2004-08-23 00:15:46 +00:00
{
MSG_WriteByte ( & buf , svc_lightstyle ) ;
MSG_WriteByte ( & buf , ( char ) i ) ;
2006-02-17 02:51:59 +00:00
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
// done
2004-11-29 01:21:00 +00:00
return true ;
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 ] ;
dir_t dir ;
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 ;
}
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
2004-08-23 00:15:46 +00:00
dir = Sys_listdir ( va ( " %s/%s " , com_gamedir , sv_demoDir . string ) , " .* " , SORT_NO ) ;
if ( sv_demoMaxDirSize . value & & dir . size > sv_demoMaxDirSize . value * 1024 )
{
Con_Printf ( " insufficient directory space, increase sv_demoMaxDirSize \n " ) ;
return ;
}
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 ) ;
2006-03-06 01:41:09 +00:00
snprintf ( name , MAX_OSPATH + MAX_MVD_NAME , " %s/%s/%s " , com_gamedir , 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 ) ) ;
2005-08-03 23:14:59 +00:00
COM_CreatePath ( name ) ;
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
}
/*
= = = = = = = = = = = = = = = = = = = =
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 ;
dir_t dir ;
char name [ 1024 ] ;
char name2 [ MAX_OSPATH * 7 ] ; // scream
//char name2[MAX_OSPATH*2];
int i ;
FILE * f ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
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 ;
}
2004-08-23 00:15:46 +00:00
dir = Sys_listdir ( va ( " %s/%s " , com_gamedir , sv_demoDir . string ) , " .* " , SORT_NO ) ;
if ( sv_demoMaxDirSize . value & & dir . size > sv_demoMaxDirSize . value * 1024 )
{
Con_Printf ( " insufficient directory space, increase sv_demoMaxDirSize \n " ) ;
return ;
}
if ( c = = 2 )
Q_strncpyz ( name , Cmd_Argv ( 1 ) , sizeof ( name ) ) ;
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 ) ) ;
Q_strncpyz ( name , va ( " %s/%s/%s " , com_gamedir , sv_demoDir . string , name ) , sizeof ( name ) ) ;
// find a filename that doesn't exist yet
Q_strncpyz ( name2 , name , sizeof ( name2 ) ) ;
Sys_mkdir ( va ( " %s/%s " , com_gamedir , sv_demoDir . string ) ) ;
// COM_StripExtension(name2, name2);
strcat ( name2 , " .mvd " ) ;
if ( ( f = fopen ( name2 , " rb " ) ) = = 0 )
f = fopen ( va ( " %s.gz " , name2 ) , " rb " ) ;
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 {
fclose ( 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 " ) ;
if ( ( f = fopen ( name2 , " rb " ) ) = = 0 )
f = fopen ( va ( " %s.gz " , name2 ) , " rb " ) ;
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
}
2004-12-08 04:14:52 +00:00
int MVD_StreamStartListening ( int port )
{
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 ) ;
if ( ( sock = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ) ) = = - 1 )
{
Sys_Error ( " MVD_StreamStartListening: socket: " , strerror ( qerrno ) ) ;
}
if ( ioctlsocket ( sock , FIONBIO , & nonblocking ) = = - 1 )
{
Sys_Error ( " FTP_TCP_OpenSocket: ioctl FIONBIO: " , strerror ( qerrno ) ) ;
}
2005-07-03 15:16:20 +00:00
2004-12-08 04:14:52 +00:00
if ( bind ( sock , ( void * ) & address , sizeof ( address ) ) = = - 1 )
{
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 ;
}
void SV_MVDStream_Poll ( void )
{
static int listensocket = INVALID_SOCKET ;
static int listenport ;
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 ;
2005-10-07 02:03:17 +00:00
2006-09-17 00:59:22 +00:00
if ( ! sv . state | | ! qtv_streamport . value )
2004-12-08 04:14:52 +00:00
wanted = false ;
2006-09-17 00:59:22 +00:00
else if ( listenport & & ( int ) qtv_streamport . value ! = listenport ) //easy way to switch... disable for a frame. :)
2004-12-08 04:14:52 +00:00
{
2006-09-17 00:59:22 +00:00
listenport = qtv_streamport . value ;
2004-12-08 04:14:52 +00:00
wanted = false ;
}
else
{
2006-09-17 00:59:22 +00:00
listenport = qtv_streamport . value ;
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
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
2006-09-17 00:59:22 +00:00
char * goawaymessage = " QTVSV 1 \n ERROR: This server enforces a limit on the number of proxies connected at any one time. Please try again later \n \n " ;
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 ) ;
2006-09-17 00:59:22 +00:00
ip = NET_AdrToString ( na ) ;
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));
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 ;
2004-08-23 00:15:46 +00:00
dir_t dir ;
file_t * list ;
float f ;
int i , j , show ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
2004-08-23 00:15:46 +00:00
Con_Printf ( " content of %s/%s/*.mvd \n " , com_gamedir , sv_demoDir . string ) ;
dir = Sys_listdir ( va ( " %s/%s " , com_gamedir , sv_demoDir . string ) , " .mvd " , SORT_BY_DATE ) ;
list = dir . files ;
if ( ! list - > name [ 0 ] )
{
Con_Printf ( " no demos \n " ) ;
}
for ( i = 1 ; list - > name [ 0 ] ; i + + , list + + )
{
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 )
dir . size + = d - > totalsize ;
2004-08-23 00:15:46 +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 )
{
2004-08-23 00:15:46 +00:00
f = ( sv_demoMaxDirSize . value * 1024 - dir . size ) / ( 1024 * 1024 ) ;
if ( f < 0 )
f = 0 ;
Con_Printf ( " space available: %.1fMB \n " , f ) ;
}
}
char * SV_MVDNum ( int num )
{
file_t * list ;
dir_t dir ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
2004-08-23 00:15:46 +00:00
dir = Sys_listdir ( va ( " %s/%s " , com_gamedir , sv_demoDir . string ) , " .mvd " , SORT_BY_DATE ) ;
list = dir . files ;
if ( num < = 0 )
return NULL ;
num - - ;
while ( list - > name [ 0 ] & & num ) { list + + ; num - - ; } ;
2005-07-03 15:16:20 +00:00
if ( list - > name [ 0 ] )
2004-08-23 00:15:46 +00:00
return list - > name ;
return NULL ;
}
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 ) ;
}
char * SV_MVDTxTNum ( int num )
{
return SV_MVDName2Txt ( SV_MVDNum ( num ) ) ;
}
void SV_MVDRemove_f ( void )
{
char name [ MAX_MVD_NAME ] , * ptr ;
char path [ MAX_OSPATH ] ;
int i ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
2004-08-23 00:15:46 +00:00
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 = = ' * ' )
{
dir_t dir ;
file_t * list ;
// remove all demos with specified token
ptr + + ;
dir = Sys_listdir ( va ( " %s/%s " , com_gamedir , sv_demoDir . string ) , " .mvd " , SORT_BY_DATE ) ;
list = dir . files ;
for ( i = 0 ; list - > name [ 0 ] ; list + + )
{
if ( strstr ( list - > name , ptr ) )
{
if ( sv . mvdrecording & & ! strcmp ( list - > name , demo . name ) )
SV_MVDStop_f ( ) ;
// stop recording first;
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , sv_demoDir . string , list - > name ) ;
2004-12-10 21:34:42 +00:00
if ( ! Sys_remove ( path ) )
{
2004-08-23 00:15:46 +00:00
Con_Printf ( " removing %s... \n " , list - > name ) ;
i + + ;
}
2005-07-03 15:16:20 +00:00
2004-08-23 00:15:46 +00:00
Sys_remove ( SV_MVDName2Txt ( path ) ) ;
}
}
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
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
if ( sv . mvdrecording & & ! strcmp ( name , demo . name ) )
SV_MVDStop_f ( ) ;
if ( ! Sys_remove ( path ) )
{
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 ) ;
Sys_remove ( SV_MVDName2Txt ( path ) ) ;
}
void SV_MVDRemoveNum_f ( void )
{
int num ;
char * val , * name ;
char path [ MAX_OSPATH ] ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
2004-08-23 00:15:46 +00:00
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 ;
}
name = SV_MVDNum ( num ) ;
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 ( ) ;
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
if ( ! Sys_remove ( path ) )
{
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 ) ;
Sys_remove ( SV_MVDName2Txt ( path ) ) ;
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 )
{
char * name , * args , path [ MAX_OSPATH ] ;
FILE * f ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
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 ;
}
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , demo . path , SV_MVDName2Txt ( demo . name ) ) ;
2004-12-10 21:34:42 +00:00
}
else
{
2004-08-23 00:15:46 +00:00
name = SV_MVDTxTNum ( atoi ( Cmd_Argv ( 1 ) ) ) ;
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 ;
}
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
}
if ( ( f = fopen ( path , " a+t " ) ) = = NULL )
{
Con_Printf ( " failed to open the file \n " ) ;
return ;
}
// skip demonum
args = Cmd_Args ( ) ;
while ( * args > 32 ) args + + ;
while ( * args & & * args < = 32 ) args + + ;
fwrite ( args , strlen ( args ) , 1 , f ) ;
fwrite ( " \n " , 1 , 1 , f ) ;
fflush ( f ) ;
fclose ( f ) ;
}
void SV_MVDInfoRemove_f ( void )
{
char * name , path [ MAX_OSPATH ] ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
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 ;
}
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , demo . path , SV_MVDName2Txt ( demo . name ) ) ;
2004-08-23 00:15:46 +00:00
}
else
{
name = SV_MVDTxTNum ( atoi ( Cmd_Argv ( 1 ) ) ) ;
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 ;
}
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
}
if ( Sys_remove ( path ) )
Con_Printf ( " failed to remove the file \n " ) ;
else Con_Printf ( " file removed \n " ) ;
}
void SV_MVDInfo_f ( void )
{
char buf [ 64 ] ;
FILE * f = NULL ;
char * name , path [ MAX_OSPATH ] ;
2004-12-10 21:34:42 +00:00
Check_DemoDir ( ) ;
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 ;
}
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , demo . path , SV_MVDName2Txt ( demo . name ) ) ;
2004-12-10 21:34:42 +00:00
}
else
{
2004-08-23 00:15:46 +00:00
name = SV_MVDTxTNum ( atoi ( Cmd_Argv ( 1 ) ) ) ;
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 ;
}
2006-03-06 01:41:09 +00:00
snprintf ( path , MAX_OSPATH , " %s/%s/%s " , com_gamedir , sv_demoDir . string , name ) ;
2004-08-23 00:15:46 +00:00
}
if ( ( f = fopen ( path , " rt " ) ) = = NULL )
{
Con_Printf ( " (empty) \n " ) ;
return ;
}
while ( ! feof ( f ) )
{
buf [ fread ( buf , 1 , sizeof ( buf ) - 1 , f ) ] = 0 ;
Con_Printf ( " %s " , buf ) ;
}
fclose ( f ) ;
}
void SV_MVDPlayNum_f ( void )
{
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 ;
}
name = SV_MVDNum ( atoi ( val ) ) ;
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 ) ;
Cmd_AddCommand ( " cancel " , SV_MVD_Cancel_f ) ;
# endif
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