Adding rewindable mechanisms to support interpolation

Signed-off-by: puvvadar <puvvadar@amazon.com>
This commit is contained in:
puvvadar
2021-07-22 13:49:05 -07:00
parent 69d5f64bb7
commit b55bad496d
10 changed files with 57 additions and 19 deletions
@@ -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;
@@ -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<HostId>(-1);
@@ -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;
@@ -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();
@@ -65,6 +65,12 @@ namespace Multiplayer
return GetValueForTime(GetCurrentTimeForProperty());
}
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
inline const BASE_TYPE& RewindableObject<BASE_TYPE, REWIND_SIZE>::GetPrevious() const
{
return GetValueForTime(GetCurrentTimeForProperty() - HostFrameId(1));
}
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
inline BASE_TYPE& RewindableObject<BASE_TYPE, REWIND_SIZE>::Modify()
{
@@ -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<float>(static_cast<AZ::TimeMs>(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<float>(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);
}
@@ -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
@@ -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;
};
}
@@ -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<Multiplayer::HostFrameId>(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(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<Multiplayer::HostFrameId>(RewindableContainerSize), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime pop_time(static_cast<Multiplayer::HostFrameId>(RewindableContainerSize), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
EXPECT_EQ(RewindableContainerSize - 1, test.size());
Multiplayer::ScopedAlterTime clear_time(static_cast<Multiplayer::HostFrameId>(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime clear_time(static_cast<Multiplayer::HostFrameId>(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<Multiplayer::HostFrameId>(idx), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(idx), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
for (uint32_t testIdx = 0; testIdx < RewindableContainerSize; ++testIdx)
{
if (testIdx < idx)
@@ -38,7 +38,7 @@ namespace UnitTest
for (uint32_t i = 0; i < 16; ++i)
{
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(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<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(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<Multiplayer::HostFrameId>(RewindableBufferFrames), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(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<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(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<uint32_t, RewindableBufferFrames> test(0);
Multiplayer::ScopedAlterTime time1(static_cast<Multiplayer::HostFrameId>(0), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time1(static_cast<Multiplayer::HostFrameId>(0), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
test = 1;
Multiplayer::ScopedAlterTime time2(static_cast<Multiplayer::HostFrameId>(31), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time2(static_cast<Multiplayer::HostFrameId>(31), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
test = 2;
for (uint32_t i = 0; i < 31; ++i)
{
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
EXPECT_EQ(1, test);
}
Multiplayer::ScopedAlterTime time3(static_cast<Multiplayer::HostFrameId>(31), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time3(static_cast<Multiplayer::HostFrameId>(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<Multiplayer::HostFrameId>(1000 - i), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId);
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(1000 - i), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
EXPECT_EQ(1000, test);
}
}