From 02bc89cd9250626e129bc8cf80dc1601e51a31df Mon Sep 17 00:00:00 2001 From: kberg-amzn Date: Tue, 28 Sep 2021 19:25:04 -0700 Subject: [PATCH] Fixes to sending entity updates and entity rpcs within an environment set up for cross host entity migration Signed-off-by: kberg-amzn --- .../ConnectionData/IConnectionData.h | 3 +- .../EntityReplicationManager.h | 10 +- .../EntityReplication/EntityReplicator.h | 1 + .../NetworkEntity/NetworkEntityRpcMessage.h | 1 + .../NetworkEntityUpdateMessage.h | 1 + .../ReplicationWindows/IReplicationWindow.h | 8 + .../AutoGen/Multiplayer.AutoPackets.xml | 6 +- .../ClientToServerConnectionData.cpp | 4 +- .../ClientToServerConnectionData.h | 2 +- .../ServerToClientConnectionData.cpp | 4 +- .../ServerToClientConnectionData.h | 2 +- .../Source/MultiplayerSystemComponent.cpp | 7 +- .../EntityReplicationManager.cpp | 182 ++++++++---------- .../EntityReplicationManager.h | 8 +- .../EntityReplication/EntityReplicator.cpp | 6 +- .../EntityReplication/PropertyPublisher.cpp | 1 - .../NullReplicationWindow.cpp | 30 +++ .../NullReplicationWindow.h | 6 +- .../ServerToClientReplicationWindow.cpp | 26 ++- .../ServerToClientReplicationWindow.h | 6 +- Gems/Multiplayer/Code/multiplayer_files.cmake | 3 + 21 files changed, 184 insertions(+), 133 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/ConnectionData/IConnectionData.h b/Gems/Multiplayer/Code/Include/Multiplayer/ConnectionData/IConnectionData.h index 66a7a1175a..c3e251ea5e 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/ConnectionData/IConnectionData.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/ConnectionData/IConnectionData.h @@ -40,8 +40,7 @@ namespace Multiplayer virtual EntityReplicationManager& GetReplicationManager() = 0; //! Creates and manages sending updates to the remote endpoint. - //! @param hostTimeMs current server game time in milliseconds - virtual void Update(AZ::TimeMs hostTimeMs) = 0; + virtual void Update() = 0; //! Returns whether update messages can be sent to the connection. //! @return true if update messages can be sent diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h index dd84c9d952..01ae966fb5 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -60,7 +60,7 @@ namespace Multiplayer const HostId& GetRemoteHostId() const; void ActivatePendingEntities(); - void SendUpdates(AZ::TimeMs hostTimeMs); + void SendUpdates(); void Clear(bool forMigration); bool SetEntityRebasing(NetworkEntityHandle& entityHandle); @@ -81,7 +81,7 @@ namespace Multiplayer void AddDeferredRpcMessage(NetworkEntityRpcMessage& rpcMessage); - void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event::Handler& handler); + void AddAutonomousEntityReplicatorCreatedHandler(AZ::Event::Handler& handler); void AddSendMigrateEntityEventHandler(SendMigrateEntityEvent::Handler& handler); bool HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message); @@ -120,10 +120,8 @@ namespace Multiplayer using EntityReplicatorList = AZStd::deque; EntityReplicatorList GenerateEntityUpdateList(); - void SendEntityUpdatesPacketHelper(AZ::TimeMs hostTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection); - - void SendEntityUpdates(AZ::TimeMs hostTimeMs); - void SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable); + void SendEntityUpdateMessages(EntityReplicatorList& replicatorList); + void SendEntityRpcs(RpcMessages& rpcMessages, bool reliable); void MigrateEntityInternal(NetEntityId entityId); void OnEntityExitDomain(const ConstNetworkEntityHandle& entityHandle); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h index 7edefc15e0..d239682c93 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h @@ -66,6 +66,7 @@ namespace Multiplayer bool IsReadyToActivate() const; NetworkEntityUpdateMessage GenerateUpdatePacket(); + void FinalizeSerialization(AzNetworking::PacketId sentId); AZ::TimeMs GetResendTimeoutTimeMs() const; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h index 9720de81eb..3f1d8e346c 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h @@ -103,6 +103,7 @@ namespace Multiplayer // Non-serialized RPC metadata ReliabilityType m_isReliable = ReliabilityType::Reliable; }; + using NetworkEntityRpcVector = AZStd::fixed_vector; struct IRpcParamStruct { diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h index 3c400d5a13..90f622a8ae 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h @@ -118,4 +118,5 @@ namespace Multiplayer // This is to prevent blowing out stack memory if we declare an array of these EntityUpdateMessages AZStd::unique_ptr m_data; }; + using NetworkEntityUpdateVector = AZStd::fixed_vector; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/ReplicationWindows/IReplicationWindow.h b/Gems/Multiplayer/Code/Include/Multiplayer/ReplicationWindows/IReplicationWindow.h index e2e4c5abfe..018d90eef3 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/ReplicationWindows/IReplicationWindow.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/ReplicationWindows/IReplicationWindow.h @@ -10,10 +10,14 @@ #include #include +#include +#include #include namespace Multiplayer { + class EntityReplicator; + struct EntityReplicationData { EntityReplicationData() = default; @@ -21,6 +25,8 @@ namespace Multiplayer float m_priority = 0.0f; }; using ReplicationSet = AZStd::map; + using RpcMessages = AZStd::list; + using EntityReplicatorList = AZStd::deque; class IReplicationWindow { @@ -33,6 +39,8 @@ namespace Multiplayer virtual uint32_t GetMaxProxyEntityReplicatorSendCount() const = 0; virtual bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const = 0; virtual void UpdateWindow() = 0; + virtual AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) = 0; + virtual void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) = 0; virtual void DebugDraw() const = 0; }; } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index a782a7dcf2..091043f034 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -15,7 +15,7 @@ - + @@ -31,11 +31,11 @@ - + - + diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp index a943406df3..392b020748 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.cpp @@ -50,9 +50,9 @@ namespace Multiplayer return m_entityReplicationManager; } - void ClientToServerConnectionData::Update(AZ::TimeMs hostTimeMs) + void ClientToServerConnectionData::Update() { m_entityReplicationManager.ActivatePendingEntities(); - m_entityReplicationManager.SendUpdates(hostTimeMs); + m_entityReplicationManager.SendUpdates(); } } diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h index 9776cbabb9..77df604b49 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ClientToServerConnectionData.h @@ -30,7 +30,7 @@ namespace Multiplayer ConnectionDataType GetConnectionDataType() const override; AzNetworking::IConnection* GetConnection() const override; EntityReplicationManager& GetReplicationManager() override; - void Update(AZ::TimeMs hostTimeMs) override; + void Update() override; bool CanSendUpdates() const override; void SetCanSendUpdates(bool canSendUpdates) override; //! @} diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp index 218f8421d2..a9b8e03126 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp @@ -69,7 +69,7 @@ namespace Multiplayer return m_entityReplicationManager; } - void ServerToClientConnectionData::Update(AZ::TimeMs hostTimeMs) + void ServerToClientConnectionData::Update() { m_entityReplicationManager.ActivatePendingEntities(); @@ -79,7 +79,7 @@ namespace Multiplayer // potentially false if we just migrated the player, if that is the case, don't send any more updates if (netBindComponent != nullptr && (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)) { - m_entityReplicationManager.SendUpdates(hostTimeMs); + m_entityReplicationManager.SendUpdates(); } } } diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h index 764497430f..8dcf08c480 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.h @@ -30,7 +30,7 @@ namespace Multiplayer ConnectionDataType GetConnectionDataType() const override; AzNetworking::IConnection* GetConnection() const override; EntityReplicationManager& GetReplicationManager() override; - void Update(AZ::TimeMs hostTimeMs) override; + void Update() override; bool CanSendUpdates() const override; void SetCanSendUpdates(bool canSendUpdates) override; //! @} diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index c1a29f9539..cb72fe9252 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -307,7 +307,6 @@ namespace Multiplayer void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { const AZ::TimeMs deltaTimeMs = aznumeric_cast(static_cast(deltaTime * 1000.0f)); - const AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs(); const AZ::TimeMs serverRateMs = static_cast(sv_serverSendRateMs); const float serverRateSeconds = static_cast(serverRateMs) / 1000.0f; @@ -344,12 +343,12 @@ namespace Multiplayer // Send out the game state update to all connections { - auto sendNetworkUpdates = [hostTimeMs, &stats](IConnection& connection) + auto sendNetworkUpdates = [&stats](IConnection& connection) { if (connection.GetUserData() != nullptr) { IConnectionData* connectionData = reinterpret_cast(connection.GetUserData()); - connectionData->Update(hostTimeMs); + connectionData->Update(); if (connectionData->GetConnectionDataType() == ConnectionDataType::ServerToClient) { stats.m_clientConnectionCount++; @@ -671,7 +670,7 @@ namespace Multiplayer else { connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket)); - AZStd::unique_ptr window = AZStd::make_unique(); + AZStd::unique_ptr window = AZStd::make_unique(connection); reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index fff9a1cbd8..cdda18e43a 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -107,10 +106,34 @@ namespace Multiplayer } } - void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs) + void EntityReplicationManager::SendUpdates() { m_frameTimeMs = AZ::GetElapsedTimeMs(); - SendEntityUpdates(hostTimeMs); + + { + EntityReplicatorList toSendList = GenerateEntityUpdateList(); + + AZLOG + ( + NET_ReplicationInfo, + "Sending %zd updates from %s to %s", + toSendList.size(), + GetNetworkEntityManager()->GetHostId().GetString().c_str(), + GetRemoteHostId().GetString().c_str() + ); + + // Prep a replication record for send, at this point, everything needs to be sent + for (EntityReplicator* replicator : toSendList) + { + replicator->GetPropertyPublisher()->PrepareSerialization(); + } + + // While our to send list is not empty, build up another packet to send + do + { + SendEntityUpdateMessages(toSendList); + } while (!toSendList.empty()); + } SendEntityRpcs(m_deferredRpcMessagesReliable, true); SendEntityRpcs(m_deferredRpcMessagesUnreliable, false); @@ -130,65 +153,6 @@ namespace Multiplayer ); } - void EntityReplicationManager::SendEntityUpdatesPacketHelper - ( - AZ::TimeMs hostTimeMs, - EntityReplicatorList& toSendList, - uint32_t maxPayloadSize, - AzNetworking::IConnection& connection - ) - { - uint32_t pendingPacketSize = 0; - EntityReplicatorList replicatorUpdatedList; - MultiplayerPackets::EntityUpdates entityUpdatePacket; - entityUpdatePacket.SetHostTimeMs(hostTimeMs); - entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId()); - // Serialize everything - while (!toSendList.empty()) - { - EntityReplicator* replicator = toSendList.front(); - NetworkEntityUpdateMessage updateMessage(replicator->GenerateUpdatePacket()); - - const uint32_t nextMessageSize = updateMessage.GetEstimatedSerializeSize(); - - // Check if we are over our limits - const bool payloadFull = (pendingPacketSize + nextMessageSize > maxPayloadSize); - const bool capacityReached = (entityUpdatePacket.GetEntityMessages().size() >= entityUpdatePacket.GetEntityMessages().capacity()); - const bool largeEntityDetected = (payloadFull && replicatorUpdatedList.empty()); - if (capacityReached || (payloadFull && !largeEntityDetected)) - { - break; - } - - pendingPacketSize += nextMessageSize; - entityUpdatePacket.ModifyEntityMessages().push_back(updateMessage); - replicatorUpdatedList.push_back(replicator); - toSendList.pop_front(); - - if (largeEntityDetected) - { - AZLOG_WARN("\n\n*******************************"); - AZLOG_WARN - ( - "Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d", - aznumeric_cast(replicator->GetEntityHandle().GetNetEntityId()), - maxPayloadSize, - nextMessageSize - ); - AZLOG_WARN("*******************************"); - break; - } - } - - const AzNetworking::PacketId sentId = connection.SendUnreliablePacket(entityUpdatePacket); - - // Update the sent things with the packet id - for (EntityReplicator* replicator : replicatorUpdatedList) - { - replicator->GetPropertyPublisher()->FinalizeSerialization(sentId); - } - } - EntityReplicationManager::EntityReplicatorList EntityReplicationManager::GenerateEntityUpdateList() { if (m_replicationWindow == nullptr) @@ -260,76 +224,92 @@ namespace Multiplayer return toSendList; } - void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs) + void EntityReplicationManager::SendEntityUpdateMessages(EntityReplicatorList& replicatorList) { - EntityReplicatorList toSendList = GenerateEntityUpdateList(); + uint32_t pendingPacketSize = 0; + EntityReplicatorList replicatorUpdatedList; + NetworkEntityUpdateVector entityUpdates; + // Serialize everything + while (!replicatorList.empty()) + { + EntityReplicator* replicator = replicatorList.front(); + NetworkEntityUpdateMessage updateMessage(replicator->GenerateUpdatePacket()); - AZLOG - ( - NET_ReplicationInfo, - "Sending %zd updates from %s to %s", - toSendList.size(), - GetNetworkEntityManager()->GetHostId().GetString().c_str(), - GetRemoteHostId().GetString().c_str() - ); + const uint32_t nextMessageSize = updateMessage.GetEstimatedSerializeSize(); - // prep a replication record for send, at this point, everything needs to be sent - for (EntityReplicator* replicator : toSendList) - { - replicator->GetPropertyPublisher()->PrepareSerialization(); + // Check if we are over our limits + const bool payloadFull = (pendingPacketSize + nextMessageSize > m_maxPayloadSize); + const bool capacityReached = (entityUpdates.size() >= entityUpdates.capacity()); + const bool largeEntityDetected = (payloadFull && replicatorUpdatedList.empty()); + if (capacityReached || (payloadFull && !largeEntityDetected)) + { + break; + } + + pendingPacketSize += nextMessageSize; + entityUpdates.push_back(updateMessage); + replicatorUpdatedList.push_back(replicator); + replicatorList.pop_front(); + + if (largeEntityDetected) + { + AZLOG_WARN + ( + "Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d", + aznumeric_cast(replicator->GetEntityHandle().GetNetEntityId()), + m_maxPayloadSize, + nextMessageSize + ); + break; + } } - // While our to send list is not empty, build up another packet to send - do + const AzNetworking::PacketId sentId = m_replicationWindow->SendEntityUpdateMessages(entityUpdates); + + // Update the sent things with the packet id + for (EntityReplicator* replicator : replicatorUpdatedList) { - SendEntityUpdatesPacketHelper(hostTimeMs, toSendList, m_maxPayloadSize, m_connection); - } while (!toSendList.empty()); + replicator->FinalizeSerialization(sentId); + } } - void EntityReplicationManager::SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable) + void EntityReplicationManager::SendEntityRpcs(RpcMessages& rpcMessages, bool reliable) { - while (!deferredRpcs.empty()) + while (!rpcMessages.empty()) { - MultiplayerPackets::EntityRpcs entityRpcsPacket; + NetworkEntityRpcVector entityRpcs; uint32_t pendingPacketSize = 0; - while (!deferredRpcs.empty()) + while (!rpcMessages.empty()) { - NetworkEntityRpcMessage& message = deferredRpcs.front(); + NetworkEntityRpcMessage& message = rpcMessages.front(); const uint32_t nextRpcSize = message.GetEstimatedSerializeSize(); if ((pendingPacketSize + nextRpcSize) > m_maxPayloadSize) { // We're over our limit, break and send an Rpc packet - if (entityRpcsPacket.GetEntityRpcs().size() == 0) + if (entityRpcs.size() == 0) { AZLOG(NET_Replicator, "Encountered an RPC that is above our MTU, message will be segmented (object size %u, max allowed size %u)", nextRpcSize, m_maxPayloadSize); - entityRpcsPacket.ModifyEntityRpcs().push_back(message); - deferredRpcs.pop_front(); + entityRpcs.push_back(message); + rpcMessages.pop_front(); } break; } pendingPacketSize += nextRpcSize; - if (entityRpcsPacket.GetEntityRpcs().full()) + if (entityRpcs.full()) { // Packet was full, send what we've accumulated so far - AZLOG(NET_Replicator, "We've hit our RPC message limit (RPC count %u, packet size %u)", aznumeric_cast(entityRpcsPacket.GetEntityRpcs().size()), pendingPacketSize); + AZLOG(NET_Replicator, "We've hit our RPC message limit (RPC count %u, packet size %u)", aznumeric_cast(entityRpcs.size()), pendingPacketSize); break; } - entityRpcsPacket.ModifyEntityRpcs().push_back(message); - deferredRpcs.pop_front(); + entityRpcs.push_back(message); + rpcMessages.pop_front(); } - if (reliable) - { - m_connection.SendReliablePacket(entityRpcsPacket); - } - else - { - m_connection.SendUnreliablePacket(entityRpcsPacket); - } + m_replicationWindow->SendEntityRpcs(entityRpcs, reliable); } } @@ -474,7 +454,7 @@ namespace Multiplayer } // @nt: TODO - delete once dropped RPC problem fixed - void EntityReplicationManager::AddAutonomousEntityReplicatorCreatedHandle(AZ::Event::Handler& handler) + void EntityReplicationManager::AddAutonomousEntityReplicatorCreatedHandler(AZ::Event::Handler& handler) { handler.Connect(m_autonomousEntityReplicatorCreated); } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h index 9eec27be27..28a0963a57 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -79,7 +79,7 @@ namespace Multiplayer void AddDeferredRpcMessage(NetworkEntityRpcMessage& rpcMessage); - void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event::Handler& handler); + void AddAutonomousEntityReplicatorCreatedHandler(AZ::Event::Handler& handler); bool HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message); bool HandleEntityDeleteMessage(EntityReplicator* entityReplicator, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage); @@ -117,10 +117,8 @@ namespace Multiplayer using EntityReplicatorList = AZStd::deque; EntityReplicatorList GenerateEntityUpdateList(); - void SendEntityUpdatesPacketHelper(AZ::TimeMs hostTimeMs, EntityReplicatorList& toSendList, uint32_t maxPayloadSize, AzNetworking::IConnection& connection); - - void SendEntityUpdates(AZ::TimeMs hostTimeMs); - void SendEntityRpcs(RpcMessages& deferredRpcs, bool reliable); + void SendEntityUpdateMessages(EntityReplicatorList& replicatorList); + void SendEntityRpcs(RpcMessages& rpcMessages, bool reliable); void MigrateEntityInternal(NetEntityId entityId); void OnEntityExitDomain(const ConstNetworkEntityHandle& entityHandle); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp index 3d6322eed5..e569389659 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicator.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -495,6 +494,11 @@ namespace Multiplayer return updateMessage; } + void EntityReplicator::FinalizeSerialization(AzNetworking::PacketId sentId) + { + m_propertyPublisher->FinalizeSerialization(sentId); + } + void EntityReplicator::DeferRpcMessage(NetworkEntityRpcMessage& entityRpcMessage) { // Received rpc metrics, log rpc sent, number of bytes, and the componentId/rpcId for bandwidth metrics diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp index 43815da6c5..4247377629 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp @@ -336,7 +336,6 @@ namespace Multiplayer case PropertyPublisher::EntityReplicatorState::Deleting: { AZ_Assert(m_serializationPhase == PropertyPublisher::EntityReplicatorSerializationPhase::Prepared, "Unexpected serialization phase"); - FinalizeDeleteEntityRecord(sentId); } break; diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.cpp index 7c9ee2a667..e4e5a7bc53 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.cpp @@ -7,9 +7,16 @@ */ #include +#include namespace Multiplayer { + NullReplicationWindow::NullReplicationWindow(AzNetworking::IConnection* connection) + : m_connection(connection) + { + ; + } + bool NullReplicationWindow::ReplicationSetUpdateReady() { return true; @@ -36,6 +43,29 @@ namespace Multiplayer ; } + AzNetworking::PacketId NullReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) + { + MultiplayerPackets::EntityUpdates entityUpdatePacket; + entityUpdatePacket.SetHostTimeMs(GetNetworkTime()->GetHostTimeMs()); + entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId()); + entityUpdatePacket.SetEntityMessages(entityUpdateVector); + return m_connection->SendUnreliablePacket(entityUpdatePacket); + } + + void NullReplicationWindow::SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) + { + MultiplayerPackets::EntityRpcs entityRpcsPacket; + entityRpcsPacket.SetEntityRpcs(entityRpcVector); + if (reliable) + { + m_connection->SendReliablePacket(entityRpcsPacket); + } + else + { + m_connection->SendUnreliablePacket(entityRpcsPacket); + } + } + void NullReplicationWindow::DebugDraw() const { // Nothing to draw diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.h b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.h index 91788a6b15..39e373f164 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.h +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/NullReplicationWindow.h @@ -9,6 +9,7 @@ #pragma once #include +#include namespace Multiplayer { @@ -16,7 +17,7 @@ namespace Multiplayer : public IReplicationWindow { public: - NullReplicationWindow() = default; + NullReplicationWindow(AzNetworking::IConnection* connection); //! IReplicationWindow interface //! @{ @@ -25,10 +26,13 @@ namespace Multiplayer uint32_t GetMaxProxyEntityReplicatorSendCount() const override; bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override; void UpdateWindow() override; + AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) override; + void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) override; void DebugDraw() const override; //! @} private: ReplicationSet m_emptySet; + AzNetworking::IConnection* m_connection = nullptr; }; } diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp index 3677eb8b5c..740495b606 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -50,7 +51,7 @@ namespace Multiplayer return m_priority < rhs.m_priority; } - ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, const AzNetworking::IConnection* connection) + ServerToClientReplicationWindow::ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, AzNetworking::IConnection* connection) : m_controlledEntity(controlledEntity) , m_entityActivatedEventHandler([this](AZ::Entity* entity) { OnEntityActivated(entity); }) , m_entityDeactivatedEventHandler([this](AZ::Entity* entity) { OnEntityDeactivated(entity); }) @@ -179,6 +180,29 @@ namespace Multiplayer //} } + AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) + { + MultiplayerPackets::EntityUpdates entityUpdatePacket; + entityUpdatePacket.SetHostTimeMs(GetNetworkTime()->GetHostTimeMs()); + entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId()); + entityUpdatePacket.SetEntityMessages(entityUpdateVector); + return m_connection->SendUnreliablePacket(entityUpdatePacket); + } + + void ServerToClientReplicationWindow::SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) + { + MultiplayerPackets::EntityRpcs entityRpcsPacket; + entityRpcsPacket.SetEntityRpcs(entityRpcVector); + if (reliable) + { + m_connection->SendReliablePacket(entityRpcsPacket); + } + else + { + m_connection->SendUnreliablePacket(entityRpcsPacket); + } + } + void ServerToClientReplicationWindow::DebugDraw() const { //static const float BoundaryStripeHeight = 1.0f; diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h index bfaa351095..b034bde90c 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h @@ -38,7 +38,7 @@ namespace Multiplayer // we sort lowest priority first, so that we can easily keep the biggest N priorities using ReplicationCandidateQueue = AZStd::priority_queue; - ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, const AzNetworking::IConnection* connection); + ServerToClientReplicationWindow(NetworkEntityHandle controlledEntity, AzNetworking::IConnection* connection); //! IReplicationWindow interface //! @{ @@ -47,6 +47,8 @@ namespace Multiplayer uint32_t GetMaxProxyEntityReplicatorSendCount() const override; bool IsInWindow(const ConstNetworkEntityHandle& entityPtr, NetEntityRole& outNetworkRole) const override; void UpdateWindow() override; + AzNetworking::PacketId SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) override; + void SendEntityRpcs(NetworkEntityRpcVector& entityRpcVector, bool reliable) override; void DebugDraw() const override; //! @} @@ -75,7 +77,7 @@ namespace Multiplayer //NetBindComponent* m_controlledNetBindComponent = nullptr; - const AzNetworking::IConnection* m_connection = nullptr; + AzNetworking::IConnection* m_connection = nullptr; // Cached values to detect a poor network connection uint32_t m_lastCheckedSentPackets = 0; diff --git a/Gems/Multiplayer/Code/multiplayer_files.cmake b/Gems/Multiplayer/Code/multiplayer_files.cmake index db76c76374..95fce35f8e 100644 --- a/Gems/Multiplayer/Code/multiplayer_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_files.cmake @@ -33,6 +33,9 @@ set(FILES Include/Multiplayer/MultiplayerConstants.h Include/Multiplayer/MultiplayerStats.h Include/Multiplayer/MultiplayerTypes.h + Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h + Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h + Include/Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.inl Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h Include/Multiplayer/NetworkEntity/IFilterEntityManager.h Include/Multiplayer/NetworkEntity/INetworkEntityManager.h