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.
25 void CL_FinishTimeDemo (void);
28 ==============================================================================
32 When a demo is playing back, all outgoing network messages are skipped, and
33 incoming messages are read from the demo file.
35 Whenever cl.time gets past the last received message, another message is
36 read from the demo file.
37 ==============================================================================
44 Called to play the next demo in the demo loop
47 void CL_NextDemo (void)
49 char str[MAX_INPUTLINE];
51 if (cls.demonum == -1)
52 return; // don't play demos
54 if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
57 if (!cls.demos[cls.demonum][0])
59 Con_Print("No demos listed with startdemos\n");
65 sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
66 Cbuf_InsertText (str);
74 Called when a demo file runs out, or the user starts a game
77 // LordHavoc: now called only by CL_Disconnect
78 void CL_StopPlayback (void)
80 if (!cls.demoplayback)
83 FS_Close (cls.demofile);
84 cls.demoplayback = false;
90 if (COM_CheckParm("-demo"))
99 Dumps the current net message, prefixed by the length and view angles
102 void CL_WriteDemoMessage (sizebuf_t *message)
108 if (cls.demopaused) // LordHavoc: pausedemo
111 len = LittleLong (message->cursize);
112 FS_Write (cls.demofile, &len, 4);
113 for (i=0 ; i<3 ; i++)
115 f = LittleFloat (cl.viewangles[i]);
116 FS_Write (cls.demofile, &f, 4);
118 FS_Write (cls.demofile, message->data, message->cursize);
125 Handles playback of demos
128 void CL_ReadDemoMessage(void)
133 if (!cls.demoplayback)
136 // LordHavoc: pausedemo
142 // decide if it is time to grab the next message
143 // always grab until fully connected
144 if (cls.signon == SIGNONS)
148 if (host_framecount == cls.td_lastframe)
150 // already read this frame's message
153 if (cls.td_lastframe == -1)
155 // render a couple frames before we start counting
156 cls.td_startframe = host_framecount + 3;
158 cls.td_lastframe = host_framecount;
159 cls.td_onesecondframes++;
160 // don't read any new messages during the warm-up period
161 if (host_framecount < cls.td_startframe)
163 // if this is the first official frame we can now grab the real
164 // td_starttime so the bogus time on the first frame doesn't
165 // count against the final report
166 if (host_framecount == cls.td_startframe)
168 cls.td_starttime = realtime;
169 cls.td_onesecondnexttime = realtime + 1;
170 cls.td_onesecondframes = 0;
171 cls.td_onesecondminframes = 0;
172 cls.td_onesecondmaxframes = 0;
173 cls.td_onesecondavgframes = 0;
174 cls.td_onesecondavgcount = 0;
176 if (realtime >= cls.td_onesecondnexttime)
178 if (cls.td_onesecondavgcount == 0)
180 cls.td_onesecondminframes = cls.td_onesecondframes;
181 cls.td_onesecondmaxframes = cls.td_onesecondframes;
183 cls.td_onesecondminframes = min(cls.td_onesecondminframes, cls.td_onesecondframes);
184 cls.td_onesecondmaxframes = max(cls.td_onesecondmaxframes, cls.td_onesecondframes);
185 cls.td_onesecondavgframes += cls.td_onesecondframes;
186 cls.td_onesecondavgcount++;
187 cls.td_onesecondframes = 0;
188 cls.td_onesecondnexttime++;
191 else if (cl.time <= cl.mtime[0])
193 // don't need another message yet
198 // get the next message
199 FS_Read(cls.demofile, &net_message.cursize, 4);
200 net_message.cursize = LittleLong(net_message.cursize);
201 if (net_message.cursize > net_message.maxsize)
202 Host_Error("Demo message (%i) > net_message.maxsize (%i)", net_message.cursize, net_message.maxsize);
203 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
204 for (i = 0;i < 3;i++)
206 r = (int)FS_Read(cls.demofile, &f, 4);
207 cl.mviewangles[0][i] = LittleFloat(f);
210 if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == net_message.cursize)
213 CL_ParseServerMessage();
215 // In case the demo contains a "svc_disconnect" message
216 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 from %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 // the first frame didn't count
398 frames = (host_framecount - cls.td_startframe) - 1;
399 time = realtime - cls.td_starttime;
400 totalfpsavg = time > 0 ? frames / time : 0;
401 fpsmin = cls.td_onesecondminframes;
402 fpsavg = cls.td_onesecondavgcount ? cls.td_onesecondavgframes / cls.td_onesecondavgcount : 0;
403 fpsmax = cls.td_onesecondmaxframes;
404 // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
405 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);
406 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);
407 if (COM_CheckParm("-benchmark"))
418 void CL_TimeDemo_f (void)
422 Con_Print("timedemo <demoname> : gets demo speeds\n");
426 srand(0); // predictable random sequence for benchmarking
430 // cls.td_starttime will be grabbed at the second frame of the demo, so
431 // all the loading time doesn't get counted
433 // instantly hide console and deactivate it
435 key_consoleactive = 0;
439 // get first message this frame
440 cls.td_lastframe = -1;