Merge pull request #4488 from aws-lumberyard-dev/MigrationFixup

Further fixes for client migration and 3-way host entity migration
monroegm-disable-blank-issue-2
SergeyAMZN 4 years ago committed by GitHub
commit 9ce6f43a22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,9 +14,8 @@ namespace AzFramework
{ {
AZ_CVAR(bool, bg_octreeUseQuadtree, false, nullptr, AZ::ConsoleFunctorFlags::ReadOnly, "If set to true, the visibility octrees will degenerate to a quadtree split along the X/Y plane"); AZ_CVAR(bool, bg_octreeUseQuadtree, false, nullptr, AZ::ConsoleFunctorFlags::ReadOnly, "If set to true, the visibility octrees will degenerate to a quadtree split along the X/Y plane");
AZ_CVAR(float, bg_octreeMaxWorldExtents, 16384.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum supported world size by the world octreeSystemComponent"); AZ_CVAR(float, bg_octreeMaxWorldExtents, 16384.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum supported world size by the world octreeSystemComponent");
AZ_CVAR(uint32_t, bg_octreeNodeMaxEntries, 64, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entries to allow in any node before forcing a split"); AZ_CVAR(uint32_t, bg_octreeNodeMaxEntries, 64, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of entries to allow in any node before forcing a split");
AZ_CVAR(uint32_t, bg_octreeNodeMinEntries, 32, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of entries to allow in a node resulting from a merge operation"); AZ_CVAR(uint32_t, bg_octreeNodeMinEntries, 32, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of entries to allow in a node resulting from a merge operation");
static uint32_t GetChildNodeCount() static uint32_t GetChildNodeCount()
{ {
@ -25,14 +24,12 @@ namespace AzFramework
return (bg_octreeUseQuadtree) ? QuadtreeNodeChildCount : OctreeNodeChildCount; return (bg_octreeUseQuadtree) ? QuadtreeNodeChildCount : OctreeNodeChildCount;
} }
OctreeNode::OctreeNode(const AZ::Aabb& bounds) OctreeNode::OctreeNode(const AZ::Aabb& bounds)
: m_bounds(bounds) : m_bounds(bounds)
{ {
; ;
} }
OctreeNode::OctreeNode(OctreeNode&& rhs) OctreeNode::OctreeNode(OctreeNode&& rhs)
: m_bounds(rhs.m_bounds) : m_bounds(rhs.m_bounds)
, m_parent(rhs.m_parent) , m_parent(rhs.m_parent)
@ -46,7 +43,6 @@ namespace AzFramework
} }
} }
OctreeNode& OctreeNode::operator=(OctreeNode&& rhs) OctreeNode& OctreeNode::operator=(OctreeNode&& rhs)
{ {
m_bounds = rhs.m_bounds; m_bounds = rhs.m_bounds;
@ -63,7 +59,6 @@ namespace AzFramework
return *this; return *this;
} }
void OctreeNode::Insert(OctreeScene& octreeScene, VisibilityEntry* entry) void OctreeNode::Insert(OctreeScene& octreeScene, VisibilityEntry* entry)
{ {
AZ_Assert(entry->m_internalNode == nullptr, "Double-insertion: Insert invoked for an entry already bound to the OctreeScene"); AZ_Assert(entry->m_internalNode == nullptr, "Double-insertion: Insert invoked for an entry already bound to the OctreeScene");
@ -98,7 +93,6 @@ namespace AzFramework
} }
} }
void OctreeNode::Update(OctreeScene& octreeScene, VisibilityEntry* entry) void OctreeNode::Update(OctreeScene& octreeScene, VisibilityEntry* entry)
{ {
AZ_Assert(entry->m_internalNode == this, "Update invoked for an entry bound to a different OctreeNode"); AZ_Assert(entry->m_internalNode == this, "Update invoked for an entry bound to a different OctreeNode");
@ -129,7 +123,6 @@ namespace AzFramework
} }
} }
void OctreeNode::Remove(OctreeScene& octreeScene, VisibilityEntry* entry) void OctreeNode::Remove(OctreeScene& octreeScene, VisibilityEntry* entry)
{ {
AZ_Assert(entry->m_internalNode == this, "Remove invoked for an entry bound to a different OctreeNode"); AZ_Assert(entry->m_internalNode == this, "Remove invoked for an entry bound to a different OctreeNode");
@ -152,25 +145,30 @@ namespace AzFramework
} }
} }
void OctreeNode::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const void OctreeNode::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const
{ {
EnumerateHelper(aabb, callback); if (AZ::ShapeIntersection::Overlaps(aabb, m_bounds))
{
EnumerateHelper(aabb, callback);
}
} }
void OctreeNode::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const void OctreeNode::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const
{ {
EnumerateHelper(sphere, callback); if (AZ::ShapeIntersection::Overlaps(sphere, m_bounds))
{
EnumerateHelper(sphere, callback);
}
} }
void OctreeNode::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const void OctreeNode::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const
{ {
EnumerateHelper(frustum, callback); if (AZ::ShapeIntersection::Overlaps(frustum, m_bounds))
{
EnumerateHelper(frustum, callback);
}
} }
void OctreeNode::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const void OctreeNode::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const
{ {
// Invoke the callback for the current node // Invoke the callback for the current node
@ -190,25 +188,21 @@ namespace AzFramework
} }
} }
const AZStd::vector<VisibilityEntry*>& OctreeNode::GetEntries() const const AZStd::vector<VisibilityEntry*>& OctreeNode::GetEntries() const
{ {
return m_entries; return m_entries;
} }
OctreeNode* OctreeNode::GetChildren() const OctreeNode* OctreeNode::GetChildren() const
{ {
return m_children; return m_children;
} }
bool OctreeNode::IsLeaf() const bool OctreeNode::IsLeaf() const
{ {
return m_children == nullptr; return m_children == nullptr;
} }
void OctreeNode::TryMerge(OctreeScene& octreeScene) void OctreeNode::TryMerge(OctreeScene& octreeScene)
{ {
if (IsLeaf()) if (IsLeaf())
@ -236,7 +230,6 @@ namespace AzFramework
} }
} }
template <typename T> template <typename T>
void OctreeNode::EnumerateHelper(const T& boundingVolume, const IVisibilityScene::EnumerateCallback& callback) const void OctreeNode::EnumerateHelper(const T& boundingVolume, const IVisibilityScene::EnumerateCallback& callback) const
{ {
@ -262,7 +255,6 @@ namespace AzFramework
} }
} }
void OctreeNode::Split(OctreeScene& octreeScene) void OctreeNode::Split(OctreeScene& octreeScene)
{ {
AZ_Assert(m_children == nullptr, "Split invoked on an octreeScene node that has already been split"); AZ_Assert(m_children == nullptr, "Split invoked on an octreeScene node that has already been split");
@ -312,7 +304,6 @@ namespace AzFramework
} }
} }
void OctreeNode::Merge(OctreeScene& octreeScene) void OctreeNode::Merge(OctreeScene& octreeScene)
{ {
AZ_Assert(m_children != nullptr, "Merge invoked on an octreeScene node that does not have children"); AZ_Assert(m_children != nullptr, "Merge invoked on an octreeScene node that does not have children");
@ -371,7 +362,6 @@ namespace AzFramework
} }
} }
void OctreeScene::RemoveEntry(VisibilityEntry& entry) void OctreeScene::RemoveEntry(VisibilityEntry& entry)
{ {
AZStd::lock_guard<AZStd::shared_mutex> lock(m_sharedMutex); AZStd::lock_guard<AZStd::shared_mutex> lock(m_sharedMutex);
@ -382,35 +372,30 @@ namespace AzFramework
} }
} }
void OctreeScene::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const void OctreeScene::Enumerate(const AZ::Aabb& aabb, const IVisibilityScene::EnumerateCallback& callback) const
{ {
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex); AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.Enumerate(aabb, callback); m_root.Enumerate(aabb, callback);
} }
void OctreeScene::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const void OctreeScene::Enumerate(const AZ::Sphere& sphere, const IVisibilityScene::EnumerateCallback& callback) const
{ {
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex); AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.Enumerate(sphere, callback); m_root.Enumerate(sphere, callback);
} }
void OctreeScene::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const void OctreeScene::Enumerate(const AZ::Frustum& frustum, const IVisibilityScene::EnumerateCallback& callback) const
{ {
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex); AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.Enumerate(frustum, callback); m_root.Enumerate(frustum, callback);
} }
void OctreeScene::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const void OctreeScene::EnumerateNoCull(const IVisibilityScene::EnumerateCallback& callback) const
{ {
AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex); AZStd::shared_lock<AZStd::shared_mutex> lock(m_sharedMutex);
m_root.EnumerateNoCull(callback); m_root.EnumerateNoCull(callback);
} }
uint32_t OctreeScene::GetEntryCount() const uint32_t OctreeScene::GetEntryCount() const
{ {
return m_entryCount; return m_entryCount;
@ -421,26 +406,22 @@ namespace AzFramework
return m_nodeCount; return m_nodeCount;
} }
uint32_t OctreeScene::GetFreeNodeCount() const uint32_t OctreeScene::GetFreeNodeCount() const
{ {
// Each entry represents GetChildNodeCount() nodes // Each entry represents GetChildNodeCount() nodes
return aznumeric_cast<uint32_t>(m_freeOctreeNodes.size() * GetChildNodeCount()); return aznumeric_cast<uint32_t>(m_freeOctreeNodes.size() * GetChildNodeCount());
} }
uint32_t OctreeScene::GetPageCount() const uint32_t OctreeScene::GetPageCount() const
{ {
return aznumeric_cast<uint32_t>(m_nodeCache.size()); return aznumeric_cast<uint32_t>(m_nodeCache.size());
} }
uint32_t OctreeScene::GetChildNodeCount() const uint32_t OctreeScene::GetChildNodeCount() const
{ {
return AzFramework::GetChildNodeCount(); return AzFramework::GetChildNodeCount();
} }
void OctreeScene::DumpStats() void OctreeScene::DumpStats()
{ {
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::EntryCount = %u", GetName().GetCStr(), GetEntryCount()); AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::EntryCount = %u", GetName().GetCStr(), GetEntryCount());
@ -450,21 +431,18 @@ namespace AzFramework
AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::ChildNodeCount = %u", GetName().GetCStr(), GetChildNodeCount()); AZ_TracePrintf("Console", "OctreeScene[\"%s\"]::ChildNodeCount = %u", GetName().GetCStr(), GetChildNodeCount());
} }
static inline uint32_t CreateNodeIndex(uint32_t page, uint32_t offset) static inline uint32_t CreateNodeIndex(uint32_t page, uint32_t offset)
{ {
AZ_Assert(page <= 0xFFFF && offset <= 0xFFFF, "Out of range values passed to CreateNodeIndex"); AZ_Assert(page <= 0xFFFF && offset <= 0xFFFF, "Out of range values passed to CreateNodeIndex");
return (page << 16) | offset; return (page << 16) | offset;
} }
static inline void ExtractPageAndOffsetFromIndex(uint32_t index, uint32_t& page, uint32_t& offset) static inline void ExtractPageAndOffsetFromIndex(uint32_t index, uint32_t& page, uint32_t& offset)
{ {
offset = index & 0x0000FFFF; offset = index & 0x0000FFFF;
page = index >> 16; page = index >> 16;
} }
uint32_t OctreeScene::AllocateChildNodes() uint32_t OctreeScene::AllocateChildNodes()
{ {
const uint32_t childCount = GetChildNodeCount(); const uint32_t childCount = GetChildNodeCount();
@ -508,14 +486,12 @@ namespace AzFramework
return CreateNodeIndex(nextChildPage, nextChildOffset); return CreateNodeIndex(nextChildPage, nextChildOffset);
} }
void OctreeScene::ReleaseChildNodes(uint32_t nodeIndex) void OctreeScene::ReleaseChildNodes(uint32_t nodeIndex)
{ {
m_nodeCount -= GetChildNodeCount(); m_nodeCount -= GetChildNodeCount();
m_freeOctreeNodes.push(nodeIndex); m_freeOctreeNodes.push(nodeIndex);
} }
OctreeNode* OctreeScene::GetChildNodesAtIndex(uint32_t nodeIndex) const OctreeNode* OctreeScene::GetChildNodesAtIndex(uint32_t nodeIndex) const
{ {
uint32_t childPage; uint32_t childPage;
@ -524,7 +500,6 @@ namespace AzFramework
return &(*m_nodeCache[childPage])[childOffset]; return &(*m_nodeCache[childPage])[childOffset];
} }
void OctreeSystemComponent::Reflect(AZ::ReflectContext* context) void OctreeSystemComponent::Reflect(AZ::ReflectContext* context)
{ {
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context)) if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@ -534,19 +509,16 @@ namespace AzFramework
} }
} }
void OctreeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) void OctreeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{ {
provided.push_back(AZ_CRC("OctreeService")); provided.push_back(AZ_CRC("OctreeService"));
} }
void OctreeSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) void OctreeSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{ {
incompatible.push_back(AZ_CRC("OctreeService")); incompatible.push_back(AZ_CRC("OctreeService"));
} }
OctreeSystemComponent::OctreeSystemComponent() OctreeSystemComponent::OctreeSystemComponent()
{ {
AZ::Interface<IVisibilitySystem>::Register(this); AZ::Interface<IVisibilitySystem>::Register(this);
@ -555,7 +527,6 @@ namespace AzFramework
m_defaultScene = aznew OctreeScene(AZ::Name("DefaultVisibilityScene")); m_defaultScene = aznew OctreeScene(AZ::Name("DefaultVisibilityScene"));
} }
OctreeSystemComponent::~OctreeSystemComponent() OctreeSystemComponent::~OctreeSystemComponent()
{ {
AZ_Assert(m_scenes.empty(), "All IVisibilityScenes must be destroyed before shutdown"); AZ_Assert(m_scenes.empty(), "All IVisibilityScenes must be destroyed before shutdown");
@ -566,13 +537,11 @@ namespace AzFramework
AZ::Interface<IVisibilitySystem>::Unregister(this); AZ::Interface<IVisibilitySystem>::Unregister(this);
} }
void OctreeSystemComponent::Activate() void OctreeSystemComponent::Activate()
{ {
; ;
} }
void OctreeSystemComponent::Deactivate() void OctreeSystemComponent::Deactivate()
{ {
; ;
@ -591,7 +560,6 @@ namespace AzFramework
return newScene; return newScene;
} }
void OctreeSystemComponent::DestroyVisibilityScene(IVisibilityScene* visScene) void OctreeSystemComponent::DestroyVisibilityScene(IVisibilityScene* visScene)
{ {
for (auto iter = m_scenes.begin(); iter != m_scenes.end(); ++iter) for (auto iter = m_scenes.begin(); iter != m_scenes.end(); ++iter)
@ -606,7 +574,6 @@ namespace AzFramework
AZ_Assert(false, "visScene[\"%s\"] not found in the OctreeSystemComponent", visScene->GetName().GetCStr()); AZ_Assert(false, "visScene[\"%s\"] not found in the OctreeSystemComponent", visScene->GetName().GetCStr());
} }
IVisibilityScene* OctreeSystemComponent::FindVisibilityScene(const AZ::Name& sceneName) IVisibilityScene* OctreeSystemComponent::FindVisibilityScene(const AZ::Name& sceneName)
{ {
for (OctreeScene* scene : m_scenes) for (OctreeScene* scene : m_scenes)
@ -619,7 +586,6 @@ namespace AzFramework
return nullptr; return nullptr;
} }
void OctreeSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) void OctreeSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{ {
for (OctreeScene* scene : m_scenes) for (OctreeScene* scene : m_scenes)

@ -53,7 +53,7 @@ namespace AzNetworking
m_timeoutItemMap.erase(timeoutId); m_timeoutItemMap.erase(timeoutId);
} }
void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts) void TimeoutQueue::UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts)
{ {
int32_t numTimeouts = 0; int32_t numTimeouts = 0;
if (maxTimeouts < 0) if (maxTimeouts < 0)
@ -103,7 +103,7 @@ namespace AzNetworking
// By this point, the item is definitely timed out // By this point, the item is definitely timed out
// Invoke the timeout function to see how to proceed // Invoke the timeout function to see how to proceed
const TimeoutResult result = timeoutHandler.HandleTimeout(mapItem); const TimeoutResult result = timeoutHandler(mapItem);
if (result == TimeoutResult::Refresh) if (result == TimeoutResult::Refresh)
{ {
@ -122,4 +122,10 @@ namespace AzNetworking
m_timeoutItemMap.erase(itemTimeoutId); m_timeoutItemMap.erase(itemTimeoutId);
} }
} }
void TimeoutQueue::UpdateTimeouts(ITimeoutHandler& timeoutHandler, int32_t maxTimeouts)
{
TimeoutHandler handler([&timeoutHandler](TimeoutQueue::TimeoutItem& item) { return timeoutHandler.HandleTimeout(item); });
UpdateTimeouts(handler, maxTimeouts);
}
} }

