1 /*
2 Copyright (C) 1997-2001 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 // cl_demo.c
22 //
23 
24 #include "cl_local.h"
25 
26 /*
27 ================
28 CL_WriteDemoPlayerstate
29 
30 Delta compresses between 'from' and 'to' then writes to 'msg' in standard
31 ORIGINAL_PROTOCOL_VERSION form. This is basically translating any other protocols
32 to the target buffer, for things like demos.
33 ================
34 */
CL_WriteDemoPlayerstate(frame_t * from,frame_t * to,netMsg_t * msg)35 void CL_WriteDemoPlayerstate (frame_t *from, frame_t *to, netMsg_t *msg)
36 {
37 	playerStateNew_t	*ps, *ops;
38 	playerStateNew_t	dummy;
39 	int					i, psFlags;
40 	int					statBits;
41 
42 	ps = &to->playerState;
43 	if (!from)
44 		ops = &dummy;
45 	else
46 		ops = &from->playerState;
47 
48 	ps = &to->playerState;
49 	if (!from) {
50 		memset (&dummy, 0, sizeof (dummy));
51 		ops = &dummy;
52 	}
53 	else
54 		ops = &from->playerState;
55 
56 	// Determine what needs to be sent
57 	psFlags = 0;
58 	if (ps->pMove.pmType != ops->pMove.pmType)
59 		psFlags |= PS_M_TYPE;
60 
61 	if (ps->pMove.origin[0] != ops->pMove.origin[0]
62 	|| ps->pMove.origin[1] != ops->pMove.origin[1]
63 	|| ps->pMove.origin[2] != ops->pMove.origin[2])
64 		psFlags |= PS_M_ORIGIN;
65 
66 	if (ps->pMove.velocity[0] != ops->pMove.velocity[0]
67 	|| ps->pMove.velocity[1] != ops->pMove.velocity[1]
68 	|| ps->pMove.velocity[2] != ops->pMove.velocity[2])
69 		psFlags |= PS_M_VELOCITY;
70 
71 	if (ps->pMove.pmTime != ops->pMove.pmTime)
72 		psFlags |= PS_M_TIME;
73 
74 	if (ps->pMove.pmFlags != ops->pMove.pmFlags)
75 		psFlags |= PS_M_FLAGS;
76 
77 	if (ps->pMove.gravity != ops->pMove.gravity)
78 		psFlags |= PS_M_GRAVITY;
79 
80 	if (ps->pMove.deltaAngles[0] != ops->pMove.deltaAngles[0]
81 	|| ps->pMove.deltaAngles[1] != ops->pMove.deltaAngles[1]
82 	|| ps->pMove.deltaAngles[2] != ops->pMove.deltaAngles[2])
83 		psFlags |= PS_M_DELTA_ANGLES;
84 
85 
86 	if (ps->viewOffset[0] != ops->viewOffset[0]
87 	|| ps->viewOffset[1] != ops->viewOffset[1]
88 	|| ps->viewOffset[2] != ops->viewOffset[2])
89 		psFlags |= PS_VIEWOFFSET;
90 
91 	if (ps->viewAngles[0] != ops->viewAngles[0]
92 	|| ps->viewAngles[1] != ops->viewAngles[1]
93 	|| ps->viewAngles[2] != ops->viewAngles[2])
94 		psFlags |= PS_VIEWANGLES;
95 
96 	if (ps->kickAngles[0] != ops->kickAngles[0]
97 	|| ps->kickAngles[1] != ops->kickAngles[1]
98 	|| ps->kickAngles[2] != ops->kickAngles[2])
99 		psFlags |= PS_KICKANGLES;
100 
101 	if (ps->viewBlend[0] != ops->viewBlend[0]
102 	|| ps->viewBlend[1] != ops->viewBlend[1]
103 	|| ps->viewBlend[2] != ops->viewBlend[2]
104 	|| ps->viewBlend[3] != ops->viewBlend[3])
105 		psFlags |= PS_BLEND;
106 
107 	if (ps->fov != ops->fov)
108 		psFlags |= PS_FOV;
109 
110 	if (ps->rdFlags != ops->rdFlags)
111 		psFlags |= PS_RDFLAGS;
112 
113 	if (ps->gunFrame != ops->gunFrame)
114 		psFlags |= PS_WEAPONFRAME;
115 
116 	psFlags |= PS_WEAPONINDEX;
117 
118 	// Write it
119 	MSG_WriteByte (msg, SVC_PLAYERINFO);
120 	MSG_WriteShort (msg, psFlags);
121 
122 	// Write the pMoveState_t
123 	if (psFlags & PS_M_TYPE)
124 		MSG_WriteByte (msg, ps->pMove.pmType);
125 
126 	if (psFlags & PS_M_ORIGIN) {
127 		MSG_WriteShort (msg, ps->pMove.origin[0]);
128 		MSG_WriteShort (msg, ps->pMove.origin[1]);
129 		MSG_WriteShort (msg, ps->pMove.origin[2]);
130 	}
131 
132 	if (psFlags & PS_M_VELOCITY) {
133 		MSG_WriteShort (msg, ps->pMove.velocity[0]);
134 		MSG_WriteShort (msg, ps->pMove.velocity[1]);
135 		MSG_WriteShort (msg, ps->pMove.velocity[2]);
136 	}
137 
138 	if (psFlags & PS_M_TIME)
139 		MSG_WriteByte (msg, ps->pMove.pmTime);
140 
141 	if (psFlags & PS_M_FLAGS)
142 		MSG_WriteByte (msg, ps->pMove.pmFlags);
143 
144 	if (psFlags & PS_M_GRAVITY)
145 		MSG_WriteShort (msg, ps->pMove.gravity);
146 
147 	if (psFlags & PS_M_DELTA_ANGLES) {
148 		MSG_WriteShort (msg, ps->pMove.deltaAngles[0]);
149 		MSG_WriteShort (msg, ps->pMove.deltaAngles[1]);
150 		MSG_WriteShort (msg, ps->pMove.deltaAngles[2]);
151 	}
152 
153 	// Write the rest of the playerState_t
154 	if (psFlags & PS_VIEWOFFSET) {
155 		MSG_WriteChar (msg, ps->viewOffset[0]*4);
156 		MSG_WriteChar (msg, ps->viewOffset[1]*4);
157 		MSG_WriteChar (msg, ps->viewOffset[2]*4);
158 	}
159 
160 	if (psFlags & PS_VIEWANGLES) {
161 		MSG_WriteAngle16 (msg, ps->viewAngles[0]);
162 		MSG_WriteAngle16 (msg, ps->viewAngles[1]);
163 		MSG_WriteAngle16 (msg, ps->viewAngles[2]);
164 	}
165 
166 	if (psFlags & PS_KICKANGLES) {
167 		MSG_WriteChar (msg, ps->kickAngles[0]*4);
168 		MSG_WriteChar (msg, ps->kickAngles[1]*4);
169 		MSG_WriteChar (msg, ps->kickAngles[2]*4);
170 	}
171 
172 	if (psFlags & PS_WEAPONINDEX) {
173 		MSG_WriteByte (msg, ps->gunIndex);
174 	}
175 
176 	if (psFlags & PS_WEAPONFRAME) {
177 		MSG_WriteByte (msg, ps->gunFrame);
178 		MSG_WriteChar (msg, ps->gunOffset[0]*4);
179 		MSG_WriteChar (msg, ps->gunOffset[1]*4);
180 		MSG_WriteChar (msg, ps->gunOffset[2]*4);
181 		MSG_WriteChar (msg, ps->gunAngles[0]*4);
182 		MSG_WriteChar (msg, ps->gunAngles[1]*4);
183 		MSG_WriteChar (msg, ps->gunAngles[2]*4);
184 	}
185 
186 	if (psFlags & PS_BLEND) {
187 		// R1: clamp the color
188 		if (ps->viewBlend[1] > 1)
189 			ps->viewBlend[1] = 1;
190 		if (ps->viewBlend[2] > 1)
191 			ps->viewBlend[2] = 1;
192 		if (ps->viewBlend[3] > 1)
193 			ps->viewBlend[3] = 1;
194 
195 		MSG_WriteByte (msg, ps->viewBlend[0]*255);
196 		MSG_WriteByte (msg, ps->viewBlend[1]*255);
197 		MSG_WriteByte (msg, ps->viewBlend[2]*255);
198 		MSG_WriteByte (msg, ps->viewBlend[3]*255);
199 	}
200 
201 	if (psFlags & PS_FOV)
202 		MSG_WriteByte (msg, ps->fov);
203 
204 	if (psFlags & PS_RDFLAGS)
205 		MSG_WriteByte (msg, ps->rdFlags);
206 
207 	// Send stats
208 	statBits = 0;
209 	for (i=0 ; i<MAX_STATS ; i++)
210 		if (ps->stats[i] != ops->stats[i])
211 			statBits |= 1<<i;
212 	MSG_WriteLong (msg, statBits);
213 	for (i=0 ; i<MAX_STATS ; i++)
214 		if (statBits & (1<<i))
215 			MSG_WriteShort (msg, ps->stats[i]);
216 }
217 
218 
219 /*
220 ================
221 CL_WriteDemoPacketEntities
222 
223 Basically for demo translation, just like CL_WriteDemoPlayerstate above.
224 ================
225 */
CL_WriteDemoPacketEntities(const frame_t * from,frame_t * to,netMsg_t * msg)226 void CL_WriteDemoPacketEntities (const frame_t *from, frame_t *to, netMsg_t *msg)
227 {
228 	entityStateOld_t	*oldEnt, *newEnt;
229 	int		oldIndex, newIndex;
230 	int		oldNum, newNum;
231 	int		from_numEntities;
232 
233 	MSG_WriteByte (msg, SVC_PACKETENTITIES);
234 
235 	if (!from)
236 		from_numEntities = 0;
237 	else
238 		from_numEntities = from->numEntities;
239 
240 	newIndex = 0;
241 	newEnt = NULL;
242 	oldIndex = 0;
243 	oldEnt = NULL;
244 	while (newIndex < to->numEntities || oldIndex < from_numEntities) {
245 		if (newIndex >= to->numEntities) {
246 			newNum = 9999;
247 		}
248 		else {
249 			newEnt =(entityStateOld_t *)&cl_parseEntities[(to->parseEntities+newIndex)&(MAX_PARSE_ENTITIES-1)];
250 			newNum = newEnt->number;
251 		}
252 
253 		if (oldIndex >= from_numEntities) {
254 			oldNum = 9999;
255 		}
256 		else {
257 			oldEnt = (entityStateOld_t *)&cl_parseEntities[(from->parseEntities+oldIndex)&(MAX_PARSE_ENTITIES-1)];
258 			oldNum = oldEnt->number;
259 		}
260 
261 		if (newNum == oldNum) {
262 			/*
263 			** delta update from old position
264 			** because the force parm is qFalse, this will not result
265 			** in any bytes being emited if the entity has not changed at all
266 			** note that players are always 'newentities', this updates their oldorigin always
267 			** and prevents warping
268 			*/
269 			MSG_WriteDeltaEntity (msg, oldEnt, newEnt, qFalse, newEnt->number <= cl.maxClients);
270 			oldIndex++;
271 			newIndex++;
272 			continue;
273 		}
274 
275 		if (newNum < oldNum) {
276 			// This is a new entity, send it from the baseline
277 			MSG_WriteDeltaEntity (msg, (entityStateOld_t *)&cl_baseLines[newNum], newEnt, qTrue, qTrue);
278 			newIndex++;
279 			continue;
280 		}
281 
282 		if (newNum > oldNum) {
283 			// This old entity isn't present in the new message
284 			MSG_WriteDeltaEntity (msg, oldEnt, NULL, qTrue, qFalse);
285 			oldIndex++;
286 			continue;
287 		}
288 	}
289 
290 	// End of packetentities
291 	MSG_WriteShort (msg, 0);
292 }
293 
294 
295 /*
296 ====================
297 CL_WriteDemoMessageChunk
298 
299 This is only used for ENHANCED_PROTOCOL_VERSION
300 ====================
301 */
CL_WriteDemoMessageChunk(byte * buffer,int length,qBool forceFlush)302 void CL_WriteDemoMessageChunk (byte *buffer, int length, qBool forceFlush)
303 {
304 	if (!cls.demoRecording || cls.serverProtocol == ORIGINAL_PROTOCOL_VERSION)
305 		return;
306 
307 	if (forceFlush) {
308 		if (!cls.demoWaiting) {
309 			int	swLen;
310 
311 			if (cl.demoBuffer.overFlowed) {
312 				Com_DevPrintf (0, "Dropped demo frame, maximum message size exceeded: %i > %i\n", cl.demoBuffer.curSize, cl.demoBuffer.maxSize);
313 
314 				// Write a message regardless, to keep sync
315 				MSG_Clear (&cl.demoBuffer);
316 				MSG_WriteByte (&cl.demoBuffer, SVC_NOP);
317 			}
318 
319 			swLen = LittleLong (cl.demoBuffer.curSize);
320 			FS_Write (&swLen, sizeof (swLen), cls.demoFile);
321 			FS_Write (cl.demoFrame, cl.demoBuffer.curSize, cls.demoFile);
322 		}
323 		MSG_Clear (&cl.demoBuffer);
324 	}
325 
326 	if (length)
327 		MSG_WriteRaw (&cl.demoBuffer, buffer, length);
328 }
329 
330 
331 /*
332 ====================
333 CL_WriteDemoMessageFull
334 
335 Dumps the current net message, prefixed by the length
336 This is only used for ORIGINAL_PROTOCOL_VERSION
337 ====================
338 */
CL_WriteDemoMessageFull(void)339 void CL_WriteDemoMessageFull (void)
340 {
341 	int		len, swLen;
342 
343 	// The first eight bytes are just packet sequencing stuff
344 	len = cls.netMessage.curSize-8;
345 	swLen = LittleLong (len);
346 	if (swLen) {
347 		FS_Write (&swLen, sizeof (swLen), cls.demoFile);
348 		FS_Write (cls.netMessage.data+8, len, cls.demoFile);
349 	}
350 }
351 
352 
353 /*
354 ====================
355 CL_StartDemoRecording
356 ====================
357 */
CL_StartDemoRecording(char * name)358 qBool CL_StartDemoRecording (char *name)
359 {
360 	byte				buf_data[MAX_SV_USABLEMSG];
361 	netMsg_t			buf;
362 	int					i, len;
363 	entityStateOld_t	*ent, temp;
364 	entityStateOld_t	nullstate;
365 
366 	// Open the demo file
367 	FS_CreatePath (name);
368 	FS_OpenFile (name, &cls.demoFile, FS_MODE_WRITE_BINARY);
369 	if (!cls.demoFile) {
370 		return qFalse;
371 	}
372 
373 	cls.demoRecording = qTrue;
374 
375 	if (cls.serverProtocol == ENHANCED_PROTOCOL_VERSION) {
376 		MSG_WriteByte (&cls.netChan.message, CLC_SETTING);
377 		MSG_WriteShort (&cls.netChan.message, CLSET_RECORDING);
378 		MSG_WriteShort (&cls.netChan.message, 1);
379 	}
380 
381 	// Don't start saving messages until a non-delta compressed message is received
382 	cls.demoWaiting = qTrue;
383 
384 	// Write out messages to hold the startup information
385 	MSG_Init (&buf, buf_data, sizeof (buf_data));
386 
387 	// Send the serverdata
388 	MSG_WriteByte (&buf, SVC_SERVERDATA);
389 	MSG_WriteLong (&buf, ORIGINAL_PROTOCOL_VERSION);
390 	MSG_WriteLong (&buf, 0x10000 + cl.serverCount);
391 	MSG_WriteByte (&buf, 1);	// demos are always attract loops
392 	MSG_WriteString (&buf, cl.gameDir);
393 	MSG_WriteShort (&buf, cl.playerNum);
394 	MSG_WriteString (&buf, cl.configStrings[CS_NAME]);
395 
396 	// Configstrings
397 	for (i=0 ; i<MAX_CFGSTRINGS ; i++) {
398 		if (cl.configStrings[i][0]) {
399 			if (buf.curSize + (int)strlen (cl.configStrings[i]) + 32 > buf.maxSize) {
400 				// write it out
401 				len = LittleLong (buf.curSize);
402 				FS_Write (&len, sizeof (len), cls.demoFile);
403 				FS_Write (buf.data, buf.curSize, cls.demoFile);
404 				buf.curSize = 0;
405 			}
406 
407 			MSG_WriteByte (&buf, SVC_CONFIGSTRING);
408 			MSG_WriteShort (&buf, i);
409 			MSG_WriteString (&buf, cl.configStrings[i]);
410 		}
411 
412 	}
413 
414 	// Baselines
415 	memset (&nullstate, 0, sizeof (nullstate));
416 	for (i=0; i<MAX_CS_EDICTS ; i++) {
417 		memcpy (&temp, &cl_baseLines[i], sizeof (entityStateOld_t));
418 		ent = &temp;
419 		if (!ent->modelIndex)
420 			continue;
421 
422 		if (buf.curSize + 64 > buf.maxSize) {
423 			// Write it out
424 			len = LittleLong (buf.curSize);
425 			FS_Write (&len, sizeof (len), cls.demoFile);
426 			FS_Write (buf.data, buf.curSize, cls.demoFile);
427 			buf.curSize = 0;
428 		}
429 
430 		MSG_WriteByte (&buf, SVC_SPAWNBASELINE);
431 		MSG_WriteDeltaEntity (&buf, &nullstate, ent, qTrue, qTrue);
432 	}
433 
434 	MSG_WriteByte (&buf, SVC_STUFFTEXT);
435 	MSG_WriteString (&buf, "precache\n");
436 
437 	// Write it to the demo file
438 	len = LittleLong (buf.curSize);
439 	FS_Write (&len, sizeof (len), cls.demoFile);
440 	FS_Write (buf.data, buf.curSize, cls.demoFile);
441 
442 	// The rest of the demo file will be individual frames
443 	return qTrue;
444 }
445 
446 
447 /*
448 ====================
449 CL_StopDemoRecording
450 ====================
451 */
CL_StopDemoRecording(void)452 void CL_StopDemoRecording (void)
453 {
454 	int		len;
455 
456 	// Write to file
457 	len = -1;
458 	FS_Write (&len, sizeof (len), cls.demoFile);
459 	FS_CloseFile (cls.demoFile);
460 
461 	if (cls.serverProtocol == ENHANCED_PROTOCOL_VERSION) {
462 		MSG_WriteByte (&cls.netChan.message, CLC_SETTING);
463 		MSG_WriteShort (&cls.netChan.message, CLSET_RECORDING);
464 		MSG_WriteShort (&cls.netChan.message, 0);
465 	}
466 
467 	// Finish up
468 	cls.demoFile = 0;
469 	cls.demoRecording = qFalse;
470 }
471