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 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 sprintf (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 Handles playback of demos
129 void CL_ReadDemoMessage(void)
134 if (!cls.demoplayback)
137 // LordHavoc: pausedemo
143 // decide if it is time to grab the next message
144 // always grab until fully connected
145 if (cls.signon == SIGNONS)
149 if (host_framecount == cls.td_lastframe)
151 // already read this frame's message
154 if (cls.td_lastframe == -1)
156 // render a couple frames before we start counting
157 cls.td_startframe = host_framecount + 3;
159 cls.td_lastframe = host_framecount;
160 cls.td_onesecondframes++;
161 // don't read any new messages during the warm-up period
162 if (host_framecount < cls.td_startframe)
164 // if this is the first official frame we can now grab the real
165 // td_starttime so the bogus time on the first frame doesn't
166 // count against the final report
167 if (host_framecount == cls.td_startframe)
169 cls.td_starttime = realtime;
170 cls.td_onesecondnexttime = realtime + 1;
171 cls.td_onesecondframes = 0;
172 cls.td_onesecondminframes = 0;
173 cls.td_onesecondmaxframes = 0;
174 cls.td_onesecondavgframes = 0;
175 cls.td_onesecondavgcount = 0;
177 if (realtime >= cls.td_onesecondnexttime)
179 if (cls.td_onesecondavgcount == 0)
181 cls.td_onesecondminframes = cls.td_onesecondframes;
182 cls.td_onesecondmaxframes = cls.td_onesecondframes;
184 cls.td_onesecondminframes = min(cls.td_onesecondminframes, cls.td_onesecondframes);
185 cls.td_onesecondmaxframes = max(cls.td_onesecondmaxframes, cls.td_onesecondframes);
186 cls.td_onesecondavgframes += cls.td_onesecondframes;
187 cls.td_onesecondavgcount++;
188 cls.td_onesecondframes = 0;
189 cls.td_onesecondnexttime++;
192 else if (cl.time <= cl.mtime[0])
194 // don't need another message yet
199 // get the next message
200 FS_Read(cls.demofile, &net_message.cursize, 4);
201 net_message.cursize = LittleLong(net_message.cursize);
202 if (net_message.cursize > net_message.maxsize)
203 Host_Error("Demo message (%i) > net_message.maxsize (%i)", net_message.cursize, net_message.maxsize);
204 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
205 for (i = 0;i < 3;i++)
207 r = (int)FS_Read(cls.demofile, &f, 4);
208 cl.mviewangles[0][i] = LittleFloat(f);
211 if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == net_message.cursize)
214 CL_ParseServerMessage();
216 // In case the demo contains a "svc_disconnect" message
217 if (!cls.demoplayback)
233 stop recording a demo
236 void CL_Stop_f (void)
239 unsigned char bufdata[64];
241 if (!cls.demorecording)
243 Con_Print("Not recording a demo.\n");
247 // write a disconnect message to the demo file
248 // LordHavoc: don't replace the net_message when doing this
250 buf.maxsize = sizeof(bufdata);
252 MSG_WriteByte(&buf, svc_disconnect);
253 CL_WriteDemoMessage(&buf);
256 FS_Close (cls.demofile);
258 cls.demorecording = false;
259 Con_Print("Completed demo\n");
266 record <demoname> <map> [cd track]
269 void CL_Record_f (void)
272 char name[MAX_OSPATH];
275 if (c != 2 && c != 3 && c != 4)
277 Con_Print("record <demoname> [<map> [cd track]]\n");
281 if (strstr(Cmd_Argv(1), ".."))
283 Con_Print("Relative pathnames are not allowed.\n");
287 if (c == 2 && cls.state == ca_connected)
289 Con_Print("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
293 if (cls.state == ca_connected)
296 // write the forced cd track number, or -1
299 track = atoi(Cmd_Argv(3));
300 Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
306 strlcpy (name, Cmd_Argv(1), sizeof (name));
307 FS_DefaultExtension (name, ".dem", sizeof (name));
311 Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
313 // open the demo file
314 Con_Printf("recording to %s.\n", name);
315 cls.demofile = FS_Open (name, "wb", false, false);
318 Con_Print("ERROR: couldn't open.\n");
322 cls.forcetrack = track;
323 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
325 cls.demorecording = true;
336 void CL_PlayDemo_f (void)
338 char name[MAX_QPATH];
340 qboolean neg = false;
344 Con_Print("play <demoname> : plays a demo\n");
348 // disconnect from server
350 Host_ShutdownServer ();
352 // update networking ports (this is mainly just needed at startup)
353 NetConn_UpdateSockets();
355 // open the demo file
356 strlcpy (name, Cmd_Argv(1), sizeof (name));
357 FS_DefaultExtension (name, ".dem", sizeof (name));
358 cls.protocol = PROTOCOL_QUAKE;
360 Con_Printf("Playing demo from %s.\n", name);
361 cls.demofile = FS_Open (name, "rb", false, false);
364 Con_Print("ERROR: couldn't open.\n");
365 cls.demonum = -1; // stop demo loop
369 strlcpy(cls.demoname, name, sizeof(cls.demoname));
370 cls.demoplayback = true;
371 cls.state = ca_connected;
374 while ((c = FS_Getc (cls.demofile)) != '\n')
378 cls.forcetrack = cls.forcetrack * 10 + (c - '0');
381 cls.forcetrack = -cls.forcetrack;
390 void CL_FinishTimeDemo (void)
393 double time, totalfpsavg;
394 double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
396 cls.timedemo = false;
398 // the first frame didn't count
399 frames = (host_framecount - cls.td_startframe) - 1;
400 time = realtime - cls.td_starttime;
401 totalfpsavg = time > 0 ? frames / time : 0;
402 fpsmin = cls.td_onesecondminframes;
403 fpsavg = cls.td_onesecondavgcount ? cls.td_onesecondavgframes / cls.td_onesecondavgcount : 0;
404 fpsmax = cls.td_onesecondmaxframes;
405 // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
406 Con_Printf("%i frames %5.7f seconds %5.7f fps, one-second min/avg/max: %.0f %.0f %.0f\n", frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax);
407 Log_Printf("benchmark.log", "date %s | enginedate %s | demo %s | commandline %s | result %i frames %5.7f seconds %5.7f fps, one-second min/avg/max: %.0f %.0f %.0f\n", Sys_TimeString("%Y-%m-%d %H:%M:%S"), buildstring, cls.demoname, cmdline.string, frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax);
408 if (COM_CheckParm("-benchmark"))
419 void CL_TimeDemo_f (void)
423 Con_Print("timedemo <demoname> : gets demo speeds\n");
427 srand(0); // predictable random sequence for benchmarking
431 // cls.td_starttime will be grabbed at the second frame of the demo, so
432 // all the loading time doesn't get counted
434 // instantly hide console and deactivate it
436 key_consoleactive = 0;
440 // get first message this frame
441 cls.td_lastframe = -1;