@ -64,6 +64,12 @@ namespace AzNetworking
//! @param timeoutId the identifier of the item to remove //! @param timeoutId the identifier of the item to remove
void RemoveItem(TimeoutId timeoutId); void RemoveItem(TimeoutId timeoutId);
//! Updates timeouts for all items, invokes the provided timeout functor if required.
//! @param timeoutHandler lambda to invoke for all timeouts
//! @param maxTimeouts the maximum number of timeouts to process before breaking iteration
using TimeoutHandler = AZStd::function<TimeoutResult(TimeoutQueue::TimeoutItem&)>;
void UpdateTimeouts(const TimeoutHandler& timeoutHandler, int32_t maxTimeouts = -1);
//! Updates timeouts for all items, invokes timeout handlers if required. //! Updates timeouts for all items, invokes timeout handlers if required.
//! @param timeoutHandler listener instance to call back on for timeouts //! @param timeoutHandler listener instance to call back on for timeouts
//! @param maxTimeouts the maximum number of timeouts to process before breaking iteration //! @param maxTimeouts the maximum number of timeouts to process before breaking iteration

@ -57,6 +57,14 @@ namespace Multiplayer
const AzNetworking::PacketEncodingBuffer& correction const AzNetworking::PacketEncodingBuffer& correction
) override; ) override;
//! Forcibly enables ProcessInput to execute on the entity.
//! Note that this function is quite dangerous and should normally never be used
void ForceEnableAutonomousUpdate();
//! Forcibly disables ProcessInput from executing on the entity.
//! Note that this function is quite dangerous and should normally never be used
void ForceDisableAutonomousUpdate();
//! Return true if we're currently migrating from one host to another. //! Return true if we're currently migrating from one host to another.
//! @return boolean true if we're currently migrating from one host to another //! @return boolean true if we're currently migrating from one host to another
bool IsMigrating() const; bool IsMigrating() const;

@ -32,7 +32,7 @@ namespace Multiplayer
using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>; using EntityStopEvent = AZ::Event<const ConstNetworkEntityHandle&>;
using EntityDirtiedEvent = AZ::Event<>; using EntityDirtiedEvent = AZ::Event<>;
using EntitySyncRewindEvent = AZ::Event<>; using EntitySyncRewindEvent = AZ::Event<>;
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&, AzNetworking::ConnectionId>; using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
using EntityPreRenderEvent = AZ::Event<float>; using EntityPreRenderEvent = AZ::Event<float>;
using EntityCorrectionEvent = AZ::Event<>; using EntityCorrectionEvent = AZ::Event<>;
@ -113,7 +113,7 @@ namespace Multiplayer
void MarkDirty(); void MarkDirty();
void NotifyLocalChanges(); void NotifyLocalChanges();
void NotifySyncRewindState(); void NotifySyncRewindState();
void NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId); void NotifyServerMigration(const HostId& remoteHostId);
void NotifyPreRender(float deltaTime); void NotifyPreRender(float deltaTime);
void NotifyCorrection(); void NotifyCorrection();

@ -30,7 +30,6 @@ namespace Multiplayer
{ {
friend class NetworkHierarchyChildComponent; friend class NetworkHierarchyChildComponent;
friend class NetworkHierarchyRootComponentController; friend class NetworkHierarchyRootComponentController;
friend class ServerToClientReplicationWindow;
public: public:
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase); AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase);

@ -45,7 +45,7 @@ namespace Multiplayer
using ClientMigrationStartEvent = AZ::Event<ClientInputId>; using ClientMigrationStartEvent = AZ::Event<ClientInputId>;
using ClientMigrationEndEvent = AZ::Event<>; using ClientMigrationEndEvent = AZ::Event<>;
using ClientDisconnectedEvent = AZ::Event<>; using ClientDisconnectedEvent = AZ::Event<>;
using NotifyClientMigrationEvent = AZ::Event<const HostId&, uint64_t, ClientInputId>; using NotifyClientMigrationEvent = AZ::Event<AzNetworking::ConnectionId, const HostId&, uint64_t, ClientInputId, NetEntityId>;
using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>; using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>; using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
using ServerAcceptanceReceivedEvent = AZ::Event<>; using ServerAcceptanceReceivedEvent = AZ::Event<>;
@ -136,10 +136,12 @@ namespace Multiplayer
virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0; virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0;
//! Signals a NotifyClientMigrationEvent with the provided parameters. //! Signals a NotifyClientMigrationEvent with the provided parameters.
//! @param hostId the host id of the host the client is migrating to //! @param connectionId the connection id of the client that is migrating
//! @param userIdentifier the user identifier the client will provide the new host to validate identity //! @param hostId the host id of the host the client is migrating to
//! @param lastClientInputId the last processed clientInputId by the current host //! @param userIdentifier the user identifier the client will provide the new host to validate identity
virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) = 0; //! @param lastClientInputId the last processed clientInputId by the current host
//! @param controlledEntityId the entityId of the clients autonomous entity
virtual void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) = 0;
//! Signals a NotifyEntityMigrationEvent with the provided parameters. //! Signals a NotifyEntityMigrationEvent with the provided parameters.
//! @param entityHandle the network entity handle of the entity being migrated //! @param entityHandle the network entity handle of the entity being migrated
@ -181,6 +183,18 @@ namespace Multiplayer
//! @return pointer to the filtered entity manager, or nullptr if not set //! @return pointer to the filtered entity manager, or nullptr if not set
virtual IFilterEntityManager* GetFilterEntityManager() = 0; virtual IFilterEntityManager* GetFilterEntityManager() = 0;
//! Registers a temp userId to allow a host to look up a players controlled entity in the event of a rejoin or migration event.
//! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts
//! @param controlledEntityId the controlled entityId of the players autonomous entity
virtual void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) = 0;
//! Completes a client migration event by informing the appropriate client to migrate between hosts.
//! @param temporaryUserIdentifier the temporary user identifier used to identify a player across hosts
//! @param connectionId the connection id of the player being migrated
//! @param publicHostId the public address of the new host the client should connect to
//! @param migratedClientInputId the last clientInputId processed prior to migration
virtual void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) = 0;
//! Enables or disables automatic instantiation of netbound entities. //! Enables or disables automatic instantiation of netbound entities.
//! This setting is controlled by the networking layer and should not be touched //! This setting is controlled by the networking layer and should not be touched
//! If enabled, netbound entities will instantiate as spawnables are loaded into the game world, generally true for the server //! If enabled, netbound entities will instantiate as spawnables are loaded into the game world, generally true for the server

@ -29,7 +29,7 @@ namespace Multiplayer
using HostId = AzNetworking::IpAddress; using HostId = AzNetworking::IpAddress;
static const HostId InvalidHostId = HostId(); static const HostId InvalidHostId = HostId();
AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint32_t); AZ_TYPE_SAFE_INTEGRAL(NetEntityId, uint64_t);
static constexpr NetEntityId InvalidNetEntityId = static_cast<NetEntityId>(-1); static constexpr NetEntityId InvalidNetEntityId = static_cast<NetEntityId>(-1);
AZ_TYPE_SAFE_INTEGRAL(NetComponentId, uint16_t); AZ_TYPE_SAFE_INTEGRAL(NetComponentId, uint16_t);
@ -68,6 +68,7 @@ namespace Multiplayer
Server, // A simulated proxy on a server Server, // A simulated proxy on a server
Authority // An authoritative proxy on a server (full authority) Authority // An authoritative proxy on a server (full authority)
}; };
const char* GetEnumString(NetEntityRole value);
enum class ComponentSerializationType : uint8_t enum class ComponentSerializationType : uint8_t
{ {
@ -113,6 +114,24 @@ namespace Multiplayer
bool Serialize(AzNetworking::ISerializer& serializer); bool Serialize(AzNetworking::ISerializer& serializer);
}; };
inline const char* GetEnumString(NetEntityRole value)
{
switch (value)
{
case NetEntityRole::InvalidRole:
return "InvalidRole";
case NetEntityRole::Client:
return "Client";
case NetEntityRole::Autonomous:
return "Autonomous";
case NetEntityRole::Server:
return "Server";
case NetEntityRole::Authority:
return "Authority";
}
return "Unknown";
}
inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset) inline PrefabEntityId::PrefabEntityId(AZ::Name name, uint32_t entityOffset)
: m_prefabName(name) : m_prefabName(name)
, m_entityOffset(entityOffset) , m_entityOffset(entityOffset)

@ -57,7 +57,6 @@ namespace Multiplayer
EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode); EntityReplicationManager(AzNetworking::IConnection& connection, AzNetworking::IConnectionListener& connectionListener, Mode mode);
~EntityReplicationManager() = default; ~EntityReplicationManager() = default;
void SetRemoteHostId(const HostId& hostId);
const HostId& GetRemoteHostId() const; const HostId& GetRemoteHostId() const;
void ActivatePendingEntities(); void ActivatePendingEntities();

@ -43,8 +43,7 @@ namespace Multiplayer
//! Constructor for an entity delete message. //! Constructor for an entity delete message.
//! @param entityId the networkId of the entity being deleted //! @param entityId the networkId of the entity being deleted
//! @param isMigrated whether or not the entity is being migrated or deleted //! @param isMigrated whether or not the entity is being migrated or deleted
//! @param takeOwnership true if the remote replicator should take ownership of the entity explicit NetworkEntityUpdateMessage(NetEntityId entityId, bool isMigrated);
explicit NetworkEntityUpdateMessage(NetEntityId entityId, bool isMigrated, bool takeOwnership);
NetworkEntityUpdateMessage& operator =(NetworkEntityUpdateMessage&& rhs); NetworkEntityUpdateMessage& operator =(NetworkEntityUpdateMessage&& rhs);
NetworkEntityUpdateMessage& operator =(const NetworkEntityUpdateMessage& rhs); NetworkEntityUpdateMessage& operator =(const NetworkEntityUpdateMessage& rhs);
@ -71,10 +70,6 @@ namespace Multiplayer
//! @return whether or not the entity was migrated //! @return whether or not the entity was migrated
bool GetWasMigrated() const; bool GetWasMigrated() const;
//! Gets the current value of TakeOwnership.
//! @return the current value of TakeOwnership
bool GetTakeOwnership() const;
//! Gets the current value of HasValidPrefabId. //! Gets the current value of HasValidPrefabId.
//! @return the current value of HasValidPrefabId //! @return the current value of HasValidPrefabId
bool GetHasValidPrefabId() const; bool GetHasValidPrefabId() const;
@ -110,7 +105,6 @@ namespace Multiplayer
NetEntityId m_entityId = InvalidNetEntityId; NetEntityId m_entityId = InvalidNetEntityId;
bool m_isDelete = false; bool m_isDelete = false;
bool m_wasMigrated = false; bool m_wasMigrated = false;
bool m_takeOwnership = false;
bool m_hasValidPrefabId = false; bool m_hasValidPrefabId = false;
PrefabEntityId m_prefabEntityId; PrefabEntityId m_prefabEntityId;

@ -13,9 +13,9 @@
<Include File="Multiplayer/MultiplayerTypes.h"/> <Include File="Multiplayer/MultiplayerTypes.h"/>
<Include File="Multiplayer/NetworkInput/NetworkInput.h"/> <Include File="Multiplayer/NetworkInput/NetworkInput.h"/>
<Include File="Source/NetworkInput/NetworkInputArray.h"/> <Include File="Multiplayer/NetworkInput/NetworkInputArray.h"/>
<Include File="Source/NetworkInput/NetworkInputHistory.h"/> <Include File="Multiplayer/NetworkInput/NetworkInputHistory.h"/>
<Include File="Source/NetworkInput/NetworkInputMigrationVector.h"/> <Include File="Multiplayer/NetworkInput/NetworkInputMigrationVector.h"/>
<Include File="AzNetworking/DataStructures/ByteBuffer.h"/> <Include File="AzNetworking/DataStructures/ByteBuffer.h"/>
<NetworkProperty Type="Multiplayer::ClientInputId" Name="LastInputId" Init="Multiplayer::ClientInputId{ 0 }" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" /> <NetworkProperty Type="Multiplayer::ClientInputId" Name="LastInputId" Init="Multiplayer::ClientInputId{ 0 }" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />

@ -9,6 +9,7 @@
<Packet Name="Connect" HandshakePacket="true" Desc="Client connection packet, on success the server will reply with an Accept"> <Packet Name="Connect" HandshakePacket="true" Desc="Client connection packet, on success the server will reply with an Accept">
<Member Type="uint16_t" Name="networkProtocolVersion" Init="0" /> <Member Type="uint16_t" Name="networkProtocolVersion" Init="0" />
<Member Type="uint64_t" Name="temporaryUserId" Init="0" />
<Member Type="Multiplayer::LongNetworkString" Name="ticket" /> <Member Type="Multiplayer::LongNetworkString" Name="ticket" />
</Packet> </Packet>

