diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h index 06e0655a9c..a9e365f5f9 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h @@ -118,7 +118,7 @@ namespace Multiplayer constexpr iterator end() { return m_container.end(); } private: - AZStd::fixed_vector, SIZE> m_container; + AZStd::array, SIZE> m_container; // Synchronized value for vector size, prefer using size() locally which checks m_container.size() RewindableObject m_rewindableSize; }; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl index 3353877478..5690e51c35 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl @@ -17,8 +17,8 @@ namespace Multiplayer template constexpr RewindableFixedVector::RewindableFixedVector(const TYPE& initialValue, uint32_t count) { - m_container.resize(count, initialValue); - m_rewindableSize = m_container.size(); + m_container.fill(initialValue); + m_rewindableSize = count; } template @@ -30,15 +30,14 @@ namespace Multiplayer template bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer) { - m_rewindableSize = m_container.size(); - if(!m_rewindableSize.Serialize(serializer) && !resize(m_rewindableSize)) + if(!m_rewindableSize.Serialize(serializer)) { return false; } - for (uint32_t i = 0; i < size(); ++i) + for (uint32_t idx = 0; idx < size(); ++idx) { - if(!m_container[i].Serialize(serializer)) + if(!m_container[idx].Serialize(serializer)) { return false; } @@ -53,8 +52,7 @@ namespace Multiplayer if (deltaRecord.GetBit(SIZE)) { const uint32_t origSize = m_rewindableSize; - m_rewindableSize = m_container.size(); - if(!m_rewindableSize.Serialize(serializer) && !resize(m_rewindableSize)) + if(!m_rewindableSize.Serialize(serializer)) { return false; } @@ -64,19 +62,19 @@ namespace Multiplayer deltaRecord.SetBit(SIZE, false); } } - for (uint32_t i = 0; i < size(); ++i) + for (uint32_t idx = 0; idx < size(); ++idx) { - if (deltaRecord.GetBit(i)) + if (deltaRecord.GetBit(idx)) { serializer.ClearTrackedChangesFlag(); - if(!m_container[i].Serialize(serializer)) + if(!m_container[idx].Serialize(serializer)) { return false; } if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && !serializer.GetTrackedChangesFlag()) { - deltaRecord.SetBit(i, false); + deltaRecord.SetBit(idx, false); } } } @@ -92,7 +90,7 @@ namespace Multiplayer return false; } - for (uint32_t idx = 0; idx < bufferSize; ++i) + for (uint32_t idx = 0; idx < bufferSize; ++idx) { m_container[idx] = buffer[idx]; } @@ -104,9 +102,9 @@ namespace Multiplayer constexpr RewindableFixedVector& RewindableFixedVector::operator=(const RewindableFixedVector& rhs) { resize(rhs.size()); - for (uint32_t idx = 0; idx < size(); ++i) + for (uint32_t idx = 0; idx < size(); ++idx) { - m_container[idx] = rhs.m_container[idx]; + m_container[idx] = rhs.m_container[idx].Get(); } return *this; } @@ -136,8 +134,14 @@ namespace Multiplayer return true; } - m_container.resize(count, TYPE()); - m_rewindableSize = m_container.size(); + if (count > size()) + { + for (uint32_t idx = size(); idx < count; ++idx) + { + m_container[idx] = TYPE(); + } + } + m_rewindableSize = count; return true; } @@ -150,8 +154,7 @@ namespace Multiplayer return false; } - m_container.resize_no_construct(count); - m_rewindableSize = m_container.size(); + m_rewindableSize = count; return true; } @@ -159,8 +162,11 @@ namespace Multiplayer template constexpr void RewindableFixedVector::clear() { - m_container.clear(); - m_rewindableSize = m_container.size(); + for (uint32_t idx = 0; idx < SIZE; ++idx) + { + m_container[idx] = TYPE(); + } + m_rewindableSize = 0; } template @@ -182,8 +188,8 @@ namespace Multiplayer { if (size() < SIZE) { - m_container.push_back(value); - m_rewindableSize = m_container.size(); + m_container[m_rewindableSize] = value; + m_rewindableSize = m_rewindableSize + 1; return true; } @@ -195,8 +201,8 @@ namespace Multiplayer { if (size() > 0) { - m_container.pop_back(); - m_rewindableSize = m_container.size(); + m_rewindableSize = m_rewindableSize - 1; + m_container[m_rewindableSize] = TYPE(); return true; } @@ -206,14 +212,14 @@ namespace Multiplayer template constexpr bool RewindableFixedVector::empty() const { - return m_container.empty(); + return m_rewindableSize.Get() == 0; } template constexpr const TYPE& RewindableFixedVector::back() const { AZ_Assert(size() > 0, "Attempted to get back element of an empty RewindableFixedVector"); - return m_container.back().Get(); + return m_container[m_rewindableSize - 1].Get(); } template diff --git a/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp new file mode 100644 index 0000000000..af39dd8c1a --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/RewindableContainerTests.cpp @@ -0,0 +1,112 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class RewindableContainerTests + : public AllocatorsFixture + { + public: + Multiplayer::NetworkTime m_networkTime; + AZ::LoggerSystemComponent m_loggerComponent; + AZ::TimeSystemComponent m_timeComponent; + }; + + static constexpr uint32_t RewindableContainerSize = 7; + static constexpr uint32_t RewindableBufferFrames = 32; + + TEST_F(RewindableContainerTests, BasicVectorTest) + { + Multiplayer::RewindableFixedVector test(0, 0); + + // Test push_back + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + test.push_back(idx); + EXPECT_EQ(idx, test[idx]); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + // 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); + EXPECT_EQ(idx + 1, test.size()); + EXPECT_EQ(idx, test.back()); + } + + // Test pop_back + test.pop_back(); + EXPECT_EQ(RewindableContainerSize - 1, test.size()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + + // Test clear and empty + test.clear(); + EXPECT_EQ(0, test.size()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + EXPECT_TRUE(test.empty()); + + // Test rewind for pop_back and clear + Multiplayer::ScopedAlterTime pop_time(static_cast(RewindableContainerSize), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(RewindableContainerSize - 1, test.size()); + Multiplayer::ScopedAlterTime clear_time(static_cast(RewindableContainerSize + 1), AZ::TimeMs{ 0 }, AzNetworking::InvalidConnectionId); + EXPECT_EQ(0, test.size()); + + // Test copy_values and resize_no_construct + test.resize_no_construct(RewindableContainerSize); + test.copy_values(&test[RewindableContainerSize-1], 1); + EXPECT_EQ(1, test.size()); + test.resize_no_construct(RewindableContainerSize); + EXPECT_EQ(test[0], test[RewindableContainerSize - 1]); + } + + TEST_F(RewindableContainerTests, BasicArrayTest) + { + Multiplayer::RewindableArray test; + + test.fill(0); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + // Test push_back + for (uint32_t idx = 0; idx < RewindableContainerSize; ++idx) + { + test[idx] = idx; + EXPECT_EQ(idx, test[idx].Get()); + Multiplayer::GetNetworkTime()->IncrementHostFrameId(); + } + + // 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); + for (uint32_t testIdx = 0; testIdx < RewindableContainerSize; ++testIdx) + { + if (testIdx < idx) + { + EXPECT_EQ(testIdx, test[testIdx].Get()); + } + else + { + EXPECT_EQ(0, test[testIdx].Get()); + } + } + } + } +} diff --git a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake index fe1ca38186..0731c25d3b 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -13,5 +13,6 @@ set(FILES Tests/Main.cpp Tests/IMultiplayerConnectionMock.h Tests/MultiplayerSystemTests.cpp + Tests/RewindableContainerTests.cpp Tests/RewindableObjectTests.cpp )