Changes to make client and entity migration functional, needed in the event of a host quitting necessitating a host migration

Signed-off-by: kberg-amzn <karlberg@amazon.com>
monroegm-disable-blank-issue-2
kberg-amzn 4 years ago
parent 7dc930b444
commit 6e84495975

@ -1,3 +1,4 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.

@ -24,7 +24,7 @@ namespace AZ
{
//! Protects from allocating too much memory. The choice of a 1MB threshold is arbitrary.
//! If you need to work with larger files, please use AZ::IO directly instead of these utility functions.
inline constexpr size_t DefaultMaxFileSize = 1024 * 1024;
inline constexpr size_t DefaultMaxFileSize = 5 * 1024 * 1024;
//! Terminates the application without going through the shutdown procedure.
//! This is used when due to abnormal circumstances the application can no

@ -103,13 +103,13 @@ namespace AzNetworking
//! @return boolean true on success
virtual bool Disconnect(ConnectionId connectionId, DisconnectReason reason) = 0;
//! Sets whether this connection interface can disconnect by virtue of a timeout
//! @param timeoutEnabled If this connection interface will automatically disconnect due to a timeout
virtual void SetTimeoutEnabled(bool timeoutEnabled) = 0;
//! Sets the timeout time in milliseconds, 0 ms means timeouts are disabled.
//! @param timeoutMs the number of milliseconds with no traffic before we timeout and close a connection
virtual void SetTimeoutMs(AZ::TimeMs timeoutMs) = 0;
//! Whether this connection interface will disconnect by virtue of a time out (does not account for cvars affecting all connections)
//! @return boolean true if this connection will not disconnect on timeout (does not account for cvars affecting all connections)
virtual bool IsTimeoutEnabled() const = 0;
//! Retrieves the timeout time in milliseconds for this network interface, 0 ms means timeouts are disabled.
//! @return the timeout time in milliseconds for this network interface, 0 ms means timeouts are disabled
virtual AZ::TimeMs GetTimeoutMs() const = 0;
//! Const access to the metrics tracked by this network interface.
//! @return const reference to the metrics tracked by this network interface

@ -321,4 +321,19 @@ namespace AzNetworking
return serializer.IsValid();
}
};
template <>
struct SerializeObjectHelper<AZ::Aabb>
{
static bool SerializeObject(ISerializer& serializer, AZ::Aabb& value)
{
AZ::Vector3 minValue = value.GetMin();
AZ::Vector3 maxValue = value.GetMax();
serializer.Serialize(minValue, "minValue");
serializer.Serialize(maxValue, "maxValue");
value.SetMin(minValue);
value.SetMax(maxValue);
return serializer.IsValid();
}
};
}

