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)
150 cls.td_onesecondframes++;
151 // if this is the first official frame we can now grab the real
152 // td_starttime so the bogus time on the first frame doesn't
153 // count against the final report
154 if (cls.td_frames == 0)
156 cls.td_starttime = realtime;
157 cls.td_onesecondnexttime = cl.time + 1;
158 cls.td_onesecondrealtime = realtime;
159 cls.td_onesecondframes = 0;
160 cls.td_onesecondminfps = 0;
161 cls.td_onesecondmaxfps = 0;
162 cls.td_onesecondavgfps = 0;
163 cls.td_onesecondavgcount = 0;
165 if (cl.time >= cls.td_onesecondnexttime)
167 double fps = cls.td_onesecondframes / (realtime - cls.td_onesecondrealtime);
168 if (cls.td_onesecondavgcount == 0)
170 cls.td_onesecondminfps = fps;
171 cls.td_onesecondmaxfps = fps;
173 cls.td_onesecondrealtime = realtime;
174 cls.td_onesecondminfps = min(cls.td_onesecondminfps, fps);
175 cls.td_onesecondmaxfps = max(cls.td_onesecondmaxfps, fps);
176 cls.td_onesecondavgfps += fps;
177 cls.td_onesecondavgcount++;
178 cls.td_onesecondframes = 0;
179 cls.td_onesecondnexttime++;
182 else if (cl.time <= cl.mtime[0])
184 // don't need another message yet
189 // get the next message
190 FS_Read(cls.demofile, &net_message.cursize, 4);
191 net_message.cursize = LittleLong(net_message.cursize);
192 if(net_message.cursize & DEMOMSG_CLIENT_TO_SERVER) // This is a client->server message! Ignore for now!
194 // skip over demo packet
195 FS_Seek(cls.demofile, 12 + (net_message.cursize & (~DEMOMSG_CLIENT_TO_SERVER)), SEEK_CUR);
198 if (net_message.cursize > net_message.maxsize)
199 Host_Error("Demo message (%i) > net_message.maxsize (%i)", net_message.cursize, net_message.maxsize);
200 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
201 for (i = 0;i < 3;i++)
203 r = (int)FS_Read(cls.demofile, &f, 4);
204 cl.mviewangles[0][i] = LittleFloat(f);
207 if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == net_message.cursize)
210 CL_ParseServerMessage();
212 // In case the demo contains a "svc_disconnect" message
213 if (!cls.demoplayback)
232 stop recording a demo
235 void CL_Stop_f (void)
238 unsigned char bufdata[64];
240 if (!cls.demorecording)
242 Con_Print("Not recording a demo.\n");
246 // write a disconnect message to the demo file
247 // LordHavoc: don't replace the net_message when doing this
249 buf.maxsize = sizeof(bufdata);
251 MSG_WriteByte(&buf, svc_disconnect);
252 CL_WriteDemoMessage(&buf);
255 FS_Close (cls.demofile);
257 cls.demorecording = false;
258 Con_Print("Completed demo\n");
265 record <demoname> <map> [cd track]
268 void CL_Record_f (void)
271 char name[MAX_OSPATH];
274 if (c != 2 && c != 3 && c != 4)
276 Con_Print("record <demoname> [<map> [cd track]]\n");
280 if (strstr(Cmd_Argv(1), ".."))
282 Con_Print("Relative pathnames are not allowed.\n");
286 if (c == 2 && cls.state == ca_connected)
288 Con_Print("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
292 if (cls.state == ca_connected)
295 // write the forced cd track number, or -1
298 track = atoi(Cmd_Argv(3));
299 Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
305 strlcpy (name, Cmd_Argv(1), sizeof (name));
306 FS_DefaultExtension (name, ".dem", sizeof (name));
310 Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
312 // open the demo file
313 Con_Printf("recording to %s.\n", name);
314 cls.demofile = FS_Open (name, "wb", false, false);
317 Con_Print("ERROR: couldn't open.\n");
321 cls.forcetrack = track;
322 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
324 cls.demorecording = true;
335 void CL_PlayDemo_f (void)
337 char name[MAX_QPATH];
339 qboolean neg = false;
343 Con_Print("play <demoname> : plays a demo\n");
347 // disconnect from server
349 Host_ShutdownServer ();
351 // update networking ports (this is mainly just needed at startup)
352 NetConn_UpdateSockets();
354 // open the demo file
355 strlcpy (name, Cmd_Argv(1), sizeof (name));
356 FS_DefaultExtension (name, ".dem", sizeof (name));
357 cls.protocol = PROTOCOL_QUAKE;
359 Con_Printf("Playing demo %s.\n", name);
360 cls.demofile = FS_Open (name, "rb", false, false);
363 Con_Print("ERROR: couldn't open.\n");
364 cls.demonum = -1; // stop demo loop
368 strlcpy(cls.demoname, name, sizeof(cls.demoname));
369 cls.demoplayback = true;
370 cls.state = ca_connected;
373 while ((c = FS_Getc (cls.demofile)) != '\n')
377 cls.forcetrack = cls.forcetrack * 10 + (c - '0');
380 cls.forcetrack = -cls.forcetrack;
389 void CL_FinishTimeDemo (void)
392 double time, totalfpsavg;
393 double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
395 cls.timedemo = false;
397 frames = cls.td_frames;
398 time = realtime - cls.td_starttime;
399 totalfpsavg = time > 0 ? frames / time : 0;
400 fpsmin = cls.td_onesecondminfps;
401 fpsavg = cls.td_onesecondavgcount ? cls.td_onesecondavgfps / cls.td_onesecondavgcount : 0;
402 fpsmax = cls.td_onesecondmaxfps;
403 // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
404 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);
405 Log_Printf("benchmark.log", "date %s | enginedate %s | demo %s | commandline %s | 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, frames, time, totalfpsavg, fpsmin, fpsavg, fpsmax, cls.td_onesecondavgcount);
406 if (COM_CheckParm("-benchmark"))
417 void CL_TimeDemo_f (void)
421 Con_Print("timedemo <demoname> : gets demo speeds\n");
425 srand(0); // predictable random sequence for benchmarking
429 // cls.td_starttime will be grabbed at the second frame of the demo, so
430 // all the loading time doesn't get counted
432 // instantly hide console and deactivate it
434 key_consoleactive = 0;
438 cls.td_frames = -2; // skip the first frame
439 cls.demonum = -1; // stop demo loop
440 cls.demonum = -1; // stop demo loop