Merge branch 'turn-predict' into 'master'

Bring back camera prediction

See merge request KartKrew/Kart!592
This commit is contained in:
Sal 2022-05-23 00:18:22 +00:00
commit d7591b0656
6 changed files with 132 additions and 19 deletions

View file

@ -50,6 +50,9 @@ typedef enum
// ticcmd turning bits
#define TICCMD_REDUCE 16
// ticcmd latency mask
#define TICCMD_LATENCYMASK 0xFF
// ticcmd flags
#define TICCMD_RECEIVED 1
#define TICCMD_TYPING 2/* chat window or console open */

View file

@ -871,6 +871,78 @@ static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvect
INT32 localaiming[MAXSPLITSCREENPLAYERS];
angle_t localangle[MAXSPLITSCREENPLAYERS];
INT32 localsteering[MAXSPLITSCREENPLAYERS];
INT32 localdelta[MAXSPLITSCREENPLAYERS];
INT32 localstoredeltas[MAXSPLITSCREENPLAYERS][TICCMD_LATENCYMASK + 1];
UINT8 localtic;
void G_ResetAnglePrediction(player_t *player)
{
UINT16 i, j;
for (i = 0; i <= r_splitscreen; i++)
{
if (&players[displayplayers[i]] == player)
{
localdelta[i] = 0;
for (j = 0; j < TICCMD_LATENCYMASK; j++)
{
localstoredeltas[i][j] = 0;
}
break;
}
}
}
// Turning was removed from G_BuildTiccmd to prevent easy client hacking.
// This brings back the camera prediction that was lost.
static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, player_t *player)
{
angle_t angleChange = 0;
angle_t destAngle = player->angleturn;
angle_t diff = 0;
localtic = cmd->latency;
if (player->pflags & PF_DRIFTEND)
{
// Otherwise, your angle slingshots off to the side violently...
G_ResetAnglePrediction(player);
}
else
{
while (realtics > 0)
{
localsteering[ssplayer - 1] = K_UpdateSteeringValue(localsteering[ssplayer - 1], cmd->turning);
angleChange = K_GetKartTurnValue(player, localsteering[ssplayer - 1]) << TICCMD_REDUCE;
// Store the angle we applied to this tic, so we can revert it later.
// If we trust the camera to do all of the work, then it can get out of sync fast.
localstoredeltas[ssplayer - 1][cmd->latency] += angleChange;
localdelta[ssplayer - 1] += angleChange;
realtics--;
}
}
// We COULD set it to destAngle directly...
// but this causes incredible jittering when the prediction turns out to be wrong. So we ease into it.
// Slight increased camera lag in all scenarios > Mostly lagless camera but with jittering
destAngle = player->angleturn + localdelta[ssplayer - 1];
diff = destAngle - localangle[ssplayer - 1];
if (diff > ANGLE_180)
{
diff = InvAngle(InvAngle(diff) / 2);
}
else
{
diff /= 2;
}
localangle[ssplayer - 1] += diff;
}
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
const UINT8 forplayer = ssplayer-1;
@ -896,8 +968,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
boolean *rd = &resetdown[forplayer];
const boolean mouseaiming = player->spectator;
(void)realtics;
if (demo.playback) return;
// Is there any reason this can't just be I_BaseTiccmd?
@ -1153,7 +1223,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
// Send leveltime when this tic was generated to the server for control lag calculations.
// Only do this when in a level. Also do this after the hook, so that it can't overwrite this.
cmd->latency = (leveltime & 0xFF);
cmd->latency = (leveltime & TICCMD_LATENCYMASK);
}
if (cmd->forwardmove > MAXPLMOVE)
@ -1171,6 +1241,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
else if (cmd->throwdir < -KART_FULLTURN)
cmd->throwdir = -KART_FULLTURN;
G_DoAnglePrediction(cmd, realtics, ssplayer, player);
// Reset away view if a command is given.
if ((cmd->forwardmove || cmd->buttons)
&& !r_splitscreen && displayplayers[0] != consoleplayer && ssplayer == 1)
@ -1936,7 +2008,7 @@ void G_Ticker(boolean run)
else
{
//@TODO add a cvar to allow setting this max
cmd->latency = min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1);
cmd->latency = min(((leveltime & TICCMD_LATENCYMASK) - cmd->latency) & TICCMD_LATENCYMASK, MAXPREDICTTICS-1);
}
}
}

View file