@ -22,14 +22,15 @@ namespace AzNetworking
#endif
AZ_CVAR(bool, net_TcpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Tcp connections");
AZ_CVAR(AZ::TimeMs, net_TcpHearthbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency");
AZ_CVAR(AZ::TimeMs, net_TcpTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection");
AZ_CVAR(AZ::TimeMs, net_TcpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Tcp connection heartbeat frequency");
AZ_CVAR(AZ::TimeMs, net_TcpDefaultTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Tcp connection");
TcpNetworkInterface::TcpNetworkInterface(AZ::Name name, IConnectionListener& connectionListener, TrustZone trustZone, TcpListenThread& listenThread)
: m_name(name)
, m_trustZone(trustZone)
, m_connectionListener(connectionListener)
, m_listenThread(listenThread)
, m_timeoutMs(net_TcpDefaultTimeoutTimeMs)
{
;
}
@ -97,7 +98,7 @@ namespace AzNetworking
}
AZLOG_INFO("Adding new socket %d", static_cast<int32_t>(tcpSocket->GetSocketFd()));
const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(tcpSocket->GetSocketFd()), net_TcpHearthbeatTimeMs);
const TimeoutId newTimeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(tcpSocket->GetSocketFd()), net_TcpHeartbeatTimeMs);
connection->SetTimeoutId(newTimeoutId);
connection->SendReliablePacket(CorePackets::InitiateConnectionPacket());
m_connectionListener.OnConnect(connection.get());
@ -174,14 +175,14 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
void TcpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
void TcpNetworkInterface::SetTimeoutMs(AZ::TimeMs timeoutMs)
{
m_timeoutEnabled = timeoutEnabled;
m_timeoutMs = timeoutMs;
}
bool TcpNetworkInterface::IsTimeoutEnabled() const
AZ::TimeMs TcpNetworkInterface::GetTimeoutMs() const
{
return m_timeoutEnabled;
return m_timeoutMs;
}
void TcpNetworkInterface::QueueNewConnection(const PendingConnection& pendingConnection)
@ -257,7 +258,7 @@ namespace AzNetworking
return;
}
AZLOG(NET_TcpTraffic, "Adding new socket %d", static_cast<int32_t>(tcpSocket.GetSocketFd()));
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(tcpSocket.GetSocketFd()), net_TcpTimeoutTimeMs);
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(static_cast<uint64_t>(tcpSocket.GetSocketFd()), m_timeoutMs);
AZStd::unique_ptr<TcpConnection> connection = AZStd::make_unique<TcpConnection>(connectionId, remoteAddress, *this, tcpSocket, timeoutId);
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Acceptor, "Invalid role for connection");
GetConnectionListener().OnConnect(connection.get());
@ -316,7 +317,7 @@ namespace AzNetworking
{
tcpConnection->SendReliablePacket(CorePackets::HeartbeatPacket());
}
else if (net_TcpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
else if (net_TcpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
{
tcpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;

@ -99,8 +99,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
void SetTimeoutEnabled(bool timeoutEnabled) override;
bool IsTimeoutEnabled() const override;
void SetTimeoutMs(AZ::TimeMs timeoutMs) override;
AZ::TimeMs GetTimeoutMs() const override;
//! @}
//! Queues a new incoming connection for this network interface.
@ -156,7 +156,7 @@ namespace AzNetworking
AZ::Name m_name;
TrustZone m_trustZone;
uint16_t m_port = 0;
bool m_timeoutEnabled = true;
AZ::TimeMs m_timeoutMs = AZ::TimeMs{ 0 };
IConnectionListener& m_connectionListener;
TcpConnectionSet m_connectionSet;
TcpSocketManager m_tcpSocketManager;

@ -31,8 +31,8 @@ namespace AzNetworking
AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections");
AZ_CVAR(AZ::TimeMs, net_UdpPacketTimeSliceMs, AZ::TimeMs{ 8 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The number of milliseconds to allow for packet processing");
AZ_CVAR(AZ::TimeMs, net_UdpHearthbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
AZ_CVAR(AZ::TimeMs, net_UdpTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection");
AZ_CVAR(AZ::TimeMs, net_UdpHeartbeatTimeMs, AZ::TimeMs{ 2 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Udp connection heartbeat frequency");
AZ_CVAR(AZ::TimeMs, net_UdpDefaultTimeoutTimeMs, AZ::TimeMs{ 10 * 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time in milliseconds before we timeout an idle Udp connection");
AZ_CVAR(AZ::TimeMs, net_MinPacketTimeoutMs, AZ::TimeMs{ 200 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Minimum time to wait before timing out an unacked packet");
AZ_CVAR(int32_t, net_MaxTimeoutsPerFrame, 1000, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of packet timeouts to allow to process in a single frame");
AZ_CVAR(float, net_RttFudgeScalar, 2.0f, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Scalar value to multiply computed Rtt by to determine an optimal packet timeout threshold");
@ -61,6 +61,7 @@ namespace AzNetworking
, m_connectionListener(connectionListener)
, m_socket(net_UdpUseEncryption ? new DtlsSocket() : new UdpSocket())
, m_readerThread(readerThread)
, m_timeoutMs(net_UdpDefaultTimeoutTimeMs)
{
const AZ::CVarFixedString compressor = static_cast<AZ::CVarFixedString>(net_UdpCompressor);
const AZ::Name compressorName = AZ::Name(compressor);
@ -138,7 +139,7 @@ namespace AzNetworking
}
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), net_UdpHearthbeatTimeMs);
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), m_timeoutMs);
AZStd::unique_ptr<UdpConnection> connection = AZStd::make_unique<UdpConnection>(connectionId, remoteAddress, *this, ConnectionRole::Connector);
UdpPacketEncodingBuffer dtlsData;
@ -403,14 +404,14 @@ namespace AzNetworking
return connection->Disconnect(reason, TerminationEndpoint::Local);
}
void UdpNetworkInterface::SetTimeoutEnabled(bool timeoutEnabled)
void UdpNetworkInterface::SetTimeoutMs(AZ::TimeMs timeoutMs)
{
m_timeoutEnabled = timeoutEnabled;
m_timeoutMs = timeoutMs;
}
bool UdpNetworkInterface::IsTimeoutEnabled() const
AZ::TimeMs UdpNetworkInterface::GetTimeoutMs() const
{
return m_timeoutEnabled;
return m_timeoutMs;
}
bool UdpNetworkInterface::IsEncrypted() const
@ -681,7 +682,7 @@ namespace AzNetworking
// How long should we sit in the timeout queue before heartbeating or disconnecting
const ConnectionId connectionId = m_connectionSet.GetNextConnectionId();
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), net_UdpTimeoutTimeMs);
const TimeoutId timeoutId = m_connectionTimeoutQueue.RegisterItem(aznumeric_cast<uint64_t>(connectionId), m_timeoutMs);
AZLOG(Debug_UdpConnect, "Accepted new Udp Connection");
AZStd::unique_ptr<UdpConnection> connection = AZStd::make_unique<UdpConnection>(connectionId, connectPacket.m_address, *this, ConnectionRole::Acceptor);
@ -745,7 +746,7 @@ namespace AzNetworking
{
udpConnection->SendUnreliablePacket(CorePackets::HeartbeatPacket());
}
else if (net_UdpTimeoutConnections && m_networkInterface.IsTimeoutEnabled())
else if (net_UdpTimeoutConnections && (m_networkInterface.GetTimeoutMs() > AZ::TimeMs{ 0 }))
{
udpConnection->Disconnect(DisconnectReason::Timeout, TerminationEndpoint::Local);
return TimeoutResult::Delete;

@ -104,8 +104,8 @@ namespace AzNetworking
bool WasPacketAcked(ConnectionId connectionId, PacketId packetId) override;
bool StopListening() override;
bool Disconnect(ConnectionId connectionId, DisconnectReason reason) override;
void SetTimeoutEnabled(bool timeoutEnabled) override;
bool IsTimeoutEnabled() const override;
void SetTimeoutMs(AZ::TimeMs timeoutMs) override;
AZ::TimeMs GetTimeoutMs() const override;
//! @}
//! Returns true if this is an encrypted socket, false if not.
@ -181,7 +181,7 @@ namespace AzNetworking
TrustZone m_trustZone;
uint16_t m_port = 0;
bool m_allowIncomingConnections = false;
bool m_timeoutEnabled = true;
AZ::TimeMs m_timeoutMs = AZ::TimeMs{ 0 };
IConnectionListener& m_connectionListener;
UdpConnectionSet m_connectionSet;
TimeoutQueue m_connectionTimeoutQueue;

@ -83,8 +83,8 @@ namespace Multiplayer
AZ::ScheduledEvent m_autonomousUpdateEvent; // Drives autonomous input collection
AZ::ScheduledEvent m_updateBankedTimeEvent; // Drives authority bank time updates
EntityMigrationStartEvent::Handler m_migrateStartHandler;
EntityMigrationEndEvent::Handler m_migrateEndHandler;
ClientMigrationStartEvent::Handler m_migrateStartHandler;
ClientMigrationEndEvent::Handler m_migrateEndHandler;
double m_moveAccumulator = 0.0;
double m_clientBankedTime = 0.0;

@ -32,8 +32,6 @@ namespace Multiplayer
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using EntityDirtiedEvent = AZ::Event<>;
using EntitySyncRewindEvent = AZ::Event<>;
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
using EntityMigrationEndEvent = AZ::Event<>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
using EntityPreRenderEvent = AZ::Event<float, float>;
using EntityCorrectionEvent = AZ::Event<>;
@ -115,8 +113,6 @@ namespace Multiplayer
void MarkDirty();
void NotifyLocalChanges();
void NotifySyncRewindState();
void NotifyMigrationStart(ClientInputId migratedInputId);
void NotifyMigrationEnd();
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
void NotifyPreRender(float deltaTime, float blendFactor);
void NotifyCorrection();
@ -124,8 +120,6 @@ namespace Multiplayer
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);
void AddEntityPreRenderEventHandler(EntityPreRenderEvent::Handler& eventHandler);
void AddEntityCorrectionEventHandler(EntityCorrectionEvent::Handler& handler);
@ -174,8 +168,6 @@ namespace Multiplayer
EntityStopEvent m_entityStopEvent;
EntityDirtiedEvent m_dirtiedEvent;
EntitySyncRewindEvent m_syncRewindEvent;
EntityMigrationStartEvent m_entityMigrationStartEvent;
EntityMigrationEndEvent m_entityMigrationEndEvent;
EntityServerMigrationEvent m_entityServerMigrationEvent;
EntityPreRenderEvent m_entityPreRenderEvent;
EntityCorrectionEvent m_entityCorrectionEvent;

@ -21,6 +21,14 @@ namespace Multiplayer
virtual ~IEntityDomain() = default;
//! For domains that operate on a region of space, this sets the area the domain is responsible for.
//! @param aabb the aabb associated with this entity domain
virtual void SetAabb(const AZ::Aabb& aabb) = 0;
//! Retrieves the aabb representing the domain area, an invalid aabb will be returned for non-spatial domains.
//! @return the aabb associated with this entity domain
virtual const AZ::Aabb& GetAabb() const = 0;
//! Returns whether or not an entity should be owned by an entity manager.
//! @param entityHandle the handle of the netbound entity to check
//! @return false if this entity should not belong to the entity manger, true if it could be owned by the entity manager

@ -42,6 +42,8 @@ namespace Multiplayer
AzNetworking::ByteBuffer<2048> m_userData;
};
using ClientMigrationStartEvent = AZ::Event<ClientInputId>;
using ClientMigrationEndEvent = AZ::Event<>;
using ClientDisconnectedEvent = AZ::Event<>;
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
@ -94,6 +96,14 @@ namespace Multiplayer
//! @param reason The reason for terminating connections
virtual void Terminate(AzNetworking::DisconnectReason reason) = 0;
//! Adds a ClientMigrationStartEvent Handler which is invoked at the start of a client migration
//! @param handler The ClientMigrationStartEvent Handler to add
virtual void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) = 0;
//! Adds a ClientMigrationEndEvent Handler which is invoked when a client completes migration
//! @param handler The ClientMigrationEndEvent Handler to add
virtual void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) = 0;
//! Adds a ClientDisconnectedEvent Handler which is invoked on the client when a disconnection occurs
//! @param handler The ClientDisconnectedEvent Handler to add
virtual void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) = 0;

@ -105,9 +105,11 @@ namespace Multiplayer
struct EntityMigrationMessage
{
NetEntityId m_entityId;
NetEntityId m_netEntityId;
PrefabEntityId m_prefabEntityId;
AzNetworking::PacketEncodingBuffer m_propertyUpdateData;
bool operator!=(const EntityMigrationMessage& rhs) const;
bool Serialize(AzNetworking::ISerializer& serializer);
};
inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset)
@ -133,6 +135,21 @@ namespace Multiplayer
serializer.Serialize(m_entityOffset, "entityOffset");
return serializer.IsValid();
}
inline bool EntityMigrationMessage::operator!=(const EntityMigrationMessage& rhs) const
{
return m_netEntityId != rhs.m_netEntityId
|| m_prefabEntityId != rhs.m_prefabEntityId
|| m_propertyUpdateData != rhs.m_propertyUpdateData;
}
inline bool EntityMigrationMessage::Serialize(AzNetworking::ISerializer& serializer)
{
serializer.Serialize(m_netEntityId, "netEntityId");
serializer.Serialize(m_prefabEntityId, "prefabEntityId");
serializer.Serialize(m_propertyUpdateData, "propertyUpdateData");
return serializer.IsValid();
}
}
AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostId);

