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") || COM_CheckParm("-demolooponly"))
99 Dumps the current net message, prefixed by the length and view angles
102 void CL_WriteDemoMessage (void)
108 if (cls.demopaused) // LordHavoc: pausedemo
111 len = LittleLong (net_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, net_message.data, net_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 // we start counting on the second frame
156 // (after parsing connection stuff)
157 cls.td_startframe = host_framecount + 1;
159 cls.td_lastframe = host_framecount;
160 // if this is the first official frame we can now grab the real
161 // td_starttime so the bogus time on the first frame doesn't
162 // count against the final report
163 if (host_framecount == cls.td_startframe)
164 cls.td_starttime = realtime;
165 if (host_framecount > cls.td_startframe + 2)
167 cls.td_minframetime = min(cls.td_minframetime, cl.realframetime);
168 cls.td_maxframetime = max(cls.td_maxframetime, cl.realframetime);
171 cls.td_minframetime = cls.td_maxframetime = cl.realframetime;
173 else if (cl.time <= cl.mtime[0])
175 // don't need another message yet
180 // get the next message
181 FS_Read(cls.demofile, &net_message.cursize, 4);
182 net_message.cursize = LittleLong(net_message.cursize);
183 if (net_message.cursize > net_message.maxsize)
184 Host_Error("Demo message (%i) > net_message.maxsize (%i)", net_message.cursize, net_message.maxsize);
185 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
186 for (i = 0;i < 3;i++)
188 r = (int)FS_Read(cls.demofile, &f, 4);
189 cl.mviewangles[0][i] = LittleFloat(f);
192 if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == net_message.cursize)
195 CL_ParseServerMessage();
197 // In case the demo contains a "svc_disconnect" message
198 if (!cls.demoplayback)
214 stop recording a demo
217 void CL_Stop_f (void)
219 if (!cls.demorecording)
221 Con_Print("Not recording a demo.\n");
225 // write a disconnect message to the demo file
226 SZ_Clear (&net_message);
227 MSG_WriteByte (&net_message, svc_disconnect);
228 CL_WriteDemoMessage ();
231 FS_Close (cls.demofile);
233 cls.demorecording = false;
234 Con_Print("Completed demo\n");
241 record <demoname> <map> [cd track]
244 void CL_Record_f (void)
247 char name[MAX_OSPATH];
250 if (c != 2 && c != 3 && c != 4)
252 Con_Print("record <demoname> [<map> [cd track]]\n");
256 if (strstr(Cmd_Argv(1), ".."))
258 Con_Print("Relative pathnames are not allowed.\n");
262 if (c == 2 && cls.state == ca_connected)
264 Con_Print("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
268 // write the forced cd track number, or -1
271 track = atoi(Cmd_Argv(3));
272 Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
278 strlcpy (name, Cmd_Argv(1), sizeof (name));
279 FS_DefaultExtension (name, ".dem", sizeof (name));
283 Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
285 // open the demo file
286 Con_Printf("recording to %s.\n", name);
287 cls.demofile = FS_Open (name, "wb", false, false);
290 Con_Print("ERROR: couldn't open.\n");
294 cls.forcetrack = track;
295 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
297 cls.demorecording = true;
308 void CL_PlayDemo_f (void)
310 char name[MAX_QPATH];
312 qboolean neg = false;
316 Con_Print("play <demoname> : plays a demo\n");
320 // disconnect from server
322 Host_ShutdownServer ();
324 // update networking ports (this is mainly just needed at startup)
325 NetConn_UpdateSockets();
327 // open the demo file
328 strlcpy (name, Cmd_Argv(1), sizeof (name));
329 FS_DefaultExtension (name, ".dem", sizeof (name));
330 cls.protocol = PROTOCOL_QUAKE;
332 Con_Printf("Playing demo from %s.\n", name);
333 cls.demofile = FS_Open (name, "rb", false, false);
336 Con_Print("ERROR: couldn't open.\n");
337 cls.demonum = -1; // stop demo loop
341 strlcpy(cls.demoname, name, sizeof(cls.demoname));
342 cls.demoplayback = true;
343 cls.state = ca_connected;
346 while ((c = FS_Getc (cls.demofile)) != '\n')
350 cls.forcetrack = cls.forcetrack * 10 + (c - '0');
353 cls.forcetrack = -cls.forcetrack;
362 void CL_FinishTimeDemo (void)
365 double time; // LordHavoc: changed timedemo accuracy to double
366 double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
368 cls.timedemo = false;
370 // the first frame didn't count
371 frames = (host_framecount - cls.td_startframe) - 1;
372 time = realtime - cls.td_starttime;
373 fpsmin = cls.td_maxframetime > 0 ? 1.0 / cls.td_maxframetime : 0;
374 fpsavg = time > 0 ? frames / time : 0;
375 fpsmax = cls.td_minframetime > 0 ? 1.0 / cls.td_minframetime : 0;
376 // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
377 Con_Printf("%i frames %5.7f seconds %5.7f fps\nmin/avg/max: %5.7f/%5.7f/%5.7f\n", frames, time, fpsavg, fpsmin, fpsavg, fpsmax);
378 Log_Printf("benchmark.log", "date %s | enginedate %s | demo %s | commandline %s | result %i frames %5.7f seconds %5.7f fps min/avg/max: %5.7f/%5.7f/%5.7f\n", Sys_TimeString("%Y-%m-%d %H:%M:%S"), buildstring, cls.demoname, cmdline.string, frames, time, fpsavg, fpsmin, fpsavg, fpsmax);
379 if (COM_CheckParm("-benchmark"))
390 void CL_TimeDemo_f (void)
394 Con_Print("timedemo <demoname> : gets demo speeds\n");
400 // cls.td_starttime will be grabbed at the second frame of the demo, so
401 // all the loading time doesn't get counted
403 // instantly hide console and deactivate it
405 key_consoleactive = 0;
409 // get first message this frame
410 cls.td_lastframe = -1;