Several updates to complete rewind support and remove unneeded functionality

main
karlberg 5 years ago
parent e0ea9e6224
commit 487e989e68

@ -19,6 +19,8 @@
namespace AzNetworking
{
using NetworkInterfaces = AZStd::unordered_map<AZ::Name, AZStd::unique_ptr<INetworkInterface>>;
//! @class INetworking
//! @brief The interface for creating and working with network interfaces.
class INetworking
@ -60,5 +62,25 @@ namespace AzNetworking
//! @param name The name of the Compressor factory to unregister, must match result of factory->GetFactoryName()
//! @return Whether the factory was found and unregistered
virtual bool UnregisterCompressorFactory(AZ::Name name) = 0;
//! Returns the raw network interfaces owned by the networking instance.
//! @return the raw network interfaces owned by the networking instance
virtual const NetworkInterfaces& GetNetworkInterfaces() const = 0;
//! Returns the number of sockets monitored by our TcpListenThread.
//! @return the number of sockets monitored by our TcpListenThread
virtual uint32_t GetTcpListenThreadSocketCount() const = 0;
//! Returns the total time spent updating our TcpListenThread.
//! @return the total time spent updating our TcpListenThread
virtual AZ::TimeMs GetTcpListenThreadUpdateTime() const = 0;
//! Returns the number of sockets monitored by our UdpReaderThread.
//! @return the number of sockets monitored by our UdpReaderThread
virtual uint32_t GetUdpReaderThreadSocketCount() const = 0;
//! Returns the total time spent updating our UdpReaderThread.
//! @return the total time spent updating our UdpReaderThread
virtual AZ::TimeMs GetUdpReaderThreadUpdateTime() const = 0;
};
}

@ -149,12 +149,37 @@ namespace AzNetworking
return m_compressorFactories.erase(name) > 0;
}
const NetworkInterfaces& NetworkingSystemComponent::GetNetworkInterfaces() const
{
return m_networkInterfaces;
}
uint32_t NetworkingSystemComponent::GetTcpListenThreadSocketCount() const
{
return m_listenThread->GetSocketCount();
}
AZ::TimeMs NetworkingSystemComponent::GetTcpListenThreadUpdateTime() const
{
return m_listenThread->GetUpdateTimeMs();
}
uint32_t NetworkingSystemComponent::GetUdpReaderThreadSocketCount() const
{
return m_readerThread->GetSocketCount();
}
AZ::TimeMs NetworkingSystemComponent::GetUdpReaderThreadUpdateTime() const
{
return m_readerThread->GetUpdateTimeMs();
}
void NetworkingSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{
AZLOG_INFO("Total sockets monitored by TcpListenThread: %u", m_listenThread->GetSocketCount());
AZLOG_INFO("Total time spent updating TcpListenThread: %lld", aznumeric_cast<AZ::s64>(m_listenThread->GetUpdateTimeMs()));
AZLOG_INFO("Total sockets monitored by UdpReaderThread: %u", m_readerThread->GetSocketCount());
AZLOG_INFO("Total time spent updating UdpReaderThread: %lld", aznumeric_cast<AZ::s64>(m_readerThread->GetUpdateTimeMs()));
AZLOG_INFO("Total sockets monitored by TcpListenThread: %u", GetTcpListenThreadSocketCount());
AZLOG_INFO("Total time spent updating TcpListenThread: %lld", aznumeric_cast<AZ::s64>(GetTcpListenThreadUpdateTime()));
AZLOG_INFO("Total sockets monitored by UdpReaderThread: %u", GetUdpReaderThreadSocketCount());
AZLOG_INFO("Total time spent updating UdpReaderThread: %lld", aznumeric_cast<AZ::s64>(GetUdpReaderThreadUpdateTime()));
for (auto& networkInterface : m_networkInterfaces)
{

@ -63,6 +63,11 @@ namespace AzNetworking
void RegisterCompressorFactory(ICompressorFactory* factory) override;
AZStd::unique_ptr<ICompressor> CreateCompressor(AZ::Name name) override;
bool UnregisterCompressorFactory(AZ::Name name) override;
const NetworkInterfaces& GetNetworkInterfaces() const override;
uint32_t GetTcpListenThreadSocketCount() const override;
AZ::TimeMs GetTcpListenThreadUpdateTime() const override;
uint32_t GetUdpReaderThreadSocketCount() const override;
AZ::TimeMs GetUdpReaderThreadUpdateTime() const override;
//! @}
//! Console commands.
@ -74,7 +79,6 @@ namespace AzNetworking
AZ_CONSOLEFUNC(NetworkingSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for all instantiated network interfaces");
using NetworkInterfaces = AZStd::unordered_map<AZ::Name, AZStd::unique_ptr<INetworkInterface>>;
NetworkInterfaces m_networkInterfaces;
AZStd::unique_ptr<TcpListenThread> m_listenThread;
AZStd::unique_ptr<UdpReaderThread> m_readerThread;

@ -35,6 +35,7 @@ namespace Multiplayer
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using EntityDirtiedEvent = AZ::Event<>;
using EntitySyncRewindEvent = AZ::Event<>;
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
using EntityMigrationEndEvent = AZ::Event<>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
@ -73,6 +74,7 @@ namespace Multiplayer
NetworkEntityHandle GetEntityHandle();
void SetOwningConnectionId(AzNetworking::ConnectionId connectionId);
AzNetworking::ConnectionId GetOwningConnectionId() const;
void SetAllowAutonomy(bool value);
MultiplayerComponentInputVector AllocateComponentInputs();
bool IsProcessingInput() const;
@ -91,12 +93,14 @@ namespace Multiplayer
void MarkDirty();
void NotifyLocalChanges();
void NotifySyncRewindState();
void NotifyMigrationStart(ClientInputId migratedInputId);
void NotifyMigrationEnd();
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler);
void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler);
void AddEntitySyncRewindEventHandler(EntitySyncRewindEvent::Handler& eventHandler);
void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler);
void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler);
void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler);
@ -144,6 +148,7 @@ namespace Multiplayer
EntityStopEvent m_entityStopEvent;
EntityDirtiedEvent m_dirtiedEvent;
EntitySyncRewindEvent m_syncRewindEvent;
EntityMigrationStartEvent m_entityMigrationStartEvent;
EntityMigrationEndEvent m_entityMigrationEndEvent;
EntityServerMigrationEvent m_entityServerMigrationEvent;
@ -157,6 +162,8 @@ namespace Multiplayer
NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole;
NetEntityId m_netEntityId = InvalidNetEntityId;
AzNetworking::ConnectionId m_owningConnectionId = AzNetworking::InvalidConnectionId;
bool m_isProcessingInput = false;
bool m_isMigrationDataValid = false;
bool m_needsToBeStopped = false;