@ -0,0 +1,218 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/ReplicationWindows/IReplicationWindow.h>
#include <AzNetworking/DataStructures/TimeoutQueue.h>
#include <AzNetworking/PacketLayer/IPacketHeader.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/deque.h>
#include <AzCore/std/limits.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/EBus/ScheduledEvent.h>
namespace AzNetworking
{
class IConnection;
class IConnectionListener;
}
namespace Multiplayer
{
class IEntityDomain;
class EntityReplicator;
using SendMigrateEntityEvent = AZ::Event<AzNetworking::IConnection&, const EntityMigrationMessage&>;
//! @class EntityReplicationManager
//! @brief Handles replication of relevant entities for one connection.
class EntityReplicationManager final
{
public:
using EntityReplicatorMap = AZStd::map<NetEntityId, AZStd::unique_ptr<EntityReplicator>>;
enum class Mode
{
Invalid,
LocalServerToRemoteClient,
LocalServerToRemoteServer,
LocalClientToRemoteServer,
};
EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode);
~EntityReplicationManager() = default;
void SetRemoteHostId(HostId hostId);
HostId GetRemoteHostId() const;
void ActivatePendingEntities();
void SendUpdates(AZ::TimeMs hostTimeMs);
void Clear(bool forMigration);
bool SetEntityRebasing(NetworkEntityHandle& entityHandle);
void MigrateAllEntities();
void MigrateEntity(NetEntityId netEntityId);
bool CanMigrateEntity(const ConstNetworkEntityHandle& entityHandle) const;
bool HasRemoteAuthority(const ConstNetworkEntityHandle& entityHandle) const;
void SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain);
IEntityDomain* GetEntityDomain();
void SetReplicationWindow(AZStd::unique_ptr<IReplicationWindow> replicationWindow);
IReplicationWindow* GetReplicationWindow();
void GetEntityReplicatorIdList(AZStd::list<NetEntityId>& outList);
uint32_t GetEntityReplicatorCount(NetEntityRole localNetworkRole);
void AddDeferredRpcMessage(NetworkEntityRpcMessage& rpcMessage);
void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event<NetEntityId>::Handler& handler);
void AddSendMigrateEntityEventHandler(SendMigrateEntityEvent::Handler& handler);
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);
AZ::TimeMs GetResendTimeoutTimeMs() const;
void SetMaxRemoteEntitiesPendingCreationCount(uint32_t maxPendingEntities);
void SetEntityActivationTimeSliceMs(AZ::TimeMs timeSliceMs);
void SetEntityPendingRemovalMs(AZ::TimeMs entityPendingRemovalMs);
AzNetworking::IConnection& GetConnection();
AZ::TimeMs GetFrameTimeMs();
void AddReplicatorToPendingSend(const EntityReplicator& entityReplicator);
bool IsUpdateModeToServerClient();
private:
AZ_DISABLE_COPY_MOVE(EntityReplicationManager);
enum class UpdateValidationResult
{
HandleMessage, // Handle an entity update message
DropMessage, // Do not handle an entity update message, but don't disconnect (could be out of order/date and isn't relevant)
DropMessageAndDisconnect, // Do not handle the message, it is malformed and we should disconnect the connection
};
UpdateValidationResult ValidateUpdate(const NetworkEntityUpdateMessage& updateMessage, AzNetworking::PacketId packetId, EntityReplicator* entityReplicator);
using RpcMessages = AZStd::list<NetworkEntityRpcMessage>;
bool DispatchOrphanedRpc(NetworkEntityRpcMessage& message, EntityReplicator* entityReplicator);
using EntityReplicatorList = AZStd::deque<EntityReplicator*>;
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 MigrateEntityInternal(NetEntityId entityId);
void OnEntityExitDomain(const ConstNetworkEntityHandle& entityHandle);
void OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, HostId remoteHostId, AzNetworking::ConnectionId connectionId);
EntityReplicator* AddEntityReplicator(const ConstNetworkEntityHandle& entityHandle, NetEntityRole netEntityRole);
const EntityReplicator* GetEntityReplicator(NetEntityId entityId) const;
EntityReplicator* GetEntityReplicator(NetEntityId entityId);
EntityReplicator* GetEntityReplicator(const ConstNetworkEntityHandle& entityHandle);
void UpdateWindow();
bool HandlePropertyChangeMessage
(
AzNetworking::IConnection* invokingConnection,
EntityReplicator* entityReplicator,
AzNetworking::PacketId packetId,
NetEntityId netEntityId,
NetEntityRole netEntityRole,
AzNetworking::ISerializer& serializer,
const PrefabEntityId& prefabEntityId
);
void AddReplicatorToPendingRemoval(const EntityReplicator& replicator);
void ClearRemovedReplicators();
class OrphanedEntityRpcs
: public AzNetworking::ITimeoutHandler
{
public:
OrphanedEntityRpcs(EntityReplicationManager& replicationManager);
void Update();
bool DispatchOrphanedRpcs(EntityReplicator& entityReplicator);
void AddOrphanedRpc(NetEntityId entityId, NetworkEntityRpcMessage& entityRpcMessage);
AZStd::size_t Size() const { return m_entityRpcMap.size(); }
private:
AzNetworking::TimeoutResult HandleTimeout(AzNetworking::TimeoutQueue::TimeoutItem& item) override;
struct OrphanedRpcs
{
OrphanedRpcs() = default;
OrphanedRpcs(OrphanedRpcs&& rhs)
{
m_rpcMessages.swap(rhs.m_rpcMessages);
m_timeoutId = rhs.m_timeoutId;
rhs.m_timeoutId = AzNetworking::TimeoutId{ 0 };
}
RpcMessages m_rpcMessages;
AzNetworking::TimeoutId m_timeoutId = AzNetworking::TimeoutId{ 0 };
};
typedef AZStd::unordered_map<NetEntityId, OrphanedRpcs> EntityRpcMap;
EntityRpcMap m_entityRpcMap;
AzNetworking::TimeoutQueue m_timeoutQueue;
EntityReplicationManager& m_replicationManager;
};
OrphanedEntityRpcs m_orphanedEntityRpcs;
EntityReplicatorMap m_entityReplicatorMap;
//! The set of entities that we have sent creation messages for, but have not received confirmation back that the create has occurred
AZStd::unordered_set<NetEntityId> m_remoteEntitiesPendingCreation;
AZStd::deque<NetEntityId> m_entitiesPendingActivation;
AZStd::set<NetEntityId> m_replicatorsPendingRemoval;
AZStd::unordered_set<NetEntityId> m_replicatorsPendingSend;
// Deferred RPC Sends
RpcMessages m_deferredRpcMessagesReliable;
RpcMessages m_deferredRpcMessagesUnreliable;
AZ::Event<NetEntityId> m_autonomousEntityReplicatorCreated;
EntityExitDomainEvent::Handler m_entityExitDomainEventHandler;
SendMigrateEntityEvent m_sendMigrateEntityEvent;
AZ::ScheduledEvent m_clearRemovedReplicators;
AZ::ScheduledEvent m_updateWindow;
AzNetworking::IConnectionListener& m_connectionListener;
AzNetworking::IConnection& m_connection;
AZStd::unique_ptr<IReplicationWindow> m_replicationWindow;
AZStd::unique_ptr<IEntityDomain> m_remoteEntityDomain;
AZ::TimeMs m_entityActivationTimeSliceMs = AZ::TimeMs{ 0 };
AZ::TimeMs m_entityPendingRemovalMs = AZ::TimeMs{ 0 };
AZ::TimeMs m_frameTimeMs = AZ::TimeMs{ 0 };
HostId m_remoteHostId = InvalidHostId;
uint32_t m_maxRemoteEntitiesPendingCreationCount = AZStd::numeric_limits<uint32_t>::max();
uint32_t m_maxPayloadSize = 0;
Mode m_updateMode = Mode::Invalid;
friend class EntityReplicator;
};
}

