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) {