2008-05-20 22:00:00 +02:00
|
|
|
|
//=======================================================================
|
|
|
|
|
// Copyright XashXT Group 2007 <20>
|
|
|
|
|
// cl_demo.c - demo record & playback
|
|
|
|
|
//=======================================================================
|
|
|
|
|
|
2008-06-09 22:00:00 +02:00
|
|
|
|
#include "common.h"
|
2008-05-20 22:00:00 +02:00
|
|
|
|
#include "client.h"
|
2010-08-04 22:00:00 +02:00
|
|
|
|
#include "net_encode.h"
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-10-16 22:00:00 +02:00
|
|
|
|
#define dem_cmd 0
|
|
|
|
|
#define dem_read 1
|
|
|
|
|
#define dem_set 2
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
====================
|
|
|
|
|
CL_WriteDemoCmd
|
|
|
|
|
|
|
|
|
|
Writes the current user cmd
|
|
|
|
|
====================
|
|
|
|
|
*/
|
|
|
|
|
void CL_WriteDemoCmd( usercmd_t *pcmd )
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
float fl;
|
|
|
|
|
usercmd_t cmd;
|
|
|
|
|
byte c;
|
|
|
|
|
|
2010-11-21 22:00:00 +01:00
|
|
|
|
fl = ( float )host.realtime;
|
2010-10-16 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, &fl, sizeof( fl ));
|
|
|
|
|
|
|
|
|
|
c = dem_cmd;
|
|
|
|
|
FS_Write( cls.demofile, &c, sizeof( c ));
|
|
|
|
|
|
|
|
|
|
// correct for byte order, bytes don't matter
|
|
|
|
|
cmd = *pcmd;
|
|
|
|
|
FS_Write( cls.demofile, &cmd, sizeof( cmd ));
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
|
|
|
{
|
2010-11-21 22:00:00 +01:00
|
|
|
|
fl = cl.refdef.cl_viewangles[i];
|
2010-10-16 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, &fl, sizeof( fl ));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-20 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
====================
|
|
|
|
|
CL_WriteDemoMessage
|
|
|
|
|
|
|
|
|
|
Dumps the current net message, prefixed by the length
|
|
|
|
|
====================
|
|
|
|
|
*/
|
2010-08-06 22:00:00 +02:00
|
|
|
|
void CL_WriteDemoMessage( sizebuf_t *msg, int head_size )
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
2010-11-21 22:00:00 +01:00
|
|
|
|
int swlen;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
if( !cls.demofile ) return;
|
|
|
|
|
if( cl.refdef.paused || cls.key_dest == key_menu )
|
2010-01-07 22:00:00 +01:00
|
|
|
|
return;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
// the first eight bytes are just packet sequencing stuff
|
2010-11-21 22:00:00 +01:00
|
|
|
|
swlen = BF_GetNumBytesWritten( msg ) - head_size;
|
2010-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
if( !swlen ) return; // ignore null messages
|
|
|
|
|
FS_Write( cls.demofile, &swlen, 4 );
|
2010-11-21 22:00:00 +01:00
|
|
|
|
FS_Write( cls.demofile, BF_GetData( msg ) + head_size, swlen );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CL_WriteDemoHeader( const char *name )
|
|
|
|
|
{
|
2010-10-02 22:00:00 +02:00
|
|
|
|
int i, j, len;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
char buf_data[MAX_MSGLEN];
|
2008-07-01 22:00:00 +02:00
|
|
|
|
entity_state_t *state, nullstate;
|
2010-02-07 22:00:00 +01:00
|
|
|
|
movevars_t nullmovevars;
|
2010-08-06 22:00:00 +02:00
|
|
|
|
sizebuf_t buf;
|
2010-10-02 22:00:00 +02:00
|
|
|
|
delta_info_t *dt;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2008-07-04 22:00:00 +02:00
|
|
|
|
MsgDev( D_INFO, "recording to %s.\n", name );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
cls.demofile = FS_Open( name, "wb" );
|
|
|
|
|
if( !cls.demofile )
|
|
|
|
|
{
|
|
|
|
|
MsgDev(D_ERROR, "CL_Record: unable to create %s\n", name );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cls.demorecording = true;
|
|
|
|
|
|
|
|
|
|
// don't start saving messages until a non-delta compressed message is received
|
|
|
|
|
cls.demowaiting = true;
|
|
|
|
|
|
|
|
|
|
// write out messages to hold the startup information
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_Init( &buf, "DemoWrite", buf_data, sizeof( buf_data ));
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// send the serverdata
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteByte( &buf, svc_serverdata );
|
|
|
|
|
BF_WriteLong( &buf, PROTOCOL_VERSION );
|
|
|
|
|
BF_WriteLong( &buf, cl.servercount );
|
|
|
|
|
BF_WriteByte( &buf, cl.playernum );
|
2010-08-20 22:00:00 +02:00
|
|
|
|
BF_WriteByte( &buf, cl.maxclients );
|
|
|
|
|
BF_WriteWord( &buf, clgame.maxEntities );
|
2010-10-28 22:00:00 +02:00
|
|
|
|
BF_WriteString( &buf, clgame.mapname );
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteString( &buf, clgame.maptitle );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
// user messages
|
|
|
|
|
for( i = 0; i < MAX_USER_MESSAGES; i++ )
|
|
|
|
|
{
|
2010-10-02 22:00:00 +02:00
|
|
|
|
if( clgame.msg[i].name[0] && clgame.msg[i].number >= svc_lastmsg )
|
2010-06-22 22:00:00 +02:00
|
|
|
|
{
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteByte( &buf, svc_usermessage );
|
|
|
|
|
BF_WriteString( &buf, clgame.msg[i].name );
|
|
|
|
|
BF_WriteByte( &buf, clgame.msg[i].number );
|
|
|
|
|
BF_WriteByte( &buf, (byte)clgame.msg[i].size );
|
2010-06-22 22:00:00 +02:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( BF_GetNumBytesWritten( &buf ) > ( BF_GetMaxBytes( &buf ) / 2 ))
|
2010-06-22 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
// write it out
|
2010-11-21 22:00:00 +01:00
|
|
|
|
len = BF_GetNumBytesWritten( &buf );
|
2010-06-22 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, &len, 4 );
|
2010-08-04 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, BF_GetData( &buf ), len );
|
|
|
|
|
BF_Clear( &buf );
|
2010-06-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-02 22:00:00 +02:00
|
|
|
|
// delta tables
|
|
|
|
|
for( i = 0; i < Delta_NumTables( ); i++ )
|
|
|
|
|
{
|
|
|
|
|
dt = Delta_FindStructByIndex( i );
|
|
|
|
|
|
|
|
|
|
for( j = 0; j < dt->numFields; j++ )
|
|
|
|
|
{
|
|
|
|
|
Delta_WriteTableField( &buf, i, &dt->pFields[j] );
|
|
|
|
|
|
|
|
|
|
if( BF_GetNumBytesWritten( &buf ) > ( BF_GetMaxBytes( &buf ) / 2 ))
|
|
|
|
|
{
|
|
|
|
|
// write it out
|
2010-11-21 22:00:00 +01:00
|
|
|
|
len = BF_GetNumBytesWritten( &buf );
|
2010-10-02 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, &len, 4 );
|
|
|
|
|
FS_Write( cls.demofile, BF_GetData( &buf ), len );
|
|
|
|
|
BF_Clear( &buf );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-20 22:00:00 +02:00
|
|
|
|
// baselines
|
2008-12-25 22:00:00 +01:00
|
|
|
|
Mem_Set( &nullstate, 0, sizeof( nullstate ));
|
2010-02-07 22:00:00 +01:00
|
|
|
|
Mem_Set( &nullmovevars, 0, sizeof( nullmovevars ));
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-08-20 22:00:00 +02:00
|
|
|
|
for( i = 0; i < clgame.maxEntities; i++ )
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
2010-08-07 22:00:00 +02:00
|
|
|
|
state = &clgame.entities[i].baseline;
|
2010-10-20 22:00:00 +02:00
|
|
|
|
if( !state->number ) continue;
|
2010-08-20 22:00:00 +02:00
|
|
|
|
if( !state->modelindex || state->effects == EF_NODRAW )
|
2009-10-13 22:00:00 +02:00
|
|
|
|
continue;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteByte( &buf, svc_spawnbaseline );
|
2010-10-20 22:00:00 +02:00
|
|
|
|
MSG_WriteDeltaEntity( &nullstate, state, &buf, true, CL_IsPlayerIndex( state->number ), sv_time( ));
|
2009-10-13 22:00:00 +02:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( BF_GetNumBytesWritten( &buf ) > ( BF_GetMaxBytes( &buf ) / 2 ))
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
// write it out
|
2010-11-21 22:00:00 +01:00
|
|
|
|
len = BF_GetNumBytesWritten( &buf );
|
2010-06-22 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, &len, 4 );
|
2010-08-04 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, BF_GetData( &buf ), len );
|
|
|
|
|
BF_Clear( &buf );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteByte( &buf, svc_stufftext );
|
|
|
|
|
BF_WriteString( &buf, "precache\n" );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteByte( &buf, svc_setview );
|
|
|
|
|
BF_WriteWord( &buf, cl.refdef.viewentity );
|
2009-10-11 22:00:00 +02:00
|
|
|
|
|
2010-02-05 22:00:00 +01:00
|
|
|
|
// write all clients userinfo
|
2010-08-20 22:00:00 +02:00
|
|
|
|
for( i = 0; i < cl.maxclients; i++ )
|
2010-02-05 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
player_info_t *pi;
|
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteByte( &buf, svc_updateuserinfo );
|
2010-10-15 22:00:00 +02:00
|
|
|
|
BF_WriteUBitLong( &buf, i, MAX_CLIENT_BITS );
|
2010-02-05 22:00:00 +01:00
|
|
|
|
pi = &cl.players[i];
|
|
|
|
|
|
|
|
|
|
if( pi->name[0] )
|
|
|
|
|
{
|
2010-10-15 22:00:00 +02:00
|
|
|
|
BF_WriteOneBit( &buf, 1 );
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_WriteString( &buf, pi->userinfo );
|
2010-02-05 22:00:00 +01:00
|
|
|
|
}
|
2010-10-15 22:00:00 +02:00
|
|
|
|
else BF_WriteOneBit( &buf, 0 );
|
2010-02-05 22:00:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-08-06 22:00:00 +02:00
|
|
|
|
MSG_WriteDeltaMovevars( &buf, &nullmovevars, &clgame.movevars );
|
2010-02-07 22:00:00 +01:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
// write it to the demo file
|
2010-11-21 22:00:00 +01:00
|
|
|
|
len = BF_GetNumBytesWritten( &buf );
|
2010-06-22 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, &len, 4 );
|
2010-08-04 22:00:00 +02:00
|
|
|
|
FS_Write( cls.demofile, BF_GetData( &buf ), len );
|
2008-12-26 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
// force client.dll update
|
|
|
|
|
Cmd_ExecuteString( "cmd fullupdate\n" );
|
2010-11-22 22:00:00 +01:00
|
|
|
|
if( clgame.hInstance ) clgame.dllFuncs.pfnReset();
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2008-07-12 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
CL_DrawDemoRecording
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
void CL_DrawDemoRecording( void )
|
|
|
|
|
{
|
2010-07-20 22:00:00 +02:00
|
|
|
|
char string[64];
|
|
|
|
|
rgba_t color = { 255, 255, 255, 255 };
|
2008-07-12 22:00:00 +02:00
|
|
|
|
fs_offset_t pos;
|
2010-07-20 22:00:00 +02:00
|
|
|
|
int len;
|
2008-07-12 22:00:00 +02:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if(!( host.developer && cls.demorecording ))
|
2008-07-12 22:00:00 +02:00
|
|
|
|
return;
|
2010-06-20 22:00:00 +02:00
|
|
|
|
|
2008-07-12 22:00:00 +02:00
|
|
|
|
pos = FS_Tell( cls.demofile );
|
2010-07-20 22:00:00 +02:00
|
|
|
|
com.snprintf( string, sizeof( string ), "RECORDING %s: %ik", cls.demoname, pos / 1024 );
|
|
|
|
|
|
|
|
|
|
Con_DrawStringLen( string, &len, NULL );
|
|
|
|
|
Con_DrawString(( scr_width->integer - len) >> 1, scr_height->integer >> 2, string, color );
|
2008-07-12 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2008-05-20 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
=======================================================================
|
|
|
|
|
|
|
|
|
|
CLIENT SIDE DEMO PLAYBACK
|
|
|
|
|
|
|
|
|
|
=======================================================================
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
CL_NextDemo
|
|
|
|
|
|
|
|
|
|
Called when a demo or cinematic finishes
|
|
|
|
|
If the "nextdemo" cvar is set, that command will be issued
|
|
|
|
|
==================
|
|
|
|
|
*/
|
2010-10-26 22:00:00 +02:00
|
|
|
|
qboolean CL_NextDemo( void )
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
2009-10-13 22:00:00 +02:00
|
|
|
|
string str;
|
|
|
|
|
|
|
|
|
|
if( cls.demonum == -1 )
|
2009-11-03 22:00:00 +01:00
|
|
|
|
return false; // don't play demos
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2009-10-13 22:00:00 +02:00
|
|
|
|
S_StopAllSounds();
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2009-10-13 22:00:00 +02:00
|
|
|
|
if( !cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS )
|
|
|
|
|
{
|
|
|
|
|
cls.demonum = 0;
|
|
|
|
|
if( !cls.demos[cls.demonum][0] )
|
|
|
|
|
{
|
2010-02-07 22:00:00 +01:00
|
|
|
|
MsgDev( D_INFO, "no demos listed with startdemos\n" );
|
2009-10-13 22:00:00 +02:00
|
|
|
|
cls.demonum = -1;
|
2009-11-03 22:00:00 +01:00
|
|
|
|
return false;
|
2009-10-13 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2009-10-13 22:00:00 +02:00
|
|
|
|
com.snprintf( str, MAX_STRING, "playdemo %s\n", cls.demos[cls.demonum] );
|
|
|
|
|
|
|
|
|
|
Cbuf_InsertText( str );
|
|
|
|
|
cls.demonum++;
|
2009-11-03 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
return true;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
CL_DemoCompleted
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
void CL_DemoCompleted( void )
|
|
|
|
|
{
|
|
|
|
|
CL_StopPlayback();
|
2010-02-07 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
if( !CL_NextDemo() && host.developer <= 2 )
|
2010-07-18 22:00:00 +02:00
|
|
|
|
UI_SetActiveMenu( true );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
2010-06-22 22:00:00 +02:00
|
|
|
|
CL_ReadDemoMessage
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
reads demo data and write it to client
|
|
|
|
|
=================
|
|
|
|
|
*/
|
2010-06-22 22:00:00 +02:00
|
|
|
|
void CL_ReadDemoMessage( void )
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
2010-08-06 22:00:00 +02:00
|
|
|
|
sizebuf_t buf;
|
2010-08-04 22:00:00 +02:00
|
|
|
|
char buf_data[MAX_MSGLEN];
|
|
|
|
|
int r, curSize;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if( !cls.demofile )
|
|
|
|
|
{
|
|
|
|
|
CL_DemoCompleted();
|
2010-06-22 22:00:00 +02:00
|
|
|
|
return;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
2009-11-10 22:00:00 +01:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
if( cl.refdef.paused || cls.key_dest != key_game )
|
|
|
|
|
return;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
// don't need another message yet
|
2010-10-09 22:00:00 +02:00
|
|
|
|
if(( cl.time + host.frametime ) <= cl.mtime[0] )
|
2010-06-22 22:00:00 +02:00
|
|
|
|
return;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
// get the length
|
2010-08-04 22:00:00 +02:00
|
|
|
|
r = FS_Read( cls.demofile, &curSize, 4 );
|
2010-06-22 22:00:00 +02:00
|
|
|
|
if( r != 4 )
|
|
|
|
|
{
|
2008-05-20 22:00:00 +02:00
|
|
|
|
CL_DemoCompleted();
|
2010-06-22 22:00:00 +02:00
|
|
|
|
return;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
2010-06-21 22:00:00 +02:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( curSize == -1 )
|
2010-06-22 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
CL_DemoCompleted();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( curSize > sizeof( buf_data ))
|
2010-06-22 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
Host_Error( "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN\n" );
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-06-21 22:00:00 +02:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
// init the message (set maxsize to cursize so overflow check will be working properly)
|
|
|
|
|
BF_Init( &buf, "DemoRead", buf_data, curSize );
|
|
|
|
|
r = FS_Read( cls.demofile, buf.pData, curSize );
|
|
|
|
|
|
|
|
|
|
if( r != curSize )
|
2010-06-22 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
MsgDev( D_ERROR, "CL_ReadDemoMessage: demo file was truncated( %d )\n", cls.state );
|
|
|
|
|
CL_DemoCompleted();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-06-21 22:00:00 +02:00
|
|
|
|
|
2010-10-09 22:00:00 +02:00
|
|
|
|
cls.connect_time = host.realtime;
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_Clear( &buf ); // reset curpos
|
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
CL_ParseServerMessage( &buf );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==============
|
|
|
|
|
CL_StopPlayback
|
|
|
|
|
|
|
|
|
|
Called when a demo file runs out, or the user starts a game
|
|
|
|
|
==============
|
|
|
|
|
*/
|
|
|
|
|
void CL_StopPlayback( void )
|
|
|
|
|
{
|
|
|
|
|
if( !cls.demoplayback ) return;
|
|
|
|
|
|
|
|
|
|
// release demofile
|
|
|
|
|
FS_Close( cls.demofile );
|
|
|
|
|
cls.demoplayback = false;
|
|
|
|
|
cls.demofile = NULL;
|
|
|
|
|
|
|
|
|
|
// let game known about movie state
|
|
|
|
|
cls.state = ca_disconnected;
|
2010-01-07 22:00:00 +01:00
|
|
|
|
cls.demoname[0] = '\0'; // clear demoname too
|
2010-11-15 22:00:00 +01:00
|
|
|
|
menu.globals->demoname[0] = '\0';
|
2010-02-07 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
if( clgame.hInstance ) clgame.dllFuncs.pfnReset(); // end of demos, stop the client
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CL_StopRecord( void )
|
|
|
|
|
{
|
2010-06-22 22:00:00 +02:00
|
|
|
|
int len = -1;
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
if( !cls.demorecording ) return;
|
2010-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-22 22:00:00 +02:00
|
|
|
|
// finish up
|
|
|
|
|
FS_Write( cls.demofile, &len, 4 );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
FS_Close( cls.demofile );
|
|
|
|
|
cls.demofile = NULL;
|
|
|
|
|
cls.demorecording = false;
|
2010-01-07 22:00:00 +01:00
|
|
|
|
cls.demoname[0] = '\0';
|
2010-11-15 22:00:00 +01:00
|
|
|
|
menu.globals->demoname[0] = '\0';
|
2010-01-07 22:00:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
CL_GetComment
|
|
|
|
|
==================
|
|
|
|
|
*/
|
2010-10-26 22:00:00 +02:00
|
|
|
|
qboolean CL_GetComment( const char *demoname, char *comment )
|
2010-01-07 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
file_t *demfile;
|
|
|
|
|
char buf_data[MAX_MSGLEN];
|
2010-08-04 22:00:00 +02:00
|
|
|
|
int r, maxClients, curSize;
|
2010-01-07 22:00:00 +01:00
|
|
|
|
string maptitle;
|
2010-08-06 22:00:00 +02:00
|
|
|
|
sizebuf_t buf;
|
2010-01-07 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
if( !comment ) return false;
|
|
|
|
|
|
|
|
|
|
demfile = FS_Open( demoname, "rb" );
|
|
|
|
|
if( !demfile )
|
|
|
|
|
{
|
|
|
|
|
com.strncpy( comment, "", MAX_STRING );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// read the first demo packet. extract info from it
|
|
|
|
|
// get the length
|
2010-08-04 22:00:00 +02:00
|
|
|
|
r = FS_Read( demfile, &curSize, 4 );
|
2010-01-07 22:00:00 +01:00
|
|
|
|
if( r != 4 )
|
|
|
|
|
{
|
|
|
|
|
FS_Close( demfile );
|
|
|
|
|
com.strncpy( comment, "<corrupted>", MAX_STRING );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( curSize == -1 )
|
2010-01-07 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
FS_Close( demfile );
|
|
|
|
|
com.strncpy( comment, "<corrupted>", MAX_STRING );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( curSize > sizeof( buf_data ))
|
2010-01-07 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
FS_Close( demfile );
|
|
|
|
|
com.strncpy( comment, "<not compatible>", MAX_STRING );
|
2010-06-22 22:00:00 +02:00
|
|
|
|
return false;
|
2010-01-07 22:00:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
// init the message (set maxsize to cursize so overflow check will be working properly)
|
|
|
|
|
BF_Init( &buf, "DemoRead", buf_data, curSize );
|
|
|
|
|
r = FS_Read( demfile, buf.pData, curSize );
|
2010-01-07 22:00:00 +01:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( r != curSize )
|
2010-01-07 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
FS_Close( demfile );
|
|
|
|
|
com.strncpy( comment, "<truncated file>", MAX_STRING );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
// skip server data ident
|
|
|
|
|
BF_ReadByte( &buf );
|
2010-01-07 22:00:00 +01:00
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
if( PROTOCOL_VERSION != BF_ReadLong( &buf ))
|
2010-01-07 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
FS_Close( demfile );
|
|
|
|
|
com.strncpy( comment, "<invalid protocol>", MAX_STRING );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-04 22:00:00 +02:00
|
|
|
|
BF_ReadLong( &buf ); // server count
|
|
|
|
|
BF_ReadByte( &buf );// playernum
|
|
|
|
|
maxClients = BF_ReadByte( &buf );
|
|
|
|
|
if( BF_ReadWord( &buf ) > GI->max_edicts )
|
2010-01-07 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
FS_Close( demfile );
|
|
|
|
|
com.strncpy( comment, "<too many edicts>", MAX_STRING );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// split comment to sections
|
2010-08-04 22:00:00 +02:00
|
|
|
|
com.strncpy( comment, BF_ReadString( &buf ), CS_SIZE ); // mapname
|
|
|
|
|
com.strncpy( maptitle, BF_ReadString( &buf ), MAX_STRING ); // maptitle
|
2010-01-07 22:00:00 +01:00
|
|
|
|
if( !com.strlen( maptitle )) com.strncpy( maptitle, "<no title>", MAX_STRING );
|
|
|
|
|
com.strncpy( comment + CS_SIZE, maptitle, CS_SIZE );
|
|
|
|
|
com.strncpy( comment + CS_SIZE * 2, va( "%i", maxClients ), CS_TIME );
|
|
|
|
|
|
|
|
|
|
// all done
|
|
|
|
|
FS_Close( demfile );
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
CL_DemoGetName
|
|
|
|
|
==================
|
|
|
|
|
*/
|
|
|
|
|
void CL_DemoGetName( int lastnum, char *filename )
|
|
|
|
|
{
|
|
|
|
|
int a, b;
|
|
|
|
|
|
|
|
|
|
if( !filename ) return;
|
|
|
|
|
if( lastnum < 0 || lastnum > 99 )
|
|
|
|
|
{
|
|
|
|
|
// bound
|
|
|
|
|
com.strcpy( filename, "demo99" );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = lastnum / 10;
|
|
|
|
|
b = lastnum % 10;
|
|
|
|
|
|
|
|
|
|
com.sprintf( filename, "demo%i%i", a, b );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
====================
|
|
|
|
|
CL_Record_f
|
|
|
|
|
|
|
|
|
|
record <demoname>
|
|
|
|
|
Begins recording a demo from the current position
|
|
|
|
|
====================
|
|
|
|
|
*/
|
|
|
|
|
void CL_Record_f( void )
|
|
|
|
|
{
|
2010-01-07 22:00:00 +01:00
|
|
|
|
const char *name;
|
2010-03-27 22:00:00 +01:00
|
|
|
|
string demoname, demopath, demoshot;
|
2010-01-07 22:00:00 +01:00
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if( Cmd_Argc() == 1 )
|
|
|
|
|
name = "new";
|
|
|
|
|
else if( Cmd_Argc() == 2 )
|
|
|
|
|
name = Cmd_Argv( 1 );
|
|
|
|
|
else
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
2010-01-07 22:00:00 +01:00
|
|
|
|
Msg( "Usage: record <demoname>\n" );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( cls.demorecording )
|
|
|
|
|
{
|
|
|
|
|
Msg("CL_Record: already recording.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( cls.state != ca_active )
|
|
|
|
|
{
|
|
|
|
|
Msg ("You must be in a level to record.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-07 22:00:00 +01:00
|
|
|
|
if( !com.stricmp( name, "new" ))
|
|
|
|
|
{
|
|
|
|
|
// scan for a free filename
|
|
|
|
|
for( n = 0; n < 100; n++ )
|
|
|
|
|
{
|
|
|
|
|
CL_DemoGetName( n, demoname );
|
|
|
|
|
if( !FS_FileExists( va( "demos/%s.dem", demoname )))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if( n == 100 )
|
|
|
|
|
{
|
|
|
|
|
Msg( "^3ERROR: no free slots for demo recording\n" );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-14 22:00:00 +02:00
|
|
|
|
else com.strncpy( demoname, name, sizeof( demoname ));
|
2010-01-07 22:00:00 +01:00
|
|
|
|
|
2008-05-20 22:00:00 +02:00
|
|
|
|
// open the demo file
|
2010-01-07 22:00:00 +01:00
|
|
|
|
com.sprintf( demopath, "demos/%s.dem", demoname );
|
2011-02-15 22:00:00 +01:00
|
|
|
|
com.sprintf( demoshot, "demos/%s.bmp", demoname );
|
2010-01-07 22:00:00 +01:00
|
|
|
|
|
2010-03-26 22:00:00 +01:00
|
|
|
|
// make sure what old demo is removed
|
2010-03-27 22:00:00 +01:00
|
|
|
|
if( FS_FileExists( demopath )) FS_Delete( demopath );
|
|
|
|
|
if( FS_FileExists( demoshot )) FS_Delete( demoshot );
|
2010-01-07 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
// write demoshot for preview
|
|
|
|
|
Cbuf_AddText( va( "demoshot \"%s\"\n", demoname ));
|
|
|
|
|
com.strncpy( cls.demoname, demoname, sizeof( cls.demoname ));
|
2010-11-15 22:00:00 +01:00
|
|
|
|
com.strncpy( menu.globals->demoname, demoname, sizeof( menu.globals->demoname ));
|
2010-07-18 22:00:00 +02:00
|
|
|
|
|
2010-01-07 22:00:00 +01:00
|
|
|
|
CL_WriteDemoHeader( demopath );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
====================
|
|
|
|
|
CL_PlayDemo_f
|
|
|
|
|
|
|
|
|
|
playdemo <demoname>
|
|
|
|
|
====================
|
|
|
|
|
*/
|
|
|
|
|
void CL_PlayDemo_f( void )
|
|
|
|
|
{
|
|
|
|
|
string filename;
|
|
|
|
|
|
2009-10-13 22:00:00 +02:00
|
|
|
|
if( Cmd_Argc() != 2 )
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
2010-01-07 22:00:00 +01:00
|
|
|
|
Msg( "Usage: playdemo <demoname>\n" );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// shutdown any game or cinematic server
|
|
|
|
|
CL_Disconnect();
|
2009-10-13 22:00:00 +02:00
|
|
|
|
Host_ShutdownServer();
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
2010-09-10 22:00:00 +02:00
|
|
|
|
com.snprintf( filename, sizeof( filename ), "demos/%s.dem", Cmd_Argv( 1 ));
|
|
|
|
|
if( !FS_FileExistsEx( filename, true ))
|
2008-05-20 22:00:00 +02:00
|
|
|
|
{
|
2009-10-13 22:00:00 +02:00
|
|
|
|
MsgDev( D_ERROR, "couldn't open %s\n", filename );
|
|
|
|
|
cls.demonum = -1; // stop demo loop
|
2008-05-20 22:00:00 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-10 22:00:00 +02:00
|
|
|
|
cls.demofile = FS_OpenEx( filename, "rb", true );
|
2009-10-13 22:00:00 +02:00
|
|
|
|
com.strncpy( cls.demoname, Cmd_Argv( 1 ), sizeof( cls.demoname ));
|
2010-11-15 22:00:00 +01:00
|
|
|
|
com.strncpy( menu.globals->demoname, Cmd_Argv( 1 ), sizeof( menu.globals->demoname ));
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
Con_Close();
|
2010-07-18 22:00:00 +02:00
|
|
|
|
UI_SetActiveMenu( false );
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
cls.demoplayback = true;
|
2010-06-22 22:00:00 +02:00
|
|
|
|
cls.state = ca_connected;
|
2009-10-13 22:00:00 +02:00
|
|
|
|
com.strncpy( cls.servername, Cmd_Argv( 1 ), sizeof( cls.servername ));
|
2008-05-20 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// begin a playback demo
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-13 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
CL_StartDemos_f
|
|
|
|
|
==================
|
|
|
|
|
*/
|
|
|
|
|
void CL_StartDemos_f( void )
|
|
|
|
|
{
|
|
|
|
|
int i, c;
|
|
|
|
|
|
|
|
|
|
c = Cmd_Argc() - 1;
|
|
|
|
|
if( c > MAX_DEMOS )
|
|
|
|
|
{
|
|
|
|
|
MsgDev( D_WARN, "Host_StartDemos: max %i demos in demoloop\n", MAX_DEMOS );
|
|
|
|
|
c = MAX_DEMOS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MsgDev( D_INFO, "%i demo%s in loop\n", c, (c > 1) ? "s" : "" );
|
|
|
|
|
|
|
|
|
|
for( i = 1; i < c + 1; i++ )
|
|
|
|
|
com.strncpy( cls.demos[i-1], Cmd_Argv( i ), sizeof( cls.demos[0] ));
|
|
|
|
|
|
|
|
|
|
if( !SV_Active() && cls.demonum != -1 && !cls.demoplayback )
|
|
|
|
|
{
|
|
|
|
|
cls.demonum = 0;
|
|
|
|
|
CL_NextDemo ();
|
|
|
|
|
}
|
|
|
|
|
else cls.demonum = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
CL_Demos_f
|
|
|
|
|
|
|
|
|
|
Return to looping demos
|
|
|
|
|
==================
|
|
|
|
|
*/
|
|
|
|
|
void CL_Demos_f( void )
|
|
|
|
|
{
|
|
|
|
|
if( cls.demonum == -1 )
|
|
|
|
|
cls.demonum = 1;
|
|
|
|
|
|
|
|
|
|
CL_Disconnect ();
|
|
|
|
|
CL_NextDemo ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-05-20 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
====================
|
|
|
|
|
CL_Stop_f
|
|
|
|
|
|
2008-08-04 22:00:00 +02:00
|
|
|
|
stop any client activity
|
2008-05-20 22:00:00 +02:00
|
|
|
|
====================
|
|
|
|
|
*/
|
|
|
|
|
void CL_Stop_f( void )
|
|
|
|
|
{
|
|
|
|
|
// stop all
|
|
|
|
|
CL_StopRecord();
|
|
|
|
|
CL_StopPlayback();
|
2008-08-04 22:00:00 +02:00
|
|
|
|
SCR_StopCinematic();
|
2010-11-06 22:00:00 +01:00
|
|
|
|
S_StopBackgroundTrack();
|
2008-08-04 22:00:00 +02:00
|
|
|
|
}
|