@ -139,4 +139,4 @@ namespace Multiplayer
};
}
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.inl>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.inl>

@ -20,6 +20,7 @@ namespace Multiplayer
class NetworkEntityAuthorityTracker;
class NetworkEntityRpcMessage;
class MultiplayerComponentRegistry;
class IEntityDomain;
using EntityExitDomainEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using ControllersActivatedEvent = AZ::Event<const ConstNetworkEntityHandle&, EntityIsMigrating>;
@ -37,6 +38,19 @@ namespace Multiplayer
virtual ~INetworkEntityManager() = default;
//! Configures the NetworkEntityManager to operate as an authoritative host.
//! @param hostId the hostId of this NetworkEntityManager
//! @param entityDomain the entity domain used to determine which entities this manager has authority over
virtual void Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain) = 0;
//! Returns whether or not the network entity manager has been initialized to host.
//! @return boolean true if this network entity manager has been intialized to host
virtual bool IsInitialized() const = 0;
//! Returns the entity domain associated with this network entity manager, this will be nullptr on clients.
//! @return boolean the entity domain for this network entity manager
virtual IEntityDomain* GetEntityDomain() const = 0;
//! Returns the NetworkEntityTracker for this INetworkEntityManager instance.
//! @return the NetworkEntityTracker for this INetworkEntityManager instance
virtual NetworkEntityTracker* GetNetworkEntityTracker() = 0;

