moved all type-specific model fields to respective structures (alias, sprite, brush)
[divverent/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         // decide if it is time to grab the next message
136         // always grab until fully connected
137         if (cls.signon == SIGNONS)
138         {
139                 if (cls.timedemo)
140                 {
141                         if (host_framecount == cls.td_lastframe)
142                         {
143                                 // already read this frame's message
144                                 return;
145                         }
146                         if (cls.td_lastframe == -1)
147                         {
148                                 // we start counting on the second frame
149                                 // (after parsing connection stuff)
150                                 cls.td_startframe = host_framecount + 1;
151                         }
152                         cls.td_lastframe = host_framecount;
153                         // if this is the first official frame we can now grab the real
154                         // td_starttime so the bogus time on the first frame doesn't
155                         // count against the final report
156                         if (host_framecount == cls.td_startframe)
157                                 cls.td_starttime = realtime;
158                 }
159                 else if (cl.time <= cl.mtime[0])
160                 {
161                         // don't need another message yet
162                         return;
163                 }
164         }
165
166         // get the next message
167         FS_Read(cls.demofile, &net_message.cursize, 4);
168         net_message.cursize = LittleLong(net_message.cursize);
169         VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
170         for (i = 0;i < 3;i++)
171         {
172                 r = FS_Read(cls.demofile, &f, 4);
173                 cl.mviewangles[0][i] = LittleFloat(f);
174         }
175
176         if (net_message.cursize > NET_MAXMESSAGE)
177                 Host_Error("Demo message > NET_MAXMESSAGE");
178         if (FS_Read(cls.demofile, net_message.data, net_message.cursize) == (size_t)net_message.cursize)
179         {
180                 MSG_BeginReading();
181                 CL_ParseServerMessage();
182         }
183         else
184                 CL_Disconnect();
185 }
186
187
188 /*
189 ====================
190 CL_Stop_f
191
192 stop recording a demo
193 ====================
194 */
195 void CL_Stop_f (void)
196 {
197         if (cmd_source != src_command)
198                 return;
199
200         if (!cls.demorecording)
201         {
202                 Con_Printf ("Not recording a demo.\n");
203                 return;
204         }
205
206 // write a disconnect message to the demo file
207         SZ_Clear (&net_message);
208         MSG_WriteByte (&net_message, svc_disconnect);
209         CL_WriteDemoMessage ();
210
211 // finish up
212         FS_Close (cls.demofile);
213         cls.demofile = NULL;
214         cls.demorecording = false;
215         Con_Printf ("Completed demo\n");
216 }
217
218 /*
219 ====================
220 CL_Record_f
221
222 record <demoname> <map> [cd track]
223 ====================
224 */
225 void CL_Record_f (void)
226 {
227         int c, track;
228         char name[MAX_OSPATH];
229
230         if (cmd_source != src_command)
231                 return;
232
233         c = Cmd_Argc();
234         if (c != 2 && c != 3 && c != 4)
235         {
236                 Con_Printf ("record <demoname> [<map> [cd track]]\n");
237                 return;
238         }
239
240         if (strstr(Cmd_Argv(1), ".."))
241         {
242                 Con_Printf ("Relative pathnames are not allowed.\n");
243                 return;
244         }
245
246         if (c == 2 && cls.state == ca_connected)
247         {
248                 Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
249                 return;
250         }
251
252         // write the forced cd track number, or -1
253         if (c == 4)
254         {
255                 track = atoi(Cmd_Argv(3));
256                 Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
257         }
258         else
259                 track = -1;
260
261         // get the demo name
262         strncpy (name, Cmd_Argv(1), sizeof (name) - 1);
263         name[sizeof (name) - 1] = '\0';
264         FS_DefaultExtension (name, ".dem");
265
266         // start the map up
267         if (c > 2)
268                 Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
269
270         // open the demo file
271         Con_Printf ("recording to %s.\n", name);
272         cls.demofile = FS_Open (name, "wb", false);
273         if (!cls.demofile)
274         {
275                 Con_Printf ("ERROR: couldn't open.\n");
276                 return;
277         }
278
279         cls.forcetrack = track;
280         FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
281
282         cls.demorecording = true;
283 }
284
285
286 /*
287 ====================
288 CL_PlayDemo_f
289
290 play [demoname]
291 ====================
292 */
293 void CL_PlayDemo_f (void)
294 {
295         char    name[256];
296         int c;
297         qboolean neg = false;
298
299         if (cmd_source != src_command)
300                 return;
301
302         if (Cmd_Argc() != 2)
303         {
304                 Con_Printf ("play <demoname> : plays a demo\n");
305                 return;
306         }
307
308         // disconnect from server
309         CL_Disconnect ();
310
311         // open the demo file
312         strcpy (name, Cmd_Argv(1));
313         FS_DefaultExtension (name, ".dem");
314
315         Con_Printf ("Playing demo from %s.\n", name);
316         cls.demofile = FS_Open (name, "rb", false);
317         if (!cls.demofile)
318         {
319                 Con_Printf ("ERROR: couldn't open.\n");
320                 cls.demonum = -1;               // stop demo loop
321                 return;
322         }
323
324         SCR_BeginLoadingPlaque ();
325
326         cls.demoplayback = true;
327         cls.state = ca_connected;
328         cls.forcetrack = 0;
329
330         while ((c = FS_Getc (cls.demofile)) != '\n')
331                 if (c == '-')
332                         neg = true;
333                 else
334                         cls.forcetrack = cls.forcetrack * 10 + (c - '0');
335
336         if (neg)
337                 cls.forcetrack = -cls.forcetrack;
338 }
339
340 /*
341 ====================
342 CL_FinishTimeDemo
343
344 ====================
345 */
346 void CL_FinishTimeDemo (void)
347 {
348         int             frames;
349         double  time; // LordHavoc: changed timedemo accuracy to double
350
351         cls.timedemo = false;
352
353 // the first frame didn't count
354         frames = (host_framecount - cls.td_startframe) - 1;
355         time = realtime - cls.td_starttime;
356         if (!time)
357                 time = 1;
358         // LordHavoc: timedemo now prints out 7 digits of fraction
359         Con_Printf ("%i frames %5.7f seconds %5.7f fps\n", frames, time, frames/time);
360 }
361
362 /*
363 ====================
364 CL_TimeDemo_f
365
366 timedemo [demoname]
367 ====================
368 */
369 void CL_TimeDemo_f (void)
370 {
371         if (cmd_source != src_command)
372                 return;
373
374         if (Cmd_Argc() != 2)
375         {
376                 Con_Printf ("timedemo <demoname> : gets demo speeds\n");
377                 return;
378         }
379
380         CL_PlayDemo_f ();
381
382 // cls.td_starttime will be grabbed at the second frame of the demo, so
383 // all the loading time doesn't get counted
384
385         // instantly hide console and deactivate it
386         key_dest = key_game;
387         key_consoleactive = 0;
388         scr_conlines = 0;
389         scr_con_current = 0;
390
391         cls.timedemo = true;
392         // get first message this frame
393         cls.td_lastframe = -1;
394 }
395