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 (!cls.demostarting) // only quit if not starting another demo
92 if (COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
101 Dumps the current net message, prefixed by the length and view angles
102 #====================
104 void CL_WriteDemoMessage (sizebuf_t *message)
110 if (cls.demopaused) // LordHavoc: pausedemo
113 len = LittleLong (message->cursize);
114 FS_Write (cls.demofile, &len, 4);
115 for (i=0 ; i<3 ; i++)
117 f = LittleFloat (cl.viewangles[i]);
118 FS_Write (cls.demofile, &f, 4);
120 FS_Write (cls.demofile, message->data, message->cursize);
127 Dumps the current demo to a buffer, and resets the demo to its starting point.
128 Used to insert csprogs.dat files as a download to the beginning of a demo file.
131 void CL_CutDemo (unsigned char **buf, fs_offset_t *filesize)
136 FS_Close(cls.demofile);
137 *buf = FS_LoadFile(cls.demoname, tempmempool, false, filesize);
139 // restart the demo recording
140 cls.demofile = FS_OpenRealFile(cls.demoname, "wb", false);
142 Sys_Error("failed to reopen the demo file");
143 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
150 Adds the cut stuff back to the demo. Also frees the buffer.
151 Used to insert csprogs.dat files as a download to the beginning of a demo file.
154 void CL_PasteDemo (unsigned char **buf, fs_offset_t *filesize)
156 fs_offset_t startoffset = 0;
162 while(startoffset < *filesize && ((char *)(*buf))[startoffset] != '\n')
164 if(startoffset < *filesize)
167 FS_Write(cls.demofile, *buf + startoffset, *filesize - startoffset);
178 Handles playback of demos
181 void CL_ReadDemoMessage(void)
186 if (!cls.demoplayback)
189 // LordHavoc: pausedemo
195 // decide if it is time to grab the next message
196 // always grab until fully connected
197 if (cls.signon == SIGNONS)
202 cls.td_onesecondframes++;
203 // if this is the first official frame we can now grab the real
204 // td_starttime so the bogus time on the first frame doesn't
205 // count against the final report
206 if (cls.td_frames == 0)
208 cls.td_starttime = realtime;
209 cls.td_onesecondnexttime = cl.time + 1;
210 cls.td_onesecondrealtime = realtime;
211 cls.td_onesecondframes = 0;
212 cls.td_onesecondminfps = 0;
213 cls.td_onesecondmaxfps = 0;
214 cls.td_onesecondavgfps = 0;
215 cls.td_onesecondavgcount = 0;
217 if (cl.time >= cls.td_onesecondnexttime)
219 double fps = cls.td_onesecondframes / (realtime - cls.td_onesecondrealtime);
220 if (cls.td_onesecondavgcount == 0)
222 cls.td_onesecondminfps = fps;
223 cls.td_onesecondmaxfps = fps;
225 cls.td_onesecondrealtime = realtime;
226 cls.td_onesecondminfps = min(cls.td_onesecondminfps, fps);
227 cls.td_onesecondmaxfps = max(cls.td_onesecondmaxfps, fps);
228 cls.td_onesecondavgfps += fps;
229 cls.td_onesecondavgcount++;
230 cls.td_onesecondframes = 0;
231 cls.td_onesecondnexttime++;
234 else if (cl.time <= cl.mtime[0])
236 // don't need another message yet
241 // get the next message
242 FS_Read(cls.demofile, &cl_message.cursize, 4);
243 cl_message.cursize = LittleLong(cl_message.cursize);
244 if(cl_message.cursize & DEMOMSG_CLIENT_TO_SERVER) // This is a client->server message! Ignore for now!
246 // skip over demo packet
247 FS_Seek(cls.demofile, 12 + (cl_message.cursize & (~DEMOMSG_CLIENT_TO_SERVER)), SEEK_CUR);
250 if (cl_message.cursize > cl_message.maxsize)
252 Con_Printf("Demo message (%i) > cl_message.maxsize (%i)", cl_message.cursize, cl_message.maxsize);
253 cl_message.cursize = 0;
257 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
258 for (i = 0;i < 3;i++)
260 FS_Read(cls.demofile, &f, 4);
261 cl.mviewangles[0][i] = LittleFloat(f);
264 if (FS_Read(cls.demofile, cl_message.data, cl_message.cursize) == cl_message.cursize)
266 MSG_BeginReading(&cl_message);
267 CL_ParseServerMessage();
269 if (cls.signon != SIGNONS)
270 Cbuf_Execute(); // immediately execute svc_stufftext if in the demo before connect!
272 // In case the demo contains a "svc_disconnect" message
273 if (!cls.demoplayback)
292 stop recording a demo
295 void CL_Stop_f (void)
298 unsigned char bufdata[64];
300 if (!cls.demorecording)
302 Con_Print("Not recording a demo.\n");
306 // write a disconnect message to the demo file
307 // LordHavoc: don't replace the cl_message when doing this
309 buf.maxsize = sizeof(bufdata);
311 MSG_WriteByte(&buf, svc_disconnect);
312 CL_WriteDemoMessage(&buf);
315 if(cl_autodemo.integer && (cl_autodemo_delete.integer & 1))
317 FS_RemoveOnClose(cls.demofile);
318 Con_Print("Completed and deleted demo\n");
321 Con_Print("Completed demo\n");
322 FS_Close (cls.demofile);
324 cls.demorecording = false;
331 record <demoname> <map> [cd track]
334 void CL_Record_f (void)
337 char name[MAX_OSPATH];
341 if (c != 2 && c != 3 && c != 4)
343 Con_Print("record <demoname> [<map> [cd track]]\n");
347 if (strstr(Cmd_Argv(1), ".."))
349 Con_Print("Relative pathnames are not allowed.\n");
353 if (c == 2 && cls.state == ca_connected)
355 Con_Print("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
359 if (cls.state == ca_connected)
362 // write the forced cd track number, or -1
365 track = atoi(Cmd_Argv(3));
366 Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
372 strlcpy (name, Cmd_Argv(1), sizeof (name));
373 FS_DefaultExtension (name, ".dem", sizeof (name));
377 Cmd_ExecuteString ( va(vabuf, sizeof(vabuf), "map %s", Cmd_Argv(2)), src_command, false);
379 // open the demo file
380 Con_Printf("recording to %s.\n", name);
381 cls.demofile = FS_OpenRealFile(name, "wb", false);
384 Con_Print("ERROR: couldn't open.\n");
387 strlcpy(cls.demoname, name, sizeof(cls.demoname));
389 cls.forcetrack = track;
390 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
392 cls.demorecording = true;
393 cls.demo_lastcsprogssize = -1;
394 cls.demo_lastcsprogscrc = -1;
405 void CL_PlayDemo_f (void)
407 char name[MAX_QPATH];
409 qboolean neg = false;
414 Con_Print("play <demoname> : plays a demo\n");
418 // open the demo file
419 strlcpy (name, Cmd_Argv(1), sizeof (name));
420 FS_DefaultExtension (name, ".dem", sizeof (name));
421 f = FS_OpenVirtualFile(name, false);
424 Con_Printf("ERROR: couldn't open %s.\n", name);
425 cls.demonum = -1; // stop demo loop
429 cls.demostarting = true;
431 // disconnect from server
433 Host_ShutdownServer ();
435 // update networking ports (this is mainly just needed at startup)
436 NetConn_UpdateSockets();
438 cls.protocol = PROTOCOL_QUAKE;
440 Con_Printf("Playing demo %s.\n", name);
442 strlcpy(cls.demoname, name, sizeof(cls.demoname));
444 cls.demoplayback = true;
445 cls.state = ca_connected;
448 while ((c = FS_Getc (cls.demofile)) != '\n')
452 cls.forcetrack = cls.forcetrack * 10 + (c - '0');
455 cls.forcetrack = -cls.forcetrack;
457 cls.demostarting = false;
463 double time, totalfpsavg;
464 double fpsmin, fpsavg, fpsmax;
467 static size_t doublecmp_offset;
468 static int doublecmp_withoffset(const void *a_, const void *b_)
470 const double *a = (const double *) ((const char *) a_ + doublecmp_offset);
471 const double *b = (const double *) ((const char *) b_ + doublecmp_offset);
485 static void CL_FinishTimeDemo (void)
489 double time, totalfpsavg;
490 double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
491 static int benchmark_runs = 0;
494 cls.timedemo = false;
496 frames = cls.td_frames;
497 time = realtime - cls.td_starttime;
498 totalfpsavg = time > 0 ? frames / time : 0;
499 fpsmin = cls.td_onesecondminfps;
500 fpsavg = cls.td_onesecondavgcount ? cls.td_onesecondavgfps / cls.td_onesecondavgcount : 0;
501 fpsmax = cls.td_onesecondmaxfps;
502 // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
503 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);
504 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);
505 if (COM_CheckParm("-benchmark"))
508 i = COM_CheckParm("-benchmarkruns");
509 if(i && i + 1 < com_argc)
511 static benchmarkhistory_t *history = NULL;
513 history = (benchmarkhistory_t *)Z_Malloc(sizeof(*history) * atoi(com_argv[i + 1]));
515 history[benchmark_runs - 1].frames = frames;
516 history[benchmark_runs - 1].time = time;
517 history[benchmark_runs - 1].totalfpsavg = totalfpsavg;
518 history[benchmark_runs - 1].fpsmin = fpsmin;
519 history[benchmark_runs - 1].fpsavg = fpsavg;
520 history[benchmark_runs - 1].fpsmax = fpsmax;
522 if(atoi(com_argv[i + 1]) > benchmark_runs)
524 // restart the benchmark
525 Cbuf_AddText(va(vabuf, sizeof(vabuf), "timedemo %s\n", cls.demoname));
526 // cannot execute here
531 int first = COM_CheckParm("-benchmarkruns_skipfirst") ? 1 : 0;
532 if(benchmark_runs > first)
535 for(i = first; i < benchmark_runs; ++i) if((i == first) || (history[i].f < f)) f = history[i].f
538 for(i = first; i < benchmark_runs; ++i) if((i == first) || (history[i].f > f)) f = history[i].f
541 doublecmp_offset = (char *)&history->f - (char *)history; \
542 qsort(history + first, benchmark_runs - first, sizeof(*history), doublecmp_withoffset); \
543 if((first + benchmark_runs) & 1) \
544 f = history[(first + benchmark_runs - 1) / 2].f; \
546 f = (history[(first + benchmark_runs - 2) / 2].f + history[(first + benchmark_runs) / 2].f) / 2
554 Con_Printf("MIN: %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);
562 Con_Printf("MED: %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);
570 Con_Printf("MAX: %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);
589 void CL_TimeDemo_f (void)
593 Con_Print("timedemo <demoname> : gets demo speeds\n");
597 srand(0); // predictable random sequence for benchmarking
601 // cls.td_starttime will be grabbed at the second frame of the demo, so
602 // all the loading time doesn't get counted
604 // instantly hide console and deactivate it
606 key_consoleactive = 0;
610 cls.td_frames = -2; // skip the first frame
611 cls.demonum = -1; // stop demo loop
612 cls.demonum = -1; // stop demo loop