• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 NET_SendMessages are skipped, and
31 NET_GetMessages 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_StopPlayback
41 
42 Called when a demo file runs out, or the user starts a game
43 ==============
44 */
CL_StopPlayback(void)45 void CL_StopPlayback (void)
46 {
47     if (!cls.demoplayback)
48         return;
49 
50     fclose (cls.demofile);
51     cls.demoplayback = false;
52     cls.demofile = NULL;
53     cls.state = ca_disconnected;
54 
55     if (cls.timedemo)
56         CL_FinishTimeDemo ();
57 }
58 
59 /*
60 ====================
61 CL_WriteDemoMessage
62 
63 Dumps the current net message, prefixed by the length and view angles
64 ====================
65 */
CL_WriteDemoMessage(void)66 void CL_WriteDemoMessage (void)
67 {
68     int        len;
69     int        i;
70     float    f;
71 
72     len = LittleLong (net_message.cursize);
73     fwrite (&len, 4, 1, cls.demofile);
74     for (i=0 ; i<3 ; i++)
75     {
76         f = LittleFloat (cl.viewangles[i]);
77         fwrite (&f, 4, 1, cls.demofile);
78     }
79     fwrite (net_message.data, net_message.cursize, 1, cls.demofile);
80     fflush (cls.demofile);
81 }
82 
83 /*
84 ====================
85 CL_GetMessage
86 
87 Handles recording and playback of demos, on top of NET_ code
88 ====================
89 */
CL_GetMessage(void)90 int CL_GetMessage (void)
91 {
92     int        r, i;
93     float    f;
94 
95     if    (cls.demoplayback)
96     {
97     // decide if it is time to grab the next message
98         if (cls.signon == SIGNONS)    // allways grab until fully connected
99         {
100             if (cls.timedemo)
101             {
102                 if (host_framecount == cls.td_lastframe)
103                     return 0;        // allready read this frame's message
104                 cls.td_lastframe = host_framecount;
105             // if this is the second frame, grab the real td_starttime
106             // so the bogus time on the first frame doesn't count
107                 if (host_framecount == cls.td_startframe + 1)
108                     cls.td_starttime = realtime;
109             }
110             else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0])
111             {
112                     return 0;        // don't need another message yet
113             }
114         }
115 
116     // get the next message
117         fread (&net_message.cursize, 4, 1, cls.demofile);
118         VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
119         for (i=0 ; i<3 ; i++)
120         {
121             r = fread (&f, 4, 1, cls.demofile);
122             cl.mviewangles[0][i] = LittleFloat (f);
123         }
124 
125         net_message.cursize = LittleLong (net_message.cursize);
126         if (net_message.cursize > MAX_MSGLEN)
127             Sys_Error ("Demo message > MAX_MSGLEN");
128         r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
129         if (r != 1)
130         {
131             CL_StopPlayback ();
132             return 0;
133         }
134 
135         return 1;
136     }
137 
138     while (1)
139     {
140         r = NET_GetMessage (cls.netcon);
141 
142         if (r != 1 && r != 2)
143             return r;
144 
145     // discard nop keepalive message
146         if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
147             Con_Printf ("<-- server to client keepalive\n");
148         else
149             break;
150     }
151 
152     if (cls.demorecording)
153         CL_WriteDemoMessage ();
154 
155     return r;
156 }
157 
158 
159 /*
160 ====================
161 CL_Stop_f
162 
163 stop recording a demo
164 ====================
165 */
CL_Stop_f(void)166 void CL_Stop_f (void)
167 {
168     if (cmd_source != src_command)
169         return;
170 
171     if (!cls.demorecording)
172     {
173         Con_Printf ("Not recording a demo.\n");
174         return;
175     }
176 
177 // write a disconnect message to the demo file
178     SZ_Clear (&net_message);
179     MSG_WriteByte (&net_message, svc_disconnect);
180     CL_WriteDemoMessage ();
181 
182 // finish up
183     fclose (cls.demofile);
184     cls.demofile = NULL;
185     cls.demorecording = false;
186     Con_Printf ("Completed demo\n");
187 }
188 
189 /*
190 ====================
191 CL_Record_f
192 
193 record <demoname> <map> [cd track]
194 ====================
195 */
CL_Record_f(void)196 void CL_Record_f (void)
197 {
198     int        c;
199     char    name[MAX_OSPATH];
200     int        track;
201 
202     if (cmd_source != src_command)
203         return;
204 
205     c = Cmd_Argc();
206     if (c != 2 && c != 3 && c != 4)
207     {
208         Con_Printf ("record <demoname> [<map> [cd track]]\n");
209         return;
210     }
211 
212     if (strstr(Cmd_Argv(1), ".."))
213     {
214         Con_Printf ("Relative pathnames are not allowed.\n");
215         return;
216     }
217 
218     if (c == 2 && cls.state == ca_connected)
219     {
220         Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
221         return;
222     }
223 
224 // write the forced cd track number, or -1
225     if (c == 4)
226     {
227         track = atoi(Cmd_Argv(3));
228         Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
229     }
230     else
231         track = -1;
232 
233     sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
234 
235 //
236 // start the map up
237 //
238     if (c > 2)
239         Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
240 
241 //
242 // open the demo file
243 //
244     COM_DefaultExtension (name, ".dem");
245 
246     Con_Printf ("recording to %s.\n", name);
247     cls.demofile = fopen (name, "wb");
248     if (!cls.demofile)
249     {
250         Con_Printf ("ERROR: couldn't open.\n");
251         return;
252     }
253 
254     cls.forcetrack = track;
255     fprintf (cls.demofile, "%i\n", cls.forcetrack);
256 
257     cls.demorecording = true;
258 }
259 
260 
261 /*
262 ====================
263 CL_PlayDemo_f
264 
265 play [demoname]
266 ====================
267 */
CL_PlayDemo_f(void)268 void CL_PlayDemo_f (void)
269 {
270     char    name[256];
271     int c;
272     qboolean neg = false;
273 
274     if (cmd_source != src_command)
275         return;
276 
277     if (Cmd_Argc() > 2)
278     {
279         Con_Printf ("play <demoname> : plays a demo\n");
280         return;
281     }
282 
283 //
284 // disconnect from server
285 //
286     CL_Disconnect ();
287 
288 //
289 // open the demo file
290 //
291     const char* cmdName = "demo1";
292     if (Cmd_Argc() == 2) {
293         cmdName = Cmd_Argv(1);
294     }
295     strcpy (name, cmdName);
296     COM_DefaultExtension (name, ".dem");
297 
298     Con_Printf ("Playing demo from %s.\n", name);
299     COM_FOpenFile (name, &cls.demofile);
300     if (!cls.demofile)
301     {
302         Con_Printf ("ERROR: couldn't open.\n");
303         cls.demonum = -1;        // stop demo loop
304         return;
305     }
306 
307     cls.demoplayback = true;
308     cls.state = ca_connected;
309     cls.forcetrack = 0;
310 
311     while ((c = getc(cls.demofile)) != '\n')
312         if (c == '-')
313             neg = true;
314         else
315             cls.forcetrack = cls.forcetrack * 10 + (c - '0');
316 
317     if (neg)
318         cls.forcetrack = -cls.forcetrack;
319 // ZOID, fscanf is evil
320 //    fscanf (cls.demofile, "%i\n", &cls.forcetrack);
321 }
322 
323 // The timedemo numbers are very important to testing, so log them even if normal console printing is disabled.
324 
325 #define LOGANDPRINT(ARGS) Con_Printf ARGS ; PMPLOG(ARGS)
326 
327 /*
328 ====================
329 CL_FinishTimeDemo
330 
331 ====================
332 */
CL_FinishTimeDemo(void)333 void CL_FinishTimeDemo (void)
334 {
335     int        frames;
336     float    time;
337 
338     cls.timedemo = false;
339 
340 // the first frame didn't count
341     frames = (host_framecount - cls.td_startframe) - 1;
342     time = realtime - cls.td_starttime;
343     if (!time)
344         time = 1;
345     LOGANDPRINT(("%i frames %5.3f seconds %5.3f fps\n", frames, time, frames/time));
346     if (frames > 0)
347     {
348         LOGANDPRINT(("Fastest: %5.1f ms on frame %d\n", fastestFrame.time * 1000.0, fastestFrame.frame));
349         LOGANDPRINT(("Average: %5.1f ms\n", (time / frames) * 1000.0));
350         LOGANDPRINT(("Slowest: %5.1f ms on frame %d\n", slowestFrame.time * 1000.0, slowestFrame.frame));
351     }
352 }
353 
354 /*
355 ====================
356 CL_TimeDemo_f
357 
358 timedemo [demoname]
359 ====================
360 */
CL_TimeDemo_f(void)361 void CL_TimeDemo_f (void)
362 {
363     if (cmd_source != src_command)
364         return;
365 
366     if (Cmd_Argc() > 2)
367     {
368         Con_Printf ("timedemo <demoname> : gets demo speeds\n");
369         return;
370     }
371 
372     CL_PlayDemo_f ();
373 
374 // cls.td_starttime will be grabbed at the second frame of the demo, so
375 // all the loading time doesn't get counted
376 
377     cls.timedemo = true;
378     cls.td_startframe = host_framecount;
379     cls.td_lastframe = -1;        // get a new message this frame
380 
381     InitFrameTimes();
382 }
383 
384