/* Copyright (C) 1997-2001 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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. */ // cl_demo.c -- demo recording #include "client.h" /* ==================== CL_WriteDemoMessage Dumps the current net message, prefixed by the length ==================== */ void CL_WriteDemoMessage( msg_t *msg ) { int len, swlen; if( cls.demofile <= 0 ) { cls.demorecording = qfalse; return; } // the first eight bytes are just packet sequencing stuff len = msg->cursize-8; swlen = LittleLong( len ); // skip bad packets if( swlen ) { FS_Write( &swlen, 4, cls.demofile ); FS_Write( msg->data+8, len, cls.demofile ); } } /* ==================== CL_Stop_f stop recording a demo ==================== */ void CL_Stop_f( void ) { int len; qboolean silent = qfalse; qboolean cancel=qfalse; int arg; char name[MAX_OSPATH]; // look through all the args for(arg=1;arg Begins recording a demo from the current position ==================== */ void CL_Record_f (void) { int i; char name[MAX_OSPATH]; char buf_data[MAX_MSGLEN]; msg_t buf; int len; entity_state_t *ent; entity_state_t nullstate; int length; qboolean silent = qfalse; if( cls.state != CA_ACTIVE ) { Com_Printf( "You must be in a level to record.\n" ); return; } if( Cmd_Argc() < 2 ) { Com_Printf( "record \n" ); return; } if( Cmd_Argc() > 2 && !Q_stricmp(Cmd_Argv(2), "silent") ) { silent = qtrue; } if( cls.demoplaying ) { if( !silent ) Com_Printf( "You can't record from another demo.\n" ); return; } if( cls.demorecording ) { if( !silent ) Com_Printf( "Already recording.\n" ); return; } // // open the demo file // Q_snprintfz( name, sizeof(name), "demos/%s", Cmd_Argv(1) ); COM_DefaultExtension( name, va(".wd%d",PROTOCOL_VERSION) , sizeof(name) ); length = FS_FOpenFile( name, &cls.demofile, FS_WRITE ); if( length == -1 ) { Com_Printf( "ERROR: couldn't create the demo file.\n" ); cls.demofile = 0; return; } // store the name in case we need it later Q_snprintfz( cls.demofilename, sizeof(cls.demofilename), Cmd_Argv(1) ); if( !silent ) Com_Printf( "recording to %s.\n", name ); cls.demorecording = qtrue; // don't start saving messages until a non-delta compressed message is received cls.demowaiting = qtrue; // // write out messages to hold the startup information // MSG_Init( &buf, (qbyte *)buf_data, sizeof(buf_data) ); // send the serverdata MSG_WriteByte( &buf, svc_serverdata ); MSG_WriteLong( &buf, PROTOCOL_VERSION ); MSG_WriteLong( &buf, 0x10000 + cl.servercount ); MSG_WriteString( &buf, cl.gamedir ); MSG_WriteShort( &buf, cl.playernum ); MSG_WriteString( &buf, cl.servermessage ); MSG_WriteByte( &buf, 0 ); // no sv_bitflags (no battleye) MSG_WriteShort( &buf, -1 ); // demos don't request configstrings to the server len = LittleLong( buf.cursize ); FS_Write( &len, 4, cls.demofile ); FS_Write( buf.data, buf.cursize, cls.demofile ); buf.cursize = 0; // configstrings for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { if( cl.configstrings[i][0] ) { MSG_WriteByte( &buf, svc_servercs ); //MSG_WriteByte( &buf, svc_servercmd ); MSG_WriteString( &buf, va("cs %i \"%s\"", i, cl.configstrings[i]) ); if( buf.cursize > buf.maxsize / 2 ) { // write it out len = LittleLong( buf.cursize ); FS_Write( &len, 4, cls.demofile ); FS_Write( buf.data, buf.cursize, cls.demofile ); buf.cursize = 0; } } } // baselines memset( &nullstate, 0, sizeof(nullstate) ); for( i = 0; i < MAX_EDICTS; i++ ) { ent = &cl_baselines[i]; if( !ent->modelindex && !ent->sound && !ent->effects ) continue; MSG_WriteByte( &buf, svc_spawnbaseline ); MSG_WriteDeltaEntity( &nullstate, &cl_baselines[i], &buf, qtrue, qtrue ); if( buf.cursize > buf.maxsize / 2 ) { // write it out len = LittleLong( buf.cursize ); FS_Write( &len, 4, cls.demofile ); FS_Write( buf.data, buf.cursize, cls.demofile ); buf.cursize = 0; } } MSG_WriteByte( &buf, svc_servercs ); //MSG_WriteByte( &buf, svc_servercmd ); MSG_WriteString( &buf, "precache" ); // write it to the demo file len = LittleLong( buf.cursize ); FS_Write( &len, 4, cls.demofile ); FS_Write( buf.data, buf.cursize, cls.demofile ); // the rest of the demo file will be individual frames } //================================================================ // // WARSOW : CLIENT SIDE DEMO PLAYBACK // //================================================================ // demo file char demoName[MAX_QPATH]; int demofilehandle; int demofilelen; // demo message qbyte msgbuf[MAX_MSGLEN]; msg_t demomsg; #ifdef DEMOCAM // -- PLX // A boolean to test if demo is paused -- PLX qboolean demopause = qfalse; #endif /* ================= CL_BeginDemoAviDump ================= */ void CL_BeginDemoAviDump( void ) { cls.demoavi = qtrue; cls.demoavi_frame = 0; R_BeginAviDemo (); } /* ================= CL_StopDemoAviDump ================= */ void CL_StopDemoAviDump( void ) { cls.demoavi = qfalse; cls.demoavi_frame = 0; R_StopAviDemo (); } /* ================= CL_DemoCompleted Close the demo file and disable demo state. Called from disconnection proccess ================= */ void CL_DemoCompleted( void ) { if( cls.demoavi ) CL_StopDemoAviDump(); if( demofilehandle ) { FS_FCloseFile( demofilehandle ); demofilehandle = 0; demofilelen = 0; } cls.demoplaying = qfalse; Com_SetDemoPlaying( qfalse ); Com_Printf( "Demo completed\n" ); if( Cvar_VariableValue("sv_demo_and_quit") ) Cbuf_ExecuteText( EXEC_NOW, "quit" ); } /* ================= CL_ReadDemoMessage Read a packet from the demo file and send it to the messages parser ================= */ void CL_ReadDemoMessage( void ) { int msglen; if ( !demofilehandle ) { CL_Disconnect( NULL ); return; } if ( demofilelen <= 0 ) { CL_Disconnect( NULL ); return; } msglen = 0; // get the next message FS_Read( &msglen, 4, demofilehandle ); demofilelen -= 4; msglen = LittleLong( msglen ); if( msglen == -1 ) { CL_Disconnect( NULL ); return; } if( msglen > MAX_MSGLEN ) Com_Error( ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN" ); if( demofilelen <= 0 ) { CL_Disconnect( NULL ); return; } FS_Read( msgbuf, msglen, demofilehandle ); demofilelen -= msglen; demomsg.allowoverflow = qtrue; demomsg.maxsize = sizeof(msgbuf); demomsg.data = msgbuf; demomsg.cursize = msglen; demomsg.readcount = 0; CL_ParseServerMessage( &demomsg ); } /* ================= CL_ReadDemoPackets See if it's time to read a new demo packet ================= */ void CL_ReadDemoPackets( void ) { cls.demoplay_lastsnaptime -= cls.demoplay_framemsecs; cls.demoplay_framemsecs = 0; if( cls.demoplay_lastsnaptime > 0 ) return; #ifdef DEMOCAM // -- PLX if (!demopause) CL_ReadDemoMessage(); #else CL_ReadDemoMessage(); #endif } /* ==================== CL_StartDemo_f ==================== */ void CL_StartDemo_f( char *arg ) { char name[MAX_OSPATH]; int i; if( !arg || !arg[0] ) { Com_Printf( "demo \n" ); return; } // have to copy the argument now, since next actions will lose it Q_snprintfz( name, sizeof(name), "demos/%s", arg ); // make sure a local server is killed Cbuf_ExecuteText( EXEC_NOW, "killserver\n" ); CL_Disconnect( NULL ); // open the demo file COM_DefaultExtension( name, va(".wd%d",PROTOCOL_VERSION), sizeof(name) ); demofilelen = FS_FOpenFile( name, &demofilehandle, FS_READ ); if( !demofilehandle || demofilelen < 1 ) { Com_Printf( "No valid demo file found\n" ); FS_FCloseFile( demofilehandle ); return; } Q_strncpyz( demoName, Cmd_Argv(1), sizeof( demoName ) ); Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) ); CL_SetClientState( CA_CONNECTING ); Com_SetDemoPlaying( qtrue ); cls.demoplaying = qtrue; while( cls.state < CA_ACTIVE ) CL_ReadDemoMessage(); // read first messages cls.demoplay_lastsnaptime = 0; cls.demoplay_framemsecs = 0; // set up for timedemo settings cl.timedemo_start = 0; cl.timedemo_frames = 0; for( i = 0; i < 100; i++ ) cl.timedemo_counts[i] = 0; } /* ==================== CL_PlayDemo_f demo ==================== */ void CL_PlayDemo_f( void ) { if( Cmd_Argc() != 2 ) { Com_Printf( "demo \n" ); return; } CL_StartDemo_f( Cmd_Argv(1) ); } #ifdef DEMOCAM /* ==================== CL_PauseDemo_f Can be used in democam -- PLX demo ==================== */ void CL_PauseDemo_f( void ) { demopause = !demopause; } #endif /* ==================== CL_PlayDemoToAvi_f demoavi (if no name suplied, toogles demoavi status) ==================== */ void CL_PlayDemoToAvi_f( void ) { if( Cmd_Argc() != 2 ) { if( !cls.demoplaying ) { Com_Printf( "Usage: %sdemoavi %s or %sdemoavi%s while playing a demo\n", S_COLOR_YELLOW, S_COLOR_WHITE, S_COLOR_YELLOW, S_COLOR_WHITE ); return; } if( Cmd_Argc() < 2 ) { // toogle demoavi mode if( !cls.demoavi ) { CL_BeginDemoAviDump(); } else { CL_StopDemoAviDump(); } } return; } if( cls.demoplaying ) { CL_Disconnect( NULL ); } CL_StartDemo_f( Cmd_Argv(1) ); CL_BeginDemoAviDump(); } //[end]