@ -48,7 +48,6 @@ namespace Multiplayer
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
using SessionShutdownEvent = AZ::Event<AzNetworking::INetworkInterface*>;
using OnConnectFunctor = AZStd::function<NetworkEntityHandle(AzNetworking::IConnection*, MultiplayerAgentDatum)>;
//! IMultiplayer provides insight into the Multiplayer session and its Agents
class IMultiplayer
@ -78,10 +77,6 @@ namespace Multiplayer
//! @param handler The SessionShutdownEvent handler to add
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
//! Overrides the default connect behaviour with the provided functor.
//! @param functor the function to invoke during a new connection event
virtual void SetOnConnectFunctor(const OnConnectFunctor& functor) = 0;
//! Sends a packet telling if entity update messages can be sent
//! @param readyForEntityUpdates Ready for entity updates or not
virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0;

@ -18,6 +18,7 @@
#include <AzCore/std/string/fixed_string.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/ConnectionEnums.h>
#include <AzNetworking/DataStructures/ByteBuffer.h>
namespace Multiplayer
{
@ -85,8 +86,7 @@ namespace Multiplayer
Activate
};
// This is just a placeholder
// The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab
// Structure for identifying a specific entity within a spawnable
struct PrefabEntityId
{
AZ_TYPE_INFO(PrefabEntityId, "{EFD37465-CCAC-4E87-A825-41B4010A2C75}");
@ -121,6 +121,13 @@ namespace Multiplayer
return serializer.IsValid();
}
};
struct EntityMigrationMessage
{
NetEntityId m_entityId;
PrefabEntityId m_prefabEntityId;
AzNetworking::PacketEncodingBuffer m_propertyUpdateData;
};
}
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostId);

@ -24,14 +24,12 @@ namespace Multiplayer
ReplicationRecordStats() = default;
ReplicationRecordStats
(
uint32_t authorityToAuthorityCount,
uint32_t authorityToClientCount,
uint32_t authorityToServerCount,
uint32_t authorityToAutonomousCount,
uint32_t autonomousToAuthorityCount
);
uint32_t m_authorityToAuthorityCount = 0;
uint32_t m_authorityToClientCount = 0;
uint32_t m_authorityToServerCount = 0;
uint32_t m_authorityToAutonomousCount = 0;
@ -63,19 +61,16 @@ namespace Multiplayer
bool Serialize(AzNetworking::ISerializer& serializer);
void ConsumeAuthorityToAuthorityBits(uint32_t consumedBits);
void ConsumeAuthorityToClientBits(uint32_t consumedBits);
void ConsumeAuthorityToServerBits(uint32_t consumedBits);
void ConsumeAuthorityToAutonomousBits(uint32_t consumedBits);
void ConsumeAutonomousToAuthorityBits(uint32_t consumedBits);
bool ContainsAuthorityToAuthorityBits() const;
bool ContainsAuthorityToClientBits() const;
bool ContainsAuthorityToServerBits() const;
bool ContainsAuthorityToAutonomousBits() const;
bool ContainsAutonomousToAuthorityBits() const;
uint32_t GetRemainingAuthorityToAuthorityBits() const;
uint32_t GetRemainingAuthorityToClientBits() const;
uint32_t GetRemainingAuthorityToServerBits() const;
uint32_t GetRemainingAuthorityToAutonomousBits() const;
@ -84,13 +79,11 @@ namespace Multiplayer
ReplicationRecordStats GetStats() const;
using RecordBitset = AzNetworking::FixedSizeVectorBitset<MaxRecordBits>;
RecordBitset m_authorityToAuthority;
RecordBitset m_authorityToClient;
RecordBitset m_authorityToServer;
RecordBitset m_authorityToAutonomous;
RecordBitset m_autonomousToAuthority;
uint32_t m_authorityToAuthorityConsumedBits = 0;
uint32_t m_authorityToClientConsumedBits = 0;
uint32_t m_authorityToServerConsumedBits = 0;
uint32_t m_authorityToAutonomousConsumedBits = 0;

