diff --git a/src/engine/client.h b/src/engine/client.h index c91c679bed4..8f8867695a0 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -393,6 +393,8 @@ class IGameClient : public IInterface virtual void ApplySkin7InfoFromSnapObj(const protocol7::CNetObj_De_ClientInfo *pObj, int ClientId) = 0; virtual int OnDemoRecSnap7(class CSnapshot *pFrom, class CSnapshot *pTo, int Conn) = 0; virtual int TranslateSnap(class CSnapshot *pSnapDstSix, class CSnapshot *pSnapSrcSeven, int Conn, bool Dummy) = 0; + + virtual bool CheckNewInput() = 0; }; void SnapshotRemoveExtraProjectileInfo(class CSnapshot *pSnap); diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 65d78ecaa73..51efa427449 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -2752,6 +2752,11 @@ void CClient::Update() // send input SendInput(); } + + if(g_Config.m_ClFastInput && GameClient()->CheckNewInput()) + { + Repredict = true; + } } // only do sane predictions @@ -5001,7 +5006,7 @@ void CClient::GetSmoothTick(int *pSmoothTick, float *pSmoothIntraTick, float Mix int64_t PredTime = m_PredictedTime.Get(time_get()); int64_t SmoothTime = clamp(GameTime + (int64_t)(MixAmount * (PredTime - GameTime)), GameTime, PredTime); - *pSmoothTick = (int)(SmoothTime * GameTickSpeed() / time_freq()) + 1; + *pSmoothTick = (int)(SmoothTime * GameTickSpeed() / time_freq()) + 1 + g_Config.m_ClFastInput; *pSmoothIntraTick = (SmoothTime - (*pSmoothTick - 1) * time_freq() / GameTickSpeed()) / (float)(time_freq() / GameTickSpeed()); } void CClient::GetSmoothFreezeTick(int *pSmoothTick, float *pSmoothIntraTick, float MixAmount) @@ -5012,7 +5017,7 @@ void CClient::GetSmoothFreezeTick(int *pSmoothTick, float *pSmoothIntraTick, flo int64_t LowestPredTime = clamp(PredTime, GameTime, UpperPredTime); int64_t SmoothTime = clamp(LowestPredTime + (int64_t)(MixAmount * (PredTime - LowestPredTime)), LowestPredTime, PredTime); - *pSmoothTick = (int)(SmoothTime * 50 / time_freq()) + 1; + *pSmoothTick = (int)(SmoothTime * 50 / time_freq()) + 1 + g_Config.m_ClFastInput; *pSmoothIntraTick = (SmoothTime - (*pSmoothTick - 1) * time_freq() / 50) / (float)(time_freq() / 50); } void CClient::AddWarning(const SWarning &Warning) diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp index 7bd626b6432..493c31c84a9 100644 --- a/src/game/client/components/controls.cpp +++ b/src/game/client/components/controls.cpp @@ -451,3 +451,38 @@ float CControls::GetMaxMouseDistance() const float MaxDistance = g_Config.m_ClDyncam ? g_Config.m_ClDyncamMaxDistance : g_Config.m_ClMouseMaxDistance; return minimum((FollowFactor != 0 ? CameraMaxDistance / FollowFactor + DeadZone : MaxDistance), MaxDistance); } + +bool CControls::CheckNewInput() +{ + CNetObj_PlayerInput TestInput = m_aInputData[g_Config.m_ClDummy]; + TestInput.m_Direction = 0; + if(m_aInputDirectionLeft[g_Config.m_ClDummy] && !m_aInputDirectionRight[g_Config.m_ClDummy]) + TestInput.m_Direction = -1; + if(!m_aInputDirectionLeft[g_Config.m_ClDummy] && m_aInputDirectionRight[g_Config.m_ClDummy]) + TestInput.m_Direction = 1; + + bool NewInput = false; + if(m_FastInput.m_Direction != TestInput.m_Direction) + NewInput = true; + if(m_FastInput.m_Hook != TestInput.m_Hook) + NewInput = true; + if(m_FastInput.m_Fire != TestInput.m_Fire) + NewInput = true; + if(m_FastInput.m_Jump != TestInput.m_Jump) + NewInput = true; + + if(g_Config.m_ClSubTickAiming) + { + TestInput.m_TargetX = (int)m_aMousePos[g_Config.m_ClDummy].x; + TestInput.m_TargetY = (int)m_aMousePos[g_Config.m_ClDummy].y; + TestInput.m_TargetX *= m_pClient->m_Camera.m_Zoom; + TestInput.m_TargetY *= m_pClient->m_Camera.m_Zoom; + } + + if (NewInput) + { + m_FastInput = TestInput; + return true; + } + return false; +} diff --git a/src/game/client/components/controls.h b/src/game/client/components/controls.h index ce6bc664dd1..c363ea7b06b 100644 --- a/src/game/client/components/controls.h +++ b/src/game/client/components/controls.h @@ -29,6 +29,8 @@ class CControls : public CComponent int m_aInputDirectionRight[NUM_DUMMIES]; int m_aShowHookColl[NUM_DUMMIES]; + CNetObj_PlayerInput m_FastInput; + CControls(); virtual int Sizeof() const override { return sizeof(*this); } @@ -42,5 +44,6 @@ class CControls : public CComponent int SnapInput(int *pData); void ClampMousePos(); void ResetInput(int Dummy); + bool CheckNewInput(); }; #endif diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 2a57c148e0f..f43f78608d1 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -2092,10 +2092,11 @@ void CGameClient::OnPredict() // predict // prediction actually happens here - for(int Tick = Client()->GameTick(g_Config.m_ClDummy) + 1; Tick <= Client()->PredGameTick(g_Config.m_ClDummy); Tick++) + int FinalTick = Client()->PredGameTick(g_Config.m_ClDummy) + g_Config.m_ClFastInput; + for(int Tick = Client()->GameTick(g_Config.m_ClDummy) + 1; Tick <= FinalTick; Tick++) { // fetch the previous characters - if(Tick == Client()->PredGameTick(g_Config.m_ClDummy)) + if(Tick == FinalTick) { m_PrevPredictedWorld.CopyWorld(&m_PredictedWorld); m_PredictedPrevChar = pLocalChar->GetCore(); @@ -2113,6 +2114,9 @@ void CGameClient::OnPredict() CNetObj_PlayerInput *pDummyInputData = !pDummyChar ? 0 : (CNetObj_PlayerInput *)Client()->GetInput(Tick, m_IsDummySwapping ^ 1); bool DummyFirst = pInputData && pDummyInputData && pDummyChar->GetCid() < pLocalChar->GetCid(); + if(g_Config.m_ClFastInput && Tick == FinalTick) + pInputData = &m_Controls.m_FastInput; + if(DummyFirst) pDummyChar->OnDirectInput(pDummyInputData); if(pInputData) @@ -2127,7 +2131,7 @@ void CGameClient::OnPredict() m_PredictedWorld.Tick(); // fetch the current characters - if(Tick == Client()->PredGameTick(g_Config.m_ClDummy)) + if(Tick == FinalTick) { m_PredictedChar = pLocalChar->GetCore(); for(int i = 0; i < MAX_CLIENTS; i++) @@ -2175,6 +2179,9 @@ void CGameClient::OnPredict() } } + if(g_Config.m_ClFastInput) + m_PredictedWorld.CopyWorld(&m_PrevPredictedWorld); + // detect mispredictions of other players and make corrections smoother when possible if(g_Config.m_ClAntiPingSmooth && Predict() && AntiPingPlayers() && m_NewTick && m_PredictedTick >= MIN_TICK && absolute(m_PredictedTick - Client()->PredGameTick(g_Config.m_ClDummy)) <= 1 && absolute(Client()->GameTick(g_Config.m_ClDummy) - Client()->PrevGameTick(g_Config.m_ClDummy)) <= 2) { @@ -2186,7 +2193,7 @@ void CGameClient::OnPredict() { if(!m_Snap.m_aCharacters[i].m_Active || i == m_Snap.m_LocalClientId || !m_aLastActive[i]) continue; - vec2 NewPos = (m_PredictedTick == Client()->PredGameTick(g_Config.m_ClDummy)) ? m_aClients[i].m_Predicted.m_Pos : m_aClients[i].m_PrevPredicted.m_Pos; + vec2 NewPos = (m_PredictedTick == FinalTick) ? m_aClients[i].m_Predicted.m_Pos : m_aClients[i].m_PrevPredicted.m_Pos; vec2 PredErr = (m_aLastPos[i] - NewPos) / (float)minimum(Client()->GetPredictionTime(), 200); if(in_range(length(PredErr), 0.05f, 5.f)) { @@ -2264,7 +2271,7 @@ void CGameClient::OnPredict() } } - m_PredictedTick = Client()->PredGameTick(g_Config.m_ClDummy); + m_PredictedTick = FinalTick; if(m_NewPredictedTick) m_Ghost.OnNewPredictedSnapshot(); @@ -4215,3 +4222,8 @@ int CGameClient::FindFirstMultiViewId() } return ClientId; } + +bool CGameClient::CheckNewInput() +{ + return m_Controls.CheckNewInput(); +} diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 46dd11cef96..8a12787b886 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -606,6 +606,8 @@ class CGameClient : public IGameClient CNetObjHandler *GetNetObjHandler() override; protocol7::CNetObjHandler *GetNetObjHandler7() override; + bool CheckNewInput() override; + void LoadGameSkin(const char *pPath, bool AsDir = false); void LoadEmoticonsSkin(const char *pPath, bool AsDir = false); void LoadParticlesSkin(const char *pPath, bool AsDir = false); diff --git a/src/game/tater_variables.h b/src/game/tater_variables.h index 9b96c9aeda0..3efc3cb04dc 100644 --- a/src/game/tater_variables.h +++ b/src/game/tater_variables.h @@ -55,7 +55,9 @@ MACRO_CONFIG_INT(ClShowSkinName, tc_skin_name, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG //MACRO_CONFIG_INT(ClFreeGhost, tc_freeghost, 0, 0, 1, CFGFLAG_CLIENT , "") -MACRO_CONFIG_INT(ClSmoothPredictionMargin, tc_prediction_margin_smooth, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Make's prediction margin transition smooth (reverts a ddnet change)") +MACRO_CONFIG_INT(ClSmoothPredictionMargin, tc_prediction_margin_smooth, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Makes prediction margin transition smooth (reverts a ddnet change)") + +MACRO_CONFIG_INT(ClFastInput, tc_fast_input, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Uses input for prediction up to 20ms faster") //Outline Variables