diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/MultiplayerComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/MultiplayerComponent.h index 64ceb6e16f..f685d453f3 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/MultiplayerComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/MultiplayerComponent.h @@ -102,11 +102,34 @@ namespace Multiplayer return GetEntity()->FindComponent(); } + inline void UpdateComponentMetrics + ( + bool modifyRecord, + uint32_t prevSerializerSize, + uint32_t currSerializerSize, + NetComponentId componentId, + PropertyIndex propertyIndex, + MultiplayerStats& stats + ) + { + const uint32_t updateSize = (currSerializerSize - prevSerializerSize); + if (updateSize > 0) + { + if (modifyRecord) + { + stats.RecordPropertyReceived(componentId, propertyIndex, updateSize); + } + else + { + stats.RecordPropertySent(componentId, propertyIndex, updateSize); + } + } + } + template inline void SerializeNetworkPropertyHelper ( AzNetworking::ISerializer& serializer, - bool modifyRecord, AzNetworking::FixedSizeBitsetView& bitset, int32_t bitIndex, TYPE& value, @@ -118,6 +141,7 @@ namespace Multiplayer { if (bitset.GetBit(bitIndex)) { + const bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject; const uint32_t prevUpdateSize = serializer.GetSize(); serializer.ClearTrackedChangesFlag(); serializer.Serialize(value, name); @@ -127,19 +151,78 @@ namespace Multiplayer bitset.SetBit(bitIndex, false); } const uint32_t postUpdateSize = serializer.GetSize(); - // Network Property metrics - const uint32_t updateSize = (postUpdateSize - prevUpdateSize); - if (updateSize > 0) + UpdateComponentMetrics(modifyRecord, prevUpdateSize, postUpdateSize, componentId, propertyIndex, stats); + } + } + + template + inline void SerializeNetworkPropertyHelperArray + ( + AzNetworking::ISerializer& serializer, + AzNetworking::FixedSizeBitsetView& bitset, + AZStd::array& value, + NetComponentId componentId, + PropertyIndex propertyIndex, + MultiplayerStats& stats + ) + { + const bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject; + const uint32_t prevUpdateSize = serializer.GetSize(); + for (uint32_t i = 0; i < SIZE; ++i) + { + if (bitset.GetBit(i)) { - if (modifyRecord) + serializer.ClearTrackedChangesFlag(); + serializer.Serialize(value[i], "Element"); + if (modifyRecord && !serializer.GetTrackedChangesFlag()) { - stats.RecordPropertyReceived(componentId, propertyIndex, updateSize); - } - else - { - stats.RecordPropertySent(componentId, propertyIndex, updateSize); + bitset.SetBit(i, false); } } } + const uint32_t postUpdateSize = serializer.GetSize(); + UpdateComponentMetrics(modifyRecord, prevUpdateSize, postUpdateSize, componentId, propertyIndex, stats); + } + + template + inline void SerializeNetworkPropertyHelperVector + ( + AzNetworking::ISerializer& serializer, + AzNetworking::FixedSizeBitsetView& bitset, + AZStd::fixed_vector& value, + NetComponentId componentId, + PropertyIndex propertyIndex, + MultiplayerStats& stats + ) + { + const bool modifyRecord = serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject; + const uint32_t prevUpdateSize = serializer.GetSize(); + if (bitset.GetBit(SIZE)) + { + using SizeType = typename AZ::SizeType(), false>::Type; + SizeType origSize = aznumeric_cast(value.size()); + SizeType newSize = origSize; + serializer.Serialize(newSize, "Size"); + value.resize(newSize); + if (modifyRecord && origSize == newSize) + { + bitset.SetBit(SIZE, false); + } + } + + for (uint32_t i = 0; i < value.size(); ++i) + { + if (bitset.GetBit(i)) + { + serializer.ClearTrackedChangesFlag(); + serializer.Serialize(value[i], "Element"); + if (modifyRecord && !serializer.GetTrackedChangesFlag()) + { + bitset.SetBit(i, false); + } + } + } + const uint32_t postUpdateSize = serializer.GetSize(); + UpdateComponentMetrics(modifyRecord, prevUpdateSize, postUpdateSize, componentId, propertyIndex, stats); } } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h index b6ed0219f8..4471aa0c2b 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerConstants.h @@ -17,12 +17,10 @@ namespace Multiplayer { - constexpr AZStd::string_view MPNetworkInterfaceName("MultiplayerNetworkInterface"); - constexpr AZStd::string_view MPEditorInterfaceName("MultiplayerEditorNetworkInterface"); - + constexpr AZStd::string_view MpNetworkInterfaceName("MultiplayerNetworkInterface"); + constexpr AZStd::string_view MpEditorInterfaceName("MultiplayerEditorNetworkInterface"); constexpr AZStd::string_view LocalHost("127.0.0.1"); + constexpr uint16_t DefaultServerPort = 33450; constexpr uint16_t DefaultServerEditorPort = 33451; - } - diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h index 704e9d2734..4ac20abc83 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h @@ -86,7 +86,7 @@ namespace Multiplayer Activate }; - // Structure for identifying a specific entity within a spawnable + //! Structure for identifying a specific entity within a spawnable. struct PrefabEntityId { AZ_TYPE_INFO(PrefabEntityId, "{EFD37465-CCAC-4E87-A825-41B4010A2C75}"); @@ -97,29 +97,10 @@ namespace Multiplayer uint32_t m_entityOffset = AllIndices; PrefabEntityId() = default; - - explicit PrefabEntityId(AZ::Name name, uint32_t entityOffset = AllIndices) - : m_prefabName(name) - , m_entityOffset(entityOffset) - { - } - - bool operator==(const PrefabEntityId& rhs) const - { - return m_prefabName == rhs.m_prefabName && m_entityOffset == rhs.m_entityOffset; - } - - bool operator!=(const PrefabEntityId& rhs) const - { - return !(*this == rhs); - } - - bool Serialize(AzNetworking::ISerializer& serializer) - { - serializer.Serialize(m_prefabName, "prefabName"); - serializer.Serialize(m_entityOffset, "entityOffset"); - return serializer.IsValid(); - } + explicit PrefabEntityId(AZ::Name name, uint32_t entityOffset = AllIndices); + bool operator==(const PrefabEntityId& rhs) const; + bool operator!=(const PrefabEntityId& rhs) const; + bool Serialize(AzNetworking::ISerializer& serializer); }; struct EntityMigrationMessage @@ -128,6 +109,30 @@ namespace Multiplayer PrefabEntityId m_prefabEntityId; AzNetworking::PacketEncodingBuffer m_propertyUpdateData; }; + + inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset) + : m_prefabName(name) + , m_entityOffset(entityOffset) + { + ; + } + + inline bool PrefabEntityId::operator==(const PrefabEntityId& rhs) const + { + return m_prefabName == rhs.m_prefabName && m_entityOffset == rhs.m_entityOffset; + } + + inline bool PrefabEntityId::operator!=(const PrefabEntityId& rhs) const + { + return !(*this == rhs); + } + + inline bool PrefabEntityId::Serialize(AzNetworking::ISerializer& serializer) + { + serializer.Serialize(m_prefabName, "prefabName"); + serializer.Serialize(m_entityOffset, "entityOffset"); + return serializer.IsValid(); + } } AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostId); diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja index 72583f9062..811039feda 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja @@ -5,12 +5,12 @@ #} {%- macro ParseRpcParams(property, outNames, outTypes, outDefines, use_default_value=False) -%} {%- for Param in property.iter('Param') -%} -{%- do outNames.append(Param.attrib['Name']) -%} +{%- do outNames.append(LowerFirst(Param.attrib['Name'])) -%} {%- do outTypes.append(Param.attrib['Type']) -%} {%- if use_default_value and Param.attrib['DefaultValue'] -%} -{%- do outDefines.append('const ' ~ Param.attrib['Type'] ~ '& ' + Param.attrib['Name'] + ' = ' + Param.attrib['DefaultValue']) -%} +{%- do outDefines.append('const ' ~ Param.attrib['Type'] ~ '& ' + LowerFirst(Param.attrib['Name']) + ' = ' + Param.attrib['DefaultValue']) -%} {%- else -%} -{%- do outDefines.append('const ' ~ Param.attrib['Type'] ~ '& ' ~ Param.attrib['Name']) -%} +{%- do outDefines.append('const ' ~ Param.attrib['Type'] ~ '& ' ~ LowerFirst(Param.attrib['Name'])) -%} {%- endif -%} {%- endfor -%} {%- endmacro -%} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 079cac329b..35b5ac3abf 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -636,7 +636,6 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re {% if networkPropertyCount.value > 0 %} [[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) %} {% if Property.attrib['Container'] != 'None' and Property.attrib['Container'] != 'Object' %} { // Serialization for Vector and Array Network Properties @@ -651,17 +650,24 @@ bool {{ ClassName }}::Serialize{{ AutoComponentMacros.GetNetPropertiesSetName(Re if (deltaRecord.AnySet()) { {% if Property.attrib['Container'] == 'Vector' %} - serializer.Serialize>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}"); + Multiplayer::SerializeNetworkPropertyHelperVector {% elif Property.attrib['Container'] == 'Array' %} - serializer.Serialize>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}"); + Multiplayer::SerializeNetworkPropertyHelperArray {% endif %} + ( + serializer, + deltaRecord, + m_{{ LowerFirst(Property.attrib['Name']) }}, + GetNetComponentId(), + static_cast({{ UpperFirst(Component.attrib['Name']) }}Internal::NetworkProperties::{{ UpperFirst(Property.attrib['Name']) }}), + stats + ); } } {% else %} Multiplayer::SerializeNetworkPropertyHelper ( serializer, - modifyRecord, replicationRecord.m_{{ LowerFirst(AutoComponentMacros.GetNetPropertiesSetName(ReplicateFrom, ReplicateTo)) }}, static_cast({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property) }}), m_{{ LowerFirst(Property.attrib['Name']) }}, diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index fc398182ef..3bf578dd03 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -31,7 +31,7 @@ namespace Multiplayer : m_byteStream(&m_buffer) { m_networkEditorInterface = AZ::Interface::Get()->CreateNetworkInterface( - AZ::Name(MPEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); + AZ::Name(MpEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this); m_networkEditorInterface->SetTimeoutEnabled(false); if (editorsv_isDedicated) { @@ -109,7 +109,7 @@ namespace Multiplayer // Setup the normal multiplayer connection AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer); - INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName)); + INetworkInterface* networkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpNetworkInterfaceName)); uint16_t serverPort = DefaultServerPort; if (auto console = AZ::Interface::Get(); console) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index d557c65215..377fb1eb56 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -105,7 +105,7 @@ namespace Multiplayer m_serverProcess->TerminateProcess(0); m_serverProcess = nullptr; } - INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPEditorInterfaceName)); + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); if (editorNetworkInterface) { editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient); @@ -194,7 +194,7 @@ namespace Multiplayer AZ::Interface::Get()->BuildSpawnablesList(); // Now that the server has launched, attempt to connect the NetworkInterface - INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MPEditorInterfaceName)); + INetworkInterface* editorNetworkInterface = AZ::Interface::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)); AZ_Assert(editorNetworkInterface, "MP Editor Network Interface was unregistered before Editor could connect."); m_editorConnId = editorNetworkInterface->Connect( AzNetworking::IpAddress(remoteAddress.c_str(), editorsv_port, AzNetworking::ProtocolType::Tcp)); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index b0a8350982..5396d53c79 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -180,7 +180,7 @@ namespace Multiplayer { AZ::TickBus::Handler::BusConnect(); AzFramework::SessionNotificationBus::Handler::BusConnect(); - m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); + m_networkInterface = AZ::Interface::Get()->CreateNetworkInterface(AZ::Name(MpNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this); if (AZ::Interface::Get()) { m_consoleCommandHandler.Connect(AZ::Interface::Get()->GetConsoleCommandInvokedEvent()); @@ -197,7 +197,7 @@ namespace Multiplayer AZ::Interface::Unregister(this); AZ::Interface::Unregister(this); m_consoleCommandHandler.Disconnect(); - AZ::Interface::Get()->DestroyNetworkInterface(AZ::Name(MPNetworkInterfaceName)); + AZ::Interface::Get()->DestroyNetworkInterface(AZ::Name(MpNetworkInterfaceName)); AzFramework::SessionNotificationBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); }