@ -8,7 +8,7 @@
OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h" OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Include File="Source/NetworkInput/NetworkInputChild.h"/> <Include File="Multiplayer/NetworkInput/NetworkInputChild.h"/>
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" /> <ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />

@ -354,6 +354,16 @@ namespace Multiplayer
} }
} }
void LocalPredictionPlayerInputComponentController::ForceEnableAutonomousUpdate()
{
m_autonomousUpdateEvent.Enqueue(AZ::TimeMs{ 1 }, true);
}
void LocalPredictionPlayerInputComponentController::ForceDisableAutonomousUpdate()
{
m_autonomousUpdateEvent.RemoveFromQueue();
}
bool LocalPredictionPlayerInputComponentController::IsMigrating() const bool LocalPredictionPlayerInputComponentController::IsMigrating() const
{ {
return m_lastMigratedInputId != ClientInputId{ 0 }; return m_lastMigratedInputId != ClientInputId{ 0 };

@ -394,9 +394,9 @@ namespace Multiplayer
m_syncRewindEvent.Signal(); m_syncRewindEvent.Signal();
} }
void NetBindComponent::NotifyServerMigration(const HostId& hostId, AzNetworking::ConnectionId connectionId) void NetBindComponent::NotifyServerMigration(const HostId& remoteHostId)
{ {
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId); m_entityServerMigrationEvent.Signal(m_netEntityHandle, remoteHostId);
} }
void NetBindComponent::NotifyPreRender(float deltaTime) void NetBindComponent::NotifyPreRender(float deltaTime)

@ -23,22 +23,16 @@ namespace Multiplayer
ServerToClientConnectionData::ServerToClientConnectionData ServerToClientConnectionData::ServerToClientConnectionData
( (
AzNetworking::IConnection* connection, AzNetworking::IConnection* connection,
AzNetworking::IConnectionListener& connectionListener, AzNetworking::IConnectionListener& connectionListener
NetworkEntityHandle controlledEntity
) )
: m_connection(connection) : m_connection(connection)
, m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); }) , m_controlledEntityRemovedHandler([this](const ConstNetworkEntityHandle&) { OnControlledEntityRemove(); })
, m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId) { OnControlledEntityMigration(entityHandle, remoteHostId, connectionId); }) , m_controlledEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
, m_controlledEntity(controlledEntity) {
OnControlledEntityMigration(entityHandle, remoteHostId);
})
, m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient) , m_entityReplicationManager(*connection, connectionListener, EntityReplicationManager::Mode::LocalServerToRemoteClient)
{ {
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
if (netBindComponent != nullptr)
{
netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler);
netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler);
}
m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(sv_ClientMaxRemoteEntitiesPendingCreationCount); m_entityReplicationManager.SetMaxRemoteEntitiesPendingCreationCount(sv_ClientMaxRemoteEntitiesPendingCreationCount);
m_entityReplicationManager.SetEntityPendingRemovalMs(sv_ClientEntityReplicatorPendingRemovalTimeMs); m_entityReplicationManager.SetEntityPendingRemovalMs(sv_ClientEntityReplicatorPendingRemovalTimeMs);
} }
@ -54,6 +48,20 @@ namespace Multiplayer
m_controlledEntityRemovedHandler.Disconnect(); m_controlledEntityRemovedHandler.Disconnect();
} }
void ServerToClientConnectionData::SetControlledEntity(NetworkEntityHandle primaryPlayerEntity)
{
m_controlledEntityRemovedHandler.Disconnect();
m_controlledEntityMigrationHandler.Disconnect();
m_controlledEntity = primaryPlayerEntity;
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
if (netBindComponent != nullptr)
{
netBindComponent->AddEntityStopEventHandler(m_controlledEntityRemovedHandler);
netBindComponent->AddEntityServerMigrationEventHandler(m_controlledEntityMigrationHandler);
}
}
ConnectionDataType ServerToClientConnectionData::GetConnectionDataType() const ConnectionDataType ServerToClientConnectionData::GetConnectionDataType() const
{ {
return ConnectionDataType::ServerToClient; return ConnectionDataType::ServerToClient;
@ -94,8 +102,7 @@ namespace Multiplayer
void ServerToClientConnectionData::OnControlledEntityMigration void ServerToClientConnectionData::OnControlledEntityMigration
( (
[[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] const ConstNetworkEntityHandle& entityHandle,
[[maybe_unused]] const HostId& remoteHostId, const HostId& remoteHostId
[[maybe_unused]] AzNetworking::ConnectionId connectionId
) )
{ {
ClientInputId migratedClientInputId = ClientInputId{ 0 }; ClientInputId migratedClientInputId = ClientInputId{ 0 };
@ -109,14 +116,12 @@ namespace Multiplayer
} }
// Generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration // Generate crypto-rand user identifier, send to both server and client so they can negotiate the autonomous entity to assume predictive control over after migration
const uint64_t randomUserIdentifier = AzNetworking::CryptoRand64(); const uint64_t temporaryUserIdentifier = AzNetworking::CryptoRand64();
// Tell the new host that a client is about to (re)join // Tell the new host that a client is about to (re)join
GetMultiplayer()->SendNotifyClientMigrationEvent(remoteHostId, randomUserIdentifier, migratedClientInputId); GetMultiplayer()->SendNotifyClientMigrationEvent(GetConnection()->GetConnectionId(), remoteHostId, temporaryUserIdentifier, migratedClientInputId, m_controlledEntity.GetNetEntityId());
// We need to send a MultiplayerPackets::ClientMigration packet to complete this process
// Tell the client who to join // This happens inside MultiplayerSystemComponent, once we're certain the remote host has appropriately prepared
MultiplayerPackets::ClientMigration clientMigration(remoteHostId, randomUserIdentifier, migratedClientInputId);
GetConnection()->SendReliablePacket(clientMigration);
m_controlledEntity = NetworkEntityHandle(); m_controlledEntity = NetworkEntityHandle();
m_canSendUpdates = false; m_canSendUpdates = false;

@ -20,11 +20,12 @@ namespace Multiplayer
ServerToClientConnectionData ServerToClientConnectionData
( (
AzNetworking::IConnection* connection, AzNetworking::IConnection* connection,
AzNetworking::IConnectionListener& connectionListener, AzNetworking::IConnectionListener& connectionListener
NetworkEntityHandle controlledEntity
); );
~ServerToClientConnectionData() override; ~ServerToClientConnectionData() override;
void SetControlledEntity(NetworkEntityHandle primaryPlayerEntity);
//! IConnectionData interface //! IConnectionData interface
//! @{ //! @{
ConnectionDataType GetConnectionDataType() const override; ConnectionDataType GetConnectionDataType() const override;
@ -44,7 +45,7 @@ namespace Multiplayer
private: private:
void OnControlledEntityRemove(); void OnControlledEntityRemove();
void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId); void OnControlledEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId);
void OnGameplayStarted(); void OnGameplayStarted();
EntityReplicationManager m_entityReplicationManager; EntityReplicationManager m_entityReplicationManager;

@ -18,7 +18,6 @@ namespace Multiplayer
m_canSendUpdates = canSendUpdates; m_canSendUpdates = canSendUpdates;
} }
inline NetworkEntityHandle ServerToClientConnectionData::GetPrimaryPlayerEntity() inline NetworkEntityHandle ServerToClientConnectionData::GetPrimaryPlayerEntity()
{ {
return m_controlledEntity; return m_controlledEntity;

@ -74,7 +74,7 @@ namespace Multiplayer
{ {
ImGui::Text("%s", entity->GetId().ToString().c_str()); ImGui::Text("%s", entity->GetId().ToString().c_str());
ImGui::NextColumn(); ImGui::NextColumn();
ImGui::Text("%u", GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId())); ImGui::Text("%llu", static_cast<AZ::u64>(GetMultiplayer()->GetNetworkEntityManager()->GetNetEntityIdById(entity->GetId())));
ImGui::NextColumn(); ImGui::NextColumn();
ImGui::Text("%s", entity->GetName().c_str()); ImGui::Text("%s", entity->GetName().c_str());
ImGui::NextColumn(); ImGui::NextColumn();

@ -28,24 +28,29 @@ namespace Multiplayer
->Version(1); ->Version(1);
} }
} }
void MultiplayerDebugSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) void MultiplayerDebugSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{ {
provided.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent")); provided.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
} }
void MultiplayerDebugSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) void MultiplayerDebugSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
{ {
; ;
} }
void MultiplayerDebugSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatbile) void MultiplayerDebugSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatbile)
{ {
incompatbile.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent")); incompatbile.push_back(AZ_CRC_CE("MultiplayerDebugSystemComponent"));
} }
void MultiplayerDebugSystemComponent::Activate() void MultiplayerDebugSystemComponent::Activate()
{ {
#ifdef IMGUI_ENABLED #ifdef IMGUI_ENABLED
ImGui::ImGuiUpdateListenerBus::Handler::BusConnect(); ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
#endif #endif
} }
void MultiplayerDebugSystemComponent::Deactivate() void MultiplayerDebugSystemComponent::Deactivate()
{ {
#ifdef IMGUI_ENABLED #ifdef IMGUI_ENABLED
@ -75,6 +80,7 @@ namespace Multiplayer
ImGui::EndMenu(); ImGui::EndMenu();
} }
} }
void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond) void AccumulatePerSecondValues(const MultiplayerStats& stats, const MultiplayerStats::Metric& metric, float& outCallsPerSecond, float& outBytesPerSecond)
{ {
uint64_t summedCalls = 0; uint64_t summedCalls = 0;
@ -107,6 +113,7 @@ namespace Multiplayer
ImGui::Text("%11.2f", bytesPerSecond); ImGui::Text("%11.2f", bytesPerSecond);
return open; return open;
} }
bool DrawSummaryRow(const char* name, const MultiplayerStats& stats) bool DrawSummaryRow(const char* name, const MultiplayerStats& stats)
{ {
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics(); const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateTotalPropertyUpdateSentMetrics();
@ -123,6 +130,7 @@ namespace Multiplayer
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond); AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond); return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
} }
bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId) bool DrawComponentRow(const char* name, const MultiplayerStats& stats, NetComponentId netComponentId)
{ {
const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId); const MultiplayerStats::Metric propertyUpdatesSent = stats.CalculateComponentPropertyUpdateSentMetrics(netComponentId);
@ -139,6 +147,7 @@ namespace Multiplayer
AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond); AccumulatePerSecondValues(stats, rpcsRecv, callsPerSecond, bytesPerSecond);
return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond); return DrawMetricsRow(name, true, totalCalls, totalBytes, callsPerSecond, bytesPerSecond);
} }
void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId) void DrawComponentDetails(const MultiplayerStats& stats, NetComponentId netComponentId)
{ {
MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry(); MultiplayerComponentRegistry* componentRegistry = GetMultiplayerComponentRegistry();
@ -503,4 +512,3 @@ void OnDebugEntities_ShowBandwidth_Changed(const bool& showBandwidth)
AZ::Interface<Multiplayer::IMultiplayerDebug>::Get()->HideEntityBandwidthDebugOverlay(); AZ::Interface<Multiplayer::IMultiplayerDebug>::Get()->HideEntityBandwidthDebugOverlay();
} }
} }

