SPEC-6590 Implemented network entities instantiation by network spawnable asset ID
This commit is contained in:
@@ -148,6 +148,12 @@ namespace AzFramework
|
||||
//! Blocks until all operations made on the provided ticket before the barrier call have completed.
|
||||
virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback) = 0;
|
||||
|
||||
//! Register a handler for OnSpawned events.
|
||||
virtual void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) = 0;
|
||||
|
||||
//! Register a handler for OnDespawned events.
|
||||
virtual void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) = 0;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] virtual void* CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) = 0;
|
||||
virtual void DestroyTicket(void* ticket) = 0;
|
||||
|
||||
@@ -114,6 +114,16 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_onSpawnedEvent);
|
||||
}
|
||||
|
||||
void SpawnableEntitiesManager::AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_onDespawnedEvent);
|
||||
}
|
||||
|
||||
auto SpawnableEntitiesManager::ProcessQueue() -> CommandQueueStatus
|
||||
{
|
||||
AZStd::queue<Requests> pendingRequestQueue;
|
||||
@@ -223,6 +233,8 @@ namespace AzFramework
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
|
||||
}
|
||||
|
||||
m_onSpawnedEvent.Signal(ticket.m_spawnable);
|
||||
|
||||
ticket.m_currentTicketId++;
|
||||
return true;
|
||||
}
|
||||
@@ -257,6 +269,8 @@ namespace AzFramework
|
||||
ticket.m_spawnedEntities.begin() + spawnedEntitiesCount, ticket.m_spawnedEntities.end()));
|
||||
}
|
||||
|
||||
m_onSpawnedEvent.Signal(ticket.m_spawnable);
|
||||
|
||||
ticket.m_currentTicketId++;
|
||||
return true;
|
||||
}
|
||||
@@ -289,6 +303,8 @@ namespace AzFramework
|
||||
request.m_completionCallback(*request.m_ticket);
|
||||
}
|
||||
|
||||
m_onDespawnedEvent.Signal(ticket.m_spawnable);
|
||||
|
||||
ticket.m_currentTicketId++;
|
||||
return true;
|
||||
}
|
||||
@@ -315,6 +331,8 @@ namespace AzFramework
|
||||
&GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants, entity->GetId());
|
||||
}
|
||||
}
|
||||
|
||||
m_onDespawnedEvent.Signal(ticket.m_spawnable);
|
||||
|
||||
// Rebuild the list of entities.
|
||||
ticket.m_spawnedEntities.clear();
|
||||
@@ -350,6 +368,9 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
ticket.m_currentTicketId++;
|
||||
|
||||
m_onSpawnedEvent.Signal(ticket.m_spawnable);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -60,6 +60,9 @@ namespace AzFramework
|
||||
|
||||
void Barrier(EntitySpawnTicket& spawnInfo, BarrierCallback completionCallback) override;
|
||||
|
||||
void AddOnSpawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) override;
|
||||
void AddOnDespawnedHandler(AZ::Event<AZ::Data::Asset<Spawnable>>::Handler& handler) override;
|
||||
|
||||
//
|
||||
// The following function is thread safe but intended to be run from the main thread.
|
||||
//
|
||||
@@ -156,5 +159,8 @@ namespace AzFramework
|
||||
AZStd::deque<Requests> m_delayedQueue; //!< Requests that were processed before, but couldn't be completed.
|
||||
AZStd::queue<Requests> m_pendingRequestQueue;
|
||||
AZStd::mutex m_pendingRequestQueueMutex;
|
||||
|
||||
AZ::Event<AZ::Data::Asset<Spawnable>> m_onSpawnedEvent;
|
||||
AZ::Event<AZ::Data::Asset<Spawnable>> m_onDespawnedEvent;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
#include <AzFramework/Entity/GameEntityContextBus.h>
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
|
||||
#include <Include/IMultiplayer.h>
|
||||
#include <Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
#include <Source/Components/NetBindComponent.h>
|
||||
@@ -34,9 +35,14 @@ 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) { this->OnSpawned(spawnable); })
|
||||
, m_onDespawnedHandler([this](AZ::Data::Asset<AzFramework::Spawnable> spawnable) { this->OnDespawned(spawnable); })
|
||||
{
|
||||
AZ::Interface<INetworkEntityManager>::Register(this);
|
||||
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
|
||||
|
||||
AzFramework::SpawnableEntitiesInterface::Get()->AddOnSpawnedHandler(m_onSpawnedHandler);
|
||||
AzFramework::SpawnableEntitiesInterface::Get()->AddOnDespawnedHandler(m_onDespawnedHandler);
|
||||
}
|
||||
|
||||
NetworkEntityManager::~NetworkEntityManager()
|
||||
@@ -365,58 +371,57 @@ namespace Multiplayer
|
||||
{
|
||||
INetworkEntityManager::EntityList returnList;
|
||||
|
||||
// TODO: Implement for non-root spawnables
|
||||
auto spawnableAssetId = m_networkPrefabLibrary.GetAssetIdByName(prefabEntryId.m_prefabName);
|
||||
if (spawnableAssetId == m_rootSpawnableAsset.GetId())
|
||||
// Required for sync-instantiation. Todo: keep the reference in NetworkSpawnableLibrary
|
||||
auto netSpawnableAsset = AZ::Data::AssetManager::Instance().GetAsset<AzFramework::Spawnable>(spawnableAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
|
||||
AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(netSpawnableAsset);
|
||||
|
||||
AzFramework::Spawnable* netSpawnable = netSpawnableAsset.GetAs<AzFramework::Spawnable>();
|
||||
if (!netSpawnable)
|
||||
{
|
||||
AzFramework::Spawnable* netSpawnable = m_rootSpawnableAsset.GetAs<AzFramework::Spawnable>();
|
||||
if (!netSpawnable)
|
||||
return returnList;
|
||||
}
|
||||
|
||||
const uint32_t entityIndex = prefabEntryId.m_entityOffset;
|
||||
|
||||
if (entityIndex == PrefabEntityId::AllIndices)
|
||||
{
|
||||
return CreateEntitiesImmediate(*netSpawnable, netEntityRole);
|
||||
}
|
||||
|
||||
const AzFramework::Spawnable::EntityList& entities = netSpawnable->GetEntities();
|
||||
size_t entitiesSize = entities.size();
|
||||
if (entityIndex >= entitiesSize)
|
||||
{
|
||||
return returnList;
|
||||
}
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
|
||||
AZ::Entity* clone = serializeContext->CloneObject(entities[entityIndex].get());
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
clone->SetId(AZ::Entity::MakeId());
|
||||
|
||||
NetBindComponent* netBindComponent = clone->FindComponent<NetBindComponent>();
|
||||
if (netBindComponent)
|
||||
{
|
||||
netBindComponent->PreInit(clone, prefabEntryId, netEntityId, netEntityRole);
|
||||
|
||||
auto* transformComponent = clone->FindComponent<AzFramework::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
return returnList;
|
||||
transformComponent->SetWorldTM(transform);
|
||||
}
|
||||
|
||||
const uint32_t entityIndex = prefabEntryId.m_entityOffset;
|
||||
|
||||
if (entityIndex == PrefabEntityId::AllIndices)
|
||||
if (autoActivate == AutoActivate::DoNotActivate)
|
||||
{
|
||||
return CreateEntitiesImmediate(*netSpawnable, netEntityRole);
|
||||
clone->SetRuntimeActiveByDefault(false);
|
||||
}
|
||||
|
||||
const AzFramework::Spawnable::EntityList& entities = netSpawnable->GetEntities();
|
||||
size_t entitiesSize = entities.size();
|
||||
if (entityIndex >= entitiesSize)
|
||||
{
|
||||
return returnList;
|
||||
}
|
||||
AzFramework::GameEntityContextRequestBus::Broadcast(&AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone);
|
||||
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
||||
|
||||
AZ::Entity* clone = serializeContext->CloneObject(entities[entityIndex].get());
|
||||
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
|
||||
clone->SetId(AZ::Entity::MakeId());
|
||||
|
||||
NetBindComponent* netBindComponent = clone->FindComponent<NetBindComponent>();
|
||||
if (netBindComponent)
|
||||
{
|
||||
netBindComponent->PreInit(clone, prefabEntryId, netEntityId, netEntityRole);
|
||||
|
||||
auto* transformComponent = clone->FindComponent<AzFramework::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
transformComponent->SetWorldTM(transform);
|
||||
}
|
||||
|
||||
if (autoActivate == AutoActivate::DoNotActivate)
|
||||
{
|
||||
clone->SetRuntimeActiveByDefault(false);
|
||||
}
|
||||
|
||||
AzFramework::GameEntityContextRequestBus::Broadcast(
|
||||
&AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone);
|
||||
|
||||
returnList.push_back(netBindComponent->GetEntityHandle());
|
||||
}
|
||||
returnList.push_back(netBindComponent->GetEntityHandle());
|
||||
}
|
||||
|
||||
return returnList;
|
||||
@@ -429,13 +434,37 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
void NetworkEntityManager::OnRootSpawnableAssigned(
|
||||
AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
|
||||
[[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
|
||||
{
|
||||
AzFramework::Spawnable* rootSpawnableData = rootSpawnable.GetAs<AzFramework::Spawnable>();
|
||||
const auto& entityList = rootSpawnableData->GetEntities();
|
||||
auto* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
const auto agentType = multiplayer->GetAgentType();
|
||||
|
||||
if (agentType == MultiplayerAgentType::Client)
|
||||
{
|
||||
multiplayer->SendReadyForEntityUpdates(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
|
||||
{
|
||||
// TODO: Do we need to clear all entities here?
|
||||
auto* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
const auto agentType = multiplayer->GetAgentType();
|
||||
|
||||
if (agentType == MultiplayerAgentType::Client)
|
||||
{
|
||||
multiplayer->SendReadyForEntityUpdates(false);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEntityManager::OnSpawned(AZ::Data::Asset<AzFramework::Spawnable> spawnable)
|
||||
{
|
||||
AzFramework::Spawnable* spawnableData = spawnable.GetAs<AzFramework::Spawnable>();
|
||||
const auto& entityList = spawnableData->GetEntities();
|
||||
if (entityList.size() == 0)
|
||||
{
|
||||
AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Root spawnable doesn't have any entities.");
|
||||
AZ_Error("NetworkEntityManager", false, "OnSpawned: Spawnable %s doesn't have any entities.",
|
||||
spawnable.GetHint().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -443,7 +472,7 @@ namespace Multiplayer
|
||||
auto* spawnableHolder = rootEntity->FindComponent<NetworkSpawnableHolderComponent>();
|
||||
if (!spawnableHolder)
|
||||
{
|
||||
AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Root entity doesn't have NetworkSpawnableHolderComponent.");
|
||||
// Root entity doesn't have NetworkSpawnableHolderComponent. It means there's no corresponding network spawnable.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -465,8 +494,6 @@ namespace Multiplayer
|
||||
return;
|
||||
}
|
||||
|
||||
m_rootSpawnableAsset = netSpawnableAsset;
|
||||
|
||||
auto* multiplayer = AZ::Interface<IMultiplayer>::Get();
|
||||
|
||||
const auto agentType = multiplayer->GetAgentType();
|
||||
@@ -477,17 +504,10 @@ namespace Multiplayer
|
||||
{
|
||||
CreateEntitiesImmediate(*netSpawnable, NetEntityRole::Authority);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we don't spawn net entities immediately (i.e. it is a client),
|
||||
// tell the server/host it can start sending updates that will instantiate entities.
|
||||
multiplayer->SendReadyForEntityUpdates(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation)
|
||||
void NetworkEntityManager::OnDespawned([[maybe_unused]]AZ::Data::Asset<AzFramework::Spawnable> spawnable)
|
||||
{
|
||||
// TODO: Do we need to clear all entities here?
|
||||
m_rootSpawnableAsset.Release();
|
||||
// TODO: Remove entities instantiated from the spawnable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,9 @@ namespace Multiplayer
|
||||
|
||||
NetEntityId NextId();
|
||||
|
||||
void OnSpawned(AZ::Data::Asset<AzFramework::Spawnable> spawnable);
|
||||
void OnDespawned(AZ::Data::Asset<AzFramework::Spawnable> spawnable);
|
||||
|
||||
NetworkEntityTracker m_networkEntityTracker;
|
||||
NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker;
|
||||
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
|
||||
@@ -111,6 +114,8 @@ namespace Multiplayer
|
||||
DeferredRpcMessages m_localDeferredRpcMessages;
|
||||
|
||||
NetworkSpawnableLibrary m_networkPrefabLibrary;
|
||||
AZ::Data::Asset<AzFramework::Spawnable> m_rootSpawnableAsset;
|
||||
|
||||
AZ::Event<AZ::Data::Asset<AzFramework::Spawnable>>::Handler m_onSpawnedHandler;
|
||||
AZ::Event<AZ::Data::Asset<AzFramework::Spawnable>>::Handler m_onDespawnedHandler;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace Multiplayer
|
||||
const AZ::Name name = AZ::Name(relativePath);
|
||||
m_spawnables[name] = id;
|
||||
m_spawnablesReverseLookup[id] = name;
|
||||
|
||||
}
|
||||
|
||||
void NetworkSpawnableLibrary::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
|
||||
|
||||
@@ -104,9 +104,10 @@ namespace Multiplayer
|
||||
networkedEntityIds.push_back(sourceEntity->GetId());
|
||||
}
|
||||
}
|
||||
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefab))
|
||||
|
||||
if (networkedEntityIds.empty())
|
||||
{
|
||||
AZ_Error("NetworkPrefabProcessor", false, "Saving exported Prefab Instance within a Prefab Dom failed.");
|
||||
// No networked entities in the prefab, no need to do anything in this processor.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user