]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_demo.c
added r_showtris
[xonotic/darkplaces.git] / cl_demo.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "quakedef.h"
22
23 void CL_FinishTimeDemo (void);
24
25 /*
26 ==============================================================================
27
28 DEMO CODE
29
30 When a demo is playing back, all outgoing network messages are skipped, and
31 incoming messages are read from the demo file.
32
33 Whenever cl.time gets past the last received message, another message is
34 read from the demo file.
35 ==============================================================================
36 */
37
38 /*
39 =====================
40 CL_NextDemo
41
42 Called to play the next demo in the demo loop
43 =====================
44 */
45 void CL_NextDemo (void)
46 {
47         char    str[1024];
48
49         if (cls.demonum == -1)
50                 return;         // don't play demos
51
52         if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
53         {
54                 cls.demonum = 0;
55                 if (!cls.demos[cls.demonum][0])
56                 {
57                         Con_Printf ("No demos listed with startdemos\n");
58                         cls.demonum = -1;
59                         return;
60                 }
61         }
62
63         sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
64         Cbuf_InsertText (str);
65         cls.demonum++;
66 }
67
68 /*
69 ==============
70 CL_StopPlayback
71
72 Called when a demo file runs out, or the user starts a game
73 ==============
74 */
75 // LordHavoc: now called only by CL_Disconnect
76 void CL_StopPlayback (void)
77 {
78         if (!cls.demoplayback)
79                 return;
80
81         FS_Close (cls.demofile);
82         cls.demoplayback = false;
83         cls.demofile = NULL;
84
85         if (cls.timedemo)
86                 CL_FinishTimeDemo ();
87 }
88
89 /*
90 ====================
91 CL_WriteDemoMessage
92
93 Dumps the current net message, prefixed by the length and view angles
94 ====================
95 */
96 void CL_WriteDemoMessage (void)
97 {
98         int             len;
99         int             i;
100         float   f;
101
102         if (cls.demopaused) // LordHavoc: pausedemo
103                 return;
104
105         len = LittleLong (net_message.cursize);
106         FS_Write (cls.demofile, &len, 4);
107         for (i=0 ; i<3 ; i++)
108         {
109                 f = LittleFloat (cl.viewangles[i]);
110                 FS_Write (cls.demofile, &f, 4);
111         }
112         FS_Write (cls.demofile, net_message.data, net_message.cursize);
113         FS_Flush (cls.demofile);
114 }
115
116 /*
117 ====================
118 CL_ReadDemoMessage
119
120 Handles playback of demos
121 ====================
122 */
123 void CL_ReadDemoMessage(void)
124 {
125         int r, i;
126         float f;
127
128         if (!cls.demoplayback)
129                 return;
130
131         // LordHavoc: pausedemo
132         if (cls.demopaused)
133                 return;
134
135         while (1)
136         {
137                 // decide if it is time to grab the next message
138                 // always grab until fully connected
139                 if (cls.signon == SIGNONS)
140                 {
141                         if (cls.timedemo)
142                         {
143                                 if (host_framecount == cls.td_lastframe)
144                                 {
145                                         // already read this frame's message
146                                         return;
147                                 }
148                                 if (cls.td_lastframe == -1)
149                                 {
150                                         // we start counting on the second frame
151                                         // (after parsing connection stuff)
152                                         cls.td_startframe = host_framecount + 1;
153                                 }
154                                 cls.td_lastframe = host_framecount;
155                                 // if this is the first official frame we can now grab the real
156                                 // td_starttime so the bogus time on the first frame doesn't
157                                 // count against the final report
158                                 if (host_framecount == cls.td_startframe)
159                                         cls.td_starttime = realtime;
160                                 if (host_framecount > cls.td_startframe + 2)
161                                 {
162                                         cls.td_minframetime = min(cls.td_minframetime, host_realframetime);
163                                         cls.td_maxframetime = max(cls.td_maxframetime, host_realframetime);
164                                 }
165                                 else
166                                         cls.td_minframetime = cls.td_maxframetime = host_realframetime;
167                         }
168                         else if (cl.time <= cl.mtime[0])
169                         {
170                                 // don't need another message yet
171                                 return;
172                         }
173                 }
174
175                 // get the next message
176                 FS_Read(cls.demofile, &net_message.cursize, 4);
177                 net_message.cursize = LittleLong(net_message.cursize);
178                 if (net_message.cursize > net_message.maxsize)
179                         Host_Error("Demo message (%i) > net_message.maxsize (%i)", net_message.cursize, net_message.maxsize);
180                 VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
181                 for (i = 0;i < 3;i++)
182                 {
183                         r = FS_Read(cls.demofile, &f, 4);
184                         cl.mviewangles[0][i] = LittleFloat(f);
185                 }
186
187                 if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == (size_t)net_message.cursize)
188                 {
189                         MSG_BeginReading();
190                         CL_ParseServerMessage();
191                 }
192                 else
193                 {
194                         CL_Disconnect();
195                         return;
196                 }
197         }
198 }
199
200
201 /*
202 ====================
203 CL_Stop_f
204
205 stop recording a demo
206 ====================
207 */
208 void CL_Stop_f (void)
209 {
210         if (cmd_source != src_command)
211                 return;
212
213         if (!cls.demorecording)
214         {
215                 Con_Printf ("Not recording a demo.\n");
216                 return;
217         }
218
219 // write a disconnect message to the demo file
220         SZ_Clear (&net_message);
221         MSG_WriteByte (&net_message, svc_disconnect);
222         CL_WriteDemoMessage ();
223
224 // finish up
225         FS_Close (cls.demofile);
226         cls.demofile = NULL;
227         cls.demorecording = false;
228         Con_Printf ("Completed demo\n");
229 }
230
231 /*
232 ====================
233 CL_Record_f
234
235 record <demoname> <map> [cd track]
236 ====================
237 */
238 void CL_Record_f (void)
239 {
240         int c, track;
241         char name[MAX_OSPATH];
242
243         if (cmd_source != src_command)
244                 return;
245
246         c = Cmd_Argc();
247         if (c != 2 && c != 3 && c != 4)
248         {
249                 Con_Printf ("record <demoname> [<map> [cd track]]\n");
250                 return;
251         }
252
253         if (strstr(Cmd_Argv(1), ".."))
254         {
255                 Con_Printf ("Relative pathnames are not allowed.\n");
256                 return;
257         }
258
259         if (c == 2 && cls.state == ca_connected)
260         {
261                 Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
262                 return;
263         }
264
265         // write the forced cd track number, or -1
266         if (c == 4)
267         {
268                 track = atoi(Cmd_Argv(3));
269                 Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
270         }
271         else
272                 track = -1;
273
274         // get the demo name
275         strlcpy (name, Cmd_Argv(1), sizeof (name));
276         FS_DefaultExtension (name, ".dem", sizeof (name));
277
278         // start the map up
279         if (c > 2)
280                 Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
281
282         // open the demo file
283         Con_Printf ("recording to %s.\n", name);
284         cls.demofile = FS_Open (name, "wb", false);
285         if (!cls.demofile)
286         {
287                 Con_Printf ("ERROR: couldn't open.\n");
288                 return;
289         }
290
291         cls.forcetrack = track;
292         FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
293
294         cls.demorecording = true;
295 }
296
297
298 /*
299 ====================
300 CL_PlayDemo_f
301
302 play [demoname]
303 ====================
304 */
305 void CL_PlayDemo_f (void)
306 {
307         char    name[256];
308         int c;
309         qboolean neg = false;
310
311         if (cmd_source != src_command)
312                 return;
313
314         if (Cmd_Argc() != 2)
315         {
316                 Con_Printf ("play <demoname> : plays a demo\n");
317                 return;
318         }
319
320         // disconnect from server
321         CL_Disconnect ();
322
323         // update networking ports (this is mainly just needed at startup)
324         NetConn_ClientFrame();
325
326         // open the demo file
327         strlcpy (name, Cmd_Argv(1), sizeof (name));
328         FS_DefaultExtension (name, ".dem", sizeof (name));
329
330         Con_Printf ("Playing demo from %s.\n", name);
331         cls.demofile = FS_Open (name, "rb", false);
332         if (!cls.demofile)
333         {
334                 Con_Printf ("ERROR: couldn't open.\n");
335                 cls.demonum = -1;               // stop demo loop
336                 return;
337         }
338
339         SCR_BeginLoadingPlaque ();
340
341         cls.demoplayback = true;
342         cls.state = ca_connected;
343         cls.forcetrack = 0;
344
345         while ((c = FS_Getc (cls.demofile)) != '\n')
346                 if (c == '-')
347                         neg = true;
348                 else
349                         cls.forcetrack = cls.forcetrack * 10 + (c - '0');
350
351         if (neg)
352                 cls.forcetrack = -cls.forcetrack;
353 }
354
355 /*
356 ====================
357 CL_FinishTimeDemo
358
359 ====================
360 */
361 void CL_FinishTimeDemo (void)
362 {
363         int frames;
364         double time; // LordHavoc: changed timedemo accuracy to double
365         double fpsmin, fpsavg, fpsmax; // report min/avg/max fps
366
367         cls.timedemo = false;
368
369 // the first frame didn't count
370         frames = (host_framecount - cls.td_startframe) - 1;
371         time = realtime - cls.td_starttime;
372         fpsmin = cls.td_maxframetime > 0 ? 1.0 / cls.td_maxframetime : 0;
373         fpsavg = time > 0 ? frames / time : 0;
374         fpsmax = cls.td_minframetime > 0 ? 1.0 / cls.td_minframetime : 0;
375         // LordHavoc: timedemo now prints out 7 digits of fraction, and min/avg/max
376         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);
377 }
378
379 /*
380 ====================
381 CL_TimeDemo_f
382
383 timedemo [demoname]
384 ====================
385 */
386 void CL_TimeDemo_f (void)
387 {
388         if (cmd_source != src_command)
389                 return;
390
391         if (Cmd_Argc() != 2)
392         {
393                 Con_Printf ("timedemo <demoname> : gets demo speeds\n");
394                 return;
395         }
396
397         CL_PlayDemo_f ();
398
399 // cls.td_starttime will be grabbed at the second frame of the demo, so
400 // all the loading time doesn't get counted
401
402         // instantly hide console and deactivate it
403         key_dest = key_game;
404         key_consoleactive = 0;
405         scr_conlines = 0;
406         scr_con_current = 0;
407
408         cls.timedemo = true;
409         // get first message this frame
410         cls.td_lastframe = -1;
411 }
412