From 4b149c103c004489f3058544370035c3c54058f1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 21 May 2022 03:11:37 -0400 Subject: [PATCH 01/10] Bring back camera turn prediction REALLY complicated, because just emulating player turning in ticcmd code is broken. --- src/d_ticcmd.h | 3 +++ src/g_game.c | 37 +++++++++++++++++++++++++++++++++---- src/g_game.h | 4 ++++ src/k_kart.c | 13 ++++++++----- src/k_kart.h | 2 +- src/p_user.c | 39 +++++++++++++++++++++++++++++++-------- 6 files changed, 80 insertions(+), 18 deletions(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 226c8f4d8..1e9bd1612 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -52,6 +52,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 */ diff --git a/src/g_game.c b/src/g_game.c index 8c42e4532..623811d78 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -871,6 +871,35 @@ 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; + +// 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) +{ + INT32 angleChange = 0; + + localtic = cmd->latency; + + 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] = player->angleturn + localdelta[ssplayer - 1]; +} + void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { const UINT8 forplayer = ssplayer-1; @@ -896,8 +925,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? @@ -1148,7 +1175,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) @@ -1161,6 +1188,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else if (cmd->turning < -KART_FULLTURN) cmd->turning = -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) @@ -1925,7 +1954,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); } } } diff --git a/src/g_game.h b/src/g_game.h index cb0cae127..8b9aed2fd 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -117,6 +117,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 diff --git a/src/k_kart.c b/src/k_kart.c index 1071f0371..6844c08ae 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8089,31 +8089,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) diff --git a/src/k_kart.h b/src/k_kart.h index aef18b19e..41465bc5e 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -97,7 +97,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); diff --git a/src/p_user.c b/src/p_user.c index 8a5d9459c..de4dbb6e6 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1988,14 +1988,41 @@ 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 <= r_splitscreen; i++) + { + if (player == &players[displayplayers[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->angleturn += angleChange; player->mo->angle = player->angleturn; + if (p != UINT8_MAX) + { + UINT8 lateTic = ((leveltime - player->cmd.latency) & TICCMD_LATENCYMASK); + UINT8 clearTic = ((localtic + 1) & TICCMD_LATENCYMASK); + + // 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) { player->aiming = 0; @@ -2006,13 +2033,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; } } From 9c0e7930887790f91b5f273fa423c7f2ab605199 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 21 May 2022 16:51:03 +0100 Subject: [PATCH 02/10] Incorporate some of the advances from `tilttrack`. * Set view context in the player HUD loop inside `ST_overlayDrawer()`. * The HUD code now gets all interpolated camera data from the view context instead of half-heartedly recreating it. * Move Lua HUDlib data to the view context system instead of an else ladder. * View roll support in HUD tracking, because the other changes made this trivial. * Remove old_viewrollangle as it is now redundant. --- src/d_player.h | 1 - src/k_hud.c | 151 +++++++++++++++-------------------------------- src/k_hud.h | 2 +- src/lua_hudlib.c | 24 ++------ src/p_user.c | 1 - src/r_fps.c | 17 +----- src/r_fps.h | 4 ++ src/st_stuff.c | 74 ++++++++++++----------- 8 files changed, 96 insertions(+), 178 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index a1247e8fe..e411225c6 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -328,7 +328,6 @@ typedef struct player_s skybox_t skybox; angle_t viewrollangle; - angle_t old_viewrollangle; // camera tilt // TODO: expose to lua angle_t tilt; diff --git a/src/k_hud.c b/src/k_hud.c index 00537f81b..3861f2e08 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -825,16 +825,11 @@ void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du } // This version of the function was prototyped in Lua by Nev3r ... a HUGE thank you goes out to them! -// TODO: This should probably support view rolling if we're adding that soon... -void K_ObjectTracking(trackingResult_t *result, vector3_t *point, UINT8 cameraNum, angle_t angleOffset) +void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean reverse) { #define NEWTAN(x) FINETANGENT(((x + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) // tan function used by Lua #define NEWCOS(x) FINECOSINE((x >> ANGLETOFINESHIFT) & FINEMASK) - camera_t *cam; - player_t *player; - - fixed_t viewpointX, viewpointY, viewpointZ; angle_t viewpointAngle, viewpointAiming, viewpointRoll; INT32 screenWidth, screenHeight; @@ -846,6 +841,8 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, UINT8 cameraNu fixed_t h; INT32 da; + UINT8 cameraNum = R_GetViewNumber(); + I_Assert(result != NULL); I_Assert(point != NULL); @@ -854,49 +851,21 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, UINT8 cameraNu result->scale = FRACUNIT; result->onScreen = false; - if (cameraNum > r_splitscreen) + // Take the view's properties as necessary. + if (reverse) { - // Invalid camera ID. - return; - } - - cam = &camera[cameraNum]; - player = &players[displayplayers[cameraNum]]; - - if (cam == NULL || player == NULL || player->mo == NULL || P_MobjWasRemoved(player->mo) == true) - { - // Shouldn't be possible? - return; - } - - // TODO: parts need interp - if (cam->chase == true && !player->spectator) - { - // Use the camera's properties. - viewpointX = R_InterpolateFixed(cam->old_x, cam->x); - viewpointY = R_InterpolateFixed(cam->old_y, cam->y); - viewpointZ = R_InterpolateFixed(cam->old_z, cam->z) - point->z; - viewpointAngle = (INT32)R_InterpolateAngle(cam->old_angle, cam->angle); - viewpointAiming = (INT32)R_InterpolateAngle(cam->old_aiming, cam->aiming); - viewpointRoll = (INT32)R_InterpolateAngle(player->old_viewrollangle, player->viewrollangle); + viewpointAngle = (INT32)(viewangle + ANGLE_180); + viewpointAiming = (INT32)InvAngle(aimingangle); + viewpointRoll = (INT32)viewroll; } else { - // Use player properties. - viewpointX = R_InterpolateFixed(player->mo->old_x, player->mo->x); - viewpointY = R_InterpolateFixed(player->mo->old_y, player->mo->y); - viewpointZ = R_InterpolateFixed(player->mo->old_z, player->mo->z) - point->z; //player->old_viewz - viewpointAngle = (INT32)R_InterpolateAngle(player->mo->old_angle, player->mo->angle); - viewpointAiming = (INT32)player->aiming; - viewpointRoll = (INT32)R_InterpolateAngle(player->old_viewrollangle, player->viewrollangle); + viewpointAngle = (INT32)viewangle; + viewpointAiming = (INT32)aimingangle; + viewpointRoll = (INT32)InvAngle(viewroll); } - viewpointAngle += (INT32)angleOffset; - - (void)viewpointRoll; // will be used later... - // Calculate screen size adjustments. - // TODO: Anyone want to make this support non-green resolutions somehow? :V screenWidth = vid.width/vid.dupx; screenHeight = vid.height/vid.dupy; @@ -917,7 +886,7 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, UINT8 cameraNu // Calculate FOV adjustments. fovDiff = cv_fov[cameraNum].value - baseFov; - fov = ((baseFov - fovDiff) / 2) - (player->fovadd / 2); + fov = ((baseFov - fovDiff) / 2) - (stplyr->fovadd / 2); fovTangent = NEWTAN(FixedAngle(fov)); if (r_splitscreen == 1) @@ -929,22 +898,35 @@ void K_ObjectTracking(trackingResult_t *result, vector3_t *point, UINT8 cameraNu fg = (screenWidth >> 1) * fovTangent; // Determine viewpoint factors. - h = R_PointToDist2(point->x, point->y, viewpointX, viewpointY); - da = AngleDeltaSigned(viewpointAngle, R_PointToAngle2(point->x, point->y, viewpointX, viewpointY)); + h = R_PointToDist2(point->x, point->y, viewx, viewy); + da = AngleDeltaSigned(viewpointAngle, R_PointToAngle2(point->x, point->y, viewx, viewy)); - // Set results! - result->x = screenHalfW + FixedMul(NEWTAN(da), fg); - result->y = screenHalfH + FixedMul((NEWTAN(viewpointAiming) - FixedDiv(viewpointZ, 1 + FixedMul(NEWCOS(da), h))), fg); + // Set results relative to top left! + result->x = FixedMul(NEWTAN(da), fg); + result->y = FixedMul((NEWTAN(viewpointAiming) - FixedDiv((viewz - point->z), 1 + FixedMul(NEWCOS(da), h))), fg); + + // Rotate for screen roll... + if (viewpointRoll) + { + fixed_t tempx = result->x; + viewpointRoll >>= ANGLETOFINESHIFT; + result->x = FixedMul(FINECOSINE(viewpointRoll), tempx) - FixedMul(FINESINE(viewpointRoll), result->y); + result->y = FixedMul(FINESINE(viewpointRoll), tempx) + FixedMul(FINECOSINE(viewpointRoll), result->y); + } + + // Flipped screen? + if (encoremode) + { + result->x = -result->x; + } + + // Center results. + result->x += screenHalfW; + result->y += screenHalfH; result->scale = FixedDiv(screenHalfW, h+1); - result->onScreen = ((abs(da) > ANG60) || (abs(AngleDeltaSigned(viewpointAiming, R_PointToAngle2(0, 0, h, viewpointZ))) > ANGLE_45)); - - if (encoremode) - { - // Flipped screen - result->x = (screenWidth << FRACBITS) - result->x; - } + result->onScreen = ((abs(da) > ANG60) || (abs(AngleDeltaSigned(viewpointAiming, R_PointToAngle2(0, 0, h, (viewz - point->z)))) > ANGLE_45)); // Cheap dirty hacks for some split-screen related cases if (result->x < 0 || result->x > (screenWidth << FRACBITS)) @@ -2841,7 +2823,6 @@ static void K_drawKartWanted(void) static void K_drawKartPlayerCheck(void) { const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - UINT8 cnum = 0; UINT8 i; INT32 splitflags = V_SNAPTOBOTTOM|V_SPLITSCREEN; fixed_t y = CHEK_Y * FRACUNIT; @@ -2861,20 +2842,6 @@ static void K_drawKartPlayerCheck(void) return; } - if (r_splitscreen) - { - y /= 2; - - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - for (i = 0; i < MAXPLAYERS; i++) { player_t *checkplayer = &players[i]; @@ -2933,7 +2900,7 @@ static void K_drawKartPlayerCheck(void) pnum += 2; } - K_ObjectTracking(&result, &v, cnum, ANGLE_180); + K_ObjectTracking(&result, &v, true); if (result.onScreen == true) { @@ -3008,7 +2975,7 @@ static void K_DrawTypingNotifier(fixed_t x, fixed_t y, player_t *p) } } -static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT8 cnum) +static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p) { const INT32 clr = skincolors[p->skincolor].chatcolor; const INT32 namelen = V_ThinStringWidth(player_names[p - players], V_6WIDTHSPACE|V_ALLOWLOWERCASE); @@ -3016,6 +2983,8 @@ static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT8 cnum UINT8 *colormap = V_GetStringColormap(clr); INT32 barx = 0, bary = 0, barw = 0; + UINT8 cnum = R_GetViewNumber(); + // Since there's no "V_DrawFixedFill", and I don't feel like making it, // fuck it, we're gonna just V_NOSCALESTART hack it if (cnum & 1) @@ -3100,9 +3069,8 @@ static void K_DrawWeakSpot(weakspotdraw_t *ws) static void K_drawKartNameTags(void) { const fixed_t maxdistance = 8192*mapobjectscale; - camera_t *thiscam; vector3_t c; - UINT8 cnum = 0; + UINT8 cnum = R_GetViewNumber(); UINT8 tobesorted[MAXPLAYERS]; fixed_t sortdist[MAXPLAYERS]; UINT8 sortlen = 0; @@ -3118,32 +3086,9 @@ static void K_drawKartNameTags(void) return; } - if (r_splitscreen) - { - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - - thiscam = &camera[cnum]; - - if (thiscam->chase == true) - { - c.x = R_InterpolateFixed(thiscam->old_x, thiscam->x); - c.y = R_InterpolateFixed(thiscam->old_y, thiscam->y); - c.z = R_InterpolateFixed(thiscam->old_z, thiscam->z); - } - else - { - c.x = R_InterpolateFixed(stplyr->mo->old_x, stplyr->mo->x); - c.y = R_InterpolateFixed(stplyr->mo->old_y, stplyr->mo->y); - c.z = R_InterpolateFixed(stplyr->mo->old_z, stplyr->mo->z); - } + c.x = viewx; + c.y = viewy; + c.z = viewz; // Maybe shouldn't be handling this here... but the camera info is too good. if (bossinfo.boss) @@ -3175,7 +3120,7 @@ static void K_drawKartNameTags(void) v.z += (bossinfo.weakspots[i].spot->height / 2); - K_ObjectTracking(&result, &v, cnum, 0); + K_ObjectTracking(&result, &v, false); if (result.onScreen == false) { continue; @@ -3328,7 +3273,7 @@ static void K_drawKartNameTags(void) v.z += headOffset; } - K_ObjectTracking(&result, &v, cnum, 0); + K_ObjectTracking(&result, &v, false); if (result.onScreen == true) { @@ -3363,7 +3308,7 @@ static void K_drawKartNameTags(void) { if (K_ShowPlayerNametag(ntplayer) == true) { - K_DrawNameTagForPlayer(result.x, result.y, ntplayer, cnum); + K_DrawNameTagForPlayer(result.x, result.y, ntplayer); K_DrawTypingNotifier(result.x, result.y, ntplayer); } } diff --git a/src/k_hud.h b/src/k_hud.h index fff718ef5..683425e8a 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -28,7 +28,7 @@ typedef struct trackingResult_s boolean onScreen; } trackingResult_t; -void K_ObjectTracking(trackingResult_t *result, vector3_t *point, UINT8 cameraNum, angle_t angleOffset); +void K_ObjectTracking(trackingResult_t *result, vector3_t *point, boolean reverse); const char *K_GetItemPatch(UINT8 item, boolean tiny); void K_LoadKartHUDGraphics(void); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 8245cb1e8..ad6555541 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -14,6 +14,7 @@ #include "fastcmp.h" #include "r_defs.h" #include "r_local.h" +#include "r_fps.h" #include "st_stuff.h" #include "g_game.h" #include "i_video.h" // rendermode @@ -1394,26 +1395,9 @@ void LUAh_GameHUD(player_t *stplayr, huddrawlist_h list) lua_remove(gL, -3); // pop HUD LUA_PushUserdata(gL, stplayr, META_PLAYER); - if (r_splitscreen > 2 && stplayr == &players[displayplayers[3]]) - { - LUA_PushUserdata(gL, &camera[3], META_CAMERA); - camnum = 4; - } - else if (r_splitscreen > 1 && stplayr == &players[displayplayers[2]]) - { - LUA_PushUserdata(gL, &camera[2], META_CAMERA); - camnum = 3; - } - else if (r_splitscreen && stplayr == &players[displayplayers[1]]) - { - LUA_PushUserdata(gL, &camera[1], META_CAMERA); - camnum = 2; - } - else - { - LUA_PushUserdata(gL, &camera[0], META_CAMERA); - camnum = 1; - } + camnum = R_GetViewNumber(); + LUA_PushUserdata(gL, &camera[camnum], META_CAMERA); + camnum++; // for compatibility lua_pushnil(gL); while (lua_next(gL, -5) != 0) { diff --git a/src/p_user.c b/src/p_user.c index b490d65dc..1c8af8611 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4313,7 +4313,6 @@ void P_PlayerThink(player_t *player) } player->old_drawangle = player->drawangle; - player->old_viewrollangle = player->viewrollangle; player->pflags &= ~PF_HITFINISHLINE; diff --git a/src/r_fps.c b/src/r_fps.c index 6f4281687..aff481910 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -163,22 +163,7 @@ void R_InterpolateView(fixed_t frac) if (frac > FRACUNIT) frac = FRACUNIT; - if (viewcontext == VIEWCONTEXT_SKY1 || viewcontext == VIEWCONTEXT_PLAYER1) - { - i = 0; - } - else if (viewcontext == VIEWCONTEXT_SKY2 || viewcontext == VIEWCONTEXT_PLAYER2) - { - i = 1; - } - else if (viewcontext == VIEWCONTEXT_SKY3 || viewcontext == VIEWCONTEXT_PLAYER3) - { - i = 2; - } - else - { - i = 3; - } + i = R_GetViewNumber(); if (oldview_invalid[i] != 0) { diff --git a/src/r_fps.h b/src/r_fps.h index a91f7004e..41fc65af0 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -36,6 +36,10 @@ enum viewcontext_e VIEWCONTEXT_SKY4 }; +extern enum viewcontext_e viewcontext; + +#define R_GetViewNumber() ((viewcontext - VIEWCONTEXT_PLAYER1) & 3) + typedef struct { fixed_t x; fixed_t y; diff --git a/src/st_stuff.c b/src/st_stuff.c index 9b98f0b68..bb1cea9ff 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -33,6 +33,7 @@ #include "p_setup.h" // NiGHTS grading #include "k_grandprix.h" // we need to know grandprix status for titlecards #include "k_boss.h" +#include "r_fps.h" //random index #include "m_random.h" @@ -1002,8 +1003,17 @@ static void ST_overlayDrawer(void) { // hu_showscores = auto hide score/time/rings when tab rankings are shown if (!(hu_showscores && (netgame || multiplayer))) + { K_drawKartHUD(); + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_game); + LUAh_GameHUD(stplyr, luahuddrawlist_game); + } + LUA_HUD_DrawList(luahuddrawlist_game); + } + if (!hu_showscores) // hide the following if TAB is held { if (cv_showviewpointtext.value) @@ -1031,16 +1041,6 @@ static void ST_overlayDrawer(void) } } - if (!(netgame || multiplayer) || !hu_showscores) - { - if (renderisnewtic) - { - LUA_HUD_ClearDrawList(luahuddrawlist_game); - LUAh_GameHUD(stplyr, luahuddrawlist_game); - } - LUA_HUD_DrawList(luahuddrawlist_game); - } - if (!hu_showscores && netgame && !mapreset) { if (stplyr->spectator && LUA_HudEnabled(hud_textspectator)) @@ -1081,32 +1081,6 @@ static void ST_overlayDrawer(void) } } } - - // Replay manual-save stuff - if (demo.recording && multiplayer && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime) - { - switch (demo.savemode) - { - case DSM_NOTSAVING: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Look Backward: Save replay"); - break; - - case DSM_WILLAUTOSAVE: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved. (Look Backward: Change title)"); - break; - - case DSM_WILLSAVE: - V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved."); - break; - - case DSM_TITLEENTRY: - ST_DrawDemoTitleEntry(); - break; - - default: // Don't render anything - break; - } - } } void ST_DrawDemoTitleEntry(void) @@ -1217,6 +1191,8 @@ void ST_Drawer(void) for (i = 0; i <= r_splitscreen; i++) { stplyr = &players[displayplayers[i]]; + R_SetViewContext(VIEWCONTEXT_PLAYER1 + i); + R_InterpolateView(rendertimefrac); // to assist with object tracking ST_overlayDrawer(); } @@ -1232,5 +1208,31 @@ void ST_Drawer(void) if (stagetitle) ST_drawTitleCard(); + // Replay manual-save stuff + if (demo.recording && multiplayer && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime) + { + switch (demo.savemode) + { + case DSM_NOTSAVING: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Look Backward: Save replay"); + break; + + case DSM_WILLAUTOSAVE: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved. (Look Backward: Change title)"); + break; + + case DSM_WILLSAVE: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|((gametyperules & GTR_BUMPERS) ? V_REDMAP : V_SKYMAP), "Replay will be saved."); + break; + + case DSM_TITLEENTRY: + ST_DrawDemoTitleEntry(); + break; + + default: // Don't render anything + break; + } + } + ST_drawDebugInfo(); } From 0dc472a2556b4da0fbc2221ddf6b574bb5d17bff Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 22 May 2022 15:38:08 -0400 Subject: [PATCH 03/10] Try resetting driftend --- src/g_game.c | 42 ++++++++++++++++++++++++++++++++++-------- src/g_game.h | 1 + src/p_user.c | 1 + 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 1020883cf..37cfa2f65 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -876,6 +876,24 @@ 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) @@ -884,17 +902,25 @@ static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, p localtic = cmd->latency; - while (realtics > 0) + if (player->pflags & PF_DRIFTEND) { - localsteering[ssplayer - 1] = K_UpdateSteeringValue(localsteering[ssplayer - 1], cmd->turning); - angleChange = K_GetKartTurnValue(player, localsteering[ssplayer - 1]) << TICCMD_REDUCE; + // 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; + // 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--; + realtics--; + } } localangle[ssplayer - 1] = player->angleturn + localdelta[ssplayer - 1]; diff --git a/src/g_game.h b/src/g_game.h index 8b9aed2fd..52c7c7a0d 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -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 diff --git a/src/p_user.c b/src/p_user.c index 6e08127d3..0d8f3e7ab 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4874,6 +4874,7 @@ void P_ForceLocalAngle(player_t *player, angle_t angle) if (player == &players[displayplayers[i]]) { localangle[i] = angle; + G_ResetAnglePrediction(player); break; } } From 986bcddbfc2e6f8437d8264fe64ff79f5243b79d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 22 May 2022 16:21:07 -0400 Subject: [PATCH 04/10] Ease prediction angle Might feel laggier, might feel better because it gets rid of jitter? idk --- src/g_game.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 37cfa2f65..03467a23f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -899,6 +899,7 @@ void G_ResetAnglePrediction(player_t *player) static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, player_t *player) { INT32 angleChange = 0; + angle_t destAngle = player->angleturn; localtic = cmd->latency; @@ -923,7 +924,8 @@ static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, p } } - localangle[ssplayer - 1] = player->angleturn + localdelta[ssplayer - 1]; + destAngle = player->angleturn + localdelta[ssplayer - 1]; + localangle[ssplayer - 1] += (destAngle - localangle[ssplayer - 1]) / 2; } void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) From c9e685be66b2dd33f5f24c5ad38e8e972d2691a9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 22 May 2022 16:30:27 -0400 Subject: [PATCH 05/10] Nope, that didn't work, move by your max turn speed until you reach it --- src/g_game.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 03467a23f..47bc94789 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -898,8 +898,10 @@ void G_ResetAnglePrediction(player_t *player) // This brings back the camera prediction that was lost. static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, player_t *player) { + const angle_t maxTurn = K_GetKartTurnValue(player, KART_FULLTURN) << TICCMD_REDUCE; INT32 angleChange = 0; - angle_t destAngle = player->angleturn; + INT32 destAngle = player->angleturn; + INT32 diff = 0; localtic = cmd->latency; @@ -925,7 +927,22 @@ static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, p } destAngle = player->angleturn + localdelta[ssplayer - 1]; - localangle[ssplayer - 1] += (destAngle - localangle[ssplayer - 1]) / 2; + diff = destAngle - localangle[ssplayer - 1]; + if (abs(diff) <= maxTurn) + { + localangle[ssplayer - 1] = destAngle; + } + else + { + if (diff > 0) + { + localangle[ssplayer - 1] += maxTurn; + } + else + { + localangle[ssplayer - 1] -= maxTurn; + } + } } void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) From 946beabdf6ff124451a175d0861996980c247b0b Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 22 May 2022 22:25:17 +0100 Subject: [PATCH 06/10] Fix lookback interpolating # Conflicts: # src/p_setup.c # src/r_fps.c --- src/p_setup.c | 4 ++++ src/p_user.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index baba85ee8..c1da43eef 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -4509,6 +4509,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (rendermode == render_none || reloadinggamestate) return true; + R_ResetViewInterpolation(0); + R_ResetViewInterpolation(0); + R_UpdateMobjInterpolators(); + // Title card! G_StartTitleCard(); diff --git a/src/p_user.c b/src/p_user.c index 145835e48..31426e455 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3593,7 +3593,11 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } if (lookbackdown) + { P_MoveChaseCamera(player, thiscam, false); + R_ResetViewInterpolation(num + 1); + R_ResetViewInterpolation(num + 1); + } return (x == thiscam->x && y == thiscam->y && z == thiscam->z && angle == thiscam->aiming); From 4f9642f537eb78d085325a503dfa338f233e852b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 4 May 2022 05:20:32 -0400 Subject: [PATCH 07/10] Do not register servers twice Advertise_OnChange is CV_NETVAR, so the server will get registered by CV_LoadNetVars when the server starts. This race condition occasionally pops up in master, but got very consistent with uncapped. --- src/d_clisrv.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e5321d225..b03eb478e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3599,10 +3599,6 @@ boolean SV_SpawnServer(void) if (netgame && I_NetOpenSocket) { I_NetOpenSocket(); -#ifdef MASTERSERVER - if (cv_advertise.value) - RegisterServer(); -#endif } // non dedicated server just connect to itself From ada93361373207d6f4340909da54078b8418111f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 22 May 2022 17:49:34 -0400 Subject: [PATCH 08/10] OK go back to easing, but now actually work --- src/g_game.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 47bc94789..c6afebd0d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -898,10 +898,9 @@ void G_ResetAnglePrediction(player_t *player) // This brings back the camera prediction that was lost. static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, player_t *player) { - const angle_t maxTurn = K_GetKartTurnValue(player, KART_FULLTURN) << TICCMD_REDUCE; - INT32 angleChange = 0; - INT32 destAngle = player->angleturn; - INT32 diff = 0; + angle_t angleChange = 0; + angle_t destAngle = player->angleturn; + angle_t diff = 0; localtic = cmd->latency; @@ -928,21 +927,17 @@ static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, p destAngle = player->angleturn + localdelta[ssplayer - 1]; diff = destAngle - localangle[ssplayer - 1]; - if (abs(diff) <= maxTurn) + + if (diff > ANGLE_180) { - localangle[ssplayer - 1] = destAngle; + diff = InvAngle(InvAngle(diff) / 2); } else { - if (diff > 0) - { - localangle[ssplayer - 1] += maxTurn; - } - else - { - localangle[ssplayer - 1] -= maxTurn; - } + diff /= 2; } + + localangle[ssplayer - 1] += diff; } void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) From 85aa7361c23857e1ca259feb1ff81a22aca889d7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 22 May 2022 18:03:32 -0400 Subject: [PATCH 09/10] Put a comment about camera easing --- src/g_game.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/g_game.c b/src/g_game.c index c6afebd0d..21467e592 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -925,6 +925,9 @@ static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, p } } + // 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]; From 6f42d84b2afb1377eff6a059dec4dc90b096699d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 22 May 2022 19:49:33 -0400 Subject: [PATCH 10/10] Fix turn prediction in F12 --- src/p_user.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index de42228a7..2bd75f0c4 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2006,9 +2006,9 @@ static void P_UpdatePlayerAngle(player_t *player) UINT8 p = UINT8_MAX; UINT8 i; - for (i = 0; i <= r_splitscreen; i++) + for (i = 0; i <= splitscreen; i++) { - if (player == &players[displayplayers[i]]) + if (player == &players[g_localplayers[i]]) { p = i; break; @@ -2018,14 +2018,20 @@ static void P_UpdatePlayerAngle(player_t *player) player->steering = K_UpdateSteeringValue(player->steering, player->cmd.turning); angleChange = K_GetKartTurnValue(player, player->steering) << TICCMD_REDUCE; - player->angleturn += angleChange; - player->mo->angle = player->angleturn; - - if (p != UINT8_MAX) + 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.