diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a648b07ae..0b51846ca 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -359,10 +359,10 @@ struct doomdata_t UINT8 reserved; // Padding union { - clientcmd_pak clientpak; // 145 bytes - client2cmd_pak client2pak; // 202 bytes - client3cmd_pak client3pak; // 258 bytes(?) - client4cmd_pak client4pak; // 316 bytes(?) + clientcmd_pak clientpak; // 147 bytes + client2cmd_pak client2pak; // 206 bytes + client3cmd_pak client3pak; // 264 bytes(?) + client4cmd_pak client4pak; // 324 bytes(?) servertics_pak serverpak; // 132495 bytes (more around 360, no?) serverconfig_pak servercfg; // 773 bytes UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index c7a37614c..a3cdf4d9b 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -71,6 +71,7 @@ struct ticcmd_t { SINT8 forwardmove; // -MAXPLMOVE to MAXPLMOVE (50) INT16 turning; // Turn speed + INT16 angle; // Predicted angle, use me if you can! INT16 throwdir; // Aiming direction INT16 aiming; // vertical aiming, see G_BuildTicCmd UINT16 buttons; diff --git a/src/g_demo.c b/src/g_demo.c index 36a6f6c63..c94386d31 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -136,15 +136,16 @@ demoghost *ghosts = NULL; #define DEMO_BOT 0x08 // For demos -#define ZT_FWD 0x01 -#define ZT_SIDE 0x02 -#define ZT_TURNING 0x04 -#define ZT_THROWDIR 0x08 -#define ZT_BUTTONS 0x10 -#define ZT_AIMING 0x20 -#define ZT_LATENCY 0x40 -#define ZT_FLAGS 0x80 -// OUT OF ZIPTICS... +#define ZT_FWD 0x0001 +#define ZT_SIDE 0x0002 +#define ZT_TURNING 0x0004 +#define ZT_ANGLE 0x0008 +#define ZT_THROWDIR 0x0010 +#define ZT_BUTTONS 0x0020 +#define ZT_AIMING 0x0040 +#define ZT_LATENCY 0x0080 +#define ZT_FLAGS 0x0100 +// Ziptics are UINT16 now, go nuts #define DEMOMARKER 0x80 // demobuf.end @@ -530,17 +531,19 @@ void G_WriteDemoExtraData(void) void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) { - UINT8 ziptic; + UINT16 ziptic; if (!demobuf.p || !demo.deferstart) return; - ziptic = READUINT8(demobuf.p); + ziptic = READUINT16(demobuf.p); if (ziptic & ZT_FWD) oldcmd[playernum].forwardmove = READSINT8(demobuf.p); if (ziptic & ZT_TURNING) oldcmd[playernum].turning = READINT16(demobuf.p); + if (ziptic & ZT_ANGLE) + oldcmd[playernum].angle = READINT16(demobuf.p); if (ziptic & ZT_THROWDIR) oldcmd[playernum].throwdir = READINT16(demobuf.p); if (ziptic & ZT_BUTTONS) @@ -564,13 +567,14 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) { - char ziptic = 0; + UINT16 ziptic = 0; UINT8 *ziptic_p; (void)playernum; if (!demobuf.p) return; - ziptic_p = demobuf.p++; // the ziptic, written at the end of this function + ziptic_p = demobuf.p; // the ziptic, written at the end of this function + demobuf.p += 2; if (cmd->forwardmove != oldcmd[playernum].forwardmove) { @@ -586,6 +590,13 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) ziptic |= ZT_TURNING; } + if (cmd->angle != oldcmd[playernum].angle) + { + WRITEINT16(demobuf.p,cmd->angle); + oldcmd[playernum].angle = cmd->angle; + ziptic |= ZT_ANGLE; + } + if (cmd->throwdir != oldcmd[playernum].throwdir) { WRITEINT16(demobuf.p,cmd->throwdir); @@ -621,7 +632,7 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) ziptic |= ZT_FLAGS; } - *ziptic_p = ziptic; + WRITEUINT16(ziptic_p, ziptic); // attention here for the ticcmd size! // latest demos with mouse aiming byte in ticcmd diff --git a/src/g_game.c b/src/g_game.c index 441f9e6c6..d1689421e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1080,80 +1080,32 @@ INT32 localaiming[MAXSPLITSCREENPLAYERS]; angle_t localangle[MAXSPLITSCREENPLAYERS]; INT32 localsteering[MAXSPLITSCREENPLAYERS]; -INT32 localdelta[MAXSPLITSCREENPLAYERS]; -INT32 localstoredeltas[MAXSPLITSCREENPLAYERS][TICCMD_LATENCYMASK + 1]; -UINT8 locallatency[MAXSPLITSCREENPLAYERS][TICRATE]; -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) + while (realtics > 0) { - // Otherwise, your angle slingshots off to the side violently... - G_ResetAnglePrediction(player); + localsteering[ssplayer - 1] = K_UpdateSteeringValue(localsteering[ssplayer - 1], cmd->turning); + angleChange = K_GetKartTurnValue(player, localsteering[ssplayer - 1]) << TICCMD_REDUCE; + + realtics--; + } + +#if 0 + // Left here in case it needs unsealing later. This tried to replicate an old localcam function, but this behavior was unpopular in tests. + //if (player->pflags & PF_DRIFTEND) + { + localangle[ssplayer - 1] = player->mo->angle; } else +#endif { - 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--; - } + localangle[ssplayer - 1] += angleChange; } - - // 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; - - // In case of angle debugging, break glass - // localangle[ssplayer - 1] = destAngle; } void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) @@ -1189,6 +1141,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) break; } + cmd->angle = localangle[forplayer] >> TICCMD_REDUCE; + // why build a ticcmd if we're paused? // Or, for that matter, if we're being reborn. if (paused || P_AutoPause() || (gamestate == GS_LEVEL && player->playerstate == PST_REBORN)) @@ -1422,6 +1376,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) { dest[i].forwardmove = src[i].forwardmove; dest[i].turning = (INT16)SHORT(src[i].turning); + dest[i].angle = (INT16)SHORT(src[i].angle); dest[i].throwdir = (INT16)SHORT(src[i].throwdir); dest[i].aiming = (INT16)SHORT(src[i].aiming); dest[i].buttons = (UINT16)SHORT(src[i].buttons); diff --git a/src/g_game.h b/src/g_game.h index 78b484a69..fac245c9a 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -101,7 +101,6 @@ extern consvar_t cv_resume; const char *G_BuildMapName(INT32 map); INT32 G_MapNumber(const char *mapname); -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 @@ -116,10 +115,6 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming); 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 locallatency[MAXSPLITSCREENPLAYERS][TICRATE]; -extern UINT8 localtic; INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers); boolean G_PlayerInputDown(UINT8 p, INT32 gc, UINT8 menuPlayers); diff --git a/src/p_spec.c b/src/p_spec.c index e252bf374..0317599e4 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -9100,15 +9100,17 @@ void T_Pusher(pusher_t *p) thing->player->carry = CR_SLIDING; thing->angle = R_PointToAngle2(0, 0, xspeed, yspeed); + if (!demo.playback) { - angle_t angle = thing->player->angleturn; + angle_t angle = thing->player->boostangle; if (thing->angle - angle > ANGLE_180) - P_SetPlayerAngle(thing->player, angle - (angle - thing->angle) / 8); + thing->player->boostangle = angle - (angle - thing->angle) / 8; else - P_SetPlayerAngle(thing->player, angle + (thing->angle - angle) / 8); + thing->player->boostangle = angle + (thing->angle - angle) / 8; //P_SetPlayerAngle(thing->player, thing->angle); } + } if (p->exclusive) diff --git a/src/p_user.c b/src/p_user.c index bc5d699d8..223af8f47 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2120,7 +2120,6 @@ static void P_3dMovement(player_t *player) static void P_UpdatePlayerAngle(player_t *player) { angle_t angleChange = ANGLE_MAX; - UINT8 maxlatency; UINT8 p = UINT8_MAX; UINT8 i; @@ -2133,8 +2132,58 @@ static void P_UpdatePlayerAngle(player_t *player) } } - player->steering = K_UpdateSteeringValue(player->steering, player->cmd.turning); - angleChange = K_GetKartTurnValue(player, player->steering) << TICCMD_REDUCE; + // Don't apply steering just yet. If we make a correction, we'll need to adjust it. + INT16 targetsteering = K_UpdateSteeringValue(player->steering, player->cmd.turning); + angleChange = K_GetKartTurnValue(player, targetsteering) << TICCMD_REDUCE; + + if (!K_PlayerUsesBotMovement(player)) + { + // With a full slam on the analog stick, how far could we steer in either direction? + INT16 steeringRight = K_UpdateSteeringValue(player->steering, KART_FULLTURN); + angle_t maxTurnRight = K_GetKartTurnValue(player, steeringRight) << TICCMD_REDUCE; + INT16 steeringLeft = K_UpdateSteeringValue(player->steering, -KART_FULLTURN); + angle_t maxTurnLeft = K_GetKartTurnValue(player, steeringLeft) << TICCMD_REDUCE; + + // Grab local camera angle from ticcmd. Where do we actually want to go? + angle_t targetAngle = (player->cmd.angle) << TICCMD_REDUCE; + angle_t targetDelta = targetAngle - (player->mo->angle); + + if (targetDelta == angleChange || player->pflags & PF_DRIFTEND || (maxTurnRight == 0 && maxTurnLeft == 0)) + { + // We are where we need to be. + // ...Or we aren't, but shouldn't be able to steer. + player->steering = targetsteering; + // Alternatively, while in DRIFTEND we want to trust inputs for a bit, not camera. + // The game client doesn't know we're DRIFTEND until after a response gets back, + // so we momentarily ignore the camera angle and let the server trust our inputs instead. + // That way, even if you're steering blind, you get the intended "kick-out" effect. + } + else if (targetDelta >= ANGLE_180 && maxTurnLeft >= targetDelta) // Overshot, so just fudge it. + { + angleChange = targetDelta; + player->steering = targetsteering; + } + else if (targetDelta <= ANGLE_180 && maxTurnRight <= targetDelta) // Overshot, so just fudge it. + { + angleChange = targetDelta; + player->steering = targetsteering; + } + else if (targetDelta >= ANGLE_180 && maxTurnLeft < targetDelta) // Undershot, slam the stick. + { + angleChange = maxTurnLeft; + player->steering = steeringLeft; + } + else if (targetDelta <= ANGLE_180 && maxTurnRight > targetDelta) // Undershot, slam the stick. + { + angleChange = maxTurnRight; + player->steering = steeringRight; + } + } + else + { + // You're a bot. Go where you're supposed to go + player->steering = targetsteering; + } if (p == UINT8_MAX) { @@ -2144,33 +2193,8 @@ static void P_UpdatePlayerAngle(player_t *player) } else { - // During standard play, our latency can vary by up to 1 tic in either direction, even on a stable connection. - // This probably comes from differences in ticcmd dispatch vs consumption rate. Probably. - // Uncorrected, this 2-tic "wobble" causes camera corrections to sometimes be skipped or batched. - // So just use the highest recent value for the furthest possible search. - // We unset the correction after applying, anyway. - locallatency[p][leveltime%TICRATE] = maxlatency = player->cmd.latency; - for (i = 0; i < TICRATE; i++) - { - maxlatency = max(locallatency[p][i], maxlatency); - } - - UINT8 lateTic = ((leveltime - maxlatency) & 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) @@ -3162,7 +3186,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (demo.playback) { - focusangle = mo->angle; + // Hack-adjacent. + // Sometimes stale ticcmds send a weird angle at the start of the race. + // P_UpdatePlayerAngle knows to ignore cmd angle when you literally can't turn, so we do the same here. + if (leveltime > introtime) + focusangle = player->cmd.angle << TICCMD_REDUCE; + else + focusangle = mo->angle; // Just use something known sane. focusaiming = 0; } else @@ -4478,7 +4508,7 @@ void P_ForceLocalAngle(player_t *player, angle_t angle) if (player == &players[displayplayers[i]]) { localangle[i] = angle; - G_ResetAnglePrediction(player); + break; } }