@ -123,8 +123,8 @@ namespace Multiplayer
if (IsAutonomous())
{
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
GetParent().GetNetBindComponent()->AddEntityMigrationStartEventHandler(m_migrateStartHandler);
GetParent().GetNetBindComponent()->AddEntityMigrationEndEventHandler(m_migrateEndHandler);
GetMultiplayer()->AddClientMigrationStartEventHandler(m_migrateStartHandler);
GetMultiplayer()->AddClientMigrationEndEventHandler(m_migrateEndHandler);
}
}

@ -388,16 +388,6 @@ namespace Multiplayer
m_syncRewindEvent.Signal();
}
void NetBindComponent::NotifyMigrationStart(ClientInputId migratedInputId)
{
m_entityMigrationStartEvent.Signal(migratedInputId);
}
void NetBindComponent::NotifyMigrationEnd()
{
m_entityMigrationEndEvent.Signal();
}
void NetBindComponent::NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId)
{
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
@ -428,16 +418,6 @@ namespace Multiplayer
eventHandler.Connect(m_syncRewindEvent);
}
void NetBindComponent::AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityMigrationStartEvent);
}
void NetBindComponent::AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityMigrationEndEvent);
}
void NetBindComponent::AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler)
{
eventHandler.Connect(m_entityServerMigrationEvent);

@ -13,6 +13,7 @@ namespace Multiplayer
// This can be used to help mitigate client side performance when large numbers of entities are created off the network
AZ_CVAR(uint32_t, cl_ClientMaxRemoteEntitiesPendingCreationCount, AZStd::numeric_limits<uint32_t>::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client");
AZ_CVAR(AZ::TimeMs, cl_ClientEntityReplicatorPendingRemovalTimeMs, AZ::TimeMs{ 10000 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "How long should wait prior to removing an entity for the client through a change in the replication window, entity deletes are still immediate");
AZ_CVAR(AZ::TimeMs, cl_DefaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
ClientToServerConnectionData::ClientToServerConnectionData
(
@ -26,6 +27,7 @@ namespace Multiplayer
{
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(cl_ClientMaxRemoteEntitiesPendingCreationCount);
m_entityReplicationManager.SetEntityPendingRemovalMs(cl_ClientEntityReplicatorPendingRemovalTimeMs);
m_entityReplicationManager.SetEntityActivationTimeSliceMs(cl_DefaultNetworkEntityActivationTimeSliceMs);
}
ClientToServerConnectionData::~ClientToServerConnectionData()

@ -9,7 +9,7 @@
#pragma once
#include <Multiplayer/ConnectionData/IConnectionData.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
namespace Multiplayer
{

@ -9,7 +9,7 @@
#pragma once
#include <Multiplayer/ConnectionData/IConnectionData.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
namespace Multiplayer
{

@ -32,7 +32,7 @@ namespace Multiplayer
{
m_networkEditorInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(
AZ::Name(MpEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this);
m_networkEditorInterface->SetTimeoutEnabled(false);
m_networkEditorInterface->SetTimeoutMs(AZ::TimeMs{ 0 }); // Disable timeouts on this network interface
if (editorsv_isDedicated)
{
uint16_t editorServerPort = DefaultServerEditorPort;

@ -10,6 +10,17 @@
namespace Multiplayer
{
void FullOwnershipEntityDomain::SetAabb([[maybe_unused]] const AZ::Aabb& aabb)
{
; // Do nothing, by definition we own everything
}
const AZ::Aabb& FullOwnershipEntityDomain::GetAabb() const
{
static AZ::Aabb nullAabb = AZ::Aabb::CreateNull();
return nullAabb;
}
bool FullOwnershipEntityDomain::IsInDomain([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const
{
return true;

@ -21,6 +21,8 @@ namespace Multiplayer
//! IEntityDomain overrides.
//! @{
void SetAabb(const AZ::Aabb& aabb) override;
const AZ::Aabb& GetAabb() const override;
bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override;
void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const override;

@ -79,8 +79,6 @@ namespace Multiplayer
AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking");
AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
AZ_CVAR(bool, sv_isTransient, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether a dedicated server shuts down if all existing connections disconnect.");
AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
"Max Ms to use to activate entities coming from the network, 0 means instantiate everything");
AZ_CVAR(AZ::TimeMs, sv_serverSendRateMs, AZ::TimeMs{ 50 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of milliseconds between each network update");
AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
"The default spawnable to use when a new player connects");
@ -94,7 +92,6 @@ namespace Multiplayer
{
serializeContext->Class<MultiplayerSystemComponent, AZ::Component>()
->Version(1);
serializeContext->Class<HostId>()
->Version(1);
serializeContext->Class<NetEntityId>()
@ -173,7 +170,12 @@ namespace Multiplayer
AZ::ConsoleInvokedFrom invokedFrom
) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); })
{
;
AZ::Interface<IMultiplayer>::Register(this);
}
MultiplayerSystemComponent::~MultiplayerSystemComponent()
{
AZ::Interface<IMultiplayer>::Unregister(this);
}
void MultiplayerSystemComponent::Activate()
@ -185,7 +187,6 @@ namespace Multiplayer
{
m_consoleCommandHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
}
AZ::Interface<IMultiplayer>::Register(this);
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Register(this);
//! Register our gems multiplayer components to assign NetComponentIds
@ -195,7 +196,6 @@ namespace Multiplayer
void MultiplayerSystemComponent::Deactivate()
{
AZ::Interface<AzFramework::ISessionHandlingClientRequests>::Unregister(this);
AZ::Interface<IMultiplayer>::Unregister(this);
m_consoleCommandHandler.Disconnect();
AZ::Interface<INetworking>::Get()->DestroyNetworkInterface(AZ::Name(MpNetworkInterfaceName));
AzFramework::SessionNotificationBus::Handler::BusDisconnect();
@ -459,7 +459,7 @@ namespace Multiplayer
AzFramework::PlayerConnectionConfig config;
config.m_playerConnectionId = aznumeric_cast<uint32_t>(connection->GetConnectionId());
config.m_playerSessionId = packet.GetTicket();
if(!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->ValidatePlayerJoinSession(config))
if (!AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get()->ValidatePlayerJoinSession(config))
{
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); };
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
@ -488,10 +488,8 @@ namespace Multiplayer
)
{
m_didHandshake = true;
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str());
AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap();
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str());
return true;
@ -606,7 +604,22 @@ namespace Multiplayer
[[maybe_unused]] MultiplayerPackets::ClientMigration& packet
)
{
return false;
if (GetAgentType() != MultiplayerAgentType::Client)
{
// Only clients are allowed to migrate from one server to another
return false;
}
// Store the temporary user identifier so we can transmit it with our next Connect packet
// The new server will use this to reattach our set of autonomous entities
// Disconnect our existing server connection
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); };
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
AZLOG_INFO("Migrating to new server shard");
//m_clientMigrateStartEvent(packet.GetLastInputGameTimeMs());
m_networkInterface->Connect(packet.GetRemoteServerAddress());
return true;
}
ConnectResult MultiplayerSystemComponent::ValidateConnect
@ -640,7 +653,7 @@ namespace Multiplayer
else
{
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str());
m_connAcquiredEvent.Signal(datum);
m_connectionAcquiredEvent.Signal(datum);
}
// Hosts will spawn a new default player prefab for the user that just connected
@ -654,27 +667,14 @@ namespace Multiplayer
}
controlledEntity.Activate();
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));
}
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
}
else
{
if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so
{
connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket));
}
else
{
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetProviderTicket(providerTicket);
}
connection->SetUserData(new ClientToServerConnectionData(connection, *this, providerTicket));
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<NullReplicationWindow>();
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs);
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
}
}
@ -757,9 +757,11 @@ namespace Multiplayer
{
m_initEvent.Signal(m_networkInterface);
//const AZ::Aabb worldBounds = AZ::Interface<IPhysics>.Get()->GetWorldBounds();
AZStd::unique_ptr<IEntityDomain> newDomain = AZStd::make_unique<FullOwnershipEntityDomain>();
m_networkEntityManager.Initialize(InvalidHostId, AZStd::move(newDomain));
if (!m_networkEntityManager.IsInitialized())
{
// Set up a full ownership domain if we didn't construct a domain during the initialize event
m_networkEntityManager.Initialize(InvalidHostId, AZStd::make_unique<FullOwnershipEntityDomain>());
}
}
}
m_agentType = multiplayerType;
@ -778,6 +780,16 @@ namespace Multiplayer
AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType));
}
void MultiplayerSystemComponent::AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler)
{
handler.Connect(m_clientMigrationStartEvent);
}
void MultiplayerSystemComponent::AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler)
{
handler.Connect(m_clientMigrationEndEvent);
}
void MultiplayerSystemComponent::AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler)
{
handler.Connect(m_clientDisconnectedEvent);
@ -785,7 +797,7 @@ namespace Multiplayer
void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler)
{
handler.Connect(m_connAcquiredEvent);
handler.Connect(m_connectionAcquiredEvent);
}
void MultiplayerSystemComponent::AddSessionInitHandler(SessionInitEvent::Handler& handler)

