summaryrefslogtreecommitdiff
path: root/src/game/g_active.c
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2017-08-14 13:07:27 +0200
committerPaweł Redman <pawel.redman@gmail.com>2017-08-14 13:31:00 +0200
commita3ff3075710c7657dad4579c90fb451736421140 (patch)
tree85e59eaf3f240b4c7653ad0cfeec4c9dedd623af /src/game/g_active.c
parent8bc5b6fdd9424823a0c46b961f4a4d12b2a8692c (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/g_active.c')
-rw-r--r--src/game/g_active.c113
1 files changed, 104 insertions, 9 deletions
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 );
}