diff options
| author | Paweł Redman <pawel.redman@gmail.com> | 2017-08-14 13:07:27 +0200 | 
|---|---|---|
| committer | Paweł Redman <pawel.redman@gmail.com> | 2017-08-14 13:31:00 +0200 | 
| commit | a3ff3075710c7657dad4579c90fb451736421140 (patch) | |
| tree | 85e59eaf3f240b4c7653ad0cfeec4c9dedd623af /src/game | |
| parent | 8bc5b6fdd9424823a0c46b961f4a4d12b2a8692c (diff) | |
Implement player extrapolation.
If a player doesn't send client frames fast enough then there will be some server frames where they don't move. This is visible as 'warping' and is generally very undesirable. Extrapolation tries to fix the problem by filling in the missing data by continuing player's trajectory.
Diffstat (limited to 'src/game')
| -rw-r--r-- | src/game/bg_misc.c | 112 | ||||
| -rw-r--r-- | src/game/bg_public.h | 1 | ||||
| -rw-r--r-- | src/game/g_active.c | 113 | ||||
| -rw-r--r-- | src/game/g_local.h | 12 | ||||
| -rw-r--r-- | src/game/g_main.c | 2 | 
5 files changed, 118 insertions, 122 deletions
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c index 4b0d56f..8810c12 100644 --- a/src/game/bg_misc.c +++ b/src/game/bg_misc.c @@ -3416,118 +3416,6 @@ void BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean    s->otherEntityNum = ps->otherEntityNum;  } - -/* -======================== -BG_PlayerStateToEntityStateExtraPolate - -This is done after each set of usercmd_t on the server, -and after local prediction on the client -======================== -*/ -void BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap ) -{ -  int     i; - -  if( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPECTATOR || ps->pm_type == PM_FREEZE ) -    s->eType = ET_INVISIBLE; -  else if( ps->persistant[ PERS_TEAM ] == TEAM_SPECTATOR ) -    s->eType = ET_INVISIBLE; -  else -    s->eType = ET_PLAYER; - -  s->number = ps->clientNum; - -  s->pos.trType = TR_LINEAR_STOP; -  VectorCopy( ps->origin, s->pos.trBase ); - -  if( snap ) -    SnapVector( s->pos.trBase ); - -  // set the trDelta for flag direction and linear prediction -  VectorCopy( ps->velocity, s->pos.trDelta ); -  // set the time for linear prediction -  s->pos.trTime = time; -  // set maximum extra polation time -  s->pos.trDuration = 50; // 1000 / sv_fps (default = 20) - -  s->apos.trType = TR_INTERPOLATE; -  VectorCopy( ps->viewangles, s->apos.trBase ); -  if( snap ) -    SnapVector( s->apos.trBase ); - -  //TA: i need for other things :) -  //s->angles2[YAW] = ps->movementDir; -  s->time2 = ps->movementDir; -  s->legsAnim = ps->legsAnim; -  s->torsoAnim = ps->torsoAnim; -  s->clientNum = ps->clientNum;   // ET_PLAYER looks here instead of at number -                    // so corpses can also reference the proper config -  s->eFlags = ps->eFlags; - -  if( ps->stats[STAT_HEALTH] <= 0 ) -    s->eFlags |= EF_DEAD; -  else -    s->eFlags &= ~EF_DEAD; - -  if( ps->stats[ STAT_STATE ] & SS_BLOBLOCKED ) -    s->eFlags |= EF_BLOBLOCKED; -  else -    s->eFlags &= ~EF_BLOBLOCKED; - -  if( ps->externalEvent ) -  { -    s->event = ps->externalEvent; -    s->eventParm = ps->externalEventParm; -  } -  else if( ps->entityEventSequence < ps->eventSequence ) -  { -    int   seq; - -    if( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS ) -      ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS; - -    seq = ps->entityEventSequence & ( MAX_PS_EVENTS - 1 ); -    s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 ); -    s->eventParm = ps->eventParms[ seq ]; -    ps->entityEventSequence++; -  } - -  s->weapon = ps->weapon; -  s->groundEntityNum = ps->groundEntityNum; - -  //store items held and active items in modelindex and modelindex2 -  s->modelindex = 0; -  s->modelindex2 = 0; - -  for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) -  { -    if( BG_InventoryContainsUpgrade( i, ps->stats ) ) -    { -      s->modelindex |= 1 << i; - -      if( BG_UpgradeIsActive( i, ps->stats ) ) -        s->modelindex2 |= 1 << i; -    } -  } - -  // use misc field to store team/class info: -  s->misc = ps->stats[ STAT_PTEAM ] | ( ps->stats[ STAT_PCLASS ] << 8 ); - -  //TA: have to get the surfNormal thru somehow... -  VectorCopy( ps->grapplePoint, s->angles2 ); -  if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) -    s->eFlags |= EF_WALLCLIMBCEILING; - -  s->loopSound = ps->loopSound; -  s->generic1 = ps->generic1; - -  if( s->generic1 <= WPM_NONE || s->generic1 >= WPM_NUM_WEAPONMODES ) -    s->generic1 = WPM_PRIMARY; - -  s->otherEntityNum = ps->otherEntityNum; -} -  /*  ========================  BG_WeaponIsFull diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 790e206..98c0755 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -1258,7 +1258,6 @@ void  BG_EvaluateTrajectoryDelta( const trajectory_t *tr, int atTime, vec3_t res  void  BG_AddPredictableEventToPlayerstate( int newEvent, int eventParm, playerState_t *ps );  void  BG_PlayerStateToEntityState( playerState_t *ps, entityState_t *s, qboolean snap ); -void  BG_PlayerStateToEntityStateExtraPolate( playerState_t *ps, entityState_t *s, int time, qboolean snap );  qboolean  BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ); diff --git a/src/game/g_active.c b/src/game/g_active.c index 5bee472..c1cc9f0 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -1568,6 +1568,98 @@ static void ClientGradualFunds( gentity_t *ent )  /*  ============== +ClientSavePosition +============== +*/ +static void ClientSavePosition(gentity_t *ent, int client_time) +{ +	savedPosition_t *pos; + +	pos = ent->client->savedPositions + +	      (ent->client->savedPositionsCount + 1) % MAX_SAVED_POSITIONS; +	ent->client->savedPositionsCount++; + +	VectorCopy(ent->s.pos.trBase, pos->origin); +	pos->time = level.time; +	pos->client_time = client_time; +} + +/* +============== +ClientExtrapolate +============== +*/ +static void ClientExtrapolate(gentity_t *ent) +{ +	savedPosition_t *pos1, *pos2; +	vec3_t dxdt; +	int flags, time_delta, time_since; + +	flags = g_smoothClients.integer; +	if (!flags) +		return; + +	// Don't bother if the player is lagging too hard. +	if (ent->s.eFlags & EF_CONNECTION) +		return; + +	// Need at least 2 samples for linear extrapolation. +	if (ent->client->savedPositionsCount < 2) +		return; + +	pos1 = ent->client->savedPositions + +	       ent->client->savedPositionsCount % MAX_SAVED_POSITIONS; +	pos2 = ent->client->savedPositions + +	       (ent->client->savedPositionsCount + MAX_SAVED_POSITIONS - 1) +	        % MAX_SAVED_POSITIONS; + +	switch ((flags & (2 | 4 | 8)) >> 1) { +	default: +		Com_Printf("^3warning: g_smoothClients has a bad value\n"); +	case 0: +		time_delta = pos2->time - pos1->time; +		break; +	case 1: +		time_delta = pos2->client_time - pos1->client_time; +		break; +	case 2: +		time_delta = 50; +		break; +	case 3: +		time_delta = 25; +		break; +	case 4: +		time_delta = 16; +		break; +	} + +	VectorSubtract(pos2->origin, pos1->origin, dxdt); +	VectorScale(dxdt, pos2->time - pos1->time, dxdt); + +	if (flags & 16) +		time_since = level.time - pos2->client_time; +	else +		time_since = level.time - pos2->time; + +	VectorMA(pos1->origin, time_delta, dxdt, ent->s.pos.trBase); + +	if (g_debugExtrapolation.integer) { +		Com_Printf("Extrapolated player %i:\n", (int)(ent - g_entities)); +		Com_Printf("  pos1=(%f, %f, %f) at t=%i, t'=%i\n", +		           pos1->origin[0], pos1->origin[1], pos1->origin[2], +		           pos1->time, pos1->client_time); +		Com_Printf("  pos2=(%f, %f, %f) at t=%i, t'=%i\n", +		           pos2->origin[0], pos2->origin[1], pos2->origin[2], +		           pos2->time, pos2->client_time); +		Com_Printf("  dxdt=(%f, %f, %f), td=%i, ts=%i\n", +		           dxdt[0], dxdt[1], dxdt[2], time_delta, time_since); +		Com_Printf("  out=(%f, %f, %f)\n", ent->s.pos.trBase[0], +		           ent->s.pos.trBase[1], ent->s.pos.trBase[2]); +	} +} + +/* +==============  ClientThink  This will be called once for each client frame, which will @@ -1882,10 +1974,7 @@ void ClientThink_real( gentity_t *ent )    if ( level.paused ) client->ps.pm_type = real_pm_type; -  if( g_smoothClients.integer ) -    BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); -  else -    BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); +  BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );    SendPendingPredictableEvents( &ent->client->ps ); @@ -2036,6 +2125,8 @@ void ClientThink_real( gentity_t *ent )      ent->suicideTime = 0;    } + +  ClientSavePosition( ent, ucmd->serverTime );  }  /* @@ -2170,7 +2261,7 @@ void ClientEndFrame( gentity_t *ent )    P_DamageFeedback( ent );    // add the EF_CONNECTION flag if we haven't gotten commands recently -  if( level.time - ent->client->lastCmdTime > 1000 ) +  if( level.time - ent->client->lastCmdTime > 500 )      ent->s.eFlags |= EF_CONNECTION;    else      ent->s.eFlags &= ~EF_CONNECTION; @@ -2184,10 +2275,14 @@ void ClientEndFrame( gentity_t *ent )    G_SetClientSound( ent );    // set the latest infor -  if( g_smoothClients.integer ) -    BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); -  else -    BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); +  BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); + +  if( ent->client->extrapolate ) +    ClientExtrapolate( ent ); + +  // This flag will go down as soon as a client frame is received. +  // If a frame isn't received then it'll stay up and trigger extrapolation. +  ent->client->extrapolate = qtrue;    SendPendingPredictableEvents( &ent->client->ps );  } diff --git a/src/game/g_local.h b/src/game/g_local.h index a3c02c8..28bd059 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -450,6 +450,13 @@ typedef struct unlagged_s {    qboolean    used;  } unlagged_t; +#define MAX_SAVED_POSITIONS 3 +typedef struct { +  vec3_t      origin; +  int         time; +  int         client_time; +} savedPosition_t; +  // this structure is cleared on each ClientSpawn(),  // except for 'client->pers' and 'client->sess'  struct gclient_s @@ -550,6 +557,10 @@ struct gclient_s    int               tkcredits[ MAX_CLIENTS ];    int                 revertCookie; + +  qboolean            extrapolate; +  savedPosition_t     savedPositions[MAX_SAVED_POSITIONS]; +  int                 savedPositionsCount;  }; @@ -1391,6 +1402,7 @@ extern  vmCvar_t  g_popularMapsVotePercent;  extern  vmCvar_t  g_banIPs;  extern  vmCvar_t  g_filterBan;  extern  vmCvar_t  g_smoothClients; +extern  vmCvar_t  g_debugExtrapolation;  extern  vmCvar_t  g_outdatedClientMessage;  extern  vmCvar_t  pmove_fixed;  extern  vmCvar_t  pmove_msec; diff --git a/src/game/g_main.c b/src/game/g_main.c index 55508be..1c6d84e 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -122,6 +122,7 @@ vmCvar_t  g_popularMapsVotePercent;  vmCvar_t  g_banIPs;  vmCvar_t  g_filterBan;  vmCvar_t  g_smoothClients; +vmCvar_t  g_debugExtrapolation;  vmCvar_t  g_outdatedClientMessage;  vmCvar_t  pmove_fixed;  vmCvar_t  pmove_msec; @@ -416,6 +417,7 @@ static cvarTable_t   gameCvarTable[ ] =      "Set a name by pressing Escape and choosing Options", CVAR_ARCHIVE, 0, qfalse},    { &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse}, +  { &g_debugExtrapolation, "g_debugExtrapolation", "0", 0, 0, qfalse},     { &g_outdatedClientMessage, "g_outdatedClientMessage", "", CVAR_ARCHIVE, 0, qfalse},    { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse},    { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse},  | 
