1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5
6 This file is part of Tremulous.
7
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 ===========================================================================
22 */
23
24 // cg_snapshot.c -- things that happen on snapshot transition,
25 // not necessarily every single rendered frame
26
27
28 #include "cg_local.h"
29
30 /*
31 ==================
32 CG_ResetEntity
33 ==================
34 */
CG_ResetEntity(centity_t * cent)35 static void CG_ResetEntity( centity_t *cent )
36 {
37 // if the previous snapshot this entity was updated in is at least
38 // an event window back in time then we can reset the previous event
39 if( cent->snapShotTime < cg.time - EVENT_VALID_MSEC )
40 cent->previousEvent = 0;
41
42 cent->trailTime = cg.snap->serverTime;
43
44 VectorCopy( cent->currentState.origin, cent->lerpOrigin );
45 VectorCopy( cent->currentState.angles, cent->lerpAngles );
46
47 if( cent->currentState.eType == ET_PLAYER )
48 CG_ResetPlayerEntity( cent );
49 }
50
51 /*
52 ===============
53 CG_TransitionEntity
54
55 cent->nextState is moved to cent->currentState and events are fired
56 ===============
57 */
CG_TransitionEntity(centity_t * cent)58 static void CG_TransitionEntity( centity_t *cent )
59 {
60 cent->currentState = cent->nextState;
61 cent->currentValid = qtrue;
62
63 // reset if the entity wasn't in the last frame or was teleported
64 if( !cent->interpolate )
65 CG_ResetEntity( cent );
66
67 // clear the next state. if will be set by the next CG_SetNextSnap
68 cent->interpolate = qfalse;
69
70 // check for events
71 CG_CheckEvents( cent );
72 }
73
74
75 /*
76 ==================
77 CG_SetInitialSnapshot
78
79 This will only happen on the very first snapshot, or
80 on tourney restarts. All other times will use
81 CG_TransitionSnapshot instead.
82
83 FIXME: Also called by map_restart?
84 ==================
85 */
CG_SetInitialSnapshot(snapshot_t * snap)86 void CG_SetInitialSnapshot( snapshot_t *snap )
87 {
88 int i;
89 centity_t *cent;
90 entityState_t *state;
91
92 cg.snap = snap;
93
94 BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
95
96 // sort out solid entities
97 CG_BuildSolidList( );
98
99 CG_ExecuteNewServerCommands( snap->serverCommandSequence );
100
101 // set our local weapon selection pointer to
102 // what the server has indicated the current weapon is
103 CG_Respawn( );
104
105 for( i = 0; i < cg.snap->numEntities; i++ )
106 {
107 state = &cg.snap->entities[ i ];
108 cent = &cg_entities[ state->number ];
109
110 memcpy( ¢->currentState, state, sizeof( entityState_t ) );
111 //cent->currentState = *state;
112 cent->interpolate = qfalse;
113 cent->currentValid = qtrue;
114
115 CG_ResetEntity( cent );
116
117 // check for events
118 CG_CheckEvents( cent );
119 }
120 }
121
122
123 /*
124 ===================
125 CG_TransitionSnapshot
126
127 The transition point from snap to nextSnap has passed
128 ===================
129 */
CG_TransitionSnapshot(void)130 static void CG_TransitionSnapshot( void )
131 {
132 centity_t *cent;
133 snapshot_t *oldFrame;
134 int i;
135
136 if( !cg.snap )
137 CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
138
139 if( !cg.nextSnap )
140 CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
141
142 // execute any server string commands before transitioning entities
143 CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
144
145 // if we had a map_restart, set everthing with initial
146 if( !cg.snap ) { } //TA: ?
147
148 // clear the currentValid flag for all entities in the existing snapshot
149 for( i = 0; i < cg.snap->numEntities; i++ )
150 {
151 cent = &cg_entities[ cg.snap->entities[ i ].number ];
152 cent->currentValid = qfalse;
153 }
154
155 // move nextSnap to snap and do the transitions
156 oldFrame = cg.snap;
157 cg.snap = cg.nextSnap;
158
159 BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
160 cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;
161
162 for( i = 0; i < cg.snap->numEntities; i++ )
163 {
164 cent = &cg_entities[ cg.snap->entities[ i ].number ];
165 CG_TransitionEntity( cent );
166
167 // remember time of snapshot this entity was last updated in
168 cent->snapShotTime = cg.snap->serverTime;
169 }
170
171 cg.nextSnap = NULL;
172
173 // check for playerstate transition events
174 if( oldFrame )
175 {
176 playerState_t *ops, *ps;
177
178 ops = &oldFrame->ps;
179 ps = &cg.snap->ps;
180 // teleporting checks are irrespective of prediction
181 if( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT )
182 cg.thisFrameTeleport = qtrue; // will be cleared by prediction code
183
184 // if we are not doing client side movement prediction for any
185 // reason, then the client events and view changes will be issued now
186 if( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ||
187 cg_nopredict.integer || cg_synchronousClients.integer )
188 CG_TransitionPlayerState( ps, ops );
189 }
190 }
191
192
193 /*
194 ===================
195 CG_SetNextSnap
196
197 A new snapshot has just been read in from the client system.
198 ===================
199 */
CG_SetNextSnap(snapshot_t * snap)200 static void CG_SetNextSnap( snapshot_t *snap )
201 {
202 int num;
203 entityState_t *es;
204 centity_t *cent;
205
206 cg.nextSnap = snap;
207
208 BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
209 cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
210
211 // check for extrapolation errors
212 for( num = 0 ; num < snap->numEntities ; num++ )
213 {
214 es = &snap->entities[ num ];
215 cent = &cg_entities[ es->number ];
216
217 memcpy( ¢->nextState, es, sizeof( entityState_t ) );
218 //cent->nextState = *es;
219
220 // if this frame is a teleport, or the entity wasn't in the
221 // previous frame, don't interpolate
222 if( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) )
223 cent->interpolate = qfalse;
224 else
225 cent->interpolate = qtrue;
226 }
227
228 // if the next frame is a teleport for the playerstate, we
229 // can't interpolate during demos
230 if( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) )
231 cg.nextFrameTeleport = qtrue;
232 else
233 cg.nextFrameTeleport = qfalse;
234
235 // if changing follow mode, don't interpolate
236 if( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum )
237 cg.nextFrameTeleport = qtrue;
238
239 // if changing server restarts, don't interpolate
240 if( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT )
241 cg.nextFrameTeleport = qtrue;
242
243 // sort out solid entities
244 CG_BuildSolidList( );
245 }
246
247
248 /*
249 ========================
250 CG_ReadNextSnapshot
251
252 This is the only place new snapshots are requested
253 This may increment cgs.processedSnapshotNum multiple
254 times if the client system fails to return a
255 valid snapshot.
256 ========================
257 */
CG_ReadNextSnapshot(void)258 static snapshot_t *CG_ReadNextSnapshot( void )
259 {
260 qboolean r;
261 snapshot_t *dest;
262
263 if( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 )
264 {
265 CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
266 cg.latestSnapshotNum, cgs.processedSnapshotNum );
267 }
268
269 while( cgs.processedSnapshotNum < cg.latestSnapshotNum )
270 {
271 // decide which of the two slots to load it into
272 if( cg.snap == &cg.activeSnapshots[ 0 ] )
273 dest = &cg.activeSnapshots[ 1 ];
274 else
275 dest = &cg.activeSnapshots[ 0 ];
276
277 // try to read the snapshot from the client system
278 cgs.processedSnapshotNum++;
279 r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
280
281 // FIXME: why would trap_GetSnapshot return a snapshot with the same server time
282 if( cg.snap && r && dest->serverTime == cg.snap->serverTime )
283 {
284 //continue;
285 }
286
287 // if it succeeded, return
288 if( r )
289 {
290 CG_AddLagometerSnapshotInfo( dest );
291 return dest;
292 }
293
294 // a GetSnapshot will return failure if the snapshot
295 // never arrived, or is so old that its entities
296 // have been shoved off the end of the circular
297 // buffer in the client system.
298
299 // record as a dropped packet
300 CG_AddLagometerSnapshotInfo( NULL );
301
302 // If there are additional snapshots, continue trying to
303 // read them.
304 }
305
306 // nothing left to read
307 return NULL;
308 }
309
310
311 /*
312 ============
313 CG_ProcessSnapshots
314
315 We are trying to set up a renderable view, so determine
316 what the simulated time is, and try to get snapshots
317 both before and after that time if available.
318
319 If we don't have a valid cg.snap after exiting this function,
320 then a 3D game view cannot be rendered. This should only happen
321 right after the initial connection. After cg.snap has been valid
322 once, it will never turn invalid.
323
324 Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
325 hasn't arrived yet (it becomes an extrapolating situation instead
326 of an interpolating one)
327
328 ============
329 */
CG_ProcessSnapshots(void)330 void CG_ProcessSnapshots( void )
331 {
332 snapshot_t *snap;
333 int n;
334
335 // see what the latest snapshot the client system has is
336 trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
337
338 if( n != cg.latestSnapshotNum )
339 {
340 if( n < cg.latestSnapshotNum )
341 {
342 // this should never happen
343 CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
344 }
345
346 cg.latestSnapshotNum = n;
347 }
348
349 // If we have yet to receive a snapshot, check for it.
350 // Once we have gotten the first snapshot, cg.snap will
351 // always have valid data for the rest of the game
352 while( !cg.snap )
353 {
354 snap = CG_ReadNextSnapshot( );
355
356 if( !snap )
357 {
358 // we can't continue until we get a snapshot
359 return;
360 }
361
362 // set our weapon selection to what
363 // the playerstate is currently using
364 if( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) )
365 CG_SetInitialSnapshot( snap );
366 }
367
368 // loop until we either have a valid nextSnap with a serverTime
369 // greater than cg.time to interpolate towards, or we run
370 // out of available snapshots
371 do
372 {
373 // if we don't have a nextframe, try and read a new one in
374 if( !cg.nextSnap )
375 {
376 snap = CG_ReadNextSnapshot( );
377
378 // if we still don't have a nextframe, we will just have to
379 // extrapolate
380 if( !snap )
381 break;
382
383 CG_SetNextSnap( snap );
384
385 // if time went backwards, we have a level restart
386 if( cg.nextSnap->serverTime < cg.snap->serverTime )
387 CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
388 }
389
390 // if our time is < nextFrame's, we have a nice interpolating state
391 if( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime )
392 break;
393
394 // we have passed the transition from nextFrame to frame
395 CG_TransitionSnapshot( );
396 } while( 1 );
397
398 // assert our valid conditions upon exiting
399 if( cg.snap == NULL )
400 CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
401
402 if( cg.time < cg.snap->serverTime )
403 {
404 // this can happen right after a vid_restart
405 cg.time = cg.snap->serverTime;
406 }
407
408 if( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time )
409 CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
410 }
411
412