Files
o3de/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesInterface.h
T
AMZN-koppersr 4ad0560d06 Removed AddOn(De)SpawnedHandler from Spawnable Entities Interface
The calls AddOnSpawnedHandler and AddOnDespawnedHandler were removed from the SpawnableEntitiesInterface. These functions will eventually be called from multiple threads and AZ::Event currently doesn't have a thread-safe version to support this. There's also a performance concern as these callbacks are called for each individual (de)spawn requests which can lead to multiple handlers being called without information that's relevant to the callback. It would be better to batch up all (de)spawn requests per ProcessQueue call and only have a single event do a single signal. Since both events are currently not being used they have been removed for now, but can be introduced -with the previously mentioned concerns in mind- when needed.
2021-06-09 14:22:40 -07:00

356 lines
18 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/RTTI/TypeSafeIntegral.h>
#include <AzCore/std/functional.h>
#include <AzFramework/Spawnable/Spawnable.h>
namespace AZ
{
class Entity;
class SerializeContext;
}
namespace AzFramework
{
AZ_TYPE_SAFE_INTEGRAL(SpawnablePriority, uint8_t);
inline static constexpr SpawnablePriority SpawnablePriority_Highest { 0 };
inline static constexpr SpawnablePriority SpawnablePriority_High { 32 };
inline static constexpr SpawnablePriority SpawnablePriority_Default { 128 };
inline static constexpr SpawnablePriority SpawnablePriority_Low { 192 };
inline static constexpr SpawnablePriority SpawnablePriority_Lowest { 255 };
class SpawnableEntityContainerView
{
public:
SpawnableEntityContainerView(AZ::Entity** begin, size_t length);
SpawnableEntityContainerView(AZ::Entity** begin, AZ::Entity** end);
AZ::Entity** begin();
AZ::Entity** end();
const AZ::Entity* const* cbegin();
const AZ::Entity* const* cend();
size_t size();
private:
AZ::Entity** m_begin;
AZ::Entity** m_end;
};
class SpawnableConstEntityContainerView
{
public:
SpawnableConstEntityContainerView(AZ::Entity** begin, size_t length);
SpawnableConstEntityContainerView(AZ::Entity** begin, AZ::Entity** end);
const AZ::Entity* const* begin();
const AZ::Entity* const* end();
const AZ::Entity* const* cbegin();
const AZ::Entity* const* cend();
size_t size();
private:
AZ::Entity** m_begin;
AZ::Entity** m_end;
};
class SpawnableIndexEntityPair
{
public:
friend class SpawnableIndexEntityIterator;
AZ::Entity* GetEntity();
const AZ::Entity* GetEntity() const;
size_t GetIndex() const;
private:
SpawnableIndexEntityPair() = default;
SpawnableIndexEntityPair(const SpawnableIndexEntityPair&) = default;
SpawnableIndexEntityPair(SpawnableIndexEntityPair&&) = default;
SpawnableIndexEntityPair(AZ::Entity** entityIterator, size_t* indexIterator);
SpawnableIndexEntityPair& operator=(const SpawnableIndexEntityPair&) = default;
SpawnableIndexEntityPair& operator=(SpawnableIndexEntityPair&&) = default;
AZ::Entity** m_entity { nullptr };
size_t* m_index { nullptr };
};
class SpawnableIndexEntityIterator
{
public:
// Limited to bidirectional iterator as there's no use case for extending it further, but can be extended if a use case is found.
using iterator_category = AZStd::bidirectional_iterator_tag;
using value_type = SpawnableIndexEntityPair;
using difference_type = size_t;
using pointer = SpawnableIndexEntityPair*;
using reference = SpawnableIndexEntityPair&;
SpawnableIndexEntityIterator(AZ::Entity** entityIterator, size_t* indexIterator);
SpawnableIndexEntityIterator& operator++();
SpawnableIndexEntityIterator operator++(int);
SpawnableIndexEntityIterator& operator--();
SpawnableIndexEntityIterator operator--(int);
bool operator==(const SpawnableIndexEntityIterator& rhs);
bool operator!=(const SpawnableIndexEntityIterator& rhs);
SpawnableIndexEntityPair& operator*();
const SpawnableIndexEntityPair& operator*() const;
SpawnableIndexEntityPair* operator->();
const SpawnableIndexEntityPair* operator->() const;
private:
SpawnableIndexEntityPair m_value;
};
class SpawnableConstIndexEntityContainerView
{
public:
SpawnableConstIndexEntityContainerView(AZ::Entity** beginEntity, size_t* beginIndices, size_t length);
const SpawnableIndexEntityIterator& begin();
const SpawnableIndexEntityIterator& end();
const SpawnableIndexEntityIterator& cbegin();
const SpawnableIndexEntityIterator& cend();
private:
SpawnableIndexEntityIterator m_begin;
SpawnableIndexEntityIterator m_end;
};
//! Requests to the SpawnableEntitiesInterface require a ticket with a valid spawnable that is used as a template. A ticket can
//! be reused for multiple calls on the same spawnable and is safe to be used by multiple threads at the same time. Entities created
//! from the spawnable may be tracked by the ticket and so using the same ticket is needed to despawn the exact entities created
//! by a call to spawn entities. The life cycle of the spawned entities is tied to the ticket and all entities spawned using a
//! ticket will be despawned when it's deleted.
class EntitySpawnTicket
{
public:
friend class SpawnableEntitiesDefinition;
using Id = uint64_t;
EntitySpawnTicket() = default;
EntitySpawnTicket(const EntitySpawnTicket&) = delete;
EntitySpawnTicket(EntitySpawnTicket&& rhs);
explicit EntitySpawnTicket(AZ::Data::Asset<Spawnable> spawnable);
~EntitySpawnTicket();
EntitySpawnTicket& operator=(const EntitySpawnTicket&) = delete;
EntitySpawnTicket& operator=(EntitySpawnTicket&& rhs);
Id GetId() const;
bool IsValid() const;
private:
void* m_payload{ nullptr };
Id m_id { 0 }; //!< An id that uniquely identifies a ticket.
};
using EntitySpawnCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using EntityPreInsertionCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>;
using EntityDespawnCallback = AZStd::function<void(EntitySpawnTicket::Id)>;
using ReloadSpawnableCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstEntityContainerView)>;
using ListIndicesEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableConstIndexEntityContainerView)>;
using ClaimEntitiesCallback = AZStd::function<void(EntitySpawnTicket::Id, SpawnableEntityContainerView)>;
using BarrierCallback = AZStd::function<void(EntitySpawnTicket::Id)>;
struct SpawnAllEntitiesOptionalArgs final
{
//! Callback that's called after instances of entities have been created, but before they're spawned into the world. This
//! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components.
EntityPreInsertionCallback m_preInsertionCallback;
//! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to spawn. The returned list of entities contains all the newly created entities.
EntitySpawnCallback m_completionCallback;
//! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used.
AZ::SerializeContext* m_serializeContext { nullptr };
//! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default };
};
struct SpawnEntitiesOptionalArgs final
{
//! Callback that's called after instances of entities have been created, but before they're spawned into the world. This
//! gives the opportunity to modify the entities if needed such as injecting additional components or modifying components.
EntityPreInsertionCallback m_preInsertionCallback;
//! Callback that's called when spawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to spawn. The returned list of entities contains all the newly created entities.
EntitySpawnCallback m_completionCallback;
//! The Serialize Context used to clone entities with. If this is not provided the global Serialize Contetx will be used.
AZ::SerializeContext* m_serializeContext{ nullptr };
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
//! Entity references are resolved by referring to the last entity spawned from a template entity in the spawnable. If this
//! is set to false entities from previous spawn calls are not taken into account. If set to true entity references may be
//! resolved to a previously spawned entity. A lookup table has to be constructed when true, which may negatively impact
//! performance, especially if a large number of entities are present on a ticket.
bool m_referencePreviouslySpawnedEntities{ false };
};
struct DespawnAllEntitiesOptionalArgs final
{
//! Callback that's called when despawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to despawn. The returned list of entities contains all the newly created entities.
EntityDespawnCallback m_completionCallback;
//! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default };
};
struct ReloadSpawnableOptionalArgs final
{
//! Callback that's called when respawning entities has completed. This can be triggered from a different thread than the one that
//! made the function call to respawn. The returned list of entities contains all the newly created entities.
ReloadSpawnableCallback m_completionCallback;
//! The Serialize Context used to clone entities with. If this is not provided the global Serialize Context will be used.
AZ::SerializeContext* m_serializeContext { nullptr };
//! The priority at which this call will be executed.
SpawnablePriority m_priority { SpawnablePriority_Default };
};
struct ListEntitiesOptionalArgs final
{
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
struct ClaimEntitiesOptionalArgs final
{
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
struct BarrierOptionalArgs final
{
//! The priority at which this call will be executed.
SpawnablePriority m_priority{ SpawnablePriority_Default };
};
//! Interface definition to (de)spawn entities from a spawnable into the game world.
//!
//! While the callbacks of the individual calls are being processed they will block processing any other request. Callbacks can be
//! issued from threads other than the one that issued the call, including the main thread.
//!
//! Calls on the same ticket are guaranteed to be executed in the order they are issued. Note that when issuing requests from
//! multiple threads on the same ticket the order in which the requests are assigned to the ticket is not guaranteed.
//!
//! Most calls have a priority with values that range from 0 (highest priority) to 255 (lowest priority). The implementation of this
//! interface may choose to use priority lanes which doesn't guarantee that higher priority requests happen before lower priority
//! requests if they don't pass the priority lane threshold. Priority lanes and their thresholds are implementation specific and may
//! differ between platforms. Note that if a call happened on a ticket with lower priority followed by a one with a higher priority
//! the first lower priority call will still need to complete before the second higher priority call can be executed and the priority
//! of the first call will not be updated.
class SpawnableEntitiesDefinition
{
public:
AZ_RTTI(AzFramework::SpawnableEntitiesDefinition, "{A9ED3F1F-4D69-4182-B0CD-EB561EEA7068}");
friend class EntitySpawnTicket;
virtual ~SpawnableEntitiesDefinition() = default;
//! Spawn instances of all entities in the spawnable.
//! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them.
//! @param optionalArgs Optional additional arguments, see SpawnAllEntitiesOptionalArgs.
virtual void SpawnAllEntities(EntitySpawnTicket& ticket, SpawnAllEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Spawn instances of some entities in the spawnable.
//! @param ticket Stores the results of the call. Use this ticket to spawn additional entities or to despawn them.
//! @param priority The priority at which this call will be executed.
//! @param entityIndices The indices into the template entities stored in the spawnable that will be used to spawn entities from.
//! @param optionalArgs Optional additional arguments, see SpawnEntitiesOptionalArgs.
virtual void SpawnEntities(
EntitySpawnTicket& ticket, AZStd::vector<size_t> entityIndices, SpawnEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes all entities in the provided list from the environment.
//! @param ticket The ticket previously used to spawn entities with.
//! @param priority The priority at which this call will be executed.
//! @param optionalArgs Optional additional arguments, see DespawnAllEntitiesOptionalArgs.
virtual void DespawnAllEntities(EntitySpawnTicket& ticket, DespawnAllEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Removes all entities in the provided list from the environment and reconstructs the entities from the provided spawnable.
//! @param ticket Holds the information on the entities to reload.
//! @param priority The priority at which this call will be executed.
//! @param spawnable The spawnable that will replace the existing spawnable. Both need to have the same asset id.
//! @param optionalArgs Optional additional arguments, see ReloadSpawnableOptionalArgs.
virtual void ReloadSpawnable(
EntitySpawnTicket& ticket, AZ::Data::Asset<Spawnable> spawnable, ReloadSpawnableOptionalArgs optionalArgs = {}) = 0;
//! List all entities that are spawned using this ticket.
//! @param ticket Only the entities associated with this ticket will be listed.
//! @param listCallback Required callback that will be called to list the entities on.
//! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs.
virtual void ListEntities(
EntitySpawnTicket& ticket, ListEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0;
//! List all entities that are spawned using this ticket with their spawnable index.
//! Spawnables contain a flat list of entities, which are used as templates to spawn entities from. For every spawned entity
//! the index of the entity in the spawnable that was used as a template is stored. This version of ListEntities will return
//! both the entities and this index. The index can be used with SpawnEntities to create the same entities again. Note that
//! the same index may appear multiple times as there are no restriction on how many instance of a specific entity can be
//! created.
//! @param ticket Only the entities associated with this ticket will be listed.
//! @param listCallback Required callback that will be called to list the entities and indices on.
//! @param optionalArgs Optional additional arguments, see ListEntitiesOptionalArgs.
virtual void ListIndicesAndEntities(
EntitySpawnTicket& ticket, ListIndicesEntitiesCallback listCallback, ListEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Claim all entities that are spawned using this ticket. Ownership of the entities is transferred from the ticket to the
//! caller through the callback. After this call the ticket will have no entities associated with it. The caller of
//! this function will need to manage the entities after this call.
//! @param ticket Only the entities associated with this ticket will be released.
//! @param listCallback Required callback that will be called to transfer the entities through.
//! @param optionalArgs Optional additional arguments, see ClaimEntitiesOptionalArgs.
virtual void ClaimEntities(
EntitySpawnTicket& ticket, ClaimEntitiesCallback listCallback, ClaimEntitiesOptionalArgs optionalArgs = {}) = 0;
//! Blocks until all operations made on the provided ticket before the barrier call have completed.
//! @param ticket The ticket to monitor.
//! @param completionCallback Required callback that will be called as soon as the barrier has been reached.
//! @param optionalArgs Optional additional arguments, see BarrierOptionalArgs.
virtual void Barrier(EntitySpawnTicket& ticket, BarrierCallback completionCallback, BarrierOptionalArgs optionalArgs = {}) = 0;
protected:
[[nodiscard]] virtual AZStd::pair<EntitySpawnTicket::Id, void*> CreateTicket(AZ::Data::Asset<Spawnable>&& spawnable) = 0;
virtual void DestroyTicket(void* ticket) = 0;
template<typename T>
static T& GetTicketPayload(EntitySpawnTicket& ticket)
{
return *reinterpret_cast<T*>(ticket.m_payload);
}
template<typename T>
static const T& GetTicketPayload(const EntitySpawnTicket& ticket)
{
return *reinterpret_cast<const T*>(ticket.m_payload);
}
template<typename T>
static T* GetTicketPayload(EntitySpawnTicket* ticket)
{
return reinterpret_cast<T*>(ticket->m_payload);
}
template<typename T>
static const T* GetTicketPayload(const EntitySpawnTicket* ticket)
{
return reinterpret_cast<const T*>(ticket->m_payload);
}
};
using SpawnableEntitiesInterface = AZ::Interface<SpawnableEntitiesDefinition>;
} // namespace AzFramework