diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h new file mode 100644 index 0000000000..36342dffc2 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.h @@ -0,0 +1,46 @@ +/* +* 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 RewindableArray + //! @brief Data structure that has a compile-time upper bound, provides array semantics and supports network serialization + template + class RewindableArray + : public AZStd::array, SIZE> + { + public: + //! Serialization method for array 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 array contained rewindable objects + //! @param serializer ISerializer instance to use for serialization + //! @param deltaRecord Bitset delta record used to detect state change during reconciliation + //! @return bool true for success, false for serialization failure + bool Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset &deltaRecord); + }; +} + +#include diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl new file mode 100644 index 0000000000..b3fe18dd79 --- /dev/null +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableArray.inl @@ -0,0 +1,53 @@ +/* +* 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 + bool RewindableArray::Serialize(AzNetworking::ISerializer& serializer) + { + for (uint32_t i = 0; i < size(); ++i) + { + if(!this[i].Serialize(serializer)) + { + return false; + } + } + + return serializer.IsValid(); + } + + template + bool RewindableArray::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) + { + for (uint32_t i = 0; i < size(); ++i) + { + if (deltaRecord.GetBit(i)) + { + serializer.ClearTrackedChangesFlag(); + if(!this[i].Serialize(serializer)) + { + return false; + } + + if ((serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) && !serializer.GetTrackedChangesFlag()) + { + deltaRecord.SetBit(i, false); + } + } + } + + return serializer.IsValid(); + } +} diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h index 6d30eabeb8..a33e223a5d 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.h @@ -43,12 +43,13 @@ namespace Multiplayer //! 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 - constexpr bool Serialize(AzNetworking::ISerializer& serializer); + bool Serialize(AzNetworking::ISerializer& serializer); //! Serialization method for fixed vector contained rewindable objects //! @param serializer ISerializer instance to use for serialization + //! @param deltaRecord Bitset delta record used to detect state change during reconciliation //! @return bool true for success, false for serialization failure - constexpr bool Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset &deltaRecord); + 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 diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl index 519431793b..3353877478 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkTime/RewindableFixedVector.inl @@ -28,7 +28,7 @@ namespace Multiplayer } template - constexpr bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer) + bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer) { m_rewindableSize = m_container.size(); if(!m_rewindableSize.Serialize(serializer) && !resize(m_rewindableSize)) @@ -48,7 +48,7 @@ namespace Multiplayer } template - constexpr bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) + bool RewindableFixedVector::Serialize(AzNetworking::ISerializer& serializer, AzNetworking::IBitset& deltaRecord) { if (deltaRecord.GetBit(SIZE)) { diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 071967165f..8cf1eeeb58 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -7,7 +7,11 @@ {% macro DeclareNetworkPropertyGetter(Property) %} {% set PropertyName = UpperFirst(Property.attrib['Name']) %} {% if Property.attrib['Container'] == 'Array' %} -const AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::k_RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% else %} +const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> &Get{{ PropertyName }}Array() const; +{% endif %} const {{ Property.attrib['Type'] }} &Get{{ PropertyName }}(int32_t index) const; {% if Property.attrib['GenerateEventBindings']|booleanTrue %} void {{ PropertyName }}AddEvent(AZ::Event::Handler& handler); @@ -160,7 +164,11 @@ AZ::Event<{{ Property.attrib['Type'] }}> m_{{ LowerFirst(Property.attrib['Name'] {% macro DeclareNetworkPropertyVars(Component, ReplicateFrom, ReplicateTo) %} {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, ReplicateFrom, ReplicateTo) %} {% 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']) }}; +{% if Property.attrib['IsRewindable']|booleanTrue %} +RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% else %} +AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; +{% endif %} {% elif Property.attrib['Container'] == 'Vector' %} {% if Property.attrib['IsRewindable']|booleanTrue %} RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}> m_{{ LowerFirst(Property.attrib['Name']) }}; @@ -236,6 +244,7 @@ AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] } #include #include #include +#include #include #include {% call(Include) AutoComponentMacros.ParseIncludes(Component) %} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 1124b0e59c..6b2c5b199a 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -3,7 +3,11 @@ {% macro LowerFirst(text) %}{{ text[0] | lower}}{{ text[1:] }}{% endmacro %} {% macro DefineNetworkPropertyGet(ClassName, Property, Prefix = '') %} {% if Property.attrib['Container'] == 'Array' %} -const AZStd::array<{% if Property.attrib['IsRewindable']|booleanTrue %}Multiplayer::RewindableObject<{% endif %}{{ Property.attrib['Type'] }}{% if Property.attrib['IsRewindable']|booleanTrue %}, Multiplayer::RewindHistorySize>{% endif %}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% else %} +const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -643,7 +647,11 @@ void {{ ClassName }}::NotifyChanges{{ AutoComponentMacros.GetNetPropertiesSetNam {% macro DefineArchetypePropertyGet(Property, ClassType, ClassName, Prefix = '') %} {% if ClassType == '' or Property.attrib['ExportTo'] == ClassType or Property.attrib['ExportTo'] == "Common" %} {% if Property.attrib['Container'] == 'Array' %} +{% if Property.attrib['IsRewindable']|booleanTrue %} +const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% else %} const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const +{% endif %} { return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}; } @@ -1474,7 +1482,7 @@ namespace {{ Component.attrib['Namespace'] }} { {% for Property in Component.iter('NetworkProperty') %} {% if Property.attrib['IsRewindable']|booleanTrue %} -{% if Property.attrib['Container'] == 'Vector' %} +{% if Property.attrib['Container'] == 'Vector' or Property.attrib['Container'] == 'Array' %} for ( auto& element: m_{{ LowerFirst(Property.attrib['Name']) }}) { element.SetOwningConnectionId(connectionId); diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index 856e4893a4..1cfef93240 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/RewindableArray.h + Include/Multiplayer/NetworkTime/RewindableArray.inl Include/Multiplayer/NetworkTime/RewindableFixedVector.h Include/Multiplayer/NetworkTime/RewindableFixedVector.inl Include/Multiplayer/NetworkTime/RewindableObject.h