Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,566 @@
/*
* 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 "BehaviorEntity.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////
// BehaviorComponentId
void BehaviorComponentId::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<BehaviorComponentId>()
->Version(1)
->Field("ComponentId", &BehaviorComponentId::m_id)
;
serializeContext->RegisterGenericType<AZStd::vector<BehaviorComponentId>>();
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<BehaviorComponentId>("ComponentId")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Constructor()
->Method("IsValid", &BehaviorComponentId::IsValid)
->Method("Equal", &BehaviorComponentId::operator==)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
->Method("ToString", &BehaviorComponentId::ToString)
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
;
}
}
BehaviorComponentId::BehaviorComponentId(AZ::ComponentId id)
: m_id(id)
{
}
BehaviorComponentId::operator AZ::ComponentId() const
{
return m_id;
}
bool BehaviorComponentId::operator==(const BehaviorComponentId& rhs) const
{
return m_id == rhs.m_id;
}
bool BehaviorComponentId::IsValid() const
{
return m_id != AZ::InvalidComponentId;
}
AZStd::string BehaviorComponentId::ToString() const
{
return AZStd::string::format("[%llu]", m_id);
}
////////////////////////////////////////////////////////////////////////////
// BehaviorEntity
namespace Internal
{
void BehaviorEntityScriptConstructor(BehaviorEntity* self, AZ::ScriptDataContext& dc)
{
if (dc.GetNumArguments() == 0)
{
*self = BehaviorEntity();
return;
}
else if (dc.GetNumArguments() == 1)
{
if (dc.IsClass<AZ::EntityId>(0))
{
AZ::EntityId entityId;
dc.ReadArg(0, entityId);
new(self) BehaviorEntity(entityId);
return;
}
else if (dc.IsNil(0))
{
new(self) BehaviorEntity(nullptr);
return;
}
// Constructor taking AZ::Entity* isn't supported.
// AZ::Entity is not exposed to BehaviorContext, so we can't detect args of that type.
}
dc.GetScriptContext()->Error(AZ::ScriptContext::ErrorType::Error, true, "Invalid arguments passed to BehaviorEntity().");
new(self) BehaviorEntity();
}
const char* GetComponentName(const AZ::TypeId& componentTypeId)
{
AZ::ComponentDescriptor* descriptor = nullptr;
AZ::ComponentDescriptorBus::EventResult(descriptor, componentTypeId, &AZ::ComponentDescriptorBus::Events::GetDescriptor);
return descriptor ? descriptor->GetName() : "<unknown>";
}
}
void BehaviorEntity::Reflect(AZ::ReflectContext* context)
{
BehaviorComponentId::Reflect(context);
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<BehaviorEntity>()
->Field("EntityId", &BehaviorEntity::m_entityId)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<BehaviorEntity>("Entity", "Entity")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &BehaviorEntity::m_entityId, "EntityId", "")
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<BehaviorEntity>("Entity")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::BehaviorEntityScriptConstructor)
->Constructor()
->Constructor<AZ::EntityId>()
->Constructor<AZ::Entity*>()
->Method("GetName", &BehaviorEntity::GetName)
->Method("SetName", &BehaviorEntity::SetName)
->Method("GetId", &BehaviorEntity::GetId)
->Method("GetOwningContextId", &BehaviorEntity::GetOwningContextId)
->Method("IsValid", &BehaviorEntity::IsValid)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::All)
->Method("Exists", &BehaviorEntity::Exists)
->Method("IsActivated", &BehaviorEntity::IsActivated)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::All)
->Method("Activate", &BehaviorEntity::Activate)
->Method("Deactivate", &BehaviorEntity::Deactivate)
->Method("CreateComponent", &BehaviorEntity::CreateComponent, behaviorContext->MakeDefaultValues(static_cast<const AZ::ComponentConfig*>(nullptr)))
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::List)
->Method("DestroyComponent", &BehaviorEntity::DestroyComponent)
->Method("GetComponents", &BehaviorEntity::GetComponents)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::All)
->Method("FindComponentOfType", &BehaviorEntity::FindComponentOfType)
->Method("FindAllComponentsOfType", &BehaviorEntity::FindAllComponentsOfType)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::All)
->Method("GetComponentType", &BehaviorEntity::GetComponentType)
->Method("GetComponentName", &BehaviorEntity::GetComponentName)
->Method("SetComponentConfiguration", &BehaviorEntity::SetComponentConfiguration)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::List)
->Method("GetComponentConfiguration", &BehaviorEntity::GetComponentConfiguration)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::List)
// Allow BehaviorEntity to be passed to functions expecting AZ::Entity*
->WrappingMember<AZ::Entity*>(&BehaviorEntity::GetRawEntityPtr)
;
}
}
BehaviorEntity::BehaviorEntity(AZ::EntityId entityId)
: m_entityId(entityId)
{
}
BehaviorEntity::BehaviorEntity(AZ::Entity* entity)
: m_entityId(entity ? entity->GetId() : AZ::EntityId())
{
}
AZStd::string BehaviorEntity::GetName() const
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot get entity name. %s", errorMessage.c_str());
return "";
}
return entity->GetName();
}
void BehaviorEntity::SetName(const char* name)
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot set entity name. %s", errorMessage.c_str());
return;
}
entity->SetName(name);
}
AzFramework::EntityContextId BehaviorEntity::GetOwningContextId() const
{
if (!m_entityId.IsValid())
{
AZ_Warning("Entity", false, "Cannot get entity context. Entity ID is invalid.");
return EntityContextId::CreateNull();
}
// no further warnings, iquerying missing entities is a valid use case
EntityContextId contextId = EntityContextId::CreateNull();
EntityIdContextQueryBus::EventResult(contextId, m_entityId, &EntityIdContextQueryBus::Events::GetOwningContextId);
return contextId;
}
bool BehaviorEntity::Exists() const
{
if (!m_entityId.IsValid())
{
AZ_Warning("Entity", false, "Cannot check entity existence. Entity ID is invalid.");
return false;
}
// no further warnings, querying missing entities is a valid use case
return GetValidEntity(nullptr, nullptr, nullptr);
}
bool BehaviorEntity::IsActivated() const
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot get entity activation. %s", errorMessage.c_str());
return false;
}
AZ::Entity::State state = entity->GetState();
return (state == AZ::Entity::State::Active || state == AZ::Entity::State::Activating);
}
void BehaviorEntity::Activate()
{
AZ::Entity* entity;
EntityContextId contextId;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, &contextId, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot activate entity. %s", errorMessage.c_str());
return;
}
if (entity->GetState() != AZ::Entity::State::Init)
{
AZ_Warning("Entity", false, "Cannot activate entity. Entity (id=%s name='%s') must be in the initialized state.", m_entityId.ToString().c_str(), entity->GetName().c_str());
return;
}
EntityContextRequestBus::Event(contextId, &EntityContextRequestBus::Events::ActivateEntity, m_entityId);
// don't warn if activation fails, Entity::Activate() already issues warnings
}
void BehaviorEntity::Deactivate()
{
AZ::Entity* entity;
EntityContextId contextId;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, &contextId, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot deactivate entity. %s", errorMessage.c_str());
return;
}
AZ::Entity::State state = entity->GetState();
if (state != AZ::Entity::State::Active && state != AZ::Entity::State::Activating)
{
AZ_Warning("Entity", false, "Cannot deactivate entity. Entity (id=%s name='%s') must be in the activated state.", m_entityId.ToString().c_str(), entity->GetName().c_str());
return;
}
EntityContextRequestBus::Event(contextId, &EntityContextRequestBus::Events::DeactivateEntity, m_entityId);
}
BehaviorComponentId BehaviorEntity::CreateComponent(const AZ::TypeId& componentTypeId, const AZ::ComponentConfig* componentConfig /*=nullptr*/)
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot create component. %s", errorMessage.c_str());
return AZ::InvalidComponentId;
}
// don't create component if an incompatible component exists on the entity
AZStd::vector<AZ::Component*> incompatibleComponents;
entity->IsComponentReadyToAdd(componentTypeId, nullptr, &incompatibleComponents);
if (!incompatibleComponents.empty())
{
AZ_Warning("Entity", false, "Cannot create component '%s' because it is incompatible with existing component '%s' on entity (id=%s name='%s').",
Internal::GetComponentName(componentTypeId), Internal::GetComponentName(azrtti_typeid(incompatibleComponents[0])), m_entityId.ToString().c_str(), entity->GetName().c_str());
return AZ::InvalidComponentId;
}
AZ::Component* component = entity->CreateComponent(componentTypeId);
if (!component)
{
AZ_Warning("Entity", false, "Failed to create component (type=%s) on entity (id=%s name='%s')", componentTypeId.ToString<AZStd::string>().c_str(), m_entityId.ToString().c_str(), entity->GetName().c_str());
return AZ::InvalidComponentId;
}
if (componentConfig)
{
component->SetConfiguration(*componentConfig);
// don't warn if configuration fails. Entity::SetConfiguration() already gives good warnings.
}
return component->GetId();
}
bool BehaviorEntity::DestroyComponent(BehaviorComponentId componentId)
{
AZ::Component* component;
AZStd::string errorMessage;
if (!GetValidComponent(componentId, &component, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot destroy component. %s", errorMessage.c_str());
return false;
}
if (!component->GetEntity()->RemoveComponent(component))
{
AZ_Warning("Entity", false, "Cannot destroy component. Failed to remove component (id=%llu) from entity (id=%s name='%s').", componentId, m_entityId.ToString().c_str(), component->GetEntity()->GetName().c_str());
return false;
}
delete component;
return true;
}
AZStd::vector<BehaviorComponentId> BehaviorEntity::GetComponents() const
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot get components. %s", errorMessage.c_str());
return AZStd::vector<BehaviorComponentId>();
}
AZStd::vector<BehaviorComponentId> components;
for (AZ::Component* component : entity->GetComponents())
{
components.emplace_back(component->GetId());
}
return components;
}
BehaviorComponentId BehaviorEntity::FindComponentOfType(const AZ::TypeId& componentTypeId) const
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot find component. %s", errorMessage.c_str());
return AZ::InvalidComponentId;
}
BehaviorComponentId componentId = AZ::InvalidComponentId;
if (const AZ::Component* component = entity->FindComponent(componentTypeId))
{
componentId = component->GetId();
}
return componentId;
}
AZStd::vector<BehaviorComponentId> BehaviorEntity::FindAllComponentsOfType(const AZ::TypeId& componentTypeId) const
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot find components. %s", errorMessage.c_str());
return AZStd::vector<BehaviorComponentId>();
}
AZStd::vector<BehaviorComponentId> components;
for (const AZ::Component* component : entity->FindComponents(componentTypeId))
{
components.emplace_back(component->GetId());
}
return components;
}
AZ::TypeId BehaviorEntity::GetComponentType(BehaviorComponentId componentId) const
{
AZ::Component* component;
AZStd::string errorMessage;
if (!GetValidComponent(componentId, &component, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot get component type. %s", errorMessage.c_str());
return AZ::TypeId::CreateNull();
}
return azrtti_typeid(component);
}
AZStd::string BehaviorEntity::GetComponentName(BehaviorComponentId componentId) const
{
AZ::Component* component;
AZStd::string errorMessage;
if (!GetValidComponent(componentId, &component, &errorMessage))
{
AZ_Warning("Entity", false, "Cannot get component name. %s", errorMessage.c_str());
return "";
}
return component->RTTI_GetTypeName();
}
bool BehaviorEntity::SetComponentConfiguration(BehaviorComponentId componentId, const AZ::ComponentConfig& componentConfig)
{
AZ::Component* component;
AZStd::string errorMessage;
if (!GetValidComponent(componentId, &component, &errorMessage))
{
AZ_Warning("Entity", false, "Failed to set component configuration. %s", errorMessage.c_str());
return false;
}
bool success = component->SetConfiguration(componentConfig);
// don't warn if configuration fails. Entity::SetConfiguration() already gives good warnings.
return success;
}
bool BehaviorEntity::GetComponentConfiguration(BehaviorComponentId componentId, AZ::ComponentConfig& outComponentConfig) const
{
AZ::Component* component;
AZStd::string errorMessage;
if (!GetValidComponent(componentId, &component, &errorMessage))
{
AZ_Warning("Entity", false, "Failed to get component configuration. %s", errorMessage.c_str());
return false;
}
bool success = component->GetConfiguration(outComponentConfig);
// don't warn if configuration fails. Entity::GetConfiguration() already gives good warnings.
return success;
}
AZ::Entity* BehaviorEntity::GetRawEntityPtr()
{
AZ::Entity* entity;
AZStd::string errorMessage;
if (!GetValidEntity(&entity, nullptr, &errorMessage))
{
AZ_Warning("Entity", false, errorMessage.c_str());
return nullptr;
}
return entity;
}
bool BehaviorEntity::GetValidEntity(AZ::Entity** outEntity, EntityContextId* outContextId, AZStd::string* outErrorMessage) const
{
AZ::Entity* entity = nullptr;
EntityContextId contextId = EntityContextId::CreateNull();
AZStd::string errorMessage;
bool success = false;
if (m_entityId.IsValid())
{
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, m_entityId);
if (entity)
{
EntityIdContextQueryBus::EventResult(contextId, m_entityId, &EntityIdContextQueryBus::Events::GetOwningContextId);
if (!contextId.IsNull())
{
success = true;
}
else
{
errorMessage = AZStd::string::format("Entity has no owning context (id=%s name='%s')", m_entityId.ToString().c_str(), entity->GetName().c_str());
}
}
else
{
errorMessage = AZStd::string::format("Entity does not exist (id=%s)", m_entityId.ToString().c_str());
}
}
else
{
errorMessage = "Entity ID is invalid";
}
if (outEntity)
{
*outEntity = success ? entity : nullptr;
}
if (outContextId)
{
*outContextId = success ? contextId : AZ::TypeId::CreateNull();
}
if (outErrorMessage)
{
if (success)
{
outErrorMessage->clear();
}
else
{
*outErrorMessage = AZStd::move(errorMessage);
}
}
return success;
}
bool BehaviorEntity::GetValidComponent(BehaviorComponentId componentId, AZ::Component** outComponent, AZStd::string* outErrorMessage) const
{
AZ::Entity* entity = nullptr;
AZ::Component* component = nullptr;
AZStd::string errorMessage;
bool success = false;
if (GetValidEntity(&entity, nullptr, &errorMessage))
{
component = entity->FindComponent(componentId);
if (component)
{
success = true;
}
else
{
errorMessage = AZStd::string::format("Component (id=%llu) not found on entity(id=%s name='%s').", static_cast<AZ::u64>(componentId), m_entityId.ToString().c_str(), entity->GetName().c_str());
}
}
if (outComponent)
{
*outComponent = success ? component : nullptr;
}
if (outErrorMessage)
{
if (success)
{
outErrorMessage->clear();
}
else
{
*outErrorMessage = AZStd::move(errorMessage);
}
}
return success;
}
} // namespace AzFramework
@@ -0,0 +1,243 @@
/*
* 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.
*
*/
#pragma once
#include <AzFramework/Entity/EntityContext.h>
namespace AzFramework
{
/**
* A wrapper around AZ::ComponentId, for use within the BehaviorContext.
* This wrapper is necessary because AZ::ComponentId is just a 64bit int and
* Lua cannot store the exact value of a 64bit int.
*
* BehaviorComponentId should only be used in coordination with the
* BehaviorEntity class to access components on deactivated entities.
* Other systems, which communicate with activated entities,
* should use the appropriate EBus to communicate with components.
*/
class BehaviorComponentId
{
public:
AZ_TYPE_INFO(BehaviorComponentId, "{60A9A069-9C3D-465A-B7AD-0D6CC803990A}");
AZ_CLASS_ALLOCATOR(BehaviorComponentId, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
BehaviorComponentId() = default;
BehaviorComponentId(AZ::ComponentId id);
operator AZ::ComponentId() const;
bool operator==(const BehaviorComponentId& rhs) const;
bool IsValid() const;
AZStd::string ToString() const;
private:
AZ::ComponentId m_id = AZ::InvalidComponentId;
};
/**
* A wrapper around calls to AZ::Entity, for use within the BehaviorContext.
* It is always safe to call functions on this class
* even if the entity it represents has been deleted from memory.
*/
class BehaviorEntity
{
public:
AZ_RTTI(BehaviorEntity, "{41CC88A4-FE07-48E6-943D-998DE68AFF5C}");
AZ_CLASS_ALLOCATOR(BehaviorEntity, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
virtual ~BehaviorEntity() = default;
/**
* Constructs an invalid BehaviorEntity.
* Any methods called on this instance will have no effect.
*/
BehaviorEntity() = default;
/**
* Constructs a BehaviorEntity with the given entity ID.
* @param entityId The ID of the entity.
*/
explicit BehaviorEntity(AZ::EntityId entityId);
/**
* Constructs a BehaviorEntity with the ID of the provided entity.
* @param entity Entity that this BehaviorEntity will represent.
* If nullptr is provided then an invalid BehaviorEntity is constructed.
*/
explicit BehaviorEntity(AZ::Entity* entity);
/**
* @copydoc AZ::Entity::GetName()
*/
AZStd::string GetName() const;
/**
* @copydoc AZ::Entity::SetName()
*/
void SetName(const char* name);
/**
* @copydoc AZ::Entity::GetId()
*/
AZ::EntityId GetId() const { return m_entityId; }
/**
* @copydoc EntityIdContextQueries::GetOwningContextId()
*/
EntityContextId GetOwningContextId() const;
/**
* Check whether this instance has a valid entity ID.
* Note that a valid entity ID does not indicate whether
* the entity it represents currently exists in memory.
* @return Returns true if the entity ID is valid. Otherwise, false.
*/
bool IsValid() const { return m_entityId.IsValid(); };
/**
* Check whether the entity exists in memory.
* Note that an entity which exists may or may not be activated.
* @return true if the entity exists in memory.
*/
bool Exists() const;
/**
* Check whether the entity is activated.
* @return Returns true if the entity is activated. Otherwise, false.
*/
bool IsActivated() const;
/**
* @copydoc AZ::Entity::Activate()
*/
void Activate();
/**
* @copydoc AZ::Entity::Deactivate()
*/
void Deactivate();
/**
* Creates a component and attaches it to the entity.
* You cannot add a component to an entity when the entity is activated.
* @param componentTypeId Type ID of component to create.
* For example, pass TransformComponentTypeId to create a TransformComponent.
* @param componentConfig (Optional) A configuration to apply to the new component.
* The configuration class must be of the appropriate type for this component.
* For example, use a TransformConfig with a TransformComponent.
* @return Returns the ID of the new component.
* If the component could not be created then AZ::InvalidEntityId is returned.
*/
BehaviorComponentId CreateComponent(const AZ::TypeId& componentTypeId, const AZ::ComponentConfig* componentConfig = nullptr);
/**
* Removes the component from the entity and destroys it.
* You cannot destroy a component while the entity is activated.
* @param componentId ID of the component to destroy.
* @return True if the component was destroyed. Otherwise, false.
*/
bool DestroyComponent(BehaviorComponentId componentId);
/**
* Gets all components registered with the entity.
* @return A vector with the IDs of all components registered with the entity.
*/
AZStd::vector<BehaviorComponentId> GetComponents() const;
/**
* Finds the first component of the requested component type.
* @param componentTypeId The type of component to find.
* @return The ID of the first component of the requested type.
* Returns invalid component ID if a component of the requested type cannot be found.
*/
BehaviorComponentId FindComponentOfType(const AZ::TypeId& componentTypeId) const;
/**
* Gets all components of a specified type registered with the entity.
* @param componentTypeId The type of component to find.
* @return A vector with the IDs of all components of a specified type registered with the entity.
*/
AZStd::vector<BehaviorComponentId> FindAllComponentsOfType(const AZ::TypeId& componentTypeId) const;
/**
* Get the type of a specific component on the entity.
* @param componentId The ID of the component to query.
* @return The type of the specified component.
* Returns an invalid type ID if the component is not found.
*/
AZ::TypeId GetComponentType(BehaviorComponentId componentId) const;
/**
* Get the name of a specific component on the entity.
* @param componentId the ID of the component to query.
* @return The name of the component.
*/
AZStd::string GetComponentName(BehaviorComponentId componentId) const;
/**
* Set the component's configuration.
* You cannot configure a component while the entity is activated.
* @param componentId The ID of the component to configure.
* @param componentConfig The component will set its properties based on this configuration.
* The configuration class must be of the appropriate type for this component.
* For example, use a TransformConfig with a TransformComponent.
* @return True if the configuration was successfully copied to the component.
* Returns false if the component was not found, or the component was not
* compatible with the provided configuration class.
*/
bool SetComponentConfiguration(BehaviorComponentId componentId, const AZ::ComponentConfig& componentConfig);
/**
* Get a component's configuration.
* @param componentId The ID of the component to query.
* @param outComponentConfig[out] The component will copy its properties into this configuration class.
* The configuration class must be of the appropriate type for this component.
* For example, use a TransformConfig with a TransformComponent.
* @return True if the configuration was successfully copied from the component.
* Returns false if the component was not found, or the component was not
* compatible with the provided configuration class.
*/
bool GetComponentConfiguration(BehaviorComponentId componentId, AZ::ComponentConfig& outComponentConfig) const;
private:
/**
* Get a pointer to the entity.
* @return Return a pointer to the entity if it exists. Otherwise, nullptr.
*/
AZ::Entity* GetRawEntityPtr();
/**
* Attempts to retrieve valid entity values.
* If anything is invalid, an error message describes the issue.
* @param[out] outEntity (Optional) On success, the valid entity pointer.
* @param[out] outEntityContextId (Optional) On success, the valid entity context ID.
* @param[out] outErrorMessage (Optional) On failure, an error message describing what went wrong.
* @return True if successful and all values are valid. Otherwise, false.
*/
bool GetValidEntity(AZ::Entity** outEntity, EntityContextId* outContextId, AZStd::string* outErrorMessage) const;
/**
* Attempt to retrieve a valid component.
* If anything is invalid, an error message describes the issue.
* @param componentId component ID to retrieve
* @param outComponent (Optional On success, the valid component pointer.
* @param outErrorMessage (Optional) On failure, an error message describing what went wrong.
* @return True if successful and component was valid. Otherwise, false.
*/
bool GetValidComponent(BehaviorComponentId componentId, AZ::Component** outComponent, AZStd::string* outErrorMessage) const;
AZ::EntityId m_entityId;
};
} // namespace AzFramework
@@ -0,0 +1,380 @@
/*
* 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 <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Debug/AssetTracking.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Debug/Profiler.h>
#include <AzCore/std/containers/stack.h>
#include "EntityContext.h"
namespace AzFramework
{
//=========================================================================
// Reflect
//=========================================================================
void EntityContext::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
// EntityContext entity data is serialized through streams / Ebus messages.
serializeContext->Class<EntityContext>()
->Version(1)
;
}
}
//=========================================================================
// EntityContext ctor
//=========================================================================
EntityContext::EntityContext(AZ::SerializeContext* serializeContext /*= nullptr*/)
: EntityContext(EntityContextId::CreateRandom(), serializeContext)
{
EntityContextRequestBus::Handler::BusConnect(m_contextId);
}
//=========================================================================
// EntityContext ctor
//=========================================================================
EntityContext::EntityContext(const AZ::Uuid& contextId, AZ::SerializeContext* serializeContext /*= nullptr*/)
: EntityContext(contextId, nullptr, serializeContext)
{
}
EntityContext::EntityContext(const EntityContextId& contextId, AZStd::unique_ptr<EntityOwnershipService> entityOwnershipService,
AZ::SerializeContext* serializeContext)
: m_serializeContext(serializeContext)
, m_contextId(contextId)
{
if (nullptr == serializeContext)
{
AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(m_serializeContext, "Failed to retrieve application serialization context.");
}
if (m_contextId.IsNull())
{
m_contextId = EntityContextId::CreateRandom();
AZ_Assert(m_contextId.IsNull(), "Failed to create an entity context id.");
}
if (nullptr == entityOwnershipService)
{
m_entityOwnershipService = AZStd::make_unique<AzFramework::SliceEntityOwnershipService>(m_contextId, m_serializeContext);
AZ_Assert(m_entityOwnershipService, "Failed to create an entity ownership service.");
}
else
{
m_entityOwnershipService = AZStd::move(entityOwnershipService);
}
EntityContextRequestBus::Handler::BusConnect(m_contextId);
EntityContextEventBus::Bind(m_eventBusPtr, m_contextId);
}
//=========================================================================
// EntityContext dtor
//=========================================================================
EntityContext::~EntityContext()
{
m_eventBusPtr = nullptr;
DestroyContext();
}
//=========================================================================
// InitContext
//=========================================================================
void EntityContext::InitContext()
{
AZ_Assert(m_entityOwnershipService, "Entity Ownership Service has not been created yet");
EntityOwnershipServiceNotificationBus::Handler::BusConnect(m_contextId);
m_entityOwnershipService->Initialize();
// If any of the entity contexts that extend the base entity context override these handler functions, those overriden functions
// will be set as the callbacks.
m_entityOwnershipService->SetEntitiesAddedCallback([this](const EntityList& entityList)
{
this->HandleEntitiesAdded(entityList);
});
m_entityOwnershipService->SetEntitiesRemovedCallback([this](const EntityIdList& entityIds)
{
this->HandleEntitiesRemoved(entityIds);
});
m_entityOwnershipService->SetValidateEntitiesCallback([this](const EntityList& entities)
{
return this->ValidateEntitiesAreValidForContext(entities);
});
}
//=========================================================================
// DestroyContext
//=========================================================================
void EntityContext::DestroyContext()
{
if (m_entityOwnershipService)
{
m_entityOwnershipService->Reset();
EntityOwnershipServiceNotificationBus::Handler::BusDisconnect(m_contextId);
m_entityOwnershipService->Destroy();
}
}
//=========================================================================
// ResetContext
//=========================================================================
void EntityContext::ResetContext()
{
m_entityOwnershipService->Reset();
}
//=========================================================================
// HandleEntitiesAdded
//=========================================================================
void EntityContext::HandleEntitiesAdded(const EntityList& entities)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
for (AZ::Entity* entity : entities)
{
AZ::EntityBus::MultiHandler::BusConnect(entity->GetId());
EntityIdContextQueryBus::MultiHandler::BusConnect(entity->GetId());
EntityContextEventBus::Event(m_eventBusPtr, &EntityContextEventBus::Events::OnEntityContextCreateEntity, *entity);
}
OnContextEntitiesAdded(entities);
}
//=========================================================================
// HandleEntitiesRemoved
//=========================================================================
void EntityContext::HandleEntitiesRemoved(const EntityIdList& entityIds)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
for (AZ::EntityId id : entityIds)
{
OnContextEntityRemoved(id);
EntityContextEventBus::Event(m_eventBusPtr, &EntityContextEventBus::Events::OnEntityContextDestroyEntity, id);
EntityIdContextQueryBus::MultiHandler::BusDisconnect(id);
AZ::EntityBus::MultiHandler::BusDisconnect(id);
}
}
//=========================================================================
// ValidateEntitiesAreValidForContext
//=========================================================================
bool EntityContext::ValidateEntitiesAreValidForContext(const EntityList&)
{
return true;
}
//=========================================================================
// IsOwnedByThisContext
//=========================================================================
bool EntityContext::IsOwnedByThisContext(const AZ::EntityId& entityId)
{
// Get ID of the owning context of the incoming entity ID and compare it to
// the id of this context.
EntityContextId owningContextId = EntityContextId::CreateNull();
EntityIdContextQueryBus::EventResult(owningContextId, entityId, &EntityIdContextQueryBus::Events::GetOwningContextId);
return owningContextId == m_contextId;
}
//=========================================================================
// CreateEntity
//=========================================================================
AZ::Entity* EntityContext::CreateEntity(const char* name)
{
AZ::Entity* entity = aznew AZ::Entity(name);
AddEntity(entity);
return entity;
}
//=========================================================================
// AddEntity
//=========================================================================
void EntityContext::AddEntity(AZ::Entity* entity)
{
AZ_Assert(!EntityIdContextQueryBus::FindFirstHandler(entity->GetId()), "Entity already belongs to a context.");
m_entityOwnershipService->AddEntity(entity);
}
//=========================================================================
// ActivateEntity
//=========================================================================
void EntityContext::ActivateEntity(AZ::EntityId entityId)
{
AZ_ASSET_ATTACH_TO_SCOPE(this);
// Verify that this context has the right to perform operations on the entity
bool validEntity = IsOwnedByThisContext(entityId);
AZ_Warning("GameEntityContext", validEntity, "Entity with id %llu does not belong to the game context.", entityId);
if (validEntity)
{
// Look up the entity and activate it.
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
if (entity)
{
// Safety Check: Is the entity initialized?
if (entity->GetState() == AZ::Entity::State::Constructed)
{
AZ_Warning("GameEntityContext", false, "Entity with id %llu was not initialized before activation requested.", entityId);
entity->Init();
}
if (entity->GetState() == AZ::Entity::State::Init)
{
entity->Activate();
}
}
}
}
//=========================================================================
// DeactivateEntity
//=========================================================================
void EntityContext::DeactivateEntity(AZ::EntityId entityId)
{
// Verify that this context has the right to perform operations on the entity
bool validEntity = IsOwnedByThisContext(entityId);
AZ_Warning("GameEntityContext", validEntity, "Entity with id %llu does not belong to the game context.", entityId);
if (validEntity)
{
// Then look up the entity and deactivate it.
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
if (entity)
{
switch (entity->GetState())
{
case AZ::Entity::State::Activating:
// Queue deactivate to trigger next frame
AZ::TickBus::QueueFunction(&AZ::Entity::Deactivate, entity);
break;
case AZ::Entity::State::Active:
// Deactivate immediately
entity->Deactivate();
break;
default:
// Don't do anything, it's not even active.
break;
}
}
}
}
//=========================================================================
// DestroyEntity
//=========================================================================
bool EntityContext::DestroyEntity(AZ::Entity* entity)
{
AZ_Assert(entity, "Invalid entity passed to DestroyEntity");
EntityContextId owningContextId = EntityContextId::CreateNull();
EntityIdContextQueryBus::EventResult(owningContextId, entity->GetId(), &EntityIdContextQueryBus::Events::GetOwningContextId);
AZ_Assert(owningContextId == m_contextId, "Entity does not belong to this context, and therefore can not be safely destroyed by this context.");
if (owningContextId == m_contextId)
{
return m_entityOwnershipService->DestroyEntity(entity);
}
return false;
}
//=========================================================================
// DestroyEntity
//=========================================================================
bool EntityContext::DestroyEntityById(AZ::EntityId entityId)
{
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
if (entity)
{
return DestroyEntity(entity);
}
return false;
}
//=========================================================================
// CloneEntity
//=========================================================================
AZ::Entity* EntityContext::CloneEntity(const AZ::Entity& sourceEntity)
{
AZ_Assert(m_entityOwnershipService->IsInitialized(), "The context has not been initialized.");
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZ_Assert(serializeContext, "Failed to retrieve application serialization context.");
AZ::Entity* entity = serializeContext->CloneObject(&sourceEntity);
AZ_Error("EntityContext", entity != nullptr, "Failed to clone source entity.");
if (entity)
{
entity->SetId(AZ::Entity::MakeId());
AddEntity(entity);
}
return entity;
}
//=========================================================================
// EntityBus::OnEntityDestruction
//=========================================================================
void EntityContext::OnEntityDestruction(const AZ::EntityId& entityId)
{
EntityContextId owningContextId = EntityContextId::CreateNull();
EntityIdContextQueryBus::EventResult(owningContextId, entityId, &EntityIdContextQueryBus::Events::GetOwningContextId);
if (owningContextId == m_contextId)
{
m_entityOwnershipService->DestroyEntityById(entityId);
}
}
AZ::SerializeContext* EntityContext::GetSerializeContext() const
{
return m_serializeContext;
}
void EntityContext::PrepareForEntityOwnershipServiceReset()
{
PrepareForContextReset();
}
void EntityContext::OnEntityOwnershipServiceReset()
{
OnContextReset();
EntityContextEventBus::Event(m_contextId, &EntityContextEventBus::Events::OnEntityContextReset);
}
void EntityContext::OnEntitiesReloadedFromStream(const EntityList& entities)
{
OnRootEntityReloaded();
EntityContextEventBus::Event(m_contextId, &EntityContextEventBus::Events::OnEntityContextLoadedFromStream, entities);
}
} // namespace AzFramework
@@ -0,0 +1,136 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_ENTITYCONTEXT_H
#define AZFRAMEWORK_ENTITYCONTEXT_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Component/EntityBus.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/IdUtils.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/SliceEntityOwnershipService.h>
namespace AZ
{
class ReflectContext;
}
namespace AzFramework
{
class EntityContext;
/**
* Provides services for a group of entities under the umbrella of a given context.
*
* e.g. Edit-time entities and runtime entities would belong to separate contexts.
*
* A context owns a root entity, which can be serialized in or out. Interfaces are
* provided for creating entities owned by the context.
*
* Entity contexts are not required to use entities, but provide a package for managing
* independent prefab hierarchies (i.e. a level, a world, etc).
*/
class EntityContext
: public EntityIdContextQueryBus::MultiHandler
, public AZ::EntityBus::MultiHandler
, public EntityContextRequestBus::Handler
, public EntityOwnershipServiceNotificationBus::Handler
{
public:
AZ_TYPE_INFO(EntityContext, "{4F98A6B9-C7B5-450E-8A8A-30EEFC411EF5}");
EntityContext(AZ::SerializeContext* serializeContext = nullptr);
EntityContext(const EntityContextId& contextId, AZ::SerializeContext* serializeContext = nullptr);
EntityContext(const EntityContextId& contextId, AZStd::unique_ptr<EntityOwnershipService> entityOwnershipService,
AZ::SerializeContext* serializeContext = nullptr);
virtual ~EntityContext();
void InitContext();
void DestroyContext();
/// \return the context's Id, which is used to listen on a given context's request or event bus.
const EntityContextId& GetContextId() const { return m_contextId; }
//////////////////////////////////////////////////////////////////////////
// EntityContextRequestBus
AZ::Entity* CreateEntity(const char* name) override;
void AddEntity(AZ::Entity* entity) override;
void ActivateEntity(AZ::EntityId entityId) override;
void DeactivateEntity(AZ::EntityId entityId) override;
bool DestroyEntity(AZ::Entity* entity) override;
bool DestroyEntityById(AZ::EntityId entityId) override;
AZ::Entity* CloneEntity(const AZ::Entity& sourceEntity) override;
void ResetContext() override;
//////////////////////////////////////////////////////////////////////////
static void Reflect(AZ::ReflectContext* context);
protected:
//////////////////////////////////////////////////////////////////////////
// EntityIdContextQueryBus
EntityContextId GetOwningContextId() override { return m_contextId; }
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// EntityOwnershipServiceNotificationBus
void PrepareForEntityOwnershipServiceReset() override;
void OnEntityOwnershipServiceReset() override;
void OnEntitiesReloadedFromStream(const EntityList& entities) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// EntityBus
void OnEntityDestruction(const AZ::EntityId& entityId) override;
//////////////////////////////////////////////////////////////////////////
void HandleEntitiesAdded(const EntityList& entities);
void HandleEntitiesRemoved(const EntityIdList& entityIds);
AZ::SerializeContext* GetSerializeContext() const;
/// Entity context derived implementations can conduct specialized actions when internal events occur, such as adds/removals/resets.
virtual void OnContextEntitiesAdded(const EntityList& /*entities*/) {}
virtual void OnContextEntityRemoved(const AZ::EntityId& /*id*/) {}
virtual void OnRootEntityReloaded() {}
virtual void PrepareForContextReset() { m_contextIsResetting = true; }
virtual void OnContextReset() { m_contextIsResetting = false; }
/// Used to validate that the given list of entities are valid for this context
/// For example they could be non-UI entities being instantiated in a UI context
virtual bool ValidateEntitiesAreValidForContext(const EntityList& entities);
/// Determine if the entity with the given ID is owned by this Entity Context
/// \param entityId An entity ID to check
/// \return true if this context owns the entity with the given id.
bool IsOwnedByThisContext(const AZ::EntityId& entityId);
AZ::SerializeContext* m_serializeContext;
//! Id of the context, used to address bus messages
EntityContextId m_contextId;
//! Pre-bound event bus for the context.
EntityContextEventBus::BusPtr m_eventBusPtr;
//! EntityOwnershipService is responsible for the management of entities used by this context. Such as loading, creation, etc.
AZStd::unique_ptr<EntityOwnershipService> m_entityOwnershipService;
// Tracks if the context is currently being reset.
// This allows systems to skip steps during teardown that will be handled in bulk by the reset.
bool m_contextIsResetting = false;
};
} // namespace AzFramework
#endif // AZFRAMEWORK_ENTITYCONTEXT_H
@@ -0,0 +1,239 @@
/*
* 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.
*
*/
/**
* @file
* Header file for buses that dispatch and receive events from an entity context.
* Entity contexts are collections of entities. Examples of entity contexts are
* the editor context, game context, a custom context, and so on.
*/
#ifndef AZFRAMEWORK_ENTITYCONTEXTBUS_H
#define AZFRAMEWORK_ENTITYCONTEXTBUS_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/ComponentBus.h>
namespace AZ
{
class Entity;
class EntityId;
}
namespace AzFramework
{
class EntityContext;
/**
* Unique ID for an entity context.
*/
using EntityContextId = AZ::Uuid;
using EntityList = AZStd::vector<AZ::Entity*>;
/**
* Interface for AzFramework::EntityContextRequestBus, which is
* the EBus that makes requests to a given entity context.
* If you want to make requests to a specific entity context, such
* as the game entity context, use the interface specific to that
* context. If you want to make requests to multiple types of entity
* contexts, use this interface.
*/
class EntityContextRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
/**
* Overrides the default AZ::EBusAddressPolicy so that the EBus has
* multiple addresses. Events that are addressed to an ID are received
* by all handlers that are connected to that ID.
*/
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
/**
* Specifies that events are addressed by entity context ID.
*/
typedef EntityContextId BusIdType;
//////////////////////////////////////////////////////////////////////////
/**
* Creates an entity and adds it to the entity context.
* This operation does not activate the entity by default.
* @param name A name for the entity.
* @return A pointer to a new entity.
* This operation succeeds unless the system is completely out of memory.
*/
virtual AZ::Entity* CreateEntity(const char* name) = 0;
/**
* Adds an entity to the entity context.
* This operation does not activate the entity by default.
* Derived classes might choose to set the entity to another state.
* @param entity A pointer to the entity to add.
*/
virtual void AddEntity(AZ::Entity* entity) = 0;
/**
* Activates an entity that is owned by the entity context.
* @param id The ID of the entity to activate.
*/
virtual void ActivateEntity(AZ::EntityId entityId) = 0;
/**
* Deactivates an entity that is owned by the entity context.
* @param id The ID of the entity to deactivate.
*/
virtual void DeactivateEntity(AZ::EntityId entityId) = 0;
/**
* Removes an entity from the entity context and destroys the entity.
* @param entity A pointer to the entity to destroy.
* @return If the entity context does not own the entity,
* this returns false and does not destroy the entity.
*/
virtual bool DestroyEntity(AZ::Entity* entity) = 0;
/**
* Removes an entity from the entity context and destroys the entity.
* @param entityId The ID of the entity to destroy.
* @return If the entity context does not own the entity,
* this returns false and does not destroy the entity.
*/
virtual bool DestroyEntityById(AZ::EntityId entityId) = 0;
/**
* Creates a copy of the entity in the entity context.
* The cloned copy is assigned a unique entity ID.
* @param sourceEntity A reference to the entity to clone.
* @return A pointer to the cloned copy of the entity. This operation
* can fail if serialization data fails to interpret the source entity.
*/
virtual AZ::Entity* CloneEntity(const AZ::Entity& sourceEntity) = 0;
/**
* Clears the entity context by destroying all entities and prefab instances
* that the entity context owns.
*/
virtual void ResetContext() = 0;
};
/**
* The EBus for requests to the entity context.
* The events are defined in the AzFramework::EntityContextRequests class.
* If you want to make requests to a specific entity context, such
* as the game entity context, use the bus specific to that context.
* If you want to make requests to multiple types of entity contexts,
* use this bus.
*/
using EntityContextRequestBus = AZ::EBus<EntityContextRequests>;
/**
* Interface for the AzFramework::EntityContextEventBus, which is the EBus
* that dispatches notification events from the global entity context.
* If you want to receive notification events from a specific entity context,
* such as the game entity context, use the interface specific to that context.
* If you want to receive notification events from multiple types of entity
* contexts, use this interface.
*/
class EntityContextEvents
: public AZ::EBusTraits
{
public:
/**
* Destroys the instance of the class.
*/
virtual ~EntityContextEvents() {}
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
/**
* Overrides the default AZ::EBusAddressPolicy to specify that the EBus
* has multiple addresses. Events that are addressed to an ID are received
* by all handlers connected to that ID.
*/
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
/**
* Specifies that events are addressed by entity context ID.
*/
typedef EntityContextId BusIdType;
//////////////////////////////////////////////////////////////////////////
/**
* Signals that an entity context was loaded from a stream.
* @param contextEntities A reference to a list of entities that
* are owned by the entity context that was loaded.
*/
virtual void OnEntityContextLoadedFromStream(const EntityList& /*contextEntities*/) {}
/**
* Signals that the entity context was reset.
*/
virtual void OnEntityContextReset() {}
/**
* Signals that the entity context created an entity.
* @param entity A reference to the entity that was created.
*/
virtual void OnEntityContextCreateEntity(AZ::Entity& /*entity*/) {}
/**
* Signals that the entity context is about to destroy an entity.
* @param id A reference to the ID of the entity that will be destroyed.
*/
virtual void OnEntityContextDestroyEntity(const AZ::EntityId& /*id*/) {}
};
/**
* The EBus for entity context events.
* The events are defined in the AzFramework::EntityContextEvents class.
* If you want to receive event notifications from a specific entity context,
* such as the game entity context, use the bus specific to that context.
* If you want to receive event notifications from multiple types of entity
* contexts, use this bus.
*/
using EntityContextEventBus = AZ::EBus<EntityContextEvents>;
/**
* Interface for AzFramework::EntityIdContextQueryBus, which is
* the EBus that queries an entity about its context.
*/
class EntityIdContextQueries
: public AZ::ComponentBus
{
public:
/**
* Destroys the instance of the class.
*/
virtual ~EntityIdContextQueries() {}
/**
* Gets the ID of the entity context that the entity belongs to.
* @return The ID of the entity context that the entity belongs to.
*/
virtual EntityContextId GetOwningContextId() = 0;
};
/**
* The EBus for querying an entity about its context.
* The events are defined in the AzFramework::EntityIdContextQueries class.
*/
using EntityIdContextQueryBus = AZ::EBus<EntityIdContextQueries>;
} // namespace AzFramework
#endif // AZFRAMEWORK_ENTITYCONTEXTBUS_H
@@ -0,0 +1,186 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/base.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Viewport/CameraState.h>
namespace AZ
{
class Entity;
}
struct DisplayContext;
class ITexture;
namespace AzFramework
{
/// DebugDisplayRequests provides a debug draw api to be used by components and viewport features.
class DebugDisplayRequests
: public AZ::EBusTraits
{
public:
// EBusTraits overrides
using BusIdType = AZ::s32;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
virtual void SetColor(float r, float g, float b, float a = 1.f) { (void)r; (void)g; (void)b; (void)a; }
virtual void SetColor(const AZ::Color& color) { (void)color; }
virtual void SetColor(const AZ::Vector4& color) { (void)color; }
virtual void SetAlpha(float a) { (void)a; }
virtual void DrawQuad(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4) { (void)p1; (void)p2; (void)p3; (void)p4; }
virtual void DrawQuad(float width, float height) { (void)width; (void)height; }
virtual void DrawWireQuad(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4) { (void)p1; (void)p2; (void)p3; (void)p4; }
virtual void DrawWireQuad(float width, float height) { (void)width; (void)height; }
virtual void DrawQuadGradient(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3, const AZ::Vector3& p4, const AZ::Vector4& firstColor, const AZ::Vector4& secondColor) { (void)p1; (void)p2; (void)p3; (void)p4; (void)firstColor; (void)secondColor; }
virtual void DrawTri(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector3& p3) { (void)p1; (void)p2; (void)p3; }
virtual void DrawTriangles(const AZStd::vector<AZ::Vector3>& vertices, const AZ::Color& color) { (void)vertices; (void)color; }
virtual void DrawTrianglesIndexed(const AZStd::vector<AZ::Vector3>& vertices, const AZStd::vector<AZ::u32>& indices, const AZ::Color& color) { (void)vertices; (void)indices, (void)color; }
virtual void DrawWireBox(const AZ::Vector3& min, const AZ::Vector3& max) { (void)min; (void)max; }
virtual void DrawSolidBox(const AZ::Vector3& min, const AZ::Vector3& max) { (void)min; (void)max; }
virtual void DrawSolidOBB(const AZ::Vector3& center, const AZ::Vector3& axisX, const AZ::Vector3& axisY, const AZ::Vector3& axisZ, const AZ::Vector3& halfExtents) { (void)center; (void)axisX; (void)axisY; (void)axisZ; (void)halfExtents; }
virtual void DrawPoint(const AZ::Vector3& p, int nSize = 1) { (void)p; (void)nSize; }
virtual void DrawLine(const AZ::Vector3& p1, const AZ::Vector3& p2) { (void)p1; (void)p2; }
virtual void DrawLine(const AZ::Vector3& p1, const AZ::Vector3& p2, const AZ::Vector4& col1, const AZ::Vector4& col2) { (void)p1; (void)p2; (void)col1; (void)col2; }
virtual void DrawLines(const AZStd::vector<AZ::Vector3>& lines, const AZ::Color& color) { (void)lines; (void)color; }
virtual void DrawPolyLine(const AZ::Vector3* pnts, int numPoints, bool cycled = true) { (void)pnts; (void)numPoints; (void)cycled; }
virtual void DrawWireQuad2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) { (void)p1; (void)p2; (void)z; }
virtual void DrawLine2d(const AZ::Vector2& p1, const AZ::Vector2& p2, float z) { (void)p1; (void)p2; (void)z; }
virtual void DrawLine2dGradient(const AZ::Vector2& p1, const AZ::Vector2& p2, float z, const AZ::Vector4& firstColor, const AZ::Vector4& secondColor) { (void)p1; (void)p2; (void)z; (void)firstColor; (void)secondColor; }
virtual void DrawWireCircle2d(const AZ::Vector2& center, float radius, float z) { (void)center; (void)radius; (void)z; }
virtual void DrawTerrainCircle(const AZ::Vector3& worldPos, float radius, float height) { (void)worldPos; (void)radius; (void)height; }
virtual void DrawTerrainCircle(const AZ::Vector3& center, float radius, float angle1, float angle2, float height) { (void)center; (void)radius; (void)angle1; (void)angle2; (void)height; }
virtual void DrawArc(const AZ::Vector3& pos, float radius, float startAngleDegrees, float sweepAngleDegrees, float angularStepDegrees, int referenceAxis = 2) { (void)pos; (void)radius; (void)startAngleDegrees; (void)sweepAngleDegrees; (void)angularStepDegrees; (void)referenceAxis; }
virtual void DrawArc(const AZ::Vector3& pos, float radius, float startAngleDegrees, float sweepAngleDegrees, float angularStepDegrees, const AZ::Vector3& fixedAxis) { (void)pos; (void)radius; (void)startAngleDegrees; (void)sweepAngleDegrees; (void)angularStepDegrees; (void)fixedAxis; }
virtual void DrawCircle(const AZ::Vector3& pos, float radius, int nUnchangedAxis = 2 /*z axis*/) { (void)pos; (void)radius; (void)nUnchangedAxis; }
virtual void DrawHalfDottedCircle(const AZ::Vector3& pos, float radius, const AZ::Vector3& viewPos, int nUnchangedAxis = 2 /*z axis*/) { (void)pos; (void)radius; (void)viewPos; (void)nUnchangedAxis; }
virtual void DrawCone(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius, float height, bool drawShaded = true) { (void)pos; (void)dir; (void)radius; (void)height; (void)drawShaded; }
virtual void DrawWireCylinder(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height) { (void)center; (void)axis; (void)radius; (void)height; }
virtual void DrawSolidCylinder(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height, bool drawShaded = true) { (void)center; (void)axis; (void)radius; (void)height; (void)drawShaded; }
virtual void DrawWireCapsule(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float heightStraightSection) { (void)center; (void)axis; (void)radius; (void)heightStraightSection; }
virtual void DrawTerrainRect(float x1, float y1, float x2, float y2, float height) { (void)x1; (void)y1; (void)x2; (void)y2; (void)height; }
virtual void DrawTerrainLine(AZ::Vector3 worldPos1, AZ::Vector3 worldPos2) { (void)worldPos1; (void)worldPos2; }
virtual void DrawWireSphere(const AZ::Vector3& pos, float radius) { (void)pos; (void)radius; }
virtual void DrawWireSphere(const AZ::Vector3& pos, const AZ::Vector3 radius) { (void)pos; (void)radius; }
virtual void DrawWireDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) { (void)pos; (void)dir; (void)radius; }
virtual void DrawBall(const AZ::Vector3& pos, float radius, bool drawShaded = true) { (void)pos; (void)radius; (void)drawShaded; }
virtual void DrawDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) { (void)pos; (void)dir; (void)radius; }
virtual void DrawArrow(const AZ::Vector3& src, const AZ::Vector3& trg, float fHeadScale = 1, bool b2SidedArrow = false) { (void)src; (void)trg; (void)fHeadScale; (void)b2SidedArrow; }
virtual void DrawTextLabel(const AZ::Vector3& pos, float size, const char* text, const bool bCenter = false, int srcOffsetX = 0, int srcOffsetY = 0) { (void)pos; (void)size; (void)text; (void)bCenter; (void)srcOffsetX; (void)srcOffsetY; }
virtual void Draw2dTextLabel(float x, float y, float size, const char* text, bool bCenter = false) { (void)x; (void)y; (void)size; (void)text; (void)bCenter; }
virtual void DrawTextOn2DBox(const AZ::Vector3& pos, const char* text, float textScale, const AZ::Vector4& TextColor, const AZ::Vector4& TextBackColor) { (void)pos; (void)text; (void)textScale; (void)TextColor; (void)TextBackColor; }
virtual void DrawTextureLabel(ITexture* texture, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) { (void)texture; (void)pos; (void)sizeX; (void)sizeY; (void)texIconFlags; }
virtual void DrawTextureLabel(int textureId, const AZ::Vector3& pos, float sizeX, float sizeY, int texIconFlags) { (void)textureId; (void)pos; (void)sizeX; (void)sizeY; (void)texIconFlags; }
virtual void SetLineWidth(float width) { (void)width; }
virtual bool IsVisible(const AZ::Aabb& bounds) { (void)bounds; return false; }
virtual int SetFillMode(int nFillMode) { (void)nFillMode; return 0; }
virtual float GetLineWidth() { return 0.0f; }
virtual float GetAspectRatio() { return 0.0f; }
virtual void DepthTestOff() {}
virtual void DepthTestOn() {}
virtual void DepthWriteOff() {}
virtual void DepthWriteOn() {}
virtual void CullOff() {}
virtual void CullOn() {}
virtual bool SetDrawInFrontMode(bool bOn) { (void)bOn; return false; }
virtual AZ::u32 GetState() { return 0; }
virtual AZ::u32 SetState(AZ::u32 state) { (void)state; return 0; }
virtual AZ::u32 SetStateFlag(AZ::u32 state) { (void)state; return 0; }
virtual AZ::u32 ClearStateFlag(AZ::u32 state) { (void)state; return 0; }
virtual void PushMatrix(const AZ::Transform& tm) { (void)tm; }
virtual void PopMatrix() {}
protected:
~DebugDisplayRequests() = default;
};
/// Inherit from DebugDisplayRequestBus::Handler to implement the DebugDisplayRequests interface.
using DebugDisplayRequestBus = AZ::EBus<DebugDisplayRequests>;
/// Structure to hold information relevant to a given viewport.
struct ViewportInfo
{
int m_viewportId; ///< Unique way to identify a given viewport.
};
/// Provide viewport drawing tied to a specific entity. Components can listen
/// to EntityDebugDisplayEvents in order to draw debug visuals in the viewport
/// for a given entity/component at the correct point in the frame.
class EntityDebugDisplayEvents
: public AZ::ComponentBus
{
public:
using Bus = AZ::EBus<EntityDebugDisplayEvents>;
/// Provide viewport drawing for a particular entity.
/// @param viewportInfo Can be used to determine information such as the camera position.
/// @param debugDisplay Contains interface for debug draw/display commands.
virtual void DisplayEntityViewport(
const ViewportInfo& /*viewportInfo*/,
DebugDisplayRequests& /*debugDisplay*/) {}
protected:
~EntityDebugDisplayEvents() = default;
};
// Inherit from this type to implement EntityDebugDisplayEvents.
using EntityDebugDisplayEventBus = AZ::EBus<EntityDebugDisplayEvents>;
/// Provide viewport drawing not tied to a specific entity. Any type can
/// implement this bus to provide drawing commands from DebugDisplayRequests.
class ViewportDebugDisplayEvents
: public AZ::EBusTraits
{
public:
using BusIdType = EntityContextId;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
/// Display drawing in world space.
virtual void DisplayViewport(
const ViewportInfo& /*viewportInfo*/,
DebugDisplayRequests& /*debugDisplay*/) {}
/// Display drawing in screen space.
virtual void DisplayViewport2d(
const ViewportInfo& /*viewportInfo*/,
DebugDisplayRequests& /*debugDisplay*/) {}
protected:
~ViewportDebugDisplayEvents() = default;
};
// Inherit from this type to implement ViewportDebugDisplayEvents.
using ViewportDebugDisplayEventBus = AZ::EBus<ViewportDebugDisplayEvents>;
class DebugDisplayEvents
: public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<DebugDisplayEvents>;
virtual void DrawGlobalDebugInfo() = 0;
};
using DebugDisplayEventBus = AZ::EBus<DebugDisplayEvents>;
} // namespace AzFramework
@@ -0,0 +1,101 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzFramework/Entity/EntityOwnershipServiceBus.h>
namespace AZ
{
class Entity;
}
namespace AzFramework
{
// Types
using EntityList = AZStd::vector<AZ::Entity*>;
using EntityIdList = AZStd::vector<AZ::EntityId>;
using EntityContextId = AZ::Uuid;
// Callbacks
using OnEntitiesAddedCallback = AZStd::function<void(const EntityList&)>;
using OnEntitiesRemovedCallback = AZStd::function<void(const EntityIdList&)>;
using ValidateEntitiesCallback = AZStd::function<bool(const EntityList&)>;
class EntityOwnershipService
: public AZ::Data::AssetBus::MultiHandler
{
public:
using EntityIdToEntityIdMap = AZStd::unordered_map<AZ::EntityId, AZ::EntityId>;
virtual ~EntityOwnershipService() = default;
//! Initializes all assets/entities/components required for managing entities.
virtual void Initialize() = 0;
//! Returns true if the entity ownership service is initialized.
virtual bool IsInitialized() = 0;
//! Destroys all the assets/entities/components created for managing entities.
virtual void Destroy() = 0;
//! Resets the assets/entities/components without fully destroying them for managing entities.
virtual void Reset() = 0;
virtual void AddEntity(AZ::Entity* entity) = 0;
virtual void AddEntities(const EntityList& entities) = 0;
virtual bool DestroyEntity(AZ::Entity* entity) = 0;
virtual bool DestroyEntityById(AZ::EntityId entityId) = 0;
/**
* Gets the entities in entity ownership service that do not belong to a prefab.
*
* \param entityList The entity list to add the entities to.
*/
virtual void GetNonPrefabEntities(EntityList& entityList) = 0;
/**
* Gets all entities, including those that are owned by prefabs in the entity ownership service.
*
* \param entityList The entity list to add the entities to.
* \return bool whether fetching entities was successful.
*/
virtual bool GetAllEntities(EntityList& entityList) = 0;
/**
* Instantiates all the prefabs that are in the entity ownership service.
*
*/
virtual void InstantiateAllPrefabs() = 0;
virtual void HandleEntitiesAdded(const EntityList& entities) = 0;
virtual bool LoadFromStream(AZ::IO::GenericStream& stream, bool remapIds,
EntityIdToEntityIdMap* idRemapTable = nullptr,
const AZ::ObjectStream::FilterDescriptor& filterDesc = AZ::ObjectStream::FilterDescriptor()) = 0;
virtual void SetEntitiesAddedCallback(OnEntitiesAddedCallback onEntitiesAddedCallback) = 0;
virtual void SetEntitiesRemovedCallback(OnEntitiesRemovedCallback onEntitiesRemovedCallback) = 0;
virtual void SetValidateEntitiesCallback(ValidateEntitiesCallback validateEntitiesCallback) = 0;
protected:
OnEntitiesAddedCallback m_entitiesAddedCallback;
OnEntitiesRemovedCallback m_entitiesRemovedCallback;
ValidateEntitiesCallback m_validateEntitiesCallback;
};
}
@@ -0,0 +1,50 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace AzFramework
{
using EntityContextId = AZ::Uuid;
using EntityList = AZStd::vector<AZ::Entity*>;
class EntityOwnershipServiceNotifications
: public AZ::EBusTraits
{
public:
// We don't want anybody other than entity contexts to listen to these events.
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = EntityContextId;
/**
* Sends a notification before resetting the Entity Ownership Service.
*/
virtual void PrepareForEntityOwnershipServiceReset() = 0;
/**
* Sends a notification indicating that the Entity Ownership Service has been reset.
*/
virtual void OnEntityOwnershipServiceReset() = 0;
/**
* Signals that entities from a given stream have been reloaded.
* @param entities A reference to a list of entities that are reloaded from the given stream.
*/
virtual void OnEntitiesReloadedFromStream(const EntityList& entities) = 0;
};
using EntityOwnershipServiceNotificationBus = AZ::EBus<EntityOwnershipServiceNotifications>;
}
@@ -0,0 +1,192 @@
/*
* 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.
*
*/
/**
* @file
* Header file for buses that dispatch and receive events
* from the game entity context.
* The game entity context holds gameplay entities, as opposed
* to system entities, editor entities, and so on.
*/
#ifndef AZFRAMEWORK_GAMEENTITYCONTEXTBUS_H
#define AZFRAMEWORK_GAMEENTITYCONTEXTBUS_H
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzFramework/Entity/BehaviorEntity.h>
namespace AZ
{
class Entity;
}
namespace AzFramework
{
/**
* Interface for AzFramework::GameEntityContextRequestBus, which is
* the EBus that makes requests to the game entity context.
* The game entity context holds gameplay entities, as opposed
* to system entities, editor entities, and so on.
*/
class GameEntityContextRequests
: public AZ::EBusTraits
{
public:
/**
* Destroys the instance of the class.
*/
virtual ~GameEntityContextRequests() = default;
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
/**
* Overrides the default AZ::EBusTraits handler policy so that this
* EBus supports a single handler at each address. This EBus has only
* one handler because it uses the default AZ::EBusTraits address
* policy, and that policy specifies that the EBus has only one address.
*/
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
//////////////////////////////////////////////////////////////////////////
/**
* Gets the ID of the game entity context.
* @return The ID of the game entity context.
*/
virtual EntityContextId GetGameEntityContextId() = 0;
/**
* Creates an entity in the game context.
* @param name A name for the new entity.
* @return A pointer to a new entity.
*/
virtual AZ::Entity* CreateGameEntity(const char* /*name*/) = 0;
/**
* Creates an entity in the game context.
* @param name A name for the new entity.
* @return An entity wrapper for use within the BehaviorContext.
*/
virtual BehaviorEntity CreateGameEntityForBehaviorContext(const char* /*name*/) = 0;
/**
* Adds an existing entity to the game context.
* @param entity A pointer to the entity to add to the game context.
*/
virtual void AddGameEntity(AZ::Entity* /*entity*/) = 0;
/**
* Destroys an entity.
* The entity is immediately deactivated and will be destroyed on the next tick.
* @param id The ID of the entity to destroy.
*/
virtual void DestroyGameEntity(const AZ::EntityId& /*id*/) = 0;
/**
* Destroys an entity and all of its descendants.
* The entity and its descendants are immediately deactivated and will be
* destroyed on the next tick.
* @param id The ID of the entity to destroy.
*/
virtual void DestroyGameEntityAndDescendants(const AZ::EntityId& /*id*/) = 0;
/**
* Activates the game entity.
* @param id The ID of the entity to activate.
*/
virtual void ActivateGameEntity(const AZ::EntityId& /*id*/) = 0;
/**
* Deactivates the game entity.
* @param id The ID of the entity to deactivate.
*/
virtual void DeactivateGameEntity(const AZ::EntityId& /*id*/) = 0;
/**
* Loads game entities from a stream.
* @param stream The stream to load the entities from.
* @param remapIds Use true to remap the entity IDs after the stream is loaded.
* @return True if the stream successfully loaded. Otherwise, false. This operation
* can fail if the source file is corrupt or the data could not be up-converted.
*/
virtual bool LoadFromStream(AZ::IO::GenericStream& /*stream*/, bool /*remapIds*/) = 0;
/**
* Completely resets the game context.
* This includes deleting all prefabs and entities.
*/
virtual void ResetGameContext() = 0;
/**
* Returns the entity's name.
* @param id The ID of the entity.
* @return The name of the entity. Returns an empty string if the entity
* cannot be found.
*/
virtual AZStd::string GetEntityName(const AZ::EntityId&) = 0;
};
/**
* The EBus for requests to the game entity context.
* The events are defined in the AzFramework::GameEntityContextRequests class.
*/
using GameEntityContextRequestBus = AZ::EBus<GameEntityContextRequests>;
/**
* Interface for the AzFramework::GameEntityContextEventBus, which is the EBus
* that dispatches notification events from the game entity context.
* The game entity context holds gameplay entities, as opposed
* to system entities, editor entities, and so on.
*/
class GameEntityContextEvents
: public AZ::EBusTraits
{
public:
/**
* Destroys the instance of the class.
*/
virtual ~GameEntityContextEvents() = default;
/**
* Signals that the game entity context is about to be loaded and activated, which happens at the
* start of a level. If the concept of levels is eradicated, this event will be removed.
*/
virtual void OnPreGameEntitiesStarted() {}
/**
* Signals that the game entity context is loaded and activated, which happens at the
* start of a level. If the concept of levels is eradicated, this event will be removed.
*/
virtual void OnGameEntitiesStarted() {}
/**
* Signals that the game entity context is shut down or reset.
* This is equivalent to the end of a level.
* This event will be valid even if the concept of levels is eradicated.
* In that case, its meaning will vary depending on how and if the game
* uses the game entity context.
*/
virtual void OnGameEntitiesReset() {}
};
/**
* The EBus for game entity context events.
* The events are defined in the AzFramework::GameEntityContextEvents class.
*/
using GameEntityContextEventBus = AZ::EBus<GameEntityContextEvents>;
} // namespace AzFramework
#endif // AZFRAMEWORK_GAMEENTITYCONTEXTBUS_H
@@ -0,0 +1,352 @@
/*
* 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 <AzCore/Component/Entity.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/API/ApplicationAPI.h>
#include "GameEntityContextComponent.h"
namespace AzFramework
{
//=========================================================================
// Reflect
//=========================================================================
void GameEntityContextComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<GameEntityContextComponent, AZ::Component>()
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<GameEntityContextComponent>(
"Game Entity Context", "Owns entities in the game runtime, as well as during play-in-editor")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Engine")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<GameEntityContextRequestBus>("GameEntityContextRequestBus")
->Attribute(AZ::Script::Attributes::Module, "entity")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Event("CreateGameEntity", &GameEntityContextRequestBus::Events::CreateGameEntityForBehaviorContext)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Event("DestroyGameEntity", &GameEntityContextRequestBus::Events::DestroyGameEntity)
->Event("DestroyGameEntityAndDescendants", &GameEntityContextRequestBus::Events::DestroyGameEntityAndDescendants)
->Event("ActivateGameEntity", &GameEntityContextRequestBus::Events::ActivateGameEntity)
->Event("DeactivateGameEntity", &GameEntityContextRequestBus::Events::DeactivateGameEntity)
->Attribute(AZ::ScriptCanvasAttributes::DeactivatesInputEntity, true)
->Event("GetEntityName", &GameEntityContextRequestBus::Events::GetEntityName)
;
}
}
//=========================================================================
// GameEntityContextComponent ctor
//=========================================================================
GameEntityContextComponent::GameEntityContextComponent()
: EntityContext(EntityContextId::CreateRandom())
{
}
//=========================================================================
// GameEntityContextComponent dtor
//=========================================================================
GameEntityContextComponent::~GameEntityContextComponent()
{
}
//=========================================================================
// Init
//=========================================================================
void GameEntityContextComponent::Init()
{
}
//=========================================================================
// Activate
//=========================================================================
void GameEntityContextComponent::Activate()
{
m_entityOwnershipService = AZStd::make_unique<SliceGameEntityOwnershipService>(GetContextId(), GetSerializeContext());
InitContext();
GameEntityContextRequestBus::Handler::BusConnect();
}
//=========================================================================
// Deactivate
//=========================================================================
void GameEntityContextComponent::Deactivate()
{
GameEntityContextRequestBus::Handler::BusDisconnect();
DestroyContext();
m_entityOwnershipService.reset();
}
//=========================================================================
// GameEntityContextRequestBus::ResetGameContext
//=========================================================================
void GameEntityContextComponent::ResetGameContext()
{
ResetContext();
}
//=========================================================================
// GameEntityContextRequestBus::CreateGameEntity
//=========================================================================
AZ::Entity* GameEntityContextComponent::CreateGameEntity(const char* name)
{
return CreateEntity(name);
}
//=========================================================================
// GameEntityContextRequestBus::CreateGameEntityForBehaviorContext
//=========================================================================
BehaviorEntity GameEntityContextComponent::CreateGameEntityForBehaviorContext(const char* name)
{
if (AZ::Entity* entity = CreateGameEntity(name))
{
return BehaviorEntity(entity->GetId());
}
return BehaviorEntity();
}
//=========================================================================
// GameEntityContextRequestBus::AddGameEntity
//=========================================================================
void GameEntityContextComponent::AddGameEntity(AZ::Entity* entity)
{
AddEntity(entity);
}
//=========================================================================
// CreateEntity
//=========================================================================
AZ::Entity* GameEntityContextComponent::CreateEntity(const char* name)
{
auto entity = aznew AZ::Entity(name);
// Caller will want to configure entity before it's activated.
entity->SetRuntimeActiveByDefault(false);
AddEntity(entity);
return entity;
}
//=========================================================================
// OnRootEntityReloaded
//=========================================================================
void GameEntityContextComponent::OnRootEntityReloaded()
{
GameEntityContextEventBus::Broadcast(&GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
}
//=========================================================================
// OnContextReset
//=========================================================================
void GameEntityContextComponent::OnContextReset()
{
EBUS_EVENT(GameEntityContextEventBus, OnGameEntitiesReset);
}
//=========================================================================
// GameEntityContextComponent::ValidateEntitiesAreValidForContext
//=========================================================================
bool GameEntityContextComponent::ValidateEntitiesAreValidForContext(const EntityList& entities)
{
// All entities in a prefab being instantiated in the level editor should
// have the TransformComponent on them. Since it is not possible to create
// a prefab with entities from different contexts, it is OK to check
// the first entity only
if (entities.size() > 0)
{
return entities[0]->FindComponent<AzFramework::TransformComponent>() != nullptr;
}
return true;
}
//=========================================================================
// GameEntityContextComponent::OnContextEntitiesAdded
//=========================================================================
void GameEntityContextComponent::OnContextEntitiesAdded(const EntityList& entities)
{
EntityContext::OnContextEntitiesAdded(entities);
#if (AZ_TRAIT_PUMP_SYSTEM_EVENTS_WHILE_LOADING)
auto timeOfLastEventPump = AZStd::chrono::high_resolution_clock::now();
auto PumpSystemEventsIfNeeded = [&timeOfLastEventPump]()
{
static const AZStd::chrono::milliseconds maxMillisecondsBetweenSystemEventPumps(AZ_TRAIT_PUMP_SYSTEM_EVENTS_WHILE_LOADING_INTERVAL_MS);
const auto now = AZStd::chrono::high_resolution_clock::now();
if (now - timeOfLastEventPump > maxMillisecondsBetweenSystemEventPumps)
{
timeOfLastEventPump = now;
ApplicationRequests::Bus::Broadcast(&ApplicationRequests::PumpSystemEventLoopUntilEmpty);
}
};
#endif // (AZ_TRAIT_PUMP_SYSTEM_EVENTS_WHILE_LOADING)
for (AZ::Entity* entity : entities)
{
if (entity->GetState() == AZ::Entity::State::Constructed)
{
entity->Init();
#if (AZ_TRAIT_PUMP_SYSTEM_EVENTS_WHILE_LOADING)
PumpSystemEventsIfNeeded();
#endif // (AZ_TRAIT_PUMP_SYSTEM_EVENTS_WHILE_LOADING)
}
}
for (AZ::Entity* entity : entities)
{
if (entity->GetState() == AZ::Entity::State::Init)
{
if (entity->IsRuntimeActiveByDefault())
{
entity->Activate();
#if (AZ_TRAIT_PUMP_SYSTEM_EVENTS_WHILE_LOADING)
PumpSystemEventsIfNeeded();
#endif // (AZ_TRAIT_PUMP_SYSTEM_EVENTS_WHILE_LOADING)
}
}
}
}
//=========================================================================
// GameEntityContextComponent::DestroyGameEntityById
//=========================================================================
void GameEntityContextComponent::DestroyGameEntity(const AZ::EntityId& id)
{
DestroyGameEntityInternal(id, false);
}
//=========================================================================
// GameEntityContextComponent::DestroyGameEntityAndDescendantsById
//=========================================================================
void GameEntityContextComponent::DestroyGameEntityAndDescendants(const AZ::EntityId& id)
{
DestroyGameEntityInternal(id, true);
}
//=========================================================================
// GameEntityContextComponent::DestroyGameEntityInternal
//=========================================================================
void GameEntityContextComponent::DestroyGameEntityInternal(const AZ::EntityId& entityId, bool destroyChildren)
{
AZStd::vector<AZ::EntityId> entityIdsToBeDeleted;
AZ::Entity* entity = nullptr;
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, entityId);
if (entity)
{
if (destroyChildren)
{
EBUS_EVENT_ID_RESULT(entityIdsToBeDeleted, entityId, AZ::TransformBus, GetAllDescendants);
}
// Inserting the parent to the list before its children; it will be deleted last by the reverse iterator
entityIdsToBeDeleted.insert(entityIdsToBeDeleted.begin(), entityId);
}
for (AZStd::vector<AZ::EntityId>::reverse_iterator entityIdIter = entityIdsToBeDeleted.rbegin();
entityIdIter != entityIdsToBeDeleted.rend(); ++entityIdIter)
{
AZ::Entity* currentEntity = nullptr;
EBUS_EVENT_RESULT(currentEntity, AZ::ComponentApplicationBus, FindEntity, *entityIdIter);
if (currentEntity)
{
if (currentEntity->GetState() == AZ::Entity::State::Active)
{
// Deactivate the entity, we'll destroy it as soon as it is safe.
currentEntity->Deactivate();
}
else
{
// Don't activate the entity, it will be destroyed.
currentEntity->SetRuntimeActiveByDefault(false);
}
}
}
// Queue the entity destruction on the tick bus for safety, this guarantees that we will not attempt to destroy
// an entity during activation.
AZStd::function<void()> destroyEntity = [this,entityIdsToBeDeleted]() mutable
{
for (AZStd::vector<AZ::EntityId>::reverse_iterator entityIdIter = entityIdsToBeDeleted.rbegin();
entityIdIter != entityIdsToBeDeleted.rend(); ++entityIdIter)
{
EntityContext::DestroyEntityById(*entityIdIter);
}
};
EBUS_QUEUE_FUNCTION(AZ::TickBus, destroyEntity);
}
//=========================================================================
// GameEntityContextComponent::ActivateGameEntity
//=========================================================================
void GameEntityContextComponent::ActivateGameEntity(const AZ::EntityId& entityId)
{
ActivateEntity(entityId);
}
//=========================================================================
// GameEntityContextComponent::DeactivateGameEntity
//=========================================================================
void GameEntityContextComponent::DeactivateGameEntity(const AZ::EntityId& entityId)
{
DeactivateEntity(entityId);
}
//=========================================================================
// EntityContextEventBus::LoadFromStream
//=========================================================================
bool GameEntityContextComponent::LoadFromStream(AZ::IO::GenericStream& stream, bool remapIds)
{
if (m_entityOwnershipService->LoadFromStream(stream, remapIds))
{
EBUS_EVENT(GameEntityContextEventBus, OnGameEntitiesStarted);
return true;
}
return false;
}
//=========================================================================
// GameEntityContextRequestBus::GetEntityName
//=========================================================================
AZStd::string GameEntityContextComponent::GetEntityName(const AZ::EntityId& id)
{
AZStd::string entityName;
AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, id);
return entityName;
}
} // namespace AzFramework
@@ -0,0 +1,96 @@
/*
* 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.
*
*/
#ifndef AZFRAMEWORK_GAMEENTITYCONTEXTCOMPONENT_H
#define AZFRAMEWORK_GAMEENTITYCONTEXTCOMPONENT_H
#include <AzCore/Math/Uuid.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/Component/Component.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Entity/SliceGameEntityOwnershipService.h>
#include "EntityContext.h"
namespace AzFramework
{
/**
* System component responsible for owning the game entity context.
*
* The game entity context owns entities in the game runtime, as well as during play-in-editor.
* These entities typically own game/runtime components, *not* inheriting from EditorComponentBase.
*/
class GameEntityContextComponent
: public AZ::Component
, public EntityContext
, private GameEntityContextRequestBus::Handler
{
public:
AZ_COMPONENT(GameEntityContextComponent, "{DA235454-DD9C-468C-AE70-404E415BAA6C}");
GameEntityContextComponent();
~GameEntityContextComponent() override;
//////////////////////////////////////////////////////////////////////////
// Component overrides
void Init() override;
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// GameEntityContextRequestBus
AZ::Uuid GetGameEntityContextId() override { return GetContextId(); }
void ResetGameContext() override;
AZ::Entity* CreateGameEntity(const char* name) override;
BehaviorEntity CreateGameEntityForBehaviorContext(const char* name) override;
void AddGameEntity(AZ::Entity* entity) override;
void DestroyGameEntity(const AZ::EntityId&) override;
void DestroyGameEntityAndDescendants(const AZ::EntityId&) override;
void ActivateGameEntity(const AZ::EntityId&) override;
void DeactivateGameEntity(const AZ::EntityId&) override;
bool LoadFromStream(AZ::IO::GenericStream& stream, bool remapIds) override;
AZStd::string GetEntityName(const AZ::EntityId& id) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void DestroyGameEntityInternal(const AZ::EntityId&, bool destroyChildren);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// EntityContext
AZ::Entity* CreateEntity(const char* name) override;
void OnRootEntityReloaded() override;
void OnContextEntitiesAdded(const EntityList& entities);
void OnContextReset() override;
bool ValidateEntitiesAreValidForContext(const EntityList& entities) override;
//////////////////////////////////////////////////////////////////////////
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("GameEntityContextService", 0xa6f2c885));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("GameEntityContextService", 0xa6f2c885));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("SliceSystemService", 0x1a5b7aad));
}
};
} // namespace AzFramework
#endif // AZFRAMEWORK_GAMEENTITYCONTEXTCOMPONENT_H
@@ -0,0 +1,17 @@
/*
* 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 <AzFramework/Entity/PrefabEntityOwnershipService.h>
namespace AzFramework
{
}
@@ -0,0 +1,24 @@
/*
* 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.
*
*/
#pragma once
#include <AzFramework/Entity/EntityOwnershipService.h>
namespace AzFramework
{
class PrefabEntityOwnershipService
: public EntityOwnershipService
{
};
}
@@ -0,0 +1,674 @@
/*
* 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 <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(AZ::Debug::ProfileCategory::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(AZ::Debug::ProfileCategory::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(AZ::Debug::ProfileCategory::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(AZ::Debug::ProfileCategory::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(AZ::Debug::ProfileCategory::AzFramework);
AZ_ASSET_ATTACH_TO_SCOPE(readyAsset.Get());
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]()
{
AZ_ASSET_ATTACH_TO_SCOPE(readyAsset.Get());
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(AZ::Debug::ProfileCategory::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(AZ::Debug::ProfileCategory::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;
}
}
@@ -0,0 +1,195 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/Slice/SliceMetadataInfoComponent.h>
#include <AzFramework/Entity/EntityOwnershipService.h>
#include <AzFramework/Entity/SliceEntityOwnershipServiceBus.h>
#include <AzFramework/Slice/SliceEntityBus.h>
namespace AzFramework
{
using SliceInstanceUnorderedSet = AZStd::unordered_set<AZ::SliceComponent::SliceInstanceAddress>;
/**
* SliceEntityOwnershipService uses slices as the prefab mechanism to manage entities used by an entity context.
* This includes using a root-slice to put all the loose entities in a level that don't belong to any layers or slices.
*/
class SliceEntityOwnershipService
: public EntityOwnershipService
, protected SliceEntityOwnershipServiceRequestBus::Handler
, private SliceEntityRequestBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(SliceEntityOwnershipService, AZ::SystemAllocator, 0);
explicit SliceEntityOwnershipService(const EntityContextId& entityContextId, AZ::SerializeContext* serializeContext);
//////////////////////////////////////////////////////////////////////////
// SliceEntityOwnershipService
//! Creates the root-slice asset under which all entities in the level belong.
void Initialize() override;
//! Returns true if root slice asset is present.
bool IsInitialized() override;
//! Destroys the root-slice asset.
void Destroy() override;
//! Destroys all the entities under the root-slice without destroying it fully.
void Reset() override;
//! Adds an entity to the root-slice.
//! @param entity
void AddEntity(AZ::Entity* entity) override;
//! Adds the given entities to the root slice.
//! @param entities
void AddEntities(const EntityList& entities) override;
//! Deletes the entity from the root-slice and destroys it.
//! @param entity
bool DestroyEntity(AZ::Entity* entity) override;
//! Deletes the entity id from the root-slice and destroys it.
//! @param entityId
bool DestroyEntityById(AZ::EntityId entityId) override;
//! Gets the entities in entity ownership service that do not belong to a prefab.
void GetNonPrefabEntities(EntityList& entityList) override;
//! Gets all entities, including those that are owned by prefabs in the entity ownership service.
//! @param entityList The entity list to add the entities to.
//! @return whether fetching entities was successful.
bool GetAllEntities(EntityList& entityList) override;
//! Instantiates all the prefabs that are in the entity ownership service.
void InstantiateAllPrefabs() override;
void SetEntitiesAddedCallback(OnEntitiesAddedCallback onEntitiesAddedCallback) override;
void SetEntitiesRemovedCallback(OnEntitiesRemovedCallback onEntityRemovedCallback) override;
void SetValidateEntitiesCallback(ValidateEntitiesCallback validateEntitiesCallback) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AssetBus
void OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
//////////////////////////////////////////////////////////////////////////
//! Load the root slice from a stream.
//! @param stream the source stream from which to load
//! @param remapIds if true, entity Ids will be remapped post-load
//! @param idRemapTable if remapIds is true, the provided table is filled with a map of original ids to new ids
//! @param filterDesc any ObjectStream::LoadFlags
//! @return whether or not the root slice was successfully loaded from the provided stream
virtual bool LoadFromStream(AZ::IO::GenericStream& stream, bool remapIds,
EntityIdToEntityIdMap* idRemapTable = nullptr,
const AZ::ObjectStream::FilterDescriptor& filterDesc = AZ::ObjectStream::FilterDescriptor());
//! Executes the post-add actions for the provided list of entities, like connecting to required ebuses.
//! @param entities The entities to perform the post-add actions for.
void HandleEntitiesAdded(const EntityList& entities) override;
static void Reflect(AZ::ReflectContext* context);
protected:
//////////////////////////////////////////////////////////////////////////
// SliceEntityRequestBus
AZ::SliceComponent::SliceInstanceAddress GetOwningSlice() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SliceEntityOwnershipServiceRequestBus
AZ::Data::AssetId CurrentlyInstantiatingSlice() override;
bool HandleRootEntityReloadedFromStream(AZ::Entity* rootEntity, bool remapIds,
AZ::SliceComponent::EntityIdToEntityIdMap* idRemapTable = nullptr) override;
AZ::SliceComponent* GetRootSlice() override;
const AZ::SliceComponent::EntityIdToEntityIdMap& GetLoadedEntityIdMap() override;
AZ::EntityId FindLoadedEntityIdMapping(const AZ::EntityId& staticId) const override;
SliceInstantiationTicket InstantiateSlice(const AZ::Data::Asset<AZ::Data::AssetData>& asset,
const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& customIdMapper = nullptr,
const AZ::Data::AssetFilterCB& assetLoadFilter = nullptr) override;
AZ::SliceComponent::SliceInstanceAddress CloneSliceInstance(AZ::SliceComponent::SliceInstanceAddress sourceInstance,
AZ::SliceComponent::EntityIdToEntityIdMap& sourceToCloneEntityIdMap) override;
void CancelSliceInstantiation(const SliceInstantiationTicket& ticket) override;
SliceInstantiationTicket GenerateSliceInstantiationTicket() override;
void SetIsDynamic(bool isDynamic) override;
const RootSliceAsset& GetRootAsset() const override;
//////////////////////////////////////////////////////////////////////////
AZ::SliceComponent::SliceInstanceAddress GetOwningSlice(AZ::EntityId entityId);
AZ::SerializeContext* GetSerializeContext();
virtual void CreateRootSlice();
void CreateRootSlice(AZ::SliceAsset* rootSliceAsset);
//! Properly process new metadata entities created during slice streaming. Because the streaming process bypasses slice creation,
//! the entity context has to make sure they're handled properly.
//! @param slice - The slice that was streamed in
virtual void HandleNewMetadataEntitiesCreated(AZ::SliceComponent& /*slice*/) {};
private:
EntityList GetRootSliceEntities();
/// Helper function to send OnSliceInstantiationFailed events.
static void DispatchOnSliceInstantiationFailed(const SliceInstantiationTicket& ticket,
const AZ::Data::AssetId& assetId, bool canceled);
/// Tracking of pending slice instantiations, each being the requested asset and the associated request's ticket.
struct InstantiatingSliceInfo
{
InstantiatingSliceInfo(const AZ::Data::Asset<AZ::Data::AssetData>& asset, const SliceInstantiationTicket& ticket,
const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& customMapper)
: m_asset(asset)
, m_ticket(ticket)
, m_customMapper(customMapper)
{
}
AZ::Data::Asset<AZ::Data::AssetData> m_asset;
SliceInstantiationTicket m_ticket;
AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper m_customMapper;
};
//! Slices queued for instantation. AZStd::list is used for its stable iterators since elements
//! are deleted during traversal in SliceEntityOwnershipService::OnAssetReady
AZStd::list<InstantiatingSliceInfo> m_queuedSliceInstantiations;
RootSliceAsset m_rootAsset;
// The id of the entity context that created this EntityOwnershipService.
EntityContextId m_entityContextId;
//! Monotic tickets for slice instantiation requests.
AZ::u64 m_nextSliceTicketId;
//! When a slice is instantiating, the associated asset ID is cached here.
AZ::Data::AssetId m_instantiatingAssetId;
AZ::SerializeContext* m_serializeContext;
//! Stores map from entity Ids loaded from stream, to remapped entity Ids, if remapping was performed.
AZ::SliceComponent::EntityIdToEntityIdMap m_loadedEntityIdMap;
};
}
@@ -0,0 +1,113 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzFramework/Slice/SliceInstantiationBus.h>
namespace AzFramework
{
using EntityContextId = AZ::Uuid;
using RootSliceAsset = AZ::Data::Asset<AZ::SliceAsset>;
class SliceEntityOwnershipServiceRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = EntityContextId;
/**
* Gets the Asset ID of the currently instantiating slice.
* If no slice is currently being instantiated, it returns an invalid ID
* @return The Asset ID of the slice currently being instantiated.
*/
virtual AZ::Data::AssetId CurrentlyInstantiatingSlice() = 0;
/**
* Initialize this entity ownership service with a newly loaded root slice.
*
* @param rootEntity the rootEntity which has been loaded
* @param remapIds if true, entity Ids will be remapped post-load
* @param idRemapTable if remapIds is true, the provided table is filled with a map of original ids to new ids
* @return whether reading root entity was successful or not
*/
virtual bool HandleRootEntityReloadedFromStream(AZ::Entity* rootEntity, bool remapIds,
AZ::SliceComponent::EntityIdToEntityIdMap* idRemapTable = nullptr) = 0;
virtual AZ::SliceComponent* GetRootSlice() = 0;
/**
* Returns a mapping of stream-loaded entity IDs to remapped entity IDs,if remapping was performed.
* If the stream was loaded without remapping enabled, the map will be empty.
* @return A mapping of entity IDs loaded from a stream to remapped values.
*/
virtual const AZ::SliceComponent::EntityIdToEntityIdMap& GetLoadedEntityIdMap() = 0;
/**
* Returns the remapped id of a stream-loaded EntityId if remapping was performed.
* @return The remapped EntityId
*
*/
virtual AZ::EntityId FindLoadedEntityIdMapping(const AZ::EntityId& staticId) const = 0;
/**
* Instantiate a slice asset in the entity ownership service. Listen for the OnSliceInstantiated() / OnSliceInstantiationFailed()
* events for details about the resulting entities.
* @param asset slice asset to instantiate.
* @param customIdMapper optional Id map callback to allow caller to customize entity Id generation.
* @param assetLoadFilterCB optional asset load filter callback. This is only necessary when heavily customizing asset loading,
* as it can allow deferral of dependent asset loading.
* @param return slice instantiation ticket.
*/
virtual SliceInstantiationTicket InstantiateSlice(const AZ::Data::Asset<AZ::Data::AssetData>& asset,
const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& customIdMapper = nullptr,
const AZ::Data::AssetFilterCB& assetLoadFilter = nullptr) = 0;
/**
* Clones an existing slice instance in the entity ownership service. New instance is immediately returned.
* This function doesn't automatically add new instance to the entity ownership service. Callers are responsible for that.
* @param sourceInstance The source instance to be cloned
* @param sourceToCloneEntityIdMap [out] The map between source entity ids and clone entity ids
* @return new slice address. A null slice address will be returned if cloning fails (.first==nullptr, .second==nullptr).
*/
virtual AZ::SliceComponent::SliceInstanceAddress CloneSliceInstance(AZ::SliceComponent::SliceInstanceAddress sourceInstance,
AZ::SliceComponent::EntityIdToEntityIdMap& sourceToCloneEntityIdMap) = 0;
/**
* Cancels the asynchronous instantiation of a slice.
* @param SliceInstantiationTicket The ticket identifies the asynchronous slice instantiation request.
*/
virtual void CancelSliceInstantiation(const SliceInstantiationTicket& ticket) = 0;
/**
* Generates a ticket that can be used for tracking asynchronous slice instantiations.
* @return SliceInstantiationTicket
*/
virtual SliceInstantiationTicket GenerateSliceInstantiationTicket() = 0;
/**
* Enables the root slice to be a dynamic slice.
*
* \param isDynamic
*/
virtual void SetIsDynamic(bool isDynamic) = 0;
virtual const RootSliceAsset& GetRootAsset() const = 0;
};
using SliceEntityOwnershipServiceRequestBus = AZ::EBus<SliceEntityOwnershipServiceRequests>;
}
@@ -0,0 +1,256 @@
/*
* 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 <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/SliceGameEntityOwnershipService.h>
namespace AzFramework
{
SliceGameEntityOwnershipService::SliceGameEntityOwnershipService(const EntityContextId& entityContextId,
AZ::SerializeContext* serializeContext)
: SliceEntityOwnershipService(entityContextId, serializeContext)
{
SliceGameEntityOwnershipServiceRequestBus::Handler::BusConnect();
}
SliceGameEntityOwnershipService::~SliceGameEntityOwnershipService()
{
SliceGameEntityOwnershipServiceRequestBus::Handler::BusDisconnect();
}
void SliceGameEntityOwnershipService::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<SliceGameEntityOwnershipServiceRequestBus>("SliceGameEntityOwnershipServiceRequestBus")
->Attribute(AZ::Script::Attributes::Module, "entity")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Event("DestroyDynamicSliceByEntity", &SliceGameEntityOwnershipServiceRequestBus::Events::DestroyDynamicSliceByEntity)
;
}
}
void SliceGameEntityOwnershipService::CreateRootSlice()
{
AZ_Assert(GetRootAsset() && GetRootAsset().Get(), "Root slice asset has not been created yet.");
AZ::SliceAsset* rootSliceAsset = GetRootAsset().Get();
if (rootSliceAsset->GetEntity() != nullptr)
{
// Clearing dynamic slice destruction queue now since all slices in it
// are being deleted during the destruction phase.
// We don't want this list holding onto deleted slices!
m_dynamicSlicesToDestroy.clear();
}
SliceEntityOwnershipService::CreateRootSlice(rootSliceAsset);
// We want all dynamic slices spawned in the game entity ownership service to be
// instantiated, which depends on the root slice itself being instantiated.
rootSliceAsset->GetComponent()->Instantiate();
}
void SliceGameEntityOwnershipService::Reset()
{
SliceEntityOwnershipService::Reset();
SliceInstantiationResultBus::MultiHandler::BusDisconnect();
m_instantiatingDynamicSlices.clear();
}
//=========================================================================
// SliceGameEntityOwnershipServiceRequestBus::InstantiateDynamicSlice
//=========================================================================
SliceInstantiationTicket SliceGameEntityOwnershipService::InstantiateDynamicSlice(
const AZ::Data::Asset<AZ::Data::AssetData>& sliceAsset, const AZ::Transform& worldTransform,
const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& customIdMapper)
{
if (sliceAsset.GetId().IsValid())
{
const SliceInstantiationTicket ticket = InstantiateSlice(sliceAsset, customIdMapper);
if (ticket.IsValid())
{
InstantiatingDynamicSliceInfo& info = m_instantiatingDynamicSlices[ticket];
info.m_asset = sliceAsset;
info.m_transform = worldTransform;
SliceInstantiationResultBus::MultiHandler::BusConnect(ticket);
return ticket;
}
}
return SliceInstantiationTicket();
}
//=========================================================================
// SliceGameEntityOwnershipServiceRequestBus::CancelDynamicSliceInstantiation
//=========================================================================
void SliceGameEntityOwnershipService::CancelDynamicSliceInstantiation(const SliceInstantiationTicket& ticket)
{
// Cleanup of m_instantiatingDynamicSlices will be handled by OnSliceInstantiationFailed()
CancelSliceInstantiation(ticket);
}
//=========================================================================
// SliceGameEntityOwnershipServiceRequestBus::DestroyDynamicSliceByEntity
//=========================================================================
bool SliceGameEntityOwnershipService::DestroyDynamicSliceByEntity(const AZ::EntityId& id)
{
AZ::SliceComponent* rootSlice = GetRootSlice();
if (rootSlice)
{
const auto address = rootSlice->FindSlice(id);
if (address.GetInstance())
{
auto sliceInstance = address.GetInstance();
const auto instantiatedSliceEntities = sliceInstance->GetInstantiated();
if (instantiatedSliceEntities)
{
for (AZ::Entity* currentEntity : instantiatedSliceEntities->m_entities)
{
if (currentEntity)
{
if (currentEntity->GetState() == AZ::Entity::State::Active)
{
currentEntity->Deactivate();
}
MarkEntityForNoActivation(currentEntity->GetId());
}
}
}
// Queue Slice deletion until next tick. This prevents deleting a dynamic slice from an active entity within that slice.
m_dynamicSlicesToDestroy.insert(address);
AZStd::function<void()> deleteDynamicSlices = [this]()
{
this->FlushDynamicSliceDeletionList();
};
AZ::TickBus::QueueFunction(deleteDynamicSlices);
return true;
}
}
return false;
}
void SliceGameEntityOwnershipService::FlushDynamicSliceDeletionList()
{
for (const auto& sliceAddress : m_dynamicSlicesToDestroy)
{
GetRootSlice()->RemoveSliceInstance(sliceAddress);
}
m_dynamicSlicesToDestroy.clear();
}
void SliceGameEntityOwnershipService::MarkEntityForNoActivation(AZ::EntityId entityId)
{
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
AZ_Error("SliceGameEntityOwnershipService", entity,
"Failed to locate entity with id %s. It is either not yet Initialized, or the Id is invalid.",
entityId.ToString().c_str());
if (entity)
{
entity->SetRuntimeActiveByDefault(false);
}
}
//=========================================================================
// SliceInstantiationResultBus::OnSlicePreInstantiate
//=========================================================================
void SliceGameEntityOwnershipService::OnSlicePreInstantiate(const AZ::Data::AssetId& /*sliceAssetId*/, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
{
const SliceInstantiationTicket ticket = *SliceInstantiationResultBus::GetCurrentBusId();
auto instantiatingIter = m_instantiatingDynamicSlices.find(ticket);
if (instantiatingIter != m_instantiatingDynamicSlices.end())
{
InstantiatingDynamicSliceInfo& instantiating = instantiatingIter->second;
const AZ::SliceComponent::EntityList& entities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
// If the entity ownership service was loaded from a stream and Ids were remapped, fix up entity Ids in that slice that
// point to entities in the stream (i.e. level entities).
if (!GetLoadedEntityIdMap().empty())
{
AZ::EntityUtils::SerializableEntityContainer instanceEntities;
instanceEntities.m_entities = entities;
AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&instanceEntities,
[this](const AZ::EntityId& originalId, bool isEntityId, const AZStd::function<AZ::EntityId()>&) -> AZ::EntityId
{
if (!isEntityId)
{
const AZ::SliceComponent::EntityIdToEntityIdMap& loadedEntityIdMap =
GetLoadedEntityIdMap();
auto iter = loadedEntityIdMap.find(originalId);
if (iter != loadedEntityIdMap.end())
{
return iter->second;
}
}
return originalId;
}, GetSerializeContext(), false);
}
// Set initial transform for slice root entity based on the requested root transform for the instance.
for (AZ::Entity* entity : entities)
{
auto* transformComponent = entity->FindComponent<AzFramework::TransformComponent>();
if (transformComponent)
{
// Non-root entities will be positioned relative to their parents.
if (!transformComponent->GetParentId().IsValid())
{
// Note: Root slice entity always has translation at origin, so this maintains scale & rotation.
transformComponent->SetWorldTM(instantiating.m_transform * transformComponent->GetWorldTM());
}
}
}
}
}
//=========================================================================
// SliceInstantiationResultBus::OnSliceInstantiated
//=========================================================================
void SliceGameEntityOwnershipService::OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& instance)
{
const SliceInstantiationTicket ticket = *SliceInstantiationResultBus::GetCurrentBusId();
if (m_instantiatingDynamicSlices.erase(ticket) > 0)
{
SliceGameEntityOwnershipServiceNotificationBus::Broadcast(
&SliceGameEntityOwnershipServiceNotificationBus::Events::OnSliceInstantiated, sliceAssetId, instance, ticket);
}
SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
}
//=========================================================================
// SliceInstantiationResultBus::OnSliceInstantiationFailed
//=========================================================================
void SliceGameEntityOwnershipService::OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId)
{
const SliceInstantiationTicket ticket = *SliceInstantiationResultBus::GetCurrentBusId();
if (m_instantiatingDynamicSlices.erase(ticket) > 0)
{
SliceGameEntityOwnershipServiceNotificationBus::Broadcast(
&SliceGameEntityOwnershipServiceNotificationBus::Events::OnSliceInstantiationFailed, sliceAssetId, ticket);
}
SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
}
}
@@ -0,0 +1,72 @@
/*
* 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.
*
*/
#pragma once
#include <AzFramework/Entity/SliceEntityOwnershipService.h>
#include <AzFramework/Entity/SliceGameEntityOwnershipServiceBus.h>
namespace AzFramework
{
class SliceGameEntityOwnershipService
: public SliceEntityOwnershipService
, private SliceGameEntityOwnershipServiceRequestBus::Handler
, private SliceInstantiationResultBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(SliceGameEntityOwnershipService, AZ::SystemAllocator, 0);
explicit SliceGameEntityOwnershipService(const EntityContextId& entityContextId, AZ::SerializeContext* serializeContext);
virtual ~SliceGameEntityOwnershipService();
static void Reflect(AZ::ReflectContext* context);
void Reset() override;
//////////////////////////////////////////////////////////////////////////
// SliceGameEntityOwnershipServiceRequestBus::Handler
SliceInstantiationTicket InstantiateDynamicSlice(const AZ::Data::Asset<AZ::Data::AssetData>& sliceAsset,
const AZ::Transform& worldTransform, const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& customIdMapper) override;
void CancelDynamicSliceInstantiation(const SliceInstantiationTicket& ticket) override;
bool DestroyDynamicSliceByEntity(const AZ::EntityId&) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SliceInstantiationResultBus::MultiHandler
void OnSlicePreInstantiate(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& instance) override;
void OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& instance) override;
void OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId) override;
//////////////////////////////////////////////////////////////////////////
protected:
void CreateRootSlice() override;
private:
void FlushDynamicSliceDeletionList();
/**
* Specifies that a given entity should not be activated by default
* after it is created.
* @param entityId The entity that should not be activated by default.
*/
void MarkEntityForNoActivation(AZ::EntityId entityId);
struct InstantiatingDynamicSliceInfo
{
AZ::Data::Asset<AZ::Data::AssetData> m_asset;
AZ::Transform m_transform;
};
AZStd::unordered_map<SliceInstantiationTicket, InstantiatingDynamicSliceInfo> m_instantiatingDynamicSlices;
SliceInstanceUnorderedSet m_dynamicSlicesToDestroy;
};
}
@@ -0,0 +1,83 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/IdUtils.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzFramework/Slice/SliceInstantiationTicket.h>
namespace AzFramework
{
class SliceGameEntityOwnershipServiceRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
/**
* Instantiates a dynamic slice asynchronously.
* @param sliceAsset A reference to the slice asset data.
* @param worldTransform A reference to the world transform to apply to the slice.
* @param customIdMapper An ID mapping function that is used when instantiating the slice.
* @return A ticket that identifies the slice instantiation request. Callers can immediately
* subscribe to the AzFramework::SliceInstantiationResultBus for this ticket to receive results
* for this request.
*/
virtual SliceInstantiationTicket InstantiateDynamicSlice(const AZ::Data::Asset<AZ::Data::AssetData>& /*sliceAsset*/,
const AZ::Transform& /*worldTransform*/, const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& /*customIdMapper*/) = 0;
/**
* Cancels the asynchronous instantiation of a dynamic slice.
* This call has no effect if the slice has already finished instantiation.
* @param ticket The ticket that identifies the slice instantiation request.
*/
virtual void CancelDynamicSliceInstantiation(const SliceInstantiationTicket& /*ticket*/) = 0;
/**
* Destroys an entire dynamic slice instance given the ID of any entity within the slice.
* @param id The ID of the entity whose dynamic slice instance you want to destroy.
* @return True if the dynamic slice instance was successfully destroyed. Otherwise, false.
*/
virtual bool DestroyDynamicSliceByEntity(const AZ::EntityId& /*id*/) = 0;
};
using SliceGameEntityOwnershipServiceRequestBus = AZ::EBus<SliceGameEntityOwnershipServiceRequests>;
class SliceGameEntityOwnershipServiceNotifications
: public AZ::EBusTraits
{
public:
/**
* Signals that a slice was instantiated successfully.
* @param sliceAssetId The asset ID of the slice to instantiate.
* @param instance The slice instance.
* @param ticket A ticket that identifies the slice instantiation request.
*/
virtual void OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, const AZ::SliceComponent::SliceInstanceAddress& /*instance*/, const SliceInstantiationTicket& /*ticket*/) {}
/**
* Signals that a slice asset could not be instantiated.
* @param sliceAssetId The asset ID of the slice that failed to instantiate.
* @param ticket A ticket that identifies the slice instantiation request.
*/
virtual void OnSliceInstantiationFailed(const AZ::Data::AssetId& /*sliceAssetId*/, const SliceInstantiationTicket& /*ticket*/) {}
};
using SliceGameEntityOwnershipServiceNotificationBus = AZ::EBus<SliceGameEntityOwnershipServiceNotifications>;
}