@ -274,13 +274,6 @@ namespace {{ Component.attrib['Namespace'] }}
//! Sets the bits in the attached record that correspond to predictable network properties.
void SetPredictableBits();
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Authority') %}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
{% if networkPropertyCount.value > 0 %}
AzNetworking::FixedSizeBitsetView m_authorityToAuthority;
{% endif %}
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Client') %}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
@ -293,7 +286,7 @@ namespace {{ Component.attrib['Namespace'] }}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
{% if networkPropertyCount.value > 0 %}
AzNetworking::FixedSizeBitsetView m_authorityToServer};
AzNetworking::FixedSizeBitsetView m_authorityToServer;
{% endif %}
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Autonomous') %}
@ -314,7 +307,6 @@ namespace {{ Component.attrib['Namespace'] }}
{{ RecordName }}
(
Multiplayer::ReplicationRecord& replicationRecord,
uint32_t authorityToAuthoritySimluationStartOffset,
uint32_t authorityToClientSimluationStartOffset,
uint32_t authorityToServerSimluationStartOffset,
uint32_t authorityToAutonomousStartOffset,
@ -367,8 +359,6 @@ namespace {{ Component.attrib['Namespace'] }}
void ProcessInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) override {}
//! @}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Authority', false)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Authority', true)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', false)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', true)|indent(8) -}}
{{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', false)|indent(8) -}}
@ -457,7 +447,6 @@ namespace {{ Component.attrib['Namespace'] }}
void NetworkAttach(Multiplayer::NetBindComponent* netBindComponent, Multiplayer::ReplicationRecord& currentEntityRecord, Multiplayer::ReplicationRecord& predictableEntityRecord) override;
//! @}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Authority', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', true)|indent(8) -}}
{{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}}
@ -471,10 +460,6 @@ namespace {{ Component.attrib['Namespace'] }}
{% endif %}
{% endfor %}
private:
//! Authority To Authority serializers (hot backup in case of server failure)
bool SerializeAuthorityToAuthorityProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer);
void NotifyChangesAuthorityToAuthorityProperties(const {{ RecordName }}& replicationRecord) const;
//! Authority to Client serializers
bool SerializeAuthorityToClientProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer);
void NotifyChangesAuthorityToClientProperties(const {{ RecordName }}& replicationRecord) const;
@ -499,21 +484,18 @@ namespace {{ Component.attrib['Namespace'] }}
AZStd::unique_ptr<{{ ControllerName }}> m_controller;
//! Network Properties
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Authority')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Server')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Client')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Authority', 'Autonomous')|indent(8) -}}
{{ DeclareNetworkPropertyVars(Component, 'Autonomous', 'Authority')|indent(8) }}
//! Network Properties for reflection and editor support
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Authority')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Server')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Client')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Autonomous')|indent(8) -}}
{{ DeclareNetworkPropertyReflectVars(Component, 'Autonomous', 'Authority')|indent(8) }}
//! NetworkProperty Events
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Authority')|indent(8) -}}
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Server')|indent(8) -}}
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Client')|indent(8) -}}
{{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Autonomous')|indent(8) -}}

@ -980,7 +980,6 @@ namespace {{ Component.attrib['Namespace'] }}
{
{{ DeclareRemoteProcedureEnumerations(Component)|indent(8) }}
{{ DeclareNetworkPropertyEnumerations(Component)|indent(8) }}
{{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Authority')|indent(8) }}
{{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Client')|indent(8) }}
{{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Server')|indent(8) }}
{{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Autonomous')|indent(8) }}
@ -995,7 +994,6 @@ namespace {{ Component.attrib['Namespace'] }}
{{ RecordName }}::{{ RecordName }}
(
[[maybe_unused]] Multiplayer::ReplicationRecord& replicationRecord,
[[maybe_unused]] uint32_t authorityToAuthorityStartOffset,
[[maybe_unused]] uint32_t authorityToClientStartOffset,
[[maybe_unused]] uint32_t authorityToServerStartOffset,
[[maybe_unused]] uint32_t authorityToAutonomousStartOffset,
@ -1003,13 +1001,6 @@ namespace {{ Component.attrib['Namespace'] }}
)
{% set comma = joiner(" ,") %}
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Authority') %}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
{% if networkPropertyCount.value > 0 %}
{{ comma()|default(" :", true) }} m_authorityToAuthority(replicationRecord.m_authorityToAuthority, authorityToAuthorityStartOffset, replicationRecord.ContainsAuthorityToAuthorityBits() ? static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count) : 0)
{% endif %}
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Client') %}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
@ -1021,7 +1012,7 @@ namespace {{ Component.attrib['Namespace'] }}
{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%}
{% endcall %}
{% if networkPropertyCount.value > 0 %}
{{ comma()|default(" :", true) }} authorityToServer }}(replicationRecord.m_authorityToServer, authorityToServerStartOffset, replicationRecord.ContainsAuthorityToServerBits() ? static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count) : 0)
{{ comma()|default(" :", true) }} m_authorityToServer(replicationRecord.m_authorityToServer, authorityToServerStartOffset, replicationRecord.ContainsAuthorityToServerBits() ? static_cast<int32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count) : 0)
{% endif %}
{% set networkPropertyCount = {'value' : 0} %}
{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Autonomous') %}
@ -1043,9 +1034,6 @@ namespace {{ Component.attrib['Namespace'] }}
AZStd::unique_ptr<{{ RecordName }}> {{ RecordName }}::AllocateRecord(Multiplayer::ReplicationRecord& replicationRecord)
{
uint32_t authorityToAuthorityStart = replicationRecord.m_authorityToAuthority.GetSize();
replicationRecord.m_authorityToAuthority.Resize(authorityToAuthorityStart + static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count));
uint32_t authorityToClientStart = replicationRecord.m_authorityToClient.GetSize();
replicationRecord.m_authorityToClient.Resize(authorityToClientStart + static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count));
@ -1059,7 +1047,6 @@ namespace {{ Component.attrib['Namespace'] }}
replicationRecord.m_autonomousToAuthority.Resize(autonomousToAuthorityStart + static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Autonomous', 'Authority') }}::Count));
return AZStd::unique_ptr<{{ RecordName }}>(new {{ RecordName }}(replicationRecord,
authorityToAuthorityStart,
authorityToClientStart,
authorityToServerStart,
authorityToAutonomousStart,
@ -1069,7 +1056,6 @@ namespace {{ Component.attrib['Namespace'] }}
bool {{ RecordName }}::CanAttachRecord(Multiplayer::ReplicationRecord& replicationRecord)
{
bool canAttach{ true };
canAttach &= replicationRecord.ContainsAuthorityToAuthorityBits() ? (replicationRecord.GetRemainingAuthorityToAuthorityBits() >= static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count)) : true;
canAttach &= replicationRecord.ContainsAuthorityToClientBits() ? (replicationRecord.GetRemainingAuthorityToClientBits() >= static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count)) : true;
canAttach &= replicationRecord.ContainsAuthorityToServerBits() ? (replicationRecord.GetRemainingAuthorityToServerBits() >= static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count)) : true;
canAttach &= replicationRecord.ContainsAuthorityToAutonomousBits() ? (replicationRecord.GetRemainingAuthorityToAutonomousBits() >= static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Autonomous') }}::Count)) : true;
@ -1079,9 +1065,6 @@ namespace {{ Component.attrib['Namespace'] }}
{{ RecordName }} {{ RecordName }}::AttachRecord(Multiplayer::ReplicationRecord& replicationRecord)
{
uint32_t authorityToAuthorityStart = replicationRecord.m_authorityToAuthorityConsumedBits;
replicationRecord.ConsumeAuthorityToAuthorityBits(static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count));
uint32_t authorityToClientStart = replicationRecord.m_authorityToClientConsumedBits;
replicationRecord.ConsumeAuthorityToClientBits(static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count));
@ -1095,7 +1078,6 @@ namespace {{ Component.attrib['Namespace'] }}
replicationRecord.ConsumeAutonomousToAuthorityBits(static_cast<uint32_t>({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Autonomous', 'Authority') }}::Count));
return {{ RecordName }}(replicationRecord,
authorityToAuthorityStart,
authorityToClientStart,
authorityToServerStart,
authorityToAutonomousStart,
@ -1169,9 +1151,7 @@ namespace {{ Component.attrib['Namespace'] }}
return static_cast<{{ ComponentName }}&>(GetOwner());
}
{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Authority', false, ControllerBaseName)|indent(4) -}}
{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Authority', true, ControllerBaseName)|indent(4) -}}
{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', false, ControllerBaseName)|indent(4) -}}
{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', false, ControllerBaseName)|indent(4) -}}
{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', true, ControllerBaseName)|indent(4) -}}
{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', false, ControllerBaseName)|indent(4) -}}
{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', true, ControllerBaseName)|indent(4) -}}
@ -1204,8 +1184,7 @@ namespace {{ Component.attrib['Namespace'] }}
{
serializeContext->Class<{{ ComponentBaseName }}, Multiplayer::MultiplayerComponent>()
->Version(1)
{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(16) -}}
{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(16) -}}
{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(16) -}}
{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(16) -}}
{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(16) -}}
{{ DefineNetworkPropertyReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(16) }}
@ -1227,8 +1206,7 @@ namespace {{ Component.attrib['Namespace'] }}
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "{{ Component.attrib['Namespace'] }}")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(20) -}}
{{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(20) }}
@ -1254,7 +1232,6 @@ namespace {{ Component.attrib['Namespace'] }}
->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}")
// Reflect Network Properties Get, Set, and OnChanged methods
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Authority', ComponentName) | indent(16) -}}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Server', ComponentName) | indent(16) -}}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Client', ComponentName) | indent(16) -}}
{{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Autonomous', ComponentName) | indent(16) -}}
@ -1348,12 +1325,10 @@ namespace {{ Component.attrib['Namespace'] }}
{% endcall %}
}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Authority', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', false, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Authority', true, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', true, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', true, ComponentBaseName)|indent(4) -}}
{{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', true, ComponentBaseName)|indent(4) -}}
@ -1411,10 +1386,6 @@ namespace {{ Component.attrib['Namespace'] }}
{{ RecordName }} record = {{ RecordName }}::AttachRecord(replicationRecord);
if (replicationRecord.ContainsAuthorityToAuthorityBits())
{
SerializeAuthorityToAuthorityProperties(record, serializer);
}
if (replicationRecord.ContainsAuthorityToClientBits())
{
SerializeAuthorityToClientProperties(record, serializer);
@ -1490,8 +1461,7 @@ namespace {{ Component.attrib['Namespace'] }}
void {{ ComponentBaseName }}::NetworkAttach(Multiplayer::NetBindComponent* netBindComponent, Multiplayer::ReplicationRecord& currentEntityRecord, Multiplayer::ReplicationRecord& predictableEntityRecord)
{
m_netBindComponent = netBindComponent;
{{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Authority', ComponentBaseName)|indent(8) -}}
{{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Server', ComponentBaseName)|indent(8) -}}
{{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Server', ComponentBaseName)|indent(8) -}}
{{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Client', ComponentBaseName)|indent(8) -}}
{{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(8) -}}
{{ DefineNetworkPropertyEditConstruction(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(8) }}
@ -1504,8 +1474,6 @@ namespace {{ Component.attrib['Namespace'] }}
m_controller.get()->NetworkAttach(netBindComponent, predictableEntityRecord);
}
{{ DeclareNetworkPropertySetSerializer(Component, 'Authority', 'Authority', ComponentBaseName, RecordName)|indent(4) }}
{{ DeclareNetworkPropertySetNotifyChanges(Component, 'Authority', 'Authority', ComponentBaseName, RecordName)|indent(4) }}
{{ DeclareNetworkPropertySetSerializer(Component, 'Authority', 'Server', ComponentBaseName, RecordName)|indent(4) }}
{{ DeclareNetworkPropertySetNotifyChanges(Component, 'Authority', 'Server', ComponentBaseName, RecordName)|indent(4) }}
{{ DeclareNetworkPropertySetSerializer(Component, 'Authority', 'Client', ComponentBaseName, RecordName)|indent(4) }}

@ -17,7 +17,7 @@
<Include File="Source/NetworkInput/NetworkInputMigrationVector.h"/>
<Include File="AzNetworking/DataStructures/ByteBuffer.h"/>
<NetworkProperty Type="Multiplayer::ClientInputId" Name="LastInputId" Init="Multiplayer::ClientInputId{ 0 }" ReplicateFrom="Authority" ReplicateTo="Authority" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" GenerateEventBindings="false" />
<NetworkProperty Type="Multiplayer::ClientInputId" Name="LastInputId" Init="Multiplayer::ClientInputId{ 0 }" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" GenerateEventBindings="false" />
<RemoteProcedure Name="SendClientInput" InvokeFrom="Autonomous" HandleOn="Authority" IsPublic="true" IsReliable="false" Description="Client to server move / input RPC">
<Param Type="Multiplayer::NetworkInputArray" Name="inputArray" />

@ -28,12 +28,6 @@
<Member Type="Multiplayer::LongNetworkString" Name="command" />
</Packet>
<Packet Name="SyncConnectionCvars" Desc="Packet for synchornizing connection quality of service simulation cvars">
<Member Type="int32_t" Name="lossPercent" Init="0" />
<Member Type="AZ::TimeMs" Name="latencyMs" Init="AZ::TimeMs{ 0 }" />
<Member Type="AZ::TimeMs" Name="varianceMs" Init="AZ::TimeMs{ 0 }" />
</Packet>
<Packet Name="EntityUpdates" Desc="A packet that contains multiple entity updates">
<Member Type="AZ::TimeMs" Name="hostTimeMs" Init="AZ::TimeMs{ 0 }" />
<Member Type="Multiplayer::HostFrameId" Name="hostFrameId" Init="Multiplayer::InvalidHostFrameId" />
@ -49,14 +43,4 @@
<Member Type="AzNetworking::IpAddress" Name="remoteServerAddress" Init="AzNetworking::IpAddress()" />
<Member Type="AZ::TimeMs" Name="lastInputGameTimeMs" Init="AZ::TimeMs{ 0 }" />
</Packet>
<Packet Name="NotifyClientMigration" Desc="Tells a server that a client is about to migrate">
<Member Type="uint64_t" Name="temporaryUserIdentifier" Init="0" />
</Packet>
<Packet Name="EntityMigration" Desc="A packet that migrates a set of entities to a new server">
<Member Type="Multiplayer::NetEntityId" Name="entityId" Init="Multiplayer::InvalidNetEntityId" />
<Member Type="Multiplayer::PrefabEntityId" Name="prefabEntityId" Init="" />
<Member Type="AzNetworking::PacketEncodingBuffer" Name="propertyUpdateData" Init="" SuppressFromInitializerList="true" />
</Packet>
</PacketGroup>

@ -154,12 +154,18 @@ namespace Multiplayer
void NetBindComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId)
{
m_owningConnectionId = connectionId;
for (MultiplayerComponent* multiplayerComponent : m_multiplayerInputComponentVector)
{
multiplayerComponent->SetOwningConnectionId(connectionId);
}
}
AzNetworking::ConnectionId NetBindComponent::GetOwningConnectionId() const
{
return m_owningConnectionId;
}
void NetBindComponent::SetAllowAutonomy(bool value)
{
// This flag allows a player host to autonomously control their player entity, even though the entity is in an authority role
@ -290,6 +296,11 @@ namespace Multiplayer
m_localNotificationRecord.Clear();
}
void NetBindComponent::NotifySyncRewindState()
{
m_syncRewindEvent.Signal();
}
void NetBindComponent::NotifyMigrationStart(ClientInputId migratedInputId)
{
m_entityMigrationStartEvent.Signal(migratedInputId);
@ -315,6 +326,11 @@ namespace Multiplayer
eventHandler.Connect(m_dirtiedEvent);
}
void NetBindComponent::AddEntitySyncRewindEventHandler(EntitySyncRewindEvent::Handler& eventHandler)
{
eventHandler.Connect(m_syncRewindEvent);
}
void NetBindComponent::AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityMigrationStartEvent);

@ -13,6 +13,8 @@
#include <Source/Debug/MultiplayerDebugSystemComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Interface/Interface.h>
#include <AzNetworking/Framework/INetworking.h>
#include <AzNetworking/Framework/INetworkInterface.h>
#include <Multiplayer/IMultiplayer.h>
namespace Multiplayer
@ -233,6 +235,43 @@ namespace Multiplayer
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
if (m_displayNetworkingStats)
{
if (ImGui::Begin("Networking Stats", &m_displayNetworkingStats, ImGuiWindowFlags_None))
{
AzNetworking::INetworking* networking = AZ::Interface<AzNetworking::INetworking>::Get();
ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount());
ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast<AZ::s64>(networking->GetTcpListenThreadUpdateTime()));
ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount());
ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast<AZ::s64>(networking->GetUdpReaderThreadUpdateTime()));
for (auto& networkInterface : networking->GetNetworkInterfaces())
{
const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp";
const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer";
const uint32_t port = aznumeric_cast<uint32_t>(networkInterface.second->GetPort());
ImGui::Text("%sNetworkInterface: %s - open to %s on port %u", protocol, networkInterface.second->GetName().GetCStr(), trustZone, port);
const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics();
ImGui::Text(" - Total time spent updating in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_updateTimeMs));
ImGui::Text(" - Total number of connections: %llu", aznumeric_cast<AZ::u64>(metrics.m_connectionCount));
ImGui::Text(" - Total send time in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_sendTimeMs));
ImGui::Text(" - Total sent packets: %llu", aznumeric_cast<AZ::s64>(metrics.m_sendPackets));
ImGui::Text(" - Total sent bytes after compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytes));
ImGui::Text(" - Total sent bytes before compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendBytesUncompressed));
ImGui::Text(" - Total sent compressed packets without benefit: %llu", aznumeric_cast<AZ::u64>(metrics.m_sendCompressedPacketsNoGain));
ImGui::Text(" - Total gain from packet compression: %lld", aznumeric_cast<AZ::s64>(metrics.m_sendBytesCompressedDelta));
ImGui::Text(" - Total packets resent: %llu", aznumeric_cast<AZ::u64>(metrics.m_resentPackets));
ImGui::Text(" - Total receive time in milliseconds: %lld", aznumeric_cast<AZ::s64>(metrics.m_recvTimeMs));
ImGui::Text(" - Total received packets: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvPackets));
ImGui::Text(" - Total received bytes after compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytes));
ImGui::Text(" - Total received bytes before compression: %llu", aznumeric_cast<AZ::u64>(metrics.m_recvBytesUncompressed));
ImGui::Text(" - Total packets discarded due to load: %llu", aznumeric_cast<AZ::u64>(metrics.m_discardedPackets));
}
}
}
if (m_displayMultiplayerStats)
{
if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None))

@ -161,9 +161,13 @@ namespace Multiplayer
// Handle deferred local rpc messages that were generated during the updates
m_networkEntityManager.DispatchLocalDeferredRpcMessages();
m_networkEntityManager.NotifyEntitiesChanged();
// INetworking ticks immediately before IMultiplayer, so all our pending RPC's and network property updates have now been processed
// Restore any entities that were rewound during input processing so that normal gameplay updates have the correct state
Multiplayer::GetNetworkTime()->ClearRewoundEntities();
// Let the network system know the frame is done and we can collect dirty bits
m_networkEntityManager.NotifyEntitiesChanged();
m_networkEntityManager.NotifyEntitiesDirtied();
MultiplayerStats& stats = GetStats();
@ -302,25 +306,32 @@ namespace Multiplayer
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] MultiplayerPackets::SyncConsole& packet
AzNetworking::IConnection* connection,
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader,
MultiplayerPackets::ReadyForEntityUpdates& packet
)
{
ExecuteConsoleCommandList(connection, packet.GetCommandSet());
return true;
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
if (connectionData)
{
connectionData->SetCanSendUpdates(packet.GetReadyForEntityUpdates());
return true;
}
return false;
}
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] MultiplayerPackets::ConsoleCommand& packet
[[maybe_unused]] MultiplayerPackets::SyncConsole& packet
)
{
const bool isAcceptor = (connection->GetConnectionRole() == ConnectionRole::Acceptor); // We're hosting if we accepted the connection
const AZ::ConsoleFunctorFlags requiredSet = isAcceptor ? AZ::ConsoleFunctorFlags::AllowClientSet : AZ::ConsoleFunctorFlags::Null;
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(packet.GetCommand().c_str(), AZ::ConsoleSilentMode::NotSilent, AZ::ConsoleInvokedFrom::AzNetworking, requiredSet);
if (GetAgentType() != MultiplayerAgentType::Client)
{
return false;
}
ExecuteConsoleCommandList(connection, packet.GetCommandSet());
return true;
}
@ -328,10 +339,12 @@ namespace Multiplayer
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const IPacketHeader& packetHeader,
[[maybe_unused]] MultiplayerPackets::SyncConnectionCvars& packet
[[maybe_unused]] MultiplayerPackets::ConsoleCommand& packet
)
{
connection->SetConnectionQuality(ConnectionQuality(packet.GetLossPercent(), packet.GetLatencyMs(), packet.GetVarianceMs()));
const bool isClient = (GetAgentType() == MultiplayerAgentType::Client);
const AZ::ConsoleFunctorFlags requiredSet = isClient ? AZ::ConsoleFunctorFlags::Null : AZ::ConsoleFunctorFlags::AllowClientSet;
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(packet.GetCommand().c_str(), AZ::ConsoleSilentMode::NotSilent, AZ::ConsoleInvokedFrom::AzNetworking, requiredSet);
return true;
}
@ -395,39 +408,6 @@ namespace Multiplayer
return false;
}
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader,
[[maybe_unused]] MultiplayerPackets::NotifyClientMigration& packet
)
{
return false;
}
bool MultiplayerSystemComponent::HandleRequest
(
[[maybe_unused]] AzNetworking::IConnection* connection,
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader,
[[maybe_unused]] MultiplayerPackets::EntityMigration& packet
)
{
return false;
}
bool MultiplayerSystemComponent::HandleRequest( AzNetworking::IConnection* connection,
[[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet)
{
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
if (connectionData)
{
connectionData->SetCanSendUpdates(packet.GetReadyForEntityUpdates());
return true;
}
return false;
}
ConnectResult MultiplayerSystemComponent::ValidateConnect
(
[[maybe_unused]] const IpAddress& remoteAddress,
@ -456,44 +436,36 @@ namespace Multiplayer
m_connAcquiredEvent.Signal(datum);
}
if (m_onConnectFunctor)
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
{
// Default OnConnect behaviour has been overridden
m_onConnectFunctor(connection, datum);
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()), 1);
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity());
NetworkEntityHandle controlledEntity;
if (entityList.size() > 0)
{
controlledEntity = entityList[0];
controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
}
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
}
else
{
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()), 1);
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity());
NetworkEntityHandle controlledEntity;
if (entityList.size() > 0)
{
controlledEntity = entityList[0];
controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
}
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
connection->SetUserData(new ClientToServerConnectionData(connection, *this));
}
else
{
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ClientToServerConnectionData(connection, *this));
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
}
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
}
}
@ -566,11 +538,6 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent);
}
void MultiplayerSystemComponent::SetOnConnectFunctor(const OnConnectFunctor& functor)
{
m_onConnectFunctor = functor;
}
void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
{
IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();

@ -63,15 +63,12 @@ namespace Multiplayer
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::SyncConsole& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ConsoleCommand& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::SyncConnectionCvars& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityUpdates& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityRpcs& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::NotifyClientMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityMigration& packet);
bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet);
//! IConnectionListener interface
//! @{
@ -89,7 +86,6 @@ namespace Multiplayer
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
void SetOnConnectFunctor(const OnConnectFunctor& functor) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
AZ::TimeMs GetCurrentHostTimeMs() const override;
INetworkTime* GetNetworkTime() override;
@ -120,8 +116,6 @@ namespace Multiplayer
SessionShutdownEvent m_shutdownEvent;
ConnectionAcquiredEvent m_connAcquiredEvent;
OnConnectFunctor m_onConnectFunctor = nullptr;
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId;
};

