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.
o3de/Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceMetadataEntityContextC...

305 lines
13 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "AzToolsFramework_precompiled.h"
#include <AzCore/Component/Entity.h>
#include <AzCore/Slice/SliceMetadataInfoBus.h>
#include <AzToolsFramework/Commands/PreemptiveUndoCache.h>
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.h>
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
#include "SliceMetadataEntityContextComponent.h"
namespace AzToolsFramework
{
/*! Default Constructor
*/
SliceMetadataEntityContextComponent::SliceMetadataEntityContextComponent()
: AzFramework::EntityContext(AzFramework::EntityContextId::CreateRandom())
, m_requiredSliceMetadataComponentTypes
// These are the components that will be force added to every slice metadata entity
({
azrtti_typeid<Components::EditorEntitySortComponent>(),
azrtti_typeid<AzToolsFramework::Components::EditorPendingCompositionComponent>(),
azrtti_typeid<AzToolsFramework::Components::EditorDisabledCompositionComponent>(),
azrtti_typeid<AzToolsFramework::Components::EditorInspectorComponent>()
})
{
}
/*! Provides basic reflection properties for the serialization and edit reflection contexts
\param context A context provided by the reflection system
*/
void SliceMetadataEntityContextComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SliceMetadataEntityContextComponent, AZ::Component>();
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<SliceMetadataEntityContextComponent>(
"Slice Metadata Entity Context", "System component responsible for owning the slice metadata entity context")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Editor")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
;
}
}
}
/*! Called when the context is activated.
Creates the root slice and establishes connections to necessary eBuses.
*/
void SliceMetadataEntityContextComponent::Activate()
{
m_entityOwnershipService = AZStd::make_unique<AzFramework::SliceEntityOwnershipService>(GetContextId(), GetSerializeContext());
InitContext();
SliceMetadataEntityContextRequestBus::Handler::BusConnect();
AZ::SliceInstanceNotificationBus::Handler::BusConnect();
}
/*! Called when the context is deactivated.
Removes all entities, resets the root slice, and disconnects from the root asset.
*/
void SliceMetadataEntityContextComponent::Deactivate()
{
SliceMetadataEntityContextRequestBus::Handler::BusDisconnect();
AZ::SliceInstanceNotificationBus::Handler::BusDisconnect();
DestroyContext();
}
/*! Retrieves the ID of this Slice Metadata Entity Context.
/return The ID of this context.
*/
AzFramework::EntityContextId SliceMetadataEntityContextComponent::GetSliceMetadataEntityContextId()
{
return GetContextId();
}
/*! Called to reset the current context.
Destroys all entities owned by the context and replaces the root slice with a new, empty one.
*/
void SliceMetadataEntityContextComponent::ResetContext()
{
// Because metadata entities are owned by the slices they're associated with, we can just
// clear our association maps.
m_editorEntityToMetadataEntityMap.clear();
m_metadataEntityByIdMap.clear();
m_sliceAddressToRootMetadataMap.clear();
EBUS_EVENT(SliceMetadataEntityContextNotificationBus, OnContextReset);
}
/*! Called to reset the current context.
Destroys all entities owned by the context and replaces the root slice with a new, empty one.
\return True for success
*/
bool SliceMetadataEntityContextComponent::IsSliceMetadataEntity(AZ::EntityId entityId)
{
return nullptr != GetMetadataEntity(entityId);
}
AZ::Entity* SliceMetadataEntityContextComponent::GetMetadataEntity(const AZ::EntityId entityId)
{
auto metadataEntityByIdIter = m_metadataEntityByIdMap.find(entityId);
if (metadataEntityByIdIter != m_metadataEntityByIdMap.end())
{
return metadataEntityByIdIter->second;
}
return nullptr;
}
AZ::EntityId SliceMetadataEntityContextComponent::GetMetadataEntityIdFromEditorEntity(const AZ::EntityId editorEntityId)
{
auto associationMapIter = m_editorEntityToMetadataEntityMap.find(editorEntityId);
if (associationMapIter != m_editorEntityToMetadataEntityMap.end())
{
return associationMapIter->second;
}
return AZ::EntityId();
}
/*! Get the ID of the slice metadata entity associated with the given slice instance address.
/param address A Slice Instance Address
/return A Component Type List of all the components that need to be added to entities in the Slice Metadata Entity Context
*/
AZ::EntityId SliceMetadataEntityContextComponent::GetMetadataEntityIdFromSliceAddress(const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
{
auto sliceAddressToRootMetadataIter = m_sliceAddressToRootMetadataMap.find(sliceAddress);
AZ_Assert(sliceAddressToRootMetadataIter != m_sliceAddressToRootMetadataMap.end(), "Metadata Entity For Slice Address Not Found");
if (sliceAddressToRootMetadataIter != m_sliceAddressToRootMetadataMap.end())
{
return sliceAddressToRootMetadataIter->second;
}
return AZ::EntityId();
}
/// Adds a slice metadata entity to the context
void SliceMetadataEntityContextComponent::AddMetadataEntityToContext(const AZ::SliceComponent::SliceInstanceAddress& sliceAddress, AZ::Entity& metadataEntity)
{
// If this fires for legit reasons, we may need to deactivate any active entities first. Right now it is assumed they are not already active.
AZ_Assert(metadataEntity.GetState() < AZ::Entity::State::Active, "Unable to add required components to already active entities");
AddRequiredComponents(metadataEntity);
if (metadataEntity.GetState() == AZ::Entity::State::Constructed)
{
metadataEntity.Init();
}
if (metadataEntity.GetState() == AZ::Entity::State::Init)
{
metadataEntity.Activate();
}
AZ_Assert(metadataEntity.GetState() == AZ::Entity::State::Active, "Metadata Entity Failed To Activate");
// All metadata entities created should have a metadata association component
AZStd::set<AZ::EntityId> associatedEntities;
AZ::SliceMetadataInfoRequestBus::Event(metadataEntity.GetId(), &AZ::SliceMetadataInfoRequestBus::Events::GetAssociatedEntities, associatedEntities);
for (const auto& editorEntityId : associatedEntities)
{
m_editorEntityToMetadataEntityMap[editorEntityId] = metadataEntity.GetId();
}
// If the metadata entity has no parent, we can assume it's the root entity in the slice hierarchy belonging to the slice address provided
AZ::EntityId parentMetadataId;
AZ::SliceMetadataInfoRequestBus::EventResult(parentMetadataId, metadataEntity.GetId(), &AZ::SliceMetadataInfoRequestBus::Events::GetParentId);
if (!parentMetadataId.IsValid())
{
m_sliceAddressToRootMetadataMap[sliceAddress] = metadataEntity.GetId();
}
m_metadataEntityByIdMap[metadataEntity.GetId()] = &metadataEntity;
AzFramework::EntityIdContextQueryBus::MultiHandler::BusConnect(metadataEntity.GetId());
AZ::SliceMetadataInfoNotificationBus::MultiHandler::BusConnect(metadataEntity.GetId());
SliceMetadataEntityContextNotificationBus::Broadcast(&SliceMetadataEntityContextNotifications::OnMetadataEntityAdded, metadataEntity.GetId());
// Register the metadata entity with the pre-emptive undo cache (if exists) so it has an initial state
auto undoCacheInterface = AZ::Interface<UndoSystem::UndoCacheInterface>::Get();
if (undoCacheInterface)
{
undoCacheInterface->UpdateCache(metadataEntity.GetId());
}
}
void SliceMetadataEntityContextComponent::OnMetadataEntityCreated(const AZ::SliceComponent::SliceInstanceAddress& sliceAddress, AZ::Entity& entity)
{
AddMetadataEntityToContext(sliceAddress, entity);
}
void SliceMetadataEntityContextComponent::OnMetadataEntityDestroyed(const AZ::EntityId entityId)
{
RemoveMetadataEntityFromContext(entityId);
}
void SliceMetadataEntityContextComponent::OnMetadataDependenciesRemoved()
{
RemoveMetadataEntityFromContext(*AZ::SliceMetadataInfoNotificationBus::GetCurrentBusId());
}
/*! Get a list of required component types.
Entities added to this context require a number of components by default. This returns a list of those required components.
/return A Component Type List of all the components that need to be added to entities in the Slice Metadata Entity Context
*/
void SliceMetadataEntityContextComponent::GetRequiredComponentTypes(AZ::ComponentTypeList& required)
{
required.insert(required.end(), m_requiredSliceMetadataComponentTypes.begin(), m_requiredSliceMetadataComponentTypes.end());
}
/*! Fills out a list of the component services provided by this context
*/
void SliceMetadataEntityContextComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SliceMetadataEntityContextService", 0xa98f0bf9));
}
/*! Fills out a list of the component services incompatible with this context
*/
void SliceMetadataEntityContextComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("SliceMetadataEntityContextService", 0xa98f0bf9));
}
/*! Fills out a list of the component services required for this context to function
*/
void SliceMetadataEntityContextComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC("AssetDatabaseService", 0x3abf5601));
}
/*! Adds all the required components for this context to the given entity if they don't already exist.
\param entity The entity to add components to
*/
void SliceMetadataEntityContextComponent::AddRequiredComponents(AZ::Entity& metadataEntity)
{
for (const auto& componentType : m_requiredSliceMetadataComponentTypes)
{
if (!metadataEntity.FindComponent(componentType))
{
metadataEntity.CreateComponent(componentType);
}
}
}
void SliceMetadataEntityContextComponent::RemoveMetadataEntityFromContext(const AZ::EntityId entityId)
{
auto metadataEntityByIdIter = m_metadataEntityByIdMap.find(entityId);
if (metadataEntityByIdIter == m_metadataEntityByIdMap.end())
{
return;
}
AzFramework::EntityIdContextQueryBus::MultiHandler::BusDisconnect(entityId);
// Clean up our quick-lookup maps
// Find our entry in the slice address map and remove it.
for (auto sliceAddressToRootMetadataIter = m_sliceAddressToRootMetadataMap.begin(); sliceAddressToRootMetadataIter != m_sliceAddressToRootMetadataMap.end();)
{
if (sliceAddressToRootMetadataIter->second == entityId)
{
m_sliceAddressToRootMetadataMap.erase(sliceAddressToRootMetadataIter);
break;
}
++sliceAddressToRootMetadataIter;
}
// Remove all association entries for the given metadata entity ID.
for (auto editorEntityToMetadataEntityIter = m_editorEntityToMetadataEntityMap.begin(); editorEntityToMetadataEntityIter != m_editorEntityToMetadataEntityMap.end();)
{
if (editorEntityToMetadataEntityIter->second == entityId)
{
editorEntityToMetadataEntityIter = m_editorEntityToMetadataEntityMap.erase(editorEntityToMetadataEntityIter);
continue;
}
++editorEntityToMetadataEntityIter;
}
SliceMetadataEntityContextNotificationBus::Broadcast(&SliceMetadataEntityContextNotifications::OnMetadataEntityRemoved, entityId);
m_metadataEntityByIdMap.erase(metadataEntityByIdIter);
}
} // namespace AzToolsFramework