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 // 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 cls.td_onesecondframes++;
161 // if this is the first official frame we can now grab the real
162 // td_starttime so the bogus time on the first frame doesn't
163 // count against the final report
164 if (host_framecount == cls.td_startframe)
166 cls.td_starttime = realtime;
167 cls.td_onesecondnexttime = realtime + 1;
168 cls.td_onesecondframes = 0;
169 cls.td_onesecondminframes = 0;
170 cls.td_onesecondmaxframes = 0;
171 cls.td_onesecondavgframes = 0;
172 cls.td_onesecondavgcount = 0;
174 if (realtime >= cls.td_onesecondnexttime)
176 if (cls.td_onesecondavgcount == 0)
178 cls.td_onesecondminframes = cls.td_onesecondframes;
179 cls.td_onesecondmaxframes = cls.td_onesecondframes;
181 cls.td_onesecondminframes = min(cls.td_onesecondminframes, cls.td_onesecondframes);
182 cls.td_onesecondmaxframes = max(cls.td_onesecondmaxframes, cls.td_onesecondframes);
183 cls.td_onesecondavgframes += cls.td_onesecondframes;
184 cls.td_onesecondavgcount++;
185 cls.td_onesecondframes = 0;
186 cls.td_onesecondnexttime++;
189 else if (cl.time <= cl.mtime[0])
191 // don't need another message yet
196 // get the next message
197 FS_Read(cls.demofile, &net_message.cursize, 4);
198 net_message.cursize = LittleLong(net_message.cursize);
199 if (net_message.cursize > net_message.maxsize)
200 Host_Error("Demo message (%i) > net_message.maxsize (%i)", net_message.cursize, net_message.maxsize);
201 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
202 for (i = 0;i < 3;i++)
204 r = (int)FS_Read(cls.demofile, &f, 4);
205 cl.mviewangles[0][i] = LittleFloat(f);
208 if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == net_message.cursize)
211 CL_ParseServerMessage();
213 // In case the demo contains a "svc_disconnect" message
214 if (!cls.demoplayback)
230 stop recording a demo
233 void CL_Stop_f (void)
236 unsigned char bufdata[64];
238 if (!cls.demorecording)
240 Con_Print("Not recording a demo.\n");
244 // write a disconnect message to the demo file
245 // LordHavoc: don't replace the net_message when doing this
247 buf.maxsize = sizeof(bufdata);
249 MSG_WriteByte(&buf, svc_disconnect);
250 CL_WriteDemoMessage(&buf);
253 FS_Close (cls.demofile);
255 cls.demorecording = false;
256 Con_Print("Completed demo\n");
263 record <demoname> <map> [cd track]
266 void CL_Record_f (void)
269 char name[MAX_OSPATH];
272 if (c != 2 && c != 3 && c != 4)
274 Con_Print("record <demoname> [<map> [cd track]]\n");
278 if (strstr(Cmd_Argv(1), ".."))
280 Con_Print("Relative pathnames are not allowed.\n");
284 if (c == 2 && cls.state == ca_connected)
286 Con_Print("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
290 if (cls.state == ca_connected)
293 // write the forced cd track number, or -1
296 track = atoi(Cmd_Argv(3));
297 Con_Printf("Forcing CD track to %i\n", cls.forcetrack);
303 strlcpy (name, Cmd_Argv(1), sizeof (name));
304 FS_DefaultExtension (name, ".dem", sizeof (name));
308 Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
310 // open the demo file
311 Con_Printf("recording to %s.\n", name);
312 cls.demofile = FS_Open (name, "wb", false, false);
315 Con_Print("ERROR: couldn't open.\n");
319 cls.forcetrack = track;
320 FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
322 cls.demorecording = true;
333 void CL_PlayDemo_f (void)
335 char name[MAX_QPATH];
337 qboolean neg = false;
341 Con_Print("play <demoname> : plays a demo\n");
345 // disconnect from server
347 Host_ShutdownServer ();
349 // update networking ports (this is mainly just needed at startup)
350 NetConn_UpdateSockets();
352 // open the demo file
353 strlcpy (name, Cmd_Argv(1), sizeof (name));
354 FS_DefaultExtension (name, ".dem", sizeof (name));
355 cls.protocol = PROTOCOL_QUAKE;
357 Con_Printf("Playing demo from %s.\n", name);
358 cls.demofile = FS_Open (name, "rb", false, false);
361 Con_Print("ERROR: couldn't open.\n");
362 cls.demonum = -1; // stop demo loop
366 strlcpy(cls.demoname, name, sizeof(cls.demoname));
367 cls.demoplayback = true;
368 cls.state = ca_connected;
371 while ((c = FS_Getc (cls.demofile)) != '\n')
375 cls.forcetrack = cls.forcetrack * 10 + (c - '0');
378 cls.forcetrack = -cls.forcetrack;
387 void CL_FinishTimeDemo (void)
390 double time, totalfpsavg;
391 double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
393 cls.timedemo = false;
395 // the first frame didn't count
396 frames = (host_framecount - cls.td_startframe) - 1;
397 time = realtime - cls.td_starttime;
398 totalfpsavg = time > 0 ? frames / time : 0;
399 fpsmin = cls.td_onesecondminframes;
400 fpsavg = cls.td_onesecondavgcount ? cls.td_onesecondavgframes / cls.td_onesecondavgcount : 0;
401 fpsmax = cls.td_onesecondmaxframes;
402 // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
403 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);
404 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);
405 if (COM_CheckParm("-benchmark"))
416 void CL_TimeDemo_f (void)
420 Con_Print("timedemo <demoname> : gets demo speeds\n");
424 srand(0); // predictable random sequence for benchmarking
428 // cls.td_starttime will be grabbed at the second frame of the demo, so
429 // all the loading time doesn't get counted
431 // instantly hide console and deactivate it
433 key_consoleactive = 0;
437 // get first message this frame
438 cls.td_lastframe = -1;