@ -656,7 +656,7 @@ namespace Multiplayer
{
case Mode::LocalServerToRemoteClient:
{
// don't trust the client by default
// Don't trust the client by default
result = UpdateValidationResult::DropMessageAndDisconnect;
// Clients sending data must have a replicator and be sending in the correct mode, further, they must have a replicator and can never delete a replicator
if (updateMessage.GetNetworkRole() == NetEntityRole::Authority && entityReplicator && !updateMessage.GetIsDelete())
@ -671,7 +671,7 @@ namespace Multiplayer
}
else
{
// we can process this
// We can process this
result = UpdateValidationResult::HandleMessage;
}
} // If we've migrated the entity away from the server, but we get this late, just drop it
@ -699,7 +699,7 @@ namespace Multiplayer
case Mode::LocalServerToRemoteServer:
{
AZ_Assert(updateMessage.GetNetworkRole() == NetEntityRole::Server || updateMessage.GetIsDelete(), "Unexpected update type coming from peer server");
// trust messages from a peer server by default
// Trust messages from a peer server by default
result = UpdateValidationResult::HandleMessage;
// If we have a replicator, make sure we're in the correct state
if (entityReplicator)
@ -782,7 +782,7 @@ namespace Multiplayer
PrefabEntityId prefabEntityId;
if (updateMessage.GetHasValidPrefabId())
{
// If the update packet contained a sliceEntryId, use that directly
// If the update packet contained a PrefabEntityId, use that directly
prefabEntityId = updateMessage.GetPrefabEntityId();
}
else
@ -940,7 +940,7 @@ namespace Multiplayer
{
const ReplicationSet& newWindow = m_replicationWindow->GetReplicationSet();
// walk both for adds and removals
// Walk both for adds and removals
auto newWindowIter = newWindow.begin();
auto currWindowIter = m_entityReplicatorMap.begin();
while (newWindowIter != newWindow.end() && currWindowIter != m_entityReplicatorMap.end())
@ -959,9 +959,9 @@ namespace Multiplayer
}
++currWindowIter;
}
else // same entity
else // Same entity
{
// check if we changed modes
// Check if we changed modes
EntityReplicator* currReplicator = currWindowIter->second.get();
if (currReplicator->GetRemoteNetworkRole() != newWindowIter->second.m_netEntityRole)
{
@ -973,14 +973,14 @@ namespace Multiplayer
}
}
// do remaining adds
// Do remaining adds
while (newWindowIter != newWindow.end())
{
AddEntityReplicator(newWindowIter->first, newWindowIter->second.m_netEntityRole);
++newWindowIter;
}
// do remaining removes
// Do remaining removes
while (currWindowIter != m_entityReplicatorMap.end())
{
EntityReplicator* currReplicator = currWindowIter->second.get();
@ -1028,13 +1028,13 @@ namespace Multiplayer
const EntityReplicator* entityReplicator = GetEntityReplicator(entityHandle.GetNetEntityId());
hasAuthority = (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority); // Make sure someone hasn't migrated this already
isInDomain = (m_remoteEntityDomain && m_remoteEntityDomain->IsInDomain(entityHandle)); // Make sure the remote side would want it
isInDomain = (m_remoteEntityDomain && m_remoteEntityDomain->IsInDomain(entityHandle)); // Make sure the remote side would want it
if (entityReplicator && entityReplicator->GetBoundLocalNetworkRole() == NetEntityRole::Authority)
{
isMarkedForRemoval = entityReplicator->IsMarkedForRemoval(); // Make sure we aren't telling the other side to remove the replicator
isMarkedForRemoval = entityReplicator->IsMarkedForRemoval(); // Make sure we aren't telling the other side to remove the replicator
const PropertyPublisher* propertyPublisher = entityReplicator->GetPropertyPublisher();
AZ_Assert(propertyPublisher, "Expected to have a property publisher");
isRemoteReplicatorEstablished = propertyPublisher->IsRemoteReplicatorEstablished(); // Make sure they are setup to receive the replicator
isRemoteReplicatorEstablished = propertyPublisher->IsRemoteReplicatorEstablished(); // Make sure they are setup to receive the replicator
}
return hasAuthority && isInDomain && !isMarkedForRemoval && isRemoteReplicatorEstablished;
@ -1094,9 +1094,9 @@ namespace Multiplayer
}
bool didSucceed = true;
MultiplayerPackets::EntityMigration message;
message.SetEntityId(replicator->GetEntityHandle().GetNetEntityId());
message.SetPrefabEntityId(netBindComponent->GetPrefabEntityId());
EntityMigrationMessage message;
message.m_entityId = replicator->GetEntityHandle().GetNetEntityId();
message.m_prefabEntityId = netBindComponent->GetPrefabEntityId();
if (localEnt->GetState() == AZ::Entity::State::Active)
{
@ -1110,17 +1110,18 @@ namespace Multiplayer
// Send an update packet if it needs one
propPublisher->GenerateRecord();
bool needsNetworkPropertyUpdate = propPublisher->PrepareSerialization();
AzNetworking::NetworkInputSerializer inputSerializer(message.ModifyPropertyUpdateData().GetBuffer(), message.ModifyPropertyUpdateData().GetCapacity());
AzNetworking::NetworkInputSerializer inputSerializer(message.m_propertyUpdateData.GetBuffer(), message.m_propertyUpdateData.GetCapacity());
if (needsNetworkPropertyUpdate)
{
// write out entity state into the buffer
// Write out entity state into the buffer
propPublisher->UpdateSerialization(inputSerializer);
}
didSucceed &= inputSerializer.IsValid();
message.ModifyPropertyUpdateData().Resize(inputSerializer.GetSize());
message.m_propertyUpdateData.Resize(inputSerializer.GetSize());
}
AZ_Assert(didSucceed, "Failed to migrate entity from server");
m_connection.SendReliablePacket(message);
// TODO: Move this to an event
//m_connection.SendReliablePacket(message);
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote manager id %d", netEntityId, aznumeric_cast<int32_t>(GetRemoteHostId()));
// Immediately add a new replicator so that we catch RPC invocations, the remote side will make us a new one, and then remove us if needs be
@ -1128,21 +1129,21 @@ namespace Multiplayer
}
}
bool EntityReplicationManager::HandleMessage([[maybe_unused]] AzNetworking::IConnection* invokingConnection, MultiplayerPackets::EntityMigration& message)
bool EntityReplicationManager::HandleEntityMigration([[maybe_unused]] AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message)
{
EntityReplicator* replicator = GetEntityReplicator(message.GetEntityId());
EntityReplicator* replicator = GetEntityReplicator(message.m_entityId);
{
if (message.GetPropertyUpdateData().GetSize() > 0)
if (message.m_propertyUpdateData.GetSize() > 0)
{
AzNetworking::TrackChangedSerializer<AzNetworking::NetworkOutputSerializer> outputSerializer(message.ModifyPropertyUpdateData().GetBuffer(), message.ModifyPropertyUpdateData().GetSize());
AzNetworking::TrackChangedSerializer<AzNetworking::NetworkOutputSerializer> outputSerializer(message.m_propertyUpdateData.GetBuffer(), message.m_propertyUpdateData.GetSize());
if (!HandlePropertyChangeMessage
(
replicator,
AzNetworking::InvalidPacketId,
message.GetEntityId(),
message.m_entityId,
NetEntityRole::Server,
outputSerializer,
message.GetPrefabEntityId()
message.m_prefabEntityId
))
{
AZ_Assert(false, "Unable to process network properties during server entity migration");
@ -1150,10 +1151,10 @@ namespace Multiplayer
}
}
}
// the HandlePropertyChangeMessage will have made a replicator if we didn't have one already
// The HandlePropertyChangeMessage will have made a replicator if we didn't have one already
if (!replicator)
{
replicator = GetEntityReplicator(message.GetEntityId());
replicator = GetEntityReplicator(message.m_entityId);
}
AZ_Assert(replicator, "Do not have replicator after handling migration message");
@ -1170,7 +1171,7 @@ namespace Multiplayer
netBindComponent->ActivateControllers(EntityIsMigrating::True);
}
// change the role on the replicator
// Change the role on the replicator
AddEntityReplicator(entityHandle, NetEntityRole::Server);
AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote manager id %d", entityHandle.GetNetEntityId(), aznumeric_cast<int32_t>(GetRemoteHostId()));