@ -8,6 +8,7 @@
#include <Multiplayer/MultiplayerConstants.h> #include <Multiplayer/MultiplayerConstants.h>
#include <Multiplayer/Components/MultiplayerComponent.h> #include <Multiplayer/Components/MultiplayerComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <MultiplayerSystemComponent.h> #include <MultiplayerSystemComponent.h>
#include <ConnectionData/ClientToServerConnectionData.h> #include <ConnectionData/ClientToServerConnectionData.h>
#include <ConnectionData/ServerToClientConnectionData.h> #include <ConnectionData/ServerToClientConnectionData.h>
@ -76,6 +77,7 @@ namespace Multiplayer
"The address of the remote server or host to connect to"); "The address of the remote server or host to connect to");
AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic"); AZ_CVAR(uint16_t, cl_serverport, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port of the remote host to connect to for game traffic");
AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic"); AZ_CVAR(uint16_t, sv_port, DefaultServerPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that this multiplayer gem will bind to for game traffic");
AZ_CVAR(uint16_t, sv_portRange, 999, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The range of ports the host will incrementally attempt to bind to when initializing");
AZ_CVAR(AZ::CVarFixedString, sv_map, "nolevel", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The map the server should load"); AZ_CVAR(AZ::CVarFixedString, sv_map, "nolevel", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The map the server should load");
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(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_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server");
@ -168,6 +170,7 @@ namespace Multiplayer
AZ::ConsoleFunctorFlags flags, AZ::ConsoleFunctorFlags flags,
AZ::ConsoleInvokedFrom invokedFrom AZ::ConsoleInvokedFrom invokedFrom
) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); }) ) { OnConsoleCommandInvoked(command, args, flags, invokedFrom); })
, m_autonomousEntityReplicatorCreatedHandler([this]([[maybe_unused]] NetEntityId netEntityId) { OnAutonomousEntityReplicatorCreated(); })
{ {
AZ::Interface<IMultiplayer>::Register(this); AZ::Interface<IMultiplayer>::Register(this);
} }
@ -205,8 +208,23 @@ namespace Multiplayer
bool MultiplayerSystemComponent::StartHosting(uint16_t port, bool isDedicated) bool MultiplayerSystemComponent::StartHosting(uint16_t port, bool isDedicated)
{ {
InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer); if (port != sv_port)
return m_networkInterface->Listen(port); {
sv_port = port;
}
const uint16_t maxPort = sv_port + sv_portRange;
while (sv_port <= maxPort)
{
if (m_networkInterface->Listen(sv_port))
{
InitializeMultiplayer(isDedicated ? MultiplayerAgentType::DedicatedServer : MultiplayerAgentType::ClientServer);
return true;
}
AZLOG_WARN("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port));
sv_port = sv_port + 1;
}
return false;
} }
bool MultiplayerSystemComponent::Connect(const AZStd::string& remoteAddress, uint16_t port) bool MultiplayerSystemComponent::Connect(const AZStd::string& remoteAddress, uint16_t port)
@ -328,6 +346,11 @@ namespace Multiplayer
void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{ {
if (bg_multiplayerDebugDraw)
{
m_networkEntityManager.DebugDraw();
}
const AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f)); const AZ::TimeMs deltaTimeMs = aznumeric_cast<AZ::TimeMs>(static_cast<int32_t>(deltaTime * 1000.0f));
const AZ::TimeMs serverRateMs = static_cast<AZ::TimeMs>(sv_serverSendRateMs); const AZ::TimeMs serverRateMs = static_cast<AZ::TimeMs>(sv_serverSendRateMs);
const float serverRateSeconds = static_cast<float>(serverRateMs) / 1000.0f; const float serverRateSeconds = static_cast<float>(serverRateMs) / 1000.0f;
@ -412,11 +435,6 @@ namespace Multiplayer
{ {
m_networkInterface->GetConnectionSet().VisitConnections(visitor); m_networkInterface->GetConnectionSet().VisitConnections(visitor);
} }
if (bg_multiplayerDebugDraw)
{
m_networkEntityManager.DebugDraw();
}
} }
int MultiplayerSystemComponent::GetTickOrder() int MultiplayerSystemComponent::GetTickOrder()
@ -487,17 +505,39 @@ namespace Multiplayer
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); }; auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::TerminatedByUser, TerminationEndpoint::Local); };
m_networkInterface->GetConnectionSet().VisitConnections(visitor); m_networkInterface->GetConnectionSet().VisitConnections(visitor);
return true; return true;
} }
} }
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str()); reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetProviderTicket(packet.GetTicket().c_str());
// Hosts will spawn a new default player prefab for the user that just connected
if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
{
// We use a temporary userId over the clients address so we can maintain client lookups even in the event of wifi handoff
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(packet.GetTemporaryUserId());
EnableAutonomousControl(controlledEntity, connection->GetConnectionId());
ServerToClientConnectionData* connectionData = reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData());
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
connectionData->GetReplicationManager().SetReplicationWindow(AZStd::move(window));
connectionData->SetControlledEntity(controlledEntity);
// If this is a migrate or rejoin, immediately ready the connection for updates
if (packet.GetTemporaryUserId() != 0)
{
connectionData->SetCanSendUpdates(true);
}
}
if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map))) if (connection->SendReliablePacket(MultiplayerPackets::Accept(sv_map)))
{ {
reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetDidHandshake(true); reinterpret_cast<ServerToClientConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
if (packet.GetTemporaryUserId() == 0)
// Sync our console {
ConsoleReplicator consoleReplicator(connection); // Sync our console
AZ::Interface<AZ::IConsole>::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); }); ConsoleReplicator consoleReplicator(connection);
AZ::Interface<AZ::IConsole>::Get()->VisitRegisteredFunctors([&consoleReplicator](AZ::ConsoleFunctorBase* functor) { consoleReplicator.Visit(functor); });
}
return true; return true;
} }
return false; return false;
@ -511,10 +551,26 @@ namespace Multiplayer
) )
{ {
reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetDidHandshake(true); reinterpret_cast<ClientToServerConnectionData*>(connection->GetUserData())->SetDidHandshake(true);
AZ::CVarFixedString commandString = "sv_map " + packet.GetMap(); if (m_temporaryUserIdentifier == 0)
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str()); {
AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap(); AZ::CVarFixedString commandString = "sv_map " + packet.GetMap();
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str()); AZ::Interface<AZ::IConsole>::Get()->PerformCommand(commandString.c_str());
AZ::CVarFixedString loadLevelString = "LoadLevel " + packet.GetMap();
AZ::Interface<AZ::IConsole>::Get()->PerformCommand(loadLevelString.c_str());
}
else
{
// Bypass map loading and immediately ready the connection for updates
IConnectionData* connectionData = reinterpret_cast<IConnectionData*>(connection->GetUserData());
if (connectionData)
{
connectionData->SetCanSendUpdates(true);
// @nt: TODO - delete once dropped RPC problem fixed
// Connection has migrated, we are now waiting for the autonomous entity replicator to be created
connectionData->GetReplicationManager().AddAutonomousEntityReplicatorCreatedHandler(m_autonomousEntityReplicatorCreatedHandler);
}
}
m_serverAcceptanceReceivedEvent.Signal(); m_serverAcceptanceReceivedEvent.Signal();
return true; return true;
@ -637,13 +693,17 @@ namespace Multiplayer
// Store the temporary user identifier so we can transmit it with our next Connect packet // Store the temporary user identifier so we can transmit it with our next Connect packet
// The new server will use this to re-attach our set of autonomous entities // The new server will use this to re-attach our set of autonomous entities
m_temporaryUserIdentifier = packet.GetTemporaryUserIdentifier();
// Disconnect our existing server connection // Disconnect our existing server connection
auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); }; auto visitor = [](IConnection& connection) { connection.Disconnect(DisconnectReason::ClientMigrated, TerminationEndpoint::Local); };
m_networkInterface->GetConnectionSet().VisitConnections(visitor); m_networkInterface->GetConnectionSet().VisitConnections(visitor);
AZLOG_INFO("Migrating to new server shard"); AZLOG_INFO("Migrating to new server shard");
m_clientMigrationStartEvent.Signal(packet.GetLastClientInputId()); m_clientMigrationStartEvent.Signal(packet.GetLastClientInputId());
m_networkInterface->Connect(packet.GetRemoteServerAddress()); if (m_networkInterface->Connect(packet.GetRemoteServerAddress()) == AzNetworking::InvalidConnectionId)
{
AZLOG_ERROR("Failed to connect to new host during client migration event");
}
return true; return true;
} }
@ -673,7 +733,7 @@ namespace Multiplayer
providerTicket = m_pendingConnectionTickets.front(); providerTicket = m_pendingConnectionTickets.front();
m_pendingConnectionTickets.pop(); m_pendingConnectionTickets.pop();
} }
connection->SendReliablePacket(MultiplayerPackets::Connect(0, providerTicket.c_str())); connection->SendReliablePacket(MultiplayerPackets::Connect(0, m_temporaryUserIdentifier, providerTicket.c_str()));
} }
else else
{ {
@ -681,29 +741,10 @@ namespace Multiplayer
m_connectionAcquiredEvent.Signal(datum); m_connectionAcquiredEvent.Signal(datum);
} }
// Hosts will spawn a new default player prefab for the user that just connected
if (GetAgentType() == MultiplayerAgentType::ClientServer if (GetAgentType() == MultiplayerAgentType::ClientServer
|| GetAgentType() == MultiplayerAgentType::DedicatedServer) || GetAgentType() == MultiplayerAgentType::DedicatedServer)
{ {
INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab(); connection->SetUserData(new ServerToClientConnectionData(connection, *this));
for (auto& netEntity : entityList)
{
if (netEntity.Exists())
{
netEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
}
netEntity.Activate();
}
NetworkEntityHandle controlledEntity;
if (entityList.size() > 0)
{
controlledEntity = entityList[0];
}
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 else
{ {
@ -725,9 +766,9 @@ namespace Multiplayer
void MultiplayerSystemComponent::OnDisconnect(AzNetworking::IConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint) void MultiplayerSystemComponent::OnDisconnect(AzNetworking::IConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint)
{ {
const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remote host disconnected"; const char* endpointString = (endpoint == TerminationEndpoint::Local) ? "Disconnecting" : "Remotely disconnected";
AZStd::string reasonString = ToString(reason); AZStd::string reasonString = ToString(reason);
AZLOG_INFO("%s due to %s from remote address: %s", endpointString, reasonString.c_str(), connection->GetRemoteAddress().GetString().c_str()); AZLOG_INFO("%s from remote address %s due to %s", endpointString, connection->GetRemoteAddress().GetString().c_str(), reasonString.c_str());
// The client is disconnecting // The client is disconnecting
if (GetAgentType() == MultiplayerAgentType::Client) if (GetAgentType() == MultiplayerAgentType::Client)
@ -809,16 +850,8 @@ namespace Multiplayer
// Spawn the default player for this host since the host is also a player (not a dedicated server) // Spawn the default player for this host since the host is also a player (not a dedicated server)
if (m_agentType == MultiplayerAgentType::ClientServer) if (m_agentType == MultiplayerAgentType::ClientServer)
{ {
INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab(); NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(0);
EnableAutonomousControl(controlledEntity, AzNetworking::InvalidConnectionId);
for (NetworkEntityHandle controlledEntity : entityList)
{
if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent())
{
controlledEntityNetBindComponent->SetAllowAutonomy(true);
}
controlledEntity.Activate();
}
} }
AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType)); AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType));
@ -869,9 +902,9 @@ namespace Multiplayer
handler.Connect(m_shutdownEvent); handler.Connect(m_shutdownEvent);
} }
void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) void MultiplayerSystemComponent::SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId)
{ {
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId); m_notifyClientMigrationEvent.Signal(connectionId, hostId, userIdentifier, lastClientInputId, controlledEntityId);
} }
void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
@ -925,6 +958,22 @@ namespace Multiplayer
return m_filterEntityManager; return m_filterEntityManager;
} }
void MultiplayerSystemComponent::RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId)
{
m_playerRejoinData[temporaryUserIdentifier] = controlledEntityId;
}
void MultiplayerSystemComponent::CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId)
{
IConnection* connection = m_networkInterface->GetConnectionSet().GetConnection(connectionId);
if (connection != nullptr) // Make sure the player has not disconnected since the start of migration
{
// Tell the client who to join
MultiplayerPackets::ClientMigration clientMigration(publicHostId, temporaryUserIdentifier, migratedClientInputId);
connection->SendReliablePacket(clientMigration);
}
}
void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value) void MultiplayerSystemComponent::SetShouldSpawnNetworkEntities(bool value)
{ {
m_spawnNetboundEntities = value; m_spawnNetboundEntities = value;
@ -1055,6 +1104,13 @@ namespace Multiplayer
m_cvarCommands.PushBackItem(AZStd::move(replicateString)); m_cvarCommands.PushBackItem(AZStd::move(replicateString));
} }
void MultiplayerSystemComponent::OnAutonomousEntityReplicatorCreated()
{
m_autonomousEntityReplicatorCreatedHandler.Disconnect();
//m_networkEntityManager.GetNetworkEntityAuthorityTracker()->ResetTimeoutTime(AZ::TimeMs{ 2000 });
m_clientMigrationEndEvent.Signal();
}
void MultiplayerSystemComponent::ExecuteConsoleCommandList(IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands) void MultiplayerSystemComponent::ExecuteConsoleCommandList(IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands)
{ {
AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get(); AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
@ -1066,19 +1122,69 @@ namespace Multiplayer
} }
} }
INetworkEntityManager::EntityList MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier)
{ {
const auto node = m_playerRejoinData.find(temporaryUserIdentifier);
if (node != m_playerRejoinData.end())
{
return m_networkEntityManager.GetNetworkEntityTracker()->Get(node->second);
}
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str())); PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()));
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate); INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
return entityList; for (NetworkEntityHandle subEntity : entityList)
{
subEntity.Activate();
}
NetworkEntityHandle controlledEntity;
if (!entityList.empty())
{
controlledEntity = entityList[0];
}
return controlledEntity;
}
void MultiplayerSystemComponent::EnableAutonomousControl(NetworkEntityHandle entityHandle, AzNetworking::ConnectionId connectionId)
{
if (!entityHandle.Exists())
{
AZLOG_WARN("Attempting to enable autonomous control for an invalid entity");
return;
}
entityHandle.GetNetBindComponent()->SetOwningConnectionId(connectionId);
if (connectionId == InvalidConnectionId)
{
entityHandle.GetNetBindComponent()->SetAllowAutonomy(true);
}
auto* hierarchyComponent = entityHandle.FindComponent<NetworkHierarchyRootComponent>();
if (hierarchyComponent != nullptr)
{
for (AZ::Entity* subEntity : hierarchyComponent->GetHierarchicalEntities())
{
NetworkEntityHandle subEntityHandle = NetworkEntityHandle(subEntity);
NetBindComponent* subEntityNetBindComponent = subEntityHandle.GetNetBindComponent();
if (subEntityNetBindComponent != nullptr)
{
subEntityNetBindComponent->SetOwningConnectionId(connectionId);
if (connectionId == InvalidConnectionId)
{
subEntityNetBindComponent->SetAllowAutonomy(true);
}
}
}
}
} }
void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
{ {
if (!AZ::Interface<IMultiplayer>::Get()->StartHosting(sv_port, sv_isDedicated)) if (!AZ::Interface<IMultiplayer>::Get()->StartHosting(sv_port, sv_isDedicated))
{ {
AZLOG_ERROR("Failed to start listening on port %u, port is in use?", static_cast<uint32_t>(sv_port)); AZLOG_ERROR("Failed to start listening on any allocated port");
} }
} }
AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to"); AZ_CONSOLEFREEFUNC(host, AZ::ConsoleFunctorFlags::DontReplicate, "Opens a multiplayer connection as a host for other clients to connect to");

@ -123,7 +123,7 @@ namespace Multiplayer
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) override; void AddServerAcceptanceReceivedHandler(ServerAcceptanceReceivedEvent::Handler& handler) override;
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override; void SendNotifyClientMigrationEvent(AzNetworking::ConnectionId connectionId, const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId, NetEntityId controlledEntityId) override;
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override; void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override; void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
AZ::TimeMs GetCurrentHostTimeMs() const override; AZ::TimeMs GetCurrentHostTimeMs() const override;
@ -132,6 +132,8 @@ namespace Multiplayer
INetworkEntityManager* GetNetworkEntityManager() override; INetworkEntityManager* GetNetworkEntityManager() override;
void SetFilterEntityManager(IFilterEntityManager* entityFilter) override; void SetFilterEntityManager(IFilterEntityManager* entityFilter) override;
IFilterEntityManager* GetFilterEntityManager() override; IFilterEntityManager* GetFilterEntityManager() override;
void RegisterPlayerIdentifierForRejoin(uint64_t temporaryUserIdentifier, NetEntityId controlledEntityId) override;
void CompleteClientMigration(uint64_t temporaryUserIdentifier, AzNetworking::ConnectionId connectionId, const HostId& publicHostId, ClientInputId migratedClientInputId) override;
void SetShouldSpawnNetworkEntities(bool value) override; void SetShouldSpawnNetworkEntities(bool value) override;
bool GetShouldSpawnNetworkEntities() const override; bool GetShouldSpawnNetworkEntities() const override;
//! @} //! @}
@ -145,9 +147,11 @@ namespace Multiplayer
void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds); void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds);
void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom); void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom);
void OnAutonomousEntityReplicatorCreated();
void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands); void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands);
INetworkEntityManager::EntityList SpawnDefaultPlayerPrefab(); NetworkEntityHandle SpawnDefaultPlayerPrefab(uint64_t temporaryUserIdentifier);
void EnableAutonomousControl(NetworkEntityHandle entityHandle, AzNetworking::ConnectionId connectionId);
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
AzNetworking::INetworkInterface* m_networkInterface = nullptr; AzNetworking::INetworkInterface* m_networkInterface = nullptr;
@ -170,12 +174,16 @@ namespace Multiplayer
ClientMigrationEndEvent m_clientMigrationEndEvent; ClientMigrationEndEvent m_clientMigrationEndEvent;
NotifyClientMigrationEvent m_notifyClientMigrationEvent; NotifyClientMigrationEvent m_notifyClientMigrationEvent;
NotifyEntityMigrationEvent m_notifyEntityMigrationEvent; NotifyEntityMigrationEvent m_notifyEntityMigrationEvent;
AZ::Event<NetEntityId>::Handler m_autonomousEntityReplicatorCreatedHandler;
AZStd::queue<AZStd::string> m_pendingConnectionTickets; AZStd::queue<AZStd::string> m_pendingConnectionTickets;
AZStd::unordered_map<uint64_t, NetEntityId> m_playerRejoinData;
AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 };
HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0); HostFrameId m_lastReplicatedHostFrameId = HostFrameId(0);
uint64_t m_temporaryUserIdentifier = 0; // Used in the event of a migration or rejoin
double m_serverSendAccumulator = 0.0; double m_serverSendAccumulator = 0.0;
float m_renderBlendFactor = 0.0f; float m_renderBlendFactor = 0.0f;
float m_tickFactor = 0.0f; float m_tickFactor = 0.0f;

