Removed spawn/despawn events from the spawnable system. Used NetworkSpawnableHolderComponent instead

Signed-off-by: pereslav <pereslav@amazon.com>
This commit is contained in:
pereslav
2021-09-21 14:50:06 +01:00
parent 454ae90afb
commit 895974db57
9 changed files with 43 additions and 126 deletions
@@ -320,15 +320,6 @@ namespace AzFramework
//! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs.
virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0;
//! Register a handler for OnSpawned events.
//! @param handler The handler to receive the event.
virtual void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>,
const AZStd::vector<AZ::Entity*>&, const void*>::Handler& handler) = 0;
//! Register a handler for OnDespawned events.
//! @param handler The handler to receive the event.
virtual void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>, const void*>::Handler& handler) = 0;
protected:
[[nodiscard]] virtual AZStd::pair<EntitySpawnTicket::Id, void*> CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) = 0;
virtual void DestroyTicket(void* ticket) = 0;
@@ -146,16 +146,6 @@ namespace AzFramework
QueueRequest(ticket, optionalArgs.m_priority, AZStd::move(queueEntry));
}
void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>, const AZStd::vector<AZ::Entity*>&, const void*>::Handler& handler)
{
handler.Connect(m_onSpawnedEvent);
}
void SpawnableEntitiesManager::AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>, const void*>::Handler& handler)
{
handler.Connect(m_onDespawnedEvent);
}
auto SpawnableEntitiesManager::ProcessQueue(CommandQueuePriority priority) -> CommandQueueStatus
{
CommandQueueStatus result = CommandQueueStatus::NoCommandsLeft;
@@ -359,8 +349,6 @@ namespace AzFramework
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket);
ticket.m_currentRequestId++;
return true;
}
@@ -441,8 +429,6 @@ namespace AzFramework
ticket.m_spawnedEntities.begin() + spawnedEntitiesInitialCount, ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket);
ticket.m_currentRequestId++;
return true;
}
@@ -474,8 +460,6 @@ namespace AzFramework
request.m_completionCallback(request.m_ticketId);
}
m_onDespawnedEvent.Signal(ticket.m_spawnable, &ticket);
ticket.m_currentRequestId++;
return true;
}
@@ -503,8 +487,6 @@ namespace AzFramework
}
}
m_onDespawnedEvent.Signal(ticket.m_spawnable, &ticket);
// Rebuild the list of entities.
ticket.m_spawnedEntities.clear();
const Spawnable::EntityList& entities = request.m_spawnable->GetEntities();
@@ -562,8 +544,6 @@ namespace AzFramework
ticket.m_spawnedEntities.begin(), ticket.m_spawnedEntities.end()));
}
m_onSpawnedEvent.Signal(ticket.m_spawnable, ticket.m_spawnedEntities, &ticket);
ticket.m_currentRequestId++;
return true;
@@ -69,9 +69,6 @@ namespace AzFramework
void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) override;
void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>, const AZStd::vector<AZ::Entity*>&, const void*>::Handler& handler) override;
void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>, const void*>::Handler& handler) override;
//
// The following function is thread safe but intended to be run from the main thread.
//
@@ -227,9 +224,6 @@ namespace AzFramework
//! SpawnablePriority_Default which gives users a bit of room to fine tune the priorities as this value can be configured
//! through the Settings Registry under the key "/O3DE/AzFramework/Spawnables/HighPriorityThreshold".
SpawnablePriority m_highPriorityThreshold { 64 };
AZ::Event<AZ::Data::Asset<Spawnable>, const AZStd::vector<AZ::Entity*>&, const void*> m_onSpawnedEvent;
AZ::Event<AZ::Data::Asset<Spawnable>, const void*> m_onDespawnedEvent;
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AzFramework::SpawnableEntitiesManager::CommandQueuePriority);
@@ -59,8 +59,6 @@ namespace AzFramework
MOCK_METHOD1(CreateTicket, AZStd::pair<EntitySpawnTicket::Id, void*>(AZ::Data::Asset<Spawnable>&& spawnable));
MOCK_METHOD1(DestroyTicket, void(void* ticket));
MOCK_METHOD1(AddOnSpawnedHandler, void(AZ::Event<AZ::Data::Asset<Spawnable>, const AZStd::vector<AZ::Entity*>&, const void*>::Handler& handler));
MOCK_METHOD1(AddOnDespawnedHandler, void(AZ::Event<AZ::Data::Asset<Spawnable>, const void*>::Handler& handler));
/** Installs some default result values for the above functions.
* Note that you can always override these in scope of your test by adding additional ON_CALL / EXPECT_CALL
@@ -13,6 +13,7 @@
#include <AzCore/Component/Entity.h>
#include <AzCore/EBus/Event.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
namespace Multiplayer
{
@@ -75,6 +76,15 @@ namespace Multiplayer
const AZ::Transform& transform
) = 0;
//! Requests a network spawnable to instantiate at a given transform
//! This is an async function. The instantiated entities are not available immediately but will be constructed by the spawnable system
//! The spawnable ticket has to be kept for the whole lifetime of the entities
//! @param netSpawnable the network spawnable to spawn
//! @param transform the transform where the spawnable should be spawned
//! @return the ticket for managing the spawned entities
[[nodiscard]] virtual AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform) = 0;
//! Configures new networked entity
//! @param netEntity the entity to setup
//! @param prefabEntryId the name of the spawnable the entity originated from
@@ -30,14 +30,9 @@ namespace Multiplayer
: m_networkEntityAuthorityTracker(*this)
, m_removeEntitiesEvent([this] { RemoveEntities(); }, AZ::Name("NetworkEntityManager remove entities event"))
, m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event"))
, m_onSpawnedHandler([this](AZ::Data::Asset<AzFramework::Spawnable> spawnable, const AZStd::vector<AZ::Entity*>& entities, const void* spawnTicket) { this->OnSpawned(spawnable, entities, spawnTicket); })
, m_onDespawnedHandler([this](AZ::Data::Asset<AzFramework::Spawnable> spawnable, const void* spawnTicket) { this->OnDespawned(spawnable, spawnTicket); })
{
AZ::Interface<INetworkEntityManager>::Register(this);
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
AzFramework::SpawnableEntitiesInterface::Get()->AddOnSpawnedHandler(m_onSpawnedHandler);
AzFramework::SpawnableEntitiesInterface::Get()->AddOnDespawnedHandler(m_onDespawnedHandler);
}
NetworkEntityManager::~NetworkEntityManager()
@@ -473,52 +468,17 @@ namespace Multiplayer
}
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> NetworkEntityManager::RequestNetSpawnableInstantiation(
const AZ::Data::Asset<AzFramework::Spawnable>& rootSpawnable, const AZStd::vector<AZ::Entity*>& entities)
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform)
{
if (entities.empty())
{
AZ_Error("NetworkEntityManager", false,
"RequestNetSpawnableInstantiation: No entities in the spawnable %s", rootSpawnable.GetHint().c_str());
return nullptr;
}
// The first entity in every spawnable is the root one
const AZ::Entity* rootEntity = *entities.begin();
if (!rootEntity)
{
AZ_Error("NetworkEntityManager", false,
"RequestNetSpawnableInstantiation: Root entity is null in the spawnable %s", rootSpawnable.GetHint().c_str());
return nullptr;
}
const auto* holderComponent = rootEntity->FindComponent<NetworkSpawnableHolderComponent>();
if (!holderComponent)
{
// This spawnable doesn't have a corresponding network spawnable.
return nullptr;
}
AzFramework::TransformComponent* rootEntityTransform =
rootEntity->FindComponent<AzFramework::TransformComponent>();
if (!rootEntityTransform)
{
AZ_Error("NetworkEntityManager", false,
"RequestNetSpawnableInstantiation: Root entity has no transform in the spawnable %s", rootSpawnable.GetHint().c_str());
return nullptr;
}
// Retrieve the corresponding network spawnable asset
AZ::Data::Asset<AzFramework::Spawnable> netSpawnableAsset = holderComponent->GetNetworkSpawnableAsset();
// 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(netSpawnableAsset.GetId());
AZ::Interface<INetworkSpawnableLibrary>::Get()->GetSpawnableNameFromAssetId(netSpawnable.GetId());
// 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 = rootEntityTransform->GetWorldTM()]
optionalArgs.m_preInsertionCallback = [netSpawnableName, rootTransform = transform]
(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView entities)
{
bool shouldUpdateTransform = (rootTransform.IsClose(AZ::Transform::Identity()) == false);
@@ -546,7 +506,7 @@ namespace Multiplayer
};
// 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>(netSpawnableAsset);
auto ticket = AZStd::make_unique<AzFramework::EntitySpawnTicket>(netSpawnable);
AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs));
return ticket;
}
@@ -572,28 +532,6 @@ namespace Multiplayer
{
multiplayer->SendReadyForEntityUpdates(false);
}
m_netSpawnableTickets.clear();
}
void NetworkEntityManager::OnSpawned(AZ::Data::Asset<AzFramework::Spawnable> spawnable,
const AZStd::vector<AZ::Entity*>& entities, const void* spawnTicket)
{
if (ShouldSpawnNetEntities())
{
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> ticket = RequestNetSpawnableInstantiation(spawnable, entities);
if (ticket)
{
AZ::Data::AssetId spawnableAssetId = spawnable.GetId();
m_netSpawnableTickets[spawnTicket] = AZStd::move(ticket);
}
}
}
void NetworkEntityManager::OnDespawned([[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> spawnable,
const void* spawnTicket)
{
m_netSpawnableTickets.erase(spawnTicket);
}
void NetworkEntityManager::SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole)
@@ -611,12 +549,4 @@ namespace Multiplayer
netEntity->GetName().c_str());
}
}
bool NetworkEntityManager::ShouldSpawnNetEntities() const
{
const auto agentType = GetMultiplayer()->GetAgentType();
const bool shouldSpawnNetEntities =
(agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer);
return shouldSpawnNetEntities;
}
}
@@ -11,7 +11,6 @@
#include <AzCore/EBus/ScheduledEvent.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkSpawnableLibrary.h>
@@ -61,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;
@@ -93,15 +95,6 @@ namespace Multiplayer
private:
void RemoveEntities();
NetEntityId NextId();
bool ShouldSpawnNetEntities() const;
void OnSpawned(AZ::Data::Asset<AzFramework::Spawnable> spawnable,
const AZStd::vector<AZ::Entity*>& entities, const void* spawnTicket);
void OnDespawned(AZ::Data::Asset<AzFramework::Spawnable> spawnable, const void* spawnTicket);
// Note: This is an async function.
// The instantiated entities are not available immediately but will be constructed by the spawnable system
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
const AZ::Data::Asset<AzFramework::Spawnable>& rootSpawnable, const AZStd::vector<AZ::Entity*>& entities);
NetworkEntityTracker m_networkEntityTracker;
NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker;
@@ -130,10 +123,5 @@ namespace Multiplayer
DeferredRpcMessages m_localDeferredRpcMessages;
NetworkSpawnableLibrary m_networkPrefabLibrary;
AZStd::unordered_map<const void*, AZStd::unique_ptr<AzFramework::EntitySpawnTicket>> m_netSpawnableTickets;
AZ::Event<AZ::Data::Asset<AzFramework::Spawnable>, const AZStd::vector<AZ::Entity*>&, const void*>::Handler m_onSpawnedHandler;
AZ::Event<AZ::Data::Asset<AzFramework::Spawnable>, const void*>::Handler m_onDespawnedHandler;
};
}
@@ -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)
@@ -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
{
@@ -37,5 +38,6 @@ namespace Multiplayer
private:
AZ::Data::Asset<AzFramework::Spawnable> m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad };
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> m_netSpawnableTicket;
};
} // namespace Multiplayer