You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
669 lines
27 KiB
C++
669 lines
27 KiB
C++
/*
|
|
* 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 <AzFramework/Entity/SliceEntityOwnershipService.h>
|
|
|
|
namespace AzFramework
|
|
{
|
|
SliceEntityOwnershipService::SliceEntityOwnershipService(const EntityContextId& entityContextId,
|
|
AZ::SerializeContext* serializeContext)
|
|
: m_entityContextId(entityContextId)
|
|
, m_serializeContext(serializeContext)
|
|
, m_nextSliceTicketId(0)
|
|
{
|
|
if (nullptr == serializeContext)
|
|
{
|
|
AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
|
|
AZ_Assert(m_serializeContext, "Failed to retrieve application serialization context.");
|
|
}
|
|
}
|
|
|
|
void SliceEntityOwnershipService::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
|
{
|
|
serializeContext->Class<SliceInstantiationTicket>()->Version(0);
|
|
}
|
|
|
|
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
|
{
|
|
behaviorContext->Class<AzFramework::SliceInstantiationTicket>()
|
|
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
|
->Attribute(AZ::Script::Attributes::Module, "entity")
|
|
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
|
|
->Method("Equal", &AzFramework::SliceInstantiationTicket::operator==)
|
|
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
|
|
->Method("ToString", &SliceInstantiationTicket::ToString)
|
|
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
|
|
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
|
->Method("IsValid", &AzFramework::SliceInstantiationTicket::IsValid);
|
|
}
|
|
}
|
|
|
|
void SliceEntityOwnershipService::Initialize()
|
|
{
|
|
if (!m_rootAsset)
|
|
{
|
|
m_rootAsset.Create(AZ::Data::AssetId(AZ::Uuid::CreateRandom()), false);
|
|
AZ::Data::AssetBus::MultiHandler::BusConnect(m_rootAsset->GetId());
|
|
SliceEntityOwnershipServiceRequestBus::Handler::BusConnect(m_entityContextId);
|
|
}
|
|
CreateRootSlice();
|
|
}
|
|
|
|
bool SliceEntityOwnershipService::IsInitialized()
|
|
{
|
|
return m_rootAsset ? true : false;
|
|
}
|
|
|
|
void SliceEntityOwnershipService::Destroy()
|
|
{
|
|
if (m_rootAsset)
|
|
{
|
|
SliceEntityOwnershipServiceRequestBus::Handler::BusDisconnect(m_entityContextId);
|
|
AZ::Data::AssetBus::MultiHandler::BusDisconnect(m_rootAsset->GetId());
|
|
m_rootAsset.Reset();
|
|
}
|
|
}
|
|
|
|
void SliceEntityOwnershipService::Reset()
|
|
{
|
|
if (m_rootAsset)
|
|
{
|
|
EntityOwnershipServiceNotificationBus::Event(m_entityContextId, &EntityOwnershipServiceNotificationBus::Events::PrepareForEntityOwnershipServiceReset);
|
|
while (!m_queuedSliceInstantiations.empty())
|
|
{
|
|
// clear out the remaining instantiations in a conservative manner, assuming that callbacks such as
|
|
// OnSliceInstantiationFailed will call back into us and potentially mutate this list.
|
|
const InstantiatingSliceInfo& instantiating = m_queuedSliceInstantiations.back();
|
|
|
|
// 'instantiating' is deleted during this loop, so capture the asset Id and Ticket before we continue and destroy it.
|
|
AZ::Data::AssetId idToNotify = instantiating.m_asset.GetId();
|
|
AzFramework::SliceInstantiationTicket ticket = instantiating.m_ticket;
|
|
|
|
// this will decrement the refcount of the asset, which could mean its invalid by the next line.
|
|
// the above line also ensures that our list no longer contains this particular instantiation.
|
|
// its important to do that, before calling any callbacks, because some listeners on the following functions
|
|
// may call additional functions on this entity ownership service, and we could get into a situation
|
|
// where we end up iterating over this list again (before returning from the below bus calls).
|
|
m_queuedSliceInstantiations.pop_back();
|
|
|
|
AZ::Data::AssetBus::MultiHandler::BusDisconnect(idToNotify);
|
|
DispatchOnSliceInstantiationFailed(ticket, idToNotify, true);
|
|
}
|
|
|
|
EntityList entities = GetRootSliceEntities();
|
|
|
|
for (AZ::Entity* entity : entities)
|
|
{
|
|
DestroyEntity(entity);
|
|
}
|
|
|
|
// Re-create fresh root slice asset.
|
|
CreateRootSlice();
|
|
|
|
EntityOwnershipServiceNotificationBus::Event(m_entityContextId, &EntityOwnershipServiceNotificationBus::Events::OnEntityOwnershipServiceReset);
|
|
}
|
|
}
|
|
|
|
void SliceEntityOwnershipService::AddEntity(AZ::Entity* entity)
|
|
{
|
|
AZ_Assert(m_rootAsset && m_rootAsset->GetComponent(), "Root slice has not been created.");
|
|
m_rootAsset->GetComponent()->AddEntity(entity);
|
|
HandleEntitiesAdded(EntityList{ entity });
|
|
}
|
|
|
|
void SliceEntityOwnershipService::AddEntities(const EntityList& entities)
|
|
{
|
|
for (AZ::Entity* entity : entities)
|
|
{
|
|
AZ_Assert(!AzFramework::SliceEntityRequestBus::MultiHandler::BusIsConnectedId(entity->GetId()),
|
|
"Entity already present.");
|
|
GetRootAsset()->GetComponent()->AddEntity(entity);
|
|
}
|
|
|
|
HandleEntitiesAdded(entities);
|
|
}
|
|
|
|
bool SliceEntityOwnershipService::DestroyEntity(AZ::Entity* entity)
|
|
{
|
|
if (entity)
|
|
{
|
|
AZ_Assert(m_rootAsset && m_rootAsset->GetComponent(), "Root slice has not been created.");
|
|
SliceEntityRequestBus::MultiHandler::BusDisconnect(entity->GetId());
|
|
m_entitiesRemovedCallback({ entity->GetId() });
|
|
return m_rootAsset->GetComponent()->RemoveEntity(entity);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SliceEntityOwnershipService::DestroyEntityById(AZ::EntityId entityId)
|
|
{
|
|
AZ_Assert(m_rootAsset && m_rootAsset->GetComponent(), "Root slice has not been created.");
|
|
AZ_Assert(m_entitiesRemovedCallback, "Callback function for DestroyEntityById has not been set.");
|
|
m_entitiesRemovedCallback({ entityId });
|
|
|
|
// Entities removed through the application (as in via manual 'delete'),
|
|
// should be removed from the root slice, but not again deleted.
|
|
return m_rootAsset->GetComponent()->RemoveEntity(entityId, false);
|
|
}
|
|
|
|
void SliceEntityOwnershipService::CreateRootSlice()
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzFramework);
|
|
|
|
AZ_Assert(m_rootAsset && m_rootAsset.Get(), "Root slice asset has not been created yet.");
|
|
|
|
CreateRootSlice(m_rootAsset.Get());
|
|
}
|
|
|
|
void SliceEntityOwnershipService::CreateRootSlice(AZ::SliceAsset* rootSliceAsset)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzFramework);
|
|
AZ_Assert(m_rootAsset && m_rootAsset.Get(), "Root slice asset has not been created yet.");
|
|
|
|
AZ::Entity* rootEntity = new AZ::Entity();
|
|
rootEntity->CreateComponent<AZ::SliceComponent>();
|
|
|
|
// Manually create an asset to hold the root slice.
|
|
rootSliceAsset->SetData(rootEntity, rootEntity->FindComponent<AZ::SliceComponent>());
|
|
AZ::SliceComponent* rootSliceComponent = rootSliceAsset->GetComponent();
|
|
rootSliceComponent->InitMetadata();
|
|
rootSliceComponent->SetMyAsset(rootSliceAsset);
|
|
rootSliceComponent->SetSerializeContext(m_serializeContext);
|
|
rootSliceComponent->ListenForAssetChanges();
|
|
|
|
// Root slice is always dynamic by default. Whether it's a "level",
|
|
// or something else, it can be instantiated at runtime.
|
|
rootSliceComponent->SetIsDynamic(true);
|
|
|
|
// Make sure the root slice metadata entity is marked as persistent.
|
|
AZ::Entity* metadataEntity = rootSliceComponent->GetMetadataEntity();
|
|
|
|
if (metadataEntity)
|
|
{
|
|
AZ::SliceMetadataInfoComponent* infoComponent = metadataEntity->FindComponent<AZ::SliceMetadataInfoComponent>();
|
|
|
|
if (infoComponent)
|
|
{
|
|
infoComponent->MarkAsPersistent(true);
|
|
}
|
|
HandleNewMetadataEntitiesCreated(*rootSliceComponent);
|
|
}
|
|
}
|
|
|
|
AZ::SliceComponent* SliceEntityOwnershipService::GetRootSlice()
|
|
{
|
|
return m_rootAsset ? m_rootAsset->GetComponent() : nullptr;
|
|
}
|
|
|
|
EntityList SliceEntityOwnershipService::GetRootSliceEntities()
|
|
{
|
|
EntityList entities;
|
|
|
|
const AZ::SliceComponent* rootSliceComponent = m_rootAsset->GetComponent();
|
|
|
|
AZ_Assert(rootSliceComponent, "Root slice component has not been created.");
|
|
if (!rootSliceComponent->IsInstantiated())
|
|
{
|
|
AZ_Assert(false, "Root slice has not been instantiated yet");
|
|
return entities;
|
|
}
|
|
|
|
const EntityList& looseEntities = rootSliceComponent->GetNewEntities();
|
|
|
|
entities.reserve(looseEntities.size());
|
|
for (AZ::Entity* entity : looseEntities)
|
|
{
|
|
entities.push_back(entity);
|
|
}
|
|
|
|
const AZ::SliceComponent::SliceList& subSlices = rootSliceComponent->GetSlices();
|
|
for (const AZ::SliceComponent::SliceReference& subSlice : subSlices)
|
|
{
|
|
for (const AZ::SliceComponent::SliceInstance& instance : subSlice.GetInstances())
|
|
{
|
|
for (AZ::Entity* entity : instance.GetInstantiated()->m_entities)
|
|
{
|
|
entities.push_back(entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
return entities;
|
|
}
|
|
|
|
bool SliceEntityOwnershipService::LoadFromStream(AZ::IO::GenericStream& stream, bool remapIds, EntityIdToEntityIdMap* idRemapTable, const AZ::ObjectStream::FilterDescriptor& filterDesc)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzFramework);
|
|
|
|
AZ_Assert(m_rootAsset, "The entity ownership service has not been initialized.");
|
|
|
|
AZ::Entity* newRootEntity = AZ::Utils::LoadObjectFromStream<AZ::Entity>(stream, m_serializeContext, filterDesc);
|
|
|
|
// Make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of
|
|
// (load asset) -> (notify) -> (init) -> (activate)
|
|
AZ::Data::AssetManager::Instance().DispatchEvents();
|
|
|
|
// For other kinds of instantiations, like slice instantiations, becuase they use the queued slice instantiation mechanism,
|
|
// they will always be instantiated after their asset is already ready.
|
|
|
|
return HandleRootEntityReloadedFromStream(newRootEntity, remapIds, idRemapTable);
|
|
}
|
|
|
|
bool SliceEntityOwnershipService::HandleRootEntityReloadedFromStream(AZ::Entity* rootEntity, bool remapIds,
|
|
AZ::SliceComponent::EntityIdToEntityIdMap* idRemapTable)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzFramework);
|
|
|
|
if (!rootEntity)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Flush asset database events after serialization, so all loaded asset statuses are updated.
|
|
if (AZ::Data::AssetManager::IsReady())
|
|
{
|
|
AZ::Data::AssetManager::Instance().DispatchEvents();
|
|
}
|
|
|
|
AZ::SliceComponent* newRootSlice = rootEntity->FindComponent<AZ::SliceComponent>();
|
|
|
|
if (!newRootSlice)
|
|
{
|
|
AZ_Error("SliceEntityOwnershipService", false, "Loaded root entity is not a slice.");
|
|
return false;
|
|
}
|
|
|
|
Reset();
|
|
|
|
AZ::SliceAsset* rootSlice = m_rootAsset.Get();
|
|
rootSlice->SetData(rootEntity, newRootSlice, true);
|
|
newRootSlice->SetMyAsset(rootSlice);
|
|
newRootSlice->SetSerializeContext(m_serializeContext);
|
|
newRootSlice->ListenForAssetChanges();
|
|
|
|
m_loadedEntityIdMap.clear();
|
|
if (remapIds)
|
|
{
|
|
newRootSlice->GenerateNewEntityIds(&m_loadedEntityIdMap);
|
|
if (idRemapTable)
|
|
{
|
|
*idRemapTable = m_loadedEntityIdMap;
|
|
}
|
|
}
|
|
|
|
|
|
AZ::SliceComponent::EntityList entities;
|
|
newRootSlice->GetEntities(entities);
|
|
|
|
if (!remapIds)
|
|
{
|
|
for (AZ::Entity* entity : entities)
|
|
{
|
|
m_loadedEntityIdMap.emplace(entity->GetId(), entity->GetId());
|
|
}
|
|
}
|
|
|
|
// Make sure the root slice metadata entity is marked as persistent.
|
|
AZ::Entity* metadataEntity = newRootSlice->GetMetadataEntity();
|
|
|
|
if (!metadataEntity)
|
|
{
|
|
AZ_Error("SliceEntityOwnershipService", false, "Root entity must have a metadata entity");
|
|
return false;
|
|
}
|
|
|
|
AZ::SliceMetadataInfoComponent* infoComponent = metadataEntity->FindComponent<AZ::SliceMetadataInfoComponent>();
|
|
|
|
if (!infoComponent)
|
|
{
|
|
AZ_Error("SliceEntityOwnershipService", false, "Root metadata entity must have a valid info component");
|
|
return false;
|
|
}
|
|
|
|
infoComponent->MarkAsPersistent(true);
|
|
|
|
EntityOwnershipServiceNotificationBus::Event(m_entityContextId,
|
|
&EntityOwnershipServiceNotificationBus::Events::OnEntitiesReloadedFromStream, entities);
|
|
|
|
HandleEntitiesAdded(entities);
|
|
|
|
HandleNewMetadataEntitiesCreated(*newRootSlice);
|
|
|
|
AZ::Data::AssetBus::MultiHandler::BusConnect(m_rootAsset->GetId());
|
|
return true;
|
|
}
|
|
|
|
SliceInstantiationTicket SliceEntityOwnershipService::GenerateSliceInstantiationTicket()
|
|
{
|
|
return SliceInstantiationTicket(m_entityContextId, ++m_nextSliceTicketId);
|
|
}
|
|
|
|
void SliceEntityOwnershipService::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
|
{
|
|
if (asset == m_rootAsset)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
|
|
|
|
for (auto iter = m_queuedSliceInstantiations.begin(); iter != m_queuedSliceInstantiations.end(); )
|
|
{
|
|
const InstantiatingSliceInfo& instantiating = *iter;
|
|
|
|
if (instantiating.m_asset.GetId() == asset.GetId())
|
|
{
|
|
// grab a refcount on the asset and copy the ticket, as 'instantiating' is about to be destroyed!
|
|
AZ::Data::AssetId cachedId = instantiating.m_asset.GetId();
|
|
SliceInstantiationTicket ticket = instantiating.m_ticket;
|
|
|
|
AZStd::function<void()> notifyCallback =
|
|
[cachedId, ticket]() // capture these by value since we're about to leave the scope in which these variables exist.
|
|
{
|
|
DispatchOnSliceInstantiationFailed(ticket, cachedId, false);
|
|
};
|
|
|
|
// Instantiation is queued against the tick bus. This ensures we're not holding the AssetBus lock
|
|
// while the instantiation is handled, which may be costly.
|
|
AZ::TickBus::QueueFunction(notifyCallback);
|
|
|
|
iter = m_queuedSliceInstantiations.erase(iter); // this invalidates the instantiating data.
|
|
}
|
|
else
|
|
{
|
|
++iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SliceEntityOwnershipService::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> readyAsset)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzFramework);
|
|
|
|
AZ_Assert(readyAsset.GetAs<AZ::SliceAsset>(), "Asset is not a slice!");
|
|
|
|
if (readyAsset == m_rootAsset)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ::Data::AssetBus::MultiHandler::BusDisconnect(readyAsset.GetId());
|
|
|
|
// we intentionally capture readyAsset by value here, so that its refcount doesn't hit 0 by the time this call happens.
|
|
AZStd::function<void()> instantiateCallback = [this, readyAsset]()
|
|
{
|
|
const AZ::Data::AssetId readyAssetId = readyAsset.GetId();
|
|
for (auto iter = m_queuedSliceInstantiations.begin(); iter != m_queuedSliceInstantiations.end(); )
|
|
{
|
|
const InstantiatingSliceInfo& instantiating = *iter;
|
|
|
|
if (instantiating.m_asset.GetId() == readyAssetId)
|
|
{
|
|
// here we actually refcount / copy by value the internals of 'instantiating' since we will destroy it later
|
|
// but still wish to send bus messages based on ticket/asset.
|
|
AZ::Data::Asset<AZ::Data::AssetData> asset = instantiating.m_asset;
|
|
SliceInstantiationTicket ticket = instantiating.m_ticket;
|
|
m_instantiatingAssetId = instantiating.m_asset.GetId();
|
|
AZ::SliceComponent::SliceInstanceAddress instance = m_rootAsset->GetComponent()->
|
|
AddSlice(asset, instantiating.m_customMapper);
|
|
|
|
// Its important to remove this instantiation from the instantiation list
|
|
// as soon as possible, before we call these below notification functions, because they might result in our
|
|
// own functions that search this list being called again.
|
|
iter = m_queuedSliceInstantiations.erase(iter);
|
|
// --------------------------- do not refer to 'instantiating' after the above call, it has been destroyed ------------
|
|
|
|
bool isSliceInstantiated = false;
|
|
|
|
if (instance.IsValid())
|
|
{
|
|
AZ_Assert(instance.GetInstance()->GetInstantiated(), "Failed to instantiate root slice!");
|
|
|
|
if (instance.GetInstance()->GetInstantiated() &&
|
|
m_validateEntitiesCallback(instance.GetInstance()->GetInstantiated()->m_entities))
|
|
{
|
|
SliceInstantiationResultBus::Event(ticket, &SliceInstantiationResultBus::Events::OnSlicePreInstantiate,
|
|
m_instantiatingAssetId, instance);
|
|
|
|
HandleEntitiesAdded(instance.GetInstance()->GetInstantiated()->m_entities);
|
|
|
|
SliceInstantiationResultBus::Event(ticket, &SliceInstantiationResultBus::Events::OnSliceInstantiated,
|
|
m_instantiatingAssetId, instance);
|
|
|
|
isSliceInstantiated = true;
|
|
}
|
|
else
|
|
{
|
|
// The slice has already been added to the root slice. But we are disallowing the
|
|
// instantiation. So we need to remove it
|
|
m_rootAsset->GetComponent()->RemoveSliceInstance(instance);
|
|
}
|
|
}
|
|
|
|
if (!isSliceInstantiated)
|
|
{
|
|
DispatchOnSliceInstantiationFailed(ticket, m_instantiatingAssetId, false);
|
|
}
|
|
|
|
// clear the Asset ID cache
|
|
m_instantiatingAssetId.SetInvalid();
|
|
}
|
|
else
|
|
{
|
|
++iter;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Instantiation is queued against the tick bus. This ensures we're not holding the AssetBus lock
|
|
// while the instantiation is handled, which may be costly. This also guarantees callers can
|
|
// jump on the SliceInstantiationResultBus for their ticket before the events are fired.
|
|
AZ::TickBus::QueueFunction(instantiateCallback);
|
|
}
|
|
|
|
void SliceEntityOwnershipService::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzFramework);
|
|
if (asset == m_rootAsset && asset.Get() != m_rootAsset.Get())
|
|
{
|
|
Reset();
|
|
|
|
m_rootAsset = asset;
|
|
|
|
auto* rootSliceComponent = m_rootAsset->GetComponent();
|
|
|
|
// Because cloned components don't listen for changes by default as they are usually discarded,
|
|
// we need to manually listen here - root is special in this way
|
|
rootSliceComponent->ListenForAssetChanges();
|
|
|
|
HandleNewMetadataEntitiesCreated(*m_rootAsset->GetComponent());
|
|
|
|
AZ::SliceComponent::EntityList entities;
|
|
m_rootAsset->GetComponent()->GetEntities(entities);
|
|
|
|
HandleEntitiesAdded(entities);
|
|
|
|
m_rootAsset->GetComponent()->ListenForDependentAssetChanges();
|
|
}
|
|
}
|
|
|
|
SliceInstantiationTicket SliceEntityOwnershipService::InstantiateSlice(const AZ::Data::Asset<AZ::Data::AssetData>& asset,
|
|
const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& customIdMapper, const AZ::Data::AssetFilterCB& assetLoadFilter)
|
|
{
|
|
if (asset.GetId().IsValid())
|
|
{
|
|
const SliceInstantiationTicket ticket = GenerateSliceInstantiationTicket();
|
|
m_queuedSliceInstantiations.emplace_back(asset, ticket, customIdMapper);
|
|
m_queuedSliceInstantiations.back().m_asset.QueueLoad(AZ::Data::AssetLoadParameters(assetLoadFilter));
|
|
|
|
AZ::Data::AssetBus::MultiHandler::BusConnect(asset.GetId());
|
|
|
|
return ticket;
|
|
}
|
|
|
|
return SliceInstantiationTicket();
|
|
}
|
|
|
|
void SliceEntityOwnershipService::CancelSliceInstantiation(const SliceInstantiationTicket& ticket)
|
|
{
|
|
auto iter = AZStd::find_if(m_queuedSliceInstantiations.begin(), m_queuedSliceInstantiations.end(),
|
|
[ticket](const InstantiatingSliceInfo& instantiating)
|
|
{
|
|
return instantiating.m_ticket == ticket;
|
|
});
|
|
|
|
if (iter != m_queuedSliceInstantiations.end())
|
|
{
|
|
const AZ::Data::AssetId assetId = iter->m_asset.GetId();
|
|
|
|
// Erase ticket, but stay connected to AssetBus in case asset is used by multiple tickets.
|
|
m_queuedSliceInstantiations.erase(iter);
|
|
|
|
// Clear the iterator so that code inserted after this point to operate on iter will raise issues.
|
|
iter = m_queuedSliceInstantiations.end();
|
|
|
|
// No need to queue this notification.
|
|
// (It's queued in other circumstances, to avoid holding the AssetBus lock any longer than necessary)
|
|
DispatchOnSliceInstantiationFailed(ticket, assetId, true);
|
|
}
|
|
}
|
|
|
|
void SliceEntityOwnershipService::DispatchOnSliceInstantiationFailed(const SliceInstantiationTicket& ticket,
|
|
const AZ::Data::AssetId& assetId, bool canceled)
|
|
{
|
|
SliceInstantiationResultBus::Event(ticket, &SliceInstantiationResultBus::Events::OnSliceInstantiationFailed, assetId);
|
|
SliceInstantiationResultBus::Event(ticket, &SliceInstantiationResultBus::Events::OnSliceInstantiationFailedOrCanceled,
|
|
assetId, canceled);
|
|
}
|
|
|
|
AZ::SliceComponent::SliceInstanceAddress SliceEntityOwnershipService::CloneSliceInstance(
|
|
AZ::SliceComponent::SliceInstanceAddress sourceInstance, AZ::SliceComponent::EntityIdToEntityIdMap& sourceToCloneEntityIdMap)
|
|
{
|
|
AZ_PROFILE_FUNCTION(AzFramework);
|
|
|
|
AZ_Assert(sourceInstance.IsValid(), "Source slice instance is invalid.");
|
|
|
|
AZ::SliceComponent::SliceInstance* newInstance = sourceInstance.GetReference()->CloneInstance(sourceInstance.GetInstance(),
|
|
sourceToCloneEntityIdMap);
|
|
|
|
return AZ::SliceComponent::SliceInstanceAddress(sourceInstance.GetReference(), newInstance);
|
|
}
|
|
|
|
AZ::SliceComponent::SliceInstanceAddress SliceEntityOwnershipService::GetOwningSlice()
|
|
{
|
|
const AZ::EntityId entityId = *SliceEntityRequestBus::GetCurrentBusId();
|
|
return GetOwningSlice(entityId);
|
|
}
|
|
|
|
AZ::SliceComponent::SliceInstanceAddress SliceEntityOwnershipService::GetOwningSlice(AZ::EntityId entityId)
|
|
{
|
|
AZ_Assert(m_rootAsset && m_rootAsset->GetComponent(), "The entity ownership service has not been initialized.");
|
|
return m_rootAsset->GetComponent()->FindSlice(entityId);
|
|
}
|
|
|
|
const AZ::SliceComponent::EntityIdToEntityIdMap& SliceEntityOwnershipService::GetLoadedEntityIdMap()
|
|
{
|
|
return m_loadedEntityIdMap;
|
|
}
|
|
|
|
AZ::EntityId SliceEntityOwnershipService::FindLoadedEntityIdMapping(const AZ::EntityId& staticId) const
|
|
{
|
|
auto idIter = m_loadedEntityIdMap.find(staticId);
|
|
|
|
if (idIter == m_loadedEntityIdMap.end())
|
|
{
|
|
return AZ::EntityId();
|
|
}
|
|
|
|
return idIter->second;
|
|
}
|
|
|
|
void SliceEntityOwnershipService::HandleEntitiesAdded(const EntityList& entities)
|
|
{
|
|
AZ_Assert(m_entitiesAddedCallback, "Callback function for AddEntity has not been set.");
|
|
for (const AZ::Entity* entity : entities)
|
|
{
|
|
SliceEntityRequestBus::MultiHandler::BusConnect(entity->GetId());
|
|
}
|
|
|
|
m_entitiesAddedCallback(entities);
|
|
}
|
|
|
|
void SliceEntityOwnershipService::GetNonPrefabEntities(EntityList& entityList)
|
|
{
|
|
AZ::SliceComponent* rootSliceComponent = GetRootSlice();
|
|
AZ_Error("SliceEntityOwnershipService", rootSliceComponent, "Root slice is not available.");
|
|
if (rootSliceComponent)
|
|
{
|
|
const EntityList& newEntities = rootSliceComponent->GetNewEntities();
|
|
entityList.insert(entityList.end(), newEntities.cbegin(), newEntities.cend());
|
|
}
|
|
}
|
|
|
|
bool SliceEntityOwnershipService::GetAllEntities(EntityList& entityList)
|
|
{
|
|
AZ::SliceComponent* rootSliceComponent = GetRootSlice();
|
|
if (rootSliceComponent)
|
|
{
|
|
return rootSliceComponent->GetEntities(entityList);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SliceEntityOwnershipService::InstantiateAllPrefabs()
|
|
{
|
|
AZ::SliceComponent* rootSliceComponent = GetRootSlice();
|
|
if (rootSliceComponent)
|
|
{
|
|
// Instantiating the root slice would in-turn instantiate all slices under it.
|
|
rootSliceComponent->Instantiate();
|
|
}
|
|
}
|
|
|
|
void SliceEntityOwnershipService::SetIsDynamic(bool isDynamic)
|
|
{
|
|
AZ::SliceComponent* rootSliceComponent = GetRootSlice();
|
|
if (rootSliceComponent)
|
|
{
|
|
rootSliceComponent->SetIsDynamic(isDynamic);
|
|
}
|
|
}
|
|
|
|
AZ::SerializeContext* SliceEntityOwnershipService::GetSerializeContext()
|
|
{
|
|
return m_serializeContext;
|
|
}
|
|
|
|
const RootSliceAsset& SliceEntityOwnershipService::GetRootAsset() const
|
|
{
|
|
return m_rootAsset;
|
|
}
|
|
|
|
void SliceEntityOwnershipService::SetEntitiesAddedCallback(OnEntitiesAddedCallback onEntitiesAddedCallback)
|
|
{
|
|
m_entitiesAddedCallback = AZStd::move(onEntitiesAddedCallback);
|
|
}
|
|
|
|
void SliceEntityOwnershipService::SetEntitiesRemovedCallback(OnEntitiesRemovedCallback onEntitiesRemovedCallback)
|
|
{
|
|
m_entitiesRemovedCallback = AZStd::move(onEntitiesRemovedCallback);
|
|
}
|
|
|
|
void SliceEntityOwnershipService::SetValidateEntitiesCallback(ValidateEntitiesCallback validateEntitiesCallback)
|
|
{
|
|
m_validateEntitiesCallback = AZStd::move(validateEntitiesCallback);
|
|
}
|
|
|
|
AZ::Data::AssetId SliceEntityOwnershipService::CurrentlyInstantiatingSlice()
|
|
{
|
|
return m_instantiatingAssetId;
|
|
}
|
|
}
|