@ -47,6 +47,9 @@ namespace Multiplayer
, m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); }) , m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); })
, m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); }) , m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); })
{ {
// Set up our remote host identifier, by default we use the IP address of the remote host
m_remoteHostId = connection.GetRemoteAddress();
// Our max payload size is whatever is passed in, minus room for a udp packetheader // Our max payload size is whatever is passed in, minus room for a udp packetheader
m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead; m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead;
@ -62,12 +65,10 @@ namespace Multiplayer
networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler); networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler);
} }
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler); if (m_updateMode == Mode::LocalServerToRemoteServer)
} {
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
void EntityReplicationManager::SetRemoteHostId(const HostId& hostId) }
{
m_remoteHostId = hostId;
} }
const HostId& EntityReplicationManager::GetRemoteHostId() const const HostId& EntityReplicationManager::GetRemoteHostId() const
@ -258,8 +259,8 @@ namespace Multiplayer
{ {
AZLOG_WARN AZLOG_WARN
( (
"Serializing extremely large entity (%u) - MaxPayload: %d NeededSize %d", "Serializing extremely large entity (%llu) - MaxPayload: %d NeededSize %d",
aznumeric_cast<uint32_t>(replicator->GetEntityHandle().GetNetEntityId()), aznumeric_cast<AZ::u64>(replicator->GetEntityHandle().GetNetEntityId()),
m_maxPayloadSize, m_maxPayloadSize,
nextMessageSize nextMessageSize
); );
@ -364,15 +365,29 @@ namespace Multiplayer
const bool changedRemoteRole = (remoteNetworkRole != entityReplicator->GetRemoteNetworkRole()); const bool changedRemoteRole = (remoteNetworkRole != entityReplicator->GetRemoteNetworkRole());
// Check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client // Check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client
bool changedLocalRole(false); bool changedLocalRole(false);
if (AZ::Entity* localEnt = entityReplicator->GetEntityHandle().GetEntity()) NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent();
if (netBindComponent != nullptr)
{ {
NetBindComponent* netBindComponent = entityReplicator->GetEntityHandle().GetNetBindComponent();
AZ_Assert(netBindComponent != nullptr, "No NetBindComponent");
changedLocalRole = (netBindComponent->GetNetEntityRole() != entityReplicator->GetBoundLocalNetworkRole()); changedLocalRole = (netBindComponent->GetNetEntityRole() != entityReplicator->GetBoundLocalNetworkRole());
} }
if (changedRemoteRole || changedLocalRole) if (changedRemoteRole || changedLocalRole)
{ {
const AZ::u64 intEntityId = static_cast<AZ::u64>(netBindComponent->GetNetEntityId());
const char* entityName = entityReplicator->GetEntityHandle().GetEntity()->GetName().c_str();
if (changedLocalRole)
{
const char* oldRoleString = GetEnumString(entityReplicator->GetRemoteNetworkRole());
const char* newRoleString = GetEnumString(remoteNetworkRole);
AZLOG(NET_ReplicatorRoles, "Replicator %s(%llu) changed local role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString);
}
if (changedRemoteRole)
{
const char* oldRoleString = GetEnumString(entityReplicator->GetBoundLocalNetworkRole());
const char* newRoleString = GetEnumString(netBindComponent->GetNetEntityRole());
AZLOG(NET_ReplicatorRoles, "Replicator %s(%llu) changed remote role, old role = %s, new role = %s", entityName, intEntityId, oldRoleString, newRoleString);
}
// If we changed roles, we need to reset everything // If we changed roles, we need to reset everything
if (!entityReplicator->IsMarkedForRemoval()) if (!entityReplicator->IsMarkedForRemoval())
{ {
@ -387,8 +402,8 @@ namespace Multiplayer
AZLOG AZLOG
( (
NET_RepDeletes, NET_RepDeletes,
"Reinited replicator for %u from remote host %s role %d", "Reinited replicator for netEntityId %llu from remote host %s role %d",
entityHandle.GetNetEntityId(), static_cast<AZ::u64>(entityHandle.GetNetEntityId()),
GetRemoteHostId().GetString().c_str(), GetRemoteHostId().GetString().c_str(),
aznumeric_cast<int32_t>(remoteNetworkRole) aznumeric_cast<int32_t>(remoteNetworkRole)
); );
@ -404,8 +419,8 @@ namespace Multiplayer
AZLOG AZLOG
( (
NET_RepDeletes, NET_RepDeletes,
"Added replicator for %u from remote host %s role %d", "Added replicator for netEntityId %llu from remote host %s role %d",
entityHandle.GetNetEntityId(), static_cast<AZ::u64>(entityHandle.GetNetEntityId()),
GetRemoteHostId().GetString().c_str(), GetRemoteHostId().GetString().c_str(),
aznumeric_cast<int32_t>(remoteNetworkRole) aznumeric_cast<int32_t>(remoteNetworkRole)
); );
@ -413,7 +428,7 @@ namespace Multiplayer
} }
else else
{ {
AZLOG_ERROR("Failed to add entity replicator, entity does not exist, entity id %u", entityHandle.GetNetEntityId()); AZLOG_ERROR("Failed to add entity replicator, entity does not exist, netEntityId %llu", static_cast<AZ::u64>(entityHandle.GetNetEntityId()));
AZ_Assert(false, "Failed to add entity replicator, entity does not exist"); AZ_Assert(false, "Failed to add entity replicator, entity does not exist");
} }
return entityReplicator; return entityReplicator;
@ -502,24 +517,20 @@ namespace Multiplayer
{ {
if (entityReplicator->IsMarkedForRemoval()) if (entityReplicator->IsMarkedForRemoval())
{ {
AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Got a replicator delete message that is a duplicate id %llu remote host %s", static_cast<AZ::u64>(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str());
} }
else if (entityReplicator->OwnsReplicatorLifetime()) else if (entityReplicator->OwnsReplicatorLifetime())
{ {
// This can occur if we migrate entities quickly - if this is a replicator from C to A, A migrates to B, B then migrates to C, and A's delete replicator has not arrived at C // This can occur if we migrate entities quickly - if this is a replicator from C to A, A migrates to B, B then migrates to C, and A's delete replicator has not arrived at C
AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Got a replicator delete message for a replicator we own id %llu remote host %s", static_cast<AZ::u64>(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str());
} }
else else
{ {
shouldDeleteEntity = true; shouldDeleteEntity = true;
entityReplicator->MarkForRemoval(); entityReplicator->MarkForRemoval();
AZLOG(NET_RepDeletes, "Deleting replicater for entity id %u remote host %s", updateMessage.GetEntityId(), GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Deleting replicater for entity id %llu remote host %s", static_cast<AZ::u64>(updateMessage.GetEntityId()), GetRemoteHostId().GetString().c_str());
} }
} }
else
{
shouldDeleteEntity = updateMessage.GetTakeOwnership();
}
// Handle entity cleanup // Handle entity cleanup
if (shouldDeleteEntity) if (shouldDeleteEntity)
@ -529,17 +540,17 @@ namespace Multiplayer
{ {
if (updateMessage.GetWasMigrated()) if (updateMessage.GetWasMigrated())
{ {
AZLOG(NET_RepDeletes, "Leaving id %u using timeout remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Leaving id %llu using timeout remote host %s", static_cast<AZ::u64>(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
} }
else else
{ {
AZLOG(NET_RepDeletes, "Deleting entity id %u remote host %s", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Deleting entity id %llu remote host %s", static_cast<AZ::u64>(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
GetNetworkEntityManager()->MarkForRemoval(entity); GetNetworkEntityManager()->MarkForRemoval(entity);
} }
} }
else else
{ {
AZLOG(NET_RepDeletes, "Trying to delete entity id %u remote host %s, but it has been removed", entity.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Trying to delete entity id %llu remote host %s, but it has been removed", static_cast<AZ::u64>(entity.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
} }
} }
@ -583,9 +594,9 @@ namespace Multiplayer
NetBindComponent* netBindComponent = replicatorEntity.GetNetBindComponent(); NetBindComponent* netBindComponent = replicatorEntity.GetNetBindComponent();
AZ_Assert(netBindComponent != nullptr, "No NetBindComponent"); AZ_Assert(netBindComponent != nullptr, "No NetBindComponent");
if (createEntity) if (netBindComponent->GetOwningConnectionId() != invokingConnection->GetConnectionId())
{ {
// Always set our invoking connectionId for any newly created entities, since this connection now 'owns' them from a rewind perspective // Always ensure our owning connectionId is correct for correct rewind behaviour
netBindComponent->SetOwningConnectionId(invokingConnection->GetConnectionId()); netBindComponent->SetOwningConnectionId(invokingConnection->GetConnectionId());
} }
@ -595,10 +606,11 @@ namespace Multiplayer
AZ_Assert(localNetworkRole != NetEntityRole::Authority, "UpdateMessage trying to set local role to Authority, this should only happen via migration"); AZ_Assert(localNetworkRole != NetEntityRole::Authority, "UpdateMessage trying to set local role to Authority, this should only happen via migration");
AZLOG_INFO AZLOG_INFO
( (
"EntityReplicationManager: Changing network role on entity %u, old role %u new role %u", "EntityReplicationManager: Changing network role on entity %s(%llu), old role %s new role %s",
aznumeric_cast<uint32_t>(netEntityId), replicatorEntity.GetEntity()->GetName().c_str(),
aznumeric_cast<uint32_t>(netBindComponent->GetNetEntityRole()), aznumeric_cast<AZ::u64>(netEntityId),
aznumeric_cast<uint32_t>(localNetworkRole) GetEnumString(netBindComponent->GetNetEntityRole()),
GetEnumString(localNetworkRole)
); );
if (NetworkRoleHasController(localNetworkRole)) if (NetworkRoleHasController(localNetworkRole))
@ -708,9 +720,9 @@ namespace Multiplayer
AZLOG_WARN AZLOG_WARN
( (
"Dropping Packet and LocalServerToRemoteClient connection, unexpected packet " "Dropping Packet and LocalServerToRemoteClient connection, unexpected packet "
"LocalShard=%s EntityId=%u RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s", "LocalShard=%s EntityId=%llu RemoteNetworkRole=%u BoundLocalNetworkRole=%u ActualNetworkRole=%u IsMarkedForRemoval=%s",
GetNetworkEntityManager()->GetHostId().GetString().c_str(), GetNetworkEntityManager()->GetHostId().GetString().c_str(),
aznumeric_cast<uint32_t>(entityReplicator->GetEntityHandle().GetNetEntityId()), aznumeric_cast<AZ::u64>(entityReplicator->GetEntityHandle().GetNetEntityId()),
aznumeric_cast<uint32_t>(entityReplicator->GetRemoteNetworkRole()), aznumeric_cast<uint32_t>(entityReplicator->GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityReplicator->GetBoundLocalNetworkRole()), aznumeric_cast<uint32_t>(entityReplicator->GetBoundLocalNetworkRole()),
aznumeric_cast<uint32_t>(entityReplicator->GetNetBindComponent()->GetNetEntityRole()), aznumeric_cast<uint32_t>(entityReplicator->GetNetBindComponent()->GetNetEntityRole()),
@ -760,13 +772,13 @@ namespace Multiplayer
result = UpdateValidationResult::DropMessage; result = UpdateValidationResult::DropMessage;
if (updateMessage.GetIsDelete()) if (updateMessage.GetIsDelete())
{ {
AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %u, sequence %d latest sequence %d from remote host %s", AZLOG(NET_RepDeletes, "EntityReplicationManager: Received old DeleteProxy message for entity id %llu, sequence %d latest sequence %d from remote host %s",
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str()); (AZ::u64)updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
} }
else else
{ {
AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %u, sequence %d latest sequence %d from remote host %s", AZLOG(NET_RepUpdate, "EntityReplicationManager: Received old PropertyChangeMessage message for entity id %llu, sequence %d latest sequence %d from remote host %s",
updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str()); (AZ::u64)updateMessage.GetEntityId(), (uint32_t)packetId, (uint32_t)propSubscriber->GetLastReceivedPacketId(), GetRemoteHostId().GetString().c_str());
} }
} }
} }
@ -853,10 +865,10 @@ namespace Multiplayer
{ {
AZLOG_INFO AZLOG_INFO
( (
"EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %u has already been deleted", "EntityReplicationManager: Dropping remote RPC message for component %s of rpc index %s, entityId %llu has already been deleted",
GetMultiplayerComponentRegistry()->GetComponentName(message.GetComponentId()), GetMultiplayerComponentRegistry()->GetComponentName(message.GetComponentId()),
GetMultiplayerComponentRegistry()->GetComponentRpcName(message.GetComponentId(), message.GetRpcIndex()), GetMultiplayerComponentRegistry()->GetComponentRpcName(message.GetComponentId(), message.GetRpcIndex()),
message.GetEntityId() static_cast<AZ::u64>(message.GetEntityId())
); );
return false; return false;
} }
@ -1113,7 +1125,7 @@ namespace Multiplayer
if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer) if (m_updateMode == EntityReplicationManager::Mode::LocalServerToRemoteServer)
{ {
netBindComponent->NotifyServerMigration(GetRemoteHostId(), GetConnection().GetConnectionId()); netBindComponent->NotifyServerMigration(GetRemoteHostId());
} }
bool didSucceed = true; bool didSucceed = true;
@ -1145,7 +1157,7 @@ namespace Multiplayer
AZ_Assert(didSucceed, "Failed to migrate entity from server"); AZ_Assert(didSucceed, "Failed to migrate entity from server");
m_sendMigrateEntityEvent.Signal(m_connection, message); m_sendMigrateEntityEvent.Signal(m_connection, message);
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote host %s", netEntityId, GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Migration packet sent %llu to remote host %s", static_cast<AZ::u64>(netEntityId), GetRemoteHostId().GetString().c_str());
// Notify all other EntityReplicationManagers that this entity has migrated so they can adjust their own replicators given our new proxy status // Notify all other EntityReplicationManagers that this entity has migrated so they can adjust their own replicators given our new proxy status
GetMultiplayer()->SendNotifyEntityMigrationEvent(entityHandle, GetRemoteHostId()); GetMultiplayer()->SendNotifyEntityMigrationEvent(entityHandle, GetRemoteHostId());
@ -1201,7 +1213,7 @@ namespace Multiplayer
// Change the role on the replicator // Change the role on the replicator
AddEntityReplicator(entityHandle, NetEntityRole::Server); AddEntityReplicator(entityHandle, NetEntityRole::Server);
AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote host %s", entityHandle.GetNetEntityId(), GetRemoteHostId().GetString().c_str()); AZLOG(NET_RepDeletes, "Handle Migration %llu new authority from remote host %s", static_cast<AZ::u64>(entityHandle.GetNetEntityId()), GetRemoteHostId().GetString().c_str());
return true; return true;
} }

