Merge branch 'upstream/development' into LYN6657_MultiplayerScriptImprovementsForDemo

This commit is contained in:
Gene Walters
2021-09-23 16:35:56 -07:00
443 changed files with 7704 additions and 2226 deletions
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<Component
Name="NetworkHierarchyChildComponent"
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="false"
OverrideInclude="Multiplayer/Components/NetworkHierarchyChildComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<NetworkProperty Type="NetEntityId" Name="hierarchyRoot" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
</Component>
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<Component
Name="NetworkHierarchyRootComponent"
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="false"
OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<NetworkProperty Type="NetEntityId" Name="hierarchyRoot" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
</Component>
@@ -0,0 +1,222 @@
/*
* 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 <AzCore/Serialization/EditContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyBus.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
namespace Multiplayer
{
void NetworkHierarchyChildComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetworkHierarchyChildComponent, NetworkHierarchyChildComponentBase>()
->Version(1);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<NetworkHierarchyChildComponent>(
"Network Hierarchy Child", "Declares a network dependency on the root of this hierarchy.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
;
}
}
NetworkHierarchyChildComponentBase::Reflect(context);
}
void NetworkHierarchyChildComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("NetworkTransformComponent"));
}
void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
}
void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
}
NetworkHierarchyChildComponent::NetworkHierarchyChildComponent()
: m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); })
, m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); })
, m_hierarchyRootNetIdChanged([this](NetEntityId rootNetId) {OnHierarchyRootNetIdChanged(rootNetId); })
{
}
void NetworkHierarchyChildComponent::OnInit()
{
}
void NetworkHierarchyChildComponent::OnActivate([[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
m_isHierarchyEnabled = true;
HierarchyRootAddEvent(m_hierarchyRootNetIdChanged);
NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId());
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
{
transformComponent->BindChildChangedEventHandler(m_childChangedHandler);
transformComponent->BindParentChangedEventHandler(m_parentChangedHandler);
}
}
void NetworkHierarchyChildComponent::OnDeactivate([[maybe_unused]] EntityIsMigrating entityIsMigrating)
{
m_isHierarchyEnabled = false;
if (m_rootEntity)
{
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
{
root->RebuildHierarchy();
}
}
NotifyChildrenHierarchyDisbanded();
NetworkHierarchyRequestBus::Handler::BusDisconnect();
}
bool NetworkHierarchyChildComponent::IsHierarchyEnabled() const
{
return m_isHierarchyEnabled;
}
bool NetworkHierarchyChildComponent::IsHierarchicalChild() const
{
return GetHierarchyRoot() != InvalidNetEntityId;
}
AZ::Entity* NetworkHierarchyChildComponent::GetHierarchicalRoot() const
{
return m_rootEntity;
}
AZStd::vector<AZ::Entity*> NetworkHierarchyChildComponent::GetHierarchicalEntities() const
{
if (m_rootEntity)
{
return m_rootEntity->FindComponent<NetworkHierarchyRootComponent>()->GetHierarchicalEntities();
}
return {};
}
void NetworkHierarchyChildComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler)
{
handler.Connect(m_networkHierarchyChangedEvent);
}
void NetworkHierarchyChildComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler)
{
handler.Connect(m_networkHierarchyLeaveEvent);
}
void NetworkHierarchyChildComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot)
{
m_rootEntity = hierarchyRoot;
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
{
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
if (m_rootEntity)
{
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(m_rootEntity->GetId());
controller->SetHierarchyRoot(netRootId);
m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId());
}
else
{
controller->SetHierarchyRoot(InvalidNetEntityId);
m_networkHierarchyLeaveEvent.Signal();
}
}
if (m_rootEntity == nullptr)
{
NotifyChildrenHierarchyDisbanded();
}
}
void NetworkHierarchyChildComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child)
{
if (m_rootEntity)
{
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
{
root->RebuildHierarchy();
}
}
}
void NetworkHierarchyChildComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, [[maybe_unused]] AZ::EntityId parent)
{
if (m_rootEntity)
{
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
{
root->RebuildHierarchy();
}
}
}
void NetworkHierarchyChildComponent::OnHierarchyRootNetIdChanged(NetEntityId rootNetId)
{
ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(rootNetId);
if (rootHandle.Exists())
{
AZ::Entity* newRoot = rootHandle.GetEntity();
if (m_rootEntity != newRoot)
{
m_rootEntity = newRoot;
m_networkHierarchyChangedEvent.Signal(m_rootEntity->GetId());
}
}
else
{
m_isHierarchyEnabled = false;
m_rootEntity = nullptr;
m_networkHierarchyLeaveEvent.Signal();
}
}
void NetworkHierarchyChildComponent::NotifyChildrenHierarchyDisbanded()
{
AZStd::vector<AZ::EntityId> allChildren;
AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren);
for (const AZ::EntityId& childEntityId : allChildren)
{
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(childEntityId))
{
if (auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>())
{
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(nullptr);
}
else if (auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
{
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(nullptr);
}
}
}
}
}
@@ -0,0 +1,329 @@
/*
* 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 <AzCore/Component/Entity.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
AZ_CVAR(uint32_t, bg_hierarchyEntityMaxLimit, 16, nullptr, AZ::ConsoleFunctorFlags::Null,
"Maximum allowed size of network entity hierarchies, including top level entity.");
namespace Multiplayer
{
void NetworkHierarchyRootComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetworkHierarchyRootComponent, NetworkHierarchyRootComponentBase>()
->Version(1);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<NetworkHierarchyRootComponent>(
"Network Hierarchy Root", "Marks the entity as the root of an entity hierarchy.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
;
}
}
NetworkHierarchyRootComponentBase::Reflect(context);
}
void NetworkHierarchyRootComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC_CE("NetworkTransformComponent"));
}
void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
}
void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
incompatible.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
}
NetworkHierarchyRootComponent::NetworkHierarchyRootComponent()
: m_childChangedHandler([this](AZ::ChildChangeType type, AZ::EntityId child) { OnChildChanged(type, child); })
, m_parentChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId parent) { OnParentChanged(oldParent, parent); })
{
}
void NetworkHierarchyRootComponent::OnInit()
{
}
void NetworkHierarchyRootComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
m_isHierarchyEnabled = true;
m_hierarchicalEntities.push_back(GetEntity());
NetworkHierarchyRequestBus::Handler::BusConnect(GetEntityId());
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
{
transformComponent->BindChildChangedEventHandler(m_childChangedHandler);
transformComponent->BindParentChangedEventHandler(m_parentChangedHandler);
}
}
void NetworkHierarchyRootComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
m_isHierarchyEnabled = false;
if (m_rootEntity)
{
// Tell parent to re-build the hierarchy
if (NetworkHierarchyRootComponent* root = m_rootEntity->FindComponent<NetworkHierarchyRootComponent>())
{
root->RebuildHierarchy();
}
}
else
{
// Notify children that the hierarchy is disbanding
AZStd::vector<AZ::EntityId> allChildren;
AZ::TransformBus::EventResult(allChildren, GetEntityId(), &AZ::TransformBus::Events::GetChildren);
for (const AZ::EntityId& childEntityId : allChildren)
{
if (const AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(childEntityId))
{
SetRootForEntity(nullptr, childEntity);
}
}
}
m_childChangedHandler.Disconnect();
m_parentChangedHandler.Disconnect();
NetworkHierarchyRequestBus::Handler::BusDisconnect();
m_hierarchicalEntities.clear();
m_rootEntity = nullptr;
}
bool NetworkHierarchyRootComponent::IsHierarchyEnabled() const
{
return m_isHierarchyEnabled;
}
bool NetworkHierarchyRootComponent::IsHierarchicalRoot() const
{
return GetHierarchyRoot() == InvalidNetEntityId;
}
bool NetworkHierarchyRootComponent::IsHierarchicalChild() const
{
return !IsHierarchicalRoot();
}
AZStd::vector<AZ::Entity*> NetworkHierarchyRootComponent::GetHierarchicalEntities() const
{
return m_hierarchicalEntities;
}
AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const
{
if (m_rootEntity)
{
return m_rootEntity;
}
return GetEntity();
}
void NetworkHierarchyRootComponent::BindNetworkHierarchyChangedEventHandler(NetworkHierarchyChangedEvent::Handler& handler)
{
handler.Connect(m_networkHierarchyChangedEvent);
}
void NetworkHierarchyRootComponent::BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler)
{
handler.Connect(m_networkHierarchyLeaveEvent);
}
void NetworkHierarchyRootComponent::OnChildChanged([[maybe_unused]] AZ::ChildChangeType type, [[maybe_unused]] AZ::EntityId child)
{
if (IsHierarchicalRoot())
{
// Parent-child notifications are not reliable enough to avoid duplicate notifications,
// so we will rebuild from scratch to avoid duplicate entries in @m_hierarchicalEntities.
RebuildHierarchy();
}
else if (NetworkHierarchyRootComponent* root = GetHierarchicalRoot()->FindComponent<NetworkHierarchyRootComponent>())
{
root->RebuildHierarchy();
}
}
void NetworkHierarchyRootComponent::OnParentChanged([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
{
// If the parent is part of a hierarchy, it will detect this entity as a new child and rebuild hierarchy.
// Thus, we only need to take care of a case when the parent is not part of a hierarchy,
// in which case, this entity will be a new root of a new hierarchy.
if (AZ::Entity* parentEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(newParent))
{
if (parentEntity->FindComponent<NetworkHierarchyRootComponent>() == nullptr &&
parentEntity->FindComponent<NetworkHierarchyChildComponent>() == nullptr)
{
RebuildHierarchy();
}
else
{
m_hierarchicalEntities.clear();
}
}
else
{
// Detached from parent
RebuildHierarchy();
}
}
void NetworkHierarchyRootComponent::RebuildHierarchy()
{
AZStd::vector<AZ::Entity*> previousEntities;
m_hierarchicalEntities.swap(previousEntities);
m_hierarchicalEntities.push_back(GetEntity()); // Add the root.
uint32_t currentEntityCount = aznumeric_cast<uint32_t>(m_hierarchicalEntities.size());
RecursiveAttachHierarchicalEntities(GetEntityId(), currentEntityCount);
bool hierarchyChanged = false;
// Send out join and leave events.
for (AZ::Entity* currentEntity : m_hierarchicalEntities)
{
const auto prevEntityIterator = AZStd::find(previousEntities.begin(), previousEntities.end(), currentEntity);
if (prevEntityIterator != previousEntities.end())
{
// This entity was here before the build of the hierarchy.
previousEntities.erase(prevEntityIterator);
}
else
{
// This is a newly added entity to the network hierarchy.
hierarchyChanged = true;
SetRootForEntity(GetEntity(), currentEntity);
}
}
// These entities were removed since last rebuild.
for (const AZ::Entity* previousEntity : previousEntities)
{
SetRootForEntity(nullptr, previousEntity);
}
if (!previousEntities.empty())
{
hierarchyChanged = true;
}
if (hierarchyChanged)
{
m_networkHierarchyChangedEvent.Signal(GetEntityId());
}
}
void NetworkHierarchyRootComponent::SetRootForEntity(AZ::Entity* root, const AZ::Entity* childEntity)
{
if (auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>())
{
hierarchyChildComponent->SetTopLevelHierarchyRootEntity(root);
}
else if (auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>())
{
hierarchyRootComponent->SetTopLevelHierarchyRootEntity(root);
}
}
bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalEntities(AZ::EntityId underEntity, uint32_t& currentEntityCount)
{
AZStd::vector<AZ::EntityId> allChildren;
AZ::TransformBus::EventResult(allChildren, underEntity, &AZ::TransformBus::Events::GetChildren);
for (const AZ::EntityId& newChildId : allChildren)
{
if (!RecursiveAttachHierarchicalChild(newChildId, currentEntityCount))
{
return false;
}
}
return true;
}
bool NetworkHierarchyRootComponent::RecursiveAttachHierarchicalChild(AZ::EntityId entity, uint32_t& currentEntityCount)
{
if (currentEntityCount >= bg_hierarchyEntityMaxLimit)
{
AZLOG_WARN("Entity %s is trying to build a network hierarchy that is too large. bg_hierarchyEntityMaxLimit is currently set to (%u)",
GetEntity()->GetName().c_str(), static_cast<uint32_t>(bg_hierarchyEntityMaxLimit));
return false;
}
if (AZ::Entity* childEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entity))
{
auto* hierarchyChildComponent = childEntity->FindComponent<NetworkHierarchyChildComponent>();
auto* hierarchyRootComponent = childEntity->FindComponent<NetworkHierarchyRootComponent>();
if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchyEnabled()) ||
(hierarchyRootComponent && hierarchyRootComponent->IsHierarchyEnabled()))
{
m_hierarchicalEntities.push_back(childEntity);
++currentEntityCount;
if (!RecursiveAttachHierarchicalEntities(entity, currentEntityCount))
{
return false;
}
}
}
return true;
}
void NetworkHierarchyRootComponent::SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot)
{
m_rootEntity = hierarchyRoot;
if (HasController() && GetNetBindComponent()->GetNetEntityRole() == NetEntityRole::Authority)
{
NetworkHierarchyChildComponentController* controller = static_cast<NetworkHierarchyChildComponentController*>(GetController());
if (hierarchyRoot)
{
const NetEntityId netRootId = GetNetworkEntityManager()->GetNetEntityIdById(hierarchyRoot->GetId());
controller->SetHierarchyRoot(netRootId);
}
else
{
controller->SetHierarchyRoot(InvalidNetEntityId);
}
}
if (m_rootEntity == nullptr)
{
// We lost the parent hierarchical entity, so as a root we need to re-build our own hierarchy.
RebuildHierarchy();
}
}
}
@@ -28,6 +28,7 @@ namespace Multiplayer
NetworkTransformComponent::NetworkTransformComponent()
: m_entityPreRenderEventHandler([this](float deltaTime) { OnPreRender(deltaTime); })
, m_entityCorrectionEventHandler([this]() { OnCorrection(); })
, m_parentChangedEventHandler([this](NetEntityId parentId) { OnParentChanged(parentId); })
{
;
}
@@ -41,6 +42,7 @@ namespace Multiplayer
{
GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler);
GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler);
ParentEntityIdAddEvent(m_parentChangedEventHandler);
}
void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
@@ -97,10 +99,26 @@ namespace Multiplayer
}
}
void NetworkTransformComponent::OnParentChanged(NetEntityId parentId)
{
const ConstNetworkEntityHandle parentEntityHandle = GetNetworkEntityManager()->GetEntity(parentId);
if (parentEntityHandle.Exists())
{
if (const AZ::Entity* parentEntity = parentEntityHandle.GetEntity())
{
GetEntity()->GetTransform()->SetParent(parentEntity->GetId());
}
}
else
{
GetEntity()->GetTransform()->SetParent(AZ::EntityId());
}
}
NetworkTransformComponentController::NetworkTransformComponentController(NetworkTransformComponent& parent)
: NetworkTransformComponentControllerBase(parent)
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformChangedEvent(worldTm); })
, m_parentIdChangedHandler([this](AZ::EntityId oldParent, AZ::EntityId newParent) { OnParentIdChangedEvent(oldParent, newParent); })
{
;
}
@@ -109,6 +127,9 @@ namespace Multiplayer
{
GetParent().GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler);
OnTransformChangedEvent(GetParent().GetTransformComponent()->GetWorldTM());
GetParent().GetTransformComponent()->BindParentChangedEventHandler(m_parentIdChangedHandler);
OnParentIdChangedEvent(AZ::EntityId(), GetParent().GetTransformComponent()->GetParentId());
}
void NetworkTransformComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
@@ -122,4 +143,14 @@ namespace Multiplayer
SetTranslation(worldTm.GetTranslation());
SetScale(worldTm.GetUniformScale());
}
void NetworkTransformComponentController::OnParentIdChangedEvent([[maybe_unused]] AZ::EntityId oldParent, AZ::EntityId newParent)
{
AZ::Entity* parentEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(newParent);
if (parentEntity)
{
const ConstNetworkEntityHandle parentHandle(parentEntity, GetNetworkEntityTracker());
SetParentEntityId(parentHandle.GetNetEntityId());
}
}
}
@@ -6,13 +6,14 @@
*
*/
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <Source/MultiplayerGem.h>
#include <Source/MultiplayerSystemComponent.h>
#include <Source/AutoGen/AutoComponentTypes.h>
#include <Source/Pipeline/NetBindMarkerComponent.h>
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzNetworking/Framework/NetworkingSystemComponent.h>
namespace Multiplayer
{
@@ -23,7 +24,6 @@ namespace Multiplayer
AzNetworking::NetworkingSystemComponent::CreateDescriptor(),
MultiplayerSystemComponent::CreateDescriptor(),
NetBindComponent::CreateDescriptor(),
NetBindMarkerComponent::CreateDescriptor(),
NetworkSpawnableHolderComponent::CreateDescriptor(),
});
@@ -75,6 +75,8 @@ namespace Multiplayer
void EntityReplicationManager::ActivatePendingEntities()
{
AZStd::vector<NetEntityId> notReadyEntities;
const AZ::TimeMs endTimeMs = AZ::GetElapsedTimeMs() + m_entityActivationTimeSliceMs;
while (!m_entitiesPendingActivation.empty())
{
@@ -83,7 +85,14 @@ namespace Multiplayer
EntityReplicator* entityReplicator = GetEntityReplicator(entityId);
if (entityReplicator && !entityReplicator->IsMarkedForRemoval())
{
entityReplicator->ActivateNetworkEntity();
if (entityReplicator->IsReadyToActivate())
{
entityReplicator->ActivateNetworkEntity();
}
else
{
notReadyEntities.push_back(entityId);
}
}
if (m_entityActivationTimeSliceMs > AZ::TimeMs{ 0 } && AZ::GetElapsedTimeMs() > endTimeMs)
{
@@ -91,6 +100,11 @@ namespace Multiplayer
break;
}
}
for (NetEntityId netEntityId : notReadyEntities)
{
m_entitiesPendingActivation.push_back(netEntityId);
}
}
void EntityReplicationManager::SendUpdates(AZ::TimeMs hostTimeMs)
@@ -249,15 +263,15 @@ namespace Multiplayer
void EntityReplicationManager::SendEntityUpdates(AZ::TimeMs hostTimeMs)
{
EntityReplicatorList toSendList = GenerateEntityUpdateList();
AZLOG(NET_ReplicationInfo, "Sending %zd updates from %d to %d", toSendList.size(), (uint8_t)GetNetworkEntityManager()->GetHostId(), (uint8_t)GetRemoteHostId());
// prep a replication record for send, at this point, everything needs to be sent
for (EntityReplicator* replicator : toSendList)
{
replicator->GetPropertyPublisher()->PrepareSerialization();
}
// While our to send list is not empty, build up another packet to send
do
{
@@ -524,7 +538,7 @@ namespace Multiplayer
bool EntityReplicationManager::HandlePropertyChangeMessage
(
AzNetworking::IConnection* invokingConnection,
AzNetworking::IConnection* invokingConnection,
EntityReplicator* entityReplicator,
AzNetworking::PacketId packetId,
NetEntityId netEntityId,
@@ -1137,7 +1151,7 @@ namespace Multiplayer
AzNetworking::TrackChangedSerializer<AzNetworking::NetworkOutputSerializer> outputSerializer(message.m_propertyUpdateData.GetBuffer(), static_cast<uint32_t>(message.m_propertyUpdateData.GetSize()));
if (!HandlePropertyChangeMessage
(
invokingConnection,
invokingConnection,
replicator,
AzNetworking::InvalidPacketId,
message.m_entityId,
@@ -6,23 +6,25 @@
*
*/
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Source/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Source/NetworkEntity/EntityReplication/PropertyPublisher.h>
#include <Source/NetworkEntity/EntityReplication/PropertySubscriber.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzNetworking/PacketLayer/IPacket.h>
#include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
#include <AzNetworking/ConnectionLayer/IConnection.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Console/IConsole.h>
@@ -48,7 +50,7 @@ namespace Multiplayer
, m_onForwardRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onSendAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onForwardAutonomousRpcHandler([this](NetworkEntityRpcMessage& entityRpcMessage) { OnSendRpcEvent(entityRpcMessage); })
, m_onEntityStopHandler([this](const ConstNetworkEntityHandle &) { OnEntityRemovedEvent(); })
, m_onEntityStopHandler([this](const ConstNetworkEntityHandle&) { OnEntityRemovedEvent(); })
, m_proxyRemovalEvent([this] { OnProxyRemovalTimedEvent(); }, AZ::Name("ProxyRemovalTimedEvent"))
{
if (auto localEnt = m_entityHandle.GetEntity())
@@ -119,12 +121,12 @@ namespace Multiplayer
{
m_replicationManager.AddReplicatorToPendingSend(*this);
m_propertyPublisher = AZStd::make_unique<PropertyPublisher>
(
GetRemoteNetworkRole(),
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
m_netBindComponent,
*m_connection
);
(
GetRemoteNetworkRole(),
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
m_netBindComponent,
*m_connection
);
m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler);
}
else
@@ -279,7 +281,7 @@ namespace Multiplayer
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
bool isAuthority = (GetBoundLocalNetworkRole() == NetEntityRole::Authority)
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
&& (GetBoundLocalNetworkRole() == netBindComponent->GetNetEntityRole());
bool isClient = GetRemoteNetworkRole() == NetEntityRole::Client;
bool isAutonomous = GetBoundLocalNetworkRole() == NetEntityRole::Autonomous;
if (isAuthority || isClient || isAutonomous)
@@ -306,9 +308,9 @@ namespace Multiplayer
bool EntityReplicator::RemoteManagerOwnsEntityLifetime() const
{
bool isServer = (GetBoundLocalNetworkRole() == NetEntityRole::Server)
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
&& (GetRemoteNetworkRole() == NetEntityRole::Authority);
bool isClient = (GetBoundLocalNetworkRole() == NetEntityRole::Client)
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
|| (GetBoundLocalNetworkRole() == NetEntityRole::Autonomous);
return isServer || isClient;
}
@@ -405,6 +407,62 @@ namespace Multiplayer
return m_replicationManager.GetResendTimeoutTimeMs();
}
bool EntityReplicator::IsReadyToActivate() const
{
const AZ::Entity* entity = m_entityHandle.GetEntity();
AZ_Assert(entity, "Entity replicator entity unexpectedly missing");
const NetworkHierarchyChildComponent* hierarchyChildComponent = entity->FindComponent<NetworkHierarchyChildComponent>();
const NetworkHierarchyRootComponent* hierarchyRootComponent = nullptr;
if (hierarchyChildComponent == nullptr)
{
// Child and root hierarchy components are mutually exclusive
hierarchyRootComponent = entity->FindComponent<NetworkHierarchyRootComponent>();
}
if ((hierarchyChildComponent && hierarchyChildComponent->IsHierarchicalChild())
|| (hierarchyRootComponent && hierarchyRootComponent->IsHierarchicalChild()))
{
// If hierarchy is enabled for the entity, check if the parent is available
if (const NetworkTransformComponent* networkTransform = entity->FindComponent<NetworkTransformComponent>())
{
const NetEntityId parentId = networkTransform->GetParentEntityId();
/*
* For root entities attached to a level, a network parent won't be set.
* In this case, this entity is the root entity of the hierarchy and it will be activated first.
*/
if (parentId != InvalidNetEntityId)
{
ConstNetworkEntityHandle parentHandle = GetNetworkEntityManager()->GetEntity(parentId);
const AZ::Entity* parentEntity = parentHandle.GetEntity();
if (parentEntity && parentEntity->GetState() == AZ::Entity::State::Active)
{
AZLOG
(
NET_HierarchyActivationInfo,
"Hierchical entity %s asking for activation - granted",
entity->GetName().c_str()
);
return true;
}
AZLOG
(
NET_HierarchyActivationInfo,
"Hierchical entity %s asking for activation - waiting on the parent %u",
entity->GetName().c_str(),
aznumeric_cast<uint32_t>(parentId)
);
return false;
}
}
}
return true;
}
NetworkEntityUpdateMessage EntityReplicator::GenerateUpdatePacket()
{
if (IsMarkedForRemoval() && OwnsReplicatorLifetime()) // TODO: clean this up
@@ -36,7 +36,7 @@ namespace Multiplayer
{
public:
EntityReplicator(EntityReplicationManager& replicationManager, AzNetworking::IConnection* connection, NetEntityRole remoteNetworkRole, const ConstNetworkEntityHandle& entityHandle);
virtual ~EntityReplicator();
~EntityReplicator() override;
NetEntityRole GetBoundLocalNetworkRole() const;
NetEntityRole GetRemoteNetworkRole() const;
@@ -62,6 +62,8 @@ namespace Multiplayer
bool IsDeletionAcknowledged() const;
bool WasMigrated() const;
void SetWasMigrated(bool wasMigrated);
// If an entity is part of a network hierarchy then it is only ready to activate when its direct parent entity is active.
bool IsReadyToActivate() const;
NetworkEntityUpdateMessage GenerateUpdatePacket();
@@ -465,8 +465,60 @@ namespace Multiplayer
return netEntityId;
}
void NetworkEntityManager::OnRootSpawnableAssigned(
[[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> NetworkEntityManager::RequestNetSpawnableInstantiation(
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform)
{
// Prepare the parameters for the spawning process
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
optionalArgs.m_priority = AzFramework::SpawnablePriority_High;
const AZ::Name netSpawnableName =
AZ::Interface<INetworkSpawnableLibrary>::Get()->GetSpawnableNameFromAssetId(netSpawnable.GetId());
if (netSpawnableName.IsEmpty())
{
AZ_Error("NetworkEntityManager", false,
"RequestNetSpawnableInstantiation: Requested spawnable %s doesn't exist in the NetworkSpawnableLibrary. Please make sure it is a network spawnable",
netSpawnable.GetHint().c_str());
return nullptr;
}
// Pre-insertion callback allows us to do network-specific setup for the entities before they are added to the scene
optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = transform]
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities)
{
bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false);
for (uint32_t netEntityIndex = 0, entitiesSize = aznumeric_cast<uint32_t>(entities.size());
netEntityIndex < entitiesSize; ++netEntityIndex)
{
AZ::Entity* netEntity = *(entities.begin() + netEntityIndex);
if (shouldUpdateTransform)
{
AzFramework::TransformComponent* netEntityTransform =
netEntity->FindComponent<AzFramework::TransformComponent>();
AZ::Transform worldTm = netEntityTransform->GetWorldTM();
worldTm = rootTransform * worldTm;
netEntityTransform->SetWorldTM(worldTm);
}
PrefabEntityId prefabEntityId;
prefabEntityId.m_prefabName = netSpawnableName;
prefabEntityId.m_entityOffset = netEntityIndex;
AZ::Interface<INetworkEntityManager>::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority);
}
};
// Spawn with the newly created ticket. This allows the calling code to manage the lifetime of the constructed entities
auto ticket = AZStd::make_unique<AzFramework::EntitySpawnTicket>(netSpawnable);
AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs));
return ticket;
}
void NetworkEntityManager::OnRootSpawnableAssigned(AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable,
[[maybe_unused]] uint32_t generation)
{
auto* multiplayer = GetMultiplayer();
const auto agentType = multiplayer->GetAgentType();
@@ -479,7 +531,6 @@ namespace Multiplayer
void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
{
// TODO: Do we need to clear all entities here?
auto* multiplayer = GetMultiplayer();
const auto agentType = multiplayer->GetAgentType();
@@ -60,6 +60,9 @@ namespace Multiplayer
const AZ::Transform& transform
) override;
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform) override;
void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override;
uint32_t GetEntityCount() const override;
@@ -37,6 +37,7 @@ namespace Multiplayer
NetworkEntityHandle Get(NetEntityId netEntityId);
ConstNetworkEntityHandle Get(NetEntityId netEntityId) const;
//! Returns Net Entity ID for a given AZ Entity ID.
NetEntityId Get(const AZ::EntityId& entityId) const;
//! Returns true if the netEntityId exists.
@@ -1,115 +0,0 @@
/*
* 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 <Source/Pipeline/NetBindMarkerComponent.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/INetworkSpawnableLibrary.h>
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Components/TransformComponent.h>
namespace Multiplayer
{
void NetBindMarkerComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetBindMarkerComponent, AZ::Component>()
->Version(1)
->Field("NetEntityIndex", &NetBindMarkerComponent::m_netEntityIndex)
->Field("NetSpawnableAsset", &NetBindMarkerComponent::m_networkSpawnableAsset);
}
}
AzFramework::Spawnable* GetSpawnableFromAsset(AZ::Data::Asset<AzFramework::Spawnable>& asset)
{
AzFramework::Spawnable* spawnable = asset.GetAs<AzFramework::Spawnable>();
if (!spawnable)
{
asset =
AZ::Data::AssetManager::Instance().GetAsset<AzFramework::Spawnable>(asset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad);
AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(asset);
spawnable = asset.GetAs<AzFramework::Spawnable>();
}
return spawnable;
}
void NetBindMarkerComponent::Activate()
{
const auto agentType = AZ::Interface<IMultiplayer>::Get()->GetAgentType();
const bool spawnImmediately =
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
if (spawnImmediately && m_networkSpawnableAsset.GetId().IsValid())
{
AZ::Transform worldTm = GetEntity()->FindComponent<AzFramework::TransformComponent>()->GetWorldTM();
auto preInsertionCallback =
[worldTm = AZStd::move(worldTm), netEntityIndex = m_netEntityIndex, spawnableAssetId = m_networkSpawnableAsset.GetId()]
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities)
{
if (entities.size() == 1)
{
AZ::Entity* netEntity = *entities.begin();
auto* transformComponent = netEntity->FindComponent<AzFramework::TransformComponent>();
transformComponent->SetWorldTM(worldTm);
AZ::Name spawnableName = AZ::Interface<INetworkSpawnableLibrary>::Get()->GetSpawnableNameFromAssetId(spawnableAssetId);
PrefabEntityId prefabEntityId;
prefabEntityId.m_prefabName = spawnableName;
prefabEntityId.m_entityOffset = static_cast<uint32_t>(netEntityIndex);
AZ::Interface<INetworkEntityManager>::Get()->SetupNetEntity(netEntity, prefabEntityId, NetEntityRole::Authority);
}
else
{
AZ_Error("NetBindMarkerComponent", false, "Requested to spawn 1 entity, but received %d", entities.size());
}
};
m_netSpawnTicket = AzFramework::EntitySpawnTicket(m_networkSpawnableAsset);
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
optionalArgs.m_preInsertionCallback = AZStd::move(preInsertionCallback);
AzFramework::SpawnableEntitiesInterface::Get()->SpawnEntities(
m_netSpawnTicket, { m_netEntityIndex }, AZStd::move(optionalArgs));
}
}
void NetBindMarkerComponent::Deactivate()
{
if(m_netSpawnTicket.IsValid())
{
AzFramework::SpawnableEntitiesInterface::Get()->DespawnAllEntities(m_netSpawnTicket);
}
}
size_t NetBindMarkerComponent::GetNetEntityIndex() const
{
return m_netEntityIndex;
}
void NetBindMarkerComponent::SetNetEntityIndex(size_t netEntityIndex)
{
m_netEntityIndex = netEntityIndex;
}
void NetBindMarkerComponent::SetNetworkSpawnableAsset(AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset)
{
m_networkSpawnableAsset = networkSpawnableAsset;
}
AZ::Data::Asset<AzFramework::Spawnable> NetBindMarkerComponent::GetNetworkSpawnableAsset() const
{
return m_networkSpawnableAsset;
}
}
@@ -1,47 +0,0 @@
/*
* 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 <AzCore/Component/Component.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
namespace Multiplayer
{
//! @class NetBindMarkerComponent
//! @brief Component for tracking net entities in the original non-networked spawnable.
class NetBindMarkerComponent final : public AZ::Component
{
public:
AZ_COMPONENT(NetBindMarkerComponent, "{40612C1B-427D-45C6-A2F0-04E16DF5B718}");
static void Reflect(AZ::ReflectContext* context);
NetBindMarkerComponent() = default;
~NetBindMarkerComponent() override = default;
//! AZ::Component overrides.
//! @{
void Activate() override;
void Deactivate() override;
//! @}
size_t GetNetEntityIndex() const;
void SetNetEntityIndex(size_t val);
void SetNetworkSpawnableAsset(AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset);
AZ::Data::Asset<AzFramework::Spawnable> GetNetworkSpawnableAsset() const;
private:
AZ::Data::Asset<AzFramework::Spawnable> m_networkSpawnableAsset{AZ::Data::AssetLoadBehavior::PreLoad};
size_t m_netEntityIndex = 0;
AzFramework::EntitySpawnTicket m_netSpawnTicket;
};
} // namespace Multiplayer
@@ -8,7 +8,6 @@
#include <Multiplayer/IMultiplayerTools.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Pipeline/NetBindMarkerComponent.h>
#include <Pipeline/NetworkPrefabProcessor.h>
#include <Pipeline/NetworkSpawnableHolderComponent.h>
@@ -46,7 +45,7 @@ namespace Multiplayer
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->Class<NetworkPrefabProcessor, PrefabProcessor>()->Version(1);
serializeContext->Class<NetworkPrefabProcessor, PrefabProcessor>()->Version(2);
}
}
@@ -137,8 +136,6 @@ namespace Multiplayer
networkSpawnableAsset.Create(networkSpawnable->GetId());
networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
size_t netEntitiesIndexCounter = 0;
for (auto* prefabEntity : prefabNetEntities)
{
Instance* instance = netEntityToInstanceMap[prefabEntity];
@@ -148,30 +145,11 @@ namespace Multiplayer
AZ_Assert(netEntity, "Unable to detach entity %s [%s] from the source prefab instance",
prefabEntity->GetName().c_str(), entityId.ToString().c_str());
// Net entity will need a new ID to avoid IDs collision
netEntity->SetId(AZ::Entity::MakeId());
netEntity->InvalidateDependencies();
netEntity->EvaluateDependencies();
// Insert the entity into the target net spawnable
netSpawnableEntities.emplace_back(netEntity);
// Use the old ID for the breadcrumb entity to keep parent-child relationship in the original spawnable
AZ::Entity* breadcrumbEntity = aznew AZ::Entity(entityId, netEntity->GetName());
breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault());
// Marker component is responsible to spawning entities based on the index.
NetBindMarkerComponent* netBindMarkerComponent = breadcrumbEntity->CreateComponent<NetBindMarkerComponent>();
netBindMarkerComponent->SetNetEntityIndex(netEntitiesIndexCounter);
netBindMarkerComponent->SetNetworkSpawnableAsset(networkSpawnableAsset);
// Copy the transform component from the original entity to have the correct transform and parent-child relationship
AzFramework::TransformComponent* transformComponent = netEntity->FindComponent<AzFramework::TransformComponent>();
breadcrumbEntity->CreateComponent<AzFramework::TransformComponent>(*transformComponent);
instance->AddEntity(*breadcrumbEntity);
netEntitiesIndexCounter++;
}
// Add net spawnable asset holder to the prefab root
@@ -8,6 +8,8 @@
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <Multiplayer/IMultiplayer.h>
namespace Multiplayer
{
@@ -28,10 +30,32 @@ namespace Multiplayer
void NetworkSpawnableHolderComponent::Activate()
{
const auto agentType = GetMultiplayer()->GetAgentType();
const bool shouldSpawnNetEntities =
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
if(shouldSpawnNetEntities)
{
AZ::Transform rootEntityTransform = AZ::Transform::CreateIdentity();
AzFramework::TransformComponent* rootEntityTransformComponent =
GetEntity()->FindComponent<AzFramework::TransformComponent>();
if (rootEntityTransformComponent)
{
rootEntityTransform = rootEntityTransformComponent->GetWorldTM();
}
INetworkEntityManager* networkEntityManager = GetNetworkEntityManager();
AZ_Assert(networkEntityManager != nullptr,
"Network Entity Manager must be initialized before NetworkSpawnableHolderComponent is activated");
m_netSpawnableTicket = networkEntityManager->RequestNetSpawnableInstantiation(m_networkSpawnableAsset, rootEntityTransform);
}
}
void NetworkSpawnableHolderComponent::Deactivate()
{
m_netSpawnableTicket.reset();
}
void NetworkSpawnableHolderComponent::SetNetworkSpawnableAsset(AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset)
@@ -39,7 +63,7 @@ namespace Multiplayer
m_networkSpawnableAsset = networkSpawnableAsset;
}
AZ::Data::Asset<AzFramework::Spawnable> NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset()
AZ::Data::Asset<AzFramework::Spawnable> NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() const
{
return m_networkSpawnableAsset;
}
@@ -11,6 +11,7 @@
#include <AzCore/Component/Component.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
namespace Multiplayer
{
@@ -33,9 +34,10 @@ namespace Multiplayer
//! @}
void SetNetworkSpawnableAsset(AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset);
AZ::Data::Asset<AzFramework::Spawnable> GetNetworkSpawnableAsset();
AZ::Data::Asset<AzFramework::Spawnable> GetNetworkSpawnableAsset() const;
private:
AZ::Data::Asset<AzFramework::Spawnable> m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad };
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> m_netSpawnableTicket;
};
} // namespace Multiplayer