@ -55,7 +55,7 @@ namespace Multiplayer
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
MultiplayerSystemComponent();
~MultiplayerSystemComponent() override = default;
~MultiplayerSystemComponent() override;
//! AZ::Component overrides.
//! @{
@ -105,13 +105,15 @@ namespace Multiplayer
//! @{
MultiplayerAgentType GetAgentType() const override;
void InitializeMultiplayer(MultiplayerAgentType state) override;
bool StartHosting(uint16_t port, bool isDedicated = true) override;
bool Connect(AZStd::string remoteAddress, uint16_t port) override;
void Terminate(AzNetworking::DisconnectReason reason) override;
void AddClientMigrationStartEventHandler(ClientMigrationStartEvent::Handler& handler) override;
void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) override;
void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) override;
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
bool StartHosting(uint16_t port, bool isDedicated = true) override;
bool Connect(AZStd::string remoteAddress, uint16_t port) override;
void Terminate(AzNetworking::DisconnectReason reason) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
AZ::TimeMs GetCurrentHostTimeMs() const override;
float GetCurrentBlendFactor() const override;
@ -148,8 +150,10 @@ namespace Multiplayer
SessionInitEvent m_initEvent;
SessionShutdownEvent m_shutdownEvent;
ConnectionAcquiredEvent m_connAcquiredEvent;
ConnectionAcquiredEvent m_connectionAcquiredEvent;
ClientDisconnectedEvent m_clientDisconnectedEvent;
ClientMigrationStartEvent m_clientMigrationStartEvent;
ClientMigrationEndEvent m_clientMigrationEndEvent;
AZStd::queue<AZStd::string> m_pendingConnectionTickets;