@ -103,8 +103,8 @@ namespace Multiplayer
AZ_Assert AZ_Assert
( (
m_boundLocalNetworkRole != m_remoteNetworkRole, m_boundLocalNetworkRole != m_remoteNetworkRole,
"Invalid configuration detected, bound local role must differ from remote network role Role: %d", "Invalid configuration detected, bound local role must differ from remote network role: %s",
aznumeric_cast<int32_t>(m_boundLocalNetworkRole) GetEnumString(m_boundLocalNetworkRole)
); );
if (RemoteManagerOwnsEntityLifetime()) if (RemoteManagerOwnsEntityLifetime())
@ -176,7 +176,6 @@ namespace Multiplayer
switch (GetBoundLocalNetworkRole()) switch (GetBoundLocalNetworkRole())
{ {
case NetEntityRole::Authority: case NetEntityRole::Authority:
{
if (GetRemoteNetworkRole() == NetEntityRole::Client || GetRemoteNetworkRole() == NetEntityRole::Autonomous) if (GetRemoteNetworkRole() == NetEntityRole::Client || GetRemoteNetworkRole() == NetEntityRole::Autonomous)
{ {
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent()); m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
@ -189,10 +188,8 @@ namespace Multiplayer
{ {
m_onForwardRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent()); m_onForwardRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
} }
} break;
break;
case NetEntityRole::Server: case NetEntityRole::Server:
{
if (GetRemoteNetworkRole() == NetEntityRole::Authority) if (GetRemoteNetworkRole() == NetEntityRole::Authority)
{ {
m_onSendRpcHandler.Connect(netBindComponent->GetSendServerToAuthorityRpcEvent()); m_onSendRpcHandler.Connect(netBindComponent->GetSendServerToAuthorityRpcEvent());
@ -204,23 +201,21 @@ namespace Multiplayer
// Listen for these to forward the rpc along to the other Client replicators // Listen for these to forward the rpc along to the other Client replicators
m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent()); m_onSendRpcHandler.Connect(netBindComponent->GetSendAuthorityToClientRpcEvent());
} }
// NOTE: e_Autonomous is not connected to e_ServerProxy, it is always connected to an e_Authority else if (GetRemoteNetworkRole() == NetEntityRole::Autonomous)
AZ_Assert(GetRemoteNetworkRole() != NetEntityRole::Autonomous, "Unexpected autonomous remote role") {
} // NOTE: Autonomous is not connected to ServerProxy, it is always connected to an Authority
break; AZ_Assert(false, "Unexpected autonomous remote role")
}
break;
case NetEntityRole::Client: case NetEntityRole::Client:
{
// Nothing allowed, no Client to Server communication // Nothing allowed, no Client to Server communication
} break;
break;
case NetEntityRole::Autonomous: case NetEntityRole::Autonomous:
{
if (GetRemoteNetworkRole() == NetEntityRole::Authority) if (GetRemoteNetworkRole() == NetEntityRole::Authority)
{ {
m_onSendRpcHandler.Connect(netBindComponent->GetSendAutonomousToAuthorityRpcEvent()); m_onSendRpcHandler.Connect(netBindComponent->GetSendAutonomousToAuthorityRpcEvent());
} }
} break;
break;
default: default:
AZ_Assert(false, "Unexpected network role"); AZ_Assert(false, "Unexpected network role");
} }
@ -252,22 +247,9 @@ namespace Multiplayer
if (entity->GetState() != AZ::Entity::State::Init) if (entity->GetState() != AZ::Entity::State::Init)
{ {
AZLOG_WARN("Trying to activate an entity that is not in the Init state (%u)", GetEntityHandle().GetNetEntityId()); AZLOG_WARN("Trying to activate an entity that is not in the Init state (%llu)", static_cast<AZ::u64>(GetEntityHandle().GetNetEntityId()));
} }
// First we need to make sure the transform component has been updated with the correct value prior to activation
// This is because vanilla az components may only depend on the transform component, not the multiplayer transform component
//if (auto* locationComponent = FindCommonComponent<LocationComponent::Common>(GetEntityHandle()))
//{
// AZ::Transform newTransform = locationComponent->GetTransform();
// auto* transformComponent = entity->FindComponent<AzFramework::TransformComponent>();
// if (transformComponent)
// {
// // We can't use EBus here since the TransFormBus does not get connected until the activate call below
// transformComponent->SetWorldTM(newTransform);
// }
//}
// Ugly, but this is the only time we need to call a non-const function on this entity
entity->Activate(); entity->Activate();
m_replicationManager.m_orphanedEntityRpcs.DispatchOrphanedRpcs(*this); m_replicationManager.m_orphanedEntityRpcs.DispatchOrphanedRpcs(*this);
@ -281,8 +263,7 @@ namespace Multiplayer
NetBindComponent* netBindComponent = m_netBindComponent; NetBindComponent* netBindComponent = m_netBindComponent;
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent"); AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client; bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client;
bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous; bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous;
if (isAuthority || isClient || isAutonomous) if (isAuthority || isClient || isAutonomous)
@ -296,10 +277,10 @@ namespace Multiplayer
bool EntityReplicator::OwnsReplicatorLifetime() const bool EntityReplicator::OwnsReplicatorLifetime() const
{ {
bool ret(false); bool ret(false);
if (GetBoundLocalNetworkRole() == NetEntityRole::Authority if (GetBoundLocalNetworkRole() == NetEntityRole::Authority // Authority always owns lifetime
|| (GetBoundLocalNetworkRole() == NetEntityRole::Server || (GetBoundLocalNetworkRole() == NetEntityRole::Server // Server also owns lifetime if the remote endpoint is a client of some form
&& (GetRemoteNetworkRole() == NetEntityRole::Client && (GetRemoteNetworkRole() == NetEntityRole::Client
|| GetRemoteNetworkRole() == NetEntityRole::Autonomous))) || GetRemoteNetworkRole() == NetEntityRole::Autonomous)))
{ {
ret = true; ret = true;
} }
@ -309,10 +290,9 @@ namespace Multiplayer
bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const
{ {
bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server) bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server)
&& (GetRemoteNetworkRole() == NetEntityRole::Authority); && (GetRemoteNetworkRole() == NetEntityRole::Authority);
bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client) bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client)
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous); || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
return isServer || isClient; return isServer || isClient;
} }
@ -429,10 +409,8 @@ namespace Multiplayer
if (const NetworkTransformComponent* networkTransform = entity->FindComponent<NetworkTransformComponent>()) if (const NetworkTransformComponent* networkTransform = entity->FindComponent<NetworkTransformComponent>())
{ {
const NetEntityId parentId = networkTransform->GetParentEntityId(); const NetEntityId parentId = networkTransform->GetParentEntityId();
/* // For root entities attached to a level, a network parent won't be set.
* For root entities attached to a level, a network parent won't be set. // In this case, this entity is the root entity of the hierarchy and it will be activated first.
* In this case, this entity is the root entity of the hierarchy and it will be activated first.
*/
if (parentId != InvalidNetEntityId) if (parentId != InvalidNetEntityId)
{ {
ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId); ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId);
@ -452,9 +430,9 @@ namespace Multiplayer
AZLOG AZLOG
( (
NET_HierarchyActivationInfo, NET_HierarchyActivationInfo,
"Hierchical entity %s asking for activation - waiting on the parent %u", "Hierchical entity %s asking for activation - waiting on the parent %llu",
entity->GetName().c_str(), entity->GetName().c_str(),
aznumeric_cast<uint32_t>(parentId) aznumeric_cast<AZ::u64>(parentId)
); );
return false; return false;
} }
@ -472,19 +450,19 @@ namespace Multiplayer
AZLOG AZLOG
( (
NET_RepDeletes, NET_RepDeletes,
"Sending delete replicator id %u migrated %d to remote host %s", "Sending delete replicator id %llu migrated %d to remote host %s",
aznumeric_cast<uint32_t>(GetEntityHandle().GetNetEntityId()), aznumeric_cast<AZ::u64>(GetEntityHandle().GetNetEntityId()),
WasMigrated() ? 1 : 0, WasMigrated() ? 1 : 0,
m_replicationManager.GetRemoteHostId().GetString().c_str() m_replicationManager.GetRemoteHostId().GetString().c_str()
); );
return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated(), m_propertyPublisher->IsRemoteReplicatorEstablished()); return NetworkEntityUpdateMessage(GetEntityHandle().GetNetEntityId(), WasMigrated());
} }
NetBindComponent* netBindComponent = GetNetBindComponent(); NetBindComponent* netBindComponent = GetNetBindComponent();
//const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished(); const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
NetworkEntityUpdateMessage updateMessage(GetRemoteNetworkRole(), GetEntityHandle().GetNetEntityId()); NetworkEntityUpdateMessage updateMessage(GetRemoteNetworkRole(), GetEntityHandle().GetNetEntityId());
//if (sendSliceName) if (sendSliceName)
{ {
updateMessage.SetPrefabEntityId(netBindComponent->GetPrefabEntityId()); updateMessage.SetPrefabEntityId(netBindComponent->GetPrefabEntityId());
} }
@ -553,42 +531,33 @@ namespace Multiplayer
switch (entityRpcMessage.GetRpcDeliveryType()) switch (entityRpcMessage.GetRpcDeliveryType())
{ {
case RpcDeliveryType::AuthorityToClient: case RpcDeliveryType::AuthorityToClient:
{
if (((GetBoundLocalNetworkRole() == NetEntityRole::Client) || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous)) if (((GetBoundLocalNetworkRole() == NetEntityRole::Client) || (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous))
&& (GetRemoteNetworkRole() == NetEntityRole::Authority)) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
{ {
// We are a local client, and we are connected to server, aka AuthorityToClient // We are a local client, and we are connected to server, aka AuthorityToClient
result = RpcValidationResult::HandleRpc; result = RpcValidationResult::HandleRpc;
} }
if ((GetBoundLocalNetworkRole() == NetEntityRole::Server) if ((GetBoundLocalNetworkRole() == NetEntityRole::Server) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
{ {
// We are on a server, and we received this message from another server, therefore we should forward this to any connected clients // We are on a server, and we received this message from another server, therefore we should forward this to any connected clients
result = RpcValidationResult::ForwardToClient; result = RpcValidationResult::ForwardToClient;
} }
} break;
break;
case RpcDeliveryType::AuthorityToAutonomous: case RpcDeliveryType::AuthorityToAutonomous:
{ if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous) && (GetRemoteNetworkRole() == NetEntityRole::Authority))
if ((GetBoundLocalNetworkRole() == NetEntityRole::Autonomous)
&& (GetRemoteNetworkRole() == NetEntityRole::Authority))
{ {
// We are an autonomous client, and we are connected to server, aka AuthorityToAutonomous // We are an autonomous client, and we are connected to server, aka AuthorityToAutonomous
result = RpcValidationResult::HandleRpc; result = RpcValidationResult::HandleRpc;
} }
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
&& (GetRemoteNetworkRole() == NetEntityRole::Server))
{ {
// We are on a server, and we received this message from another server, therefore we should forward this to our autonomous player // We are on a server, and we received this message from another server, therefore we should forward this to our autonomous player
// This can occur if we've recently migrated // This can occur if we've recently migrated
result = RpcValidationResult::ForwardToAutonomous; result = RpcValidationResult::ForwardToAutonomous;
} }
} break;
break;
case RpcDeliveryType::AutonomousToAuthority: case RpcDeliveryType::AutonomousToAuthority:
{ if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Autonomous))
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
&& (GetRemoteNetworkRole() == NetEntityRole::Autonomous))
{ {
if (IsMarkedForRemoval()) if (IsMarkedForRemoval())
{ {
@ -610,12 +579,9 @@ namespace Multiplayer
result = RpcValidationResult::HandleRpc; result = RpcValidationResult::HandleRpc;
} }
} }
} break;
break;
case RpcDeliveryType::ServerToAuthority: case RpcDeliveryType::ServerToAuthority:
{ if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority) && (GetRemoteNetworkRole() == NetEntityRole::Server))
if ((GetBoundLocalNetworkRole() == NetEntityRole::Authority)
&& (GetRemoteNetworkRole() == NetEntityRole::Server))
{ {
// if we're marked for removal, then we should forward to whomever now owns this entity // if we're marked for removal, then we should forward to whomever now owns this entity
if (IsMarkedForRemoval()) if (IsMarkedForRemoval())
@ -638,9 +604,9 @@ namespace Multiplayer
result = RpcValidationResult::HandleRpc; result = RpcValidationResult::HandleRpc;
} }
} }
break;
} }
break;
}
if (result == RpcValidationResult::DropRpcAndDisconnect) if (result == RpcValidationResult::DropRpcAndDisconnect)
{ {
bool isLocalServer = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) || (GetBoundLocalNetworkRole() == NetEntityRole::Server); bool isLocalServer = (GetBoundLocalNetworkRole() == NetEntityRole::Authority) || (GetBoundLocalNetworkRole() == NetEntityRole::Server);
@ -654,30 +620,29 @@ namespace Multiplayer
{ {
AZLOG_ERROR AZLOG_ERROR
( (
"Dropping RPC and Connection EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s", "Dropping RPC and Connection EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()), aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()), GetEnumString(GetBoundLocalNetworkRole()),
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()), GetEnumString(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()), aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()), GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false", entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false" IsMarkedForRemoval() ? "true" : "false"
); );
} }
} }
if (result == RpcValidationResult::DropRpc) if (result == RpcValidationResult::DropRpc)
{ {
AZLOG AZLOG
( (
NET_Rpc, NET_Rpc,
"Dropping RPC EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s", "Dropping RPC EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()), aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()), GetEnumString(GetBoundLocalNetworkRole()),
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()), GetEnumString(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()), aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()), GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false", entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false" IsMarkedForRemoval() ? "true" : "false"
); );
@ -696,13 +661,12 @@ namespace Multiplayer
{ {
AZLOG_WARN AZLOG_WARN
( (
"Dropping RPC since entity deleted EntityId=%u LocalRole=%u RemoteRole=%u RpcDeliveryType=%u ComponentId=%u RpcType=%u IsReliable=%s IsMarkedForRemoval=%s", "Dropping RPC since entity deleted EntityId=%llu LocalRole=%s RemoteRole=%s RpcDeliveryType=%u RpcName=%s IsReliable=%s IsMarkedForRemoval=%s",
aznumeric_cast<uint32_t>(m_entityHandle.GetNetEntityId()), aznumeric_cast<AZ::u64>(m_entityHandle.GetNetEntityId()),
aznumeric_cast<uint32_t>(GetBoundLocalNetworkRole()), GetEnumString(GetBoundLocalNetworkRole()),
aznumeric_cast<uint32_t>(GetRemoteNetworkRole()), GetEnumString(GetRemoteNetworkRole()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()), aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcDeliveryType()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetComponentId()), GetMultiplayerComponentRegistry()->GetComponentRpcName(entityRpcMessage.GetComponentId(), entityRpcMessage.GetRpcIndex()),
aznumeric_cast<uint32_t>(entityRpcMessage.GetRpcIndex()),
entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false", entityRpcMessage.GetReliability() == ReliabilityType::Reliable ? "true" : "false",
IsMarkedForRemoval() ? "true" : "false" IsMarkedForRemoval() ? "true" : "false"
); );
@ -740,23 +704,23 @@ namespace Multiplayer
case RpcValidationResult::DropRpcAndDisconnect: case RpcValidationResult::DropRpcAndDisconnect:
return false; return false;
case RpcValidationResult::ForwardToClient: case RpcValidationResult::ForwardToClient:
{ {
ScopedForwardingMessage forwarding(*this); ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage); m_netBindComponent->GetSendAuthorityToClientRpcEvent().Signal(entityRpcMessage);
}
return true; return true;
}
case RpcValidationResult::ForwardToAutonomous: case RpcValidationResult::ForwardToAutonomous:
{ {
ScopedForwardingMessage forwarding(*this); ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage); m_netBindComponent->GetSendAuthorityToAutonomousRpcEvent().Signal(entityRpcMessage);
}
return true; return true;
}
case RpcValidationResult::ForwardToAuthority: case RpcValidationResult::ForwardToAuthority:
{ {
ScopedForwardingMessage forwarding(*this); ScopedForwardingMessage forwarding(*this);
m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage); m_netBindComponent->GetSendServerToAuthorityRpcEvent().Signal(entityRpcMessage);
}
return true; return true;
}
default: default:
break; break;
} }

