Added delta replication and stat tracking to vector and array network property container types, + some minor cleanup I ran into when looking for the best hook points for this functionality
Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
@@ -102,11 +102,34 @@ namespace Multiplayer
|
||||
return GetEntity()->FindComponent<ComponentType>();
|
||||
}
|
||||
|
||||
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 <typename TYPE>
|
||||
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 <typename TYPE, AZStd::size_t SIZE>
|
||||
inline void SerializeNetworkPropertyHelperArray
|
||||
(
|
||||
AzNetworking::ISerializer& serializer,
|
||||
AzNetworking::FixedSizeBitsetView& bitset,
|
||||
AZStd::array<TYPE, SIZE>& 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 <typename TYPE, AZStd::size_t SIZE>
|
||||
inline void SerializeNetworkPropertyHelperVector
|
||||
(
|
||||
AzNetworking::ISerializer& serializer,
|
||||
AzNetworking::FixedSizeBitsetView& bitset,
|
||||
AZStd::fixed_vector<TYPE, SIZE>& 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<AZ::RequiredBytesForValue<SIZE>(), false>::Type;
|
||||
SizeType origSize = aznumeric_cast<SizeType>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 -%}
|
||||
|
||||
@@ -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<AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}");
|
||||
Multiplayer::SerializeNetworkPropertyHelperVector
|
||||
{% elif Property.attrib['Container'] == 'Array' %}
|
||||
serializer.Serialize<AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>>(m_{{ LowerFirst(Property.attrib['Name']) }}, "{{ LowerFirst(Property.attrib['Name']) }}");
|
||||
Multiplayer::SerializeNetworkPropertyHelperArray
|
||||
{% endif %}
|
||||
(
|
||||
serializer,
|
||||
deltaRecord,
|
||||
m_{{ LowerFirst(Property.attrib['Name']) }},
|
||||
GetNetComponentId(),
|
||||
static_cast<Multiplayer::PropertyIndex>({{ 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<int32_t>({{ AutoComponentMacros.GetNetPropertiesQualifiedPropertyDirtyEnum(Component.attrib['Name'], ReplicateFrom, ReplicateTo, Property) }}),
|
||||
m_{{ LowerFirst(Property.attrib['Name']) }},
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Multiplayer
|
||||
: m_byteStream(&m_buffer)
|
||||
{
|
||||
m_networkEditorInterface = AZ::Interface<INetworking>::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<IMultiplayer>::Get()->InitializeMultiplayer(MultiplayerAgentType::DedicatedServer);
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPNetworkInterfaceName));
|
||||
INetworkInterface* networkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MpNetworkInterfaceName));
|
||||
|
||||
uint16_t serverPort = DefaultServerPort;
|
||||
if (auto console = AZ::Interface<AZ::IConsole>::Get(); console)
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Multiplayer
|
||||
m_serverProcess->TerminateProcess(0);
|
||||
m_serverProcess = nullptr;
|
||||
}
|
||||
INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPEditorInterfaceName));
|
||||
INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName));
|
||||
if (editorNetworkInterface)
|
||||
{
|
||||
editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient);
|
||||
@@ -194,7 +194,7 @@ namespace Multiplayer
|
||||
AZ::Interface<INetworkSpawnableLibrary>::Get()->BuildSpawnablesList();
|
||||
|
||||
// Now that the server has launched, attempt to connect the NetworkInterface
|
||||
INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MPEditorInterfaceName));
|
||||
INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::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));
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace Multiplayer
|
||||
{
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
AzFramework::SessionNotificationBus::Handler::BusConnect();
|
||||
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(MPNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
|
||||
m_networkInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(AZ::Name(MpNetworkInterfaceName), sv_protocol, TrustZone::ExternalClientToServer, *this);
|
||||
if (AZ::Interface<AZ::IConsole>::Get())
|
||||
{
|
||||
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
|
||||
@@ -197,7 +197,7 @@ namespace Multiplayer
|
||||
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Unregister(this);
|
||||
AZ::Interface<IMultiplayer>::Unregister(this);
|
||||
m_consoleCommandHandler.Disconnect();
|
||||
AZ::Interface<INetworking>::Get()->DestroyNetworkInterface(AZ::Name(MPNetworkInterfaceName));
|
||||
AZ::Interface<INetworking>::Get()->DestroyNetworkInterface(AZ::Name(MpNetworkInterfaceName));
|
||||
AzFramework::SessionNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user