1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 // cg_snapshot.c -- things that happen on snapshot transition,
30 // not necessarily every single rendered frame
31
32
33
34 #include "cg_local.h"
35
36
37
38 /*
39 ==================
40 CG_ResetEntity
41 ==================
42 */
CG_ResetEntity(centity_t * cent)43 static void CG_ResetEntity( centity_t *cent ) {
44 // if an event is set, assume it is new enough to use
45 // if the event had timed out, it would have been cleared
46 // RF, not needed for wolf
47 // DHM - Nerve :: Wolf is now using this.
48 cent->previousEvent = 0;
49 cent->previousEventSequence = cent->currentState.eventSequence;
50
51 cent->trailTime = cg.snap->serverTime;
52
53 // Ridah
54 cent->headJuncIndex = 0;
55 cent->headJuncIndex2 = 0;
56 // done.
57
58 VectorCopy( cent->currentState.origin, cent->lerpOrigin );
59 VectorCopy( cent->currentState.angles, cent->lerpAngles );
60 if ( cent->currentState.eType == ET_PLAYER ) {
61 CG_ResetPlayerEntity( cent );
62 }
63 }
64
65
66
67 /*
68 ===============
69 CG_TransitionEntity
70
71 cent->nextState is moved to cent->currentState and events are fired
72 ===============
73 */
CG_TransitionEntity(centity_t * cent)74 static void CG_TransitionEntity( centity_t *cent ) {
75
76 // Ridah, update the fireDir if it's on fire
77 if ( CG_EntOnFire( cent ) ) {
78 vec3_t newDir, newPos, oldPos;
79 float adjust;
80 //
81 BG_EvaluateTrajectory( ¢->nextState.pos, cg.snap->serverTime, newPos );
82 BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, oldPos );
83 // update the fireRiseDir
84 VectorSubtract( oldPos, newPos, newDir );
85 // fire should go upwards if travelling slow
86 newDir[2] += 2;
87 if ( VectorNormalize( newDir ) < 1 ) {
88 VectorClear( newDir );
89 newDir[2] = 1;
90 }
91 // now move towards the newDir
92 adjust = 0.3;
93 VectorMA( cent->fireRiseDir, adjust, newDir, cent->fireRiseDir );
94 if ( VectorNormalize( cent->fireRiseDir ) <= 0.1 ) {
95 VectorCopy( newDir, cent->fireRiseDir );
96 }
97 }
98
99 //----(SA) the ent lost or gained some part(s), do any necessary effects
100 //TODO: check for ai first
101 if ( cent->currentState.dmgFlags != cent->nextState.dmgFlags ) {
102 CG_AttachedPartChange( cent );
103 }
104
105
106 cent->currentState = cent->nextState;
107 cent->currentValid = qtrue;
108
109 // reset if the entity wasn't in the last frame or was teleported
110 if ( !cent->interpolate ) {
111 CG_ResetEntity( cent );
112 }
113
114 // clear the next state. if will be set by the next CG_SetNextSnap
115 cent->interpolate = qfalse;
116
117 // check for events
118 CG_CheckEvents( cent );
119 }
120
121
122 /*
123 ==================
124 CG_SetInitialSnapshot
125
126 This will only happen on the very first snapshot.
127 All other times will use CG_TransitionSnapshot instead.
128 ==================
129 */
CG_SetInitialSnapshot(snapshot_t * snap)130 void CG_SetInitialSnapshot( snapshot_t *snap ) {
131 int i;
132 centity_t *cent;
133 entityState_t *state;
134
135 cg.snap = snap;
136
137 BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
138
139 // sort out solid entities
140 CG_BuildSolidList();
141
142 CG_ExecuteNewServerCommands( snap->serverCommandSequence );
143
144 // set our local weapon selection pointer to
145 // what the server has indicated the current weapon is
146 CG_Respawn();
147
148 for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
149 state = &cg.snap->entities[ i ];
150 cent = &cg_entities[ state->number ];
151
152 memcpy( ¢->currentState, state, sizeof( entityState_t ) );
153 //cent->currentState = *state;
154 cent->interpolate = qfalse;
155 cent->currentValid = qtrue;
156
157 CG_ResetEntity( cent );
158
159 // check for events
160 CG_CheckEvents( cent );
161 }
162
163 // JPW NERVE -- make sure we didn't break anything
164 cg_fxflags = 0;
165 // jpw
166
167 // DHM - Nerve :: Set cg.clientNum so that it may be used elsewhere
168 // NERVE - SMF - commented out, TA merge, this has been moved to CG_Init
169 // cg.clientNum = snap->ps.clientNum;
170
171 // NERVE - SMF
172 {
173 static char prevmap[64] = { 0 };
174 char curmap[64];
175
176 trap_Cvar_VariableStringBuffer( "mapname", curmap, 64 );
177
178 if ( cgs.gametype >= GT_WOLF && Q_stricmp( curmap, prevmap ) ) {
179 strcpy( prevmap, curmap );
180 trap_SendConsoleCommand( "openLimboMenu\n" );
181 }
182 }
183 // -NERVE - SMF
184 }
185
186
187 /*
188 ===================
189 CG_TransitionSnapshot
190
191 The transition point from snap to nextSnap has passed
192 ===================
193 */
CG_TransitionSnapshot(void)194 static void CG_TransitionSnapshot( void ) {
195 centity_t *cent;
196 snapshot_t *oldFrame;
197 int i;
198
199 if ( !cg.snap ) {
200 CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
201 }
202 if ( !cg.nextSnap ) {
203 CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
204 }
205
206 // execute any server string commands before transitioning entities
207 CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
208
209 // if we had a map_restart, set everything with initial
210 if ( cg.mapRestart ) {
211 }
212
213 // clear the currentValid flag for all entities in the existing snapshot
214 for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
215 cent = &cg_entities[ cg.snap->entities[ i ].number ];
216 cent->currentValid = qfalse;
217 }
218
219 // move nextSnap to snap and do the transitions
220 oldFrame = cg.snap;
221 cg.snap = cg.nextSnap;
222
223 BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
224 cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;
225
226 for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
227 cent = &cg_entities[ cg.snap->entities[ i ].number ];
228 CG_TransitionEntity( cent );
229 }
230
231 cg.nextSnap = NULL;
232
233 // check for playerstate transition events
234 if ( oldFrame ) {
235 playerState_t *ops, *ps;
236
237 ops = &oldFrame->ps;
238 ps = &cg.snap->ps;
239 // teleporting checks are irrespective of prediction
240 if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
241 cg.thisFrameTeleport = qtrue; // will be cleared by prediction code
242 }
243
244 // if we are not doing client side movement prediction for any
245 // reason, then the client events and view changes will be issued now
246 if ( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW )
247 || cg_nopredict.integer || cg_synchronousClients.integer ) {
248 CG_TransitionPlayerState( ps, ops );
249 }
250 }
251
252 }
253
254
255 /*
256 ===================
257 CG_SetNextSnap
258
259 A new snapshot has just been read in from the client system.
260 ===================
261 */
CG_SetNextSnap(snapshot_t * snap)262 static void CG_SetNextSnap( snapshot_t *snap ) {
263 int num;
264 entityState_t *es;
265 centity_t *cent;
266
267 cg.nextSnap = snap;
268
269 BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
270 cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
271
272 // check for extrapolation errors
273 for ( num = 0 ; num < snap->numEntities ; num++ ) {
274 es = &snap->entities[num];
275 cent = &cg_entities[ es->number ];
276
277 memcpy( ¢->nextState, es, sizeof( entityState_t ) );
278 //cent->nextState = *es;
279
280 // if this frame is a teleport, or the entity wasn't in the
281 // previous frame, don't interpolate
282 if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) {
283 cent->interpolate = qfalse;
284 } else {
285 cent->interpolate = qtrue;
286 }
287 }
288
289 // if the next frame is a teleport for the playerstate, we
290 // can't interpolate during demos
291 if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
292 cg.nextFrameTeleport = qtrue;
293 } else {
294 cg.nextFrameTeleport = qfalse;
295 }
296
297 // if changing follow mode, don't interpolate
298 if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) {
299 cg.nextFrameTeleport = qtrue;
300 }
301
302 // if changing server restarts, don't interpolate
303 if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) {
304 cg.nextFrameTeleport = qtrue;
305 }
306
307 // sort out solid entities
308 CG_BuildSolidList();
309 }
310
311
312 /*
313 ========================
314 CG_ReadNextSnapshot
315
316 This is the only place new snapshots are requested
317 This may increment cgs.processedSnapshotNum multiple
318 times if the client system fails to return a
319 valid snapshot.
320 ========================
321 */
CG_ReadNextSnapshot(void)322 static snapshot_t *CG_ReadNextSnapshot( void ) {
323 qboolean r;
324 snapshot_t *dest;
325
326 if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) {
327 CG_Printf( "[skipnotify]WARNING: CG_ReadNextSnapshot: way out of range, %i > %i\n",
328 cg.latestSnapshotNum, cgs.processedSnapshotNum );
329 }
330
331 while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) {
332 // decide which of the two slots to load it into
333 if ( cg.snap == &cg.activeSnapshots[0] ) {
334 dest = &cg.activeSnapshots[1];
335 } else {
336 dest = &cg.activeSnapshots[0];
337 }
338
339 // try to read the snapshot from the client system
340 cgs.processedSnapshotNum++;
341 r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
342
343 // FIXME: why would trap_GetSnapshot return a snapshot with the same server time
344 if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) {
345 //continue;
346 }
347
348 // if it succeeded, return
349 if ( r ) {
350 CG_AddLagometerSnapshotInfo( dest );
351 // JPW NERVE pulled for MP, maybe this was the code that was causing crashes. Shouldn't need it for savegame stuff
352 // Ridah, savegame: we should use this as our new base snapshot
353 // server has been restarted
354 if ( cg.snap && ( dest->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) {
355 // int i;
356 // centity_t backupCent;
357 // CG_SetInitialSnapshot( dest );
358 // cg.nextFrameTeleport = qtrue;
359 cg.damageTime = 0;
360 cg.duckTime = -1;
361 cg.landTime = -1;
362 cg.stepTime = -1;
363 // go through an reset the cent's
364 /* // JPW NERVE -- return NULL mighta been bad, q3ta doesn't include this stuff
365 for ( i = 0; i < MAX_GENTITIES; i++ ) {
366 backupCent = cg_entities[i];
367 memset( &cg_entities[i], 0, sizeof( centity_t ) );
368 cg_entities[i].currentState = backupCent.currentState;
369 cg_entities[i].nextState = backupCent.nextState;
370 cg_entities[i].currentValid = backupCent.currentValid;
371 cg_entities[i].interpolate = backupCent.interpolate;
372 }
373 // reset the predicted cent
374 backupCent = cg.predictedPlayerEntity; // NERVE - SMF
375 memset( &cg.predictedPlayerEntity, 0, sizeof( centity_t ) );
376 cg.predictedPlayerEntity.currentState = backupCent.currentState;
377 cg.predictedPlayerEntity.nextState = backupCent.nextState;
378 cg.predictedPlayerEntity.currentValid = backupCent.currentValid;
379 cg.predictedPlayerEntity.interpolate = backupCent.interpolate;
380 return NULL;
381 // jpw */
382 }
383
384 return dest;
385 }
386
387 // a GetSnapshot will return failure if the snapshot
388 // never arrived, or is so old that its entities
389 // have been shoved off the end of the circular
390 // buffer in the client system.
391
392 // record as a dropped packet
393 CG_AddLagometerSnapshotInfo( NULL );
394
395 // If there are additional snapshots, continue trying to
396 // read them.
397 }
398
399 // nothing left to read
400 return NULL;
401 }
402
403
404 /*
405 ============
406 CG_ProcessSnapshots
407
408 We are trying to set up a renderable view, so determine
409 what the simulated time is, and try to get snapshots
410 both before and after that time if available.
411
412 If we don't have a valid cg.snap after exiting this function,
413 then a 3D game view cannot be rendered. This should only happen
414 right after the initial connection. After cg.snap has been valid
415 once, it will never turn invalid.
416
417 Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
418 hasn't arrived yet (it becomes an extrapolating situation instead
419 of an interpolating one)
420
421 ============
422 */
CG_ProcessSnapshots(void)423 void CG_ProcessSnapshots( void ) {
424 snapshot_t *snap;
425 int n;
426
427 // see what the latest snapshot the client system has is
428 trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
429 if ( n != cg.latestSnapshotNum ) {
430 if ( n < cg.latestSnapshotNum ) {
431 // this should never happen
432 CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
433 }
434 cg.latestSnapshotNum = n;
435 }
436
437 // If we have yet to receive a snapshot, check for it.
438 // Once we have gotten the first snapshot, cg.snap will
439 // always have valid data for the rest of the game
440 while ( !cg.snap ) {
441 snap = CG_ReadNextSnapshot();
442 if ( !snap ) {
443 // we can't continue until we get a snapshot
444 return;
445 }
446
447 // set our weapon selection to what
448 // the playerstate is currently using
449 if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
450 CG_SetInitialSnapshot( snap );
451 }
452 }
453
454 // loop until we either have a valid nextSnap with a serverTime
455 // greater than cg.time to interpolate towards, or we run
456 // out of available snapshots
457 do {
458 // if we don't have a nextframe, try and read a new one in
459 if ( !cg.nextSnap ) {
460 snap = CG_ReadNextSnapshot();
461
462 // if we still don't have a nextframe, we will just have to
463 // extrapolate
464 if ( !snap ) {
465 break;
466 }
467
468 CG_SetNextSnap( snap );
469
470 // if time went backwards, we have a level restart
471 if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
472 CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
473 }
474 }
475
476 // if our time is < nextFrame's, we have a nice interpolating state
477 if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) {
478 break;
479 }
480
481 // we have passed the transition from nextFrame to frame
482 CG_TransitionSnapshot();
483 } while ( 1 );
484
485 // assert our valid conditions upon exiting
486 if ( cg.snap == NULL ) {
487 CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
488 }
489 if ( cg.time < cg.snap->serverTime ) {
490 // this can happen right after a vid_restart
491 cg.time = cg.snap->serverTime;
492 }
493 if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) {
494 CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
495 }
496
497 }
498