From 63cbb69797791a1490e4ae35b9643cacc4c749e0 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 13 Jul 2021 15:05:27 -0700 Subject: [PATCH 1/9] Update previous transform to prevent jitter from lerping Signed-off-by: puvvadar --- .../Code/Source/Components/NetworkTransformComponent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index e956245724..7305c1f94e 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -91,6 +91,7 @@ namespace Multiplayer blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor)); blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor)); GetTransformComponent()->SetWorldTM(blendTransform); + m_previousTransform = blendTransform; } } From 7950c2b54906b2976b44c097403eb8607042f3a6 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 15 Jul 2021 13:45:49 -0700 Subject: [PATCH 2/9] Add target host frame ID tracking to network transform Signed-off-by: puvvadar --- .../Components/NetworkTransformComponent.h | 4 +++ .../Components/NetworkTransformComponent.cpp | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index 5575de9b0d..c37c28db62 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -35,6 +35,8 @@ namespace Multiplayer void OnScaleChangedEvent(float scale); void OnResetCountChangedEvent(); + void UpdateTargetHostFrameId(); + AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity(); AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity(); @@ -44,6 +46,8 @@ namespace Multiplayer AZ::Event::Handler m_resetCountEventHandler; EntityPreRenderEvent::Handler m_entityPreRenderEventHandler; + + Multiplayer::HostFrameId m_targetHostFrameId = HostFrameId(0); }; class NetworkTransformComponentController diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index 7305c1f94e..54b24ccf10 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -60,18 +60,21 @@ namespace Multiplayer { m_previousTransform.SetRotation(m_targetTransform.GetRotation()); m_targetTransform.SetRotation(rotation); + UpdateTargetHostFrameId(); } void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation) { m_previousTransform.SetTranslation(m_targetTransform.GetTranslation()); m_targetTransform.SetTranslation(translation); + UpdateTargetHostFrameId(); } void NetworkTransformComponent::OnScaleChangedEvent(float scale) { m_previousTransform.SetUniformScale(m_targetTransform.GetUniformScale()); m_targetTransform.SetUniformScale(scale); + UpdateTargetHostFrameId(); } void NetworkTransformComponent::OnResetCountChangedEvent() @@ -82,16 +85,32 @@ namespace Multiplayer m_previousTransform = m_targetTransform; } + void NetworkTransformComponent::UpdateTargetHostFrameId() + { + HostFrameId currentHostFrameId = Multiplayer::GetNetworkTime()->GetHostFrameId(); + if (currentHostFrameId > m_targetHostFrameId) + { + m_targetHostFrameId = currentHostFrameId; + } + } + void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime, float blendFactor) { if (!HasController()) { AZ::Transform blendTransform; - blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor)); - blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor)); - blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor)); + if (Multiplayer::GetNetworkTime() && Multiplayer::GetNetworkTime()->GetHostFrameId() > m_targetHostFrameId) + { + m_previousTransform = m_targetTransform; + blendTransform = m_targetTransform; + } + else + { + blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor)); + blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor)); + blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor)); + } GetTransformComponent()->SetWorldTM(blendTransform); - m_previousTransform = blendTransform; } } From ebe326f6e9cbbd2cf49e6aa791494dcc86947a8e Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 20 Jul 2021 14:12:06 -0700 Subject: [PATCH 3/9] Add server side accounting for blend factor Signed-off-by: puvvadar --- .../Code/Include/Multiplayer/IMultiplayer.h | 2 ++ .../Multiplayer/NetworkInput/NetworkInput.h | 4 ++++ .../LocalPredictionPlayerInputComponent.cpp | 6 +++++- .../Code/Source/MultiplayerSystemComponent.cpp | 5 +++++ .../Code/Source/MultiplayerSystemComponent.h | 1 + .../Code/Source/NetworkInput/NetworkInput.cpp | 14 +++++++++++++- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 182173c464..35af0034e5 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -121,6 +121,8 @@ namespace Multiplayer //! @return the current server time in milliseconds virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0; + virtual float GetCurrentBlendFactor() const = 0; + //! Returns the network time instance bound to this multiplayer instance. //! @return pointer to the network time instance bound to this multiplayer instance virtual INetworkTime* GetNetworkTime() = 0; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h index 5d57ea6343..874ccad789 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h @@ -44,6 +44,9 @@ namespace Multiplayer AZ::TimeMs GetHostTimeMs() const; AZ::TimeMs& ModifyHostTimeMs(); + void SetHostBlendFactor(float hostBlendFactor); + float GetHostBlendFactor() const; + void AttachNetBindComponent(NetBindComponent* netBindComponent); bool Serialize(AzNetworking::ISerializer& serializer); @@ -72,6 +75,7 @@ namespace Multiplayer ClientInputId m_inputId = ClientInputId{ 0 }; HostFrameId m_hostFrameId = InvalidHostFrameId; AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 }; + float m_hostBlendFactor = 0.f; ConstNetworkEntityHandle m_owner; bool m_wasAttached = false; }; diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 0b4fa99111..da299401d8 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -154,9 +154,12 @@ namespace Multiplayer // Discard move input events, client may be speed hacking if (m_clientBankedTime < sv_MaxBankTimeWindowSec) { + // Client blends from previous frame to target so here we subtract blend factor to get to that state + const float adjustedBlendFactor = std::pow(0.2f, input.GetHostBlendFactor()); + const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * adjustedBlendFactor); m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary { - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, static_cast(clientInputRateSec)); } @@ -498,6 +501,7 @@ namespace Multiplayer input.SetClientInputId(m_clientInputId); input.SetHostFrameId(networkTime->GetHostFrameId()); input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs()); + input.SetHostBlendFactor(multiplayer->GetCurrentBlendFactor()); // Allow components to form the input for this frame GetNetBindComponent()->CreateInput(input, inputRate); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 0100cfe3d2..16acf45872 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -802,6 +802,11 @@ namespace Multiplayer } } + float MultiplayerSystemComponent::GetCurrentBlendFactor() const + { + return m_renderBlendFactor; + } + INetworkTime* MultiplayerSystemComponent::GetNetworkTime() { return &m_networkTime; diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 7977a39443..5aaa52a7bd 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -112,6 +112,7 @@ namespace Multiplayer void Terminate(AzNetworking::DisconnectReason reason) override; void SendReadyForEntityUpdates(bool readyForEntityUpdates) override; AZ::TimeMs GetCurrentHostTimeMs() const override; + float GetCurrentBlendFactor() const override; INetworkTime* GetNetworkTime() override; INetworkEntityManager* GetNetworkEntityManager() override; void SetFilterEntityManager(IFilterEntityManager* entityFilter) override; diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp index 75889d9f83..33b5a535e0 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp @@ -75,6 +75,16 @@ namespace Multiplayer return m_hostTimeMs; } + void NetworkInput::SetHostBlendFactor(float hostBlendFactor) + { + m_hostBlendFactor = hostBlendFactor; + } + + float NetworkInput::GetHostBlendFactor() const + { + return m_hostBlendFactor; + } + void NetworkInput::AttachNetBindComponent(NetBindComponent* netBindComponent) { m_wasAttached = true; @@ -90,7 +100,8 @@ namespace Multiplayer { if (!serializer.Serialize(m_inputId, "InputId") || !serializer.Serialize(m_hostTimeMs, "HostTimeMs") - || !serializer.Serialize(m_hostFrameId, "HostFrameId")) + || !serializer.Serialize(m_hostFrameId, "HostFrameId") + || !serializer.Serialize(m_hostBlendFactor, "HostBlendFactor")) { return false; } @@ -163,6 +174,7 @@ namespace Multiplayer m_inputId = rhs.m_inputId; m_hostFrameId = rhs.m_hostFrameId; m_hostTimeMs = rhs.m_hostTimeMs; + m_hostBlendFactor = rhs.m_hostBlendFactor; m_componentInputs.resize(rhs.m_componentInputs.size()); for (int32_t i = 0; i < rhs.m_componentInputs.size(); ++i) { From 69d5f64bb7c209e8bcd3b680ec6f1dc371d3294b Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 20 Jul 2021 14:21:00 -0700 Subject: [PATCH 4/9] Add function documentation for GetCurrentBlendFactor Signed-off-by: puvvadar --- Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 35af0034e5..23885e6c64 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -121,6 +121,9 @@ namespace Multiplayer //! @return the current server time in milliseconds virtual AZ::TimeMs GetCurrentHostTimeMs() const = 0; + //! Returns the current blend factor for client side interpolation + //! This value is only relevant on the client and is used to smooth between host frames + //! @return the current blend factor virtual float GetCurrentBlendFactor() const = 0; //! Returns the network time instance bound to this multiplayer instance. From b55bad496d2e6516bac233bc148a269002774733 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 22 Jul 2021 13:49:05 -0700 Subject: [PATCH 5/9] Adding rewindable mechanisms to support interpolation Signed-off-by: puvvadar --- .../Code/Include/Multiplayer/IMultiplayer.h | 4 +++- .../Include/Multiplayer/MultiplayerTypes.h | 3 +++ .../Multiplayer/NetworkTime/INetworkTime.h | 8 ++++++++ .../Multiplayer/NetworkTime/RewindableObject.h | 4 ++++ .../NetworkTime/RewindableObject.inl | 6 ++++++ .../LocalPredictionPlayerInputComponent.cpp | 11 ++++++----- .../Code/Source/NetworkTime/NetworkTime.cpp | 11 +++++++++++ .../Code/Source/NetworkTime/NetworkTime.h | 3 +++ .../Code/Tests/RewindableContainerTests.cpp | 8 ++++---- .../Code/Tests/RewindableObjectTests.cpp | 18 +++++++++--------- 10 files changed, 57 insertions(+), 19 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 23885e6c64..17d2cffafd 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -186,18 +186,20 @@ namespace Multiplayer class ScopedAlterTime final { public: - inline ScopedAlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId connectionId) + inline ScopedAlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId connectionId) { INetworkTime* time = GetNetworkTime(); m_previousHostFrameId = time->GetHostFrameId(); m_previousHostTimeMs = time->GetHostTimeMs(); m_previousRewindConnectionId = time->GetRewindingConnectionId(); time->AlterTime(frameId, timeMs, connectionId); + time->AlterBlendFactor(blendFactor); } inline ~ScopedAlterTime() { INetworkTime* time = GetNetworkTime(); time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId); + time->AlterBlendFactor(DefaultBlendFactor); } private: HostFrameId m_previousHostFrameId = InvalidHostFrameId; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h index 9f81ca97a9..4fdf428e37 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h @@ -21,6 +21,9 @@ namespace Multiplayer //! The default number of rewindable samples for us to store. static constexpr uint32_t RewindHistorySize = 128; + //! The default blend factor for ScopedAlterTime + static constexpr float DefaultBlendFactor = 1.f; + AZ_TYPE_SAFE_INTEGRAL(HostId, uint32_t); static constexpr HostId InvalidHostId = static_cast(-1); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h index c88c971636..5e5e7aab25 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h @@ -42,6 +42,10 @@ namespace Multiplayer //! @return the hosts current timeMs virtual AZ::TimeMs GetHostTimeMs() const = 0; + //! Retrieves the hosts current blend factor (may be rewound on the server during backward reconciliation). + //! @return the hosts current blend factor + virtual float GetHostBlendFactor() const = 0; + //! Get the controlling connection that may be currently altering global game time. //! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics //! @return the ConnectionId of the connection requesting the rewind operation @@ -59,6 +63,10 @@ namespace Multiplayer //! @param rewindConnectionId the rewinding ConnectionId virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0; + //! Alters the current Host blend factor. Used to drive interpolation in rewound states. + //! @param blendFactor the blend factor to use + virtual void AlterBlendFactor(float blendFactor) = 0; + //! Syncs all entities contained within a volume to the current rewind state. //! @param rewindVolume the volume to rewind entities within (needed for physics entities) virtual void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) = 0; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h index 796eec5412..2eff6d8bb0 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h @@ -59,6 +59,10 @@ namespace Multiplayer //! @return value in const base type form const BASE_TYPE& Get() const; + //! Const base type retriever for one host frame behind Get(). + //! @return value in const base type form + const BASE_TYPE& GetPrevious() const; + //! Base type retriever. //! @return value in base type form BASE_TYPE& Modify(); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl index dc8d98fb45..7027069b27 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.inl @@ -65,6 +65,12 @@ namespace Multiplayer return GetValueForTime(GetCurrentTimeForProperty()); } + template + inline const BASE_TYPE& RewindableObject::GetPrevious() const + { + return GetValueForTime(GetCurrentTimeForProperty() - HostFrameId(1)); + } + template inline BASE_TYPE& RewindableObject::Modify() { diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index da299401d8..15945b12ff 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -155,11 +155,12 @@ namespace Multiplayer if (m_clientBankedTime < sv_MaxBankTimeWindowSec) { // Client blends from previous frame to target so here we subtract blend factor to get to that state - const float adjustedBlendFactor = std::pow(0.2f, input.GetHostBlendFactor()); + const float blendFactor = AZStd::max(0.f, input.GetHostBlendFactor()); + const float adjustedBlendFactor = std::pow(0.2f, blendFactor); const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * adjustedBlendFactor); m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary { - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, static_cast(clientInputRateSec)); } @@ -313,7 +314,7 @@ namespace Multiplayer ++ModifyLastInputId(); input.SetClientInputId(GetLastInputId()); - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), DefaultBlendFactor, invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, clientInputRateSec); AZLOG @@ -393,7 +394,7 @@ namespace Multiplayer { // Reprocess the input for this frame NetworkInput& input = m_inputHistory[replayIndex]; - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), DefaultBlendFactor, invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, clientInputRateSec); AZLOG @@ -576,7 +577,7 @@ namespace Multiplayer NetworkInput& input = m_lastInputReceived[0]; { - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), AzNetworking::InvalidConnectionId); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), DefaultBlendFactor, AzNetworking::InvalidConnectionId); GetNetBindComponent()->ProcessInput(input, inputRate); } diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp index 88ffa0b5d7..b15125b5d2 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp @@ -54,6 +54,11 @@ namespace Multiplayer return m_hostTimeMs; } + float NetworkTime::GetHostBlendFactor() const + { + return m_hostBlendFactor; + } + AzNetworking::ConnectionId NetworkTime::GetRewindingConnectionId() const { return m_rewindingConnectionId; @@ -71,6 +76,11 @@ namespace Multiplayer m_rewindingConnectionId = rewindConnectionId; } + void NetworkTime::AlterBlendFactor(float blendFactor) + { + m_hostBlendFactor = blendFactor; + } + void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) { // Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities @@ -94,6 +104,7 @@ namespace Multiplayer if (networkTransform != nullptr) { + // We're not presently factoring in interpolated position here const AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); // Get the rewound position const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h index 53c9540843..f6b2907c92 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h @@ -29,9 +29,11 @@ namespace Multiplayer HostFrameId GetUnalteredHostFrameId() const override; void IncrementHostFrameId() override; AZ::TimeMs GetHostTimeMs() const override; + float GetHostBlendFactor() const override; AzNetworking::ConnectionId GetRewindingConnectionId() const override; HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override; void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) override; + void AlterBlendFactor(float blendFactor) override; void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) override; void ClearRewoundEntities() override; //! @} @@ -43,6 +45,7 @@ namespace Multiplayer HostFrameId m_hostFrameId = HostFrameId{ 0 }; HostFrameId m_unalteredFrameId = HostFrameId{ 0 }; AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 }; + float m_hostBlendFactor = DefaultBlendFactor; AzNetworking::ConnectionId m_rewindingConnectionId = AzNetworking::InvalidConnectionId; }; } diff --git a/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp index 2e3a65a5e5..ae56fdcf39 100644 --- a/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp +++ b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp @@ -42,7 +42,7 @@ namespace UnitTest // Test rewind for all pushed values and overall size for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) { - Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(idx + 1, test.size()); EXPECT_EQ(idx, test.back()); } @@ -69,9 +69,9 @@ namespace UnitTest EXPECT_TRUE(test.empty()); // Test rewind for pop_back and clear - Multiplayer::ScopedAlterTime pop_time(static_cast(RewindableContainerSize), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime pop_time(static_cast(RewindableContainerSize), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(RewindableContainerSize - 1, test.size()); - Multiplayer::ScopedAlterTime clear_time(static_cast(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime clear_time(static_cast(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(0, test.size()); // Test copy_values and resize_no_construct @@ -99,7 +99,7 @@ namespace UnitTest // Test rewind for all values and overall size for (uint32_t idx = 1; idx <= RewindableContainerSize; ++idx) { - Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(idx), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); for (uint32_t testIdx = 0; testIdx < RewindableContainerSize; ++testIdx) { if (testIdx < idx) diff --git a/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp b/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp index 472a1ce148..e992eb6848 100644 --- a/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp +++ b/Gems/Multiplayer/Code/Tests/RewindableObjectTests.cpp @@ -38,7 +38,7 @@ namespace UnitTest for (uint32_t i = 0; i < 16; ++i) { - Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(i, test); } @@ -51,7 +51,7 @@ namespace UnitTest for (uint32_t i = 16; i < 48; ++i) { - Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(i, test); } } @@ -69,7 +69,7 @@ namespace UnitTest { // Note that we didn't actually set any value for time rewindableBufferFrames, so we're testing fetching a value past the last time set - Multiplayer::ScopedAlterTime time(static_cast(RewindableBufferFrames), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(RewindableBufferFrames), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(RewindableBufferFrames - 1, test); } } @@ -92,7 +92,7 @@ namespace UnitTest for (uint32_t i = 0; i < RewindableBufferFrames; ++i) { - Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); const Object& value = test; EXPECT_EQ(value.value, i); } @@ -101,19 +101,19 @@ namespace UnitTest TEST_F(RewindableObjectTests, TestBackfillOnLargeTimestep) { Multiplayer::RewindableObject test(0); - Multiplayer::ScopedAlterTime time1(static_cast(0), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time1(static_cast(0), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); test = 1; - Multiplayer::ScopedAlterTime time2(static_cast(31), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time2(static_cast(31), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); test = 2; for (uint32_t i = 0; i < 31; ++i) { - Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(1, test); } - Multiplayer::ScopedAlterTime time3(static_cast(31), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time3(static_cast(31), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(2, test); } @@ -129,7 +129,7 @@ namespace UnitTest for (uint32_t i = 0; i < 1000; ++i) { - Multiplayer::ScopedAlterTime time(static_cast(1000 - i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + Multiplayer::ScopedAlterTime time(static_cast(1000 - i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId); EXPECT_EQ(1000, test); } } From b4e88010957cc0ce0c09b535402d9bd6c04b4949 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Fri, 23 Jul 2021 14:51:53 -0700 Subject: [PATCH 6/9] Account for new blend factor calc and updated ScopedAlterTime usages Signed-off-by: puvvadar --- Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h | 4 +++- .../Code/Include/Multiplayer/NetworkTime/INetworkTime.h | 2 +- .../Components/LocalPredictionPlayerInputComponent.cpp | 9 ++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index cf15e8c9b7..4f714068db 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -194,18 +194,20 @@ namespace Multiplayer m_previousHostTimeMs = time->GetHostTimeMs(); m_previousRewindConnectionId = time->GetRewindingConnectionId(); time->AlterTime(frameId, timeMs, connectionId); + m_previousBlendFactor = time->GetHostBlendFactor(); time->AlterBlendFactor(blendFactor); } inline ~ScopedAlterTime() { INetworkTime* time = GetNetworkTime(); time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId); - time->AlterBlendFactor(DefaultBlendFactor); + time->AlterBlendFactor(m_previousBlendFactor); } private: HostFrameId m_previousHostFrameId = InvalidHostFrameId; AZ::TimeMs m_previousHostTimeMs = AZ::TimeMs{ 0 }; AzNetworking::ConnectionId m_previousRewindConnectionId = AzNetworking::InvalidConnectionId; + float m_previousBlendFactor = DefaultBlendFactor; }; inline const char* GetEnumString(MultiplayerAgentType value) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h index 54953b8e7e..43bcf5404e 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/INetworkTime.h @@ -65,7 +65,7 @@ namespace Multiplayer virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0; //! Alters the current Host blend factor. Used to drive interpolation in rewound states. - //! @param blendFactor the blend factor to use + //! @param blendFactor the blend factor to use virtual void AlterBlendFactor(float blendFactor) = 0; //! Syncs all entities contained within a volume to the current rewind state. diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 3c164713ea..a3a5a31eb2 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -156,9 +156,8 @@ namespace Multiplayer if (m_clientBankedTime < sv_MaxBankTimeWindowSec) { // Client blends from previous frame to target so here we subtract blend factor to get to that state - const float blendFactor = AZStd::max(0.f, input.GetHostBlendFactor()); - const float adjustedBlendFactor = std::pow(0.2f, blendFactor); - const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * adjustedBlendFactor); + const float blendFactor = AZStd::min(AZStd::max(0.f, input.GetHostBlendFactor()), 1.f); + const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * blendFactor); m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary { ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); @@ -315,7 +314,7 @@ namespace Multiplayer ++ModifyLastInputId(); input.SetClientInputId(GetLastInputId()); - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), DefaultBlendFactor, invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, clientInputRateSec); AZLOG @@ -395,7 +394,7 @@ namespace Multiplayer { // Reprocess the input for this frame NetworkInput& input = m_inputHistory[replayIndex]; - ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), DefaultBlendFactor, invokingConnection->GetConnectionId()); + ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); GetNetBindComponent()->ProcessInput(input, clientInputRateSec); AZLOG From 8612d7bce293c4f79ca115262cbf42d051e352a9 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 29 Jul 2021 13:35:32 -0700 Subject: [PATCH 7/9] Fix incorrect blending math Signed-off-by: puvvadar --- .../Components/LocalPredictionPlayerInputComponent.cpp | 2 +- .../Code/Source/MultiplayerSystemComponent.cpp | 8 ++++---- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index a3a5a31eb2..23578e5c18 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -157,7 +157,7 @@ namespace Multiplayer { // Client blends from previous frame to target so here we subtract blend factor to get to that state const float blendFactor = AZStd::min(AZStd::max(0.f, input.GetHostBlendFactor()), 1.f); - const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * blendFactor); + const AZ::TimeMs blendMs = AZ::TimeMs(static_cast(static_cast(cl_InputRateMs)) * (1.f - blendFactor)); m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary { ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, input.GetHostBlendFactor(), invokingConnection->GetConnectionId()); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index c16d35e725..893e53fbf6 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -861,12 +861,12 @@ namespace Multiplayer { m_tickFactor += deltaTime / serverRateSeconds; // Linear close to the origin, but asymptote at y = 1 - const float renderBlendFactor = AZStd::clamp(1.0f - (std::pow(cl_renderTickBlendBase, m_tickFactor)), 0.0f, 1.0f); + m_renderBlendFactor = AZStd::clamp(1.0f - (std::pow(cl_renderTickBlendBase, m_tickFactor)), 0.0f, 1.0f); AZLOG ( NET_Blending, "Computed blend factor of %0.3f using a tick factor of %0.3f, a frametime of %0.3f and a serverTickRate of %0.3f", - renderBlendFactor, + m_renderBlendFactor, m_tickFactor, deltaTime, serverRateSeconds @@ -913,7 +913,7 @@ namespace Multiplayer for (NetBindComponent* netBindComponent : gatheredEntities) { - netBindComponent->NotifyPreRender(deltaTime, renderBlendFactor); + netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor); } } else @@ -925,7 +925,7 @@ namespace Multiplayer NetBindComponent* netBindComponent = entity->FindComponent(); if (netBindComponent != nullptr) { - netBindComponent->NotifyPreRender(deltaTime, renderBlendFactor); + netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor); } } } diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 36ef45d647..5f0e23b2dc 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -156,6 +156,7 @@ namespace Multiplayer HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0); double m_serverSendAccumulator = 0.0; + float m_renderBlendFactor = 0.0f; float m_tickFactor = 0.0f; #if !defined(AZ_RELEASE_BUILD) From 318fee8e22a87a73483a1fc5d2d4c46b6220382c Mon Sep 17 00:00:00 2001 From: puvvadar Date: Fri, 30 Jul 2021 10:38:15 -0700 Subject: [PATCH 8/9] Fix tabs Signed-off-by: puvvadar --- Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 5f0e23b2dc..e2fb7deacc 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -156,7 +156,7 @@ namespace Multiplayer HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0); double m_serverSendAccumulator = 0.0; - float m_renderBlendFactor = 0.0f; + float m_renderBlendFactor = 0.0f; float m_tickFactor = 0.0f; #if !defined(AZ_RELEASE_BUILD) From 02486a6fbeeaf9898232e5a4dccf4644a8f64c92 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Fri, 30 Jul 2021 11:38:53 -0700 Subject: [PATCH 9/9] Add usage intention to GetPrevious descriptor Signed-off-by: puvvadar --- .../Code/Include/Multiplayer/NetworkTime/RewindableObject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h index 072c1843f2..a8af564c15 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h @@ -60,7 +60,7 @@ namespace Multiplayer //! @return value in const base type form const BASE_TYPE& Get() const; - //! Const base type retriever for one host frame behind Get(). + //! Const base type retriever for one host frame behind Get(). Only intended for use in SyncRewind contexts. //! @return value in const base type form const BASE_TYPE& GetPrevious() const;