From 7129cad1ce504545ce793dc2d9f916960a0fa24a Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 25 May 2021 14:12:47 -0700 Subject: [PATCH] Add RewindableFixedVector and update jinja components to use it --- .../NetworkTime/RewindableFixedVector.h | 129 ++++++++++ .../NetworkTime/RewindableFixedVector.inl | 242 ++++++++++++++++++ .../NetworkTime/RewindableObject.h | 2 +- .../Source/AutoGen/AutoComponent_Header.jinja | 13 +- .../Source/AutoGen/AutoComponent_Source.jinja | 75 +++--- ...tionPlayerInputComponent.AutoComponent.xml | 3 +- Gems/Multiplayer/Code/multiplayer_files.cmake | 2 + 7 files changed, 424 insertions(+), 42 deletions(-) create mode 100644 Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h create mode 100644 Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h new file mode 100644 index 0000000000..2e265bdb6f --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h @@ -0,0 +1,129 @@ +/* +* 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. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Multiplayer +{ + //! @class RewindableFixedVector + //! @brief Data structure that has a compile-time upper bound, provides vector semantics and supports network serialization + template + class RewindableFixedVector + { + public: + //! Default constructor + RewindableFixedVector() = default; + + //! Construct and initialize buffer to the provided value + //! @param initialValue initial value to set the internal buffer to + //! @param count initial value to reserve in the vector + RewindableFixedVector(const TYPE& initialValue, uint32_t count); + + //! Destructor + ~RewindableFixedVector(); + + //! Serialization method for fixed vector contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer); + + //! Serialization method for fixed vector contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset &deltaRecord); + + //! Copies elements from the buffer pointed to by Buffer to this FixedSizeVector instance, vector size will be set to BufferSize + //! @param buffer pointer to the buffer to copy + //! @param bufferSize number of elements in the buffer to copy + //! @return bool true on success, false if the input data was too large to fit in the vector + bool copy_values(const TYPE* buffer, uint32_t bufferSize); + + //! Copy buffer from the provided vector + //! @param RHS instance to copy from + RewindableFixedVector& operator=(const RewindableFixedVector& RHS); + + //! Equality operator, returns true if the current instance is equal to RHS + //! @param RHS the FixedSizeVector instance to test for equality against + //! @return bool true if equal, false if not + bool operator ==(const RewindableFixedVector& RHS) const; + + //! Inequality operator, returns true if the current instance is not equal to RHS + //! @param RHS the FixedSizeVector instance to test for inequality against + //! @return bool false if equal, true if not equal + bool operator !=(const RewindableFixedVector& RHS) const; + + //! Resizes the vector to the requested number of elements, initializing new elements if necessary + //! @param count the number of elements to size the vector to + //! @return bool true on success + bool resize(uint32_t count); + + //! Resizes the vector to the requested number of elements, without initialization + //! @param count the number of elements to size the vector to + //! @return bool true on success + bool resize_no_construct(uint32_t count); + + //! Resets the vector, returning it to size 0 + void clear(); + + //! Const element access + //! @param Index index of the element to return + //! @return const reference to the requested element + const TYPE& operator[](uint32_t index) const; + + //! Non-const element access + //! @param Index index of the element to return + //! @return non-const reference to the requested element + TYPE& operator[](uint32_t index); + + //! Pushes a new element to the back of the vector + //! @param Value value to append to the back of this vector + //! @return boolean true on success, false if the vector was full + bool push_back(const TYPE& value); + + //! Pops the last element off the vector, decreasing the vector's size by one + //! @return bool true on success, false if the vector was empty + bool pop_back(); + + //! Returns if the vector is empty + //! @return bool true on empty, false if the vector contains valid elements + bool empty() const; + + //! Gets the last element of the vector + const TYPE& back() const; + + //! Gets the size of the vector + uint32_t size() const; + + typedef const RewindableObject* const_iterator; + const_iterator begin() const { return m_container.cbegin(); } + const_iterator end() const { return m_container.cend(); } + typedef RewindableObject* iterator; + iterator begin() { return m_container.begin(); } + iterator end() { return m_container.end(); } + + private: + AZStd::fixed_vector, SIZE> m_container; + // Synchronized value for vector size, prefer using size() locally which checks m_container.size() + RewindableObject m_size; + }; +} + +#include diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl new file mode 100644 index 0000000000..f1c4284fa2 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl @@ -0,0 +1,242 @@ +/* +* 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. +* +*/ + +#pragma once + +namespace Multiplayer +{ + template + inline RewindableFixedVector::RewindableFixedVector(const TYPE& initialValue, uint32_t count) + { + resize_no_construct(count); + for (uint32_t idx = 0l idx < size(); ++idx) + { + m_container[idx] = initialValue; + } + } + + template + inline RewindableFixedVector::~RewindableFixedVector() + { + ; + } + + template + inline bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer) + { + m_size = m_container.size(); + if(!m_size.Serialize(serializer) && !resize(m_size)) + { + return false; + } + + for (uint32_t i = 0; i < size(); ++i) + { + if(!m_container[i].Serialize(serializer)) + { + return false; + } + } + + return serializer.IsValid(); + } + + template + inline bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) + { + if (deltaRecord.GetBit(SIZE)) + { + uint32_t origSize = m_size; + m_size = m_container.size(); + if(!m_size.Serialize(serializer) && !resize(m_size)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && origSize == m_size) + { + deltaRecord.SetBit(SIZE, false); + } + } + for (uint32_t i = 0; i < size(); ++i) + { + if (deltaRecord.GetBit(i)) + { + serializer.ClearTrackedChangesFlag(); + if(!m_container[i].Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && !serializer.GetTrackedChangesFlag()) + { + deltaRecord.SetBit(i, false); + } + } + } + + return serializer.IsValid(); + } + + template + inline bool RewindableFixedVector::copy_values(const TYPE* buffer, uint32_t bufferSize) + { + if (!resize(bufferSize)) + { + return false; + } + + for (uint32_t idx = 0; idx < bufferSize; ++i) + { + m_container[idx] = buffer[idx]; + } + + return true; + } + + + template + inline RewindableFixedVector& RewindableFixedVector::operator=(const RewindableFixedVector& RHS) + { + resize(RHS.size()); + for (uint32_t idx = 0; idx < size(); ++i) + { + m_container[idx] = RHS.m_container[idx]; + } + return *this; + } + + template + bool RewindableFixedVector::operator ==(const RewindableFixedVector& RHS) const + { + if (this->size() != RHS.size()) + { + return false; + } + + return m_container == RHS.m_container && m_size == m_size; + } + + template + bool RewindableFixedVector::operator !=(const RewindableFixedVector& RHS) const + { + return !(*this == RHS); + } + + template + bool RewindableFixedVector::resize(uint32_t count) + { + if (count > SIZE) + { + return false; + } + + if (count == size()) + { + return true; + } + + if (count > size()) + { + for (uint32_t idx = size(); idx < count; ++idx) + { + m_container[idx] = TYPE(); + } + } + + m_container.resize(count); + + return true; + } + + template + inline bool RewindableFixedVector::resize_no_construct(uint32_t count) + { + if (count > SIZE) + { + return false; + } + + m_container.resize_no_construct(count); + + return true; + } + + template + inline void RewindableFixedVector::clear() + { + resize(0); + } + + template + inline const TYPE& RewindableFixedVector::operator[](uint32_t index) const + { + AZ_Assert(index < size(), "Out of bounds access (requested %u, reserved %u)", index, size()); + return m_container[index].Get(); + } + + template + inline TYPE& RewindableFixedVector::operator[](uint32_t index) + { + AZ_Assert(index < size(), "Out of bounds access (requested %u, reserved %u)", index, size()); + return m_container[index].Modify(); + } + + template + inline bool RewindableFixedVector::push_back(const TYPE& value) + { + const uint32_t iBufferSize = size(); + + if (!resize(iBufferSize + 1)) + { + return false; + } + + m_container[iBufferSize] = value; + + return true; + } + + template + inline bool RewindableFixedVector::pop_back() + { + const uint32_t iBufferSize = size(); + + if (iBufferSize <= 0) + { + return false; + } + + resize(iBufferSize - 1); + + return true; + } + + template + inline bool RewindableFixedVector::empty() const + { + return m_container.empty(); + } + + template + inline const TYPE& RewindableFixedVector::back() const + { + AZ_Assert(size() > 0, "Attempted to get back element of an empty RewindableFixedVector"); + return m_container[size() - 1].Get(); + } + + template + inline uint32_t RewindableFixedVector::size() const + { + return m_container.size(); + } +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h index d5b7d563ab..f7e92bbe26 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableObject.h @@ -32,7 +32,7 @@ namespace Multiplayer RewindableObject() = default; //! Constructor. - //! @param connectionId the connectionId of the connection that owns the object. + //! @param value base type value to construct from RewindableObject(const BASE_TYPE& value); //! Copy construct from underlying base type. diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 4061ddd7b6..071967165f 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -13,7 +13,11 @@ const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const; void {{ PropertyName }}AddEvent(AZ::Event::Handler& handler); {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} -const AZStd::fixed_vector<{% if Property.attrib['IsRewindable']|booleanTrue %}RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const; +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const; +{% else %} +const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Vector() const; +{% endif %} const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const; const {{ Property.attrib['Type'] }} &{{ PropertyName }}GetBack() const; uint32_t {{ PropertyName }}GetSize() const; @@ -158,7 +162,11 @@ AZ::Event<{{ Property.attrib['Type'] }}> m_{{ LowerFirst(Property.attrib['Name'] {% if Property.attrib['Container'] == 'Array' %} AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; {% elif Property.attrib['Container'] == 'Vector' %} -AZStd::fixed_vector<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% if Property.attrib['IsRewindable']|booleanTrue %} +RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% else %} +AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% endif %} {% elif Property.attrib['IsRewindable']|booleanTrue %} Multiplayer::RewindableObject<{{ Property.attrib['Type'] }}, Multiplayer::RewindHistorySize> m_{{ LowerFirst(Property.attrib['Name']) }} = {{ Property.attrib['Init'] }}; {% else %} @@ -228,6 +236,7 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] } #include #include #include +#include #include {% call(Include) AutoComponentMacros.ParseIncludes(Component) %} #include <{{ Include.attrib['File'] }}> diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 3437969901..ef83973f48 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -21,7 +21,11 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Even {% endif %} {% elif Property.attrib['Container'] == 'Vector' %} -const AZStd::fixed_vector<{% if Property.attrib['IsRewindable']|booleanTrue %}RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% else %} +const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -110,25 +114,26 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value) { - int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); - int32_t bitIndex = indexToSet + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - return true; + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value)) + { + int32_t indexToSet = GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size(); + int32_t bitIndex = indexToSet + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; + } } bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack(const Multiplayer::NetworkInput&) { - if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back()) { - return false; + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; } - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); - return true; + return false; } void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear(const Multiplayer::NetworkInput&) @@ -202,30 +207,32 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(int32_t index int32_t bitIndex = index + static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); GetParent().MarkDirty(); - return static_cast<{{ Property.attrib['Type'] }}&>(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}[index]{% if Property.attrib['IsRewindable']|booleanTrue %}.Modify(){% endif %}); + return static_cast<{{ Property.attrib['Type'] }}&>(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}[index]); } bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PushBack(const {{ Property.attrib['Type'] }} &value) { - uint32_t indexToSet = aznumeric_cast(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size()); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value); - uint32_t bitIndex = indexToSet + aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - return true; + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.push_back(value)) + { + uint32_t indexToSet = aznumeric_cast(GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.size()); + uint32_t bitIndex = indexToSet + aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Start') }}); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(bitIndex, true); + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(aznumeric_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; + } + return false; } bool {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}PopBack() { - if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.empty()) + if (GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back()) { - return false; + GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); + GetParent().MarkDirty(); + return true; } - GetParent().m_currentRecord->m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}.SetBit(static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property, 'Size') }}), true); - GetParent().MarkDirty(); - GetParent().m_{{ LowerFirst(Property.attrib['Name']) }}.pop_back(); - return true; + return false; } void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}Clear() @@ -562,7 +569,7 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} {% endcall %} {% if networkPropertyCount.value > 0 %} - Multiplayer::MultiplayerStats& stats = Multiplayer::GetMultiplayer()->GetStats(); + [[maybe_unused]] Multiplayer::MultiplayerStats& stats = Multiplayer::GetMultiplayer()->GetStats(); // We modify the record if we are writing an update so that we don't notify for a change that really didn't change the value (just a duplicated send from the server) [[maybe_unused]] bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject; {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} @@ -576,15 +583,7 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re {% endif %} AzNetworking::FixedSizeBitsetView deltaRecord(replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}, firstBit, lastBit - firstBit + 1); - if (deltaRecord.AnySet()) - { -{% if Property.attrib['Container'] == 'Vector' %} - Multiplayer::SerializableFixedSizeVectorDeltaStruct<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ Property.attrib['Name'] }}, deltaRecord); -{% else %} - Multiplayer::SerializableFixedSizeArrayDeltaStruct<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> deltaStruct(m_{{ Property.attrib['Name'] }}, deltaRecord); -{% endif %} - serializer.Serialize(deltaStruct, "{{ UpperFirst(Property.attrib['Name']) }}"); - } + m_{{ LowerFirst(Property.attrib['Name']) }}.Serialize(serializer, deltaRecord); } {% else %} Multiplayer::SerializeNetworkPropertyHelper diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index b6edd0e3be..78dc35c111 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -18,7 +18,8 @@ - + + diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index eb856a48db..856e4893a4 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -33,6 +33,8 @@ set(FILES Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/NetworkInput.h Include/Multiplayer/NetworkTime/INetworkTime.h + Include/Multiplayer/NetworkTime/RewindableFixedVector.h + Include/Multiplayer/NetworkTime/RewindableFixedVector.inl Include/Multiplayer/NetworkTime/RewindableObject.h Include/Multiplayer/NetworkTime/RewindableObject.inl Include/Multiplayer/ReplicationWindows/IReplicationWindow.h