@ -33,8 +33,8 @@ namespace Multiplayer
AZLOG AZLOG
( (
NET_AuthTracker, NET_AuthTracker,
"AuthTracker: Removing timeout for networkEntityId %u from %s, new owner is %s", "AuthTracker: Removing timeout for networkEntityId %llu from %s, new owner is %s",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
timeoutData->second.m_previousOwner.GetString().c_str(), timeoutData->second.m_previousOwner.GetString().c_str(),
newOwner.GetString().c_str() newOwner.GetString().c_str()
); );
@ -48,8 +48,8 @@ namespace Multiplayer
AZLOG AZLOG
( (
NET_AuthTracker, NET_AuthTracker,
"AuthTracker: Assigning networkEntityId %u from %s to %s", "AuthTracker: Assigning networkEntityId %llu from %s to %s",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
iter->second.back().GetString().c_str(), iter->second.back().GetString().c_str(),
newOwner.GetString().c_str() newOwner.GetString().c_str()
); );
@ -59,8 +59,8 @@ namespace Multiplayer
AZLOG AZLOG
( (
NET_AuthTracker, NET_AuthTracker,
"AuthTracker: Assigning networkEntityId %u to %s", "AuthTracker: Assigning networkEntityId %llu to %s",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
newOwner.GetString().c_str() newOwner.GetString().c_str()
); );
} }
@ -87,7 +87,7 @@ namespace Multiplayer
} }
} }
AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %u from %s", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str()); AZLOG(NET_AuthTracker, "AuthTracker: Removing networkEntityId %llu from %s", aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()), previousOwner.GetString().c_str());
if (auto localEnt = entityHandle.GetEntity()) if (auto localEnt = entityHandle.GetEntity())
{ {
if (authorityStack.empty()) if (authorityStack.empty())
@ -114,14 +114,14 @@ namespace Multiplayer
} }
else else
{ {
AZLOG(NET_AuthTracker, "AuthTracker: Skipping timeout for Autonomous networkEntityId %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId())); AZLOG(NET_AuthTracker, "AuthTracker: Skipping timeout for Autonomous networkEntityId %llu", aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()));
} }
} }
} }
} }
else else
{ {
AZLOG(NET_AuthTracker, "AuthTracker: Remove authority called on networkEntityId that was never added %u", aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId())); AZLOG(NET_AuthTracker, "AuthTracker: Remove authority called on networkEntityId that was never added %llu", aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()));
AZ_Assert(false, "AuthTracker: Remove authority called on entity that was never added"); AZ_Assert(false, "AuthTracker: Remove authority called on entity that was never added");
} }
} }
@ -205,8 +205,8 @@ namespace Multiplayer
{ {
AZLOG_ERROR AZLOG_ERROR
( (
"Timed out entity id %u during migration previous owner %s, removing it", "Timed out entity id %llu during migration previous owner %s, removing it",
aznumeric_cast<uint32_t>(entityHandle.GetNetEntityId()), aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
timeoutData->second.m_previousOwner.GetString().c_str() timeoutData->second.m_previousOwner.GetString().c_str()
); );
m_networkEntityManager.MarkForRemoval(entityHandle); m_networkEntityManager.MarkForRemoval(entityHandle);

@ -18,21 +18,13 @@ namespace Multiplayer
{ {
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* networkEntityTracker) ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* networkEntityTracker)
: m_entity(entity) : m_entity(entity)
, m_networkEntityTracker(networkEntityTracker) , m_networkEntityTracker((networkEntityTracker != nullptr) ? networkEntityTracker : GetNetworkEntityTracker())
{ {
if (m_networkEntityTracker == nullptr) AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid");
{ m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
m_networkEntityTracker = GetNetworkEntityTracker();
}
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
if (entity) if (entity)
{ {
AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid");
m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity); m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity);
if (m_netBindComponent != nullptr) if (m_netBindComponent != nullptr)
{ {

@ -48,6 +48,20 @@ namespace Multiplayer
void NetworkEntityManager::Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain) void NetworkEntityManager::Initialize(const HostId& hostId, AZStd::unique_ptr<IEntityDomain> entityDomain)
{ {
m_hostId = hostId; m_hostId = hostId;
// Configure our vended NetEntityIds so that no two hosts generate the same NetEntityId
{
// Needs more thought
const uint64_t addrPortion = hostId.GetAddress(AzNetworking::ByteOrder::Host);
const uint64_t portPortion = hostId.GetPort(AzNetworking::ByteOrder::Host);
const uint64_t hostIdentifier = (portPortion << 32) | addrPortion;
const AZ::HashValue32 hostHash = AZ::TypeHash32(hostIdentifier);
NetEntityId hostEntityIdOffset = static_cast<NetEntityId>(hostHash) << 32;
m_nextEntityId &= NetEntityId{ 0x0000000000000000FFFFFFFFFFFFFFFF };
m_nextEntityId |= hostEntityIdOffset;
}
m_entityDomain = AZStd::move(entityDomain); m_entityDomain = AZStd::move(entityDomain);
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true); m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
m_entityDomain->ActivateTracking(m_ownedEntities); m_entityDomain->ActivateTracking(m_ownedEntities);
@ -227,11 +241,19 @@ namespace Multiplayer
{ {
AZ::Entity* entity = it->second; AZ::Entity* entity = it->second;
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity); NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
entityBounds.Expand(AZ::Vector3(0.01f));
if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority) if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
{ {
const AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId()); debugDisplay->SetColor(AZ::Colors::Black);
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax()); debugDisplay->SetAlpha(0.5f);
}
else
{
debugDisplay->SetColor(AZ::Colors::DeepSkyBlue);
debugDisplay->SetAlpha(0.25f);
} }
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
} }
if (m_entityDomain != nullptr) if (m_entityDomain != nullptr)

@ -18,7 +18,6 @@ namespace Multiplayer
, m_entityId(rhs.m_entityId) , m_entityId(rhs.m_entityId)
, m_isDelete(rhs.m_isDelete) , m_isDelete(rhs.m_isDelete)
, m_wasMigrated(rhs.m_wasMigrated) , m_wasMigrated(rhs.m_wasMigrated)
, m_takeOwnership(rhs.m_takeOwnership)
, m_hasValidPrefabId(rhs.m_hasValidPrefabId) , m_hasValidPrefabId(rhs.m_hasValidPrefabId)
, m_prefabEntityId(rhs.m_prefabEntityId) , m_prefabEntityId(rhs.m_prefabEntityId)
, m_data(AZStd::move(rhs.m_data)) , m_data(AZStd::move(rhs.m_data))
@ -31,7 +30,6 @@ namespace Multiplayer
, m_entityId(rhs.m_entityId) , m_entityId(rhs.m_entityId)
, m_isDelete(rhs.m_isDelete) , m_isDelete(rhs.m_isDelete)
, m_wasMigrated(rhs.m_wasMigrated) , m_wasMigrated(rhs.m_wasMigrated)
, m_takeOwnership(rhs.m_takeOwnership)
, m_hasValidPrefabId(rhs.m_hasValidPrefabId) , m_hasValidPrefabId(rhs.m_hasValidPrefabId)
, m_prefabEntityId(rhs.m_prefabEntityId) , m_prefabEntityId(rhs.m_prefabEntityId)
{ {
@ -58,11 +56,10 @@ namespace Multiplayer
; ;
} }
NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated, bool takeOwnership) NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated)
: m_entityId(entityId) : m_entityId(entityId)
, m_isDelete(true) , m_isDelete(true)
, m_wasMigrated(wasMigrated) , m_wasMigrated(wasMigrated)
, m_takeOwnership(takeOwnership)
{ {
// this is a delete entity message c-tor // this is a delete entity message c-tor
} }
@ -73,7 +70,6 @@ namespace Multiplayer
m_entityId = rhs.m_entityId; m_entityId = rhs.m_entityId;
m_isDelete = rhs.m_isDelete; m_isDelete = rhs.m_isDelete;
m_wasMigrated = rhs.m_wasMigrated; m_wasMigrated = rhs.m_wasMigrated;
m_takeOwnership = rhs.m_takeOwnership;
m_hasValidPrefabId = rhs.m_hasValidPrefabId; m_hasValidPrefabId = rhs.m_hasValidPrefabId;
m_prefabEntityId = rhs.m_prefabEntityId; m_prefabEntityId = rhs.m_prefabEntityId;
m_data = AZStd::move(rhs.m_data); m_data = AZStd::move(rhs.m_data);
@ -86,7 +82,6 @@ namespace Multiplayer
m_entityId = rhs.m_entityId; m_entityId = rhs.m_entityId;
m_isDelete = rhs.m_isDelete; m_isDelete = rhs.m_isDelete;
m_wasMigrated = rhs.m_wasMigrated; m_wasMigrated = rhs.m_wasMigrated;
m_takeOwnership = rhs.m_takeOwnership;
m_hasValidPrefabId = rhs.m_hasValidPrefabId; m_hasValidPrefabId = rhs.m_hasValidPrefabId;
m_prefabEntityId = rhs.m_prefabEntityId; m_prefabEntityId = rhs.m_prefabEntityId;
if (rhs.m_data != nullptr) if (rhs.m_data != nullptr)
@ -104,7 +99,6 @@ namespace Multiplayer
&& (m_entityId == rhs.m_entityId) && (m_entityId == rhs.m_entityId)
&& (m_isDelete == rhs.m_isDelete) && (m_isDelete == rhs.m_isDelete)
&& (m_wasMigrated == rhs.m_wasMigrated) && (m_wasMigrated == rhs.m_wasMigrated)
&& (m_takeOwnership == rhs.m_takeOwnership)
&& (m_hasValidPrefabId == rhs.m_hasValidPrefabId) && (m_hasValidPrefabId == rhs.m_hasValidPrefabId)
&& (m_prefabEntityId == rhs.m_prefabEntityId)); && (m_prefabEntityId == rhs.m_prefabEntityId));
} }
@ -160,11 +154,6 @@ namespace Multiplayer
return m_wasMigrated; return m_wasMigrated;
} }
bool NetworkEntityUpdateMessage::GetTakeOwnership() const
{
return m_takeOwnership;
}
bool NetworkEntityUpdateMessage::GetHasValidPrefabId() const bool NetworkEntityUpdateMessage::GetHasValidPrefabId() const
{ {
return m_hasValidPrefabId; return m_hasValidPrefabId;
@ -210,17 +199,15 @@ namespace Multiplayer
serializer.Serialize(m_entityId, "EntityId"); serializer.Serialize(m_entityId, "EntityId");
// Use the upper 4 bits for boolean flags, and the lower 4 bits for the network role // Use the upper 4 bits for boolean flags, and the lower 4 bits for the network role
uint8_t networkTypeAndFlags = (m_isDelete ? 0x80 : 0x00) uint8_t networkTypeAndFlags = (m_isDelete ? 0x40 : 0x00)
| (m_wasMigrated ? 0x40 : 0x00) | (m_wasMigrated ? 0x20 : 0x00)
| (m_takeOwnership ? 0x20 : 0x00)
| (m_hasValidPrefabId ? 0x10 : 0x00) | (m_hasValidPrefabId ? 0x10 : 0x00)
| static_cast<uint8_t>(m_networkRole); | static_cast<uint8_t>(m_networkRole);
if (serializer.Serialize(networkTypeAndFlags, "TypeAndFlags")) if (serializer.Serialize(networkTypeAndFlags, "TypeAndFlags"))
{ {
m_isDelete = (networkTypeAndFlags & 0x80) == 0x80; m_isDelete = (networkTypeAndFlags & 0x40) == 0x40;
m_wasMigrated = (networkTypeAndFlags & 0x40) == 0x40; m_wasMigrated = (networkTypeAndFlags & 0x20) == 0x20;
m_takeOwnership = (networkTypeAndFlags & 0x20) == 0x20;
m_hasValidPrefabId = (networkTypeAndFlags & 0x10) == 0x10; m_hasValidPrefabId = (networkTypeAndFlags & 0x10) == 0x10;
m_networkRole = static_cast<NetEntityRole>(networkTypeAndFlags & 0x0F); m_networkRole = static_cast<NetEntityRole>(networkTypeAndFlags & 0x0F);
} }

@ -6,7 +6,7 @@
* *
*/ */
#include <Source/NetworkInput/NetworkInputArray.h> #include <Multiplayer/NetworkInput/NetworkInputArray.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h> #include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <AzNetworking/Serialization/ISerializer.h> #include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/Serialization/DeltaSerializer.h> #include <AzNetworking/Serialization/DeltaSerializer.h>

@ -6,7 +6,7 @@
* *
*/ */
#include <Source/NetworkInput/NetworkInputChild.h> #include <Multiplayer/NetworkInput/NetworkInputChild.h>
#include <Multiplayer/IMultiplayer.h> #include <Multiplayer/IMultiplayer.h>
#include <AzNetworking/Serialization/ISerializer.h> #include <AzNetworking/Serialization/ISerializer.h>

@ -6,7 +6,7 @@
* *
*/ */
#include <Source/NetworkInput/NetworkInputHistory.h> #include <Multiplayer/NetworkInput/NetworkInputHistory.h>
namespace Multiplayer namespace Multiplayer
{ {

@ -6,7 +6,7 @@
* *
*/ */
#include <Source/NetworkInput/NetworkInputMigrationVector.h> #include <Multiplayer/NetworkInput/NetworkInputMigrationVector.h>
#include <Multiplayer/IMultiplayer.h> #include <Multiplayer/IMultiplayer.h>
#include <AzNetworking/Serialization/ISerializer.h> #include <AzNetworking/Serialization/ISerializer.h>

@ -92,17 +92,10 @@ namespace Multiplayer
return m_isPoorConnection ? sv_MinEntitiesToReplicate : sv_MaxEntitiesToReplicate; return m_isPoorConnection ? sv_MinEntitiesToReplicate : sv_MaxEntitiesToReplicate;
} }
bool ServerToClientReplicationWindow::IsInWindow(const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const bool ServerToClientReplicationWindow::IsInWindow([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, NetEntityRole& outNetworkRole) const
{ {
// TODO: Clean up this interface, this function is used for server->server migrations, and probably shouldn't be exposed in it's current setup
AZ_Assert(false, "IsInWindow should not be called on the ServerToClientReplicationWindow"); AZ_Assert(false, "IsInWindow should not be called on the ServerToClientReplicationWindow");
outNetworkRole = NetEntityRole::InvalidRole; outNetworkRole = NetEntityRole::InvalidRole;
auto iter = m_replicationSet.find(entityHandle);
if (iter != m_replicationSet.end())
{
outNetworkRole = iter->second.m_netEntityRole;
return true;
}
return false; return false;
} }
@ -146,7 +139,7 @@ namespace Multiplayer
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker(); NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
IFilterEntityManager* filterEntityManager = GetMultiplayer()->GetFilterEntityManager(); IFilterEntityManager* filterEntityManager = GetMultiplayer()->GetFilterEntityManager();
// Add all the neighbors // Add all the neighbours
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries) for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
{ {
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData); AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
@ -301,7 +294,6 @@ namespace Multiplayer
void ServerToClientReplicationWindow::AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, [[maybe_unused]] float distanceSquared) void ServerToClientReplicationWindow::AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, [[maybe_unused]] float distanceSquared)
{ {
// Assumption: the entity has been checked for filtering prior to this call. // Assumption: the entity has been checked for filtering prior to this call.
if (!sv_ReplicateServerProxies) if (!sv_ReplicateServerProxies)
{ {
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
@ -312,11 +304,11 @@ namespace Multiplayer
} }
} }
const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set const bool isQueueFull = (m_candidateQueue.size() >= sv_MaxEntitiesToTrackReplication); // See if have the maximum number of entities in our set
const bool isInReplicationSet = m_replicationSet.find(entityHandle) != m_replicationSet.end(); const bool isInReplicationSet = m_replicationSet.find(entityHandle) != m_replicationSet.end();
if (!isInReplicationSet) if (!isInReplicationSet)
{ {
if (isQueueFull) // if our set is full, then we need to remove the worst priority in our set if (isQueueFull) // If our set is full, then we need to remove the worst priority in our set
{ {
ConstNetworkEntityHandle removeEnt = m_candidateQueue.top().m_entityHandle; ConstNetworkEntityHandle removeEnt = m_candidateQueue.top().m_entityHandle;
m_candidateQueue.pop(); m_candidateQueue.pop();
@ -332,7 +324,7 @@ namespace Multiplayer
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get(); INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
for (const AZ::Entity* controlledEntity : hierarchyComponent.m_hierarchicalEntities) for (const AZ::Entity* controlledEntity : hierarchyComponent.GetHierarchicalEntities())
{ {
NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId()); NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId());
AZ_Assert(controlledNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); AZ_Assert(controlledNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");

@ -76,8 +76,6 @@ namespace Multiplayer
AZ::EntityActivatedEvent::Handler m_entityActivatedEventHandler; AZ::EntityActivatedEvent::Handler m_entityActivatedEventHandler;
AZ::EntityDeactivatedEvent::Handler m_entityDeactivatedEventHandler; AZ::EntityDeactivatedEvent::Handler m_entityDeactivatedEventHandler;
//NetBindComponent* m_controlledNetBindComponent = nullptr;
AzNetworking::IConnection* m_connection = nullptr; AzNetworking::IConnection* m_connection = nullptr;
// Cached values to detect a poor network connection // Cached values to detect a poor network connection

@ -16,7 +16,7 @@
#include <Multiplayer/Components/NetBindComponent.h> #include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h> #include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h> #include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkInput/NetworkInputArray.h> #include <Multiplayer/NetworkInput/NetworkInputArray.h>
#include <Source/NetworkEntity/NetworkEntityManager.h> #include <Source/NetworkEntity/NetworkEntityManager.h>
namespace Multiplayer namespace Multiplayer
@ -213,9 +213,8 @@ namespace Multiplayer
constexpr uint32_t bufferSize = 100; constexpr uint32_t bufferSize = 100;
AZStd::array<uint8_t, bufferSize> buffer = {}; AZStd::array<uint8_t, bufferSize> buffer = {};
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
inSerializer.Serialize(reinterpret_cast<uint32_t&>(value), ISerializer& serializer = inSerializer;
"hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);

@ -342,8 +342,11 @@ namespace Multiplayer
void AddClientMigrationEndEventHandler([[maybe_unused]] ClientMigrationEndEvent::Handler& handler) override {} void AddClientMigrationEndEventHandler([[maybe_unused]] ClientMigrationEndEvent::Handler& handler) override {}
void AddNotifyClientMigrationHandler([[maybe_unused]] NotifyClientMigrationEvent::Handler& handler) override {} void AddNotifyClientMigrationHandler([[maybe_unused]] NotifyClientMigrationEvent::Handler& handler) override {}
void AddNotifyEntityMigrationEventHandler([[maybe_unused]] NotifyEntityMigrationEvent::Handler& handler) override {} void AddNotifyEntityMigrationEventHandler([[maybe_unused]] NotifyEntityMigrationEvent::Handler& handler) override {}
void SendNotifyClientMigrationEvent([[maybe_unused]] const HostId& hostId, [[maybe_unused]] uint64_t userIdentifier, [[maybe_unused]] ClientInputId lastClientInputId) override {} void SendNotifyClientMigrationEvent([[maybe_unused]] AzNetworking::ConnectionId connectionId, [[maybe_unused]] const HostId& hostId,
[[maybe_unused]] uint64_t userIdentifier, [[maybe_unused]] ClientInputId lastClientInputId, [[maybe_unused]] NetEntityId netEntityId) override {}
void SendNotifyEntityMigrationEvent([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] const HostId& remoteHostId) override {} void SendNotifyEntityMigrationEvent([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] const HostId& remoteHostId) override {}
void RegisterPlayerIdentifierForRejoin(uint64_t, NetEntityId) override {}
void CompleteClientMigration(uint64_t, AzNetworking::ConnectionId, const HostId&, ClientInputId) override {}
void SetShouldSpawnNetworkEntities([[maybe_unused]] bool value) override {} void SetShouldSpawnNetworkEntities([[maybe_unused]] bool value) override {}
bool GetShouldSpawnNetworkEntities() const override { return true; } bool GetShouldSpawnNetworkEntities() const override { return true; }
@ -535,9 +538,8 @@ namespace Multiplayer
constexpr uint32_t bufferSize = 100; constexpr uint32_t bufferSize = 100;
AZStd::array<uint8_t, bufferSize> buffer = {}; AZStd::array<uint8_t, bufferSize> buffer = {};
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
inSerializer.Serialize(reinterpret_cast<uint32_t&>(netParentId), ISerializer& serializer = inSerializer;
"parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */ serializer.Serialize(netParentId, "parentEntityId"); // Derived from NetworkTransformComponent.AutoComponent.xml
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
@ -560,9 +562,8 @@ namespace Multiplayer
constexpr uint32_t bufferSize = 100; constexpr uint32_t bufferSize = 100;
AZStd::array<uint8_t, bufferSize> buffer = {}; AZStd::array<uint8_t, bufferSize> buffer = {};
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
inSerializer.Serialize(reinterpret_cast<uint32_t&>(value), ISerializer& serializer = inSerializer;
"hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);

@ -317,9 +317,8 @@ namespace Multiplayer
constexpr uint32_t bufferSize = 100; constexpr uint32_t bufferSize = 100;
AZStd::array<uint8_t, bufferSize> buffer = {}; AZStd::array<uint8_t, bufferSize> buffer = {};
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
inSerializer.Serialize(reinterpret_cast<uint32_t&>(netParentId), ISerializer& serializer = inSerializer;
"parentEntityId", /* Derived from NetworkTransformComponent.AutoComponent.xml */ serializer.Serialize(netParentId, "parentEntityId"); // Derived from NetworkTransformComponent.AutoComponent.xml
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
@ -365,9 +364,8 @@ namespace Multiplayer
constexpr uint32_t bufferSize = 100; constexpr uint32_t bufferSize = 100;
AZStd::array<uint8_t, bufferSize> buffer = {}; AZStd::array<uint8_t, bufferSize> buffer = {};
NetworkInputSerializer inSerializer(buffer.begin(), bufferSize); NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
inSerializer.Serialize(reinterpret_cast<uint32_t&>(value), ISerializer& serializer = inSerializer;
"hierarchyRoot", /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */ serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml
AZStd::numeric_limits<uint32_t>::min(), AZStd::numeric_limits<uint32_t>::max());
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize); NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);

@ -33,7 +33,7 @@ namespace UnitTest
MOCK_METHOD1(AddServerAcceptanceReceivedHandler, void(Multiplayer::ServerAcceptanceReceivedEvent::Handler&)); MOCK_METHOD1(AddServerAcceptanceReceivedHandler, void(Multiplayer::ServerAcceptanceReceivedEvent::Handler&));
MOCK_METHOD1(AddSessionInitHandler, void(Multiplayer::SessionInitEvent::Handler&)); MOCK_METHOD1(AddSessionInitHandler, void(Multiplayer::SessionInitEvent::Handler&));
MOCK_METHOD1(AddSessionShutdownHandler, void(Multiplayer::SessionShutdownEvent::Handler&)); MOCK_METHOD1(AddSessionShutdownHandler, void(Multiplayer::SessionShutdownEvent::Handler&));
MOCK_METHOD3(SendNotifyClientMigrationEvent, void(const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId)); MOCK_METHOD5(SendNotifyClientMigrationEvent, void(AzNetworking::ConnectionId, const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId, Multiplayer::NetEntityId));
MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&)); MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&));
MOCK_METHOD1(SendReadyForEntityUpdates, void(bool)); MOCK_METHOD1(SendReadyForEntityUpdates, void(bool));
MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs()); MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs());
@ -42,6 +42,8 @@ namespace UnitTest
MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ()); MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ());
MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*)); MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*));
MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ()); MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ());
MOCK_METHOD2(RegisterPlayerIdentifierForRejoin, void(uint64_t, Multiplayer::NetEntityId));
MOCK_METHOD4(CompleteClientMigration, void(uint64_t, AzNetworking::ConnectionId, const Multiplayer::HostId&, Multiplayer::ClientInputId));
MOCK_METHOD1(SetShouldSpawnNetworkEntities, void(bool)); MOCK_METHOD1(SetShouldSpawnNetworkEntities, void(bool));
MOCK_CONST_METHOD0(GetShouldSpawnNetworkEntities, bool()); MOCK_CONST_METHOD0(GetShouldSpawnNetworkEntities, bool());
}; };

