123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <CommonHierarchySetup.h>
- #include <MockInterfaces.h>
- #include <AzCore/Component/Entity.h>
- #include <AzCore/Console/Console.h>
- #include <AzCore/Name/Name.h>
- #include <AzCore/UnitTest/TestTypes.h>
- #include <AzCore/UnitTest/UnitTest.h>
- #include <AzTest/AzTest.h>
- #include <Multiplayer/Components/NetBindComponent.h>
- #include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
- #include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
- #include <Multiplayer/NetworkInput/NetworkInputArray.h>
- #include <Source/NetworkEntity/NetworkEntityManager.h>
- namespace Multiplayer
- {
- using namespace testing;
- using namespace ::UnitTest;
- /*
- * Test NetBindComponent creation / destruction without activation. This must work before more complicated tests.
- */
- TEST_F(HierarchyTests, On_Client_NetBindComponent_Init)
- {
- auto multiplayer = AZ::Interface<IMultiplayer>::Get();
- auto networkEntityManager = multiplayer->GetNetworkEntityManager();
- auto netTracker = networkEntityManager->GetNetworkEntityTracker();
- // At the start, there shouldn't be anything in the Network Entity Manager.
- EXPECT_EQ(networkEntityManager->GetEntityCount(), 0);
- AZStd::unique_ptr<AZ::Entity> entity = AZStd::make_unique<AZ::Entity>();
- AZ::Entity* rawEntityPtr = entity.get();
- auto netBindComponent = entity->CreateComponent<NetBindComponent>();
- SetupEntity(entity, NetEntityId{ 1 }, NetEntityRole::Client);
- // After the netBindComponent is initialized, it should appear in the NetworkEntityManager and the NetworkEntityTracker.
- EXPECT_EQ(networkEntityManager->GetEntityCount(), 1);
- EXPECT_EQ(netBindComponent, netTracker->GetNetBindComponent(rawEntityPtr));
- StopEntity(entity);
- entity.reset();
- // After the entity is destroyed, it should no longer appear in the NetworkEntityManager and the NetworkEntityTracker.
- // This should be true whether or not the entity was ever activated.
- EXPECT_EQ(networkEntityManager->GetEntityCount(), 0);
- EXPECT_EQ(netTracker->GetNetBindComponent(rawEntityPtr), nullptr);
- }
- /*
- * Test NetBindComponent activation. This must work before more complicated tests.
- */
- TEST_F(HierarchyTests, On_Client_NetBindComponent_Activate)
- {
- auto multiplayer = AZ::Interface<IMultiplayer>::Get();
- auto networkEntityManager = multiplayer->GetNetworkEntityManager();
- auto netTracker = networkEntityManager->GetNetworkEntityTracker();
- // At the start, there shouldn't be anything in the Network Entity Manager.
- EXPECT_EQ(networkEntityManager->GetEntityCount(), 0);
- AZStd::unique_ptr<AZ::Entity> entity = AZStd::make_unique<AZ::Entity>();
- AZ::Entity* rawEntityPtr = entity.get();
- entity->CreateComponent<NetBindComponent>();
- SetupEntity(entity, NetEntityId{ 1 }, NetEntityRole::Client);
- entity->Activate();
- StopEntity(entity);
- entity->Deactivate();
- entity.reset();
- // After the entity is destroyed, it should no longer appear in the NetworkEntityManager and the NetworkEntityTracker.
- // This should be true whether or not the entity was ever activated.
- EXPECT_EQ(networkEntityManager->GetEntityCount(), 0);
- EXPECT_EQ(netTracker->GetNetBindComponent(rawEntityPtr), nullptr);
- }
- /*
- * Hierarchy test - a child entity on a client delaying activation until its hierarchical parent has been activated
- */
- TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_BeforeParent)
- {
- // Create a child entity that will be tested for activation inside a hierarchy
- AZStd::unique_ptr<AZ::Entity> childEntity = AZStd::make_unique<AZ::Entity>();
- CreateEntityWithChildHierarchy(childEntity);
- SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client);
- // child entity is not activated on purpose here, we are about to test conditional activation check
- // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
- SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 });
- SetHierarchyRootFieldOnNetworkHierarchyChild<NetworkHierarchyChildComponent>(childEntity, NetEntityId{ 1 });
- // Create an entity replicator for the child entity
- const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get());
- EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle);
- entityReplicator.Initialize(childHandle);
- // Entity replicator should not be ready to activate the entity because its parent does not exist
- EXPECT_EQ(entityReplicator.IsReadyToActivate(), false);
- }
- TEST_F(HierarchyTests, On_Client_EntityReplicator_DontActivate_Inner_Root_Before_Top_Root)
- {
- // Create a child entity that will be tested for activation inside a hierarchy
- AZStd::unique_ptr<AZ::Entity> innerRootEntity = AZStd::make_unique<AZ::Entity>();
- CreateEntityWithRootHierarchy(innerRootEntity);
- SetupEntity(innerRootEntity, NetEntityId{ 2 }, NetEntityRole::Client);
- // child entity is not activated on purpose here, we are about to test conditional activation check
- // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
- SetParentIdOnNetworkTransform(innerRootEntity, NetEntityId{ 1 });
- SetHierarchyRootFieldOnNetworkHierarchyChild<NetworkHierarchyRootComponent>(innerRootEntity, NetEntityId{ 1 });
- // Create an entity replicator for the child entity
- const NetworkEntityHandle innerRootHandle(innerRootEntity.get(), m_networkEntityTracker.get());
- EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, innerRootHandle);
- entityReplicator.Initialize(innerRootHandle);
- // Entity replicator should not be ready to activate the entity because its parent does not exist
- EXPECT_EQ(entityReplicator.IsReadyToActivate(), false);
- }
- TEST_F(HierarchyTests, On_Client_Not_In_Hierarchy_EntityReplicator_Ignores_Parent)
- {
- // Create a child entity that will be tested for activation inside a hierarchy
- AZStd::unique_ptr<AZ::Entity> childEntity = AZStd::make_unique<AZ::Entity>();
- CreateEntityWithChildHierarchy(childEntity);
- SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client);
- // child entity is not activated on purpose here, we are about to test conditional activation check
- // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
- SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 });
- SetHierarchyRootFieldOnNetworkHierarchyChild<NetworkHierarchyChildComponent>(childEntity, InvalidNetEntityId);
- // Create an entity replicator for the child entity
- const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get());
- EntityReplicator entityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle);
- entityReplicator.Initialize(childHandle);
- // Entity replicator should not be ready to activate the entity because its parent does not exist
- EXPECT_EQ(entityReplicator.IsReadyToActivate(), true);
- }
- /*
- * Hierarchy test - a child entity on a client allowing activation when its hierarchical parent is active
- */
- TEST_F(HierarchyTests, On_Client_EntityReplicator_Activates_AfterParent)
- {
- AZStd::unique_ptr<AZ::Entity> childEntity = AZStd::make_unique<AZ::Entity>();
- CreateEntityWithChildHierarchy(childEntity);
- SetupEntity(childEntity, NetEntityId{ 2 }, NetEntityRole::Client);
- // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
- SetParentIdOnNetworkTransform(childEntity, NetEntityId{ 1 });
- SetHierarchyRootFieldOnNetworkHierarchyChild<NetworkHierarchyChildComponent>(childEntity, NetEntityId{ 1 });
- // Create an entity replicator for the child entity
- const NetworkEntityHandle childHandle(childEntity.get(), m_networkEntityTracker.get());
- EntityReplicator childEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle);
- childEntityReplicator.Initialize(childHandle);
- // Now let's create a parent entity and activate it
- AZStd::unique_ptr<AZ::Entity> parentEntity = AZStd::make_unique<AZ::Entity>();
- CreateEntityWithRootHierarchy(parentEntity);
- SetupEntity(parentEntity, NetEntityId{ 1 }, NetEntityRole::Client);
- // Create an entity replicator for the parent entity
- const NetworkEntityHandle parentHandle(parentEntity.get(), m_networkEntityTracker.get());
- ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Return(parentHandle));
- EntityReplicator parentEntityReplicator(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, parentHandle);
- parentEntityReplicator.Initialize(parentHandle);
- parentEntity->Activate();
- // The child should be ready to be activated
- EXPECT_EQ(childEntityReplicator.IsReadyToActivate(), true);
- StopEntity(parentEntity);
- parentEntity->Deactivate();
- }
- /*
- * Parent -> Child
- */
- class ClientSimpleHierarchyTests : public HierarchyTests
- {
- public:
- const NetEntityId RootNetEntityId = NetEntityId{ 1 };
- const NetEntityId ChildNetEntityId = NetEntityId{ 2 };
- void SetUp() override
- {
- HierarchyTests::SetUp();
- m_root = AZStd::make_unique<EntityInfo>(1, "root", RootNetEntityId, EntityInfo::Role::Root);
- m_child = AZStd::make_unique<EntityInfo>(2, "child", ChildNetEntityId, EntityInfo::Role::Child);
- CreateSimpleHierarchy(*m_root, *m_child);
- m_child->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_root->m_entity->GetId());
- // now the two entities are under one hierarchy
- }
- void TearDown() override
- {
- m_child.reset();
- m_root.reset();
- HierarchyTests::TearDown();
- }
- void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child)
- {
- PopulateHierarchicalEntity(root);
- SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Autonomous);
- PopulateHierarchicalEntity(child);
- SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Autonomous);
- // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
- SetParentIdOnNetworkTransform(child.m_entity, root.m_netId);
- SetHierarchyRootFieldOnNetworkHierarchyChild<NetworkHierarchyChildComponent>(child.m_entity, root.m_netId);
- // Create an entity replicator for the child entity
- const NetworkEntityHandle childHandle(child.m_entity.get(), m_networkEntityTracker.get());
- child.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childHandle);
- child.m_replicator->Initialize(childHandle);
- // Create an entity replicator for the root entity
- const NetworkEntityHandle rootHandle(root.m_entity.get(), m_networkEntityTracker.get());
- root.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, rootHandle);
- root.m_replicator->Initialize(rootHandle);
- root.m_entity->Activate();
- child.m_entity->Activate();
- }
- void SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(const AZStd::unique_ptr<AZ::Entity>& entity, NetEntityId value)
- {
- /* Derived from NetworkHierarchyChildComponent.AutoComponent.xml */
- constexpr int totalBits = 1 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::Count*/;
- constexpr int inHierarchyBit = 0 /*NetworkHierarchyChildComponentInternal::AuthorityToClientDirtyEnum::hierarchyRoot_DirtyFlag*/;
- ReplicationRecord currentRecord(NetEntityRole::Client);
- currentRecord.m_authorityToClient.AddBits(totalBits);
- currentRecord.m_authorityToClient.SetBit(inHierarchyBit, true);
- constexpr uint32_t bufferSize = 100;
- AZStd::array<uint8_t, bufferSize> buffer = {};
- NetworkInputSerializer inSerializer(buffer.begin(), bufferSize);
- ISerializer& serializer = inSerializer;
- serializer.Serialize(value, "hierarchyRoot"); // Derived from NetworkHierarchyChildComponent.AutoComponent.xml
- NetworkOutputSerializer outSerializer(buffer.begin(), bufferSize);
- ReplicationRecord notifyRecord = currentRecord;
- entity->FindComponent<NetworkHierarchyChildComponent>()->SerializeStateDeltaMessage(currentRecord, outSerializer);
- entity->FindComponent<NetworkHierarchyChildComponent>()->NotifyStateDeltaChanges(notifyRecord);
- }
- AZStd::unique_ptr<EntityInfo> m_root;
- AZStd::unique_ptr<EntityInfo> m_child;
- };
- TEST_F(ClientSimpleHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields)
- {
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchyRoot(),
- InvalidNetEntityId
- );
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
- RootNetEntityId
- );
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchicalRoot(),
- m_root->m_entity.get()
- );
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities().size(),
- 2
- );
- if (m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities().size() == 2)
- {
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[0],
- m_root->m_entity.get()
- );
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[1],
- m_child->m_entity.get()
- );
- }
- }
- TEST_F(ClientSimpleHierarchyTests, Client_Detaches_Child_When_Server_Detaches)
- {
- // simulate server detaching child entity
- SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId);
- SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
- InvalidNetEntityId
- );
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchicalRoot(),
- nullptr
- );
- }
- TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Updated_Event_On_Child_Detached_On_Server)
- {
- MockNetworkHierarchyCallbackHandler mock;
- EXPECT_CALL(mock, OnNetworkHierarchyUpdated(m_root->m_entity->GetId()));
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->BindNetworkHierarchyChangedEventHandler(mock.m_changedHandler);
- // simulate server detaching a child entity
- SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId);
- SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
- }
- TEST_F(ClientSimpleHierarchyTests, Client_Sends_NetworkHierarchy_Leave_Event_On_Child_Detached_On_Server)
- {
- MockNetworkHierarchyCallbackHandler mock;
- EXPECT_CALL(mock, OnNetworkHierarchyLeave);
- m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->BindNetworkHierarchyLeaveEventHandler(mock.m_leaveHandler);
- // simulate server detaching a child entity
- SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId);
- SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
- }
- TEST_F(ClientSimpleHierarchyTests, ChildHasOwningConnectionIdOfParent)
- {
- // disconnect and assign new connection ids
- SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId);
- SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
- m_root->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 1 });
- m_child->m_entity->FindComponent<NetBindComponent>()->SetOwningConnectionId(ConnectionId{ 2 });
- const ConnectionId previousConnectionId = m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId();
- // re-attach, child's owning connection id should then be root's connection id
- SetParentIdOnNetworkTransform(m_child->m_entity, RootNetEntityId);
- SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, RootNetEntityId);
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
- m_root->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId()
- );
- // detach, the child should roll back to his previous owning connection id
- SetParentIdOnNetworkTransform(m_child->m_entity, InvalidNetEntityId);
- SetHierarchyRootFieldOnNetworkHierarchyChildOnClient(m_child->m_entity, InvalidNetEntityId);
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetBindComponent>()->GetOwningConnectionId(),
- previousConnectionId
- );
- }
- /*
- * Parent -> Child -> ChildOfChild
- */
- class ClientDeepHierarchyTests : public ClientSimpleHierarchyTests
- {
- public:
- const NetEntityId ChildOfChildNetEntityId = NetEntityId{ 3 };
- void SetUp() override
- {
- ClientSimpleHierarchyTests::SetUp();
- m_childOfChild = AZStd::make_unique<EntityInfo>((3), "child of child", ChildOfChildNetEntityId, EntityInfo::Role::Child);
- CreateDeepHierarchyOnClient(*m_childOfChild);
- m_childOfChild->m_entity->FindComponent<AzFramework::TransformComponent>()->SetParent(m_child->m_entity->GetId());
- }
- void TearDown() override
- {
- m_childOfChild.reset();
- ClientSimpleHierarchyTests::TearDown();
- }
- void CreateDeepHierarchyOnClient(EntityInfo& childOfChild)
- {
- PopulateHierarchicalEntity(childOfChild);
- SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Autonomous);
- // we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
- SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childOfChild->m_netId);
- SetHierarchyRootFieldOnNetworkHierarchyChild<NetworkHierarchyChildComponent>(childOfChild.m_entity, m_root->m_netId);
- // Create an entity replicator for the child entity
- const NetworkEntityHandle childOfChildHandle(childOfChild.m_entity.get(), m_networkEntityTracker.get());
- childOfChild.m_replicator = AZStd::make_unique<EntityReplicator>(*m_entityReplicationManager, m_mockConnection.get(), NetEntityRole::Authority, childOfChildHandle);
- childOfChild.m_replicator->Initialize(childOfChildHandle);
- childOfChild.m_entity->Activate();
- }
- AZStd::unique_ptr<EntityInfo> m_childOfChild;
- };
- TEST_F(ClientDeepHierarchyTests, Client_Activates_Hierarchy_From_Network_Fields)
- {
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchyRoot(),
- InvalidNetEntityId
- );
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
- RootNetEntityId
- );
- EXPECT_EQ(
- m_childOfChild->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchyRoot(),
- RootNetEntityId
- );
- EXPECT_EQ(
- m_child->m_entity->FindComponent<NetworkHierarchyChildComponent>()->GetHierarchicalRoot(),
- m_root->m_entity.get()
- );
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities().size(),
- 3
- );
- if (m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities().size() == 3)
- {
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[0],
- m_root->m_entity.get()
- );
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[1],
- m_child->m_entity.get()
- );
- EXPECT_EQ(
- m_root->m_entity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities()[2],
- m_childOfChild->m_entity.get()
- );
- }
- }
- TEST_F(ClientDeepHierarchyTests, CreateProcessInputTest)
- {
- using MultiplayerTest::TestMultiplayerComponent;
- using MultiplayerTest::TestMultiplayerComponentController;
- using MultiplayerTest::TestMultiplayerComponentNetworkInput;
- auto* rootNetBind = m_root->m_entity->FindComponent<NetBindComponent>();
- NetworkInputArray inputArray(rootNetBind->GetEntityHandle());
- NetworkInput& input = inputArray[0];
- const float deltaTime = 0.16f;
- rootNetBind->CreateInput(input, deltaTime);
- auto ValidateCreatedInput = [](const NetworkInput& input, const HierarchyTests::EntityInfo& entityInfo)
- {
- // Validate test input for the root entity's TestMultiplayerComponent
- auto* testInput = input.FindComponentInput<TestMultiplayerComponentNetworkInput>();
- EXPECT_NE(testInput, nullptr);
- auto* testMultiplayerComponent = entityInfo.m_entity->FindComponent<TestMultiplayerComponent>();
- EXPECT_NE(testMultiplayerComponent, nullptr);
- EXPECT_EQ(testInput->m_ownerId, testMultiplayerComponent->GetId());
- };
- // Validate root input
- ValidateCreatedInput(input, *m_root);
- // Validate children input
- {
- NetworkHierarchyRootComponentNetworkInput* rootHierarchyInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>();
- const AZStd::vector<NetworkInputChild>& childInputs = rootHierarchyInput->m_childInputs;
- EXPECT_EQ(childInputs.size(), 2);
- ValidateCreatedInput(childInputs[0].GetNetworkInput(), *m_child);
- ValidateCreatedInput(childInputs[1].GetNetworkInput(), *m_childOfChild);
- }
- // Test ProcessInput
- {
- NetEntityIdSet inputProcessedEntities;
- size_t processInputCallCounter = 0;
- auto processInputCallback = [&inputProcessedEntities, &processInputCallCounter](
- NetEntityId netEntityId, [[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
- {
- inputProcessedEntities.insert(netEntityId);
- processInputCallCounter++;
- };
- // Set the callbacks for processing input. This allows us to inspect how many times the input was processed
- // and which entity's controller was invoked.
- m_root->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
- m_child->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
- m_childOfChild->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
- rootNetBind->ProcessInput(input, deltaTime);
- EXPECT_EQ(processInputCallCounter, 3);
- EXPECT_EQ(inputProcessedEntities,
- NetEntityIdSet({ m_root->m_netId, m_child->m_netId, m_childOfChild->m_netId }));
- }
- }
- }
|