/* * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Multiplayer { using namespace testing; using namespace ::UnitTest; class NetworkEntityTests : public LeakDetectionFixture { public: void SetUp() override { AZ::NameDictionary::Create(); m_mockComponentApplicationRequests = AZStd::make_unique>(); AZ::Interface::Register(m_mockComponentApplicationRequests.get()); ON_CALL(*m_mockComponentApplicationRequests, AddEntity(_)).WillByDefault(Invoke(this, &NetworkEntityTests::AddEntity)); ON_CALL(*m_mockComponentApplicationRequests, FindEntity(_)).WillByDefault(Invoke(this, &NetworkEntityTests::FindEntity)); // register components involved in testing m_serializeContext = AZStd::make_unique(); m_behaviorContext = AZStd::make_unique(); m_transformDescriptor.reset(AzFramework::TransformComponent::CreateDescriptor()); m_transformDescriptor->Reflect(m_serializeContext.get()); m_netBindDescriptor.reset(NetBindComponent::CreateDescriptor()); m_netBindDescriptor->Reflect(m_serializeContext.get()); m_netBindDescriptor->Reflect(m_behaviorContext.get()); m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor()); m_netTransformDescriptor->Reflect(m_serializeContext.get()); m_testMultiplayerComponentDescriptor.reset(MultiplayerTest::TestMultiplayerComponent::CreateDescriptor()); m_testMultiplayerComponentDescriptor->Reflect(m_serializeContext.get()); m_testInputDriverComponentDescriptor.reset(MultiplayerTest::TestInputDriverComponent::CreateDescriptor()); m_testInputDriverComponentDescriptor->Reflect(m_serializeContext.get()); m_mockMultiplayer = AZStd::make_unique>(); AZ::Interface::Register(m_mockMultiplayer.get()); EXPECT_NE(AZ::Interface::Get(), nullptr); // Create space for replication stats // Without Multiplayer::RegisterMultiplayerComponents() the stats go to invalid id, which is fine for unit tests GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0); m_visisbilitySystem = AZStd::make_unique(); m_visisbilitySystem->Connect(); m_networkEntityManager = AZStd::make_unique(); m_mockTime = AZStd::make_unique(); m_eventScheduler = AZStd::make_unique(); m_mockNetworkTime = AZStd::make_unique>(); AZ::Interface::Register(m_mockNetworkTime.get()); ON_CALL(*m_mockMultiplayer, GetNetworkEntityManager()).WillByDefault(Return(m_networkEntityManager.get())); EXPECT_NE(AZ::Interface::Get()->GetNetworkEntityManager(), nullptr); const IpAddress address("localhost", 1, ProtocolType::Udp); m_mockConnection = AZStd::make_unique>(ConnectionId{ 1 }, address, ConnectionRole::Connector); m_mockConnectionListener = AZStd::make_unique(); m_entityReplicationManager = AZStd::make_unique(*m_mockConnection, *m_mockConnectionListener, EntityReplicationManager::Mode::LocalClientToRemoteServer); m_console.reset(aznew AZ::Console()); AZ::Interface::Register(m_console.get()); m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead()); m_multiplayerComponentRegistry = AZStd::make_unique(); RegisterMultiplayerComponents(); MultiplayerTest::RegisterMultiplayerComponents(); } void TearDown() override { m_multiplayerComponentRegistry.reset(); AZ::Interface::Unregister(m_console.get()); m_console.reset(); m_entities.clear(); m_entityReplicationManager.reset(); m_mockConnection.reset(); m_mockConnectionListener.reset(); AZ::Interface::Unregister(m_mockNetworkTime.get()); AZ::Interface::Unregister(m_mockMultiplayer.get()); AZ::Interface::Unregister(m_mockComponentApplicationRequests.get()); m_eventScheduler.reset(); m_mockTime.reset(); m_networkEntityManager.reset(); m_mockMultiplayer.reset(); m_visisbilitySystem->Disconnect(); m_visisbilitySystem.reset(); m_testInputDriverComponentDescriptor.reset(); m_testMultiplayerComponentDescriptor.reset(); m_transformDescriptor.reset(); m_netTransformDescriptor.reset(); m_hierarchyRootDescriptor.reset(); m_hierarchyChildDescriptor.reset(); m_netBindDescriptor.reset(); m_behaviorContext.reset(); m_serializeContext.reset(); m_mockComponentApplicationRequests.reset(); AZ::NameDictionary::Destroy(); } AZStd::unique_ptr m_console; AZStd::unique_ptr> m_mockComponentApplicationRequests; AZStd::unique_ptr m_serializeContext; AZStd::unique_ptr m_behaviorContext; AZStd::unique_ptr m_transformDescriptor; AZStd::unique_ptr m_netBindDescriptor; AZStd::unique_ptr m_hierarchyRootDescriptor; AZStd::unique_ptr m_hierarchyChildDescriptor; AZStd::unique_ptr m_netTransformDescriptor; AZStd::unique_ptr m_testMultiplayerComponentDescriptor; AZStd::unique_ptr m_testInputDriverComponentDescriptor; AZStd::unique_ptr> m_mockMultiplayer; AZStd::unique_ptr m_networkEntityManager; AZStd::unique_ptr m_eventScheduler; AZStd::unique_ptr m_mockTime; AZStd::unique_ptr> m_mockNetworkTime; AZStd::unique_ptr m_visisbilitySystem; AZStd::unique_ptr> m_mockConnection; AZStd::unique_ptr m_mockConnectionListener; AZStd::unique_ptr m_entityReplicationManager; AZStd::unique_ptr m_multiplayerComponentRegistry;; AZStd::map m_entities; bool AddEntity(AZ::Entity* entity) { m_entities[entity->GetId()] = entity; return true; } AZ::Entity* FindEntity(AZ::EntityId entityId) { const auto iterator = m_entities.find(entityId); if (iterator != m_entities.end()) { return iterator->second; } return nullptr; } void SetupEntity(const AZStd::unique_ptr& entity, NetEntityId netId, NetEntityRole role) { if (const auto netBindComponent = entity->FindComponent()) { netBindComponent->PreInit(entity.get(), PrefabEntityId{ AZ::Name("test"), 1 }, netId, role); entity->Init(); } } static void StopEntity(const AZStd::unique_ptr& entity) { if (const auto netBindComponent = entity->FindComponent()) { netBindComponent->StopEntity(); } } static void StopAndDeactivateEntity(AZStd::unique_ptr& entity) { if (entity) { StopEntity(entity); entity->Deactivate(); entity.reset(); } } struct EntityInfo { enum class Role { Root, Child, None }; EntityInfo(AZ::u64 entityId, const char* entityName, NetEntityId netId, Role role) : m_entity(AZStd::make_unique(AZ::EntityId(entityId), entityName)) , m_netId(netId) , m_role(role) { } ~EntityInfo() { StopAndDeactivateEntity(m_entity); } AZStd::unique_ptr m_entity; NetEntityId m_netId; AZStd::unique_ptr m_replicator; Role m_role = Role::None; }; void PopulateHierarchicalEntity(const EntityInfo& entityInfo) { entityInfo.m_entity->CreateComponent(); entityInfo.m_entity->CreateComponent(); entityInfo.m_entity->CreateComponent(); entityInfo.m_entity->CreateComponent(); entityInfo.m_entity->CreateComponent(); switch (entityInfo.m_role) { case EntityInfo::Role::Root: entityInfo.m_entity->CreateComponent(); break; case EntityInfo::Role::Child: entityInfo.m_entity->CreateComponent(); break; case EntityInfo::Role::None: break; } } }; }