@ -17,9 +17,9 @@
#include <Multiplayer/Components/NetBindComponent.h> #include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/NetworkInput/NetworkInput.h> #include <Multiplayer/NetworkInput/NetworkInput.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h> #include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <NetworkInput/NetworkInputArray.h> #include <Multiplayer/NetworkInput/NetworkInputArray.h>
#include <NetworkInput/NetworkInputHistory.h> #include <Multiplayer/NetworkInput/NetworkInputHistory.h>
#include <NetworkInput/NetworkInputMigrationVector.h> #include <Multiplayer/NetworkInput/NetworkInputMigrationVector.h>
namespace Multiplayer namespace Multiplayer
{ {

@ -45,6 +45,10 @@ set(FILES
Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h Include/Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h
Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h Include/Multiplayer/NetworkInput/IMultiplayerComponentInput.h
Include/Multiplayer/NetworkInput/NetworkInput.h Include/Multiplayer/NetworkInput/NetworkInput.h
Include/Multiplayer/NetworkInput/NetworkInputArray.h
Include/Multiplayer/NetworkInput/NetworkInputChild.h
Include/Multiplayer/NetworkInput/NetworkInputHistory.h
Include/Multiplayer/NetworkInput/NetworkInputMigrationVector.h
Include/Multiplayer/NetworkTime/INetworkTime.h Include/Multiplayer/NetworkTime/INetworkTime.h
Include/Multiplayer/NetworkTime/RewindableArray.h Include/Multiplayer/NetworkTime/RewindableArray.h
Include/Multiplayer/NetworkTime/RewindableArray.inl Include/Multiplayer/NetworkTime/RewindableArray.inl
@ -114,13 +118,9 @@ set(FILES
Source/NetworkEntity/NetworkSpawnableLibrary.h Source/NetworkEntity/NetworkSpawnableLibrary.h
Source/NetworkInput/NetworkInput.cpp Source/NetworkInput/NetworkInput.cpp
Source/NetworkInput/NetworkInputArray.cpp Source/NetworkInput/NetworkInputArray.cpp
Source/NetworkInput/NetworkInputArray.h
Source/NetworkInput/NetworkInputChild.cpp Source/NetworkInput/NetworkInputChild.cpp
Source/NetworkInput/NetworkInputChild.h
Source/NetworkInput/NetworkInputHistory.cpp Source/NetworkInput/NetworkInputHistory.cpp
Source/NetworkInput/NetworkInputHistory.h
Source/NetworkInput/NetworkInputMigrationVector.cpp Source/NetworkInput/NetworkInputMigrationVector.cpp
Source/NetworkInput/NetworkInputMigrationVector.h
Source/NetworkTime/NetworkTime.cpp Source/NetworkTime/NetworkTime.cpp
Source/NetworkTime/NetworkTime.h Source/NetworkTime/NetworkTime.h
Source/Pipeline/NetworkSpawnableHolderComponent.cpp Source/Pipeline/NetworkSpawnableHolderComponent.cpp

Loading…
Cancel
Save