Replace non-ASCII characters in comments with ASCII equivalents
Em dashes (---) replaced with --, arrows replaced with ->. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6e00a47416
commit
643c1d0294
7 changed files with 6643 additions and 6643 deletions
|
|
@ -1,459 +1,459 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_snapshot.c -- things that happen on snapshot transition,
|
||||
// not necessarily every single rendered frame
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_ResetEntity
|
||||
==================
|
||||
*/
|
||||
static void CG_ResetEntity( centity_t *cent ) {
|
||||
// if the previous snapshot this entity was updated in is at least
|
||||
// an event window back in time then we can reset the previous event
|
||||
if ( cent->snapShotTime < cg.time - EVENT_VALID_MSEC ) {
|
||||
cent->previousEvent = 0;
|
||||
}
|
||||
|
||||
cent->trailTime = cg.snap->serverTime;
|
||||
|
||||
VectorCopy (cent->currentState.origin, cent->lerpOrigin);
|
||||
VectorCopy (cent->currentState.angles, cent->lerpAngles);
|
||||
if ( cent->currentState.eType == ET_PLAYER ) {
|
||||
CG_ResetPlayerEntity( cent );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_TransitionEntity
|
||||
|
||||
cent->nextState is moved to cent->currentState and events are fired
|
||||
===============
|
||||
*/
|
||||
static void CG_TransitionEntity( centity_t *cent ) {
|
||||
cent->currentState = cent->nextState;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
// reset if the entity wasn't in the last frame or was teleported
|
||||
if ( !cent->interpolate ) {
|
||||
CG_ResetEntity( cent );
|
||||
}
|
||||
|
||||
// clear the next state. if will be set by the next CG_SetNextSnap
|
||||
cent->interpolate = qfalse;
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_SetInitialSnapshot
|
||||
|
||||
This will only happen on the very first snapshot, or
|
||||
on tourney restarts. All other times will use
|
||||
CG_TransitionSnapshot instead.
|
||||
|
||||
FIXME: Also called by map_restart?
|
||||
==================
|
||||
*/
|
||||
void CG_SetInitialSnapshot( snapshot_t *snap ) {
|
||||
int i;
|
||||
centity_t *cent;
|
||||
entityState_t *state;
|
||||
|
||||
cg.snap = snap;
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList();
|
||||
|
||||
CG_ExecuteNewServerCommands( snap->serverCommandSequence );
|
||||
|
||||
// set our local weapon selection pointer to
|
||||
// what the server has indicated the current weapon is
|
||||
CG_Respawn();
|
||||
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
state = &cg.snap->entities[ i ];
|
||||
cent = &cg_entities[ state->number ];
|
||||
|
||||
memcpy(¢->currentState, state, sizeof(entityState_t));
|
||||
//cent->currentState = *state;
|
||||
cent->interpolate = qfalse;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
CG_ResetEntity( cent );
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_TransitionSnapshot
|
||||
|
||||
The transition point from snap to nextSnap has passed
|
||||
===================
|
||||
*/
|
||||
static void CG_TransitionSnapshot( void ) {
|
||||
centity_t *cent;
|
||||
snapshot_t *oldFrame;
|
||||
int i;
|
||||
|
||||
if ( !cg.snap ) {
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
|
||||
}
|
||||
if ( !cg.nextSnap ) {
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
|
||||
}
|
||||
|
||||
// execute any server string commands before transitioning entities
|
||||
CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
|
||||
|
||||
// if we had a map_restart, set everthing with initial
|
||||
if ( !cg.snap ) {
|
||||
}
|
||||
|
||||
// clear the currentValid flag for all entities in the existing snapshot
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
cent->currentValid = qfalse;
|
||||
}
|
||||
|
||||
// move nextSnap to snap and do the transitions
|
||||
oldFrame = cg.snap;
|
||||
cg.snap = cg.nextSnap;
|
||||
|
||||
BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;
|
||||
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
CG_TransitionEntity( cent );
|
||||
|
||||
// remember time of snapshot this entity was last updated in
|
||||
cent->snapShotTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
cg.nextSnap = NULL;
|
||||
|
||||
// check for playerstate transition events
|
||||
if ( oldFrame ) {
|
||||
playerState_t *ops, *ps;
|
||||
|
||||
ops = &oldFrame->ps;
|
||||
ps = &cg.snap->ps;
|
||||
// teleporting checks are irrespective of prediction
|
||||
if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
|
||||
cg.thisFrameTeleport = qtrue; // will be cleared by prediction code
|
||||
}
|
||||
|
||||
// if we are not doing client side movement prediction for any
|
||||
// reason, then the client events and view changes will be issued now
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)
|
||||
|| cg_nopredict.integer || cg_synchronousClients.integer ) {
|
||||
CG_TransitionPlayerState( ps, ops );
|
||||
}
|
||||
|
||||
// server demo: detect follow→free camera transition
|
||||
if ( cg.svDemoPlayback ) {
|
||||
qboolean wasFollowing = ( ops->pm_flags & PMF_FOLLOW ) != 0;
|
||||
qboolean isFollowing = ( ps->pm_flags & PMF_FOLLOW ) != 0;
|
||||
if ( wasFollowing && !isFollowing ) {
|
||||
// exiting follow mode — init camera from last known position
|
||||
cg.svDemoFreeCamera = qtrue;
|
||||
cg.svDemoCameraTime = trap_Milliseconds();
|
||||
VectorCopy( ops->origin, cg.svDemoCameraPs.origin );
|
||||
VectorCopy( ops->viewangles, cg.svDemoCameraPs.viewangles );
|
||||
cg.svDemoCameraPs.pm_type = PM_SPECTATOR;
|
||||
cg.svDemoCameraPs.speed = 480;
|
||||
cg.svDemoCameraPs.clientNum = cg.clientNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_SetNextSnap
|
||||
|
||||
A new snapshot has just been read in from the client system.
|
||||
===================
|
||||
*/
|
||||
static void CG_SetNextSnap( snapshot_t *snap ) {
|
||||
int num;
|
||||
entityState_t *es;
|
||||
centity_t *cent;
|
||||
|
||||
cg.nextSnap = snap;
|
||||
|
||||
// SNAPFLAG_RESET_ENTITIES: invalidate all entities so they are
|
||||
// treated as new (no interpolation from old positions).
|
||||
// Must happen before the entity loop below.
|
||||
if ( snap->snapFlags & SNAPFLAG_RESET_ENTITIES ) {
|
||||
for ( num = 0 ; num < MAX_GENTITIES ; num++ ) {
|
||||
cg_entities[ num ].currentValid = qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
|
||||
|
||||
// check for extrapolation errors
|
||||
for ( num = 0 ; num < snap->numEntities ; num++ ) {
|
||||
es = &snap->entities[num];
|
||||
cent = &cg_entities[ es->number ];
|
||||
|
||||
memcpy(¢->nextState, es, sizeof(entityState_t));
|
||||
//cent->nextState = *es;
|
||||
|
||||
// if this frame is a teleport, or the entity wasn't in the
|
||||
// previous frame, don't interpolate
|
||||
if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) {
|
||||
cent->interpolate = qfalse;
|
||||
} else {
|
||||
cent->interpolate = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
// if the next frame is a teleport for the playerstate, we
|
||||
// can't interpolate during demos
|
||||
if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
} else {
|
||||
cg.nextFrameTeleport = qfalse;
|
||||
}
|
||||
|
||||
// if changing follow mode, don't interpolate
|
||||
if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// if changing server restarts, don't interpolate
|
||||
if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// entity reset also prevents playerstate interpolation
|
||||
if ( cg.nextSnap->snapFlags & SNAPFLAG_RESET_ENTITIES ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
CG_ReadNextSnapshot
|
||||
|
||||
This is the only place new snapshots are requested
|
||||
This may increment cgs.processedSnapshotNum multiple
|
||||
times if the client system fails to return a
|
||||
valid snapshot.
|
||||
========================
|
||||
*/
|
||||
static snapshot_t *CG_ReadNextSnapshot( void ) {
|
||||
qboolean r;
|
||||
snapshot_t *dest;
|
||||
|
||||
if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) {
|
||||
CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
|
||||
cg.latestSnapshotNum, cgs.processedSnapshotNum );
|
||||
}
|
||||
|
||||
while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) {
|
||||
// decide which of the two slots to load it into
|
||||
if ( cg.snap == &cg.activeSnapshots[0] ) {
|
||||
dest = &cg.activeSnapshots[1];
|
||||
} else {
|
||||
dest = &cg.activeSnapshots[0];
|
||||
}
|
||||
|
||||
// try to read the snapshot from the client system
|
||||
cgs.processedSnapshotNum++;
|
||||
r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
|
||||
|
||||
// FIXME: why would trap_GetSnapshot return a snapshot with the same server time
|
||||
if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) {
|
||||
//continue;
|
||||
}
|
||||
|
||||
// if it succeeded, return
|
||||
if ( r ) {
|
||||
CG_AddLagometerSnapshotInfo( dest );
|
||||
return dest;
|
||||
}
|
||||
|
||||
// a GetSnapshot will return failure if the snapshot
|
||||
// never arrived, or is so old that its entities
|
||||
// have been shoved off the end of the circular
|
||||
// buffer in the client system.
|
||||
|
||||
// record as a dropped packet
|
||||
CG_AddLagometerSnapshotInfo( NULL );
|
||||
|
||||
// If there are additional snapshots, continue trying to
|
||||
// read them.
|
||||
}
|
||||
|
||||
// nothing left to read
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
CG_ProcessSnapshots
|
||||
|
||||
We are trying to set up a renderable view, so determine
|
||||
what the simulated time is, and try to get snapshots
|
||||
both before and after that time if available.
|
||||
|
||||
If we don't have a valid cg.snap after exiting this function,
|
||||
then a 3D game view cannot be rendered. This should only happen
|
||||
right after the initial connection. After cg.snap has been valid
|
||||
once, it will never turn invalid.
|
||||
|
||||
Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
|
||||
hasn't arrived yet (it becomes an extrapolating situation instead
|
||||
of an interpolating one)
|
||||
|
||||
============
|
||||
*/
|
||||
void CG_ProcessSnapshots( void ) {
|
||||
snapshot_t *snap;
|
||||
int n;
|
||||
|
||||
// see what the latest snapshot the client system has is
|
||||
trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
|
||||
if ( n != cg.latestSnapshotNum ) {
|
||||
if ( n < cg.latestSnapshotNum ) {
|
||||
// this should never happen
|
||||
CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
|
||||
}
|
||||
cg.latestSnapshotNum = n;
|
||||
}
|
||||
|
||||
// If we have yet to receive a snapshot, check for it.
|
||||
// Once we have gotten the first snapshot, cg.snap will
|
||||
// always have valid data for the rest of the game
|
||||
while ( !cg.snap ) {
|
||||
snap = CG_ReadNextSnapshot();
|
||||
if ( !snap ) {
|
||||
// we can't continue until we get a snapshot
|
||||
return;
|
||||
}
|
||||
|
||||
// set our weapon selection to what
|
||||
// the playerstate is currently using
|
||||
if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
|
||||
CG_SetInitialSnapshot( snap );
|
||||
}
|
||||
}
|
||||
|
||||
// loop until we either have a valid nextSnap with a serverTime
|
||||
// greater than cg.time to interpolate towards, or we run
|
||||
// out of available snapshots
|
||||
do {
|
||||
// if we don't have a nextframe, try and read a new one in
|
||||
if ( !cg.nextSnap ) {
|
||||
snap = CG_ReadNextSnapshot();
|
||||
|
||||
// if we still don't have a nextframe, we will just have to
|
||||
// extrapolate
|
||||
if ( !snap ) {
|
||||
break;
|
||||
}
|
||||
|
||||
CG_SetNextSnap( snap );
|
||||
|
||||
|
||||
// if time went backwards, we have a level restart or demo seek
|
||||
if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
|
||||
if ( cg.svDemoPlayback ) {
|
||||
// demo seek — discard old snap, use nextSnap as current,
|
||||
// and wait for another snapshot before rendering
|
||||
cg.snap = cg.nextSnap;
|
||||
cg.nextSnap = NULL;
|
||||
cg.time = cg.snap->serverTime;
|
||||
// reset all entity state and time-dependent fields
|
||||
{
|
||||
int e;
|
||||
for ( e = 0; e < MAX_GENTITIES; e++ ) {
|
||||
cg_entities[e].currentValid = qfalse;
|
||||
cg_entities[e].interpolate = qfalse;
|
||||
cg_entities[e].muzzleFlashTime = 0;
|
||||
cg_entities[e].trailTime = 0;
|
||||
cg_entities[e].dustTrailTime = 0;
|
||||
cg_entities[e].miscTime = 0;
|
||||
cg_entities[e].snapShotTime = 0;
|
||||
cg_entities[e].previousEvent = 0;
|
||||
cg_entities[e].teleportFlag = 0;
|
||||
}
|
||||
}
|
||||
// clear local entities (particles, gibs, etc.)
|
||||
// they reference old times and would render incorrectly
|
||||
CG_InitLocalEntities();
|
||||
break; // exit loop, wait for next snapshot
|
||||
}
|
||||
CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
|
||||
}
|
||||
}
|
||||
|
||||
// if our time is < nextFrame's, we have a nice interpolating state
|
||||
if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// we have passed the transition from nextFrame to frame
|
||||
CG_TransitionSnapshot();
|
||||
} while ( 1 );
|
||||
|
||||
// assert our valid conditions upon exiting
|
||||
if ( cg.snap == NULL ) {
|
||||
CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
|
||||
}
|
||||
if ( cg.time < cg.snap->serverTime ) {
|
||||
// this can happen right after a vid_restart
|
||||
cg.time = cg.snap->serverTime;
|
||||
}
|
||||
if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) {
|
||||
CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
// cg_snapshot.c -- things that happen on snapshot transition,
|
||||
// not necessarily every single rendered frame
|
||||
|
||||
#include "cg_local.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_ResetEntity
|
||||
==================
|
||||
*/
|
||||
static void CG_ResetEntity( centity_t *cent ) {
|
||||
// if the previous snapshot this entity was updated in is at least
|
||||
// an event window back in time then we can reset the previous event
|
||||
if ( cent->snapShotTime < cg.time - EVENT_VALID_MSEC ) {
|
||||
cent->previousEvent = 0;
|
||||
}
|
||||
|
||||
cent->trailTime = cg.snap->serverTime;
|
||||
|
||||
VectorCopy (cent->currentState.origin, cent->lerpOrigin);
|
||||
VectorCopy (cent->currentState.angles, cent->lerpAngles);
|
||||
if ( cent->currentState.eType == ET_PLAYER ) {
|
||||
CG_ResetPlayerEntity( cent );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CG_TransitionEntity
|
||||
|
||||
cent->nextState is moved to cent->currentState and events are fired
|
||||
===============
|
||||
*/
|
||||
static void CG_TransitionEntity( centity_t *cent ) {
|
||||
cent->currentState = cent->nextState;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
// reset if the entity wasn't in the last frame or was teleported
|
||||
if ( !cent->interpolate ) {
|
||||
CG_ResetEntity( cent );
|
||||
}
|
||||
|
||||
// clear the next state. if will be set by the next CG_SetNextSnap
|
||||
cent->interpolate = qfalse;
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CG_SetInitialSnapshot
|
||||
|
||||
This will only happen on the very first snapshot, or
|
||||
on tourney restarts. All other times will use
|
||||
CG_TransitionSnapshot instead.
|
||||
|
||||
FIXME: Also called by map_restart?
|
||||
==================
|
||||
*/
|
||||
void CG_SetInitialSnapshot( snapshot_t *snap ) {
|
||||
int i;
|
||||
centity_t *cent;
|
||||
entityState_t *state;
|
||||
|
||||
cg.snap = snap;
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList();
|
||||
|
||||
CG_ExecuteNewServerCommands( snap->serverCommandSequence );
|
||||
|
||||
// set our local weapon selection pointer to
|
||||
// what the server has indicated the current weapon is
|
||||
CG_Respawn();
|
||||
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
state = &cg.snap->entities[ i ];
|
||||
cent = &cg_entities[ state->number ];
|
||||
|
||||
memcpy(¢->currentState, state, sizeof(entityState_t));
|
||||
//cent->currentState = *state;
|
||||
cent->interpolate = qfalse;
|
||||
cent->currentValid = qtrue;
|
||||
|
||||
CG_ResetEntity( cent );
|
||||
|
||||
// check for events
|
||||
CG_CheckEvents( cent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_TransitionSnapshot
|
||||
|
||||
The transition point from snap to nextSnap has passed
|
||||
===================
|
||||
*/
|
||||
static void CG_TransitionSnapshot( void ) {
|
||||
centity_t *cent;
|
||||
snapshot_t *oldFrame;
|
||||
int i;
|
||||
|
||||
if ( !cg.snap ) {
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
|
||||
}
|
||||
if ( !cg.nextSnap ) {
|
||||
CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
|
||||
}
|
||||
|
||||
// execute any server string commands before transitioning entities
|
||||
CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
|
||||
|
||||
// if we had a map_restart, set everthing with initial
|
||||
if ( !cg.snap ) {
|
||||
}
|
||||
|
||||
// clear the currentValid flag for all entities in the existing snapshot
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
cent->currentValid = qfalse;
|
||||
}
|
||||
|
||||
// move nextSnap to snap and do the transitions
|
||||
oldFrame = cg.snap;
|
||||
cg.snap = cg.nextSnap;
|
||||
|
||||
BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;
|
||||
|
||||
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
|
||||
cent = &cg_entities[ cg.snap->entities[ i ].number ];
|
||||
CG_TransitionEntity( cent );
|
||||
|
||||
// remember time of snapshot this entity was last updated in
|
||||
cent->snapShotTime = cg.snap->serverTime;
|
||||
}
|
||||
|
||||
cg.nextSnap = NULL;
|
||||
|
||||
// check for playerstate transition events
|
||||
if ( oldFrame ) {
|
||||
playerState_t *ops, *ps;
|
||||
|
||||
ops = &oldFrame->ps;
|
||||
ps = &cg.snap->ps;
|
||||
// teleporting checks are irrespective of prediction
|
||||
if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
|
||||
cg.thisFrameTeleport = qtrue; // will be cleared by prediction code
|
||||
}
|
||||
|
||||
// if we are not doing client side movement prediction for any
|
||||
// reason, then the client events and view changes will be issued now
|
||||
if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)
|
||||
|| cg_nopredict.integer || cg_synchronousClients.integer ) {
|
||||
CG_TransitionPlayerState( ps, ops );
|
||||
}
|
||||
|
||||
// server demo: detect follow->free camera transition
|
||||
if ( cg.svDemoPlayback ) {
|
||||
qboolean wasFollowing = ( ops->pm_flags & PMF_FOLLOW ) != 0;
|
||||
qboolean isFollowing = ( ps->pm_flags & PMF_FOLLOW ) != 0;
|
||||
if ( wasFollowing && !isFollowing ) {
|
||||
// exiting follow mode -- init camera from last known position
|
||||
cg.svDemoFreeCamera = qtrue;
|
||||
cg.svDemoCameraTime = trap_Milliseconds();
|
||||
VectorCopy( ops->origin, cg.svDemoCameraPs.origin );
|
||||
VectorCopy( ops->viewangles, cg.svDemoCameraPs.viewangles );
|
||||
cg.svDemoCameraPs.pm_type = PM_SPECTATOR;
|
||||
cg.svDemoCameraPs.speed = 480;
|
||||
cg.svDemoCameraPs.clientNum = cg.clientNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
CG_SetNextSnap
|
||||
|
||||
A new snapshot has just been read in from the client system.
|
||||
===================
|
||||
*/
|
||||
static void CG_SetNextSnap( snapshot_t *snap ) {
|
||||
int num;
|
||||
entityState_t *es;
|
||||
centity_t *cent;
|
||||
|
||||
cg.nextSnap = snap;
|
||||
|
||||
// SNAPFLAG_RESET_ENTITIES: invalidate all entities so they are
|
||||
// treated as new (no interpolation from old positions).
|
||||
// Must happen before the entity loop below.
|
||||
if ( snap->snapFlags & SNAPFLAG_RESET_ENTITIES ) {
|
||||
for ( num = 0 ; num < MAX_GENTITIES ; num++ ) {
|
||||
cg_entities[ num ].currentValid = qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
|
||||
cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
|
||||
|
||||
// check for extrapolation errors
|
||||
for ( num = 0 ; num < snap->numEntities ; num++ ) {
|
||||
es = &snap->entities[num];
|
||||
cent = &cg_entities[ es->number ];
|
||||
|
||||
memcpy(¢->nextState, es, sizeof(entityState_t));
|
||||
//cent->nextState = *es;
|
||||
|
||||
// if this frame is a teleport, or the entity wasn't in the
|
||||
// previous frame, don't interpolate
|
||||
if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) {
|
||||
cent->interpolate = qfalse;
|
||||
} else {
|
||||
cent->interpolate = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
// if the next frame is a teleport for the playerstate, we
|
||||
// can't interpolate during demos
|
||||
if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
} else {
|
||||
cg.nextFrameTeleport = qfalse;
|
||||
}
|
||||
|
||||
// if changing follow mode, don't interpolate
|
||||
if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// if changing server restarts, don't interpolate
|
||||
if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// entity reset also prevents playerstate interpolation
|
||||
if ( cg.nextSnap->snapFlags & SNAPFLAG_RESET_ENTITIES ) {
|
||||
cg.nextFrameTeleport = qtrue;
|
||||
}
|
||||
|
||||
// sort out solid entities
|
||||
CG_BuildSolidList();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
CG_ReadNextSnapshot
|
||||
|
||||
This is the only place new snapshots are requested
|
||||
This may increment cgs.processedSnapshotNum multiple
|
||||
times if the client system fails to return a
|
||||
valid snapshot.
|
||||
========================
|
||||
*/
|
||||
static snapshot_t *CG_ReadNextSnapshot( void ) {
|
||||
qboolean r;
|
||||
snapshot_t *dest;
|
||||
|
||||
if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) {
|
||||
CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
|
||||
cg.latestSnapshotNum, cgs.processedSnapshotNum );
|
||||
}
|
||||
|
||||
while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) {
|
||||
// decide which of the two slots to load it into
|
||||
if ( cg.snap == &cg.activeSnapshots[0] ) {
|
||||
dest = &cg.activeSnapshots[1];
|
||||
} else {
|
||||
dest = &cg.activeSnapshots[0];
|
||||
}
|
||||
|
||||
// try to read the snapshot from the client system
|
||||
cgs.processedSnapshotNum++;
|
||||
r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
|
||||
|
||||
// FIXME: why would trap_GetSnapshot return a snapshot with the same server time
|
||||
if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) {
|
||||
//continue;
|
||||
}
|
||||
|
||||
// if it succeeded, return
|
||||
if ( r ) {
|
||||
CG_AddLagometerSnapshotInfo( dest );
|
||||
return dest;
|
||||
}
|
||||
|
||||
// a GetSnapshot will return failure if the snapshot
|
||||
// never arrived, or is so old that its entities
|
||||
// have been shoved off the end of the circular
|
||||
// buffer in the client system.
|
||||
|
||||
// record as a dropped packet
|
||||
CG_AddLagometerSnapshotInfo( NULL );
|
||||
|
||||
// If there are additional snapshots, continue trying to
|
||||
// read them.
|
||||
}
|
||||
|
||||
// nothing left to read
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
CG_ProcessSnapshots
|
||||
|
||||
We are trying to set up a renderable view, so determine
|
||||
what the simulated time is, and try to get snapshots
|
||||
both before and after that time if available.
|
||||
|
||||
If we don't have a valid cg.snap after exiting this function,
|
||||
then a 3D game view cannot be rendered. This should only happen
|
||||
right after the initial connection. After cg.snap has been valid
|
||||
once, it will never turn invalid.
|
||||
|
||||
Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
|
||||
hasn't arrived yet (it becomes an extrapolating situation instead
|
||||
of an interpolating one)
|
||||
|
||||
============
|
||||
*/
|
||||
void CG_ProcessSnapshots( void ) {
|
||||
snapshot_t *snap;
|
||||
int n;
|
||||
|
||||
// see what the latest snapshot the client system has is
|
||||
trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
|
||||
if ( n != cg.latestSnapshotNum ) {
|
||||
if ( n < cg.latestSnapshotNum ) {
|
||||
// this should never happen
|
||||
CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
|
||||
}
|
||||
cg.latestSnapshotNum = n;
|
||||
}
|
||||
|
||||
// If we have yet to receive a snapshot, check for it.
|
||||
// Once we have gotten the first snapshot, cg.snap will
|
||||
// always have valid data for the rest of the game
|
||||
while ( !cg.snap ) {
|
||||
snap = CG_ReadNextSnapshot();
|
||||
if ( !snap ) {
|
||||
// we can't continue until we get a snapshot
|
||||
return;
|
||||
}
|
||||
|
||||
// set our weapon selection to what
|
||||
// the playerstate is currently using
|
||||
if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
|
||||
CG_SetInitialSnapshot( snap );
|
||||
}
|
||||
}
|
||||
|
||||
// loop until we either have a valid nextSnap with a serverTime
|
||||
// greater than cg.time to interpolate towards, or we run
|
||||
// out of available snapshots
|
||||
do {
|
||||
// if we don't have a nextframe, try and read a new one in
|
||||
if ( !cg.nextSnap ) {
|
||||
snap = CG_ReadNextSnapshot();
|
||||
|
||||
// if we still don't have a nextframe, we will just have to
|
||||
// extrapolate
|
||||
if ( !snap ) {
|
||||
break;
|
||||
}
|
||||
|
||||
CG_SetNextSnap( snap );
|
||||
|
||||
|
||||
// if time went backwards, we have a level restart or demo seek
|
||||
if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
|
||||
if ( cg.svDemoPlayback ) {
|
||||
// demo seek -- discard old snap, use nextSnap as current,
|
||||
// and wait for another snapshot before rendering
|
||||
cg.snap = cg.nextSnap;
|
||||
cg.nextSnap = NULL;
|
||||
cg.time = cg.snap->serverTime;
|
||||
// reset all entity state and time-dependent fields
|
||||
{
|
||||
int e;
|
||||
for ( e = 0; e < MAX_GENTITIES; e++ ) {
|
||||
cg_entities[e].currentValid = qfalse;
|
||||
cg_entities[e].interpolate = qfalse;
|
||||
cg_entities[e].muzzleFlashTime = 0;
|
||||
cg_entities[e].trailTime = 0;
|
||||
cg_entities[e].dustTrailTime = 0;
|
||||
cg_entities[e].miscTime = 0;
|
||||
cg_entities[e].snapShotTime = 0;
|
||||
cg_entities[e].previousEvent = 0;
|
||||
cg_entities[e].teleportFlag = 0;
|
||||
}
|
||||
}
|
||||
// clear local entities (particles, gibs, etc.)
|
||||
// they reference old times and would render incorrectly
|
||||
CG_InitLocalEntities();
|
||||
break; // exit loop, wait for next snapshot
|
||||
}
|
||||
CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
|
||||
}
|
||||
}
|
||||
|
||||
// if our time is < nextFrame's, we have a nice interpolating state
|
||||
if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// we have passed the transition from nextFrame to frame
|
||||
CG_TransitionSnapshot();
|
||||
} while ( 1 );
|
||||
|
||||
// assert our valid conditions upon exiting
|
||||
if ( cg.snap == NULL ) {
|
||||
CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
|
||||
}
|
||||
if ( cg.time < cg.snap->serverTime ) {
|
||||
// this can happen right after a vid_restart
|
||||
cg.time = cg.snap->serverTime;
|
||||
}
|
||||
if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) {
|
||||
CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
2394
code/game/g_active.c
2394
code/game/g_active.c
File diff suppressed because it is too large
Load diff
3852
code/game/g_main.c
3852
code/game/g_main.c
File diff suppressed because it is too large
Load diff
|
|
@ -1,199 +1,199 @@
|
|||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
|
||||
SESSION DATA
|
||||
|
||||
Session data is the only data that stays persistant across level loads
|
||||
and tournament restarts.
|
||||
=======================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
G_WriteClientSessionData
|
||||
|
||||
Called on game shutdown
|
||||
================
|
||||
*/
|
||||
void G_WriteClientSessionData( gclient_t *client ) {
|
||||
const char *s;
|
||||
const char *var;
|
||||
|
||||
s = va("%i %i %i %i %i %i %i",
|
||||
client->sess.sessionTeam,
|
||||
client->sess.spectatorTime,
|
||||
client->sess.spectatorState,
|
||||
client->sess.spectatorClient,
|
||||
client->sess.wins,
|
||||
client->sess.losses,
|
||||
client->sess.teamLeader
|
||||
);
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
|
||||
trap_Cvar_Set( var, s );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
G_ReadSessionData
|
||||
|
||||
Called on a reconnect
|
||||
================
|
||||
*/
|
||||
void G_ReadSessionData( gclient_t *client ) {
|
||||
char s[MAX_STRING_CHARS];
|
||||
const char *var;
|
||||
|
||||
// bk001205 - format
|
||||
int teamLeader;
|
||||
int spectatorState;
|
||||
int sessionTeam;
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
|
||||
|
||||
sscanf( s, "%i %i %i %i %i %i %i",
|
||||
&sessionTeam, // bk010221 - format
|
||||
&client->sess.spectatorTime,
|
||||
&spectatorState, // bk010221 - format
|
||||
&client->sess.spectatorClient,
|
||||
&client->sess.wins,
|
||||
&client->sess.losses,
|
||||
&teamLeader // bk010221 - format
|
||||
);
|
||||
|
||||
// bk001205 - format issues
|
||||
client->sess.sessionTeam = (team_t)sessionTeam;
|
||||
client->sess.spectatorState = (spectatorState_t)spectatorState;
|
||||
client->sess.teamLeader = (qboolean)teamLeader;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_InitSessionData
|
||||
|
||||
Called on a first-time connect
|
||||
================
|
||||
*/
|
||||
void G_InitSessionData( gclient_t *client, char *userinfo ) {
|
||||
clientSession_t *sess;
|
||||
const char *value;
|
||||
|
||||
sess = &client->sess;
|
||||
|
||||
// initial team determination
|
||||
if ( g_gametype.integer >= GT_TEAM ) {
|
||||
if ( g_teamAutoJoin.integer ) {
|
||||
sess->sessionTeam = PickTeam( -1 );
|
||||
BroadcastTeamChange( client, -1 );
|
||||
} else {
|
||||
// always spawn as spectator in team games
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
}
|
||||
} else {
|
||||
value = Info_ValueForKey( userinfo, "team" );
|
||||
if ( value[0] == 's' ) {
|
||||
// a willing spectator, not a waiting-in-line
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
switch ( g_gametype.integer ) {
|
||||
default:
|
||||
case GT_FFA:
|
||||
case GT_SINGLE_PLAYER:
|
||||
if ( g_maxGameClients.integer > 0 &&
|
||||
level.numNonSpectatorClients >= g_maxGameClients.integer ) {
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
sess->sessionTeam = TEAM_FREE;
|
||||
}
|
||||
break;
|
||||
case GT_TOURNAMENT:
|
||||
// if the game is full, go into a waiting mode
|
||||
if ( level.numNonSpectatorClients >= 2 ) {
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
sess->sessionTeam = TEAM_FREE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sess->spectatorState = SPECTATOR_FREE;
|
||||
sess->spectatorTime = level.time;
|
||||
|
||||
G_WriteClientSessionData( client );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
G_InitWorldSession
|
||||
|
||||
==================
|
||||
*/
|
||||
void G_InitWorldSession( void ) {
|
||||
char s[MAX_STRING_CHARS];
|
||||
int gt;
|
||||
|
||||
trap_Cvar_VariableStringBuffer( "session", s, sizeof(s) );
|
||||
gt = atoi( s );
|
||||
|
||||
// if the gametype changed since the last session, don't use any
|
||||
// client sessions
|
||||
if ( g_gametype.integer != gt ) {
|
||||
level.newSession = qtrue;
|
||||
G_Printf( "Gametype changed, clearing session data.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
G_WriteSessionData
|
||||
|
||||
==================
|
||||
*/
|
||||
void G_WriteSessionData( void ) {
|
||||
int i;
|
||||
|
||||
// don't persist demo spectator sessions — the forced TEAM_SPECTATOR
|
||||
// would carry over to the next normal game
|
||||
if ( g_svDemoPlaying.integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Cvar_Set( "session", va("%i", g_gametype.integer) );
|
||||
|
||||
for ( i = 0 ; i < level.maxclients ; i++ ) {
|
||||
if ( level.clients[i].pers.connected == CON_CONNECTED ) {
|
||||
G_WriteClientSessionData( &level.clients[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
|
||||
This file is part of Quake III Arena source code.
|
||||
|
||||
Quake III Arena source code is free software; you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the License,
|
||||
or (at your option) any later version.
|
||||
|
||||
Quake III Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Foobar; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
//
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
|
||||
SESSION DATA
|
||||
|
||||
Session data is the only data that stays persistant across level loads
|
||||
and tournament restarts.
|
||||
=======================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
G_WriteClientSessionData
|
||||
|
||||
Called on game shutdown
|
||||
================
|
||||
*/
|
||||
void G_WriteClientSessionData( gclient_t *client ) {
|
||||
const char *s;
|
||||
const char *var;
|
||||
|
||||
s = va("%i %i %i %i %i %i %i",
|
||||
client->sess.sessionTeam,
|
||||
client->sess.spectatorTime,
|
||||
client->sess.spectatorState,
|
||||
client->sess.spectatorClient,
|
||||
client->sess.wins,
|
||||
client->sess.losses,
|
||||
client->sess.teamLeader
|
||||
);
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
|
||||
trap_Cvar_Set( var, s );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
G_ReadSessionData
|
||||
|
||||
Called on a reconnect
|
||||
================
|
||||
*/
|
||||
void G_ReadSessionData( gclient_t *client ) {
|
||||
char s[MAX_STRING_CHARS];
|
||||
const char *var;
|
||||
|
||||
// bk001205 - format
|
||||
int teamLeader;
|
||||
int spectatorState;
|
||||
int sessionTeam;
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
|
||||
|
||||
sscanf( s, "%i %i %i %i %i %i %i",
|
||||
&sessionTeam, // bk010221 - format
|
||||
&client->sess.spectatorTime,
|
||||
&spectatorState, // bk010221 - format
|
||||
&client->sess.spectatorClient,
|
||||
&client->sess.wins,
|
||||
&client->sess.losses,
|
||||
&teamLeader // bk010221 - format
|
||||
);
|
||||
|
||||
// bk001205 - format issues
|
||||
client->sess.sessionTeam = (team_t)sessionTeam;
|
||||
client->sess.spectatorState = (spectatorState_t)spectatorState;
|
||||
client->sess.teamLeader = (qboolean)teamLeader;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_InitSessionData
|
||||
|
||||
Called on a first-time connect
|
||||
================
|
||||
*/
|
||||
void G_InitSessionData( gclient_t *client, char *userinfo ) {
|
||||
clientSession_t *sess;
|
||||
const char *value;
|
||||
|
||||
sess = &client->sess;
|
||||
|
||||
// initial team determination
|
||||
if ( g_gametype.integer >= GT_TEAM ) {
|
||||
if ( g_teamAutoJoin.integer ) {
|
||||
sess->sessionTeam = PickTeam( -1 );
|
||||
BroadcastTeamChange( client, -1 );
|
||||
} else {
|
||||
// always spawn as spectator in team games
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
}
|
||||
} else {
|
||||
value = Info_ValueForKey( userinfo, "team" );
|
||||
if ( value[0] == 's' ) {
|
||||
// a willing spectator, not a waiting-in-line
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
switch ( g_gametype.integer ) {
|
||||
default:
|
||||
case GT_FFA:
|
||||
case GT_SINGLE_PLAYER:
|
||||
if ( g_maxGameClients.integer > 0 &&
|
||||
level.numNonSpectatorClients >= g_maxGameClients.integer ) {
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
sess->sessionTeam = TEAM_FREE;
|
||||
}
|
||||
break;
|
||||
case GT_TOURNAMENT:
|
||||
// if the game is full, go into a waiting mode
|
||||
if ( level.numNonSpectatorClients >= 2 ) {
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
sess->sessionTeam = TEAM_FREE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sess->spectatorState = SPECTATOR_FREE;
|
||||
sess->spectatorTime = level.time;
|
||||
|
||||
G_WriteClientSessionData( client );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
G_InitWorldSession
|
||||
|
||||
==================
|
||||
*/
|
||||
void G_InitWorldSession( void ) {
|
||||
char s[MAX_STRING_CHARS];
|
||||
int gt;
|
||||
|
||||
trap_Cvar_VariableStringBuffer( "session", s, sizeof(s) );
|
||||
gt = atoi( s );
|
||||
|
||||
// if the gametype changed since the last session, don't use any
|
||||
// client sessions
|
||||
if ( g_gametype.integer != gt ) {
|
||||
level.newSession = qtrue;
|
||||
G_Printf( "Gametype changed, clearing session data.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
G_WriteSessionData
|
||||
|
||||
==================
|
||||
*/
|
||||
void G_WriteSessionData( void ) {
|
||||
int i;
|
||||
|
||||
// don't persist demo spectator sessions -- the forced TEAM_SPECTATOR
|
||||
// would carry over to the next normal game
|
||||
if ( g_svDemoPlaying.integer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Cvar_Set( "session", va("%i", g_gametype.integer) );
|
||||
|
||||
for ( i = 0 ; i < level.maxclients ; i++ ) {
|
||||
if ( level.clients[i].pers.connected == CON_CONNECTED ) {
|
||||
G_WriteClientSessionData( &level.clients[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue