Converted to use AZ::Events for hiearchy notifications. Added unittests.
Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com>
This commit is contained in:
@@ -12,18 +12,8 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class NetworkHierarchyNotifications
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
//! Called when a hierarchy has been updated (a child added or removed, etc.)
|
||||
virtual void OnNetworkHierarchyUpdated([[maybe_unused]] const AZ::EntityId& rootEntityId) {}
|
||||
|
||||
//! Called when an entity has left a hierarchy
|
||||
virtual void OnNetworkHierarchyLeave() {}
|
||||
};
|
||||
|
||||
typedef AZ::EBus<NetworkHierarchyNotifications> NetworkHierarchyNotificationBus;
|
||||
using NetworkHierarchyChangedEvent = AZ::Event<const AZ::EntityId&>;
|
||||
using NetworkHierarchyLeaveEvent = AZ::Event<>;
|
||||
|
||||
class NetworkHierarchyRequests
|
||||
: public AZ::ComponentBus
|
||||
@@ -40,6 +30,14 @@ namespace Multiplayer
|
||||
|
||||
//! @return true if this entity is the top level root of a hierarchy
|
||||
virtual bool IsHierarchicalRoot() const = 0;
|
||||
|
||||
//! Binds the provided NetworkHierarchyChangedEvent handler to a Network Hierarchy component.
|
||||
//! @param handler the handler to invoke when the entity's network hierarchy has been modified.
|
||||
virtual void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) = 0;
|
||||
|
||||
//! Binds the provided NetworkHierarchyLeaveEvent handler to a Network Hierarchy component.
|
||||
//! @param handler the handler to invoke when the entity left its network hierarchy.
|
||||
virtual void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) = 0;
|
||||
};
|
||||
|
||||
typedef AZ::EBus<NetworkHierarchyRequests> NetworkHierarchyRequestBus;
|
||||
|
||||
@@ -52,6 +52,8 @@ namespace Multiplayer
|
||||
bool IsHierarchicalRoot() const override { return false; }
|
||||
AZ::Entity* GetHierarchicalRoot() const override;
|
||||
AZStd::vector<AZ::Entity*> GetHierarchicalEntities() const override;
|
||||
void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override;
|
||||
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
@@ -63,5 +65,8 @@ namespace Multiplayer
|
||||
|
||||
AZ::Event<NetEntityId>::Handler m_hierarchyRootNetIdChanged;
|
||||
void OnHierarchyRootNetIdChanged(NetEntityId rootNetId);
|
||||
|
||||
NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent;
|
||||
NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace Multiplayer
|
||||
bool IsHierarchicalChild() const override;
|
||||
AZStd::vector<AZ::Entity*> GetHierarchicalEntities() const override;
|
||||
AZ::Entity* GetHierarchicalRoot() const override;
|
||||
void BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler) override;
|
||||
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
|
||||
//! @}
|
||||
|
||||
protected:
|
||||
@@ -63,6 +65,9 @@ namespace Multiplayer
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot);
|
||||
|
||||
private:
|
||||
NetworkHierarchyChangedEvent m_networkHierarchyChangedEvent;
|
||||
NetworkHierarchyLeaveEvent m_networkHierarchyLeaveEvent;
|
||||
|
||||
AZ::Entity* m_higherRootEntity = nullptr;
|
||||
AZStd::vector<AZ::Entity*> m_hierarchicalEntities;
|
||||
|
||||
|
||||
@@ -98,6 +98,16 @@ namespace Multiplayer
|
||||
return {};
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyChangedEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyLeaveEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootComponent(NetworkHierarchyRootComponent* hierarchyRoot)
|
||||
{
|
||||
m_hierarchyRootComponent = hierarchyRoot;
|
||||
@@ -109,12 +119,13 @@ namespace Multiplayer
|
||||
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetEntityId());
|
||||
controller->SetHierarchyRoot(netRootId);
|
||||
|
||||
NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnNetworkHierarchyUpdated, hierarchyRoot->GetEntityId());
|
||||
m_networkHierarchyChangedEvent.Signal(hierarchyRoot->GetEntityId());
|
||||
}
|
||||
else
|
||||
{
|
||||
controller->SetHierarchyRoot(InvalidNetEntityId);
|
||||
NetworkHierarchyNotificationBus::Event(GetEntityId(), &NetworkHierarchyNotificationBus::Events::OnNetworkHierarchyLeave);
|
||||
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,10 +136,12 @@ namespace Multiplayer
|
||||
if (rootHandle.Exists())
|
||||
{
|
||||
m_hierarchyRootComponent = rootHandle.FindComponent<NetworkHierarchyRootComponent>();
|
||||
m_networkHierarchyChangedEvent.Signal(m_hierarchyRootComponent->GetEntityId());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hierarchyRootComponent = nullptr;
|
||||
m_networkHierarchyLeaveEvent.Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,16 @@ namespace Multiplayer
|
||||
return GetEntity();
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyChangedEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_networkHierarchyLeaveEvent);
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
|
||||
{
|
||||
const AZ::EntityId entityBusId = *AZ::TransformNotificationBus::GetCurrentBusId();
|
||||
@@ -168,6 +178,8 @@ namespace Multiplayer
|
||||
|
||||
uint32_t currentEntityCount = aznumeric_cast<uint32_t>(m_hierarchicalEntities.size());
|
||||
RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount);
|
||||
|
||||
m_networkHierarchyChangedEvent.Signal(GetEntityId());
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount)
|
||||
@@ -255,6 +267,8 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_networkHierarchyChangedEvent.Signal(GetEntityId());
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot)
|
||||
|
||||
@@ -97,15 +97,28 @@ namespace Multiplayer
|
||||
|
||||
void NetworkTransformComponent::OnParentIdChangedEvent([[maybe_unused]] NetEntityId newParent)
|
||||
{
|
||||
const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(newParent);
|
||||
if (rootHandle.Exists())
|
||||
if (newParent == InvalidNetEntityId)
|
||||
{
|
||||
const AZ::EntityId parentEntityId = rootHandle.GetEntity()->GetId();
|
||||
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
|
||||
{
|
||||
if (transformComponent->GetParentId() != parentEntityId)
|
||||
if (transformComponent->GetParentId() != AZ::EntityId())
|
||||
{
|
||||
transformComponent->SetParent(parentEntityId);
|
||||
transformComponent->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(newParent);
|
||||
if (rootHandle.Exists())
|
||||
{
|
||||
const AZ::EntityId parentEntityId = rootHandle.GetEntity()->GetId();
|
||||
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
|
||||
{
|
||||
if (transformComponent->GetParentId() != parentEntityId)
|
||||
{
|
||||
transformComponent->SetParent(parentEntityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,6 @@
|
||||
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
|
||||
AZ_CVAR(bool, bg_debugHierarchyActivation, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Helpful messages when debugging network hierarchy behavior");
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
EntityReplicator::EntityReplicator
|
||||
@@ -441,22 +439,22 @@ namespace Multiplayer
|
||||
const AZ::Entity* parentEntity = parentHandle.GetEntity();
|
||||
if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active)
|
||||
{
|
||||
if (bg_debugHierarchyActivation)
|
||||
{
|
||||
AZLOG_DEBUG(
|
||||
"Entity %s asking for activation - granted",
|
||||
entity->GetName().c_str());
|
||||
}
|
||||
AZLOG
|
||||
(
|
||||
NET_HierarchyActivationInfo,
|
||||
"Hierchical entity %s asking for activation - granted",
|
||||
entity->GetName().c_str()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bg_debugHierarchyActivation)
|
||||
{
|
||||
AZLOG_DEBUG(
|
||||
"Entity %s asking for activation - waiting on the parent %u",
|
||||
entity->GetName().c_str(),
|
||||
aznumeric_cast<uint32_t>(parentId));
|
||||
}
|
||||
AZLOG
|
||||
(
|
||||
NET_HierarchyActivationInfo,
|
||||
"Hierchical entity %s asking for activation - waiting on the parent %u",
|
||||
entity->GetName().c_str(),
|
||||
aznumeric_cast<uint32_t>(parentId)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,6 @@ namespace Multiplayer
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
|
||||
entity.FindComponent<NetworkHierarchyChildComponent>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
|
||||
entity.FindComponent<NetworkHierarchyChildComponent>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
@@ -287,6 +286,30 @@ namespace Multiplayer
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Updated_Event_On_Child_Detached_On_Server)
|
||||
{
|
||||
MockNetworkHierarchyCallbackHandler mock;
|
||||
EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_rootEntity->GetId()));
|
||||
|
||||
m_rootEntity->FindComponent<NetworkHierarchyRootComponent>()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler);
|
||||
|
||||
// simulate server detaching a child entity
|
||||
SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId);
|
||||
SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId);
|
||||
}
|
||||
|
||||
TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Leave_Event_On_Child_Detached_On_Server)
|
||||
{
|
||||
MockNetworkHierarchyCallbackHandler mock;
|
||||
EXPECT_CALL(mock, OnNetworkHierarchyLeave);
|
||||
|
||||
m_childEntity->FindComponent<NetworkHierarchyChildComponent>()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler);
|
||||
|
||||
// simulate server detaching a child entity
|
||||
SetParentIdOnNetworkTransform(*m_childEntity, InvalidNetEntityId);
|
||||
SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(*m_childEntity, InvalidNetEntityId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parent -> Child -> ChildOfChild
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,29 @@ namespace Multiplayer
|
||||
using namespace testing;
|
||||
using namespace ::UnitTest;
|
||||
|
||||
class NetworkHierarchyCallbacks
|
||||
{
|
||||
public:
|
||||
virtual void OnNetworkHierarchyLeave() = 0;
|
||||
virtual void OnNetworkHierarchyUpdated(const AZ::EntityId& hierarchyRootId) = 0;
|
||||
};
|
||||
|
||||
class MockNetworkHierarchyCallbackHandler : public NetworkHierarchyCallbacks
|
||||
{
|
||||
public:
|
||||
MockNetworkHierarchyCallbackHandler()
|
||||
: m_leaveHandler([this]() { OnNetworkHierarchyLeave(); })
|
||||
, m_changedHandler([this](const AZ::EntityId& rootId) { OnNetworkHierarchyUpdated(rootId); })
|
||||
{
|
||||
}
|
||||
|
||||
NetworkHierarchyLeaveEvent::Handler m_leaveHandler;
|
||||
NetworkHierarchyChangedEvent::Handler m_changedHandler;
|
||||
|
||||
MOCK_METHOD0(OnNetworkHierarchyLeave, void());
|
||||
MOCK_METHOD1(OnNetworkHierarchyUpdated, void(const AZ::EntityId&));
|
||||
};
|
||||
|
||||
class HierarchyTests
|
||||
: public AllocatorsFixture
|
||||
{
|
||||
@@ -282,8 +305,9 @@ namespace Multiplayer
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
entity.FindComponent<NetworkTransformComponent>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
// now the parent id is in the component
|
||||
entity.FindComponent<NetworkTransformComponent>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
@@ -306,8 +330,9 @@ namespace Multiplayer
|
||||
|
||||
NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
|
||||
|
||||
ReplicationRecord notifyRecord = currentRecord;
|
||||
entity.FindComponent<Component>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
|
||||
// now the parent id is in the component
|
||||
entity.FindComponent<Component>()->NotifyStateDeltaChanges(notifyRecord);
|
||||
}
|
||||
|
||||
struct EntityInfo
|
||||
|
||||
Reference in New Issue
Block a user