@ -6,8 +6,8 @@
*
*/
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
@ -444,6 +444,11 @@ namespace Multiplayer
handler.Connect(m_autonomousEntityReplicatorCreated);
}
void EntityReplicationManager::AddSendMigrateEntityEventHandler(SendMigrateEntityEvent::Handler& handler)
{
handler.Connect(m_sendMigrateEntityEvent);
}
const EntityReplicator* EntityReplicationManager::GetEntityReplicator(NetEntityId netEntityId) const
{
auto it = m_entityReplicatorMap.find(netEntityId);
@ -1094,7 +1099,7 @@ namespace Multiplayer
bool didSucceed = true;
EntityMigrationMessage message;
message.m_entityId = replicator->GetEntityHandle().GetNetEntityId();
message.m_netEntityId = replicator->GetEntityHandle().GetNetEntityId();
message.m_prefabEntityId = netBindComponent->GetPrefabEntityId();
if (localEnt->GetState() == AZ::Entity::State::Active)
@ -1119,8 +1124,8 @@ namespace Multiplayer
message.m_propertyUpdateData.Resize(inputSerializer.GetSize());
}
AZ_Assert(didSucceed, "Failed to migrate entity from server");
// TODO: Move this to an event
//m_connection.SendReliablePacket(message);
m_sendMigrateEntityEvent.Signal(m_connection, message);
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote manager id %d", netEntityId, aznumeric_cast<int32_t>(GetRemoteHostId()));
// Immediately add a new replicator so that we catch RPC invocations, the remote side will make us a new one, and then remove us if needs be
@ -1130,7 +1135,7 @@ namespace Multiplayer
bool EntityReplicationManager::HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message)
{
EntityReplicator* replicator = GetEntityReplicator(message.m_entityId);
EntityReplicator* replicator = GetEntityReplicator(message.m_netEntityId);
{
if (message.m_propertyUpdateData.GetSize() > 0)
{
@ -1140,7 +1145,7 @@ namespace Multiplayer
invokingConnection,
replicator,
AzNetworking::InvalidPacketId,
message.m_entityId,
message.m_netEntityId,
NetEntityRole::Server,
outputSerializer,
message.m_prefabEntityId
@ -1154,7 +1159,7 @@ namespace Multiplayer
// The HandlePropertyChangeMessage will have made a replicator if we didn't have one already
if (!replicator)
{
replicator = GetEntityReplicator(message.m_entityId);
replicator = GetEntityReplicator(message.m_netEntityId);
}
AZ_Assert(replicator, "Do not have replicator after handling migration message");

@ -8,7 +8,7 @@
#pragma once
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>

@ -6,8 +6,8 @@
*
*/
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>

@ -7,7 +7,7 @@
*/
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/Components/NetBindComponent.h>
namespace Multiplayer

@ -48,6 +48,16 @@ namespace Multiplayer
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
}
bool NetworkEntityManager::IsInitialized() const
{
return m_entityDomain != nullptr;
}
IEntityDomain* NetworkEntityManager::GetEntityDomain() const
{
return m_entityDomain.get();
}
NetworkEntityTracker* NetworkEntityManager::GetNetworkEntityTracker()
{
return &m_networkEntityTracker;

@ -31,11 +31,11 @@ namespace Multiplayer
NetworkEntityManager();
~NetworkEntityManager();
//! Only invoked for authoritative hosts
void Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain);
//! INetworkEntityManager overrides.
//! @{
void Initialize(HostId hostId, AZStd::unique_ptr<IEntityDomain> entityDomain) override;
bool IsInitialized() const override;
IEntityDomain* GetEntityDomain() const override;
NetworkEntityTracker* GetNetworkEntityTracker() override;
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override;
MultiplayerComponentRegistry* GetMultiplayerComponentRegistry() override;

@ -28,6 +28,9 @@ set(FILES
Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h
Include/Multiplayer/NetworkEntity/NetworkEntityHandle.h
Include/Multiplayer/NetworkEntity/NetworkEntityHandle.inl
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/NetworkInput/IMultiplayerComponentInput.h
Include/Multiplayer/NetworkInput/NetworkInput.h
@ -69,10 +72,7 @@ set(FILES
Source/EntityDomains/FullOwnershipEntityDomain.cpp
Source/EntityDomains/FullOwnershipEntityDomain.h
Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp
Source/NetworkEntity/EntityReplication/EntityReplicationManager.h
Source/NetworkEntity/EntityReplication/EntityReplicator.cpp
Source/NetworkEntity/EntityReplication/EntityReplicator.h
Source/NetworkEntity/EntityReplication/EntityReplicator.inl
Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp
Source/NetworkEntity/EntityReplication/PropertyPublisher.h
Source/NetworkEntity/EntityReplication/PropertySubscriber.cpp

Loading…
Cancel
Save