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