From ef3552df69b27ef0c8bb04934fb5187f2f5f6999 Mon Sep 17 00:00:00 2001 From: pereslav Date: Fri, 1 Oct 2021 00:07:23 +0100 Subject: [PATCH 01/13] First pass for network hierarchy input processing Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.h | 22 ++++ .../Multiplayer/NetworkInput/NetworkInput.h | 25 +++++ ...rkHierarchyRootComponent.AutoComponent.xml | 4 +- .../NetworkHierarchyRootComponent.cpp | 100 ++++++++++++++++++ .../Code/Source/NetworkInput/NetworkInput.cpp | 49 +++++++++ 5 files changed, 199 insertions(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 4c9f94c004..8e488c1029 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -57,6 +57,8 @@ namespace Multiplayer void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} + const AZStd::vector& GetHierarchicalEntitiesRef() const; + protected: void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); @@ -97,4 +99,24 @@ namespace Multiplayer //! Set to false when deactivating or otherwise not to be included in hierarchy considerations. bool m_isHierarchyEnabled = true; }; + + + //! NetworkCharacterComponentController + //! This is the network controller for NetworkHierarchyRootComponent. + //! Class provides the ability to process input for hierarchies. + class NetworkHierarchyRootComponentController + : public NetworkHierarchyRootComponentControllerBase + { + public: + NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent); + + // NetworkHierarchyRootComponentControllerBase + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + //! MultiplayerController interface + Multiplayer::MultiplayerController::InputPriorityOrder GetInputOrder() const override; + void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override; + void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override; + }; } diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h index 1e02d6bf56..4575d88e24 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h @@ -29,6 +29,7 @@ namespace Multiplayer friend class NetworkInputMigrationVector; friend class NetworkInputHistory; friend class NetworkInputChild; + friend class NetworkSubInput; NetworkInput(const NetworkInput&); NetworkInput& operator= (const NetworkInput&); @@ -80,4 +81,28 @@ namespace Multiplayer ConstNetworkEntityHandle m_owner; bool m_wasAttached = false; }; + + // Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify dependent entities + // Since it is possible to for the Client/Server to disagree about the state of related entities, this input encodes the entity that + // is associated with it. + class NetworkSubInput final + { + public: + NetworkSubInput() = default; + NetworkSubInput(const NetworkSubInput&) = default; + NetworkSubInput(const ConstNetworkEntityHandle& entityHandle); + + NetworkSubInput& operator=(const NetworkSubInput&) = default; + + void Attach(const ConstNetworkEntityHandle& entityHandle); + const ConstNetworkEntityHandle& GetOwner() const; + NetworkInput& GetNetworkInput(); + const NetworkInput& GetNetworkInput() const; + + bool Serialize(AzNetworking::ISerializer& serializer); + private: + ConstNetworkEntityHandle m_owner; + NetworkInput m_networkInput; + }; + } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml index 0f33e1f642..c93f300da3 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -4,11 +4,13 @@ Name="NetworkHierarchyRootComponent" Namespace="Multiplayer" OverrideComponent="true" - OverrideController="false" + OverrideController="true" OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 4a5e9ce8d2..ca4bbec66b 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -139,6 +139,11 @@ namespace Multiplayer return m_hierarchicalEntities; } + const AZStd::vector& NetworkHierarchyRootComponent::GetHierarchicalEntitiesRef() const + { + return m_hierarchicalEntities; + } + AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const { if (m_rootEntity) @@ -326,4 +331,99 @@ namespace Multiplayer RebuildHierarchy(); } } + + NetworkHierarchyRootComponentController::NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent) + : NetworkHierarchyRootComponentControllerBase(parent) + { + + } + + void NetworkHierarchyRootComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + + } + + void NetworkHierarchyRootComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + + } + + Multiplayer::MultiplayerController::InputPriorityOrder NetworkHierarchyRootComponentController::GetInputOrder() const + { + return Multiplayer::MultiplayerController::InputPriorityOrder::SubEntities; + } + + void NetworkHierarchyRootComponentController::CreateInput(Multiplayer::NetworkInput& input, float deltaTime) + { + NetworkHierarchyRootComponent& component = GetParent(); + if(!component.IsHierarchicalRoot()) + { + return; + } + + const AZStd::vector& entities = component.GetHierarchicalEntitiesRef(); + + auto* networkInput = input.FindComponentInput(); + networkInput->m_childInputs.clear(); + networkInput->m_childInputs.reserve(entities.size()); + + for (AZ::Entity* child : entities) + { + if(child == component.GetEntity()) + { + return; // Avoid infinite recursion + } + + auto* netComp = child->FindComponent(); + AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + // Validate we still have a controller and we aren't in the middle of removing them + if (netComp->HasController()) + { + ConstNetworkEntityHandle childEntityHandle = netComp->GetEntityHandle(); + NetworkSubInput subInput; + subInput.Attach(childEntityHandle); + subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); + + netComp->CreateInput(subInput.GetNetworkInput(), deltaTime); + + // make sure our input sub commands have the same time as the original + subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); + networkInput->m_childInputs.emplace_back(subInput); + } + } + } + + void NetworkHierarchyRootComponentController::ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) + { + // Prevent replaying process input commands for child entities that weren't part of the hierarchy at that time + //if (!m_firstProcessInputOccurred) + //{ + // m_firstProcessInputOccurred = true; + // m_firstProcessInputTime = input.GetInputId().GetGameTimePoint(); + //} + //else if (m_firstProcessInputTime > input.GetInputId().GetGameTimePoint()) + //{ + // return; + //} + + if (auto* networkInput = input.FindComponentInput()) + { + for (NetworkSubInput& subInput : networkInput->m_childInputs) + { + const ConstNetworkEntityHandle& childEntity = subInput.GetOwner(); + if (auto* localChild = childEntity.GetEntity()) + { + auto* netComp = localChild->FindComponent(); + AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + // We do not rewind entity role changes, so make sure we are the correct role prior to processing + if (netComp->HasController()) + { + subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); + netComp->ProcessInput(subInput.GetNetworkInput(), deltaTime); + } + } + } + } + } + } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp index 35ca02bfba..2bbda48551 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp @@ -188,4 +188,53 @@ namespace Multiplayer } m_wasAttached = rhs.m_wasAttached; } + + NetworkSubInput::NetworkSubInput(const ConstNetworkEntityHandle& entityHandle) + : m_owner(entityHandle) + { + Attach(m_owner); + } + + void NetworkSubInput::Attach(const ConstNetworkEntityHandle& entityHandle) + { + m_owner = entityHandle; + if (auto* localEnt = entityHandle.GetEntity()) + { + NetBindComponent* netBindComponent = localEnt->FindComponent(); + if (netBindComponent) + { + m_networkInput.AttachNetBindComponent(netBindComponent); + } + } + } + + const ConstNetworkEntityHandle& NetworkSubInput::GetOwner() const + { + return m_owner; + } + + NetworkInput& NetworkSubInput::GetNetworkInput() + { + return m_networkInput; + } + + const NetworkInput& NetworkSubInput::GetNetworkInput() const + { + return m_networkInput; + } + + bool NetworkSubInput::Serialize(AzNetworking::ISerializer& serializer) + { + NetEntityId tmpId = m_owner.GetNetEntityId(); + serializer.Serialize(tmpId, "OwnerId"); + + if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) + { + m_owner = AZ::Interface::Get()->GetEntity(tmpId); + } + + serializer.Serialize(m_networkInput, "NetInput"); + return serializer.IsValid(); + } + } From a58325e691209cbe2596fd11c4919c2f4c41e0d1 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 7 Oct 2021 00:38:07 +0100 Subject: [PATCH 02/13] Enabled multi-entities player prefabs. Added updating replication set for hierarchy children. PR feedback addressing Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.h | 6 +-- .../Multiplayer/NetworkInput/NetworkInput.h | 25 ---------- ...rkHierarchyRootComponent.AutoComponent.xml | 6 ++- .../NetworkHierarchyRootComponent.cpp | 30 ++++++------ .../Source/MultiplayerSystemComponent.cpp | 38 ++++++++------ .../Code/Source/MultiplayerSystemComponent.h | 2 +- .../Code/Source/NetworkInput/NetworkInput.cpp | 49 ------------------- .../Source/NetworkInput/NetworkInputChild.h | 5 +- .../ServerToClientReplicationWindow.cpp | 39 ++++++++------- .../ServerToClientReplicationWindow.h | 3 +- 10 files changed, 69 insertions(+), 134 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index 414f488f88..a31f47317a 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -29,6 +29,8 @@ namespace Multiplayer , public NetworkHierarchyRequestBus::Handler { friend class NetworkHierarchyChildComponent; + friend class NetworkHierarchyRootComponentController; + friend class ServerToClientReplicationWindow; public: AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase); @@ -57,8 +59,6 @@ namespace Multiplayer void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} - const AZStd::vector& GetHierarchicalEntitiesRef() const; - protected: void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); @@ -103,7 +103,7 @@ namespace Multiplayer }; - //! NetworkCharacterComponentController + //! NetworkHierarchyRootComponentController //! This is the network controller for NetworkHierarchyRootComponent. //! Class provides the ability to process input for hierarchies. class NetworkHierarchyRootComponentController diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h index 4575d88e24..1e02d6bf56 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkInput/NetworkInput.h @@ -29,7 +29,6 @@ namespace Multiplayer friend class NetworkInputMigrationVector; friend class NetworkInputHistory; friend class NetworkInputChild; - friend class NetworkSubInput; NetworkInput(const NetworkInput&); NetworkInput& operator= (const NetworkInput&); @@ -81,28 +80,4 @@ namespace Multiplayer ConstNetworkEntityHandle m_owner; bool m_wasAttached = false; }; - - // Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify dependent entities - // Since it is possible to for the Client/Server to disagree about the state of related entities, this input encodes the entity that - // is associated with it. - class NetworkSubInput final - { - public: - NetworkSubInput() = default; - NetworkSubInput(const NetworkSubInput&) = default; - NetworkSubInput(const ConstNetworkEntityHandle& entityHandle); - - NetworkSubInput& operator=(const NetworkSubInput&) = default; - - void Attach(const ConstNetworkEntityHandle& entityHandle); - const ConstNetworkEntityHandle& GetOwner() const; - NetworkInput& GetNetworkInput(); - const NetworkInput& GetNetworkInput() const; - - bool Serialize(AzNetworking::ISerializer& serializer); - private: - ConstNetworkEntityHandle m_owner; - NetworkInput m_networkInput; - }; - } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml index c93f300da3..f8df21a220 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -8,9 +8,11 @@ OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + + - - + + diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index ca4bbec66b..89db2180a5 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -139,11 +139,6 @@ namespace Multiplayer return m_hierarchicalEntities; } - const AZStd::vector& NetworkHierarchyRootComponent::GetHierarchicalEntitiesRef() const - { - return m_hierarchicalEntities; - } - AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const { if (m_rootEntity) @@ -361,7 +356,10 @@ namespace Multiplayer return; } - const AZStd::vector& entities = component.GetHierarchicalEntitiesRef(); + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + + const AZStd::vector& entities = component.m_hierarchicalEntities; auto* networkInput = input.FindComponentInput(); networkInput->m_childInputs.clear(); @@ -371,21 +369,23 @@ namespace Multiplayer { if(child == component.GetEntity()) { - return; // Avoid infinite recursion + continue; // Avoid infinite recursion } - auto* netComp = child->FindComponent(); - AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); + NetBindComponent* netComp = childEntityHandle.GetNetBindComponent(); + + AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // Validate we still have a controller and we aren't in the middle of removing them if (netComp->HasController()) { - ConstNetworkEntityHandle childEntityHandle = netComp->GetEntityHandle(); - NetworkSubInput subInput; + NetworkInputChild subInput; subInput.Attach(childEntityHandle); subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); netComp->CreateInput(subInput.GetNetworkInput(), deltaTime); - + // make sure our input sub commands have the same time as the original subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId()); networkInput->m_childInputs.emplace_back(subInput); @@ -408,13 +408,13 @@ namespace Multiplayer if (auto* networkInput = input.FindComponentInput()) { - for (NetworkSubInput& subInput : networkInput->m_childInputs) + for (NetworkInputChild& subInput : networkInput->m_childInputs) { const ConstNetworkEntityHandle& childEntity = subInput.GetOwner(); if (auto* localChild = childEntity.GetEntity()) { - auto* netComp = localChild->FindComponent(); - AZ_Assert(netComp, "No NetSystemComponent, this should be impossible"); + auto* netComp = childEntity.GetNetBindComponent(); + AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // We do not rewind entity role changes, so make sure we are the correct role prior to processing if (netComp->HasController()) { diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 37e87ace84..bda824e953 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -665,12 +665,21 @@ namespace Multiplayer if (GetAgentType() == MultiplayerAgentType::ClientServer || GetAgentType() == MultiplayerAgentType::DedicatedServer) { - NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); - if (controlledEntity.Exists()) + INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab(); + for (auto& netEntity : entityList) { - controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); + if (netEntity.Exists()) + { + netEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); + } + netEntity.Activate(); + } + + NetworkEntityHandle controlledEntity; + if (entityList.size() > 0) + { + controlledEntity = entityList[0]; } - controlledEntity.Activate(); connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); @@ -780,12 +789,16 @@ namespace Multiplayer // Spawn the default player for this host since the host is also a player (not a dedicated server) if (m_agentType == MultiplayerAgentType::ClientServer) { - NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); - if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent()) + INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab(); + + for (NetworkEntityHandle controlledEntity : entityList) { - controlledEntityNetBindComponent->SetAllowAutonomy(true); + if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent()) + { + controlledEntityNetBindComponent->SetAllowAutonomy(true); + } + controlledEntity.Activate(); } - controlledEntity.Activate(); } AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType)); @@ -1028,17 +1041,12 @@ namespace Multiplayer } } - NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() + INetworkEntityManager::EntityList MultiplayerSystemComponent::SpawnDefaultPlayerPrefab() { PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str())); INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate); - NetworkEntityHandle controlledEntity; - if (entityList.size() > 0) - { - controlledEntity = entityList[0]; - } - return controlledEntity; + return entityList; } void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 316ddd41d5..495642b043 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -139,7 +139,7 @@ namespace Multiplayer void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds); void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom); void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector& commands); - NetworkEntityHandle SpawnDefaultPlayerPrefab(); + INetworkEntityManager::EntityList SpawnDefaultPlayerPrefab(); AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session"); diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp index 2bbda48551..35ca02bfba 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInput.cpp @@ -188,53 +188,4 @@ namespace Multiplayer } m_wasAttached = rhs.m_wasAttached; } - - NetworkSubInput::NetworkSubInput(const ConstNetworkEntityHandle& entityHandle) - : m_owner(entityHandle) - { - Attach(m_owner); - } - - void NetworkSubInput::Attach(const ConstNetworkEntityHandle& entityHandle) - { - m_owner = entityHandle; - if (auto* localEnt = entityHandle.GetEntity()) - { - NetBindComponent* netBindComponent = localEnt->FindComponent(); - if (netBindComponent) - { - m_networkInput.AttachNetBindComponent(netBindComponent); - } - } - } - - const ConstNetworkEntityHandle& NetworkSubInput::GetOwner() const - { - return m_owner; - } - - NetworkInput& NetworkSubInput::GetNetworkInput() - { - return m_networkInput; - } - - const NetworkInput& NetworkSubInput::GetNetworkInput() const - { - return m_networkInput; - } - - bool NetworkSubInput::Serialize(AzNetworking::ISerializer& serializer) - { - NetEntityId tmpId = m_owner.GetNetEntityId(); - serializer.Serialize(tmpId, "OwnerId"); - - if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject) - { - m_owner = AZ::Interface::Get()->GetEntity(tmpId); - } - - serializer.Serialize(m_networkInput, "NetInput"); - return serializer.IsValid(); - } - } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h index 98778cce17..471d86f718 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h @@ -12,10 +12,7 @@ namespace Multiplayer { - //! Max number of entities that can be children of our netbound player entity. - static constexpr uint32_t MaxEntityHierarchyChildren = 16; - - //! Used by the EntityHierarchyComponent. This component allows the gameplay programmer to specify inputs for dependent entities. + //! Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify inputs for dependent entities. //! Since it is possible to for the Client/Server to disagree about the state of related entities, //! this network input encodes the entity that is associated with it. class NetworkInputChild diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp index 9d9cc74d2b..d8195bf29a 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -174,11 +175,11 @@ namespace Multiplayer // Note: Do not add any Client entities after this point, otherwise you stomp over the Autonomous mode m_replicationSet[m_controlledEntity] = { NetEntityRole::Autonomous, 1.0f }; // Always replicate autonomous entities - //auto hierarchyController = FindController(m_ControlledEntity); - //if (hierarchyController != nullptr) - //{ - // CollectControlledEntitiesRecursive(m_replicationSet, *hierarchyController); - //} + auto* hierarchyComponent = m_controlledEntity.FindComponent(); + if (hierarchyComponent != nullptr) + { + UpdateHierarchyReplicationSet(m_replicationSet, *hierarchyComponent); + } } AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector) @@ -326,18 +327,18 @@ namespace Multiplayer } } - //void ServerToClientReplicationWindow::CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController) - //{ - // auto controlledEnts = hierarchyController.GetChildrenRelatedEntities(); - // for (auto& controlledEnt : controlledEnts) - // { - // AZ_Assert(controlledEnt != nullptr, "We have lost a controlled entity unexpectedly"); - // replicationSet[controlledEnt.GetConstEntity()] = EntityReplicationData(EntityNetworkRoleT::e_Autonomous, EntityPrioritySystem::k_MaxPriority); // Always replicate controlled entities - // auto hierarchyController = controlledEnt.FindController(); - // if (hierarchyController != nullptr) - // { - // CollectControlledEntitiesRecursive(replicationSet, *hierarchyController); - // } - // } - //} + void ServerToClientReplicationWindow::UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent) + { + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + + for (const AZ::Entity* controlledEntity : hierarchyComponent.m_hierarchicalEntities) + { + NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId()); + ConstNetworkEntityHandle controlledEntityHandle = networkEntityManager->GetEntity(controlledNetEntitydId); + AZ_Assert(controlledEntityHandle != nullptr, "We have lost a controlled entity unexpectedly"); + + replicationSet[controlledEntityHandle] = { NetEntityRole::Autonomous, 1.0f }; + } + } } diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h index b034bde90c..8816209d7b 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.h @@ -20,6 +20,7 @@ namespace Multiplayer { class NetSystemComponent; + class NetworkHierarchyRootComponent; class ServerToClientReplicationWindow : public IReplicationWindow @@ -56,7 +57,7 @@ namespace Multiplayer void OnEntityActivated(AZ::Entity* entity); void OnEntityDeactivated(AZ::Entity* entity); - //void CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController); + void UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent); void EvaluateConnection(); void AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, float distanceSquared); From 28a40fa4dfa0a9d51596042ef307b6f1e2addef7 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 7 Oct 2021 16:24:21 +0100 Subject: [PATCH 03/13] Added look up of the child entities when processing input to make sure the entity still exists Signed-off-by: pereslav --- .../Components/NetworkHierarchyRootComponent.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 89db2180a5..d62b103702 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -408,12 +408,16 @@ namespace Multiplayer if (auto* networkInput = input.FindComponentInput()) { + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + for (NetworkInputChild& subInput : networkInput->m_childInputs) { - const ConstNetworkEntityHandle& childEntity = subInput.GetOwner(); - if (auto* localChild = childEntity.GetEntity()) + const ConstNetworkEntityHandle& childEntityFromInput = subInput.GetOwner(); + ConstNetworkEntityHandle localChildHandle = networkEntityManager->GetEntity(childEntityFromInput.GetNetEntityId()); + if (localChildHandle.Exists()) { - auto* netComp = childEntity.GetNetBindComponent(); + auto* netComp = localChildHandle.GetNetBindComponent(); AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // We do not rewind entity role changes, so make sure we are the correct role prior to processing if (netComp->HasController()) From cb53389004b6510f696174ef206049ec17121701 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 7 Oct 2021 16:58:37 +0100 Subject: [PATCH 04/13] Added validation into input processing Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.cpp | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index d62b103702..19958bace1 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -411,13 +411,35 @@ namespace Multiplayer INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + // Build a set of Net IDs for the children + AZStd::unordered_set currentChildren; // TODO: Cache inside the component if this becomes a performance issue. + NetworkHierarchyRootComponent& component = GetParent(); + for (AZ::Entity* child : component.m_hierarchicalEntities) + { + if (child == component.GetEntity()) // Skip the root entity + { + continue; + } + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); // TODO: Cache net IDs in the component if this becomes a performance issue + currentChildren.insert(childNetEntitydId); + } + + // Process the input for the child entities for (NetworkInputChild& subInput : networkInput->m_childInputs) { - const ConstNetworkEntityHandle& childEntityFromInput = subInput.GetOwner(); - ConstNetworkEntityHandle localChildHandle = networkEntityManager->GetEntity(childEntityFromInput.GetNetEntityId()); - if (localChildHandle.Exists()) + const ConstNetworkEntityHandle& inputOwnerHandle = subInput.GetOwner(); + NetEntityId inputOwnerNetEntitydId = inputOwnerHandle.GetNetEntityId(); + + if (currentChildren.count(inputOwnerNetEntitydId) == 0) { - auto* netComp = localChildHandle.GetNetBindComponent(); + // Skip the input for entities which are not a part of this hierarchy + continue; + } + + ConstNetworkEntityHandle localEntityHandle = networkEntityManager->GetEntity(inputOwnerNetEntitydId); + if (localEntityHandle.Exists()) + { + auto* netComp = localEntityHandle.GetNetBindComponent(); AZ_Assert(netComp, "No NetBindComponent, this should be impossible"); // We do not rewind entity role changes, so make sure we are the correct role prior to processing if (netComp->HasController()) From 0c2c042fde8835d0dcaa198286d1a7cdda1c0756 Mon Sep 17 00:00:00 2001 From: pereslav Date: Fri, 8 Oct 2021 21:19:22 +0100 Subject: [PATCH 05/13] Fixed crash in ConstEntityHandle. Added hierarchical correction data serialization Signed-off-by: pereslav --- .../LocalPredictionPlayerInputComponent.h | 2 ++ .../NetworkHierarchyRootComponent.h | 2 ++ ...tionPlayerInputComponent.AutoComponent.xml | 1 + .../LocalPredictionPlayerInputComponent.cpp | 25 ++++++++++++++----- .../NetworkHierarchyRootComponent.cpp | 25 +++++++++++++++++++ .../NetworkEntity/NetworkEntityHandle.cpp | 4 +-- 6 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h index bcbea6542f..25fe473135 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h @@ -71,6 +71,8 @@ namespace Multiplayer void UpdateAutonomous(AZ::TimeMs deltaTimeMs); void UpdateBankedTime(AZ::TimeMs deltaTimeMs); + bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer); + using StateHistoryItem = AZStd::unique_ptr; AZStd::map m_predictiveStateHistory; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index a31f47317a..cafd63b223 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -59,6 +59,8 @@ namespace Multiplayer void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override; //! @} + bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer); + protected: void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot); diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index 1a7496a77d..194c05577d 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -9,6 +9,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 747f53d37b..4d0756d9a5 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Multiplayer { @@ -211,7 +212,7 @@ namespace Multiplayer m_lastCorrectionSentTimeMs = currentTimeMs; AzNetworking::HashSerializer hashSerializer; - GetNetBindComponent()->SerializeEntityCorrection(hashSerializer); + SerializeEntityCorrection(hashSerializer); const AZ::HashValue32 localAuthorityHash = hashSerializer.GetHash(); @@ -233,7 +234,7 @@ namespace Multiplayer // only deserialize if we have data (for client/server profile/debug mismatches) if (correction.GetSize() > 0) { - GetNetBindComponent()->SerializeEntityCorrection(serializer); + SerializeEntityCorrection(serializer); } correction.Resize(serializer.GetSize()); @@ -313,7 +314,7 @@ namespace Multiplayer // Apply the correction AzNetworking::TrackChangedSerializer serializer(correction.GetBuffer(), static_cast(correction.GetSize())); - GetNetBindComponent()->SerializeEntityCorrection(serializer); + SerializeEntityCorrection(serializer); GetNetBindComponent()->NotifyCorrection(); #ifndef AZ_RELEASE_BUILD @@ -325,7 +326,7 @@ namespace Multiplayer { // Read out state values AzNetworking::StringifySerializer serverValues; - GetNetBindComponent()->SerializeEntityCorrection(serverValues); + SerializeEntityCorrection(serverValues); PrintCorrectionDifferences(*iter->second, serverValues); } else @@ -452,7 +453,7 @@ namespace Multiplayer // Generate a hash based on the current client predicted states AzNetworking::HashSerializer hashSerializer; - GetNetBindComponent()->SerializeEntityCorrection(hashSerializer); + SerializeEntityCorrection(hashSerializer); // Save this input and discard move history outside our client rewind window m_inputHistory.PushBack(input); @@ -480,7 +481,7 @@ namespace Multiplayer { m_predictiveStateHistory.erase(m_predictiveStateHistory.begin()); } - GetNetBindComponent()->SerializeEntityCorrection(*inputHistory); + SerializeEntityCorrection(*inputHistory); m_predictiveStateHistory.emplace(m_clientInputId, AZStd::move(inputHistory)); } #endif @@ -493,6 +494,18 @@ namespace Multiplayer } } + bool LocalPredictionPlayerInputComponentController::SerializeEntityCorrection(AzNetworking::ISerializer& serializer) + { + bool result = GetNetBindComponent()->SerializeEntityCorrection(serializer); + + NetworkHierarchyRootComponent* hierarchyComponent = GetParent().GetNetworkHierarchyRootComponent(); + if (result && hierarchyComponent) + { + result = hierarchyComponent->SerializeEntityCorrection(serializer); + } + return result; + } + void LocalPredictionPlayerInputComponentController::UpdateBankedTime(AZ::TimeMs deltaTimeMs) { const double deltaTime = static_cast(deltaTimeMs) / 1000.0; diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index 19958bace1..dd83878b25 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -452,4 +452,29 @@ namespace Multiplayer } } + bool NetworkHierarchyRootComponent::SerializeEntityCorrection(AzNetworking::ISerializer& serializer) + { + bool result = true; + + INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); + AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); + + for (AZ::Entity* child : m_hierarchicalEntities) + { + if (child == GetEntity()) + { + // Skip the root entity + continue; + } + + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); + NetBindComponent* netBindComponent = childEntityHandle.GetNetBindComponent(); + AZ_Assert(netBindComponent, "No NetBindComponent, this should be impossible"); + + result = result && netBindComponent->SerializeEntityCorrection(serializer); + } + + return result; + } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp index ef338840f9..7794d9026b 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityHandle.cpp @@ -32,8 +32,8 @@ namespace Multiplayer if (entity) { - AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid"); - m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity); + AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid"); + m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity); if (m_netBindComponent != nullptr) { m_netEntityId = m_netBindComponent->GetNetEntityId(); From b1a76feead62a5fc8d0cee7ac104ed47ddbeae93 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 14 Oct 2021 23:21:04 +0100 Subject: [PATCH 06/13] Added tests. Made network spawnable to serialize in binary Signed-off-by: pereslav --- Gems/Multiplayer/Code/CMakeLists.txt | 11 +++ .../Pipeline/NetworkPrefabProcessor.cpp | 2 +- ...TestMultiplayerComponent.AutoComponent.xml | 13 ++++ .../Code/Tests/ClientHierarchyTests.cpp | 70 ++++++++++++++++- .../Code/Tests/CommonHierarchySetup.h | 17 ++++ .../Code/Tests/TestMultiplayerComponent.cpp | 78 +++++++++++++++++++ .../Code/Tests/TestMultiplayerComponent.h | 61 +++++++++++++++ .../Code/multiplayer_tests_files.cmake | 8 ++ 8 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml create mode 100644 Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp create mode 100644 Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index 909590a25d..fd2c3f3f06 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -163,6 +163,7 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE Gem FILES_CMAKE multiplayer_tests_files.cmake + multiplayer_autogen_files.cmake INCLUDE_DIRECTORIES PRIVATE Tests @@ -175,6 +176,16 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) PRIVATE AZ::AzTest Gem::Multiplayer.Static + AUTOGEN_RULES + *.AutoPackets.xml,AutoPackets_Header.jinja,$path/$fileprefix.AutoPackets.h + *.AutoPackets.xml,AutoPackets_Inline.jinja,$path/$fileprefix.AutoPackets.inl + *.AutoPackets.xml,AutoPackets_Source.jinja,$path/$fileprefix.AutoPackets.cpp + *.AutoPackets.xml,AutoPacketDispatcher_Header.jinja,$path/$fileprefix.AutoPacketDispatcher.h + *.AutoPackets.xml,AutoPacketDispatcher_Inline.jinja,$path/$fileprefix.AutoPacketDispatcher.inl + *.AutoComponent.xml,AutoComponent_Header.jinja,$path/$fileprefix.AutoComponent.h + *.AutoComponent.xml,AutoComponent_Source.jinja,$path/$fileprefix.AutoComponent.cpp + *.AutoComponent.xml,AutoComponentTypes_Header.jinja,$path/AutoComponentTypes.h + *.AutoComponent.xml,AutoComponentTypes_Source.jinja,$path/AutoComponentTypes.cpp ) ly_add_googletest( NAME Gem::Multiplayer.Tests diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index e0990aa785..27e59c0bf4 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -110,7 +110,7 @@ namespace Multiplayer auto serializer = [](AZStd::vector& output, const ProcessedObjectStore& object) -> bool { AZ::IO::ByteContainerStream stream(&output); auto& asset = object.GetAsset(); - return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_JSON, &asset, asset.GetType()); + return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_BINARY, &asset, asset.GetType()); }; auto&& [object, networkSpawnable] = diff --git a/Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml new file mode 100644 index 0000000000..6b18e5ac70 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp index d6dd068c3f..badee73e04 100644 --- a/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp +++ b/Gems/Multiplayer/Code/Tests/ClientHierarchyTests.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include namespace Multiplayer { @@ -175,10 +177,10 @@ namespace Multiplayer void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child) { PopulateHierarchicalEntity(root); - SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client); + SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Autonomous); PopulateHierarchicalEntity(child); - SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client); + 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); @@ -330,7 +332,7 @@ namespace Multiplayer void CreateDeepHierarchyOnClient(EntityInfo& childOfChild) { PopulateHierarchicalEntity(childOfChild); - SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client); + 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); @@ -387,4 +389,66 @@ namespace Multiplayer ); } } + + TEST_F(ClientDeepHierarchyTests, CreateProcessInputTest) + { + using MultiplayerTest::TestMultiplayerComponent; + using MultiplayerTest::TestMultiplayerComponentController; + using MultiplayerTest::TestMultiplayerComponentNetworkInput; + + auto* rootNetBind = m_root->m_entity->FindComponent(); + + 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(); + EXPECT_NE(testInput, nullptr); + + auto* testMultiplayerComponent = entityInfo.m_entity->FindComponent(); + 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(); + const AZStd::vector& childInputs = rootHierarchyInput->m_childInputs; + EXPECT_EQ(childInputs.size(), 2); + ValidateCreatedInput(childInputs[0].GetNetworkInput(), *m_child); + ValidateCreatedInput(childInputs[1].GetNetworkInput(), *m_childOfChild); + } + + // Test ProcessInput + { + AZStd::unordered_set inputProcessedEntities; + size_t processInputCallCounter = 0; + auto processInputCallback = [&inputProcessedEntities, &processInputCallCounter](NetEntityId netEntityId) + { + 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()->m_processInputCallback = processInputCallback; + m_child->m_entity->FindComponent()->m_processInputCallback = processInputCallback; + m_childOfChild->m_entity->FindComponent()->m_processInputCallback = processInputCallback; + + rootNetBind->ProcessInput(input, deltaTime); + + EXPECT_EQ(processInputCallCounter, 3); + EXPECT_EQ(inputProcessedEntities, + AZStd::unordered_set({ m_root->m_netId, m_child->m_netId, m_childOfChild->m_netId })); + } + } } diff --git a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h index d6f65918c0..38027c11a9 100644 --- a/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h +++ b/Gems/Multiplayer/Code/Tests/CommonHierarchySetup.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace Multiplayer { @@ -93,6 +94,12 @@ namespace Multiplayer 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()); @@ -103,6 +110,7 @@ namespace Multiplayer GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0); m_mockNetworkEntityManager = AZStd::make_unique>(); + AZ::Interface::Register(m_mockNetworkEntityManager.get()); ON_CALL(*m_mockNetworkEntityManager, AddEntityToEntityMap(_, _)).WillByDefault(Invoke(this, &HierarchyTests::AddEntityToEntityMap)); ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::GetEntity)); @@ -136,6 +144,7 @@ namespace Multiplayer m_multiplayerComponentRegistry = AZStd::make_unique(); ON_CALL(*m_mockNetworkEntityManager, GetMultiplayerComponentRegistry()).WillByDefault(Return(m_multiplayerComponentRegistry.get())); RegisterMultiplayerComponents(); + MultiplayerTest::RegisterMultiplayerComponents(); } void TearDown() override @@ -157,6 +166,7 @@ namespace Multiplayer AZ::Interface::Unregister(m_mockNetworkTime.get()); AZ::Interface::Unregister(m_mockTime.get()); + AZ::Interface::Unregister(m_mockNetworkEntityManager.get()); AZ::Interface::Unregister(m_mockMultiplayer.get()); AZ::Interface::Unregister(m_mockComponentApplicationRequests.get()); @@ -165,6 +175,8 @@ namespace Multiplayer m_mockNetworkEntityManager.reset(); m_mockMultiplayer.reset(); + m_testInputDriverComponentDescriptor.reset(); + m_testMultiplayerComponentDescriptor.reset(); m_transformDescriptor.reset(); m_netTransformDescriptor.reset(); m_hierarchyRootDescriptor.reset(); @@ -186,6 +198,8 @@ namespace Multiplayer 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_mockNetworkEntityManager; @@ -394,6 +408,9 @@ namespace Multiplayer 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: diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp new file mode 100644 index 0000000000..336082eb01 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp @@ -0,0 +1,78 @@ +/* +* 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 + +namespace MultiplayerTest +{ + void TestInputDriverComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + } + + void TestMultiplayerComponent::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (serializeContext) + { + serializeContext->Class() + ->Version(1); + } + TestMultiplayerComponentBase::Reflect(context); + } + + void TestMultiplayerComponent::OnInit() + { + } + + void TestMultiplayerComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + void TestMultiplayerComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + TestMultiplayerComponentController::TestMultiplayerComponentController(TestMultiplayerComponent& parent) + : TestMultiplayerComponentControllerBase(parent) + { + } + + void TestMultiplayerComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + void TestMultiplayerComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) + { + } + + void TestMultiplayerComponentController::CreateInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) + { + auto* networkInput = input.FindComponentInput(); + networkInput->m_ownerId = GetParent().GetId(); + } + + void TestMultiplayerComponentController::ProcessInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) + { + auto& component = GetParent(); + auto* networkInput = input.FindComponentInput(); + AZ_Assert(networkInput->m_ownerId == component.GetId(), "Input Id doesn't match the owner component Id"); + + if (component.m_processInputCallback) + { + component.m_processInputCallback(GetNetEntityId()); + } + } +} diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h new file mode 100644 index 0000000000..6b2f8e49a5 --- /dev/null +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h @@ -0,0 +1,61 @@ +/* +* 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 + +namespace MultiplayerTest +{ + // Dummy class for satisfying "MultiplayerInputDriver" component dependency + class TestInputDriverComponent : public AZ::Component + { + public: + AZ_COMPONENT(TestInputDriverComponent, "{C3877905-3B61-45AE-A636-9845C3AAA39D}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.emplace_back(AZ_CRC_CE("MultiplayerInputDriver")); + } + + void Activate(){}; + void Deactivate(){}; + }; + + // Test multiplayer component with ability to create and process network input + class TestMultiplayerComponent + : public TestMultiplayerComponentBase + { + public: + AZ_MULTIPLAYER_COMPONENT(MultiplayerTest::TestMultiplayerComponent, s_testMultiplayerComponentConcreteUuid, MultiplayerTest::TestMultiplayerComponentBase); + + static void Reflect(AZ::ReflectContext* context); + + void OnInit() override; + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + AZStd::function m_processInputCallback; + }; + + // Multiplayer controller for the test component + class TestMultiplayerComponentController + : public TestMultiplayerComponentControllerBase + { + public: + TestMultiplayerComponentController(TestMultiplayerComponent& parent); + + //! TestMultiplayerComponentControllerBase + void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; + + //! MultiplayerController interface + void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override; + void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override; + }; +} diff --git a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake index 2114143b68..d1aea1cb82 100644 --- a/Gems/Multiplayer/Code/multiplayer_tests_files.cmake +++ b/Gems/Multiplayer/Code/multiplayer_tests_files.cmake @@ -7,6 +7,12 @@ # set(FILES + Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja + Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja + Include/Multiplayer/AutoGen/AutoComponent_Common.jinja + Include/Multiplayer/AutoGen/AutoComponent_Header.jinja + Include/Multiplayer/AutoGen/AutoComponent_Source.jinja + Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml Tests/ClientHierarchyTests.cpp Tests/ServerHierarchyBenchmarks.cpp Tests/CommonHierarchySetup.h @@ -19,4 +25,6 @@ set(FILES Tests/RewindableContainerTests.cpp Tests/RewindableObjectTests.cpp Tests/ServerHierarchyTests.cpp + Tests/TestMultiplayerComponent.h + Tests/TestMultiplayerComponent.cpp ) From 3c1a2db636858fa758cfb6d4cef743a5e520c38f Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 14 Oct 2021 23:27:58 +0100 Subject: [PATCH 07/13] Removed unnecessary files from CMakeLists.txt Signed-off-by: pereslav --- Gems/Multiplayer/Code/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Gems/Multiplayer/Code/CMakeLists.txt b/Gems/Multiplayer/Code/CMakeLists.txt index fd2c3f3f06..b971e6a2ce 100644 --- a/Gems/Multiplayer/Code/CMakeLists.txt +++ b/Gems/Multiplayer/Code/CMakeLists.txt @@ -163,7 +163,6 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAMESPACE Gem FILES_CMAKE multiplayer_tests_files.cmake - multiplayer_autogen_files.cmake INCLUDE_DIRECTORIES PRIVATE Tests @@ -177,11 +176,6 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED) AZ::AzTest Gem::Multiplayer.Static AUTOGEN_RULES - *.AutoPackets.xml,AutoPackets_Header.jinja,$path/$fileprefix.AutoPackets.h - *.AutoPackets.xml,AutoPackets_Inline.jinja,$path/$fileprefix.AutoPackets.inl - *.AutoPackets.xml,AutoPackets_Source.jinja,$path/$fileprefix.AutoPackets.cpp - *.AutoPackets.xml,AutoPacketDispatcher_Header.jinja,$path/$fileprefix.AutoPacketDispatcher.h - *.AutoPackets.xml,AutoPacketDispatcher_Inline.jinja,$path/$fileprefix.AutoPacketDispatcher.inl *.AutoComponent.xml,AutoComponent_Header.jinja,$path/$fileprefix.AutoComponent.h *.AutoComponent.xml,AutoComponent_Source.jinja,$path/$fileprefix.AutoComponent.cpp *.AutoComponent.xml,AutoComponentTypes_Header.jinja,$path/AutoComponentTypes.h From 05493b538f13f8f85131ea301d9204f43e6d3356 Mon Sep 17 00:00:00 2001 From: pereslav Date: Fri, 15 Oct 2021 13:13:19 +0100 Subject: [PATCH 08/13] Added tracking of hierarchy root ownership when hierarchies are migrated Signed-off-by: pereslav --- .../NetworkEntity/NetworkEntityManager.cpp | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index db7f7243cc..b72ea21719 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include namespace Multiplayer @@ -272,15 +274,32 @@ namespace Multiplayer bool safeToExit = true; NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId); - // We also need special handling for the EntityHierarchyComponent as well, since related entities need to be migrated together - //auto* hierarchyController = FindController(nonConstExitingEntityPtr); - //if (hierarchyController) - //{ - // if (hierarchyController->GetParentRelatedEntity()) - // { - // safeToExit = false; - // } - //} + // We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together + NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController(); + NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController(); + + // 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) From 62971fa7bd2a2222ed97fb91f7a8e9f1ef82e23d Mon Sep 17 00:00:00 2001 From: pereslav Date: Tue, 19 Oct 2021 13:52:32 +0100 Subject: [PATCH 09/13] PR feedback, removed unnecessary code Signed-off-by: pereslav --- .../NetworkHierarchyRootComponent.h | 2 +- .../NetworkHierarchyRootComponent.cpp | 21 +++++++------------ .../ServerToClientReplicationWindow.cpp | 2 ++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h index cafd63b223..f0ad3baf94 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkHierarchyRootComponent.h @@ -108,7 +108,7 @@ namespace Multiplayer //! NetworkHierarchyRootComponentController //! This is the network controller for NetworkHierarchyRootComponent. //! Class provides the ability to process input for hierarchies. - class NetworkHierarchyRootComponentController + class NetworkHierarchyRootComponentController final : public NetworkHierarchyRootComponentControllerBase { public: diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp index dd83878b25..e45389b7d9 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkHierarchyRootComponent.cpp @@ -373,6 +373,8 @@ namespace Multiplayer } NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); NetBindComponent* netComp = childEntityHandle.GetNetBindComponent(); @@ -395,24 +397,13 @@ namespace Multiplayer void NetworkHierarchyRootComponentController::ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) { - // Prevent replaying process input commands for child entities that weren't part of the hierarchy at that time - //if (!m_firstProcessInputOccurred) - //{ - // m_firstProcessInputOccurred = true; - // m_firstProcessInputTime = input.GetInputId().GetGameTimePoint(); - //} - //else if (m_firstProcessInputTime > input.GetInputId().GetGameTimePoint()) - //{ - // return; - //} - if (auto* networkInput = input.FindComponentInput()) { INetworkEntityManager* networkEntityManager = AZ::Interface::Get(); AZ_Assert(networkEntityManager, "NetworkEntityManager must be created."); // Build a set of Net IDs for the children - AZStd::unordered_set currentChildren; // TODO: Cache inside the component if this becomes a performance issue. + AZStd::unordered_set currentChildren; NetworkHierarchyRootComponent& component = GetParent(); for (AZ::Entity* child : component.m_hierarchicalEntities) { @@ -420,7 +411,9 @@ namespace Multiplayer { continue; } - NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); // TODO: Cache net IDs in the component if this becomes a performance issue + + NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); currentChildren.insert(childNetEntitydId); } @@ -468,6 +461,8 @@ namespace Multiplayer } NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId()); + AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); + ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId); NetBindComponent* netBindComponent = childEntityHandle.GetNetBindComponent(); AZ_Assert(netBindComponent, "No NetBindComponent, this should be impossible"); diff --git a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp index d8195bf29a..856fd59f9d 100644 --- a/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp +++ b/Gems/Multiplayer/Code/Source/ReplicationWindows/ServerToClientReplicationWindow.cpp @@ -335,6 +335,8 @@ namespace Multiplayer for (const AZ::Entity* controlledEntity : hierarchyComponent.m_hierarchicalEntities) { NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId()); + AZ_Assert(controlledNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager"); + ConstNetworkEntityHandle controlledEntityHandle = networkEntityManager->GetEntity(controlledNetEntitydId); AZ_Assert(controlledEntityHandle != nullptr, "We have lost a controlled entity unexpectedly"); From 725be128aa80effa26846772a529f08d7e856b2a Mon Sep 17 00:00:00 2001 From: pereslav Date: Tue, 19 Oct 2021 16:20:40 +0100 Subject: [PATCH 10/13] tidy up Signed-off-by: pereslav --- Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h index 6b2f8e49a5..4cf0be2c2d 100644 --- a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.h @@ -23,8 +23,8 @@ namespace MultiplayerTest provided.emplace_back(AZ_CRC_CE("MultiplayerInputDriver")); } - void Activate(){}; - void Deactivate(){}; + void Activate() override {} + void Deactivate() override {} }; // Test multiplayer component with ability to create and process network input From faea65e84007484a9664390baf08127136579169 Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Tue, 19 Oct 2021 17:59:06 +0100 Subject: [PATCH 11/13] Removed pragma once from cpp Signed-off-by: Sergey Pereslavtsev --- Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp index 336082eb01..f47c102118 100644 --- a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 OR MIT * */ -#pragma once - #include #include From e3a14c08093cab7db003778a1f4a16bdfed8041c Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Tue, 19 Oct 2021 18:42:35 +0100 Subject: [PATCH 12/13] Fixed warning about unused local variable Signed-off-by: Sergey Pereslavtsev --- Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp index f47c102118..ac723300a9 100644 --- a/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Tests/TestMultiplayerComponent.cpp @@ -65,7 +65,7 @@ namespace MultiplayerTest void TestMultiplayerComponentController::ProcessInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) { auto& component = GetParent(); - auto* networkInput = input.FindComponentInput(); + [[maybe_unused]] auto* networkInput = input.FindComponentInput(); AZ_Assert(networkInput->m_ownerId == component.GetId(), "Input Id doesn't match the owner component Id"); if (component.m_processInputCallback) From 8e520c8db8f845f3162d866c58eb4148a4247a2a Mon Sep 17 00:00:00 2001 From: Sergey Pereslavtsev Date: Wed, 20 Oct 2021 12:36:35 +0100 Subject: [PATCH 13/13] PR feedback Signed-off-by: Sergey Pereslavtsev --- .../AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml | 4 ++-- Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml index f8df21a220..fbe6ff2135 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml @@ -12,7 +12,7 @@ - - + + diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h index 471d86f718..417d9d65f7 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputChild.h @@ -34,4 +34,6 @@ namespace Multiplayer ConstNetworkEntityHandle m_owner; NetworkInput m_networkInput; }; + + using NetworkInputChildList = AZStd::vector; }