From 0f9a8176f54185d6ee1b73f1819788f2c92491ee Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 16:57:31 +1300 Subject: [PATCH 01/10] Unprediction needs to restore selected inventory --- src/p_user.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 7b049a2df..3bbebaffb 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2744,9 +2744,12 @@ void P_UnPredictPlayer () if (player->cheats & CF_PREDICTING) { unsigned int i; - AActor *act = player->mo; + APlayerPawn *act = player->mo; AActor *savedcamera = player->camera; + TObjPtr InvSel = act->InvSel; + int inventorytics = player->inventorytics; + *player = PredictionPlayerBackup; // Restore the camera instead of using the backup's copy, because spynext/prev @@ -2859,6 +2862,9 @@ void P_UnPredictPlayer () } block = block->NextBlock; } + + act->InvSel = InvSel; + player->inventorytics = inventorytics; } } From ae71f94ed2679ac7c8ba45ea83ace50896aa222c Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 18:04:11 +1300 Subject: [PATCH 02/10] Remove disconnecting players safely Players who were disconnecting would be removed outside of the playsims control, causing problems with sector lists. --- src/d_net.cpp | 75 ++++++++++++++------------------------------------ src/d_player.h | 3 +- src/g_game.cpp | 63 ++++++++++++++++++++++++++++++++++++++++-- src/g_game.h | 1 + 4 files changed, 83 insertions(+), 59 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index d33f47e38..0e057369c 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -39,7 +39,6 @@ #include "cmdlib.h" #include "s_sound.h" #include "m_cheat.h" -#include "p_effect.h" #include "p_local.h" #include "c_dispatch.h" #include "sbar.h" @@ -670,6 +669,9 @@ void PlayerIsGone (int netnode, int netconsole) { int i; + if (!nodeingame[netnode]) + return; + for (i = netnode + 1; i < doomcom.numnodes; ++i) { if (nodeingame[i]) @@ -680,55 +682,17 @@ void PlayerIsGone (int netnode, int netconsole) doomcom.numnodes = netnode; } + if (playeringame[netconsole]) + { + players[netconsole].playerstate = PST_GONE; + } nodeingame[netnode] = false; - playeringame[netconsole] = false; nodejustleft[netnode] = false; - if (deathmatch) - { - Printf ("%s left the game with %d frags\n", - players[netconsole].userinfo.GetName(), - players[netconsole].fragcount); - } - else - { - Printf ("%s left the game\n", players[netconsole].userinfo.GetName()); - } - - // [RH] Revert each player to their own view if spying through the player who left - for (int ii = 0; ii < MAXPLAYERS; ++ii) - { - if (playeringame[ii] && players[ii].camera == players[netconsole].mo) - { - players[ii].camera = players[ii].mo; - if (ii == consoleplayer && StatusBar != NULL) - { - StatusBar->AttachToPlayer (&players[ii]); - } - } - } - - // [RH] Make the player disappear - FBehavior::StaticStopMyScripts (players[netconsole].mo); - if (players[netconsole].mo != NULL) - { - P_DisconnectEffect (players[netconsole].mo); - players[netconsole].mo->player = NULL; - players[netconsole].mo->Destroy (); - if (!(players[netconsole].mo->ObjectFlags & OF_EuthanizeMe)) - { // We just destroyed a morphed player, so now the original player - // has taken their place. Destroy that one too. - players[netconsole].mo->Destroy(); - } - players[netconsole].mo = NULL; - players[netconsole].camera = NULL; - } - // [RH] Let the scripts know the player left - FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, netconsole); if (netconsole == Net_Arbitrator) { - bglobal.RemoveAllBots (true); - Printf ("Removed all bots\n"); + bglobal.RemoveAllBots(true); + Printf("Removed all bots\n"); // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) @@ -737,20 +701,21 @@ void PlayerIsGone (int netnode, int netconsole) { Net_Arbitrator = i; players[i].settings_controller = true; - Printf ("%s is the new arbitrator\n", players[i].userinfo.GetName()); + Printf("%s is the new arbitrator\n", players[i].userinfo.GetName()); break; } } - if (debugfile && NetMode == NET_PacketServer) + } + + if (debugfile && NetMode == NET_PacketServer) + { + if (Net_Arbitrator == consoleplayer) { - if (Net_Arbitrator == consoleplayer) - { - fprintf (debugfile, "I am the new master!\n"); - } - else - { - fprintf (debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]); - } + fprintf(debugfile, "I am the new master!\n"); + } + else + { + fprintf(debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]); } } diff --git a/src/d_player.h b/src/d_player.h index f50db97b1..4ee147dfe 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -176,7 +176,8 @@ typedef enum PST_LIVE, // Playing or camping. PST_DEAD, // Dead on the ground, view follows killer. PST_REBORN, // Ready to restart/respawn??? - PST_ENTER // [BC] Entered the game + PST_ENTER, // [BC] Entered the game + PST_GONE // Player has left the game } playerstate_t; diff --git a/src/g_game.cpp b/src/g_game.cpp index 2058c2df0..95b0f587d 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -76,6 +76,7 @@ #include "d_net.h" #include "d_event.h" #include "p_acs.h" +#include "p_effect.h" #include "m_joy.h" #include "farchive.h" #include "r_renderer.h" @@ -1013,10 +1014,16 @@ void G_Ticker () // do player reborns if needed for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && - (players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER)) + if (playeringame[i]) { - G_DoReborn (i, false); + if ((players[i].playerstate == PST_GONE)) + { + G_DoPlayerPop(i); + } + if ((players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER)) + { + G_DoReborn(i, false); + } } } @@ -1658,6 +1665,56 @@ void G_DoReborn (int playernum, bool freshbot) } } +// +// G_DoReborn +// +void G_DoPlayerPop(int playernum) +{ + playeringame[playernum] = false; + + if (deathmatch) + { + Printf("%s left the game with %d frags\n", + players[playernum].userinfo.GetName(), + players[playernum].fragcount); + } + else + { + Printf("%s left the game\n", players[playernum].userinfo.GetName()); + } + + // [RH] Revert each player to their own view if spying through the player who left + for (int ii = 0; ii < MAXPLAYERS; ++ii) + { + if (playeringame[ii] && players[ii].camera == players[playernum].mo) + { + players[ii].camera = players[ii].mo; + if (ii == consoleplayer && StatusBar != NULL) + { + StatusBar->AttachToPlayer(&players[ii]); + } + } + } + + // [RH] Make the player disappear + FBehavior::StaticStopMyScripts(players[playernum].mo); + if (players[playernum].mo != NULL) + { + P_DisconnectEffect(players[playernum].mo); + players[playernum].mo->player = NULL; + players[playernum].mo->Destroy(); + if (!(players[playernum].mo->ObjectFlags & OF_EuthanizeMe)) + { // We just destroyed a morphed player, so now the original player + // has taken their place. Destroy that one too. + players[playernum].mo->Destroy(); + } + players[playernum].mo = NULL; + players[playernum].camera = NULL; + } + // [RH] Let the scripts know the player left + FBehavior::StaticStartTypedScripts(SCRIPT_Disconnect, NULL, true, playernum); +} + void G_ScreenShot (char *filename) { shotfile = filename; diff --git a/src/g_game.h b/src/g_game.h index 051be86b1..4714d8b55 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -81,6 +81,7 @@ enum EFinishLevelType void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags); void G_DoReborn (int playernum, bool freshbot); +void G_DoPlayerPop(int playernum); // Adds pitch to consoleplayer's viewpitch and clamps it void G_AddViewPitch (int look); From 0c1fde81ad9097421040cfa5445ed987c5395e7a Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 18:32:32 +1300 Subject: [PATCH 03/10] Remove PacketServer auto select PacketServer has the tendency to amplify netgame latency times, so it really shouldn't be auto selected. Added a notice in case it's used. --- src/d_net.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 0e057369c..f15bdf45d 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -697,7 +697,7 @@ void PlayerIsGone (int netnode, int netconsole) // Pick a new network arbitrator for (int i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && !players[i].isbot) + if (i != netconsole && playeringame[i] && !players[i].isbot) { Net_Arbitrator = i; players[i].settings_controller = true; @@ -1725,10 +1725,19 @@ void D_CheckNetGame (void) resendto[i] = 0; // which tic to start sending } + // Packet server has proven to be rather slow over the internet. Print a warning about it. + v = Args->CheckValue("-netmode"); + if (v != NULL && (atoi(v) != 0)) + { + Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is unreliable and is prone to running too slow on some internet configurations." + "\nIf the game is running well below excpected speeds, use netmode 0 (P2P) instead.\n"); + } + // I_InitNetwork sets doomcom and netgame if (I_InitNetwork ()) { - NetMode = NET_PacketServer; + // For now, stop auto selecting PacketServer, as it's more likely to cause confusion. + //NetMode = NET_PacketServer; } if (doomcom.id != DOOMCOM_ID) { From c9d603d1c3313b208d83fc9a3c5a3de54f1c33a3 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 18:52:56 +1300 Subject: [PATCH 04/10] Unreliable isn't exactly the problem --- src/d_net.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index f15bdf45d..bc4bc7352 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1729,7 +1729,7 @@ void D_CheckNetGame (void) v = Args->CheckValue("-netmode"); if (v != NULL && (atoi(v) != 0)) { - Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is unreliable and is prone to running too slow on some internet configurations." + Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is prone to running too slow on some internet configurations." "\nIf the game is running well below excpected speeds, use netmode 0 (P2P) instead.\n"); } From 71753743504660e73a25ec29c6328a0818f8fd42 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Sun, 5 Oct 2014 19:46:34 +1300 Subject: [PATCH 05/10] Keep gateway sockets open during handshake --- src/i_net.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/i_net.cpp b/src/i_net.cpp index 3ec9d7881..6fdcb52ba 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -110,6 +110,7 @@ const char *neterror (void); enum { PRE_CONNECT, // Sent from guest to host for initial connection + PRE_KEEPALIVE, PRE_DISCONNECT, // Sent from guest that aborts the game PRE_ALLHERE, // Sent from host to guest when everybody has connected PRE_CONACK, // Sent from host to guest to acknowledge PRE_CONNECT receipt @@ -548,10 +549,15 @@ bool Host_CheckForConnects (void *userdata) SendConAck (doomcom.numnodes, numplayers); } break; + + case PRE_KEEPALIVE: + break; } } if (doomcom.numnodes < numplayers) { + // Send message to everyone as a keepalive + SendConAck(doomcom.numnodes, numplayers); return false; } @@ -822,6 +828,10 @@ bool Guest_WaitForOthers (void *userdata) } } + packet.Fake = PRE_FAKE; + packet.Message = PRE_KEEPALIVE; + PreSend(&packet, 2, &sendaddress[1]); + return false; } From e1b8b4a8716e92316d4228469913ab2deb6dcf2b Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 6 Oct 2014 15:27:13 +1300 Subject: [PATCH 06/10] Started work on prediction lerping --- src/p_user.cpp | 67 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 3bbebaffb..e7e130e56 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -63,6 +63,22 @@ static FRandom pr_skullpop ("SkullPop"); // Variables for prediction CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +#define LERPSCALE 0.05 + +struct PredictCheck +{ + int gametic; + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t pitch; + fixed_t yaw; + bool onground; +} static PredictionResults[BACKUPTICS], PredictionResult_Last; +static int PredictionLerptics; +static int PredictionMaxLerptics; + static player_t PredictionPlayerBackup; static BYTE PredictionActorBackup[sizeof(AActor)]; static TArray PredictionTouchingSectorsBackup; @@ -1882,6 +1898,7 @@ void P_MovePlayer (player_t *player) else { mo->angle += cmd->ucmd.yaw << 16; + Printf("%d\n", cmd->ucmd.yaw); } player->onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (player->cheats & CF_NOCLIP2); @@ -2648,7 +2665,7 @@ void P_PredictPlayer (player_t *player) player->mo == NULL || player != &players[consoleplayer] || player->playerstate != PST_LIVE || - !netgame || + //!netgame || /*player->morphTics ||*/ (player->cheats & CF_PREDICTING)) { @@ -2665,8 +2682,8 @@ void P_PredictPlayer (player_t *player) // Save original values for restoration later PredictionPlayerBackup = *player; - AActor *act = player->mo; - memcpy (PredictionActorBackup, &act->x, sizeof(AActor)-((BYTE *)&act->x-(BYTE *)act)); + APlayerPawn *act = player->mo; + memcpy(PredictionActorBackup, &act->x, sizeof(APlayerPawn) - ((BYTE *)&act->x - (BYTE *)act)); act->flags &= ~MF_PICKUP; act->flags2 &= ~MF2_PUSHWALL; @@ -2732,7 +2749,49 @@ void P_PredictPlayer (player_t *player) player->cmd = localcmds[i % LOCALCMDTICS]; P_PlayerThink (player); player->mo->Tick (); + + if (PredictionResults[i % BACKUPTICS].gametic && i == PredictionResults[i % BACKUPTICS].gametic && !NoInterpolateOld && PredictionLerptics >= PredictionMaxLerptics) + { + if (PredictionResults[i % BACKUPTICS].x != player->mo->x || + PredictionResults[i % BACKUPTICS].y != player->mo->y || + (PredictionResults[i % BACKUPTICS].z != player->mo->z && PredictionResults[i % BACKUPTICS].onground && player->onground)) + // If the player was always on the ground, they might be on a lift, and lerping would be disruptive on z height changes alone + { + PredictionLerptics = 0; + PredictionMaxLerptics = (maxtic - gametic); + } + } + + PredictionResults[i % BACKUPTICS].gametic = i; + PredictionResults[i % BACKUPTICS].x = player->mo->x; + PredictionResults[i % BACKUPTICS].y = player->mo->y; + PredictionResults[i % BACKUPTICS].z = player->mo->z; + PredictionResults[i % BACKUPTICS].onground = player->onground; } + + if (PredictionLerptics < PredictionMaxLerptics) + { + PredictionLerptics++; + FVector3 pointold, pointnew, step, difference, result; + pointold.X = FIXED2FLOAT(PredictionResult_Last.x); // Old player pos + pointold.Y = FIXED2FLOAT(PredictionResult_Last.y); + pointold.Z = FIXED2FLOAT(PredictionResult_Last.z); + pointnew.X = FIXED2FLOAT(player->mo->x); // New player pos + pointnew.Y = FIXED2FLOAT(player->mo->y); + pointnew.Z = FIXED2FLOAT(player->mo->z); + + difference = pointnew - pointold; + step = difference / PredictionMaxLerptics; + result = step * PredictionLerptics; + result += pointold; + + player->mo->x = FLOAT2FIXED(result.X); + player->mo->y = FLOAT2FIXED(result.Y); + player->mo->z = FLOAT2FIXED(result.Z); + Printf("Lerped! x%f y%f z%f\n", result.X, result.Y, result.Z); + } + if (PredictionLerptics >= PredictionMaxLerptics) + PredictionResult_Last = PredictionResults[(maxtic - 1) % BACKUPTICS]; } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); @@ -2757,7 +2816,7 @@ void P_UnPredictPlayer () player->camera = savedcamera; act->UnlinkFromWorld(); - memcpy(&act->x, PredictionActorBackup, sizeof(AActor)-((BYTE *)&act->x - (BYTE *)act)); + memcpy(&act->x, PredictionActorBackup, sizeof(APlayerPawn) - ((BYTE *)&act->x - (BYTE *)act)); // The blockmap ordering needs to remain unchanged, too. // Restore sector links and refrences. From 3e6ad8c1a8944800ad928b6f9b534150eadefbd0 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 00:29:15 +1300 Subject: [PATCH 07/10] Further work on prediction lerping --- src/g_game.cpp | 6 +++ src/p_local.h | 1 + src/p_user.cpp | 111 +++++++++++++++++++++----------------- wadsrc/static/menudef.txt | 2 + 4 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 95b0f587d..9020f2da1 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1635,6 +1635,12 @@ void G_DoReborn (int playernum, bool freshbot) } else { + // Old lerp data needs to go + if (playernum == consoleplayer) + { + P_PredictionLerpReset(); + } + // respawn at the start // first disassociate the corpse if (players[playernum].mo) diff --git a/src/p_local.h b/src/p_local.h index 8ba1dd40b..2dc8c773e 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -105,6 +105,7 @@ void P_FallingDamage (AActor *ent); void P_PlayerThink (player_t *player); void P_PredictPlayer (player_t *player); void P_UnPredictPlayer (); +void P_PredictionLerpReset(); // // P_MOBJ diff --git a/src/p_user.cpp b/src/p_user.cpp index e7e130e56..7d93f6f24 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -64,20 +64,25 @@ static FRandom pr_skullpop ("SkullPop"); CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -#define LERPSCALE 0.05 +CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + P_PredictionLerpReset(); +} +CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0.1) + self = 0.1; + P_PredictionLerpReset(); +} -struct PredictCheck +struct PredictPos { int gametic; - fixed_t x; - fixed_t y; - fixed_t z; + FVector3 point; fixed_t pitch; fixed_t yaw; - bool onground; -} static PredictionResults[BACKUPTICS], PredictionResult_Last; +} static PredictionLerpFrom, PredictionLerpResult, PredictionLast; static int PredictionLerptics; -static int PredictionMaxLerptics; static player_t PredictionPlayerBackup; static BYTE PredictionActorBackup[sizeof(AActor)]; @@ -1898,7 +1903,6 @@ void P_MovePlayer (player_t *player) else { mo->angle += cmd->ucmd.yaw << 16; - Printf("%d\n", cmd->ucmd.yaw); } player->onground = (mo->z <= mo->floorz) || (mo->flags2 & MF2_ONMOBJ) || (mo->BounceFlags & BOUNCE_MBF) || (player->cheats & CF_NOCLIP2); @@ -2655,6 +2659,21 @@ void P_PlayerThink (player_t *player) } } +void P_PredictionLerpReset() +{ + PredictionLerptics = PredictionLast.gametic = PredictionLerpFrom.gametic = PredictionLerpResult.gametic = 0; +} + +bool P_LerpCalculate(FVector3 from, FVector3 to, FVector3 &result, float scale) +{ + result = to - from; + result *= scale; + result = result + from; + FVector3 delta = result - to; + + return (delta.LengthSquared() > cl_predict_lerpthreshold); +} + void P_PredictPlayer (player_t *player) { int maxtic; @@ -2740,7 +2759,7 @@ void P_PredictPlayer (player_t *player) } act->BlockNode = NULL; - bool NoInterpolateOld = R_GetViewInterpolationStatus(); + bool CanLerp = (cl_predict_lerpscale > 0), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { if (!NoInterpolateOld) @@ -2750,48 +2769,44 @@ void P_PredictPlayer (player_t *player) P_PlayerThink (player); player->mo->Tick (); - if (PredictionResults[i % BACKUPTICS].gametic && i == PredictionResults[i % BACKUPTICS].gametic && !NoInterpolateOld && PredictionLerptics >= PredictionMaxLerptics) + if (CanLerp && PredictionLast.gametic > 0 && i == PredictionLast.gametic && !NoInterpolateOld) { - if (PredictionResults[i % BACKUPTICS].x != player->mo->x || - PredictionResults[i % BACKUPTICS].y != player->mo->y || - (PredictionResults[i % BACKUPTICS].z != player->mo->z && PredictionResults[i % BACKUPTICS].onground && player->onground)) - // If the player was always on the ground, they might be on a lift, and lerping would be disruptive on z height changes alone - { - PredictionLerptics = 0; - PredictionMaxLerptics = (maxtic - gametic); - } + // Z is not compared as lifts will alter this with no apparent change + DoLerp = (PredictionLast.point.X != FIXED2FLOAT(player->mo->x) || + PredictionLast.point.Y != FIXED2FLOAT(player->mo->y)); + } + } + + if (CanLerp) + { + if (DoLerp) + { + // If lerping is already in effect, use the previous camera postion so the view doesn't suddenly snap + PredictionLerpFrom = (PredictionLerptics == 0) ? PredictionLast : PredictionLerpResult; + PredictionLerptics = 1; } - PredictionResults[i % BACKUPTICS].gametic = i; - PredictionResults[i % BACKUPTICS].x = player->mo->x; - PredictionResults[i % BACKUPTICS].y = player->mo->y; - PredictionResults[i % BACKUPTICS].z = player->mo->z; - PredictionResults[i % BACKUPTICS].onground = player->onground; + PredictionLast.gametic = maxtic - 1; + PredictionLast.point.X = FIXED2FLOAT(player->mo->x); + PredictionLast.point.Y = FIXED2FLOAT(player->mo->y); + PredictionLast.point.Z = FIXED2FLOAT(player->mo->z); + + if (PredictionLerptics > 0) + { + if (PredictionLerpFrom.gametic > 0 && + P_LerpCalculate(PredictionLerpFrom.point, PredictionLast.point, PredictionLerpResult.point, (float)PredictionLerptics * cl_predict_lerpscale)) + { + PredictionLerptics++; + player->mo->x = FLOAT2FIXED(PredictionLerpResult.point.X); + player->mo->y = FLOAT2FIXED(PredictionLerpResult.point.Y); + player->mo->z = FLOAT2FIXED(PredictionLerpResult.point.Z); + } + else + { + PredictionLerptics = 0; + } + } } - - if (PredictionLerptics < PredictionMaxLerptics) - { - PredictionLerptics++; - FVector3 pointold, pointnew, step, difference, result; - pointold.X = FIXED2FLOAT(PredictionResult_Last.x); // Old player pos - pointold.Y = FIXED2FLOAT(PredictionResult_Last.y); - pointold.Z = FIXED2FLOAT(PredictionResult_Last.z); - pointnew.X = FIXED2FLOAT(player->mo->x); // New player pos - pointnew.Y = FIXED2FLOAT(player->mo->y); - pointnew.Z = FIXED2FLOAT(player->mo->z); - - difference = pointnew - pointold; - step = difference / PredictionMaxLerptics; - result = step * PredictionLerptics; - result += pointold; - - player->mo->x = FLOAT2FIXED(result.X); - player->mo->y = FLOAT2FIXED(result.Y); - player->mo->z = FLOAT2FIXED(result.Z); - Printf("Lerped! x%f y%f z%f\n", result.X, result.Y, result.Z); - } - if (PredictionLerptics >= PredictionMaxLerptics) - PredictionResult_Last = PredictionResults[(maxtic - 1) % BACKUPTICS]; } extern msecnode_t *P_AddSecnode (sector_t *s, AActor *thing, msecnode_t *nextnode); diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 6d313faf1..627c11385 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1612,6 +1612,8 @@ OptionMenu NetworkOptions StaticText "Local options", 1 Option "Movement prediction", "cl_noprediction", "OffOn" Option "Predict line actions", "cl_predict_specials", "OnOff" + Option "Prediction Lerp Scale", "cl_predict_lerpscale", 0.0, 0.5, 0.01 + Option "Lerp Threshold", "cl_predict_lerpthreshold", 0.1, 16.0, 0.1 StaticText " " StaticText "Host options", 1 Option "Extra Tics", "net_extratic", "ExtraTicMode" From 98904039b77a1c8afa0eb2d2477ea908b72f766e Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 16:32:49 +1300 Subject: [PATCH 08/10] Final work on prediction lerping Added cl_predict_lerpscale and cl_predict_lerpthreshold Added options in menudef Made sure that lerping cannot extrapolate or run on small scales Lerping gets reset when rendering interpolation does or respawn --- src/p_user.cpp | 19 ++++++++++++------- wadsrc/static/menudef.txt | 4 ++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index 7d93f6f24..abf9256f7 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -64,14 +64,14 @@ static FRandom pr_skullpop ("SkullPop"); CVAR (Bool, cl_noprediction, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, cl_predict_lerpscale, 0.05f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { P_PredictionLerpReset(); } -CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Float, cl_predict_lerpthreshold, 2.00f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - if (self < 0.1) - self = 0.1; + if (self < 0.1f) + self = 0.1f; P_PredictionLerpReset(); } @@ -2671,7 +2671,8 @@ bool P_LerpCalculate(FVector3 from, FVector3 to, FVector3 &result, float scale) result = result + from; FVector3 delta = result - to; - return (delta.LengthSquared() > cl_predict_lerpthreshold); + // As a fail safe, assume extrapolation is the threshold. + return (delta.LengthSquared() > cl_predict_lerpthreshold && scale <= 1.00f); } void P_PredictPlayer (player_t *player) @@ -2759,7 +2760,8 @@ void P_PredictPlayer (player_t *player) } act->BlockNode = NULL; - bool CanLerp = (cl_predict_lerpscale > 0), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); + // Values too small to be usable for lerping can be considered "off". + bool CanLerp = !(cl_predict_lerpscale < 0.01f), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { if (!NoInterpolateOld) @@ -2779,7 +2781,10 @@ void P_PredictPlayer (player_t *player) if (CanLerp) { - if (DoLerp) + if (NoInterpolateOld) + P_PredictionLerpReset(); + + else if (DoLerp) { // If lerping is already in effect, use the previous camera postion so the view doesn't suddenly snap PredictionLerpFrom = (PredictionLerptics == 0) ? PredictionLast : PredictionLerpResult; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 627c11385..1fd8dfe2b 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1612,8 +1612,8 @@ OptionMenu NetworkOptions StaticText "Local options", 1 Option "Movement prediction", "cl_noprediction", "OffOn" Option "Predict line actions", "cl_predict_specials", "OnOff" - Option "Prediction Lerp Scale", "cl_predict_lerpscale", 0.0, 0.5, 0.01 - Option "Lerp Threshold", "cl_predict_lerpthreshold", 0.1, 16.0, 0.1 + Slider "Prediction Lerp Scale", "cl_predict_lerpscale", 0.0, 0.5, 0.05 + Slider "Lerp Threshold", "cl_predict_lerpthreshold", 0.1, 16.0, 0.1 StaticText " " StaticText "Host options", 1 Option "Extra Tics", "net_extratic", "ExtraTicMode" From 3e4446e46b56eb3688491b7cd601b88fe5038237 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 16:49:53 +1300 Subject: [PATCH 09/10] Moved prediction lerp reset to cover all cases. --- src/g_game.cpp | 6 ------ src/p_mobj.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 9020f2da1..95b0f587d 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1635,12 +1635,6 @@ void G_DoReborn (int playernum, bool freshbot) } else { - // Old lerp data needs to go - if (playernum == consoleplayer) - { - P_PredictionLerpReset(); - } - // respawn at the start // first disassociate the corpse if (players[playernum].mo) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index d035e0117..1d97311eb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4246,6 +4246,12 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) if ((unsigned)playernum >= (unsigned)MAXPLAYERS || !playeringame[playernum]) return NULL; + // Old lerp data needs to go + if (playernum == consoleplayer) + { + P_PredictionLerpReset(); + } + p = &players[playernum]; if (p->cls == NULL) From 0cd736b48736d6bf8ded7e18acb8eacdc40baf29 Mon Sep 17 00:00:00 2001 From: Edward Richardson Date: Mon, 13 Oct 2014 17:09:29 +1300 Subject: [PATCH 10/10] Prevent lerping when tic duplication not 1 Network dup makes player prediction inaccurate --- src/p_user.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_user.cpp b/src/p_user.cpp index abf9256f7..b40e44424 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2685,7 +2685,7 @@ void P_PredictPlayer (player_t *player) player->mo == NULL || player != &players[consoleplayer] || player->playerstate != PST_LIVE || - //!netgame || + !netgame || /*player->morphTics ||*/ (player->cheats & CF_PREDICTING)) { @@ -2761,7 +2761,7 @@ void P_PredictPlayer (player_t *player) act->BlockNode = NULL; // Values too small to be usable for lerping can be considered "off". - bool CanLerp = !(cl_predict_lerpscale < 0.01f), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); + bool CanLerp = (!(cl_predict_lerpscale < 0.01f) && (ticdup == 1)), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { if (!NoInterpolateOld)