Reduce the diffs in cl_demo.c.

Starting to look like a change in protocol rather than a complete rewrite.
This commit is contained in:
Bill Currie 2011-08-24 20:33:05 +09:00
parent d508da3cf0
commit 514f085e88
5 changed files with 402 additions and 308 deletions

View file

@ -348,6 +348,7 @@ float CL_KeyState (kbutton_t *key);
// cl_demo.c // cl_demo.c
void CL_StopPlayback (void); void CL_StopPlayback (void);
void CL_StopRecording (void); void CL_StopRecording (void);
void CL_Record (const char *argv1, int track);
int CL_GetMessage (void); int CL_GetMessage (void);
void CL_Demo_Init (void); void CL_Demo_Init (void);

View file

@ -1,7 +1,7 @@
/* /*
cl_demo.c cl_demo.c
@description@ demo playback support
Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 1996-1997 Id Software, Inc.
@ -56,50 +56,59 @@ typedef struct {
double fps; double fps;
} td_stats_t; } td_stats_t;
int demo_timeframes_isactive;
int demo_timeframes_index;
char demoname[1024]; char demoname[1024];
double *demo_timeframes_array;
#define CL_TIMEFRAMES_ARRAYBLOCK 4096
int timedemo_count; int timedemo_count;
int timedemo_runs; int timedemo_runs;
td_stats_t *timedemo_data; td_stats_t *timedemo_data;
static void CL_FinishTimeDemo (void); static void CL_FinishTimeDemo (void);
static void CL_TimeFrames_DumpLog (void);
static void CL_TimeFrames_AddTimestamp (void);
static void CL_TimeFrames_Reset (void);
cvar_t *demo_gzip; cvar_t *demo_gzip;
cvar_t *demo_speed; cvar_t *demo_speed;
cvar_t *demo_quit; cvar_t *demo_quit;
cvar_t *demo_timeframes;
#define MAX_DEMMSG (MAX_MSGLEN)
/* /*
DEMO CODE DEMO CODE
When a demo is playing back, all NET_SendMessages are skipped, and When a demo is playing back, all NET_SendMessages are skipped, and
NET_GetMessages are read from the demo file. NET_GetMessages are read from the demo file.
Whenever cl.time gets past the last received message, another message is Whenever cl.time gets past the last received message, another message is
read from the demo file. read from the demo file.
*/ */
/* /*
CL_WriteDemoMessage CL_WriteDemoMessage
Dumps the current net message, prefixed by the length and view angles Dumps the current net message, prefixed by the length and view angles
*/ */
static void static void
CL_WriteDemoMessage (void) CL_WriteDemoMessage (sizebuf_t *msg)
{ {
float f;
int len; int len;
int i; int i;
float f;
len = LittleLong (net_message->message->cursize); len = LittleLong (msg->cursize);
Qwrite (cls.demofile, &len, 4); Qwrite (cls.demofile, &len, 4);
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
f = LittleFloat (cl.viewangles[i]); f = LittleFloat (cl.viewangles[i]);
Qwrite (cls.demofile, &f, 4); Qwrite (cls.demofile, &f, 4);
} }
Qwrite (cls.demofile, net_message->message->data, Qwrite (cls.demofile, msg->data, msg->cursize);
net_message->message->cursize);
Qflush (cls.demofile); Qflush (cls.demofile);
} }
@ -115,78 +124,75 @@ CL_StopPlayback (void)
return; return;
Qclose (cls.demofile); Qclose (cls.demofile);
cls.demoplayback = false;
cls.demofile = NULL; cls.demofile = NULL;
CL_SetState (ca_disconnected); CL_SetState (ca_disconnected);
cls.demoplayback = 0;
if (cls.timedemo) if (cls.timedemo)
CL_FinishTimeDemo (); CL_FinishTimeDemo ();
} }
void void
CL_StopRecording (void) CL_StopRecording (void)
{ {
// write a disconnect message to the demo file // write a disconnect message to the demo file
SZ_Clear (net_message->message); SZ_Clear (net_message->message);
MSG_WriteByte (net_message->message, svc_disconnect); MSG_WriteByte (net_message->message, svc_disconnect);
CL_WriteDemoMessage (); CL_WriteDemoMessage (net_message->message);
// finish up // finish up
Qclose (cls.demofile); Qclose (cls.demofile);
cls.demofile = NULL; cls.demofile = NULL;
cls.demorecording = false; cls.demorecording = false;
Sys_Printf ("Completed demo\n"); Sys_Printf ("Completed demo\n");
} }
static int
/* CL_GetDemoMessage (void)
CL_GetMessage
Handles recording and playback of demos, on top of NET_ code
*/
int
CL_GetMessage (void)
{ {
int r, i; int i, r;
float f; float f;
if (cls.demoplayback) { // decide if it is time to grab the next message
// decide if it is time to grab the next message if (cls.signon == SIGNONS) { // always grab until fully connected
if (cls.signon == SIGNONS) { // always grab until fully connected if (cls.timedemo) {
if (cls.timedemo) { if (host_framecount == cls.td_lastframe)
if (host_framecount == cls.td_lastframe) return 0; // already read this frame's message
return 0; // already read this frame's message cls.td_lastframe = host_framecount;
cls.td_lastframe = host_framecount; // if this is the second frame, grab the real td_starttime
// if this is the second frame, grab the real td_starttime // so the bogus time on the first frame doesn't count
// so the bogus time on the first frame doesn't count if (host_framecount == cls.td_startframe + 1)
if (host_framecount == cls.td_startframe + 1) cls.td_starttime = realtime;
cls.td_starttime = realtime; } else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0]) {
} else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0]) { return 0; // don't need another message yet
return 0; // don't need another message yet
}
} }
// get the next message
Qread (cls.demofile, &net_message->message->cursize, 4);
VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
for (i = 0; i < 3; i++) {
r = Qread (cls.demofile, &f, 4);
cl.mviewangles[0][i] = LittleFloat (f);
}
net_message->message->cursize =
LittleLong (net_message->message->cursize);
if (net_message->message->cursize > MAX_MSGLEN)
Sys_Error ("Demo message > MAX_MSGLEN");
r = Qread (cls.demofile, net_message->message->data,
net_message->message->cursize);
if (r != net_message->message->cursize) {
CL_StopPlayback ();
return 0;
}
return 1;
} }
// get the next message
Qread (cls.demofile, &net_message->message->cursize, 4);
net_message->message->cursize =
LittleLong (net_message->message->cursize);
if (net_message->message->cursize > MAX_DEMMSG)
Host_Error ("Demo message > MAX_DEMMSG: %d/%d",
net_message->message->cursize, MAX_DEMMSG);
VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
for (i = 0; i < 3; i++) {
r = Qread (cls.demofile, &f, 4);
cl.mviewangles[0][i] = LittleFloat (f);
}
r = Qread (cls.demofile, net_message->message->data,
net_message->message->cursize);
if (r != net_message->message->cursize) {
CL_StopPlayback ();
return 0;
}
return 1;
}
static int
CL_GetPacket (void)
{
int r;
while (1) { while (1) {
r = NET_GetMessage (cls.netcon); r = NET_GetMessage (cls.netcon);
@ -201,13 +207,34 @@ CL_GetMessage (void)
else else
break; break;
} }
if (cls.demorecording)
CL_WriteDemoMessage ();
return r; return r;
} }
/*
CL_GetMessage
Handles recording and playback of demos, on top of NET_ code
*/
int
CL_GetMessage (void)
{
if (cls.demoplayback) {
int ret = CL_GetDemoMessage ();
if (!ret && demo_timeframes_isactive && cls.td_starttime) {
CL_TimeFrames_AddTimestamp ();
}
return ret;
}
if (!CL_GetPacket ())
return 0;
if (cls.demorecording)
CL_WriteDemoMessage (net_message->message);
return 1;
}
/* /*
CL_Stop_f CL_Stop_f
@ -227,7 +254,6 @@ CL_Stop_f (void)
CL_StopRecording (); CL_StopRecording ();
} }
/* /*
CL_Record_f CL_Record_f
@ -237,7 +263,6 @@ static void
CL_Record_f (void) CL_Record_f (void)
{ {
int c; int c;
dstring_t *name;
int track; int track;
if (cmd_source != src_command) if (cmd_source != src_command)
@ -266,16 +291,23 @@ CL_Record_f (void)
} else } else
track = -1; track = -1;
name = dstring_new ();
dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1));
// start the map up // start the map up
// //
if (c > 2) if (c > 2)
Cmd_ExecuteString (va ("map %s", Cmd_Argv (2)), src_command); Cmd_ExecuteString (va ("map %s", Cmd_Argv (2)), src_command);
// open the demo file CL_Record (Cmd_Argv (1), track);
// }
void
CL_Record (const char *argv1, int track)
{
dstring_t *name;
name = dstring_new ();
dsprintf (name, "%s/%s", qfs_gamedir->dir.def, argv1);
// open the demo file
#ifdef HAVE_ZLIB #ifdef HAVE_ZLIB
if (demo_gzip->int_val) { if (demo_gzip->int_val) {
QFS_DefaultExtension (name, ".dem.gz"); QFS_DefaultExtension (name, ".dem.gz");
@ -299,7 +331,6 @@ CL_Record_f (void)
dstring_delete (name); dstring_delete (name);
} }
static void static void
CL_StartDemo (void) CL_StartDemo (void)
{ {
@ -307,12 +338,10 @@ CL_StartDemo (void)
int c; int c;
qboolean neg = false; qboolean neg = false;
// disconnect from server // disconnect from server
//
CL_Disconnect (); CL_Disconnect ();
// open the demo file // open the demo file
//
name = dstring_strdup (demoname); name = dstring_strdup (demoname);
QFS_DefaultExtension (name, ".dem"); QFS_DefaultExtension (name, ".dem");
@ -356,7 +385,7 @@ CL_PlayDemo_f (void)
Sys_Printf ("play <demoname> : plays a demo\n"); Sys_Printf ("play <demoname> : plays a demo\n");
return; return;
} }
timedemo_runs = timedemo_count = 1; // make sure looped timedemos stop timedemo_runs = timedemo_count = 1; // make sure looped timedemos stop
strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); strncpy (demoname, Cmd_Argv (1), sizeof (demoname));
CL_StartDemo (); CL_StartDemo ();
} }
@ -373,6 +402,10 @@ CL_StartTimeDemo (void)
cls.td_starttime = 0; cls.td_starttime = 0;
cls.td_startframe = host_framecount; cls.td_startframe = host_framecount;
cls.td_lastframe = -1; // get a new message this frame cls.td_lastframe = -1; // get a new message this frame
CL_TimeFrames_Reset ();
if (demo_timeframes->int_val)
demo_timeframes_isactive = 1;
} }
static inline double static inline double
@ -397,6 +430,9 @@ CL_FinishTimeDemo (void)
Sys_Printf ("%i frame%s %.4g seconds %.4g fps\n", frames, Sys_Printf ("%i frame%s %.4g seconds %.4g fps\n", frames,
frames == 1 ? "" : "s", time, frames / time); frames == 1 ? "" : "s", time, frames / time);
CL_TimeFrames_DumpLog ();
demo_timeframes_isactive = 0;
timedemo_count--; timedemo_count--;
if (timedemo_data) { if (timedemo_data) {
timedemo_data[timedemo_count].frames = frames; timedemo_data[timedemo_count].frames = frames;
@ -432,7 +468,6 @@ CL_FinishTimeDemo (void)
} }
} }
/* /*
CL_TimeDemo_f CL_TimeDemo_f
@ -450,15 +485,15 @@ CL_TimeDemo_f (void)
Sys_Printf ("timedemo <demoname> [count]: gets demo speeds\n"); Sys_Printf ("timedemo <demoname> [count]: gets demo speeds\n");
return; return;
} }
timedemo_runs = timedemo_count = 1; // make sure looped timedemos stop timedemo_runs = timedemo_count = 1; // make sure looped timedemos stop
if (Cmd_Argc () == 3) if (Cmd_Argc () == 3)
count = atoi (Cmd_Argv (2)); count = atoi (Cmd_Argv (2));
timedemo_runs = timedemo_count = 1;
if (timedemo_data) { if (timedemo_data) {
free (timedemo_data); free (timedemo_data);
timedemo_data = 0; timedemo_data = 0;
} }
timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t));
strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); strncpy (demoname, Cmd_Argv (1), sizeof (demoname));
CL_StartTimeDemo (); CL_StartTimeDemo ();
timedemo_runs = timedemo_count = max (count, 1); timedemo_runs = timedemo_count = max (count, 1);
@ -468,6 +503,10 @@ CL_TimeDemo_f (void)
void void
CL_Demo_Init (void) CL_Demo_Init (void)
{ {
demo_timeframes_isactive = 0;
demo_timeframes_index = 0;
demo_timeframes_array = NULL;
demo_gzip = Cvar_Get ("demo_gzip", "0", CVAR_ARCHIVE, NULL, demo_gzip = Cvar_Get ("demo_gzip", "0", CVAR_ARCHIVE, NULL,
"Compress demos using gzip. 0 = none, 1 = least " "Compress demos using gzip. 0 = none, 1 = least "
"compression, 9 = most compression. Compressed " "compression, 9 = most compression. Compressed "
@ -477,8 +516,60 @@ CL_Demo_Init (void)
"< 1 slow-mo, > 1 timelapse"); "< 1 slow-mo, > 1 timelapse");
demo_quit = Cvar_Get ("demo_quit", "0", CVAR_NONE, NULL, demo_quit = Cvar_Get ("demo_quit", "0", CVAR_NONE, NULL,
"automaticly quit after a timedemo has finished"); "automaticly quit after a timedemo has finished");
Cmd_AddCommand ("record", CL_Record_f, "No Description"); demo_timeframes = Cvar_Get ("demo_timeframes", "0", CVAR_NONE, NULL,
Cmd_AddCommand ("stop", CL_Stop_f, "No Description"); "write timestamps for every frame");
Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "No Description"); Cmd_AddCommand ("record", CL_Record_f, "Record a demo, if no filename "
Cmd_AddCommand ("timedemo", CL_TimeDemo_f, "No Description"); "argument is given\n"
"the demo will be called Year-Month-Day-Hour-Minute-"
"Mapname");
Cmd_AddCommand ("stop", CL_Stop_f, "Stop recording a demo");
Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "Play a recorded demo");
Cmd_AddCommand ("timedemo", CL_TimeDemo_f, "Play a demo as fast as your "
"hardware can. Useful for benchmarking.");
}
static void
CL_TimeFrames_Reset (void)
{
demo_timeframes_index = 0;
free (demo_timeframes_array);
demo_timeframes_array = NULL;
}
static void
CL_TimeFrames_AddTimestamp (void)
{
if (!(demo_timeframes_index % CL_TIMEFRAMES_ARRAYBLOCK))
demo_timeframes_array = realloc
(demo_timeframes_array, sizeof (demo_timeframes_array[0]) *
((demo_timeframes_index / CL_TIMEFRAMES_ARRAYBLOCK) + 1) *
CL_TIMEFRAMES_ARRAYBLOCK);
if (demo_timeframes_array == NULL)
Sys_Error ("Unable to allocate timeframes buffer");
demo_timeframes_array[demo_timeframes_index] = Sys_DoubleTime ();
demo_timeframes_index++;
}
static void
CL_TimeFrames_DumpLog (void)
{
const char *filename = "timeframes.txt";
int i;
long frame;
QFile *outputfile;
if (demo_timeframes_isactive == 0)
return;
Sys_Printf ("Dumping Timed Frames log: %s\n", filename);
outputfile = QFS_Open (filename, "w");
if (!outputfile) {
Sys_Printf ("Could not open: %s\n", filename);
return;
}
for (i = 1; i < demo_timeframes_index; i++) {
frame = (demo_timeframes_array[i] - demo_timeframes_array[i - 1]) * 1e6;
Qprintf (outputfile, "%09ld\n", frame);
}
Qclose (outputfile);
} }

View file

@ -33,15 +33,11 @@
#include "qw/protocol.h" #include "qw/protocol.h"
void CL_StopPlayback (void); void CL_StopPlayback (void);
qboolean CL_GetMessage (void); int CL_GetMessage (void);
void CL_WriteDemoCmd (usercmd_t *pcmd); void CL_WriteDemoCmd (usercmd_t *pcmd);
void CL_Stop_f (void); void CL_StopRecording (void);
void CL_Record_f (void); void CL_Record (const char *argv1, int track); // track ignored
void CL_Record (const char *argv1);
void CL_ReRecord_f (void);
void CL_PlayDemo_f (void);
void CL_TimeDemo_f (void);
void CL_Demo_Init (void); void CL_Demo_Init (void);

View file

@ -1,7 +1,7 @@
/* /*
cl_demo.c cl_demo.c
(description) demo playback support
Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 1996-1997 Id Software, Inc.
@ -28,8 +28,7 @@
# include "config.h" # include "config.h"
#endif #endif
static __attribute__ ((used)) const char rcsid[] = static __attribute__ ((used)) const char rcsid[] = "$Id$";
"$Id$";
#ifdef HAVE_STRING_H #ifdef HAVE_STRING_H
# include <string.h> # include <string.h>
@ -70,29 +69,30 @@ typedef struct {
double fps; double fps;
} td_stats_t; } td_stats_t;
int demo_timeframes_isactive; int demo_timeframes_isactive;
int demo_timeframes_index; int demo_timeframes_index;
int demotime_cached; int demotime_cached;
float nextdemotime; float nextdemotime;
char demoname[1024]; char demoname[1024];
double *demo_timeframes_array; double *demo_timeframes_array;
#define CL_TIMEFRAMES_ARRAYBLOCK 4096 #define CL_TIMEFRAMES_ARRAYBLOCK 4096
int timedemo_count; int timedemo_count;
int timedemo_runs; int timedemo_runs;
td_stats_t *timedemo_data; td_stats_t *timedemo_data;
static void CL_FinishTimeDemo (void); static void CL_FinishTimeDemo (void);
static void CL_TimeFrames_DumpLog (void); static void CL_TimeFrames_DumpLog (void);
static void CL_TimeFrames_AddTimestamp (void); static void CL_TimeFrames_AddTimestamp (void);
static void CL_TimeFrames_Reset (void); static void CL_TimeFrames_Reset (void);
cvar_t *demo_speed;
cvar_t *demo_gzip; cvar_t *demo_gzip;
cvar_t *demo_speed;
cvar_t *demo_quit; cvar_t *demo_quit;
cvar_t *demo_timeframes; cvar_t *demo_timeframes;
#define MAX_DEMMSG (MAX_MSGLEN + 8) //+8 for header
/* /*
DEMO CODE DEMO CODE
@ -104,6 +104,31 @@ cvar_t *demo_timeframes;
*/ */
/*
CL_WriteDemoMessage
Dumps the current net message, prefixed by the length and view angles
*/
static void
CL_WriteDemoMessage (sizebuf_t *msg)
{
byte c;
float f;
int len;
f = LittleFloat ((float) realtime);
Qwrite (cls.demofile, &f, sizeof (f));
c = dem_read;
Qwrite (cls.demofile, &c, sizeof (c));
len = LittleLong (msg->cursize);
Qwrite (cls.demofile, &len, 4);
Qwrite (cls.demofile, msg->data, msg->cursize);
Qflush (cls.demofile);
}
/* /*
CL_StopPlayback CL_StopPlayback
@ -127,70 +152,21 @@ CL_StopPlayback (void)
CL_FinishTimeDemo (); CL_FinishTimeDemo ();
} }
/*
CL_WriteDemoCmd
Writes the current user cmd
*/
void void
CL_WriteDemoCmd (usercmd_t *pcmd) CL_StopRecording (void)
{ {
byte c; // write a disconnect message to the demo file
float fl; SZ_Clear (net_message->message);
int i; MSG_WriteLong (net_message->message, -1); // -1 sequence means out of band
usercmd_t cmd; MSG_WriteByte (net_message->message, svc_disconnect);
MSG_WriteString (net_message->message, "EndOfDemo");
CL_WriteDemoMessage (net_message->message);
fl = LittleFloat ((float) realtime); // finish up
Qwrite (cls.demofile, &fl, sizeof (fl)); Qclose (cls.demofile);
cls.demofile = NULL;
c = dem_cmd; cls.demorecording = false;
Qwrite (cls.demofile, &c, sizeof (c)); Sys_Printf ("Completed demo\n");
// correct for byte order, bytes don't matter
cmd = *pcmd;
for (i = 0; i < 3; i++)
cmd.angles[i] = LittleFloat (cmd.angles[i]);
cmd.forwardmove = LittleShort (cmd.forwardmove);
cmd.sidemove = LittleShort (cmd.sidemove);
cmd.upmove = LittleShort (cmd.upmove);
Qwrite (cls.demofile, &cmd, sizeof (cmd));
for (i = 0; i < 3; i++) {
fl = LittleFloat (cl.viewangles[i]);
Qwrite (cls.demofile, &fl, 4);
}
Qflush (cls.demofile);
}
/*
CL_WriteDemoMessage
Dumps the current net message, prefixed by the length and view angles
*/
static void
CL_WriteDemoMessage (sizebuf_t *msg)
{
byte c;
float fl;
int len;
if (!cls.demorecording)
return;
fl = LittleFloat ((float) realtime);
Qwrite (cls.demofile, &fl, sizeof (fl));
c = dem_read;
Qwrite (cls.demofile, &c, sizeof (c));
len = LittleLong (msg->cursize);
Qwrite (cls.demofile, &len, 4);
Qwrite (cls.demofile, msg->data, msg->cursize);
Qflush (cls.demofile);
} }
#if 0 #if 0
@ -206,7 +182,7 @@ static const char *dem_names[] = {
}; };
#endif #endif
static qboolean static int
CL_GetDemoMessage (void) CL_GetDemoMessage (void)
{ {
byte c, newtime; byte c, newtime;
@ -326,11 +302,11 @@ nextdemomessage:
readit: readit:
// get the next message // get the next message
Qread (cls.demofile, &net_message->message->cursize, 4); Qread (cls.demofile, &net_message->message->cursize, 4);
net_message->message->cursize = LittleLong net_message->message->cursize =
(net_message->message->cursize); LittleLong (net_message->message->cursize);
if (net_message->message->cursize > MAX_MSGLEN + 8) //+8 for header if (net_message->message->cursize > MAX_DEMMSG)
Host_Error ("Demo message > MAX_MSGLEN + 8: %d/%d", Host_Error ("Demo message > MAX_DEMMSG: %d/%d",
net_message->message->cursize, MAX_MSGLEN + 8); net_message->message->cursize, MAX_DEMMSG);
r = Qread (cls.demofile, net_message->message->data, r = Qread (cls.demofile, net_message->message->data,
net_message->message->cursize); net_message->message->cursize);
if (r != net_message->message->cursize) { if (r != net_message->message->cursize) {
@ -352,7 +328,6 @@ readit:
} }
} }
break; break;
case dem_set: case dem_set:
Qread (cls.demofile, &i, 4); Qread (cls.demofile, &i, 4);
cls.netchan.outgoing_sequence = LittleLong (i); cls.netchan.outgoing_sequence = LittleLong (i);
@ -364,7 +339,6 @@ readit:
goto nextdemomessage; goto nextdemomessage;
} }
break; break;
case dem_multiple: case dem_multiple:
r = Qread (cls.demofile, &i, 4); r = Qread (cls.demofile, &i, 4);
if (r != 4) { if (r != 4) {
@ -374,41 +348,42 @@ readit:
cls.lastto = LittleLong (i); cls.lastto = LittleLong (i);
cls.lasttype = dem_multiple; cls.lasttype = dem_multiple;
goto readit; goto readit;
case dem_single: case dem_single:
cls.lastto = c >> 3; cls.lastto = c >> 3;
cls.lasttype = dem_single; cls.lasttype = dem_single;
goto readit; goto readit;
case dem_stats: case dem_stats:
cls.lastto = c >> 3; cls.lastto = c >> 3;
cls.lasttype = dem_stats; cls.lasttype = dem_stats;
goto readit; goto readit;
case dem_all: case dem_all:
cls.lastto = 0; cls.lastto = 0;
cls.lasttype = dem_all; cls.lasttype = dem_all;
goto readit; goto readit;
default: default:
Sys_Printf ("Corrupted demo.\n"); Sys_Printf ("Corrupted demo.\n");
CL_StopPlayback (); CL_StopPlayback ();
return 0; return 0;
} }
return 1; return 1;
} }
static int
CL_GetPacket (void)
{
return NET_GetPacket ();
}
/* /*
CL_GetMessage CL_GetMessage
Handles recording and playback of demos, on top of NET_ code Handles recording and playback of demos, on top of NET_ code
*/ */
qboolean int
CL_GetMessage (void) CL_GetMessage (void)
{ {
if (cls.demoplayback) { if (cls.demoplayback) {
qboolean ret = CL_GetDemoMessage (); int ret = CL_GetDemoMessage ();
if (!ret && demo_timeframes_isactive && cls.td_starttime) { if (!ret && demo_timeframes_isactive && cls.td_starttime) {
CL_TimeFrames_AddTimestamp (); CL_TimeFrames_AddTimestamp ();
@ -416,16 +391,56 @@ CL_GetMessage (void)
return ret; return ret;
} }
if (!NET_GetPacket ()) if (!CL_GetPacket ())
return false; return 0;
if (net_packetlog->int_val) if (net_packetlog->int_val)
Log_Incoming_Packet (net_message->message->data, Log_Incoming_Packet (net_message->message->data,
net_message->message->cursize, 1); net_message->message->cursize, 1);
CL_WriteDemoMessage (net_message->message);
return true; if (cls.demorecording)
CL_WriteDemoMessage (net_message->message);
return 1;
}
/*
CL_WriteDemoCmd
Writes the current user cmd
*/
void
CL_WriteDemoCmd (usercmd_t *pcmd)
{
byte c;
float fl;
int i;
usercmd_t cmd;
fl = LittleFloat ((float) realtime);
Qwrite (cls.demofile, &fl, sizeof (fl));
c = dem_cmd;
Qwrite (cls.demofile, &c, sizeof (c));
// correct for byte order, bytes don't matter
cmd = *pcmd;
for (i = 0; i < 3; i++)
cmd.angles[i] = LittleFloat (cmd.angles[i]);
cmd.forwardmove = LittleShort (cmd.forwardmove);
cmd.sidemove = LittleShort (cmd.sidemove);
cmd.upmove = LittleShort (cmd.upmove);
Qwrite (cls.demofile, &cmd, sizeof (cmd));
for (i = 0; i < 3; i++) {
fl = LittleFloat (cl.viewangles[i]);
Qwrite (cls.demofile, &fl, 4);
}
Qflush (cls.demofile);
} }
/* /*
@ -433,29 +448,90 @@ CL_GetMessage (void)
stop recording a demo stop recording a demo
*/ */
void static void
CL_Stop_f (void) CL_Stop_f (void)
{ {
if (!cls.demorecording) { if (!cls.demorecording) {
Sys_Printf ("Not recording a demo.\n"); Sys_Printf ("Not recording a demo.\n");
return; return;
} }
// write a disconnect message to the demo file CL_StopRecording ();
SZ_Clear (net_message->message);
MSG_WriteLong (net_message->message, -1); // -1 sequence means out of band
MSG_WriteByte (net_message->message, svc_disconnect);
MSG_WriteString (net_message->message, "EndOfDemo");
CL_WriteDemoMessage (net_message->message);
// finish up
Qclose (cls.demofile);
cls.demofile = NULL;
cls.demorecording = false;
Sys_Printf ("Completed demo\n");
} }
/* /*
CL_WriteDemoMessage CL_Record_f
record <demoname> <server>
*/
static void
CL_Record_f (void)
{
if (Cmd_Argc () > 2) {
// we use a demo name like year-month-day-hours-minutes-mapname.qwd
// if there is no argument
Sys_Printf ("record [demoname]\n");
return;
}
if (cls.demoplayback || cls.state != ca_active) {
Sys_Printf ("You must be connected to record.\n");
return;
}
if (cls.demorecording)
CL_Stop_f ();
if (Cmd_Argc () == 2)
CL_Record (Cmd_Argv (1), -1);
else
CL_Record (0, -1);
}
/*
CL_ReRecord_f
record <demoname>
*/
static void
CL_ReRecord_f (void)
{
dstring_t *name;
int c;
c = Cmd_Argc ();
if (c != 2) {
Sys_Printf ("rerecord <demoname>\n");
return;
}
if (!cls.servername || !cls.servername->str) {
Sys_Printf ("No server to which to reconnect...\n");
return;
}
if (cls.demorecording)
CL_Stop_f ();
name = dstring_newstr ();
dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1));
// open the demo file
QFS_DefaultExtension (name, ".qwd");
cls.demofile = QFS_WOpen (name->str, 0);
if (!cls.demofile) {
Sys_Printf ("ERROR: couldn't open.\n");
} else {
Sys_Printf ("recording to %s.\n", name->str);
cls.demorecording = true;
CL_Disconnect ();
CL_BeginServerConnect ();
}
dstring_delete (name);
}
/*
CL_WriteRecordDemoMessage
Dumps the current net message, prefixed by the length and view angles Dumps the current net message, prefixed by the length and view angles
*/ */
@ -512,7 +588,7 @@ CL_WriteSetDemoMessage (void)
} }
void void
CL_Record (const char *argv1) CL_Record (const char *argv1, int track)
{ {
byte buf_data[MAX_MSGLEN + 10]; // + 10 for header byte buf_data[MAX_MSGLEN + 10]; // + 10 for header
dstring_t *name; dstring_t *name;
@ -803,93 +879,21 @@ CL_Record (const char *argv1)
// done // done
} }
/*
CL_Record_f
record <demoname> <server>
*/
void
CL_Record_f (void)
{
if (Cmd_Argc () > 2) {
// we use a demo name like year-month-day-hours-minutes-mapname.qwd
// if there is no argument
Sys_Printf ("record [demoname]\n");
return;
}
if (cls.demoplayback || cls.state != ca_active) {
Sys_Printf ("You must be connected to record.\n");
return;
}
if (cls.demorecording)
CL_Stop_f ();
if (Cmd_Argc () == 2)
CL_Record (Cmd_Argv (1));
else
CL_Record (0);
}
/*
CL_ReRecord_f
record <demoname>
*/
void
CL_ReRecord_f (void)
{
dstring_t *name;
int c;
c = Cmd_Argc ();
if (c != 2) {
Sys_Printf ("rerecord <demoname>\n");
return;
}
if (!cls.servername || !cls.servername->str) {
Sys_Printf ("No server to which to reconnect...\n");
return;
}
if (cls.demorecording)
CL_Stop_f ();
name = dstring_newstr ();
dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1));
// open the demo file
QFS_DefaultExtension (name, ".qwd");
cls.demofile = QFS_WOpen (name->str, 0);
if (!cls.demofile) {
Sys_Printf ("ERROR: couldn't open.\n");
} else {
Sys_Printf ("recording to %s.\n", name->str);
cls.demorecording = true;
CL_Disconnect ();
CL_BeginServerConnect ();
}
dstring_delete (name);
}
static void static void
CL_StartDemo (void) CL_StartDemo (void)
{ {
dstring_t *name = dstring_newstr (); dstring_t *name;
// open the demo file // open the demo file
dstring_copystr (name, demoname); name = dstring_strdup (demoname);
QFS_DefaultExtension (name, ".qwd"); QFS_DefaultExtension (name, ".qwd");
Sys_Printf ("Playing demo from %s.\n", name->str); Sys_Printf ("Playing demo from %s.\n", name->str);
QFS_FOpenFile (name->str, &cls.demofile); QFS_FOpenFile (name->str, &cls.demofile);
dstring_delete (name);
if (!cls.demofile) { if (!cls.demofile) {
Sys_Printf ("ERROR: couldn't open.\n"); Sys_Printf ("ERROR: couldn't open.\n");
cls.demonum = -1; // stop demo loop cls.demonum = -1; // stop demo loop
dstring_delete (name);
return; return;
} }
@ -921,7 +925,7 @@ CL_StartDemo (void)
play [demoname] play [demoname]
*/ */
void static void
CL_PlayDemo_f (void) CL_PlayDemo_f (void)
{ {
if (Cmd_Argc () != 2) { if (Cmd_Argc () != 2) {
@ -966,8 +970,8 @@ sqr (double x)
static void static void
CL_FinishTimeDemo (void) CL_FinishTimeDemo (void)
{ {
float time; int frames;
int frames; float time;
cls.timedemo = false; cls.timedemo = false;
@ -983,13 +987,15 @@ CL_FinishTimeDemo (void)
demo_timeframes_isactive = 0; demo_timeframes_isactive = 0;
timedemo_count--; timedemo_count--;
timedemo_data[timedemo_count].frames = frames; if (timedemo_data) {
timedemo_data[timedemo_count].time = time; timedemo_data[timedemo_count].frames = frames;
timedemo_data[timedemo_count].fps = frames / time; timedemo_data[timedemo_count].time = time;
timedemo_data[timedemo_count].fps = frames / time;
}
if (timedemo_count > 0) { if (timedemo_count > 0) {
CL_StartTimeDemo (); CL_StartTimeDemo ();
} else { } else {
if (--timedemo_runs > 0) { if (--timedemo_runs > 0 && timedemo_data) {
double average = 0; double average = 0;
double variance = 0; double variance = 0;
double min, max; double min, max;
@ -1010,8 +1016,6 @@ CL_FinishTimeDemo (void)
Sys_Printf (" min/max fps: %.3f/%.3f\n", min, max); Sys_Printf (" min/max fps: %.3f/%.3f\n", min, max);
Sys_Printf ("std deviation: %.3f fps\n", sqrt (variance)); Sys_Printf ("std deviation: %.3f fps\n", sqrt (variance));
} }
free (timedemo_data);
timedemo_data = 0;
if (demo_quit->int_val) if (demo_quit->int_val)
Cbuf_InsertText (cl_cbuf, "quit\n"); Cbuf_InsertText (cl_cbuf, "quit\n");
} }
@ -1022,9 +1026,11 @@ CL_FinishTimeDemo (void)
timedemo [demoname] timedemo [demoname]
*/ */
void static void
CL_TimeDemo_f (void) CL_TimeDemo_f (void)
{ {
int count = 1;
if (Cmd_Argc () < 2 || Cmd_Argc () > 3) { if (Cmd_Argc () < 2 || Cmd_Argc () > 3) {
Sys_Printf ("timedemo <demoname> [count]: gets demo speeds\n"); Sys_Printf ("timedemo <demoname> [count]: gets demo speeds\n");
return; return;
@ -1033,17 +1039,17 @@ CL_TimeDemo_f (void)
// disconnect from server // disconnect from server
CL_Disconnect (); CL_Disconnect ();
if (Cmd_Argc () == 3) { if (Cmd_Argc () == 3)
timedemo_count = atoi (Cmd_Argv (2)); count = atoi (Cmd_Argv (2));
} else { if (timedemo_data) {
timedemo_count = 1;
}
timedemo_runs = timedemo_count = max (timedemo_count, 1);
if (timedemo_data)
free (timedemo_data); free (timedemo_data);
timedemo_data = 0;
}
timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t)); timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t));
strncpy (demoname, Cmd_Argv (1), sizeof (demoname)); strncpy (demoname, Cmd_Argv (1), sizeof (demoname));
CL_StartTimeDemo (); CL_StartTimeDemo ();
timedemo_runs = timedemo_count = max (count, 1);
timedemo_data = calloc (timedemo_runs, sizeof (td_stats_t));
} }
void void
@ -1064,6 +1070,16 @@ CL_Demo_Init (void)
"automaticly quit after a timedemo has finished"); "automaticly quit after a timedemo has finished");
demo_timeframes = Cvar_Get ("demo_timeframes", "0", CVAR_NONE, NULL, demo_timeframes = Cvar_Get ("demo_timeframes", "0", CVAR_NONE, NULL,
"write timestamps for every frame"); "write timestamps for every frame");
Cmd_AddCommand ("record", CL_Record_f, "Record a demo, if no filename "
"argument is given\n"
"the demo will be called Year-Month-Day-Hour-Minute-"
"Mapname");
Cmd_AddCommand ("rerecord", CL_ReRecord_f, "Rerecord a demo on the same "
"server");
Cmd_AddCommand ("stop", CL_Stop_f, "Stop recording a demo");
Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "Play a recorded demo");
Cmd_AddCommand ("timedemo", CL_TimeDemo_f, "Play a demo as fast as your "
"hardware can. Useful for benchmarking.");
} }
static void static void
@ -1092,9 +1108,9 @@ static void
CL_TimeFrames_DumpLog (void) CL_TimeFrames_DumpLog (void)
{ {
const char *filename = "timeframes.txt"; const char *filename = "timeframes.txt";
int i; int i;
long frame; long frame;
QFile *outputfile; QFile *outputfile;
if (demo_timeframes_isactive == 0) if (demo_timeframes_isactive == 0)
return; return;

View file

@ -486,7 +486,7 @@ CL_Disconnect (void)
CL_StopPlayback (); CL_StopPlayback ();
else if (cls.state != ca_disconnected) { else if (cls.state != ca_disconnected) {
if (cls.demorecording) if (cls.demorecording)
CL_Stop_f (); CL_StopRecording ();
final[0] = clc_stringcmd; final[0] = clc_stringcmd;
strcpy ((char *) final + 1, "drop"); strcpy ((char *) final + 1, "drop");
@ -1155,7 +1155,7 @@ CL_SetState (cactive_t state)
// Auto demo recorder stops here // Auto demo recorder stops here
if (cl_autorecord->int_val && cls.demorecording) if (cl_autorecord->int_val && cls.demorecording)
CL_Stop_f (); CL_StopRecording ();
} else if (state == ca_active) { } else if (state == ca_active) {
// entering active state // entering active state
VID_SetCaption (cls.servername->str); VID_SetCaption (cls.servername->str);
@ -1167,7 +1167,7 @@ CL_SetState (cactive_t state)
// Auto demo recorder starts here // Auto demo recorder starts here
if (cl_autorecord->int_val && !cls.demoplayback if (cl_autorecord->int_val && !cls.demoplayback
&& !cls.demorecording) && !cls.demorecording)
CL_Record (0); CL_Record (0, -1);
} }
} }
if (con_module) if (con_module)
@ -1192,18 +1192,8 @@ CL_Init (void)
Cmd_AddCommand ("version", CL_Version_f, "Report version information"); Cmd_AddCommand ("version", CL_Version_f, "Report version information");
Cmd_AddCommand ("changing", CL_Changing_f, "Used when maps are changing"); Cmd_AddCommand ("changing", CL_Changing_f, "Used when maps are changing");
Cmd_AddCommand ("disconnect", CL_Disconnect_f, "Disconnect from server"); Cmd_AddCommand ("disconnect", CL_Disconnect_f, "Disconnect from server");
Cmd_AddCommand ("record", CL_Record_f, "Record a demo, if no filename "
"argument is given\n"
"the demo will be called Year-Month-Day-Hour-Minute-"
"Mapname");
Cmd_AddCommand ("rerecord", CL_ReRecord_f, "Rerecord a demo on the same "
"server");
Cmd_AddCommand ("snap", CL_RSShot_f, "Take a screenshot and upload it to " Cmd_AddCommand ("snap", CL_RSShot_f, "Take a screenshot and upload it to "
"the server"); "the server");
Cmd_AddCommand ("stop", CL_Stop_f, "Stop recording a demo");
Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "Play a recorded demo");
Cmd_AddCommand ("timedemo", CL_TimeDemo_f, "Play a demo as fast as your "
"hardware can. Useful for benchmarking.");
Cmd_AddCommand ("maplist", Con_Maplist_f, "List maps available"); Cmd_AddCommand ("maplist", Con_Maplist_f, "List maps available");
Cmd_AddCommand ("skinlist", Con_Skinlist_f, "List skins available"); Cmd_AddCommand ("skinlist", Con_Skinlist_f, "List skins available");
Cmd_AddCommand ("skyboxlist", Con_Skyboxlist_f, "List skyboxes available"); Cmd_AddCommand ("skyboxlist", Con_Skyboxlist_f, "List skyboxes available");