From a3ff3075710c7657dad4579c90fb451736421140 Mon Sep 17 00:00:00 2001 From: Paweł Redman Date: Mon, 14 Aug 2017 13:07:27 +0200 Subject: 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. --- src/game/g_active.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 9 deletions(-) (limited to 'src/game/g_active.c') 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 @@ -1566,6 +1566,98 @@ static void ClientGradualFunds( gentity_t *ent ) ent->client->pers.lastFreekillTime += FREEKILL_PERIOD; } +/* +============== +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 @@ -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 ); } -- cgit