@ -17,6 +17,8 @@
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
#include <AzNetworking/DataStructures/TimeoutQueue.h>
#include <AzNetworking/PacketLayer/IPacketHeader.h>
@ -26,7 +28,6 @@
#include <AzCore/std/limits.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/EBus/ScheduledEvent.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
namespace AzNetworking
{
@ -82,7 +83,7 @@ namespace Multiplayer
void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler);
bool HandleMessage(AzNetworking::IConnection* invokingConnection, MultiplayerPackets::EntityMigration& message);
bool HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message);
bool HandleEntityDeleteMessage(EntityReplicator* entityReplicator, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
bool HandleEntityUpdateMessage(AzNetworking::IConnection* invokingConnection, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage);
bool HandleEntityRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& message);

@ -16,14 +16,12 @@ namespace Multiplayer
{
ReplicationRecordStats::ReplicationRecordStats
(
uint32_t authorityToAuthorityCount,
uint32_t authorityToClientCount,
uint32_t authorityToServerCount,
uint32_t authorityToAutonomousCount,
uint32_t autonomousToAuthorityCount
)
: m_authorityToAuthorityCount(authorityToAuthorityCount)
, m_authorityToClientCount(authorityToClientCount)
: m_authorityToClientCount(authorityToClientCount)
, m_authorityToServerCount(authorityToServerCount)
, m_authorityToAutonomousCount(authorityToAutonomousCount)
, m_autonomousToAuthorityCount(autonomousToAuthorityCount)
@ -33,8 +31,7 @@ namespace Multiplayer
bool ReplicationRecordStats::operator ==(const ReplicationRecordStats& rhs) const
{
return (m_authorityToAuthorityCount == rhs.m_authorityToAuthorityCount)
&& (m_authorityToClientCount == rhs.m_authorityToClientCount)
return (m_authorityToClientCount == rhs.m_authorityToClientCount)
&& (m_authorityToServerCount == rhs.m_authorityToServerCount)
&& (m_authorityToAutonomousCount == rhs.m_authorityToAutonomousCount)
&& (m_autonomousToAuthorityCount == rhs.m_autonomousToAuthorityCount);
@ -44,7 +41,6 @@ namespace Multiplayer
{
return ReplicationRecordStats
{
(m_authorityToAuthorityCount - rhs.m_authorityToAuthorityCount),
(m_authorityToClientCount - rhs.m_authorityToClientCount),
(m_authorityToServerCount - rhs.m_authorityToServerCount),
(m_authorityToAutonomousCount - rhs.m_authorityToAutonomousCount),
@ -71,7 +67,6 @@ namespace Multiplayer
bool ReplicationRecord::AreAllBitsConsumed() const
{
bool ret = true;
ret &= m_authorityToAuthorityConsumedBits == m_authorityToAuthority.GetSize();
ret &= m_authorityToClientConsumedBits == m_authorityToClient.GetSize();
ret &= m_authorityToServerConsumedBits == m_authorityToServer.GetSize();
ret &= m_authorityToAutonomousConsumedBits == m_authorityToAutonomous.GetSize();
@ -81,7 +76,6 @@ namespace Multiplayer
void ReplicationRecord::ResetConsumedBits()
{
m_authorityToAuthorityConsumedBits = 0;
m_authorityToClientConsumedBits = 0;
m_authorityToServerConsumedBits = 0;
m_authorityToAutonomousConsumedBits = 0;
@ -92,11 +86,7 @@ namespace Multiplayer
{
ResetConsumedBits();
uint32_t recordSize = m_authorityToAuthority.GetSize();
m_authorityToAuthority.Clear();
m_authorityToAuthority.Resize(recordSize);
recordSize = m_authorityToClient.GetSize();
uint32_t recordSize = m_authorityToClient.GetSize();
m_authorityToClient.Clear();
m_authorityToClient.Resize(recordSize);
@ -115,7 +105,6 @@ namespace Multiplayer
void ReplicationRecord::Append(const ReplicationRecord &rhs)
{
m_authorityToAuthority |= rhs.m_authorityToAuthority;
m_authorityToClient |= rhs.m_authorityToClient;
m_authorityToServer |= rhs.m_authorityToServer;
m_authorityToAutonomous |= rhs.m_authorityToAutonomous;
@ -124,7 +113,6 @@ namespace Multiplayer
void ReplicationRecord::Subtract(const ReplicationRecord &rhs)
{
m_authorityToAuthority.Subtract(rhs.m_authorityToAuthority);
m_authorityToClient.Subtract(rhs.m_authorityToClient);
m_authorityToServer.Subtract(rhs.m_authorityToServer);
m_authorityToAutonomous.Subtract(rhs.m_authorityToAutonomous);
@ -134,10 +122,6 @@ namespace Multiplayer
bool ReplicationRecord::HasChanges() const
{
bool hasChanges(false);
if (ContainsAuthorityToAuthorityBits())
{
hasChanges = hasChanges ? hasChanges : m_authorityToAuthority.AnySet();
}
if (ContainsAuthorityToClientBits())
{
hasChanges = hasChanges ? hasChanges : m_authorityToClient.AnySet();
@ -159,10 +143,6 @@ namespace Multiplayer
bool ReplicationRecord::Serialize(AzNetworking::ISerializer& serializer)
{
if (ContainsAuthorityToAuthorityBits())
{
serializer.Serialize(m_authorityToAuthority, "AuthorityToAuthorityRecord");
}
if (ContainsAuthorityToClientBits())
{
serializer.Serialize(m_authorityToClient, "AuthorityToClientRecord");
@ -182,14 +162,6 @@ namespace Multiplayer
return serializer.IsValid();
}
void ReplicationRecord::ConsumeAuthorityToAuthorityBits(uint32_t consumedBits)
{
if (ContainsAuthorityToAuthorityBits())
{
m_authorityToAuthorityConsumedBits += consumedBits;
}
}
void ReplicationRecord::ConsumeAuthorityToClientBits(uint32_t consumedBits)
{
if (ContainsAuthorityToClientBits())
@ -222,12 +194,6 @@ namespace Multiplayer
}
}
bool ReplicationRecord::ContainsAuthorityToAuthorityBits() const
{
return (m_netEntityRole == NetEntityRole::Authority)
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
bool ReplicationRecord::ContainsAuthorityToClientBits() const
{
return (m_netEntityRole != NetEntityRole::Authority)
@ -252,11 +218,6 @@ namespace Multiplayer
|| (m_netEntityRole == NetEntityRole::InvalidRole);
}
uint32_t ReplicationRecord::GetRemainingAuthorityToAuthorityBits() const
{
return m_authorityToAuthorityConsumedBits < m_authorityToAuthority.GetValidBitCount() ? m_authorityToAuthority.GetValidBitCount() - m_authorityToAuthorityConsumedBits : 0;
}
uint32_t ReplicationRecord::GetRemainingAuthorityToClientBits() const
{
return m_authorityToClientConsumedBits < m_authorityToClient.GetValidBitCount() ? m_authorityToClient.GetValidBitCount() - m_authorityToClientConsumedBits : 0;
@ -281,7 +242,6 @@ namespace Multiplayer
{
return ReplicationRecordStats
{
m_authorityToAuthorityConsumedBits,
m_authorityToClientConsumedBits,
m_authorityToServerConsumedBits,
m_authorityToAutonomousConsumedBits,

@ -13,10 +13,15 @@
#include <Source/NetworkTime/NetworkTime.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <AzCore/Math/ShapeIntersection.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
namespace Multiplayer
{
AZ_CVAR(float, sv_RewindVolumeExtrudeDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The amount to increase rewind volume checks to account for fast moving entities");
NetworkTime::NetworkTime()
{
AZ::Interface<INetworkTime>::Register(this);
@ -73,35 +78,59 @@ namespace Multiplayer
void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume)
{
// TODO: extrude rewind volume for initial gather
AZStd::vector<AzFramework::VisibilityEntry*> gatheredEntries;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(rewindVolume, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData)
// Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities
const AZ::Aabb expandedVolume = rewindVolume.GetExpanded(AZ::Vector3(sv_RewindVolumeExtrudeDistance));
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
AZStd::vector<NetBindComponent*> gatheredEntities;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
[entityBoundsUnion, rewindVolume, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size());
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
{
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
// TODO: offset aabb for exact rewound position and check against the non-extruded rewind volume
gatheredEntries.push_back(visEntry);
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityLocalBoundsUnion(entity->GetId());
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
NetworkTransformComponent* networkTransform = entity->template FindComponent<NetworkTransformComponent>();
if (networkTransform != nullptr)
{
const AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); // Get the rewound position
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume
{
// Due to component constraints, netBindComponent must exist if networkTransform exists
NetBindComponent* netBindComponent = entity->template FindComponent<NetBindComponent>();
gatheredEntities.push_back(netBindComponent);
}
}
}
}
});
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
for (NetBindComponent* netBindComponent : gatheredEntities)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
[[maybe_unused]] NetBindComponent* entryNetBindComponent = entity->template FindComponent<NetBindComponent>();
if (entryNetBindComponent != nullptr)
{
// TODO: invoke the sync to rewind event on the netBindComponent and add the entity to the rewound entity set
}
netBindComponent->NotifySyncRewindState();
m_rewoundEntities.push_back(NetworkEntityHandle(netBindComponent, networkEntityTracker));
}
}
void NetworkTime::ClearRewoundEntities()
{
AZ_Assert(!IsTimeRewound(), "Cannot clear rewound entity state while still within scoped rewind");
// TODO: iterate all rewound entities, signal them to sync rewind state, and clear the rewound entity set
for (NetworkEntityHandle entityHandle : m_rewoundEntities)
{
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
netBindComponent->NotifySyncRewindState();
}
m_rewoundEntities.clear();
}
}

@ -13,6 +13,7 @@
#pragma once
#include <Multiplayer/NetworkTime/INetworkTime.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Console/IConsole.h>
@ -42,6 +43,8 @@ namespace Multiplayer
private:
AZStd::vector<NetworkEntityHandle> m_rewoundEntities;
HostFrameId m_hostFrameId = HostFrameId{ 0 };
HostFrameId m_unalteredFrameId = HostFrameId{ 0 };
AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 };

Loading…
Cancel
Save