diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h b/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h index 72ffce4202..fb6d217b80 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h +++ b/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h @@ -19,6 +19,8 @@ namespace AzNetworking { + using NetworkInterfaces = AZStd::unordered_map>; + //! @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; }; } diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp index c275ad9057..1a1476dcc8 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp @@ -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(m_listenThread->GetUpdateTimeMs())); - AZLOG_INFO("Total sockets monitored by UdpReaderThread: %u", m_readerThread->GetSocketCount()); - AZLOG_INFO("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(m_readerThread->GetUpdateTimeMs())); + AZLOG_INFO("Total sockets monitored by TcpListenThread: %u", GetTcpListenThreadSocketCount()); + AZLOG_INFO("Total time spent updating TcpListenThread: %lld", aznumeric_cast(GetTcpListenThreadUpdateTime())); + AZLOG_INFO("Total sockets monitored by UdpReaderThread: %u", GetUdpReaderThreadSocketCount()); + AZLOG_INFO("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(GetUdpReaderThreadUpdateTime())); for (auto& networkInterface : m_networkInterfaces) { diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h index b0b4d83d54..2fdc773fb0 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h +++ b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h @@ -63,6 +63,11 @@ namespace AzNetworking void RegisterCompressorFactory(ICompressorFactory* factory) override; AZStd::unique_ptr 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>; NetworkInterfaces m_networkInterfaces; AZStd::unique_ptr m_listenThread; AZStd::unique_ptr m_readerThread; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index 41503396fb..4fe60f14a3 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -35,6 +35,7 @@ namespace Multiplayer using EntityStopEvent = AZ::Event; using EntityDirtiedEvent = AZ::Event<>; + using EntitySyncRewindEvent = AZ::Event<>; using EntityMigrationStartEvent = AZ::Event; using EntityMigrationEndEvent = AZ::Event<>; using EntityServerMigrationEvent = AZ::Event; @@ -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; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 6a615465c2..579ca195e5 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -48,7 +48,6 @@ namespace Multiplayer using ConnectionAcquiredEvent = AZ::Event; using SessionInitEvent = AZ::Event; using SessionShutdownEvent = AZ::Event; - using OnConnectFunctor = AZStd::function; //! 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; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h index e9f3865563..16cc4146dd 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h @@ -18,6 +18,7 @@ #include #include #include +#include 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); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h index f6eb93c4ba..3dfc4b8016 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h @@ -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; - 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; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 8ae8fee618..ea55d43dcb 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -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) -}} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 141c35e1fe..6a3fea82c0 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -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({{ 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({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count) : 0) + {{ comma()|default(" :", true) }} m_authorityToServer(replicationRecord.m_authorityToServer, authorityToServerStartOffset, replicationRecord.ContainsAuthorityToServerBits() ? static_cast({{ 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({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count)); - uint32_t authorityToClientStart = replicationRecord.m_authorityToClient.GetSize(); replicationRecord.m_authorityToClient.Resize(authorityToClientStart + static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count)); @@ -1059,7 +1047,6 @@ namespace {{ Component.attrib['Namespace'] }} replicationRecord.m_autonomousToAuthority.Resize(autonomousToAuthorityStart + static_cast({{ 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({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count)) : true; canAttach &= replicationRecord.ContainsAuthorityToClientBits() ? (replicationRecord.GetRemainingAuthorityToClientBits() >= static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count)) : true; canAttach &= replicationRecord.ContainsAuthorityToServerBits() ? (replicationRecord.GetRemainingAuthorityToServerBits() >= static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count)) : true; canAttach &= replicationRecord.ContainsAuthorityToAutonomousBits() ? (replicationRecord.GetRemainingAuthorityToAutonomousBits() >= static_cast({{ 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({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count)); - uint32_t authorityToClientStart = replicationRecord.m_authorityToClientConsumedBits; replicationRecord.ConsumeAuthorityToClientBits(static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count)); @@ -1095,7 +1078,6 @@ namespace {{ Component.attrib['Namespace'] }} replicationRecord.ConsumeAutonomousToAuthorityBits(static_cast({{ 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) }} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index 94bdac2b5d..57e8a67fb3 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -17,7 +17,7 @@ - + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 2f934979b1..642832805d 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -28,12 +28,6 @@ - - - - - - @@ -49,14 +43,4 @@ - - - - - - - - - - diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index e48d0b0d09..adc369e9ed 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -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); diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp index 4ae7c3fdfe..88029430c3 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include 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::Get(); + + ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount()); + ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast(networking->GetTcpListenThreadUpdateTime())); + ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount()); + ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(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(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(metrics.m_updateTimeMs)); + ImGui::Text(" - Total number of connections: %llu", aznumeric_cast(metrics.m_connectionCount)); + ImGui::Text(" - Total send time in milliseconds: %lld", aznumeric_cast(metrics.m_sendTimeMs)); + ImGui::Text(" - Total sent packets: %llu", aznumeric_cast(metrics.m_sendPackets)); + ImGui::Text(" - Total sent bytes after compression: %llu", aznumeric_cast(metrics.m_sendBytes)); + ImGui::Text(" - Total sent bytes before compression: %llu", aznumeric_cast(metrics.m_sendBytesUncompressed)); + ImGui::Text(" - Total sent compressed packets without benefit: %llu", aznumeric_cast(metrics.m_sendCompressedPacketsNoGain)); + ImGui::Text(" - Total gain from packet compression: %lld", aznumeric_cast(metrics.m_sendBytesCompressedDelta)); + ImGui::Text(" - Total packets resent: %llu", aznumeric_cast(metrics.m_resentPackets)); + ImGui::Text(" - Total receive time in milliseconds: %lld", aznumeric_cast(metrics.m_recvTimeMs)); + ImGui::Text(" - Total received packets: %llu", aznumeric_cast(metrics.m_recvPackets)); + ImGui::Text(" - Total received bytes after compression: %llu", aznumeric_cast(metrics.m_recvBytes)); + ImGui::Text(" - Total received bytes before compression: %llu", aznumeric_cast(metrics.m_recvBytesUncompressed)); + ImGui::Text(" - Total packets discarded due to load: %llu", aznumeric_cast(metrics.m_discardedPackets)); + } + } + } + if (m_displayMultiplayerStats) { if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None)) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 8eb5bf7b0c..ce776904d5 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -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(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::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::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(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(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 window = AZStd::make_unique(controlledEntity, connection); + reinterpret_cast(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(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 window = AZStd::make_unique(controlledEntity, connection); - reinterpret_cast(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 window = AZStd::make_unique(); - reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs); - } + AZStd::unique_ptr window = AZStd::make_unique(); + reinterpret_cast(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(); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 6bedd0599b..da6ff14a2b 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -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; }; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index e47d142be8..13cfd3d6fd 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -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(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 outputSerializer(message.ModifyPropertyUpdateData().GetBuffer(), message.ModifyPropertyUpdateData().GetSize()); + AzNetworking::TrackChangedSerializer 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(GetRemoteHostId())); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h index 083413e19e..6172f30e8a 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -26,7 +28,6 @@ #include #include #include -#include namespace AzNetworking { @@ -82,7 +83,7 @@ namespace Multiplayer void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event::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); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp index 41cc86aaee..7fe0efd323 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp @@ -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, diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp index 98ece0a8cc..f94a8c59d0 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp @@ -13,10 +13,15 @@ #include #include #include +#include +#include #include +#include 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::Register(this); @@ -73,35 +78,59 @@ namespace Multiplayer void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) { - // TODO: extrude rewind volume for initial gather - AZStd::vector gatheredEntries; - AZ::Interface::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::Get(); + AZStd::vector gatheredEntities; + AZ::Interface::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(visEntry->m_userData); + const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityLocalBoundsUnion(entity->GetId()); + const AZ::Vector3 currentCenter = currentBounds.GetCenter(); + + NetworkTransformComponent* networkTransform = entity->template FindComponent(); + + 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(); + gatheredEntities.push_back(netBindComponent); + } + } } } }); - for (AzFramework::VisibilityEntry* visEntry : gatheredEntries) + NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker(); + for (NetBindComponent* netBindComponent : gatheredEntities) { - AZ::Entity* entity = static_cast(visEntry->m_userData); - [[maybe_unused]] NetBindComponent* entryNetBindComponent = entity->template FindComponent(); - 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(); } } diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h index f714e046b3..c36b04be27 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include #include @@ -42,6 +43,8 @@ namespace Multiplayer private: + AZStd::vector m_rewoundEntities; + HostFrameId m_hostFrameId = HostFrameId{ 0 }; HostFrameId m_unalteredFrameId = HostFrameId{ 0 }; AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 };