1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the included (GNU.txt) GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20
21#include "quakedef.h"
22
23void CL_FinishTimeDemo (void);
24
25/*
26==============================================================================
27
28DEMO CODE
29
30When a demo is playing back, all NET_SendMessages are skipped, and
31NET_GetMessages are read from the demo file.
32
33Whenever cl.time gets past the last received message, another message is
34read from the demo file.
35==============================================================================
36*/
37
38/*
39==============
40CL_StopPlayback
41
42Called when a demo file runs out, or the user starts a game
43==============
44*/
45void CL_StopPlayback (void)
46{
47	if (!cls.demoplayback)
48		return;
49
50	fclose (cls.demofile);
51	cls.demofile = NULL;
52	cls.state = ca_disconnected;
53	cls.demoplayback = 0;
54
55	if (cls.timedemo)
56		CL_FinishTimeDemo ();
57}
58
59#define dem_cmd		0
60#define dem_read	1
61#define dem_set		2
62
63/*
64====================
65CL_WriteDemoCmd
66
67Writes the current user cmd
68====================
69*/
70void CL_WriteDemoCmd (usercmd_t *pcmd)
71{
72	int		i;
73	float	fl;
74	byte	c;
75	usercmd_t cmd;
76
77//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
78
79	fl = LittleFloat((float)realtime);
80	fwrite (&fl, sizeof(fl), 1, cls.demofile);
81
82	c = dem_cmd;
83	fwrite (&c, sizeof(c), 1, cls.demofile);
84
85	// correct for byte order, bytes don't matter
86	cmd = *pcmd;
87
88	for (i = 0; i < 3; i++)
89		cmd.angles[i] = LittleFloat(cmd.angles[i]);
90	cmd.forwardmove = LittleShort(cmd.forwardmove);
91	cmd.sidemove    = LittleShort(cmd.sidemove);
92	cmd.upmove      = LittleShort(cmd.upmove);
93
94	fwrite(&cmd, sizeof(cmd), 1, cls.demofile);
95
96	for (i=0 ; i<3 ; i++)
97	{
98		fl = LittleFloat (cl.viewangles[i]);
99		fwrite (&fl, 4, 1, cls.demofile);
100	}
101
102	fflush (cls.demofile);
103}
104
105/*
106====================
107CL_WriteDemoMessage
108
109Dumps the current net message, prefixed by the length and view angles
110====================
111*/
112void CL_WriteDemoMessage (sizebuf_t *msg)
113{
114	int		len;
115	float	fl;
116	byte	c;
117
118//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
119
120	if (!cls.demorecording)
121		return;
122
123	fl = LittleFloat((float)realtime);
124	fwrite (&fl, sizeof(fl), 1, cls.demofile);
125
126	c = dem_read;
127	fwrite (&c, sizeof(c), 1, cls.demofile);
128
129	len = LittleLong (msg->cursize);
130	fwrite (&len, 4, 1, cls.demofile);
131	fwrite (msg->data, msg->cursize, 1, cls.demofile);
132
133	fflush (cls.demofile);
134}
135
136/*
137====================
138CL_GetDemoMessage
139
140  FIXME...
141====================
142*/
143qboolean CL_GetDemoMessage (void)
144{
145	int		r, i, j;
146	float	f;
147	float	demotime;
148	byte	c;
149	usercmd_t *pcmd;
150
151	// read the time from the packet
152	fread(&demotime, sizeof(demotime), 1, cls.demofile);
153	demotime = LittleFloat(demotime);
154
155// decide if it is time to grab the next message
156	if (cls.timedemo) {
157		if (cls.td_lastframe < 0)
158			cls.td_lastframe = demotime;
159		else if (demotime > cls.td_lastframe) {
160			cls.td_lastframe = demotime;
161			// rewind back to time
162			fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
163					SEEK_SET);
164			return 0;		// allready read this frame's message
165		}
166		if (!cls.td_starttime && cls.state == ca_active) {
167			cls.td_starttime = Sys_DoubleTime();
168			cls.td_startframe = host_framecount;
169		}
170		realtime = demotime; // warp
171	} else if (!cl.paused && cls.state >= ca_onserver) {	// allways grab until fully connected
172		if (realtime + 1.0 < demotime) {
173			// too far back
174			realtime = demotime - 1.0;
175			// rewind back to time
176			fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
177					SEEK_SET);
178			return 0;
179		} else if (realtime < demotime) {
180			// rewind back to time
181			fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
182					SEEK_SET);
183			return 0;		// don't need another message yet
184		}
185	} else
186		realtime = demotime; // we're warping
187
188	if (cls.state < ca_demostart)
189		Host_Error ("CL_GetDemoMessage: cls.state != ca_active");
190
191	// get the msg type
192	fread (&c, sizeof(c), 1, cls.demofile);
193
194	switch (c) {
195	case dem_cmd :
196		// user sent input
197		i = cls.netchan.outgoing_sequence & UPDATE_MASK;
198		pcmd = &cl.frames[i].cmd;
199		r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile);
200		if (r != 1)
201		{
202			CL_StopPlayback ();
203			return 0;
204		}
205		// byte order stuff
206		for (j = 0; j < 3; j++)
207			pcmd->angles[j] = LittleFloat(pcmd->angles[j]);
208		pcmd->forwardmove = LittleShort(pcmd->forwardmove);
209		pcmd->sidemove    = LittleShort(pcmd->sidemove);
210		pcmd->upmove      = LittleShort(pcmd->upmove);
211		cl.frames[i].senttime = demotime;
212		cl.frames[i].receivedtime = -1;		// we haven't gotten a reply yet
213		cls.netchan.outgoing_sequence++;
214		for (i=0 ; i<3 ; i++)
215		{
216			r = fread (&f, 4, 1, cls.demofile);
217			cl.viewangles[i] = LittleFloat (f);
218		}
219		break;
220
221	case dem_read:
222		// get the next message
223		fread (&net_message.cursize, 4, 1, cls.demofile);
224		net_message.cursize = LittleLong (net_message.cursize);
225	//Con_Printf("read: %ld bytes\n", net_message.cursize);
226		if (net_message.cursize > MAX_MSGLEN)
227			Sys_Error ("Demo message > MAX_MSGLEN");
228		r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
229		if (r != 1)
230		{
231			CL_StopPlayback ();
232			return 0;
233		}
234		break;
235
236	case dem_set :
237		fread (&i, 4, 1, cls.demofile);
238		cls.netchan.outgoing_sequence = LittleLong(i);
239		fread (&i, 4, 1, cls.demofile);
240		cls.netchan.incoming_sequence = LittleLong(i);
241		break;
242
243	default :
244		Con_Printf("Corrupted demo.\n");
245		CL_StopPlayback ();
246		return 0;
247	}
248
249	return 1;
250}
251
252/*
253====================
254CL_GetMessage
255
256Handles recording and playback of demos, on top of NET_ code
257====================
258*/
259qboolean CL_GetMessage (void)
260{
261	if	(cls.demoplayback)
262		return CL_GetDemoMessage ();
263
264	if (!NET_GetPacket ())
265		return false;
266
267	CL_WriteDemoMessage (&net_message);
268
269	return true;
270}
271
272
273/*
274====================
275CL_Stop_f
276
277stop recording a demo
278====================
279*/
280void CL_Stop_f (void)
281{
282	if (!cls.demorecording)
283	{
284		Con_Printf ("Not recording a demo.\n");
285		return;
286	}
287
288// write a disconnect message to the demo file
289	SZ_Clear (&net_message);
290	MSG_WriteLong (&net_message, -1);	// -1 sequence means out of band
291	MSG_WriteByte (&net_message, svc_disconnect);
292	MSG_WriteString (&net_message, "EndOfDemo");
293	CL_WriteDemoMessage (&net_message);
294
295// finish up
296	fclose (cls.demofile);
297	cls.demofile = NULL;
298	cls.demorecording = false;
299	Con_Printf ("Completed demo\n");
300}
301
302
303/*
304====================
305CL_WriteDemoMessage
306
307Dumps the current net message, prefixed by the length and view angles
308====================
309*/
310void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq)
311{
312	int		len;
313	int		i;
314	float	fl;
315	byte	c;
316
317//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
318
319	if (!cls.demorecording)
320		return;
321
322	fl = LittleFloat((float)realtime);
323	fwrite (&fl, sizeof(fl), 1, cls.demofile);
324
325	c = dem_read;
326	fwrite (&c, sizeof(c), 1, cls.demofile);
327
328	len = LittleLong (msg->cursize + 8);
329	fwrite (&len, 4, 1, cls.demofile);
330
331	i = LittleLong(seq);
332	fwrite (&i, 4, 1, cls.demofile);
333	fwrite (&i, 4, 1, cls.demofile);
334
335	fwrite (msg->data, msg->cursize, 1, cls.demofile);
336
337	fflush (cls.demofile);
338}
339
340
341void CL_WriteSetDemoMessage (void)
342{
343	int		len;
344	float	fl;
345	byte	c;
346
347//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
348
349	if (!cls.demorecording)
350		return;
351
352	fl = LittleFloat((float)realtime);
353	fwrite (&fl, sizeof(fl), 1, cls.demofile);
354
355	c = dem_set;
356	fwrite (&c, sizeof(c), 1, cls.demofile);
357
358	len = LittleLong(cls.netchan.outgoing_sequence);
359	fwrite (&len, 4, 1, cls.demofile);
360	len = LittleLong(cls.netchan.incoming_sequence);
361	fwrite (&len, 4, 1, cls.demofile);
362
363	fflush (cls.demofile);
364}
365
366
367
368
369/*
370====================
371CL_Record_f
372
373record <demoname> <server>
374====================
375*/
376void CL_Record_f (void)
377{
378	int		c;
379	char	name[MAX_OSPATH];
380	sizebuf_t	buf;
381	char	buf_data[MAX_MSGLEN];
382	int n, i, j;
383	char *s;
384	entity_t *ent;
385	entity_state_t *es, blankes;
386	player_info_t *player;
387	extern	char gamedirfile[];
388	int seq = 1;
389
390	c = Cmd_Argc();
391	if (c != 2)
392	{
393		Con_Printf ("record <demoname>\n");
394		return;
395	}
396
397	if (cls.state != ca_active) {
398		Con_Printf ("You must be connected to record.\n");
399		return;
400	}
401
402	if (cls.demorecording)
403		CL_Stop_f();
404
405	sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
406
407//
408// open the demo file
409//
410	COM_DefaultExtension (name, ".qwd");
411
412	cls.demofile = fopen (name, "wb");
413	if (!cls.demofile)
414	{
415		Con_Printf ("ERROR: couldn't open.\n");
416		return;
417	}
418
419	Con_Printf ("recording to %s.\n", name);
420	cls.demorecording = true;
421
422/*-------------------------------------------------*/
423
424// serverdata
425	// send the info about the new client to all connected clients
426	memset(&buf, 0, sizeof(buf));
427	buf.data = (byte*) buf_data;
428	buf.maxsize = sizeof(buf_data);
429
430// send the serverdata
431	MSG_WriteByte (&buf, svc_serverdata);
432	MSG_WriteLong (&buf, PROTOCOL_VERSION);
433	MSG_WriteLong (&buf, cl.servercount);
434	MSG_WriteString (&buf, gamedirfile);
435
436	if (cl.spectator)
437		MSG_WriteByte (&buf, cl.playernum | 128);
438	else
439		MSG_WriteByte (&buf, cl.playernum);
440
441	// send full levelname
442	MSG_WriteString (&buf, cl.levelname);
443
444	// send the movevars
445	MSG_WriteFloat(&buf, movevars.gravity);
446	MSG_WriteFloat(&buf, movevars.stopspeed);
447	MSG_WriteFloat(&buf, movevars.maxspeed);
448	MSG_WriteFloat(&buf, movevars.spectatormaxspeed);
449	MSG_WriteFloat(&buf, movevars.accelerate);
450	MSG_WriteFloat(&buf, movevars.airaccelerate);
451	MSG_WriteFloat(&buf, movevars.wateraccelerate);
452	MSG_WriteFloat(&buf, movevars.friction);
453	MSG_WriteFloat(&buf, movevars.waterfriction);
454	MSG_WriteFloat(&buf, movevars.entgravity);
455
456	// send music
457	MSG_WriteByte (&buf, svc_cdtrack);
458	MSG_WriteByte (&buf, 0); // none in demos
459
460	// send server info string
461	MSG_WriteByte (&buf, svc_stufftext);
462	MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) );
463
464	// flush packet
465	CL_WriteRecordDemoMessage (&buf, seq++);
466	SZ_Clear (&buf);
467
468// soundlist
469	MSG_WriteByte (&buf, svc_soundlist);
470	MSG_WriteByte (&buf, 0);
471
472	n = 0;
473	s = cl.sound_name[n+1];
474	while (*s) {
475		MSG_WriteString (&buf, s);
476		if (buf.cursize > MAX_MSGLEN/2) {
477			MSG_WriteByte (&buf, 0);
478			MSG_WriteByte (&buf, n);
479			CL_WriteRecordDemoMessage (&buf, seq++);
480			SZ_Clear (&buf);
481			MSG_WriteByte (&buf, svc_soundlist);
482			MSG_WriteByte (&buf, n + 1);
483		}
484		n++;
485		s = cl.sound_name[n+1];
486	}
487	if (buf.cursize) {
488		MSG_WriteByte (&buf, 0);
489		MSG_WriteByte (&buf, 0);
490		CL_WriteRecordDemoMessage (&buf, seq++);
491		SZ_Clear (&buf);
492	}
493
494// modellist
495	MSG_WriteByte (&buf, svc_modellist);
496	MSG_WriteByte (&buf, 0);
497
498	n = 0;
499	s = cl.model_name[n+1];
500	while (*s) {
501		MSG_WriteString (&buf, s);
502		if (buf.cursize > MAX_MSGLEN/2) {
503			MSG_WriteByte (&buf, 0);
504			MSG_WriteByte (&buf, n);
505			CL_WriteRecordDemoMessage (&buf, seq++);
506			SZ_Clear (&buf);
507			MSG_WriteByte (&buf, svc_modellist);
508			MSG_WriteByte (&buf, n + 1);
509		}
510		n++;
511		s = cl.model_name[n+1];
512	}
513	if (buf.cursize) {
514		MSG_WriteByte (&buf, 0);
515		MSG_WriteByte (&buf, 0);
516		CL_WriteRecordDemoMessage (&buf, seq++);
517		SZ_Clear (&buf);
518	}
519
520// spawnstatic
521
522	for (i = 0; i < cl.num_statics; i++) {
523		ent = cl_static_entities + i;
524
525		MSG_WriteByte (&buf, svc_spawnstatic);
526
527		for (j = 1; j < MAX_MODELS; j++)
528			if (ent->model == cl.model_precache[j])
529				break;
530		if (j == MAX_MODELS)
531			MSG_WriteByte (&buf, 0);
532		else
533			MSG_WriteByte (&buf, j);
534
535		MSG_WriteByte (&buf, ent->frame);
536		MSG_WriteByte (&buf, 0);
537		MSG_WriteByte (&buf, ent->skinnum);
538		for (j=0 ; j<3 ; j++)
539		{
540			MSG_WriteCoord (&buf, ent->origin[j]);
541			MSG_WriteAngle (&buf, ent->angles[j]);
542		}
543
544		if (buf.cursize > MAX_MSGLEN/2) {
545			CL_WriteRecordDemoMessage (&buf, seq++);
546			SZ_Clear (&buf);
547		}
548	}
549
550// spawnstaticsound
551	// static sounds are skipped in demos, life is hard
552
553// baselines
554
555	memset(&blankes, 0, sizeof(blankes));
556	for (i = 0; i < MAX_EDICTS; i++) {
557		es = cl_baselines + i;
558
559		if (memcmp(es, &blankes, sizeof(blankes))) {
560			MSG_WriteByte (&buf,svc_spawnbaseline);
561			MSG_WriteShort (&buf, i);
562
563			MSG_WriteByte (&buf, es->modelindex);
564			MSG_WriteByte (&buf, es->frame);
565			MSG_WriteByte (&buf, es->colormap);
566			MSG_WriteByte (&buf, es->skinnum);
567			for (j=0 ; j<3 ; j++)
568			{
569				MSG_WriteCoord(&buf, es->origin[j]);
570				MSG_WriteAngle(&buf, es->angles[j]);
571			}
572
573			if (buf.cursize > MAX_MSGLEN/2) {
574				CL_WriteRecordDemoMessage (&buf, seq++);
575				SZ_Clear (&buf);
576			}
577		}
578	}
579
580	MSG_WriteByte (&buf, svc_stufftext);
581	MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) );
582
583	if (buf.cursize) {
584		CL_WriteRecordDemoMessage (&buf, seq++);
585		SZ_Clear (&buf);
586	}
587
588// send current status of all other players
589
590	for (i = 0; i < MAX_CLIENTS; i++) {
591		player = cl.players + i;
592
593		MSG_WriteByte (&buf, svc_updatefrags);
594		MSG_WriteByte (&buf, i);
595		MSG_WriteShort (&buf, player->frags);
596
597		MSG_WriteByte (&buf, svc_updateping);
598		MSG_WriteByte (&buf, i);
599		MSG_WriteShort (&buf, player->ping);
600
601		MSG_WriteByte (&buf, svc_updatepl);
602		MSG_WriteByte (&buf, i);
603		MSG_WriteByte (&buf, player->pl);
604
605		MSG_WriteByte (&buf, svc_updateentertime);
606		MSG_WriteByte (&buf, i);
607		MSG_WriteFloat (&buf, player->entertime);
608
609		MSG_WriteByte (&buf, svc_updateuserinfo);
610		MSG_WriteByte (&buf, i);
611		MSG_WriteLong (&buf, player->userid);
612		MSG_WriteString (&buf, player->userinfo);
613
614		if (buf.cursize > MAX_MSGLEN/2) {
615			CL_WriteRecordDemoMessage (&buf, seq++);
616			SZ_Clear (&buf);
617		}
618	}
619
620// send all current light styles
621	for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
622	{
623		MSG_WriteByte (&buf, svc_lightstyle);
624		MSG_WriteByte (&buf, (char)i);
625		MSG_WriteString (&buf, cl_lightstyle[i].map);
626	}
627
628	for (i = 0; i < MAX_CL_STATS; i++) {
629		MSG_WriteByte (&buf, svc_updatestatlong);
630		MSG_WriteByte (&buf, i);
631		MSG_WriteLong (&buf, cl.stats[i]);
632		if (buf.cursize > MAX_MSGLEN/2) {
633			CL_WriteRecordDemoMessage (&buf, seq++);
634			SZ_Clear (&buf);
635		}
636	}
637
638#if 0
639	MSG_WriteByte (&buf, svc_updatestatlong);
640	MSG_WriteByte (&buf, STAT_TOTALMONSTERS);
641	MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]);
642
643	MSG_WriteByte (&buf, svc_updatestatlong);
644	MSG_WriteByte (&buf, STAT_SECRETS);
645	MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]);
646
647	MSG_WriteByte (&buf, svc_updatestatlong);
648	MSG_WriteByte (&buf, STAT_MONSTERS);
649	MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]);
650#endif
651
652	// get the client to check and download skins
653	// when that is completed, a begin command will be issued
654	MSG_WriteByte (&buf, svc_stufftext);
655	MSG_WriteString (&buf, va("skins\n") );
656
657	CL_WriteRecordDemoMessage (&buf, seq++);
658
659	CL_WriteSetDemoMessage();
660
661	// done
662}
663
664/*
665====================
666CL_ReRecord_f
667
668record <demoname>
669====================
670*/
671void CL_ReRecord_f (void)
672{
673	int		c;
674	char	name[MAX_OSPATH];
675
676	c = Cmd_Argc();
677	if (c != 2)
678	{
679		Con_Printf ("rerecord <demoname>\n");
680		return;
681	}
682
683	if (!*cls.servername) {
684		Con_Printf("No server to reconnect to...\n");
685		return;
686	}
687
688	if (cls.demorecording)
689		CL_Stop_f();
690
691	sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
692
693//
694// open the demo file
695//
696	COM_DefaultExtension (name, ".qwd");
697
698	cls.demofile = fopen (name, "wb");
699	if (!cls.demofile)
700	{
701		Con_Printf ("ERROR: couldn't open.\n");
702		return;
703	}
704
705	Con_Printf ("recording to %s.\n", name);
706	cls.demorecording = true;
707
708	CL_Disconnect();
709	CL_BeginServerConnect();
710}
711
712
713/*
714====================
715CL_PlayDemo_f
716
717play [demoname]
718====================
719*/
720void CL_PlayDemo_f (void)
721{
722	char	name[256];
723
724	if (Cmd_Argc() != 2)
725	{
726		Con_Printf ("play <demoname> : plays a demo\n");
727		return;
728	}
729
730//
731// disconnect from server
732//
733	CL_Disconnect ();
734
735//
736// open the demo file
737//
738	strcpy (name, Cmd_Argv(1));
739	COM_DefaultExtension (name, ".qwd");
740
741	Con_Printf ("Playing demo from %s.\n", name);
742	COM_FOpenFile (name, &cls.demofile);
743	if (!cls.demofile)
744	{
745		Con_Printf ("ERROR: couldn't open.\n");
746		cls.demonum = -1;		// stop demo loop
747		return;
748	}
749
750	cls.demoplayback = true;
751	cls.state = ca_demostart;
752	Netchan_Setup (&cls.netchan, net_from, 0);
753	realtime = 0;
754}
755
756/*
757====================
758CL_FinishTimeDemo
759
760====================
761*/
762void CL_FinishTimeDemo (void)
763{
764	int		frames;
765	float	time;
766
767	cls.timedemo = false;
768
769// the first frame didn't count
770	frames = (host_framecount - cls.td_startframe) - 1;
771	time = Sys_DoubleTime() - cls.td_starttime;
772	if (!time)
773		time = 1;
774	Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time);
775}
776
777/*
778====================
779CL_TimeDemo_f
780
781timedemo [demoname]
782====================
783*/
784void CL_TimeDemo_f (void)
785{
786	if (Cmd_Argc() != 2)
787	{
788		Con_Printf ("timedemo <demoname> : gets demo speeds\n");
789		return;
790	}
791
792	CL_PlayDemo_f ();
793
794	if (cls.state != ca_demostart)
795		return;
796
797// cls.td_starttime will be grabbed at the second frame of the demo, so
798// all the loading time doesn't get counted
799
800	cls.timedemo = true;
801	cls.td_starttime = 0;
802	cls.td_startframe = host_framecount;
803	cls.td_lastframe = -1;		// get a new message this frame
804}
805
806