2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 extern cvar_t cl_capturevideo;
26 static void CL_FinishTimeDemo (void);
29 ==============================================================================
33 When a demo is playing back, all outgoing network messages are skipped, and
34 incoming messages are read from the demo file.
36 Whenever cl.time gets past the last received message, another message is
37 read from the demo file.
38 ==============================================================================
45 Called to play the next demo in the demo loop
48 void CL_NextDemo (void)
50 char str[MAX_INPUTLINE];
52 if (cls.demonum == -1)
53 return; // don't play demos
55 if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
58 if (!cls.demos[cls.demonum][0])
60 Con_Print("No demos listed with startdemos\n");
66 dpsnprintf (str, sizeof(str), "playdemo %s\n", cls.demos[cls.demonum]);
67 Cbuf_InsertText (str);
75 Called when a demo file runs out, or the user starts a game
78 // LordHavoc: now called only by CL_Disconnect
79 void CL_StopPlayback (void)
81 if (!cls.demoplayback)
84 FS_Close (cls.demofile);
85 cls.demoplayback = false;
91 if (COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
100 Dumps the current net message, prefixed by the length and view angles
103 void CL_WriteDemoMessage (sizebuf_t *message)
109 if (cls.demopaused) // LordHavoc: pausedemo
112 len = LittleLong (message->cursize);
113 FS_Write (cls.demofile, &len, 4);
114 for (i=0 ; i<3 ; i++)
116 f = LittleFloat (cl.viewangles[i]);
117 FS_Write (cls.demofile, &f, 4);
119 FS_Write (cls.demofile, message->data, message->cursize);
126 Dumps the current demo to a buffer, and resets the demo to its starting point.
127 Used to insert csprogs.dat files as a download to the beginning of a demo file.
130 void CL_CutDemo (unsigned char **buf, fs_offset_t *filesize)
135 FS_Close(cls.demofile);
136 *buf = FS_LoadFile(cls.demoname, tempmempool, false, filesize);
138 // restart the demo recording
139 cls.demofile = FS_OpenRealFile(cls.demoname, "wb", false);
141 Sys_Error("failed to reopen the demo file");
142 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
149 Adds the cut stuff back to the demo. Also frees the buffer.
150 Used to insert csprogs.dat files as a download to the beginning of a demo file.
153 void CL_PasteDemo (unsigned char **buf, fs_offset_t *filesize)
155 fs_offset_t startoffset = 0;
161 while(startoffset < *filesize && ((char *)(*buf))[startoffset] != '\n')
163 if(startoffset < *filesize)
166 FS_Write(cls.demofile, *buf + startoffset, *filesize - startoffset);
177 Handles playback of demos
180 void CL_ReadDemoMessage(void)
185 if (!cls.demoplayback)
188 // LordHavoc: pausedemo
194 // decide if it is time to grab the next message
195 // always grab until fully connected
196 if (cls.signon == SIGNONS)
201 cls.td_onesecondframes++;
202 // if this is the first official frame we can now grab the real
203 // td_starttime so the bogus time on the first frame doesn't
204 // count against the final report
205 if (cls.td_frames == 0)
207 cls.td_starttime = realtime;
208 cls.td_onesecondnexttime = cl.time + 1;
209 cls.td_onesecondrealtime = realtime;
210 cls.td_onesecondframes = 0;
211 cls.td_onesecondminfps = 0;
212 cls.td_onesecondmaxfps = 0;
213 cls.td_onesecondavgfps = 0;
214 cls.td_onesecondavgcount = 0;
216 if (cl.time >= cls.td_onesecondnexttime)
218 double fps = cls.td_onesecondframes / (realtime - cls.td_onesecondrealtime);
219 if (cls.td_onesecondavgcount == 0)
221 cls.td_onesecondminfps = fps;
222 cls.td_onesecondmaxfps = fps;
224 cls.td_onesecondrealtime = realtime;
225 cls.td_onesecondminfps = min(cls.td_onesecondminfps, fps);
226 cls.td_onesecondmaxfps = max(cls.td_onesecondmaxfps, fps);
227 cls.td_onesecondavgfps += fps;
228 cls.td_onesecondavgcount++;
229 cls.td_onesecondframes = 0;
230 cls.td_onesecondnexttime++;
233 else if (cl.time <= cl.mtime[0])
235 // don't need another message yet
240 // get the next message
241 FS_Read(cls.demofile, &cl_message.cursize, 4);
242 cl_message.cursize = LittleLong(cl_message.cursize);
243 if(cl_message.cursize & DEMOMSG_CLIENT_TO_SERVER) // This is a client->server message! Ignore for now!
245 // skip over demo packet
246 FS_Seek(cls.demofile, 12 + (cl_message.cursize & (~DEMOMSG_CLIENT_TO_SERVER)), SEEK_CUR);
249 if (cl_message.cursize > cl_message.maxsize)
251 Con_Printf("Demo message (%i) > cl_message.maxsize (%i)", cl_message.cursize, cl_message.maxsize);
252 cl_message.cursize = 0;
256 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
257 for (i = 0;i < 3;i++)
259 FS_Read(cls.demofile, &f, 4);
260 cl.mviewangles[0][i] = LittleFloat(f);
263 if (FS_Read(cls.demofile, cl_message.data, cl_message.cursize) == cl_message.cursize)
265 MSG_BeginReading(&cl_message);
266 CL_ParseServerMessage();
268 if (cls.signon != SIGNONS)
269 Cbuf_Execute(); // immediately execute svc_stufftext if in the demo before connect!
271 // In case the demo contains a "svc_disconnect" message
272 if (!cls.demoplayback)
291 stop recording a demo
294 void CL_Stop_f (void)
297 unsigned char bufdata[64];
299 if (!cls.demorecording)
301 Con_Print("Not recording a demo.\n");
305 // write a disconnect message to the demo file
306 // LordHavoc: don't replace the cl_message when doing this
308 buf.maxsize = sizeof(bufdata);
310 MSG_WriteByte(&buf, svc_disconnect);
311 CL_WriteDemoMessage(&buf);
314 if(cl_autodemo.integer && (cl_autodemo_delete.integer & 1))
316 FS_RemoveOnClose(cls.demofile);
317 Con_Print("Completed and deleted demo\n");
320 Con_Print("Completed demo\n");
321 FS_Close (cls.demofile);
323 cls.demorecording = false;
330 record <demoname> <map> [cd track]
333 void CL_Record_f (void)
336 char name[MAX_OSPATH];
340 if (c != 2 && c != 3 && c != 4)
342 Con_Print("record <demoname> [<map> [cd track]]\n");
346 if (strstr(Cmd_Argv(1), ".."))
348 Con_Print("Relative pathnames are not allowed.\n");
352 if (c == 2 && cls.state == ca_connected)
354 Con_Print("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
358 if (cls.state == ca_connected)
361 // write the forced cd track number, or -1
364 track = atoi(Cmd_Argv(3));
365 Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
371 strlcpy (name, Cmd_Argv(1), sizeof (name));
372 FS_DefaultExtension (name, ".dem", sizeof (name));
376 Cmd_ExecuteString ( va(vabuf, sizeof(vabuf), "map %s", Cmd_Argv(2)), src_command, false);
378 // open the demo file
379 Con_Printf("recording to %s.\n", name);
380 cls.demofile = FS_OpenRealFile(name, "wb", false);
383 Con_Print("ERROR: couldn't open.\n");
386 strlcpy(cls.demoname, name, sizeof(cls.demoname));
388 cls.forcetrack = track;
389 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
391 cls.demorecording = true;
392 cls.demo_lastcsprogssize = -1;
393 cls.demo_lastcsprogscrc = -1;
404 void CL_PlayDemo_f (void)
406 char name[MAX_QPATH];
408 qboolean neg = false;
412 Con_Print("play <demoname> : plays a demo\n");
416 // disconnect from server
418 Host_ShutdownServer ();
420 // update networking ports (this is mainly just needed at startup)
421 NetConn_UpdateSockets();
423 // open the demo file
424 strlcpy (name, Cmd_Argv(1), sizeof (name));
425 FS_DefaultExtension (name, ".dem", sizeof (name));
426 cls.protocol = PROTOCOL_QUAKE;
428 Con_Printf("Playing demo %s.\n", name);
429 cls.demofile = FS_OpenVirtualFile(name, false);
432 Con_Print("ERROR: couldn't open.\n");
433 cls.demonum = -1; // stop demo loop
436 strlcpy(cls.demoname, name, sizeof(cls.demoname));
438 cls.demoplayback = true;
439 cls.state = ca_connected;
442 while ((c = FS_Getc (cls.demofile)) != '\n')
446 cls.forcetrack = cls.forcetrack * 10 + (c - '0');
449 cls.forcetrack = -cls.forcetrack;
458 static void CL_FinishTimeDemo (void)
462 double time, totalfpsavg;
463 double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
464 static int benchmark_runs = 0;
467 cls.timedemo = false;
469 frames = cls.td_frames;
470 time = realtime - cls.td_starttime;
471 totalfpsavg = time > 0 ? frames / time : 0;
472 fpsmin = cls.td_onesecondminfps;
473 fpsavg = cls.td_onesecondavgcount ? cls.td_onesecondavgfps / cls.td_onesecondavgcount : 0;
474 fpsmax = cls.td_onesecondmaxfps;
475 // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
476 Con_Printf("%i frames %5.7f seconds %5.7f fps, one-second fps min/avg/max: %.0f %.0f %.0f (%i seconds)\n", frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax, cls.td_onesecondavgcount);
477 Log_Printf("benchmark.log", "date %s | enginedate %s | demo %s | commandline %s | run %d | result %i frames %5.7f seconds %5.7f fps, one-second fps min/avg/max: %.0f %.0f %.0f (%i seconds)\n", Sys_TimeString("%Y-%m-%d %H:%M:%S"), buildstring, cls.demoname, cmdline.string, benchmark_runs + 1, frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax, cls.td_onesecondavgcount);
478 if (COM_CheckParm("-benchmark"))
481 i = COM_CheckParm("-benchmarkruns");
482 if(i && i + 1 < com_argc)
484 if(atoi(com_argv[i + 1]) > benchmark_runs)
486 // restart the benchmark
487 Cbuf_AddText(va(vabuf, sizeof(vabuf), "timedemo %s\n", cls.demoname));
488 // cannot execute here
505 void CL_TimeDemo_f (void)
509 Con_Print("timedemo <demoname> : gets demo speeds\n");
513 srand(0); // predictable random sequence for benchmarking
517 // cls.td_starttime will be grabbed at the second frame of the demo, so
518 // all the loading time doesn't get counted
520 // instantly hide console and deactivate it
522 key_consoleactive = 0;
526 cls.td_frames = -2; // skip the first frame
527 cls.demonum = -1; // stop demo loop
528 cls.demonum = -1; // stop demo loop