Some cleanup around handling of migrations to simplify interfaces and add additional hooks for functionality

Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
kberg-amzn
2021-11-03 19:34:10 -07:00
parent 70a1eb65d8
commit 8a3d055f8b
13 changed files with 194 additions and 116 deletions
@@ -9,6 +9,7 @@
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/NetworkEntity/INetworkEntityManager.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/EBus/IEventScheduler.h>
@@ -17,14 +18,20 @@
namespace Multiplayer
{
AZ_CVAR(AZ::TimeMs, net_EntityMigrationTimeoutMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time to wait for a new authority to attach to an entity before we delete the entity");
AZ_CVAR(AZ::TimeMs, net_DefaultEntityMigrationTimeoutMs, AZ::TimeMs{ 1000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Time to wait for a new authority to attach to an entity before we delete the entity");
NetworkEntityAuthorityTracker::NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager)
: m_networkEntityManager(networkEntityManager)
, m_timeoutTimeMs(net_DefaultEntityMigrationTimeoutMs)
{
;
}
void NetworkEntityAuthorityTracker::SetTimeoutTimeMs(AZ::TimeMs timeoutTimeMs)
{
m_timeoutTimeMs = timeoutTimeMs;
}
bool NetworkEntityAuthorityTracker::AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner)
{
bool ret = false;
@@ -92,7 +99,7 @@ namespace Multiplayer
"Trying to add something twice to the timeout map, this is unexpected"
);
m_timeoutDataMap.insert(entityHandle.GetNetEntityId());
AZ::Interface<AZ::IEventScheduler>::Get()->AddCallback([this, netEntityId = entityHandle.GetNetEntityId(), previousOwner]
AZ::Interface<AZ::IEventScheduler>::Get()->AddCallback([this, netEntityId = entityHandle.GetNetEntityId()]
{
auto timeoutData = m_timeoutDataMap.find(netEntityId);
if (timeoutData != m_timeoutDataMap.end())
@@ -109,19 +116,13 @@ namespace Multiplayer
}
if (networkRole != NetEntityRole::Authority)
{
AZLOG_ERROR
(
"Timed out entity id %llu during migration previous owner %s, removing it",
aznumeric_cast<AZ::u64>(entityHandle.GetNetEntityId()),
previousOwner.GetString().c_str()
);
m_networkEntityManager.MarkForRemoval(entityHandle);
m_networkEntityManager.GetEntityDomain()->HandleLossOfAuthoritativeReplicator(entityHandle);
}
}
}
},
AZ::Name("Entity authority removal functor"),
net_EntityMigrationTimeoutMs
m_timeoutTimeMs
);
}
else
@@ -23,6 +23,7 @@ namespace Multiplayer
public:
NetworkEntityAuthorityTracker(INetworkEntityManager& networkEntityManager);
void SetTimeoutTimeMs(AZ::TimeMs timeoutTimeMs);
bool DoesEntityHaveOwner(ConstNetworkEntityHandle entityHandle) const;
bool AddEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& newOwner);
void RemoveEntityAuthorityManager(ConstNetworkEntityHandle entityHandle, const HostId& previousOwner);
@@ -37,5 +38,7 @@ namespace Multiplayer
TimeoutDataMap m_timeoutDataMap;
EntityAuthorityMap m_entityAuthorityMap;
INetworkEntityManager& m_networkEntityManager;
AZ::TimeMs m_timeoutTimeMs = AZ::TimeMs{ 0 };
};
}
@@ -28,12 +28,10 @@
namespace Multiplayer
{
AZ_CVAR(bool, net_DebugCheckNetworkEntityManager, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables extra debug checks inside the NetworkEntityManager");
AZ_CVAR(AZ::TimeMs, net_EntityDomainUpdateMs, AZ::TimeMs{ 500 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Frequency for updating the entity domain in ms");
NetworkEntityManager::NetworkEntityManager()
: m_networkEntityAuthorityTracker(*this)
, m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event"))
, m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event"))
{
AZ::Interface<INetworkEntityManager>::Register(this);
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
@@ -63,8 +61,6 @@ namespace Multiplayer
}
m_entityDomain = AZStd::move(entityDomain);
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
m_entityDomain->ActivateTracking(m_ownedEntities);
}
bool NetworkEntityManager::IsInitialized() const
@@ -231,6 +227,74 @@ namespace Multiplayer
m_localDeferredRpcMessages.emplace_back(AZStd::move(message));
}
void NetworkEntityManager::HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain)
{
for (NetEntityId exitingId : entitiesNotInDomain)
{
bool safeToExit = true;
NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(exitingId);
// We need special handling for the NetworkHierarchy as well, since related entities need to be migrated together
NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController<NetworkHierarchyRootComponentController>();
NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController<NetworkHierarchyChildComponentController>();
// Find the root entity
AZ::Entity* hierarchyRootEntity = nullptr;
if (hierarchyRootController)
{
hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot();
}
else if (hierarchyChildController)
{
hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot();
}
if (hierarchyRootEntity)
{
NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId());
ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId);
// Check if the root entity is still tracked by this authority
if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController())
{
safeToExit = false;
}
}
// Validate that we aren't already planning to remove this entity
if (safeToExit)
{
for (auto remoteEntityId : m_removeList)
{
if (remoteEntityId == remoteEntityId)
{
safeToExit = false;
}
}
}
if (safeToExit)
{
// Tell all the attached replicators for this entity that it's exited the domain
m_entityExitDomainEvent.Signal(entityHandle);
}
}
}
void NetworkEntityManager::ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle)
{
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
netBindComponent->ConstructControllers();
}
}
void NetworkEntityManager::SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs)
{
m_networkEntityAuthorityTracker.SetTimeoutTimeMs(timeoutTimeMs);
}
void NetworkEntityManager::DebugDraw() const
{
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
@@ -243,7 +307,7 @@ namespace Multiplayer
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 != nullptr) && netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
{
debugDisplay->SetColor(AZ::Colors::Black);
debugDisplay->SetAlpha(0.5f);
@@ -277,77 +341,11 @@ namespace Multiplayer
m_localDeferredRpcMessages.clear();
}
void NetworkEntityManager::UpdateEntityDomain()
{
if (m_entityDomain == nullptr)
{
return;
}
const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain = m_entityDomain->RetrieveEntitiesNotInDomain();
for (NetEntityId exitingId : entitiesNotInDomain)
{
OnEntityExitDomain(exitingId);
}
}
void NetworkEntityManager::OnEntityExitDomain(NetEntityId entityId)
{
bool safeToExit = true;
NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId);
// We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together
NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController<NetworkHierarchyRootComponentController>();
NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController<NetworkHierarchyChildComponentController>();
// Find the root entity
AZ::Entity* hierarchyRootEntity = nullptr;
if (hierarchyRootController)
{
hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot();
}
else if (hierarchyChildController)
{
hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot();
}
if (hierarchyRootEntity)
{
NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId());
ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId);
// Check if the root entity is still tracked by this authority
if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController())
{
safeToExit = false;
}
}
// Validate that we aren't already planning to remove this entity
if (safeToExit)
{
for (auto remoteEntityId : m_removeList)
{
if (remoteEntityId == remoteEntityId)
{
safeToExit = false;
}
}
}
if (safeToExit)
{
m_entityExitDomainEvent.Signal(entityHandle);
}
}
void NetworkEntityManager::Reset()
{
m_multiplayerComponentRegistry.Reset();
m_removeList.clear();
m_entityDomain = nullptr;
m_updateEntityDomainEvent.RemoveFromQueue();
m_ownedEntities.clear();
m_entityExitDomainEvent.DisconnectAllHandlers();
m_onEntityMarkedDirty.DisconnectAllHandlers();
m_onEntityNotifyChanges.DisconnectAllHandlers();
@@ -79,12 +79,13 @@ namespace Multiplayer
void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override;
void HandleEntitiesExitDomain(const NetEntityIdSet& entitiesNotInDomain) override;
void ForceAssumeAuthority(const ConstNetworkEntityHandle& entityHandle) override;
void SetMigrateTimeoutTimeMs(AZ::TimeMs timeoutTimeMs) override;
void DebugDraw() const override;
//! @}
void DispatchLocalDeferredRpcMessages();
void UpdateEntityDomain();
void OnEntityExitDomain(NetEntityId entityId);
//! RootSpawnableNotificationBus
//! @{
@@ -106,9 +107,6 @@ namespace Multiplayer
AZ::ScheduledEvent m_removeEntitiesEvent;
AZStd::vector<NetEntityId> m_removeList;
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
AZ::ScheduledEvent m_updateEntityDomainEvent;
OwnedEntitySet m_ownedEntities;
EntityExitDomainEvent m_entityExitDomainEvent;
AZ::Event<> m_onEntityMarkedDirty;