@ -84,6 +84,7 @@ extern consvar_t cv_resume;
// build an internal map name MAPxx from map number
const char *G_BuildMapName(INT32 map);
void G_ResetAnglePrediction(player_t *player);
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer);
// copy ticcmd_t to and fro the normal way
@ -117,6 +118,10 @@ INT32 PlayerJoyAxis(UINT8 player, axis_input_e axissel);
extern angle_t localangle[MAXSPLITSCREENPLAYERS];
extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but signed
extern INT32 localsteering[MAXSPLITSCREENPLAYERS];
extern INT32 localdelta[MAXSPLITSCREENPLAYERS];
extern INT32 localstoredeltas[MAXSPLITSCREENPLAYERS][TICCMD_LATENCYMASK + 1];
extern UINT8 localtic;
//
// GAME

View file

@ -8233,31 +8233,34 @@ static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer)
return basedrift + (FixedMul(driftadjust * FRACUNIT, countersteer) / FRACUNIT);
}
void K_UpdateSteeringValue(player_t *player, INT16 destSteering)
INT16 K_UpdateSteeringValue(INT16 inputSteering, INT16 destSteering)
{
// player->steering is the turning value, but with easing applied.
// Keeps micro-turning from old easing, but isn't controller dependent.
const INT16 amount = KART_FULLTURN/4;
INT16 diff = destSteering - player->steering;
INT16 diff = destSteering - inputSteering;
INT16 outputSteering = inputSteering;
if (abs(diff) <= amount)
{
// Reached the intended value, set instantly.
player->steering = destSteering;
outputSteering = destSteering;
}
else
{
// Go linearly towards the value we wanted.
if (diff < 0)
{
player->steering -= amount;
outputSteering -= amount;
}
else
{
player->steering += amount;
outputSteering += amount;
}
}
return outputSteering;
}
INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)

View file

@ -98,7 +98,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source);
INT32 K_GetKartRingPower(player_t *player, boolean boosted);
void K_UpdateDistanceFromFinishLine(player_t *const player);
boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
void K_UpdateSteeringValue(player_t *player, INT16 destSteering);
INT16 K_UpdateSteeringValue(INT16 inputSteering, INT16 destSteering);
INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue);
INT32 K_GetUnderwaterTurnAdjust(player_t *player);
INT32 K_GetKartDriftSparkValue(player_t *player);

View file

@ -2003,13 +2003,46 @@ static void P_3dMovement(player_t *player)
static void P_UpdatePlayerAngle(player_t *player)
{
angle_t angleChange = ANGLE_MAX;
UINT8 p = UINT8_MAX;
UINT8 i;
K_UpdateSteeringValue(player, player->cmd.turning);
for (i = 0; i <= splitscreen; i++)
{
if (player == &players[g_localplayers[i]])
{
p = i;
break;
}
}
player->steering = K_UpdateSteeringValue(player->steering, player->cmd.turning);
angleChange = K_GetKartTurnValue(player, player->steering) << TICCMD_REDUCE;
P_SetPlayerAngle(player, player->angleturn + angleChange);
player->mo->angle = player->angleturn;
if (p == UINT8_MAX)
{
// When F12ing players, set local angle directly.
P_SetPlayerAngle(player, player->angleturn + angleChange);
player->mo->angle = player->angleturn;
}
else
{
UINT8 lateTic = ((leveltime - player->cmd.latency) & TICCMD_LATENCYMASK);
UINT8 clearTic = ((localtic + 1) & TICCMD_LATENCYMASK);
player->angleturn += angleChange;
player->mo->angle = player->angleturn;
// Undo the ticcmd's old emulated angle,
// now that we added the actual game logic angle.
while (lateTic != clearTic)
{
localdelta[p] -= localstoredeltas[p][lateTic];
localstoredeltas[p][lateTic] = 0;
lateTic = (lateTic - 1) & TICCMD_LATENCYMASK;
}
}
if (!cv_allowmlook.value || player->spectator == false)
{
@ -2021,13 +2054,9 @@ static void P_UpdatePlayerAngle(player_t *player)
player->aiming = G_ClipAimingPitch((INT32 *)&player->aiming);
}
for (i = 0; i <= r_splitscreen; i++)
if (p != UINT8_MAX)
{
if (player == &players[displayplayers[i]])
{
localaiming[i] = player->aiming;
break;
}
localaiming[p] = player->aiming;
}
}
@ -4866,6 +4895,7 @@ void P_ForceLocalAngle(player_t *player, angle_t angle)
if (player == &players[displayplayers[i]])
{
localangle[i] = angle;
G_ResetAnglePrediction(player);
break;
}
}