Integrating latest 47acbe8
This commit is contained in:
@@ -349,7 +349,7 @@ namespace AZ
|
||||
// Update the currentGameSpecialization
|
||||
currentGameSpecialization = specializationKey;
|
||||
|
||||
// Update all the runtime filepaths based on the new "sys_game_folder" value
|
||||
// Update all the runtime filepaths based on the new "sys_game_folder" value
|
||||
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
||||
}
|
||||
}
|
||||
@@ -460,6 +460,23 @@ namespace AZ
|
||||
//=========================================================================
|
||||
void ComponentApplication::CreateCommon()
|
||||
{
|
||||
{
|
||||
AZ::SettingsRegistryInterface::FixedValueString registryValue;
|
||||
m_settingsRegistry->Get(registryValue, AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage);
|
||||
AZ::IO::FixedMaxPath outputPath{ registryValue };
|
||||
outputPath /= "eventlogger";
|
||||
|
||||
registryValue.clear();
|
||||
|
||||
AZ::IO::FixedMaxPathString baseFileName{ "EventLog" }; // default name
|
||||
if (m_settingsRegistry->Get(registryValue, AZ::SettingsRegistryMergeUtils::BuildTargetNameKey))
|
||||
{
|
||||
baseFileName = registryValue;
|
||||
}
|
||||
|
||||
m_eventLogger.Start(outputPath.c_str(), baseFileName.c_str());
|
||||
}
|
||||
|
||||
CreateDrillers();
|
||||
|
||||
Sfmt::Create();
|
||||
@@ -584,6 +601,8 @@ namespace AZ
|
||||
m_drillerManager = nullptr;
|
||||
}
|
||||
|
||||
m_eventLogger.Stop();
|
||||
|
||||
// Clear the descriptor to deallocate all strings (owned by ModuleDescriptor)
|
||||
m_descriptor = Descriptor();
|
||||
|
||||
@@ -997,7 +1016,7 @@ namespace AZ
|
||||
AZ::OSString m_dynamicLibraryPath;
|
||||
bool m_autoLoad{ true };
|
||||
};
|
||||
|
||||
|
||||
struct GemModuleVisitor
|
||||
: AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
@@ -1020,7 +1039,7 @@ namespace AZ
|
||||
{
|
||||
return gemName == moduleLoadData.m_gemName;
|
||||
};
|
||||
|
||||
|
||||
if (auto foundIt = AZStd::find_if(m_modulesLoadData.begin(), m_modulesLoadData.end(), FindGemModuleLoadEntry);
|
||||
foundIt == m_modulesLoadData.end())
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Debug/ProfileModuleInit.h>
|
||||
#include <AzCore/Debug/LocalFileEventLogger.h>
|
||||
#include <AzCore/Memory/AllocationRecords.h>
|
||||
#include <AzCore/Memory/OSAllocator.h>
|
||||
#include <AzCore/Module/DynamicModuleHandle.h>
|
||||
@@ -269,7 +270,7 @@ namespace AZ
|
||||
void ResolveModulePath(AZ::OSString& modulePath) override;
|
||||
|
||||
void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override;
|
||||
|
||||
|
||||
/**
|
||||
* Returns Parsed CommandLine structure which supports query command line options and positional parameters
|
||||
*/
|
||||
@@ -282,12 +283,12 @@ namespace AZ
|
||||
* and on some operating systems (MacOS) its fairly difficult to reliably retrieve them without resorting to NS libraries
|
||||
* and making some assumptions. Instead, we allow you to pass your args in from the main(...) function.
|
||||
* Another thing to notice here is that these are non-const pointers to the argc and argv values
|
||||
* instead of int, char**, these are int*, char***.
|
||||
* This is because some application layers (such as Qt) actually require that the ArgC and ArgV are modifiable,
|
||||
* instead of int, char**, these are int*, char***.
|
||||
* This is because some application layers (such as Qt) actually require that the ArgC and ArgV are modifiable,
|
||||
* as they actually patch them to add/remove command line parameters during initialization.
|
||||
* but also to highlight the fact that they are pointers to static memory that must remain relevant throughout the existence
|
||||
* of the Application object.
|
||||
* For best results, simply pass in &argc and &argv from your void main(argc, argv) in here - that memory is
|
||||
* For best results, simply pass in &argc and &argv from your void main(argc, argv) in here - that memory is
|
||||
* permanently tied to your process and is going to be available at all times during run.
|
||||
*/
|
||||
int* GetArgC();
|
||||
@@ -368,7 +369,7 @@ namespace AZ
|
||||
/**
|
||||
* Check/verify a given path for the engine marker (file) so that we can identify that
|
||||
* a given path is the engine root. This is only valid for target platforms that are built
|
||||
* for the host platform and not deployable (ie windows, mac).
|
||||
* for the host platform and not deployable (ie windows, mac).
|
||||
* @param fullPath The full path to look for the engine marker
|
||||
* @return true if the input path contains the engine marker file, false if not
|
||||
*/
|
||||
@@ -422,6 +423,11 @@ namespace AZ
|
||||
AZ::CommandLine m_commandLine; // < Stores parsed command line supplied to the constructor
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> m_systemEntity; ///< Track the system entity to ensure we free it on shutdown.
|
||||
|
||||
// Created early to allow events to be logged before anything else. These will be kept in memory until
|
||||
// a file is associated with the logger. The internal buffer is limited to 64kb and once full unexpected
|
||||
// behavior may happen. The LocalFileEventLogger will register itself automatically with AZ::Interface<IEventLogger>.
|
||||
AZ::Debug::LocalFileEventLogger m_eventLogger;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/EntityBus.h>
|
||||
#include <AzCore/Component/EntityIdSerializer.h>
|
||||
#include <AzCore/Component/EntitySerializer.h>
|
||||
#include <AzCore/Component/EntityUtils.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
@@ -71,38 +72,27 @@ namespace AZ
|
||||
}
|
||||
};
|
||||
|
||||
Entity::Entity(const char* name)
|
||||
: m_state(State::Constructed)
|
||||
, m_transform(nullptr)
|
||||
, m_isDependencyReady(false)
|
||||
, m_isRuntimeActiveByDefault(true)
|
||||
//=========================================================================
|
||||
// Entity
|
||||
// [5/31/2012]
|
||||
//=========================================================================
|
||||
Entity::Entity(AZStd::string name)
|
||||
: Entity(MakeId(), AZStd::move(name))
|
||||
{
|
||||
m_id = MakeId();
|
||||
if (name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
to_string(m_name, static_cast<u64>(m_id));
|
||||
}
|
||||
}
|
||||
|
||||
Entity::Entity(const EntityId& id, const char* name)
|
||||
//=========================================================================
|
||||
// Entity
|
||||
// [5/30/2012]
|
||||
//=========================================================================
|
||||
Entity::Entity(const EntityId& id, AZStd::string name)
|
||||
: m_id(id)
|
||||
, m_state(State::Constructed)
|
||||
, m_transform(nullptr)
|
||||
, m_name{ name.empty() ? AZStd::to_string(static_cast<u64>(m_id)) : AZStd::move(name) }
|
||||
, m_state(State::Constructed)
|
||||
, m_isDependencyReady(false)
|
||||
, m_isRuntimeActiveByDefault(true)
|
||||
{
|
||||
if (name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
to_string(m_name, static_cast<u64>(m_id));
|
||||
}
|
||||
}
|
||||
|
||||
Entity::~Entity()
|
||||
@@ -825,6 +815,7 @@ namespace AZ
|
||||
AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast<AZ::JsonRegistrationContext*>(reflection);
|
||||
if (jsonRegistration)
|
||||
{
|
||||
jsonRegistration->Serializer<JsonEntitySerializer>()->HandlesType<Entity>();
|
||||
jsonRegistration->Serializer<JsonEntityIdSerializer>()->HandlesType<EntityId>();
|
||||
}
|
||||
}
|
||||
@@ -1153,8 +1144,8 @@ namespace AZ
|
||||
{
|
||||
if (processingRequiredServices)
|
||||
{
|
||||
return FailureCode(DependencySortResult::MissingRequiredService, "Component '%s' is missing another required component.",
|
||||
componentInfo.m_component->RTTI_GetTypeName());
|
||||
return FailureCode(DependencySortResult::MissingRequiredService, "Component '%s' is missing another required service: 0x%0x",
|
||||
componentInfo.m_component->RTTI_GetTypeName(), service);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace AZ
|
||||
//! An entity has an ID and, optionally, a name.
|
||||
class Entity
|
||||
{
|
||||
friend class JsonEntitySerializer;
|
||||
|
||||
public:
|
||||
|
||||
//! Specifies that this class should use AZ::SystemAllocator for memory management by default.
|
||||
@@ -84,16 +86,20 @@ namespace AZ
|
||||
DSR_CYCLIC_DEPENDENCY = HasCyclicDependency,
|
||||
};
|
||||
|
||||
//! Constructs an entity and automatically generates an entity ID.
|
||||
//! @param name (Optional) A name for the entity. The entity ID is used for addressing and identification,
|
||||
//! but a name is useful for debugging.
|
||||
Entity(const char* name = nullptr);
|
||||
/**
|
||||
* Constructs an entity and automatically generates an entity ID.
|
||||
* @param name (Optional) A name for the entity. The entity ID is used for addressing and identification,
|
||||
* but a name is useful for debugging.
|
||||
*/
|
||||
explicit Entity(AZStd::string name = {});
|
||||
|
||||
//! Constructs an entity with the entity ID that you specify.
|
||||
//! @param id An ID for the entity.
|
||||
//! @param name (Optional) A name for the entity. The entity ID is used for addressing and identification,
|
||||
//! but a name is useful for debugging.
|
||||
Entity(const EntityId& id, const char* name = nullptr);
|
||||
/**
|
||||
* Constructs an entity with the entity ID that you specify.
|
||||
* @param id An ID for the entity.
|
||||
* @param name (Optional) A name for the entity. The entity ID is used for addressing and identification,
|
||||
* but a name is useful for debugging.
|
||||
*/
|
||||
explicit Entity(const EntityId& id, AZStd::string name = {});
|
||||
|
||||
// Delete the copy constructor, because this contains vector of pointers and other pointers that
|
||||
// are supposed to be unique, this would be a mistake. Its safer to cause code that tries to
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace AZ
|
||||
|
||||
virtual JsonSerializationResult::Result MapJsonToId(EntityId& outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context) = 0;
|
||||
virtual JsonSerializationResult::Result MapIdToJson(rapidjson::Value& outputValue, const EntityId& inputValue, JsonSerializerContext& context) = 0;
|
||||
|
||||
inline void SetIsEntityReference(bool isEntityReference) { m_isEntityReference = isEntityReference; };
|
||||
|
||||
protected:
|
||||
bool m_isEntityReference = true;
|
||||
};
|
||||
|
||||
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/EntityIdSerializer.h>
|
||||
#include <AzCore/Component/EntitySerializer.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonEntitySerializer, AZ::SystemAllocator, 0);
|
||||
|
||||
JsonSerializationResult::Result JsonEntitySerializer::Load(void* outputValue, [[maybe_unused]] const Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = JsonSerializationResult;
|
||||
|
||||
AZ_Assert(azrtti_typeid<AZ::Entity>() == outputValueTypeId, "Unable to deserialize Entity from json because the provided type is %s.",
|
||||
outputValueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
AZ::Entity* entityInstance = reinterpret_cast<AZ::Entity*>(outputValue);
|
||||
AZ_Assert(entityInstance, "Output value for JsonEntitySerializer can't be null.");
|
||||
|
||||
JsonEntityIdSerializer::JsonEntityIdMapper** idMapper = context.GetMetadata().Find<JsonEntityIdSerializer::JsonEntityIdMapper*>();
|
||||
bool hasValidIdMapper = idMapper && *idMapper;
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
{
|
||||
if (hasValidIdMapper)
|
||||
{
|
||||
(*idMapper)->SetIsEntityReference(false);
|
||||
}
|
||||
|
||||
JSR::ResultCode idLoadResult =
|
||||
ContinueLoadingFromJsonObjectField(&entityInstance->m_id,
|
||||
azrtti_typeid<decltype(entityInstance->m_id)>(),
|
||||
inputValue, "Id", context);
|
||||
|
||||
if (hasValidIdMapper)
|
||||
{
|
||||
(*idMapper)->SetIsEntityReference(true);
|
||||
}
|
||||
|
||||
result.Combine(idLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode nameLoadResult =
|
||||
ContinueLoadingFromJsonObjectField(&entityInstance->m_name,
|
||||
azrtti_typeid<decltype(entityInstance->m_name)>(),
|
||||
inputValue, "Name", context);
|
||||
|
||||
result.Combine(nameLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode componentLoadResult =
|
||||
ContinueLoadingFromJsonObjectField(&entityInstance->m_components,
|
||||
azrtti_typeid<decltype(entityInstance->m_components)>(),
|
||||
inputValue, "Components", context);
|
||||
|
||||
result.Combine(componentLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode dependencyReadyLoadResult =
|
||||
ContinueLoadingFromJsonObjectField(&entityInstance->m_isDependencyReady,
|
||||
azrtti_typeid<decltype(entityInstance->m_isDependencyReady)>(),
|
||||
inputValue, "IsDependencyReady", context);
|
||||
|
||||
result.Combine(dependencyReadyLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
JSR::ResultCode runtimeActiveLoadResult =
|
||||
ContinueLoadingFromJsonObjectField(&entityInstance->m_isRuntimeActiveByDefault,
|
||||
azrtti_typeid<decltype(entityInstance->m_isRuntimeActiveByDefault)>(),
|
||||
inputValue, "IsRuntimeActive", context);
|
||||
}
|
||||
|
||||
return context.Report(result,
|
||||
result.GetProcessing() == JSR::Processing::Halted ? "Succesfully loaded entity information." :
|
||||
"Failed to load entity information.");
|
||||
}
|
||||
|
||||
JsonSerializationResult::Result JsonEntitySerializer::Store(rapidjson::Value& outputValue,
|
||||
const void* inputValue, const void* defaultValue, [[maybe_unused]] const Uuid& valueTypeId, JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = AZ::JsonSerializationResult;
|
||||
|
||||
AZ_Assert(azrtti_typeid<AZ::Entity>() == valueTypeId, "Unable to Serialize Entity because the provided type is %s.",
|
||||
valueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
const AZ::Entity* entityInstance = reinterpret_cast<const AZ::Entity*>(inputValue);
|
||||
AZ_Assert(entityInstance, "Input value for JsonEntitySerializer can't be null.");
|
||||
const AZ::Entity* defaultEntityInstance = reinterpret_cast<const AZ::Entity*>(defaultValue);
|
||||
|
||||
JsonEntityIdSerializer::JsonEntityIdMapper** idMapper = context.GetMetadata().Find<JsonEntityIdSerializer::JsonEntityIdMapper*>();
|
||||
bool hasValidIdMapper = idMapper && *idMapper;
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
AZ::ScopedContextPath subPathId(context, "m_id");
|
||||
const AZ::EntityId* id = &entityInstance->m_id;
|
||||
const AZ::EntityId* defaultId = defaultEntityInstance ? &defaultEntityInstance->m_id : nullptr;
|
||||
|
||||
if (hasValidIdMapper)
|
||||
{
|
||||
(*idMapper)->SetIsEntityReference(false);
|
||||
}
|
||||
|
||||
result = ContinueStoringToJsonObjectField(outputValue, "Id",
|
||||
id, defaultId, azrtti_typeid<decltype(entityInstance->m_id)>(), context);
|
||||
|
||||
if (hasValidIdMapper)
|
||||
{
|
||||
(*idMapper)->SetIsEntityReference(true);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_name");
|
||||
const AZStd::string* name = &entityInstance->m_name;
|
||||
const AZStd::string* defaultName =
|
||||
defaultEntityInstance ? &defaultEntityInstance->m_name : nullptr;
|
||||
|
||||
JSR::ResultCode resultName =
|
||||
ContinueStoringToJsonObjectField(outputValue, "Name",
|
||||
name, defaultName, azrtti_typeid<decltype(entityInstance->m_name)>(), context);
|
||||
|
||||
result.Combine(resultName);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathComponents(context, "m_components");
|
||||
const AZ::Entity::ComponentArrayType* components = &entityInstance->m_components;
|
||||
const AZ::Entity::ComponentArrayType* defaultComponents =
|
||||
defaultEntityInstance ? &defaultEntityInstance->m_components : nullptr;
|
||||
|
||||
JSR::ResultCode resultComponents =
|
||||
ContinueStoringToJsonObjectField(outputValue, "Components",
|
||||
components, defaultComponents, azrtti_typeid<decltype(entityInstance->m_components)>(), context);
|
||||
|
||||
result.Combine(resultComponents);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathDependencyReady(context, "m_isDependencyReady");
|
||||
const bool* dependencyReady = &entityInstance->m_isDependencyReady;
|
||||
const bool* dependencyReadyDefault =
|
||||
defaultEntityInstance ? &defaultEntityInstance->m_isDependencyReady : nullptr;
|
||||
|
||||
JSR::ResultCode resultDependencyReady =
|
||||
ContinueStoringToJsonObjectField(outputValue, "IsDependencyReady",
|
||||
dependencyReady, dependencyReadyDefault,
|
||||
azrtti_typeid<decltype(entityInstance->m_isDependencyReady)>(), context);
|
||||
|
||||
result.Combine(resultDependencyReady);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathRuntimeActive(context, "m_isRuntimeActiveByDefault");
|
||||
const bool* runtimeActive = &entityInstance->m_isRuntimeActiveByDefault;
|
||||
const bool* runtimeActiveDefault =
|
||||
defaultEntityInstance ? &defaultEntityInstance->m_isRuntimeActiveByDefault : nullptr;
|
||||
|
||||
JSR::ResultCode resultRuntimeActive =
|
||||
ContinueStoringToJsonObjectField(outputValue, "IsRuntimeActive",
|
||||
runtimeActive, runtimeActiveDefault, azrtti_typeid<decltype(entityInstance->m_isRuntimeActiveByDefault)>(), context);
|
||||
|
||||
result.Combine(resultRuntimeActive);
|
||||
}
|
||||
|
||||
return context.Report(result,
|
||||
result.GetProcessing() == JSR::Processing::Halted ? "Successfully stored Entity information." :
|
||||
"Failed to store Entity information.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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/Memory/Memory.h>
|
||||
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class JsonEntitySerializer
|
||||
: public BaseJsonSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonEntitySerializer, "{015BBF46-E21A-41AA-816A-C63119FB2852}", BaseJsonSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
|
||||
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
JsonDeserializerContext& context) override;
|
||||
|
||||
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId,
|
||||
JsonSerializerContext& context) override;
|
||||
};
|
||||
}
|
||||
@@ -19,6 +19,9 @@ namespace AZ
|
||||
{
|
||||
class Vector3;
|
||||
|
||||
//! Do not allow the scale to be zero to avoid problems with inverting scale.
|
||||
static constexpr float MinNonUniformScale = 1e-3f;
|
||||
|
||||
using NonUniformScaleChangedEvent = AZ::Event<const AZ::Vector3&>;
|
||||
|
||||
//! Requests for working with non-uniform scale.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 <type_traits>
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
//! Simple hash structure based on DJB2a to generate event IDs at compile time
|
||||
class EventNameHash
|
||||
{
|
||||
public:
|
||||
constexpr explicit EventNameHash(AZStd::string_view name)
|
||||
{
|
||||
for (auto c : name)
|
||||
{
|
||||
m_hash = static_cast<uint32_t>(((static_cast<uint64_t>(m_hash) << 5) + m_hash) ^ c);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool operator==(const EventNameHash& rhs) const
|
||||
{
|
||||
return m_hash == rhs.m_hash;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const EventNameHash& rhs) const
|
||||
{
|
||||
return m_hash != rhs.m_hash;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_hash{ 5381 }; // standard starting value for DJB2a hash
|
||||
};
|
||||
|
||||
|
||||
constexpr EventNameHash PrologEventHash("Prolog");
|
||||
constexpr uint16_t EventBoundary = 8;
|
||||
|
||||
|
||||
class IEventLogger
|
||||
{
|
||||
public:
|
||||
struct LogHeader
|
||||
{
|
||||
int8_t m_4cc[4]{ 'A', 'Z', 'E', 'L' }; //!< 4CC to uniquely identify the data type. Defaults to 'AZEL'
|
||||
uint32_t m_majorVersion{ 1 }; //!< Major version of the log format.
|
||||
uint32_t m_minorVersion{ 0 }; //!< Minor version of the log format.
|
||||
//! A user defined version. This will always be zero but allows users
|
||||
//! make modifications without needing to change the main version number
|
||||
//! which in turn makes integrations easier.
|
||||
uint32_t m_userVersion{ 0 };
|
||||
};
|
||||
|
||||
struct EventHeader
|
||||
{
|
||||
EventNameHash m_eventId; //!< Unique id that identifies the event. This is typically a hash of the event name.
|
||||
uint16_t m_size; //!< The size of the event. Events can be up to 64Kib large.
|
||||
//! Event specific flags set by the caller. The flags can be to reuse the same event with a slight
|
||||
//! alteration, such as a begin/end pair. If two similar have different data, such as a begin
|
||||
//! having a bit of extra data that the end doesn't have, then it's recommended to create two unique
|
||||
//! events instead to keep the log small.
|
||||
uint16_t m_flags;
|
||||
};
|
||||
|
||||
struct Prolog : public EventHeader
|
||||
{
|
||||
uint64_t m_threadId; //!< Unique id of the thread the log buffer is being recorded on.
|
||||
};
|
||||
|
||||
AZ_TYPE_INFO(AZ::Debug::IEventLogger, "{D39D09FA-DEA0-4874-BC45-4B310C3DD52E}");
|
||||
|
||||
virtual ~IEventLogger() = default;
|
||||
|
||||
//! Writes and flushes all thread local buffers to disk and flushes the disk to store the recorded events.
|
||||
virtual void Flush() = 0;
|
||||
|
||||
//! Starts a new event. If there is not enough room left in the thread local buffer then the buffer will be stored to disk and cleared.
|
||||
//! @param id Id that uniquely identifies this event.
|
||||
//! @param size The total size of the event, excluding the event header. Typically this is the size of the structure that describes the event.
|
||||
//! @param flags Optional flags unique to the event. For instance a "Thread" event can use the flags to indicate whether the
|
||||
//! the thread is starting or stopping.
|
||||
//! @return A void pointer to reserved data in the thread local buffer to write to.
|
||||
virtual void* RecordEventBegin(EventNameHash id, uint16_t size, uint16_t flags = 0) = 0;
|
||||
|
||||
//! End a previously started event. After calling RecordEventBegin, flushing will not be possible until RecordEventEnd is called.
|
||||
virtual void RecordEventEnd() = 0;
|
||||
|
||||
//! Utility function to write an event that only has a string.
|
||||
//! @param id Id that uniquely identifies this event.
|
||||
//! @param text The string that will be logged.
|
||||
//! @param flags Optional flags unique to the event.
|
||||
virtual void RecordStringEvent(EventNameHash id, AZStd::string_view text, uint16_t flags = 0) = 0;
|
||||
|
||||
//! Utility function to begin an event with a specific structure.
|
||||
//! For example this can be used as:
|
||||
//! struct ThreadInfo
|
||||
//! {
|
||||
//! uint64_t m_threadId;
|
||||
//! uint64_t m_processorId;
|
||||
//! };
|
||||
//! auto& info = RecordEventBegin<ThreadInfo>("ThreadInfo");
|
||||
//! info.m_threadId = ...;
|
||||
//! info.m_processorId = ...;
|
||||
//! RecordEventEnd();
|
||||
//! @param id Id that uniquely identifies this event.
|
||||
//! @param flags Optional flags unique to the event.
|
||||
//! @return A reference of the provided type to store event information in.
|
||||
template<typename T>
|
||||
T& RecordEventBegin(EventNameHash id, uint16_t flags = 0);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T& IEventLogger::RecordEventBegin(EventNameHash id, uint16_t flags)
|
||||
{
|
||||
constexpr size_t typeSize = sizeof(T);
|
||||
|
||||
static_assert(AZStd::is_trivially_copyable_v<T>, "Only simple classes can be added to the event logger.");
|
||||
static_assert(typeSize <= AZStd::numeric_limits<decltype(EventHeader::m_size)>::max(), "Class too large to store with the event logger.");
|
||||
|
||||
void* eventData = RecordEventBegin(id, aznumeric_cast<uint16_t>(typeSize), flags);
|
||||
return *reinterpret_cast<T*>(eventData);
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* 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/Platform.h>
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Debug/LocalFileEventLogger.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
static constexpr const char* RegistryKey_TimestampLogFiles = "/Amazon/AzCore/EventLogger/TimestampLogFiles";
|
||||
|
||||
//
|
||||
// EventLogReader
|
||||
//
|
||||
|
||||
bool EventLogReader::ReadLog(const char* filePath)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
if (SystemFile::Exists(filePath))
|
||||
{
|
||||
SystemFile::SizeType size = SystemFile::Length(filePath);
|
||||
if (size == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_buffer.resize_no_construct(size);
|
||||
uint8_t* buffer = m_buffer.data();
|
||||
|
||||
SystemFile::SizeType readSize = SystemFile::Read(filePath, buffer, size);
|
||||
if (readSize == size)
|
||||
{
|
||||
memcpy(&m_logHeader, buffer, sizeof(m_logHeader));
|
||||
m_current = reinterpret_cast<IEventLogger::EventHeader*>(buffer + sizeof(m_logHeader));
|
||||
UpdateThreadId();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EventNameHash EventLogReader::GetEventName() const
|
||||
{
|
||||
return m_current->m_eventId;
|
||||
}
|
||||
|
||||
uint16_t EventLogReader::GetEventSize() const
|
||||
{
|
||||
return m_current->m_size;
|
||||
}
|
||||
|
||||
uint16_t EventLogReader::GetEventFlags() const
|
||||
{
|
||||
return m_current->m_flags;
|
||||
}
|
||||
|
||||
uint64_t EventLogReader::GetThreadId() const
|
||||
{
|
||||
return m_currentThreadId;
|
||||
}
|
||||
|
||||
AZStd::string_view EventLogReader::GetString() const
|
||||
{
|
||||
return AZStd::string_view(reinterpret_cast<char*>(m_current + 1), m_current->m_size);
|
||||
}
|
||||
|
||||
bool EventLogReader::Next()
|
||||
{
|
||||
size_t increment = AZ_SIZE_ALIGN_UP(sizeof(IEventLogger::EventHeader) + m_current->m_size, EventBoundary);
|
||||
uint8_t* bufferPosition = reinterpret_cast<uint8_t*>(m_current) + increment;
|
||||
if (bufferPosition < m_buffer.end())
|
||||
{
|
||||
m_current = reinterpret_cast<IEventLogger::EventHeader*>(bufferPosition);
|
||||
UpdateThreadId();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EventLogReader::UpdateThreadId()
|
||||
{
|
||||
if (GetEventName() == PrologEventHash)
|
||||
{
|
||||
auto prolog = reinterpret_cast<IEventLogger::Prolog*>(m_current);
|
||||
m_currentThreadId = prolog->m_threadId;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// LocalFileEventLogger
|
||||
//
|
||||
|
||||
LocalFileEventLogger::~LocalFileEventLogger()
|
||||
{
|
||||
if (m_file.IsOpen())
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
// thread blocks should have already been flushed above, so
|
||||
// this is purely to clear the logger ownership safely
|
||||
while (!m_threadDataBlocks.empty())
|
||||
{
|
||||
m_threadDataBlocks.back()->Reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalFileEventLogger::Start(const AZ::IO::Path& filePath)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
AZStd::scoped_lock lock(m_fileGuard);
|
||||
if (m_file.Open(filePath.c_str(), SystemFile::SF_OPEN_WRITE_ONLY | SystemFile::SF_OPEN_CREATE | SystemFile::SF_OPEN_CREATE_PATH))
|
||||
{
|
||||
LogHeader defaultHeader;
|
||||
m_file.Write(&defaultHeader, sizeof(LogHeader));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LocalFileEventLogger::Start(AZStd::string_view outputPath, AZStd::string_view fileNameHint)
|
||||
{
|
||||
using namespace AZ::IO;
|
||||
|
||||
FixedMaxPath filePath{ outputPath };
|
||||
FixedMaxPathString fileName{ fileNameHint };
|
||||
|
||||
SystemFile::CreateDir(filePath.c_str());
|
||||
|
||||
bool includeTimestamp = false;
|
||||
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
settingsRegistry->Get(includeTimestamp, RegistryKey_TimestampLogFiles);
|
||||
|
||||
if (includeTimestamp)
|
||||
{
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
|
||||
tm timeinfo;
|
||||
azlocaltime(&rawtime, &timeinfo);
|
||||
|
||||
constexpr int timestampSize = 64;
|
||||
char timestamp[timestampSize]{ 0 };
|
||||
|
||||
// based the ISO-8601 standard (YYYY-MM-DDTHH-mm-ssTZD) e.g., 20210224_1122
|
||||
strftime(timestamp, timestampSize, "%Y%m%d_%H%M", &timeinfo);
|
||||
|
||||
fileName = AZ::IO::FixedMaxPathString::format("%.*s_%s",
|
||||
aznumeric_cast<int>(fileNameHint.size()), fileNameHint.data(),
|
||||
timestamp);
|
||||
}
|
||||
|
||||
filePath /= fileName;
|
||||
filePath.ReplaceExtension("azel");
|
||||
|
||||
return Start(filePath.c_str());
|
||||
}
|
||||
|
||||
void LocalFileEventLogger::Stop()
|
||||
{
|
||||
Flush();
|
||||
m_file.Close();
|
||||
}
|
||||
|
||||
void LocalFileEventLogger::Flush()
|
||||
{
|
||||
// Create new storage for a thread to write to. This will replace the storage already on the thread
|
||||
// so it can continue to write and is not blocked during a flush. The data that was swapped in can
|
||||
// then again be used for the next thread.
|
||||
ThreadData* replacementData = new ThreadData();
|
||||
bool flushedThread[MaxThreadCount] = {};
|
||||
|
||||
{
|
||||
AZStd::scoped_lock fileGuardLock(m_fileGuard);
|
||||
bool allFlushed;
|
||||
do
|
||||
{
|
||||
allFlushed = true;
|
||||
for (size_t i = 0; i < m_threadDataBlocks.size(); ++i)
|
||||
{
|
||||
// Don't flush threads that have already been flushed because during high activity
|
||||
// this can cause this loop to always find more threads to flush resulting in
|
||||
// taking a long time to exit the Flush function. As a side effect of this it will
|
||||
// decrease the time between retrying a thread it previous failed to claim which
|
||||
// increases the chance it gets to switch the data.
|
||||
if (flushedThread[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ThreadStorage* thread = m_threadDataBlocks[i];
|
||||
ThreadData* threadData = thread->m_data;
|
||||
if (!threadData)
|
||||
{
|
||||
allFlushed = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// ensure the thread ID propagates after the exchange
|
||||
replacementData->m_threadId = threadData->m_threadId;
|
||||
if (!thread->m_data.compare_exchange_strong(threadData, replacementData))
|
||||
{
|
||||
// Since no other flush can reach this point due to the lock, failing this means
|
||||
// that between looking up the address for the data and the swap the owning thread
|
||||
// has started a write, so bail for now and come back to this one at a later time
|
||||
// to try again.
|
||||
allFlushed = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::scoped_lock fileWriteGuardLock(m_fileWriteGuard);
|
||||
WriteCacheToDisk(*threadData);
|
||||
}
|
||||
replacementData = threadData;
|
||||
flushedThread[i] = true;
|
||||
}
|
||||
} while (!allFlushed);
|
||||
m_file.Flush();
|
||||
}
|
||||
|
||||
delete replacementData;
|
||||
}
|
||||
|
||||
void* LocalFileEventLogger::RecordEventBegin(EventNameHash id, uint16_t size, uint16_t flags)
|
||||
{
|
||||
ThreadStorage& threadStorage = GetThreadStorage();
|
||||
ThreadData* threadData = threadStorage.m_data;
|
||||
|
||||
// Set to nullptr so other threads doing a flush can't pick this up.
|
||||
while (!threadStorage.m_data.compare_exchange_strong(threadData, nullptr));
|
||||
|
||||
uint32_t writeSize = AZ_SIZE_ALIGN_UP(sizeof(EventHeader) + size, EventBoundary);
|
||||
if (threadData->m_usedBytes + writeSize >= ThreadData::BufferSize)
|
||||
{
|
||||
AZStd::scoped_lock lock(m_fileWriteGuard);
|
||||
WriteCacheToDisk(*threadData);
|
||||
}
|
||||
|
||||
char* eventBuffer = (threadData->m_buffer + threadData->m_usedBytes);
|
||||
EventHeader* header = reinterpret_cast<EventHeader*>(eventBuffer);
|
||||
header->m_eventId = id;
|
||||
header->m_size = size;
|
||||
header->m_flags = flags;
|
||||
threadData->m_usedBytes += writeSize;
|
||||
|
||||
// cache the event data so it doesn't get picked up by calls to flush
|
||||
// before it has been committed
|
||||
threadStorage.m_pendingData = threadData;
|
||||
|
||||
return (eventBuffer + sizeof(EventHeader));
|
||||
}
|
||||
|
||||
void LocalFileEventLogger::RecordEventEnd()
|
||||
{
|
||||
// swap the pending data to commit the event
|
||||
ThreadStorage& threadStorage = GetThreadStorage();
|
||||
ThreadData* expectedData = nullptr;
|
||||
while (!threadStorage.m_data.compare_exchange_strong(expectedData, threadStorage.m_pendingData));
|
||||
threadStorage.m_pendingData = nullptr;
|
||||
}
|
||||
|
||||
void LocalFileEventLogger::RecordStringEvent(EventNameHash id, AZStd::string_view text, uint16_t flags)
|
||||
{
|
||||
constexpr size_t maxSize = AZStd::numeric_limits<decltype(EventHeader::m_size)>::max();
|
||||
const size_t stringLen = text.length();
|
||||
|
||||
if (stringLen > maxSize)
|
||||
{
|
||||
AZ_Assert(false, "Failed to write event! String too large to store with the event logger.");
|
||||
return;
|
||||
}
|
||||
|
||||
void* eventText = RecordEventBegin(id, aznumeric_cast<uint16_t>(stringLen), flags);
|
||||
memcpy(eventText, text.data(), stringLen);
|
||||
RecordEventEnd();
|
||||
}
|
||||
|
||||
void LocalFileEventLogger::WriteCacheToDisk(ThreadData& threadData)
|
||||
{
|
||||
// ensure the front loaded prolog is accurate, ThreadData objects
|
||||
// are recycled during flush
|
||||
Prolog* prologHeader = reinterpret_cast<Prolog*>(threadData.m_buffer);
|
||||
prologHeader->m_eventId = PrologEventHash;
|
||||
prologHeader->m_size = sizeof(prologHeader->m_threadId);
|
||||
prologHeader->m_flags = 0; // unused in the prolog
|
||||
prologHeader->m_threadId = threadData.m_threadId;
|
||||
|
||||
m_file.Write(threadData.m_buffer, threadData.m_usedBytes);
|
||||
threadData.m_usedBytes = sizeof(Prolog); // keep enough room for the next chunk's prolog
|
||||
}
|
||||
|
||||
auto LocalFileEventLogger::GetThreadStorage()->ThreadStorage&
|
||||
{
|
||||
thread_local static ThreadStorage s_storage;
|
||||
s_storage.Reset(this);
|
||||
return s_storage;
|
||||
}
|
||||
|
||||
//
|
||||
// LocalFileEventLogger::ThreadStorage
|
||||
//
|
||||
|
||||
LocalFileEventLogger::ThreadStorage::~ThreadStorage()
|
||||
{
|
||||
Reset(nullptr);
|
||||
}
|
||||
|
||||
void LocalFileEventLogger::ThreadStorage::Reset(LocalFileEventLogger* owner)
|
||||
{
|
||||
if (m_owner == owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_owner)
|
||||
{
|
||||
AZStd::scoped_lock guard(m_owner->m_fileGuard);
|
||||
|
||||
// Save to access thread data because of the lock.
|
||||
ThreadData* data = m_data;
|
||||
if (data->m_usedBytes > 0)
|
||||
{
|
||||
m_owner->WriteCacheToDisk(*data);
|
||||
}
|
||||
|
||||
auto it = AZStd::find(m_owner->m_threadDataBlocks.begin(), m_owner->m_threadDataBlocks.end(), this);
|
||||
if (it != m_owner->m_threadDataBlocks.end())
|
||||
{
|
||||
m_owner->m_threadDataBlocks.erase(it);
|
||||
}
|
||||
|
||||
delete data;
|
||||
}
|
||||
|
||||
m_owner = owner;
|
||||
|
||||
if (m_owner)
|
||||
{
|
||||
// Deliberately using system memory instead of regular allocators. If debug allocators
|
||||
// are available in the future those should be used instead.
|
||||
ThreadData* data = new ThreadData();
|
||||
data->m_threadId = azlossy_caster(AZStd::hash<AZStd::thread_id>{}(AZStd::this_thread::get_id()));
|
||||
m_data = data;
|
||||
|
||||
AZStd::scoped_lock guard(m_owner->m_fileGuard);
|
||||
m_owner->m_threadDataBlocks.push_back(this);
|
||||
}
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 <limits>
|
||||
#include <AzCore/Debug/IEventLogger.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/std/containers/fixed_vector.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
#include <AzCore/std/parallel/mutex.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
class EventLogReader
|
||||
{
|
||||
public:
|
||||
bool ReadLog(const char* filePath);
|
||||
|
||||
EventNameHash GetEventName() const;
|
||||
uint16_t GetEventSize() const;
|
||||
uint16_t GetEventFlags() const;
|
||||
uint64_t GetThreadId() const;
|
||||
|
||||
AZStd::string_view GetString() const;
|
||||
template<typename T>
|
||||
const T* GetValue() const
|
||||
{
|
||||
AZ_Assert(sizeof(T) <= m_current->m_size, "Attempting to retrieve a value that's larger than the amount of stored data.");
|
||||
return reinterpret_cast<T*>(m_current + 1);
|
||||
}
|
||||
|
||||
bool Next();
|
||||
|
||||
private:
|
||||
void UpdateThreadId();
|
||||
|
||||
AZStd::vector<uint8_t> m_buffer;
|
||||
IEventLogger::LogHeader m_logHeader;
|
||||
|
||||
uint64_t m_currentThreadId{ 0 };
|
||||
IEventLogger::EventHeader* m_current{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
class LocalFileEventLogger
|
||||
: public Interface<IEventLogger>::Registrar
|
||||
{
|
||||
public:
|
||||
inline static constexpr size_t MaxThreadCount = 512;
|
||||
|
||||
~LocalFileEventLogger() override;
|
||||
|
||||
bool Start(const AZ::IO::Path& filePath);
|
||||
bool Start(AZStd::string_view outputPath, AZStd::string_view fileNameHint);
|
||||
void Stop();
|
||||
|
||||
void Flush() override;
|
||||
|
||||
void* RecordEventBegin(EventNameHash id, uint16_t size, uint16_t flags = 0) override;
|
||||
void RecordEventEnd() override;
|
||||
|
||||
void RecordStringEvent(EventNameHash id, AZStd::string_view text, uint16_t flags = 0) override;
|
||||
|
||||
protected:
|
||||
struct ThreadData
|
||||
{
|
||||
// ensure there is enough room for one large event with header + prolog
|
||||
static constexpr size_t BufferSize = AZStd::numeric_limits<decltype(EventHeader::m_size)>::max() + sizeof(EventHeader) + sizeof(Prolog);
|
||||
char m_buffer[BufferSize]{ 0 };
|
||||
uint64_t m_threadId{ 0 };
|
||||
uint32_t m_usedBytes{ sizeof(Prolog) }; // always front load the buffer with a prolog
|
||||
};
|
||||
|
||||
struct ThreadStorage
|
||||
{
|
||||
ThreadStorage() = default;
|
||||
~ThreadStorage();
|
||||
|
||||
void Reset(LocalFileEventLogger* owner);
|
||||
|
||||
AZStd::atomic<ThreadData*> m_data{ nullptr };
|
||||
ThreadData* m_pendingData{ nullptr };
|
||||
LocalFileEventLogger* m_owner{ nullptr };
|
||||
};
|
||||
|
||||
void WriteCacheToDisk(ThreadData& threadData);
|
||||
|
||||
ThreadStorage& GetThreadStorage();
|
||||
|
||||
AZStd::fixed_vector<ThreadStorage*, MaxThreadCount> m_threadDataBlocks;
|
||||
|
||||
AZ::IO::SystemFile m_file;
|
||||
AZStd::recursive_mutex m_fileGuard;
|
||||
AZStd::recursive_mutex m_fileWriteGuard;
|
||||
};
|
||||
} // namespace AZ::Debug
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <AzCore/Debug/StackTracer.h>
|
||||
#include <AzCore/Debug/TraceMessageBus.h>
|
||||
#include <AzCore/Debug/TraceMessagesDrillerBus.h>
|
||||
#include <AzCore/Debug/IEventLogger.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
@@ -74,6 +76,11 @@ namespace AZ
|
||||
static AZ::EnvironmentVariable<int> g_assertVerbosityLevel;
|
||||
static AZ::EnvironmentVariable<int> g_logVerbosityLevel;
|
||||
|
||||
static constexpr auto PrintfEventId = EventNameHash("Printf");
|
||||
static constexpr auto WarningEventId = EventNameHash("Warning");
|
||||
static constexpr auto ErrorEventId = EventNameHash("Error");
|
||||
static constexpr auto AssertEventId = EventNameHash("Assert");
|
||||
|
||||
constexpr LogLevel DefaultLogLevel = LogLevel::Info;
|
||||
|
||||
AZ_CVAR_SCOPED(int, bg_traceLogLevel, DefaultLogLevel, nullptr, ConsoleFunctorFlags::Null, "Enable trace message logging in release mode. 0=disabled, 1=errors, 2=warnings, 3=info.");
|
||||
@@ -230,6 +237,12 @@ namespace AZ
|
||||
azvsnprintf(message, g_maxMessageLength - 1, format, mark); // -1 to make room for the "/n" that will be appended below
|
||||
va_end(mark);
|
||||
|
||||
if (auto logger = Interface<IEventLogger>::Get(); logger)
|
||||
{
|
||||
logger->RecordStringEvent(AssertEventId, message);
|
||||
logger->Flush(); // Flush as an assert may indicate a crash is imminent.
|
||||
}
|
||||
|
||||
EBUS_EVENT(TraceMessageDrillerBus, OnPreAssert, fileName, line, funcName, message);
|
||||
|
||||
TraceMessageResult result;
|
||||
@@ -338,6 +351,11 @@ namespace AZ
|
||||
azvsnprintf(message, g_maxMessageLength-1, format, mark); // -1 to make room for the "/n" that will be appended below
|
||||
va_end(mark);
|
||||
|
||||
if (auto logger = Interface<IEventLogger>::Get(); logger)
|
||||
{
|
||||
logger->RecordStringEvent(ErrorEventId, message);
|
||||
}
|
||||
|
||||
EBUS_EVENT(TraceMessageDrillerBus, OnPreError, window, fileName, line, funcName, message);
|
||||
|
||||
TraceMessageResult result;
|
||||
@@ -385,6 +403,11 @@ namespace AZ
|
||||
azvsnprintf(message, g_maxMessageLength - 1, format, mark); // -1 to make room for the "/n" that will be appended below
|
||||
va_end(mark);
|
||||
|
||||
if (auto logger = Interface<IEventLogger>::Get(); logger)
|
||||
{
|
||||
logger->RecordStringEvent(WarningEventId, message);
|
||||
}
|
||||
|
||||
EBUS_EVENT(TraceMessageDrillerBus, OnPreWarning, window, fileName, line, funcName, message);
|
||||
|
||||
TraceMessageResult result;
|
||||
@@ -424,6 +447,11 @@ namespace AZ
|
||||
azvsnprintf(message, g_maxMessageLength, format, mark);
|
||||
va_end(mark);
|
||||
|
||||
if (auto logger = Interface<IEventLogger>::Get(); logger)
|
||||
{
|
||||
logger->RecordStringEvent(PrintfEventId, message);
|
||||
}
|
||||
|
||||
EBUS_EVENT(TraceMessageDrillerBus, OnPrintf, window, message);
|
||||
|
||||
TraceMessageResult result;
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/stack.h>
|
||||
#include <AzCore/std/function/function_fwd.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -34,64 +35,31 @@ namespace AZ
|
||||
//! event.Signal(1); // Our handlers lambda will now get invoked with the value 1
|
||||
//! };
|
||||
//! @endcode
|
||||
|
||||
template <typename... Params>
|
||||
class Event;
|
||||
|
||||
template <typename... Params>
|
||||
class EventHandler;
|
||||
|
||||
template <typename... Params>
|
||||
class Event final
|
||||
{
|
||||
friend class EventHandler<Params...>; // This is required in order to allow for relocating the handle pointers on a move
|
||||
|
||||
public:
|
||||
|
||||
using Callback = AZStd::function<void(Params...)>;
|
||||
using Handler = EventHandler<Params...>;
|
||||
|
||||
//! A handler class that can connect to an Event
|
||||
class Handler final
|
||||
{
|
||||
friend class Event;
|
||||
|
||||
public:
|
||||
|
||||
// We support default constructing of event handles (with no callback function being bound) to allow for better usage with container types
|
||||
// An unbound event handle cannot be added to an event and we do not support dynamically binding the callback post construction
|
||||
// (except for on assignment since that will also add the handle to the event; i.e. there is no way to unbind the callback after being added to an event)
|
||||
Handler() = default;
|
||||
explicit Handler(std::nullptr_t);
|
||||
explicit Handler(Callback callback);
|
||||
Handler(const Handler& rhs);
|
||||
Handler(Handler&& rhs);
|
||||
|
||||
~Handler();
|
||||
|
||||
Handler& operator=(const Handler& rhs);
|
||||
Handler& operator=(Handler&& rhs);
|
||||
|
||||
//! Connects the handler to the provided event.
|
||||
//! @param event the Event to connect to
|
||||
void Connect(Event<Params...>& event);
|
||||
|
||||
//! Disconnects the handler from its connected event, does nothing if the event is not connected.
|
||||
void Disconnect();
|
||||
|
||||
//! Returns true if this handler is connected to an event.
|
||||
//! @return boolean true if this handler is connected to an event
|
||||
bool IsConnected() const;
|
||||
|
||||
private:
|
||||
|
||||
//! Swaps the event handler pointers from the from instance to this instance
|
||||
//! @param from the handler instance we are replacing on the attached event
|
||||
void SwapEventHandlerPointers(const Handler& from);
|
||||
|
||||
const Event<Params...>* m_event = nullptr; //< The connected event
|
||||
int32_t m_index = 0; //< Index into the add or handler vectors (negative means pending add)
|
||||
Callback m_callback; //< The lambda to invoke during events
|
||||
};
|
||||
AZ_CLASS_ALLOCATOR(Event<Params...>, AZ::SystemAllocator, 0);
|
||||
|
||||
Event() = default;
|
||||
Event(Event&& rhs);
|
||||
Event(const Event& rhs) = delete; // Cannot copy events
|
||||
|
||||
|
||||
~Event();
|
||||
|
||||
Event& operator=(Event&& rhs);
|
||||
Event& operator=(const Event& rhs) = delete; // Cannot copy events
|
||||
|
||||
//! Returns true if at least one handler is connected to this event.
|
||||
bool HasHandlerConnected() const;
|
||||
@@ -120,6 +88,56 @@ namespace AZ
|
||||
|
||||
mutable bool m_updating = false; //< Raised during a Signal, false otherwise, used to guard m_handlers during handler iteration
|
||||
};
|
||||
|
||||
//! A handler class that can connect to an Event
|
||||
template <typename... Params>
|
||||
class EventHandler final
|
||||
{
|
||||
friend class Event<Params...>;
|
||||
|
||||
public:
|
||||
using Callback = AZStd::function<void(Params...)>;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(EventHandler<Params...>, AZ::SystemAllocator, 0);
|
||||
|
||||
// We support default constructing of event handles (with no callback function being bound) to allow for better usage with container types
|
||||
// An unbound event handle cannot be added to an event and we do not support dynamically binding the callback post construction
|
||||
// (except for on assignment since that will also add the handle to the event; i.e. there is no way to unbind the callback after being added to an event)
|
||||
EventHandler() = default;
|
||||
explicit EventHandler(std::nullptr_t);
|
||||
explicit EventHandler(Callback callback);
|
||||
EventHandler(const EventHandler& rhs);
|
||||
EventHandler(EventHandler&& rhs);
|
||||
|
||||
~EventHandler();
|
||||
|
||||
EventHandler& operator=(const EventHandler& rhs);
|
||||
EventHandler& operator=(EventHandler&& rhs);
|
||||
|
||||
//! Connects the handler to the provided event.
|
||||
//! @param event the Event to connect to
|
||||
void Connect(Event<Params...>& event);
|
||||
|
||||
//! Disconnects the handler from its connected event, does nothing if the event is not connected.
|
||||
void Disconnect();
|
||||
|
||||
//! Returns true if this handler is connected to an event.
|
||||
//! @return boolean true if this handler is connected to an event
|
||||
bool IsConnected() const;
|
||||
|
||||
private:
|
||||
|
||||
//! Swaps the event handler pointers from the from instance to this instance
|
||||
//! @param from the handler instance we are replacing on the attached event
|
||||
void SwapEventHandlerPointers(const EventHandler& from);
|
||||
|
||||
const Event<Params...>* m_event = nullptr; //< The connected event
|
||||
int32_t m_index = 0; //< Index into the add or handler vectors (negative means pending add)
|
||||
Callback m_callback; //< The lambda to invoke during events
|
||||
};
|
||||
|
||||
AZ_TYPE_INFO_INTERNAL_SPECIALIZED_TEMPLATE_PREFIX_UUID(AZ::Event, "Event", "{B7388760-18BF-486A-BE96-D5765791C53C}", AZ_TYPE_INFO_INTERNAL_TYPENAME_VARARGS);
|
||||
AZ_TYPE_INFO_INTERNAL_SPECIALIZED_TEMPLATE_PREFIX_UUID(AZ::EventHandler, "EventHandler", "{F85EFDA5-FBD0-4557-A3EF-9E077B41EA59}", AZ_TYPE_INFO_INTERNAL_TYPENAME_VARARGS);
|
||||
}
|
||||
|
||||
#include <AzCore/EBus/Event.inl>
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
namespace AZ
|
||||
{
|
||||
template <typename... Params>
|
||||
Event<Params...>::Handler::Handler(std::nullptr_t)
|
||||
EventHandler<Params...>::EventHandler(std::nullptr_t)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
Event<Params...>::Handler::Handler(Event<Params...>::Callback callback)
|
||||
EventHandler<Params...>::EventHandler(Callback callback)
|
||||
: m_callback(AZStd::move(callback))
|
||||
{
|
||||
;
|
||||
@@ -30,7 +30,7 @@ namespace AZ
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
Event<Params...>::Handler::Handler(const Handler& rhs)
|
||||
EventHandler<Params...>::EventHandler(const EventHandler& rhs)
|
||||
: m_callback(rhs.m_callback)
|
||||
{
|
||||
// Copy the callback function, then perform a Connect with the new event
|
||||
@@ -42,7 +42,7 @@ namespace AZ
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
Event<Params...>::Handler::Handler(Handler&& rhs)
|
||||
EventHandler<Params...>::EventHandler(EventHandler&& rhs)
|
||||
: m_event(rhs.m_event)
|
||||
, m_index(rhs.m_index)
|
||||
, m_callback(AZStd::move(rhs.m_callback))
|
||||
@@ -56,14 +56,14 @@ namespace AZ
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
Event<Params...>::Handler::~Handler()
|
||||
EventHandler<Params...>::~EventHandler()
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
typename Event<Params...>::Handler& Event<Params...>::Handler::operator=(const Handler& rhs)
|
||||
EventHandler<Params...>& EventHandler<Params...>::operator=(const EventHandler& rhs)
|
||||
{
|
||||
// Copy the callback function, then perform a Connect with the new event
|
||||
if (this != &rhs)
|
||||
@@ -81,7 +81,7 @@ namespace AZ
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
typename Event<Params...>::Handler& Event<Params...>::Handler::operator=(Handler&& rhs)
|
||||
EventHandler<Params...>& EventHandler<Params...>::operator=(EventHandler&& rhs)
|
||||
{
|
||||
if (this != &rhs)
|
||||
{
|
||||
@@ -96,12 +96,13 @@ namespace AZ
|
||||
|
||||
SwapEventHandlerPointers(rhs);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
void Event<Params...>::Handler::Connect(Event<Params...>& event)
|
||||
void EventHandler<Params...>::Connect(Event<Params...>& event)
|
||||
{
|
||||
// Cannot add an unbound event handle (no function callback) to an event, this is a programmer error
|
||||
// We explicitly do not support binding the callback after the handler has been constructed so we can just reject the event handle here
|
||||
@@ -119,7 +120,7 @@ namespace AZ
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
void Event<Params...>::Handler::Disconnect()
|
||||
void EventHandler<Params...>::Disconnect()
|
||||
{
|
||||
if (m_event)
|
||||
{
|
||||
@@ -127,16 +128,14 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
bool Event<Params...>::Handler::IsConnected() const
|
||||
bool EventHandler<Params...>::IsConnected() const
|
||||
{
|
||||
return m_event != nullptr;
|
||||
}
|
||||
|
||||
|
||||
template <typename... Params>
|
||||
void Event<Params...>::Handler::SwapEventHandlerPointers([[maybe_unused]] const Handler& from)
|
||||
void EventHandler<Params...>::SwapEventHandlerPointers([[maybe_unused]]const EventHandler& from)
|
||||
{
|
||||
// Find the pointer to the 'from' handler and point it to this handler
|
||||
if (m_event)
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace AZ
|
||||
return AZ::TICK_ATTACHMENT;
|
||||
}
|
||||
|
||||
ScheduledEventHandle* EventSchedulerSystemComponent::Add(ScheduledEvent* timedEvent, TimeMs durationMs)
|
||||
ScheduledEventHandle* EventSchedulerSystemComponent::AddEvent(ScheduledEvent* timedEvent, TimeMs durationMs)
|
||||
{
|
||||
if (durationMs < TimeMs{ 0 })
|
||||
{
|
||||
@@ -118,6 +118,19 @@ namespace AZ
|
||||
return timedEvent->m_handle;
|
||||
}
|
||||
|
||||
void EventSchedulerSystemComponent::AddCallback(const AZStd::function<void()>& callback, const Name& eventName, TimeMs durationMs)
|
||||
{
|
||||
if (durationMs < TimeMs{ 0 })
|
||||
{
|
||||
durationMs = TimeMs{ 0 };
|
||||
}
|
||||
|
||||
TimeMs currentMilliseconds = GetElapsedTimeMs();
|
||||
ScheduledEvent* timedEvent = AllocateManagedEvent(TimeMs(currentMilliseconds + durationMs), durationMs, callback, eventName);
|
||||
timedEvent->m_timeInserted = currentMilliseconds;
|
||||
m_queue.push(timedEvent->m_handle);
|
||||
}
|
||||
|
||||
AZStd::size_t EventSchedulerSystemComponent::GetHandleCount() const
|
||||
{
|
||||
return m_handles.size();
|
||||
@@ -153,7 +166,26 @@ namespace AZ
|
||||
m_handles.resize(m_handles.size() + 1);
|
||||
result = &(m_handles.back());
|
||||
}
|
||||
*result = ScheduledEventHandle(executeTimeMs, durationTimeMs, scheduledEvent);
|
||||
*result = ScheduledEventHandle(executeTimeMs, durationTimeMs, scheduledEvent, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
ScheduledEvent* EventSchedulerSystemComponent::AllocateManagedEvent(TimeMs executeTimeMs, TimeMs durationTimeMs, const AZStd::function<void()>& callback, const Name& eventName)
|
||||
{
|
||||
ScheduledEvent* result = new ScheduledEvent(callback, eventName);
|
||||
ScheduledEventHandle* handle = nullptr;
|
||||
if (!m_freeHandles.empty())
|
||||
{
|
||||
handle = m_freeHandles.back();
|
||||
m_freeHandles.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_handles.resize(m_handles.size() + 1);
|
||||
handle = &(m_handles.back());
|
||||
}
|
||||
*handle = ScheduledEventHandle(executeTimeMs, durationTimeMs, result, true);
|
||||
result->m_handle = handle;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace AZ
|
||||
}
|
||||
};
|
||||
|
||||
class Name;
|
||||
class ScheduledEvent;
|
||||
|
||||
//! @class EventSchedulerSystemComponent
|
||||
@@ -75,7 +76,8 @@ namespace AZ
|
||||
|
||||
//! IEventScheduler interface
|
||||
//! @{
|
||||
ScheduledEventHandle* Add(ScheduledEvent* scheduledEvent, TimeMs durationMs) override;
|
||||
ScheduledEventHandle* AddEvent(ScheduledEvent* scheduledEvent, TimeMs durationMs) override;
|
||||
void AddCallback(const AZStd::function<void()>& callback, const Name& eventName, TimeMs durationMs) override;
|
||||
// @}
|
||||
|
||||
//! EventSchedulerSystemComponent stats
|
||||
@@ -88,6 +90,8 @@ namespace AZ
|
||||
|
||||
private:
|
||||
ScheduledEventHandle* AllocateHandle(TimeMs executeTimeMs, TimeMs durationTimeMs, ScheduledEvent* scheduledEvent);
|
||||
// Allocates a single use event to capture the passed in callback. Event is cleaned up on completion.
|
||||
ScheduledEvent* AllocateManagedEvent(TimeMs executeTimeMs, TimeMs durationTimeMs, const AZStd::function<void()>& callback, const Name& eventName);
|
||||
void FreeHandle(ScheduledEventHandle* handle);
|
||||
|
||||
// Bind the DumpStats member function to the console as 'EventSchedulerSystemComponent.DumpStats'
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class Name;
|
||||
class ScheduledEvent;
|
||||
class ScheduledEventHandle;
|
||||
|
||||
@@ -36,12 +37,19 @@ namespace AZ
|
||||
IEventScheduler() = default;
|
||||
virtual ~IEventScheduler() = default;
|
||||
|
||||
//! Adds a scheduled event and intervalMs to run it.
|
||||
//! Adds a scheduled event to run in durationMs.
|
||||
//! Actual duration is not guaranteed but will not be less than the value provided.
|
||||
//! @param scheduledEvent a scheduled event to add
|
||||
//! @param durationMs an interval Ms to run the scheduled event
|
||||
//! @param durationMs a millisecond interval to run the scheduled event
|
||||
//! @return pointer to the handle for this scheduled event, IEventScheduler maintains ownership
|
||||
virtual ScheduledEventHandle* Add(ScheduledEvent* scheduledEvent, TimeMs durationMs) = 0;
|
||||
virtual ScheduledEventHandle* AddEvent(ScheduledEvent* scheduledEvent, TimeMs durationMs) = 0;
|
||||
|
||||
//! Schedules a callback to run in durationMs.
|
||||
//! Actual duration is not guaranteed but will not be less than the value provided.
|
||||
//! @param callback a callback to invoke after durationMs
|
||||
//! @param eventName a text descriptor of the callback
|
||||
//! @param durationMs a millisecond interval to run the scheduled callback
|
||||
virtual void AddCallback(const AZStd::function<void()>& callback, const Name& eventName, TimeMs durationMs) = 0;
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(IEventScheduler);
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace AZ
|
||||
RemoveFromQueue();
|
||||
m_durationMs = durationMs;
|
||||
m_autoRequeue = autoRequeue;
|
||||
m_handle = eventScheduler->Add(this, durationMs);
|
||||
m_handle = eventScheduler->AddEvent(this, durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace AZ
|
||||
IEventScheduler* eventScheduler = Interface<IEventScheduler>::Get();
|
||||
if (eventScheduler)
|
||||
{
|
||||
m_handle = eventScheduler->Add(this, m_durationMs);
|
||||
m_handle = eventScheduler->AddEvent(this, m_durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
ScheduledEventHandle::ScheduledEventHandle(TimeMs executeTimeMs, TimeMs durationTimeMs, ScheduledEvent* scheduledEvent)
|
||||
ScheduledEventHandle::ScheduledEventHandle(TimeMs executeTimeMs, TimeMs durationTimeMs, ScheduledEvent* scheduledEvent, bool isAutoDelete)
|
||||
: m_executeTimeMs(executeTimeMs)
|
||||
, m_durationMs(durationTimeMs)
|
||||
, m_event(scheduledEvent)
|
||||
, m_autoDelete(isAutoDelete)
|
||||
{
|
||||
;
|
||||
}
|
||||
@@ -48,6 +49,10 @@ namespace AZ
|
||||
else // Not configured to auto-requeue, so remove the handle
|
||||
{
|
||||
m_event->ClearHandle();
|
||||
if (m_autoDelete)
|
||||
{
|
||||
delete m_event;
|
||||
}
|
||||
m_event = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace AZ
|
||||
//! @param executeTimeMs an absolute time in ms at which point the scheduled event should trigger
|
||||
//! @param durationTimeMs the interval time in ms used for prioritization as well as re-queueing
|
||||
//! @param scheduledEvent a scheduled event to run
|
||||
ScheduledEventHandle(TimeMs executeTimeMs, TimeMs durationTimeMs, ScheduledEvent* scheduledEvent);
|
||||
//! @param autoDelete if the event handle will be automatically deleted after execution completes
|
||||
ScheduledEventHandle(TimeMs executeTimeMs, TimeMs durationTimeMs, ScheduledEvent* scheduledEvent, bool isAutoDelete);
|
||||
|
||||
//! operator of comparing a scheduled event by execute time.
|
||||
//! @param a_Rhs a scheduled event handle to compare
|
||||
@@ -58,6 +59,7 @@ namespace AZ
|
||||
TimeMs m_executeTimeMs = TimeMs{ 0 }; //< execution time of the scheduled event
|
||||
TimeMs m_durationMs = TimeMs{ 0 }; //< interval time of the scheduled event
|
||||
ScheduledEvent* m_event = nullptr; //< pointer to the scheduled event
|
||||
bool m_autoDelete = false; //< if the handle manages the memory of its own event
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace AZ
|
||||
int32_t numArgs = dc.GetNumArguments();
|
||||
switch (numArgs)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
*thisPtr = Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (dc.IsNumber(0))
|
||||
@@ -42,9 +46,12 @@ namespace AZ
|
||||
dc.ReadArg(0, number);
|
||||
*thisPtr = Color(number);
|
||||
}
|
||||
else
|
||||
else if (!(ConstructOnTypedArgument<Color>(*thisPtr, dc, 0)
|
||||
|| ConstructOnTypedArgument<Vector4>(*thisPtr, dc, 0)
|
||||
|| ConstructOnTypedArgument<Vector3>(*thisPtr, dc, 0)
|
||||
|| ConstructOnTypedArgument<Vector2>(*thisPtr, dc, 0)))
|
||||
{
|
||||
dc.GetScriptContext()->Error(AZ::ScriptContext::ErrorType::Error, true, "When only providing 1 argument to Color(), it must be a number!");
|
||||
dc.GetScriptContext()->Error(AZ::ScriptContext::ErrorType::Error, true, "When only providing 1 argument to Color(), it must be a number, Color, Vector4, Vector3, or Vector2");
|
||||
}
|
||||
} break;
|
||||
case 3:
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/Vector4.h>
|
||||
|
||||
@@ -32,12 +33,16 @@ namespace AZ
|
||||
Color() = default;
|
||||
Color(const Vector4& v) { m_color = v; }
|
||||
|
||||
explicit Color(const Vector2& source);
|
||||
|
||||
explicit Color(const Vector3& source);
|
||||
|
||||
//! Constructs vector with all components set to the same specified value.
|
||||
explicit Color(float rgba);
|
||||
|
||||
explicit Color(float r, float g, float b, float a);
|
||||
Color(float r, float g, float b, float a);
|
||||
|
||||
explicit Color(u8 r, u8 g, u8 b, u8 a);
|
||||
Color(u8 r, u8 g, u8 b, u8 a);
|
||||
|
||||
//! Creates a vector with all components set to zero, more efficient than calling Color(0.0f).
|
||||
static Color CreateZero();
|
||||
|
||||
@@ -13,20 +13,29 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
|
||||
AZ_MATH_INLINE Color::Color(const Vector2& source)
|
||||
{
|
||||
m_color = Vector4(source);
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE Color::Color(const Vector3& source)
|
||||
{
|
||||
m_color = Vector4(source);
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE Color::Color(float rgba)
|
||||
: m_color(rgba)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Color::Color(float r, float g, float b, float a)
|
||||
: m_color(r, g, b, a)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Color::Color(u8 r, u8 g, u8 b, u8 a)
|
||||
{
|
||||
SetR8(r);
|
||||
|
||||
@@ -289,6 +289,10 @@ namespace AZ
|
||||
->Attribute(AZ::Script::Attributes::MethodOverride, &Internal::GetSinCosMultipleReturn)
|
||||
->Method<bool(double, double, double)>("IsClose", &AZ::IsClose, context.MakeDefaultValues(static_cast<double>(Constants::FloatEpsilon)))
|
||||
->Method<float(float)>("Abs", &GetAbs)
|
||||
->Method("Divide By Number", [](float lhs, float rhs) { return lhs / rhs; })
|
||||
->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Divide By Number (/)", "Math"))
|
||||
->Attribute(AZ::ScriptCanvasAttributes::OperatorOverride, AZ::Script::Attributes::OperatorType::Div)
|
||||
->Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "DivideGroup", "" }, { "DivideGroup" }))
|
||||
;
|
||||
|
||||
// Uuid
|
||||
|
||||
@@ -301,6 +301,10 @@ namespace AZ
|
||||
Attribute(Script::Attributes::Ignore, 0)-> // ignore for script since we already got the generic multiply above
|
||||
Method<Matrix4x4(Matrix4x4::*)(const Matrix4x4&) const>("MultiplyMatrix4x4", &Matrix4x4::operator*)->
|
||||
Attribute(Script::Attributes::Ignore, 0)-> // ignore for script since we already got the generic multiply above
|
||||
Method<Matrix4x4(Matrix4x4::*)(const Matrix4x4&) const>("Add", &Matrix4x4::operator+)->
|
||||
Attribute(Script::Attributes::Operator, Script::Attributes::OperatorType::Add)->
|
||||
Method<Matrix4x4(Matrix4x4::*)(const Matrix4x4&) const>("Subtract", &Matrix4x4::operator-)->
|
||||
Attribute(Script::Attributes::Operator, Script::Attributes::OperatorType::Sub)->
|
||||
Method("Equal", &Matrix4x4::operator==)->
|
||||
Attribute(Script::Attributes::Operator, Script::Attributes::OperatorType::Equal)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
|
||||
@@ -171,6 +171,12 @@ namespace AZ
|
||||
void SetTranslation(const Vector3& v);
|
||||
//! @}
|
||||
|
||||
Matrix4x4 operator+(const Matrix4x4& rhs) const;
|
||||
Matrix4x4& operator+=(const Matrix4x4& rhs);
|
||||
|
||||
Matrix4x4 operator-(const Matrix4x4& rhs) const;
|
||||
Matrix4x4& operator-=(const Matrix4x4& rhs);
|
||||
|
||||
Matrix4x4 operator*(const Matrix4x4& rhs) const;
|
||||
Matrix4x4& operator*=(const Matrix4x4& rhs);
|
||||
|
||||
|
||||
@@ -477,6 +477,37 @@ namespace AZ
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator+(const Matrix4x4& rhs) const
|
||||
{
|
||||
return Matrix4x4
|
||||
( Simd::Vec4::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
|
||||
, Simd::Vec4::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
|
||||
, Simd::Vec4::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
|
||||
, Simd::Vec4::Add(m_rows[3].GetSimdValue(), rhs.m_rows[3].GetSimdValue()));
|
||||
}
|
||||
|
||||
|
||||
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator-(const Matrix4x4& rhs) const
|
||||
{
|
||||
return Matrix4x4
|
||||
( Simd::Vec4::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
|
||||
, Simd::Vec4::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
|
||||
, Simd::Vec4::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
|
||||
, Simd::Vec4::Sub(m_rows[3].GetSimdValue(), rhs.m_rows[3].GetSimdValue()));
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator+=(const Matrix4x4& rhs)
|
||||
{
|
||||
*this = *this + rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator-=(const Matrix4x4& rhs)
|
||||
{
|
||||
*this = *this - rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator*(const Matrix4x4& rhs) const
|
||||
{
|
||||
Matrix4x4 result;
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonPrism::OnChangeHeight() const
|
||||
void PolygonPrism::OnChangeHeight()
|
||||
{
|
||||
if (m_onChangeHeightCallback)
|
||||
{
|
||||
@@ -78,10 +78,30 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonPrism::SetNonUniformScale(const AZ::Vector3& nonUniformScale)
|
||||
{
|
||||
m_nonUniformScale = nonUniformScale;
|
||||
OnChangeNonUniformScale();
|
||||
}
|
||||
|
||||
void PolygonPrism::OnChangeNonUniformScale()
|
||||
{
|
||||
if (m_onChangeNonUniformScaleCallback)
|
||||
{
|
||||
m_onChangeNonUniformScaleCallback();
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Vector3 PolygonPrism::GetNonUniformScale() const
|
||||
{
|
||||
return m_nonUniformScale;
|
||||
}
|
||||
|
||||
void PolygonPrism::SetCallbacks(
|
||||
const VoidFunction& onChangeElement,
|
||||
const VoidFunction& onChangeContainer,
|
||||
const VoidFunction& onChangeHeight)
|
||||
const VoidFunction& onChangeHeight,
|
||||
const VoidFunction& onChangeNonUniformScale)
|
||||
{
|
||||
m_vertexContainer.SetCallbacks(
|
||||
[onChangeContainer](size_t) { onChangeContainer(); },
|
||||
@@ -91,12 +111,14 @@ namespace AZ
|
||||
onChangeContainer);
|
||||
|
||||
m_onChangeHeightCallback = onChangeHeight;
|
||||
m_onChangeNonUniformScaleCallback = onChangeNonUniformScale;
|
||||
}
|
||||
|
||||
void PolygonPrism::SetCallbacks(
|
||||
const IndexFunction& onAddVertex, const IndexFunction& onRemoveVertex,
|
||||
const IndexFunction& onUpdateVertex, const VoidFunction& onSetVertices,
|
||||
const VoidFunction& onClearVertices, const VoidFunction& onChangeHeight)
|
||||
const VoidFunction& onClearVertices, const VoidFunction& onChangeHeight,
|
||||
const VoidFunction& onChangeNonUniformScale)
|
||||
{
|
||||
m_vertexContainer.SetCallbacks(
|
||||
onAddVertex,
|
||||
@@ -106,6 +128,7 @@ namespace AZ
|
||||
onClearVertices);
|
||||
|
||||
m_onChangeHeightCallback = onChangeHeight;
|
||||
m_onChangeNonUniformScaleCallback = onChangeNonUniformScale;
|
||||
}
|
||||
|
||||
AZ_CLASS_ALLOCATOR_IMPL(PolygonPrism, SystemAllocator, 0)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Math/VertexContainer.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
|
||||
@@ -22,11 +23,9 @@ namespace AZ
|
||||
class ReflectContext;
|
||||
void PolygonPrismReflect(ReflectContext* context);
|
||||
|
||||
/**
|
||||
* Formal Definition: A (right) polygonal prism is a 3-dimensional prism made from two translated polygons connected by rectangles. Parallelogram sides are not allowed.
|
||||
* Here the representation is defined by one polygon (internally represented as a vertex container - list of vertices) and a height (extrusion) property.
|
||||
* All vertices lie on the local plane Z = 0.
|
||||
*/
|
||||
//! Formal Definition: A (right) polygonal prism is a 3-dimensional prism made from two translated polygons connected by rectangles. Parallelogram sides are not allowed.
|
||||
//! Here the representation is defined by one polygon (internally represented as a vertex container - list of vertices) and a height (extrusion) property.
|
||||
//! All vertices lie on the local plane Z = 0.
|
||||
class PolygonPrism
|
||||
{
|
||||
public:
|
||||
@@ -36,46 +35,50 @@ namespace AZ
|
||||
PolygonPrism() = default;
|
||||
virtual ~PolygonPrism() = default;
|
||||
|
||||
/**
|
||||
* Set the height of the polygon prism.
|
||||
*/
|
||||
//! Set the height of the polygon prism.
|
||||
void SetHeight(float height);
|
||||
/**
|
||||
* Return the height of the polygon prism.
|
||||
*/
|
||||
|
||||
//! Return the height of the polygon prism.
|
||||
float GetHeight() const { return m_height; }
|
||||
|
||||
/**
|
||||
* Override callbacks to be used when polygon prism changes/is modified (general).
|
||||
*/
|
||||
//! Set the non-uniform scale applied to the polygon prism.
|
||||
void SetNonUniformScale(const AZ::Vector3& nonUniformScale);
|
||||
|
||||
//! Return the non-uniform scale applied to the polygon prism.
|
||||
AZ::Vector3 GetNonUniformScale() const;
|
||||
|
||||
//! Override callbacks to be used when polygon prism changes/is modified (general).
|
||||
void SetCallbacks(
|
||||
const VoidFunction& onChangeElement,
|
||||
const VoidFunction& onChangeContainer,
|
||||
const VoidFunction& onChangeHeight);
|
||||
const VoidFunction& onChangeHeight,
|
||||
const VoidFunction& onChangeNonUniformScale);
|
||||
|
||||
/**
|
||||
* Override callbacks to be used when spline changes/is modified (specific).
|
||||
* (use if you need more fine grained control over modifications to the container)
|
||||
*/
|
||||
//! Override callbacks to be used when spline changes/is modified (specific).
|
||||
//! (use if you need more fine grained control over modifications to the container)
|
||||
void SetCallbacks(
|
||||
const IndexFunction& onAddVertex, const IndexFunction& onRemoveVertex,
|
||||
const IndexFunction& onUpdateVertex, const VoidFunction& onSetVertices,
|
||||
const VoidFunction& onClearVertices, const VoidFunction& onChangeHeight);
|
||||
const VoidFunction& onClearVertices, const VoidFunction& onChangeHeight,
|
||||
const VoidFunction& onChangeNonUniformScale);
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
VertexContainer<Vector2> m_vertexContainer; ///< Reference to underlying vertex data.
|
||||
|
||||
private:
|
||||
VoidFunction m_onChangeHeightCallback = nullptr; ///< Callback for when height is changed.
|
||||
float m_height = 1.0f; ///< Height of polygon prism (about local Z) - default to 1m.
|
||||
VoidFunction m_onChangeHeightCallback = nullptr; //!< Callback for when height is changed.
|
||||
VoidFunction m_onChangeNonUniformScaleCallback = nullptr; //!< Callback for when non-uniform scale is changed.
|
||||
float m_height = 1.0f; //!< Height of polygon prism (about local Z) - default to 1m.
|
||||
AZ::Vector3 m_nonUniformScale = AZ::Vector3::CreateOne(); //!< Allows non-uniform scale to be applied to the prism.
|
||||
|
||||
/**
|
||||
* Internally used to call OnChangeCallback when height values are modified in the property grid.
|
||||
*/
|
||||
void OnChangeHeight() const;
|
||||
//! Internally used to call OnChangeCallback when height values are modified in the property grid.
|
||||
void OnChangeHeight();
|
||||
|
||||
//! Internally used to call OnChangeCallback when non-uniform scale values are modified.
|
||||
void OnChangeNonUniformScale();
|
||||
};
|
||||
|
||||
using PolygonPrismPtr = AZStd::shared_ptr<PolygonPrism>;
|
||||
using ConstPolygonPrismPtr = AZStd::shared_ptr<const PolygonPrism>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ namespace AZ
|
||||
Method("SetElement", &Quaternion::SetElement)->
|
||||
Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)->
|
||||
Method("GetLength", &Quaternion::GetLength)->
|
||||
Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Length", "Math"))->
|
||||
Method("GetLengthSq", &Quaternion::GetLengthSq)->
|
||||
Method("GetLengthReciprocal", &Quaternion::GetLengthReciprocal)->
|
||||
Method("GetNormalized", &Quaternion::GetNormalized)->
|
||||
|
||||
@@ -240,6 +240,7 @@ namespace AZ
|
||||
Method("SetElement", &Vector2::SetElement, { { {"Index", ""}, {"Value",""} } })->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("GetLength", &Vector2::GetLength)->
|
||||
Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Length", "Math"))->
|
||||
Method("GetLengthSq", &Vector2::GetLengthSq)->
|
||||
Method("SetLength", &Vector2::SetLength)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
@@ -311,7 +312,10 @@ namespace AZ
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("Madd", &Vector2::Madd)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("ConstructFromValues", &Internal::ConstructVector2)
|
||||
Method("ConstructFromValues", &Internal::ConstructVector2)->
|
||||
Method<Vector2(Vector2::*)(float) const>("DivideFloatExplicit", &Vector2::operator/)->
|
||||
Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Divide By Number (/)", "Math"))->
|
||||
Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "DivideGroup", "" }, { "DivideGroup" }))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,6 +256,7 @@ namespace AZ
|
||||
Method("SetElement", &Vector3::SetElement)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("GetLength", &Vector3::GetLength)->
|
||||
Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Length", "Math"))->
|
||||
Method("GetLengthSq", &Vector3::GetLengthSq)->
|
||||
Method("GetLengthReciprocal", &Vector3::GetLengthReciprocal)->
|
||||
Method("GetNormalized", &Vector3::GetNormalized)->
|
||||
@@ -343,7 +344,10 @@ namespace AZ
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("CreateZero", &Vector3::CreateZero)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("ConstructFromValues", &ScriptCanvas::ConstructVector3)
|
||||
Method("ConstructFromValues", &ScriptCanvas::ConstructVector3)->
|
||||
Method<Vector3(Vector3::*)(float) const>("DivideFloatExplicit", &Vector3::operator/)->
|
||||
Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Divide By Number (/)", "Math"))->
|
||||
Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "DivideGroup", "" }, { "DivideGroup" }))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +266,7 @@ namespace AZ
|
||||
Method("SetElement", &Vector4::SetElement)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("GetLength", &Vector4::GetLength)->
|
||||
Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Length", "Math"))->
|
||||
Method("GetLengthSq", &Vector4::GetLengthSq)->
|
||||
Method("GetLengthReciprocal", &Vector4::GetLengthReciprocal)->
|
||||
Method("GetNormalized", &Vector4::GetNormalized)->
|
||||
@@ -316,7 +317,10 @@ namespace AZ
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("CreateZero", &Vector4::CreateZero)->
|
||||
Attribute(Script::Attributes::ExcludeFrom, Script::Attributes::ExcludeFlags::All)->
|
||||
Method("ConstructFromValues", &Internal::ConstructVector4)
|
||||
Method("ConstructFromValues", &Internal::ConstructVector4)->
|
||||
Method<Vector4(Vector4::*)(float) const>("DivideFloatExplicit", &Vector4::operator/)->
|
||||
Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Divide By Number (/)", "Math"))->
|
||||
Attribute(AZ::ScriptCanvasAttributes::OverloadArgumentGroup, AZ::OverloadArgumentGroupInfo({ "DivideGroup", "" }, { "DivideGroup" }))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/// @cond EXCLUDE_DOCS
|
||||
|
||||
/** @file
|
||||
* @deprecated Use <AzCore/Slice/SliceBus.h>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Slice/SliceBus.h>
|
||||
|
||||
/// @endcond
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/// @cond EXCLUDE_DOCS
|
||||
|
||||
/** @file
|
||||
* @deprecated Use <AzCore/Slice/SliceComponent.h>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Slice/SliceComponent.h>
|
||||
|
||||
/// @endcond
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <AzCore/std/string/tokenize.h>
|
||||
#include <AzCore/RTTI/AzStdOnDemandPrettyName.inl>
|
||||
#include <AzCore/RTTI/AzStdOnDemandReflectionLuaFunctions.inl>
|
||||
#include <AzCore/EBus/Event.h>
|
||||
|
||||
// forward declare specialized types
|
||||
namespace AZStd
|
||||
@@ -361,6 +362,62 @@ namespace AZ
|
||||
typename ContainerType::iterator m_end;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using decay_array = AZStd::conditional_t<AZStd::is_array_v<AZStd::remove_reference_t<T>>, std::remove_extent_t<AZStd::remove_reference_t<T>>*, T&&>;
|
||||
|
||||
template<typename... T>
|
||||
BehaviorObject CreateConnectedAZEventHandler(void* voidPtr, BehaviorFunction&& function)
|
||||
{
|
||||
auto behaviorForwardingFunction = [function](T... args)
|
||||
{
|
||||
AZStd::tuple<decay_array<T>...> lvalueWrapper(AZStd::forward<T>(args)...);
|
||||
using BVPReserveArray = AZStd::array<AZ::BehaviorValueParameter, sizeof...(args)>;
|
||||
auto MakeBVPArrayFunction = [](auto&&... element)
|
||||
{
|
||||
return BVPReserveArray{ {AZ::BehaviorValueParameter{&element}...} };
|
||||
};
|
||||
BVPReserveArray argsBVPs = AZStd::apply(MakeBVPArrayFunction, lvalueWrapper);
|
||||
function(nullptr, argsBVPs.data(), sizeof...(T));
|
||||
};
|
||||
|
||||
auto result = aznew AZ::EventHandler<T...>(AZStd::move(behaviorForwardingFunction));
|
||||
auto eventPtr = reinterpret_cast<AZ::Event<T...>*>(voidPtr);
|
||||
result->Connect(*eventPtr);
|
||||
return { reinterpret_cast<void*>(result), azrtti_typeid<AZ::EventHandler<T...>>() };
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
struct OnDemandReflection<AZ::Event<T...>>
|
||||
{
|
||||
template<typename U>
|
||||
static AZ::BehaviorParameter CreateBehaviorEventParameter()
|
||||
{
|
||||
AZ::BehaviorParameter param;
|
||||
AZ::Internal::SetParametersStripped<U>(¶m, nullptr);
|
||||
return param;
|
||||
}
|
||||
static void Reflect(ReflectContext* context)
|
||||
{
|
||||
if (auto behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
AZ::EventHandlerCreationFunctionHolder createHandlerHolder;
|
||||
createHandlerHolder.m_function = &CreateConnectedAZEventHandler<T...>;
|
||||
|
||||
AZStd::vector<AZ::BehaviorParameter> eventParamsTypes{ AZStd::initializer_list<AZ::BehaviorParameter>{
|
||||
CreateBehaviorEventParameter<decay_array<T>>()... } };
|
||||
behaviorContext->Class<AZ::Event<T...>>()
|
||||
->Attribute(AZ::Script::Attributes::EventHandlerCreationFunction, createHandlerHolder)
|
||||
->Attribute(AZ::Script::Attributes::EventParameterTypes, eventParamsTypes)
|
||||
->Method("HasHandlerConnected", &AZ::Event<T...>::HasHandlerConnected)
|
||||
;
|
||||
|
||||
behaviorContext->Class<AZ::EventHandler<T...>>()
|
||||
->Method("Disconnect", &AZ::EventHandler<T...>::Disconnect)
|
||||
;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// OnDemand reflection for AZStd::vector
|
||||
template<class T, class A>
|
||||
struct OnDemandReflection< AZStd::vector<T, A> >
|
||||
@@ -908,6 +965,11 @@ namespace AZ
|
||||
emptyBranchInfo.m_trueToolTip = "The container is empty";
|
||||
emptyBranchInfo.m_falseToolTip = "The container is not empty";
|
||||
|
||||
auto ContainsTransparent = [](const ContainerType& containerType, typename ContainerType::key_type& key)->bool
|
||||
{
|
||||
return containerType.contains(key);
|
||||
};
|
||||
|
||||
ExplicitOverloadInfo explicitOverloadInfo;
|
||||
behaviorContext->Class<ContainerType>()
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::ListOnly)
|
||||
@@ -917,7 +979,7 @@ namespace AZ
|
||||
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::ScriptOwn)
|
||||
->Method(k_accessElementName, &At)
|
||||
->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
|
||||
->Attribute(AZ::ScriptCanvasAttributes::CheckedOperation, CheckedOperationInfo("Contains", {}, "Out", "Key Not Found"))
|
||||
->Attribute(AZ::ScriptCanvasAttributes::CheckedOperation, CheckedOperationInfo("contains", {}, "Out", "Key Not Found"))
|
||||
->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Get Element", "Containers"))
|
||||
->Method("BucketCount", static_cast<typename ContainerType::size_type(ContainerType::*)() const>(&ContainerType::bucket_count))
|
||||
->Method("Empty", static_cast<bool(ContainerType::*)() const>(&ContainerType::empty), { { { "Container", "The container to check if it is empty", nullptr, {} } } })
|
||||
@@ -933,8 +995,9 @@ namespace AZ
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Method("GetKeys", &GetKeys)
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Method("Contains", [](ContainerType& map, Key& key)->bool { return map.contains(key); }, { { { "Key", "The key to check for", nullptr, {} } } })
|
||||
->Method("contains", ContainsTransparent, { { { "Key", "The key to check for", nullptr, {} } } })
|
||||
->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Has Key", "Containers"))
|
||||
->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
|
||||
->Method("Insert", &Insert, { { {}, { "Index", "The index at which to insert the value", nullptr, {} }, {} } })
|
||||
->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
|
||||
->Attribute(AZ::ScriptCanvasAttributes::ExplicitOverloadCrc, ExplicitOverloadInfo("Insert", "Containers"))
|
||||
@@ -1011,6 +1074,11 @@ namespace AZ
|
||||
{
|
||||
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
auto ContainsTransparent = [](const ContainerType& containerType, typename ContainerType::key_type& key)->bool
|
||||
{
|
||||
return containerType.contains(key);
|
||||
};
|
||||
|
||||
behaviorContext->Class<ContainerType>()
|
||||
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
|
||||
->Attribute(AZ::ScriptCanvasAttributes::PrettyName, ScriptCanvasOnDemandReflection::OnDemandPrettyName<ContainerType>::Get(*behaviorContext))
|
||||
@@ -1020,7 +1088,8 @@ namespace AZ
|
||||
->Method("BucketCount", static_cast<typename ContainerType::size_type(ContainerType::*)() const>(&ContainerType::bucket_count))
|
||||
->Method("Erase", &Erase)
|
||||
->Method("Empty", [](ContainerType& thisSet)->bool { return thisSet.empty(); })
|
||||
->Method("contains", [](ContainerType& thisSet, Key& key)->bool { return thisSet.find(key) != thisSet.end(); })
|
||||
->Method("contains", ContainsTransparent)
|
||||
->Attribute(AZ::Script::Attributes::TreatAsMemberFunction, AZ::AttributeIsValid::IfPresent)
|
||||
->Method("Insert", &Insert)
|
||||
->Method(k_sizeName, [](ContainerType* thisPtr) { return aznumeric_cast<int>(thisPtr->size()); })
|
||||
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Length)
|
||||
|
||||
@@ -16,6 +16,98 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
bool MethodReturnsAzEventByReferenceOrPointer(const AZ::BehaviorMethod& method)
|
||||
{
|
||||
const AZ::BehaviorParameter* resultParameter = method.GetResult();
|
||||
if (resultParameter == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The return parameter must have AZ Rtti to in order for it be an AZ::Event parameter
|
||||
AZ::IRttiHelper* rttiHelper = resultParameter->m_azRtti;
|
||||
if (!rttiHelper || rttiHelper->GetGenericTypeId() != azrtti_typeid<AZ::Event>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr auto PointerValueTrait = AZ::BehaviorParameter::Traits::TR_REFERENCE | AZ::BehaviorParameter::Traits::TR_POINTER;
|
||||
return (resultParameter->m_traits & PointerValueTrait) != AZ::BehaviorParameter::Traits::TR_NONE;
|
||||
}
|
||||
|
||||
bool ValidateAzEventDescription(const BehaviorContext& context, const AZ::BehaviorMethod& method)
|
||||
{
|
||||
const AZ::BehaviorParameter* resultParameter = method.GetResult();
|
||||
if (resultParameter == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The return parameter must have AZ Rtti to in order for it be an AZ::Event& or AZ::Event* parameter
|
||||
AZ::IRttiHelper* rttiHelper = resultParameter->m_azRtti;
|
||||
if (!rttiHelper || rttiHelper->GetGenericTypeId() != azrtti_typeid<AZ::Event>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
constexpr auto PointerValueTrait = AZ::BehaviorParameter::Traits::TR_REFERENCE | AZ::BehaviorParameter::Traits::TR_POINTER;
|
||||
const auto parameterTraits = static_cast<AZ::BehaviorParameter::Traits>(resultParameter->m_traits) & PointerValueTrait;
|
||||
if (parameterTraits == AZ::BehaviorParameter::Traits::TR_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool azEventDescValid = true;
|
||||
AZ::Attribute* azEventDescAttribute = AZ::FindAttribute(AZ::Script::Attributes::AzEventDescription, method.m_attributes);
|
||||
AZ::AttributeReader azEventDescAttributeReader(nullptr, azEventDescAttribute);
|
||||
AZ::BehaviorAzEventDescription behaviorAzEventDesc;
|
||||
if (!azEventDescAttributeReader.Read<decltype(behaviorAzEventDesc)>(behaviorAzEventDesc))
|
||||
{
|
||||
AZ_Error("BehaviorContext", false, "Unable to read AzEventDescription attribute of method %s"
|
||||
" that returns an AZ::Event", method.m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (behaviorAzEventDesc.m_eventName.empty())
|
||||
{
|
||||
AZ_Error("BehaviorContext", false, "AzEventDescription attribute on method %s"
|
||||
" has an empty event name", method.m_name.c_str());
|
||||
azEventDescValid = false;
|
||||
}
|
||||
|
||||
auto azEventClassIter = context.m_typeToClassMap.find(rttiHelper->GetTypeId());
|
||||
if (azEventClassIter != context.m_typeToClassMap.end() && azEventClassIter->second != nullptr)
|
||||
{
|
||||
AZ::BehaviorClass* azEventClass = azEventClassIter->second;
|
||||
AZ::Attribute* eventParameterTypesAttribute = AZ::FindAttribute(AZ::Script::Attributes::EventParameterTypes,
|
||||
azEventClass->m_attributes);
|
||||
AZStd::vector<AZ::BehaviorParameter> eventParameterTypes;
|
||||
if (AZ::AttributeReader(nullptr, eventParameterTypesAttribute).Read<decltype(eventParameterTypes)>(eventParameterTypes))
|
||||
{
|
||||
if (eventParameterTypes.size() != behaviorAzEventDesc.m_parameterNames.size())
|
||||
{
|
||||
AZ_Error("BehaviorContext", false, "AzEventDescription only contains names for %zu parameters,"
|
||||
" while the AZ::Event(%s) accepts %zu parameters", behaviorAzEventDesc.m_parameterNames.size(),
|
||||
behaviorAzEventDesc.m_eventName.c_str(), eventParameterTypes.size());
|
||||
azEventDescValid = false;
|
||||
}
|
||||
|
||||
size_t parameterIndex = 0;
|
||||
for (AZStd::string_view parameterName : behaviorAzEventDesc.m_parameterNames)
|
||||
{
|
||||
if (parameterName.empty())
|
||||
{
|
||||
AZ_Error("BehaviorContext", false, "AzEventDescription parameter %zu contains an empty name parameter"
|
||||
" for AZ::Event(%s)", parameterIndex, behaviorAzEventDesc.m_eventName.c_str());
|
||||
azEventDescValid = false;
|
||||
}
|
||||
++parameterIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return azEventDescValid;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// BehaviorMethod
|
||||
//=========================================================================
|
||||
@@ -233,6 +325,10 @@ namespace AZ
|
||||
|
||||
if (m_method)
|
||||
{
|
||||
if (MethodReturnsAzEventByReferenceOrPointer(*m_method))
|
||||
{
|
||||
ValidateAzEventDescription(*Base::m_context, *m_method);
|
||||
}
|
||||
BehaviorContextBus::Event(m_context, &BehaviorContextBus::Events::OnAddGlobalMethod, m_name, m_method);
|
||||
}
|
||||
}
|
||||
@@ -268,6 +364,12 @@ namespace AZ
|
||||
|
||||
if (m_prop)
|
||||
{
|
||||
// Only the property getter needs to be validated to determine if it returns an AZ::Event
|
||||
// and have the AzEventDescription attribute attached to that event
|
||||
if (m_prop->m_getter && MethodReturnsAzEventByReferenceOrPointer(*m_prop->m_getter))
|
||||
{
|
||||
ValidateAzEventDescription(*Base::m_context, *m_prop->m_getter);
|
||||
}
|
||||
BehaviorContextBus::Event(m_context, &BehaviorContextBus::Events::OnAddGlobalProperty, m_prop->m_name.c_str(), m_prop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace AZ
|
||||
*/
|
||||
struct BehaviorParameter
|
||||
{
|
||||
AZ_TYPE_INFO(BehaviorParameter, "{BD7B664E-5B8C-4B51-84F3-DE89B271E075}")
|
||||
/// Temporary POD buffer when we convert parameters on the stack.
|
||||
typedef AZStd::static_buffer_allocator<32, 32> TempValueParameterAllocator;
|
||||
|
||||
@@ -243,6 +244,27 @@ namespace AZ
|
||||
virtual BehaviorDefaultValuePtr GetDefaultValue(size_t i) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores the name of an AZ::Event<Params...> and names for each of it's parameters
|
||||
* For use in scripting to annotate functions and nodes with user-friendly names
|
||||
*/
|
||||
struct BehaviorAzEventDescription
|
||||
{
|
||||
AZ_TYPE_INFO(BehaviorAzEventDescription, "{B5D95E87-FA17-41C7-AC90-7258A520FE82}");
|
||||
AZStd::string m_eventName;
|
||||
AZStd::vector<AZStd::string> m_parameterNames;
|
||||
};
|
||||
|
||||
//! Checks if the supplied BehaviorMethod returns AZ::Event by either pointer or reference
|
||||
bool MethodReturnsAzEventByReferenceOrPointer(const AZ::BehaviorMethod& method);
|
||||
|
||||
//! Validates that a method that returns an AZ::Event fulfills the following conditions.
|
||||
//! 1. It has an AzEventDescription that stores a BehaviorAzEventDescription instance
|
||||
//! 2. The number of parameters that the method accepts, matches the number of elements
|
||||
//! in the parameter names array
|
||||
//! 3. Neither the AZ::Event name nor any of it's parameters are an empty string
|
||||
bool ValidateAzEventDescription(const AZ::BehaviorContext& context, const AZ::BehaviorMethod& method);
|
||||
|
||||
/**
|
||||
* Use behavior method to get type information and invoke reflected methods.
|
||||
*/
|
||||
@@ -393,7 +415,10 @@ namespace AZStd
|
||||
template<>
|
||||
struct hash<AZ::ExplicitOverloadInfo>
|
||||
{
|
||||
AZ_INLINE size_t operator() (const AZ::ExplicitOverloadInfo& info) const
|
||||
using argument_type = AZ::ExplicitOverloadInfo;
|
||||
using result_type = size_t;
|
||||
|
||||
AZ_INLINE result_type operator() (const argument_type& info) const
|
||||
{
|
||||
size_t h = 0;
|
||||
hash_combine(h, info.m_name);
|
||||
@@ -404,7 +429,10 @@ namespace AZStd
|
||||
template<>
|
||||
struct hash<AZ::CheckedOperationInfo>
|
||||
{
|
||||
AZ_INLINE size_t operator() (const AZ::CheckedOperationInfo& info) const
|
||||
using argument_type = AZ::CheckedOperationInfo;
|
||||
using result_type = size_t;
|
||||
|
||||
AZ_INLINE result_type operator() (const argument_type& info) const
|
||||
{
|
||||
size_t h = 0;
|
||||
hash_combine(h, info.m_safetyCheckName);
|
||||
@@ -415,6 +443,18 @@ namespace AZStd
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
// AZ::Event support
|
||||
using BehaviorFunction = AZStd::function<void(BehaviorValueParameter* result, BehaviorValueParameter* arguments, int numArguments)>;
|
||||
using EventHandlerCreationFunction = AZStd::function<BehaviorObject(void* , BehaviorFunction&&)>;
|
||||
|
||||
struct EventHandlerCreationFunctionHolder
|
||||
{
|
||||
AZ_TYPE_INFO(EventHandlerCreationFunctionHolder, "{40F7C5D8-8DA0-4979-BC8C-0A52EDA80633}");
|
||||
AZ_CLASS_ALLOCATOR(EventHandlerCreationFunctionHolder, AZ::SystemAllocator, 0);
|
||||
|
||||
EventHandlerCreationFunction m_function;
|
||||
};
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
const AZ::TypeId& GetUnderlyingTypeId(const IRttiHelper& enumRttiHelper);
|
||||
@@ -1695,17 +1735,6 @@ namespace AZ
|
||||
BehaviorEBus* m_ebus;
|
||||
};
|
||||
|
||||
/// @cond EXCLUDE_DOCS
|
||||
using GlobalMethodInfo = GlobalMethodBuilder; ///< @deprecated Use BehaviorContext::GlobalMethodBuilder
|
||||
using GlobalPropertyInfo = GlobalPropertyBuilder; ///< @deprecated Use BehaviorContext::GlobalPropertyBuilder
|
||||
|
||||
template<class C>
|
||||
using ClassReflection = ClassBuilder<C>; ///< @deprecated Use BehaviorContext::ClassBuilder
|
||||
|
||||
template<typename Bus>
|
||||
using EBusReflection = EBusBuilder<Bus>; ///< @deprecated Use BehaviorContext::EBusBuilder
|
||||
/// @endcond
|
||||
|
||||
BehaviorContext();
|
||||
~BehaviorContext();
|
||||
|
||||
@@ -1779,9 +1808,6 @@ namespace AZ
|
||||
return uuid == GetVoidTypeId();
|
||||
}
|
||||
|
||||
// TODO: This is only for searching by string, do we even need that?
|
||||
//ClassBuilder< OpenNamespace(const char* name);
|
||||
//ClassBuilder<T> CloneNamespace();
|
||||
|
||||
AZStd::unordered_map<AZStd::string, BehaviorMethod*> m_methods; // TODO: make it a set and use the name inside method
|
||||
AZStd::unordered_map<AZStd::string, BehaviorProperty*> m_properties; // TODO: make it a set and use the name inside property
|
||||
@@ -2313,95 +2339,68 @@ namespace AZ
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class T>
|
||||
struct SetResult
|
||||
{
|
||||
static bool Set(BehaviorValueParameter& param, T& result, bool IsValueCopy)
|
||||
// MSVC does not allow an incomplete type to be used in a compiler intrinsic which is the reason why
|
||||
// std::is_assignable_v is not being used here
|
||||
// For some reason the Script.cpp test validates that an incomplete type can be used with the SetResult struct
|
||||
template<typename T, typename U, typename = void>
|
||||
static constexpr bool IsCopyAssignable = false;
|
||||
template<typename T, typename U>
|
||||
static constexpr bool IsCopyAssignable<T, U, AZStd::void_t<decltype(AZStd::declval<T>() = AZStd::declval<U>())>> = true;
|
||||
|
||||
template<class T>
|
||||
static bool Set(BehaviorValueParameter& param, T&& result, bool IsValueCopy)
|
||||
{
|
||||
using Type = AZStd::decay_t<T>;
|
||||
if (param.m_traits & BehaviorParameter::TR_POINTER)
|
||||
{
|
||||
*reinterpret_cast<void**>(param.m_value) = (Type*)&result;
|
||||
if constexpr (AZStd::is_pointer_v<Type>)
|
||||
{
|
||||
using ValueType = AZStd::remove_cvref_t<AZStd::remove_pointer_t<Type>>;
|
||||
*reinterpret_cast<void**>(param.m_value) = const_cast<ValueType*>(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<void**>(param.m_value) = &const_cast<Type&>(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (param.m_traits & BehaviorParameter::TR_REFERENCE)
|
||||
{
|
||||
param.m_value = (Type*)&result;
|
||||
if constexpr (AZStd::is_pointer_v<Type>)
|
||||
{
|
||||
using ValueType = AZStd::remove_cvref_t<AZStd::remove_pointer_t<Type>>;
|
||||
param.m_value = const_cast<ValueType*>(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
param.m_value = &const_cast<Type&>(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (IsValueCopy)
|
||||
{
|
||||
// value copy
|
||||
*reinterpret_cast<Type*>(param.m_value) = result;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct SetResult<T*>
|
||||
{
|
||||
using Type = AZStd::decay_t<T>;
|
||||
|
||||
static bool ValueCopy(BehaviorValueParameter& param, T* result, const AZStd::true_type& /*AZStd::is_copy_constructible */)
|
||||
{
|
||||
new(param.m_value) Type(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ValueCopy(BehaviorValueParameter&, T* , const AZStd::false_type& /*AZStd::is_copy_constructible */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool Set(BehaviorValueParameter& param, T* result, bool IsValueCopy)
|
||||
{
|
||||
if (param.m_traits & BehaviorParameter::TR_POINTER)
|
||||
{
|
||||
*reinterpret_cast<void**>(param.m_value) = (Type*)result;
|
||||
return true;
|
||||
}
|
||||
else if (param.m_traits & BehaviorParameter::TR_REFERENCE)
|
||||
{
|
||||
param.m_value = (Type*)result;
|
||||
return true;
|
||||
}
|
||||
else if (IsValueCopy)
|
||||
{
|
||||
// we need AZStd::is_complete so we can work with incomplete types, then we can enable the code below
|
||||
return false;
|
||||
|
||||
//return ValueCopy(param, result, typename AZStd::conditional<AZStd::is_copy_constructible<Type>::value && !AZStd::is_abstract<Type>::value, AZStd::true_type, AZStd::false_type>::type());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct SetResult<T*&> : public SetResult<T*>
|
||||
{
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct SetResult<T&>
|
||||
{
|
||||
static bool Set(BehaviorValueParameter& param, T& result, bool IsValueCopy)
|
||||
{
|
||||
using Type = AZStd::decay_t<T>;
|
||||
if (param.m_traits & BehaviorParameter::TR_POINTER)
|
||||
{
|
||||
*reinterpret_cast<void**>(param.m_value) = (Type*)&result;
|
||||
return true;
|
||||
}
|
||||
else if (param.m_traits & BehaviorParameter::TR_REFERENCE)
|
||||
{
|
||||
param.m_value = (Type*)&result;
|
||||
return true;
|
||||
}
|
||||
else if (IsValueCopy)
|
||||
{
|
||||
*reinterpret_cast<Type*>(param.m_value) = result;
|
||||
if constexpr (AZStd::is_pointer_v<Type>)
|
||||
{
|
||||
using ValueType = AZStd::remove_cvref_t<AZStd::remove_pointer_t<Type>>;
|
||||
if constexpr (IsCopyAssignable<ValueType&, AZStd::add_lvalue_reference_t<AZStd::remove_pointer_t<T>>>)
|
||||
{
|
||||
// copy if result is non-nullptr
|
||||
if (result != nullptr)
|
||||
{
|
||||
*reinterpret_cast<ValueType*>(param.m_value) = *result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// value copy
|
||||
if constexpr (IsCopyAssignable<Type&, T>)
|
||||
{
|
||||
*reinterpret_cast<Type*>(param.m_value) = AZStd::forward<T>(result);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2435,7 +2434,7 @@ namespace AZ
|
||||
|
||||
if (m_typeId == typeId)
|
||||
{
|
||||
isResult = SetResult<T>::Set(*this, result, true);
|
||||
isResult = SetResult::Set(*this, AZStd::forward<T>(result), true);
|
||||
}
|
||||
else if (GetRttiHelper<Type>())
|
||||
{
|
||||
@@ -2450,7 +2449,7 @@ namespace AZ
|
||||
else if (m_typeId.IsNull()) // if nullptr we can accept any type, by pointer or reference
|
||||
{
|
||||
m_typeId = typeId;
|
||||
isResult = SetResult<T>::Set(*this, result, false);
|
||||
isResult = SetResult::Set(*this, AZStd::forward<T>(result), false);
|
||||
}
|
||||
|
||||
if (isResult && m_onAssignedResult)
|
||||
@@ -2519,8 +2518,15 @@ namespace AZ
|
||||
template<class Getter>
|
||||
bool BehaviorProperty::SetGetter(Getter getter, BehaviorClass* currentClass, BehaviorContext* context, const AZStd::false_type&)
|
||||
{
|
||||
typedef AZ::Internal::BehaviorMethodImpl<typename AZStd::RemoveFunctionConst<typename AZStd::remove_pointer<Getter>::type>::type> GetterType;
|
||||
m_getter = aznew GetterType(getter, context, AZStd::string::format("%s::%s::Getter", currentClass ? currentClass->m_name.c_str() : "", m_name.c_str()));
|
||||
using GetterType = AZ::Internal::BehaviorMethodImpl<typename AZStd::RemoveFunctionConst<AZStd::remove_pointer_t<Getter>>::type>;
|
||||
AZStd::string getterPropertyName = currentClass ? currentClass->m_name : AZStd::string{};
|
||||
if (!getterPropertyName.empty())
|
||||
{
|
||||
getterPropertyName += "::";
|
||||
}
|
||||
getterPropertyName += m_name;
|
||||
getterPropertyName += "::Getter";
|
||||
m_getter = aznew GetterType(getter, context, getterPropertyName);
|
||||
|
||||
if (AZStd::is_class<typename GetterType::ClassType>::value)
|
||||
{
|
||||
@@ -2590,8 +2596,15 @@ namespace AZ
|
||||
template<class Setter>
|
||||
bool BehaviorProperty::SetSetter(Setter setter, BehaviorClass* currentClass, BehaviorContext* context, const AZStd::false_type&)
|
||||
{
|
||||
typedef AZ::Internal::BehaviorMethodImpl<typename AZStd::RemoveFunctionConst<typename AZStd::remove_pointer<Setter>::type>::type> SetterType;
|
||||
m_setter = aznew SetterType(setter, context, AZStd::string::format("%s::%s::Setter", currentClass ? currentClass->m_name.c_str() : "", m_name.c_str()));
|
||||
using SetterType = AZ::Internal::BehaviorMethodImpl<typename AZStd::RemoveFunctionConst<AZStd::remove_pointer_t<Setter>>::type>;
|
||||
AZStd::string setterPropertyName = currentClass ? currentClass->m_name : AZStd::string{};
|
||||
if (!setterPropertyName.empty())
|
||||
{
|
||||
setterPropertyName += "::";
|
||||
}
|
||||
setterPropertyName += m_name;
|
||||
setterPropertyName += "::Setter";
|
||||
m_setter = aznew SetterType(setter, context, setterPropertyName);
|
||||
if (AZStd::is_class<typename SetterType::ClassType>::value)
|
||||
{
|
||||
AZ_Assert(currentClass, "We should declare class property with in the class!");
|
||||
@@ -2998,6 +3011,19 @@ namespace AZ
|
||||
for (auto method : m_class->m_methods)
|
||||
{
|
||||
m_class->PostProcessMethod(Base::m_context, *method.second);
|
||||
if (MethodReturnsAzEventByReferenceOrPointer(*method.second))
|
||||
{
|
||||
ValidateAzEventDescription(*Base::m_context, *method.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the AzEvent description of the class property getter's
|
||||
for (auto&& [propertyName, propertyInst]: m_class->m_properties)
|
||||
{
|
||||
if (propertyInst->m_getter && MethodReturnsAzEventByReferenceOrPointer(*propertyInst->m_getter))
|
||||
{
|
||||
ValidateAzEventDescription(*Base::m_context, *propertyInst->m_getter);
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorContextBus::Event(Base::m_context, &BehaviorContextBus::Events::OnAddClass, m_class->m_name.c_str(), m_class);
|
||||
@@ -3307,8 +3333,7 @@ namespace AZ
|
||||
BehaviorContext::ClassBuilder<C>* BehaviorContext::ClassBuilder<C>::Enum(const char* name)
|
||||
{
|
||||
Property(name, []() { return Value; }, nullptr);
|
||||
BehaviorContext::ClassBuilder<C>::Attribute(AZ::Script::Attributes::ClassConstantValue, true);
|
||||
|
||||
ClassBuilder::Attribute(AZ::Script::Attributes::ClassConstantValue, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3448,6 +3473,13 @@ namespace AZ
|
||||
|
||||
if (!Base::m_context->IsRemovingReflection())
|
||||
{
|
||||
for (auto&& [eventName, eventSender] : m_ebus->m_events)
|
||||
{
|
||||
if (MethodReturnsAzEventByReferenceOrPointer(*eventSender.m_broadcast))
|
||||
{
|
||||
ValidateAzEventDescription(*Base::m_context, *eventSender.m_broadcast);
|
||||
}
|
||||
}
|
||||
BehaviorContextBus::Event(Base::m_context, &BehaviorContextBus::Events::OnAddEBus, m_ebus->m_name.c_str(), m_ebus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,4 +75,44 @@ namespace AZ
|
||||
/// returns true iff a and b have the same type and traits
|
||||
bool TypeCompare(const BehaviorParameter& a, const BehaviorParameter& b);
|
||||
|
||||
// RAII class which scopes the creation and destruction of a BehaviorEBusHandler
|
||||
// contained within the supplied BehaviorEBus class
|
||||
struct ScopedBehaviorEBusHandler
|
||||
{
|
||||
ScopedBehaviorEBusHandler(const AZ::BehaviorEBus& behaviorEbus)
|
||||
: m_behaviorEbus{ behaviorEbus }
|
||||
{
|
||||
if (m_behaviorEbus.m_createHandler)
|
||||
{
|
||||
m_behaviorEbus.m_createHandler->InvokeResult(m_handler);
|
||||
}
|
||||
}
|
||||
~ScopedBehaviorEBusHandler()
|
||||
{
|
||||
if (m_handler && m_behaviorEbus.m_destroyHandler)
|
||||
{
|
||||
m_behaviorEbus.m_destroyHandler->Invoke(m_handler);
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_handler;
|
||||
}
|
||||
|
||||
AZ::BehaviorEBusHandler* operator->() const
|
||||
{
|
||||
return m_handler;
|
||||
}
|
||||
|
||||
AZ::BehaviorEBusHandler& operator*() const
|
||||
{
|
||||
return *m_handler;
|
||||
}
|
||||
|
||||
private:
|
||||
const AZ::BehaviorEBus& m_behaviorEbus;
|
||||
AZ::BehaviorEBusHandler* m_handler{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -99,6 +99,159 @@ namespace AzLsvInternal
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
struct ExposedLambda
|
||||
{
|
||||
AZ_TYPE_INFO(ExposedLambda, "{B702DB0B-516B-4807-8007-DC50A5CE180A}");
|
||||
AZ_CLASS_ALLOCATOR(ExposedLambda, AZ::SystemAllocator, 0);
|
||||
|
||||
// assumes a lambda is at the top of the stack and will pop it
|
||||
ExposedLambda(lua_State* lua)
|
||||
: m_lambdaRegistryIndex(luaL_ref_Checked(lua))
|
||||
, m_lua(lua)
|
||||
{
|
||||
lua_pushinteger(lua, 1);
|
||||
m_refCountRegistryIndex = luaL_ref_Checked(lua);
|
||||
}
|
||||
|
||||
ExposedLambda(ExposedLambda&& source) noexcept
|
||||
{
|
||||
*this = AZStd::move(source);
|
||||
}
|
||||
|
||||
ExposedLambda(const ExposedLambda& source)
|
||||
{
|
||||
*this = source;
|
||||
}
|
||||
|
||||
~ExposedLambda()
|
||||
{
|
||||
if (m_lua && DecrementRefCount() == 0)
|
||||
{
|
||||
luaL_unref(m_lua, LUA_REGISTRYINDEX, m_lambdaRegistryIndex);
|
||||
luaL_unref(m_lua, LUA_REGISTRYINDEX, m_refCountRegistryIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ExposedLambda& operator=(const ExposedLambda& source)
|
||||
{
|
||||
if (this != &source)
|
||||
{
|
||||
m_lambdaRegistryIndex = source.m_lambdaRegistryIndex;
|
||||
m_refCountRegistryIndex = source.m_refCountRegistryIndex;
|
||||
m_lua = source.m_lua;
|
||||
IncrementRefCount();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ExposedLambda& operator=(ExposedLambda&& source) noexcept
|
||||
{
|
||||
if (this != &source)
|
||||
{
|
||||
m_lambdaRegistryIndex = source.m_lambdaRegistryIndex;
|
||||
m_refCountRegistryIndex = source.m_refCountRegistryIndex;
|
||||
source.m_lambdaRegistryIndex = source.m_refCountRegistryIndex = LUA_NOREF;
|
||||
m_lua = source.m_lua;
|
||||
source.m_lua = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()([[maybe_unused]] AZ::BehaviorValueParameter* resultBVP, AZ::BehaviorValueParameter* argsBVPs, int numArguments)
|
||||
{
|
||||
auto behaviorContext = AZ::ScriptContext::FromNativeContext(m_lua)->GetBoundContext();
|
||||
// Lua:
|
||||
lua_rawgeti(m_lua, LUA_REGISTRYINDEX, m_lambdaRegistryIndex);
|
||||
// Lua: lambda
|
||||
|
||||
for (int i = 0; i < numArguments; ++i)
|
||||
{
|
||||
ExposedLambda::StackPush(m_lua, behaviorContext, argsBVPs[i]);
|
||||
}
|
||||
|
||||
// Lua: lambda, args...
|
||||
Internal::LuaSafeCall(m_lua, numArguments, 0);
|
||||
}
|
||||
|
||||
// \note Do not use, these are here for compiler compatibility only
|
||||
ExposedLambda()
|
||||
: m_lua(nullptr)
|
||||
, m_lambdaRegistryIndex(LUA_NOREF)
|
||||
, m_refCountRegistryIndex(LUA_NOREF)
|
||||
{}
|
||||
|
||||
private:
|
||||
static int luaL_ref_Checked(lua_State* lua)
|
||||
{
|
||||
AZ_Assert(lua, "null lua_State");
|
||||
int ref = luaL_ref(lua, LUA_REGISTRYINDEX);
|
||||
AZ_Assert(ref != LUA_NOREF && ref != LUA_REFNIL, "invalid lambdaRegistryIndex");
|
||||
return ref;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T* GetAs(AZ::BehaviorValueParameter& argument)
|
||||
{
|
||||
return argument.m_typeId == azrtti_typeid<T>()
|
||||
? reinterpret_cast<T*>(argument.GetValueAddress())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
static void StackPush(lua_State* lua, AZ::BehaviorContext* context, AZ::BehaviorValueParameter& argument)
|
||||
{
|
||||
if (auto cStringPtr = GetAs<const char*>(argument))
|
||||
{
|
||||
auto realValue = reinterpret_cast<const char*>(cStringPtr);
|
||||
lua_pushstring(lua, realValue);
|
||||
}
|
||||
else if (auto stringPtr = GetAs<AZStd::string>(argument))
|
||||
{
|
||||
lua_pushlstring(lua, stringPtr->data(), stringPtr->size());
|
||||
}
|
||||
else if (auto viewPtr = GetAs<AZStd::string_view>(argument))
|
||||
{
|
||||
lua_pushlstring(lua, viewPtr->data(), viewPtr->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::StackPush(lua, context, argument);
|
||||
}
|
||||
}
|
||||
|
||||
int m_lambdaRegistryIndex;
|
||||
int m_refCountRegistryIndex;
|
||||
lua_State* m_lua;
|
||||
|
||||
int AddRefCount(int value)
|
||||
{
|
||||
AZ_Assert(value == 1 || value == -1, "ModRefCount is only for incrementing or decrementing on copy or destruction of ExposedLambda")
|
||||
lua_rawgeti(m_lua, LUA_REGISTRYINDEX, m_refCountRegistryIndex);
|
||||
// Lua: refCount-old
|
||||
const int refCount = Internal::azlua_tointeger(m_lua, -1) + value;
|
||||
lua_pushinteger(m_lua, m_refCountRegistryIndex);
|
||||
// Lua: refCount-old, registry index
|
||||
lua_pushinteger(m_lua, refCount);
|
||||
// Lua: refCount-old, registry index, refCount-new
|
||||
lua_rawset(m_lua, LUA_REGISTRYINDEX);
|
||||
// Lua: refCount-old
|
||||
lua_pop(m_lua, 1);
|
||||
// Lua:
|
||||
return refCount;
|
||||
}
|
||||
|
||||
int DecrementRefCount()
|
||||
{
|
||||
return AddRefCount(-1);
|
||||
}
|
||||
|
||||
void IncrementRefCount()
|
||||
{
|
||||
AddRefCount(1);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr const char* StorageTypeToString(Script::Attributes::StorageType storageType)
|
||||
{
|
||||
switch (storageType)
|
||||
@@ -4942,6 +5095,11 @@ LUA_API const Node* lua_getDummyNode()
|
||||
//lua_rawseti(l, -2, AZ_LUA_CLASS_METATABLE_STORAGE_CREATOR_INDEX);
|
||||
|
||||
BindClassMethodAndProperties(behaviorClass);
|
||||
|
||||
if (AZ::Attribute* eventHandlerCreationFunctionAttribute = FindAttribute(AZ::Script::Attributes::EventHandlerCreationFunction, behaviorClass->m_attributes))
|
||||
{
|
||||
BindEventSupport();
|
||||
}
|
||||
|
||||
if (storageType != Script::Attributes::StorageType::Value)
|
||||
{
|
||||
@@ -5194,6 +5352,50 @@ LUA_API const Node* lua_getDummyNode()
|
||||
|
||||
Internal::azlua_setglobal(m_lua, ValidateName(ebusName));
|
||||
}
|
||||
|
||||
static int ConnectToExposedEvent(lua_State* lua)
|
||||
{
|
||||
if (!(lua_isuserdata(lua, -2) && !lua_islightuserdata(lua, -2)))
|
||||
{
|
||||
ScriptContext::FromNativeContext(lua)->Error(ScriptContext::ErrorType::Error, true, "1st argument to ConnectToExposedEvent is not userdata");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lua_isfunction(lua, -1))
|
||||
{
|
||||
ScriptContext::FromNativeContext(lua)->Error(ScriptContext::ErrorType::Error, true, "2nd argument to ConnectToExposedEvent is not a function (lambda need to get around atypically routed arguments)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto userData = reinterpret_cast<AZ::LuaUserData*>(lua_touserdata(lua, -2));
|
||||
AZ_Assert(userData && userData->magicData == AZ_CRC_CE("AZLuaUserData"), "1st argument is not user AZ supported userdata");
|
||||
AZ::Attribute* eventHandlerCreationFunctionAttribute = FindAttribute(AZ::Script::Attributes::EventHandlerCreationFunction, userData->behaviorClass->m_attributes);
|
||||
AZ_Assert(eventHandlerCreationFunctionAttribute, "failure to expose AZ::Event type in class reflected to Lua");
|
||||
AZ::AttributeReader attributeReader(nullptr, eventHandlerCreationFunctionAttribute);
|
||||
AZ::EventHandlerCreationFunctionHolder holder;
|
||||
attributeReader.Read<EventHandlerCreationFunctionHolder>(holder);
|
||||
|
||||
// Lua: ExposedEvent, lambda
|
||||
lua_pushvalue(lua, -1);
|
||||
// Lua: ExposedEvent, lambda, lambda
|
||||
auto handlerAndType = AZStd::invoke(holder.m_function, userData->value, AZStd::move(ExposedLambda(lua)));
|
||||
// Lua: ExposedEvent, lambda
|
||||
Internal::RegisteredObjectToLua(lua, handlerAndType.m_address, handlerAndType.m_typeId, ObjectToLua::ByReference, AcquisitionOnPush::ScriptAcquire);
|
||||
// Lua: ExposedEvent, lambda, handler
|
||||
return 1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void BindEventSupport()
|
||||
{
|
||||
// Lua: ..., class_mt
|
||||
lua_pushliteral(m_lua, "Connect");
|
||||
// Lua: ..., class_mt, "Connect"
|
||||
lua_pushcfunction(m_lua, &ConnectToExposedEvent);
|
||||
// Lua: ..., class_mt, "Connect", ConnectToExposedEvent
|
||||
lua_rawset(m_lua, -3);
|
||||
// Lua: ..., class_mt
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void BindTo(BehaviorContext* behaviorContext)
|
||||
@@ -5674,7 +5876,7 @@ LUA_API const Node* lua_getDummyNode()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool ScriptContext::Call(const char* functionName, ScriptDataContext& dc, bool warnIfNotFound)
|
||||
bool ScriptContext::Call(const char* functionName, ScriptDataContext& dc)
|
||||
{
|
||||
dc.Reset();
|
||||
Internal::azlua_getglobal(m_impl->m_lua, functionName);
|
||||
@@ -5686,10 +5888,7 @@ LUA_API const Node* lua_getDummyNode()
|
||||
else
|
||||
{
|
||||
lua_pop(m_impl->m_lua, 1);
|
||||
if (warnIfNotFound)
|
||||
{
|
||||
AZ_Warning("Script", false, "%s is not a function!", functionName);
|
||||
}
|
||||
AZ_Warning("Script", false, "%s is not a function!", functionName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -888,7 +888,7 @@ namespace AZ
|
||||
int CacheGlobal(const char* name);
|
||||
/// Release any cached resource (global or local)
|
||||
void ReleaseCached(int cacheIndex);
|
||||
bool Call(const char* functionName, ScriptDataContext& dc, bool warnIfNotFound = true);
|
||||
bool Call(const char* functionName, ScriptDataContext& dc);
|
||||
bool CallCached(int cachedIndex, ScriptDataContext& dc);
|
||||
|
||||
bool InspectTable(const char* tableName, ScriptDataContext& dc);
|
||||
|
||||
@@ -35,6 +35,14 @@ namespace AZ
|
||||
const static AZ::Crc32 DisallowBroadcast = AZ_CRC("DisallowBroadcast", 0x389b0ac7); ///< Marks a reflected EBus as not allowing Broadcasts, only Events.
|
||||
const static AZ::Crc32 ClassConstantValue = AZ_CRC_CE("ClassConstantValue"); ///< Indicates the property is backed by a constant value
|
||||
|
||||
//! Attribute which stores BehaviorAzEventDescription structure which contains
|
||||
//! the script name of an AZ::Event and the name of it's parameter arguments
|
||||
static constexpr AZ::Crc32 AzEventDescription = AZ_CRC_CE("AzEventDescription");
|
||||
|
||||
//! Applied to AZ Event reflected classes.
|
||||
//! Stores a vector<TypeId> containing the Uuid of each event param
|
||||
static constexpr AZ::Crc32 EventParameterTypes = AZ_CRC_CE("EventParameterTypes");
|
||||
|
||||
///< Recommends that the Lua runtime look up the member function in the meta table of the first argument, rather than in the original table
|
||||
const static AZ::Crc32 TreatAsMemberFunction = AZ_CRC("TreatAsMemberFunction", 0x64be831a);
|
||||
|
||||
|
||||
@@ -46,14 +46,14 @@ namespace AZ
|
||||
const AZStd::chrono::system_clock::time_point& Get() { return m_timePoint; }
|
||||
|
||||
// Returns the time point in seconds
|
||||
double GetSeconds()
|
||||
double GetSeconds() const
|
||||
{
|
||||
typedef AZStd::chrono::duration<double> double_seconds;
|
||||
return AZStd::chrono::duration_cast<double_seconds>(m_timePoint.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
// Returns the time point in milliseconds
|
||||
double GetMilliseconds()
|
||||
double GetMilliseconds() const
|
||||
{
|
||||
typedef AZStd::chrono::duration<double, AZStd::milli> double_ms;
|
||||
return AZStd::chrono::duration_cast<double_ms>(m_timePoint.time_since_epoch()).count();
|
||||
|
||||
@@ -28,8 +28,8 @@ namespace AZ
|
||||
AZ_RTTI(JsonRegistrationContext, "{5A763774-CA8B-4245-A897-A03C503DCD60}", ReflectContext);
|
||||
|
||||
class SerializerBuilder;
|
||||
using SerializerMap = AZStd::unordered_map<const Uuid&, AZStd::unique_ptr<BaseJsonSerializer>, AZStd::hash<Uuid>>;
|
||||
using HandledTypesMap = AZStd::unordered_map<const Uuid&, BaseJsonSerializer*, AZStd::hash<Uuid>>;
|
||||
using SerializerMap = AZStd::unordered_map<Uuid, AZStd::unique_ptr<BaseJsonSerializer>, AZStd::hash<Uuid>>;
|
||||
using HandledTypesMap = AZStd::unordered_map<Uuid, BaseJsonSerializer*, AZStd::hash<Uuid>>;
|
||||
|
||||
~JsonRegistrationContext() override;
|
||||
|
||||
|
||||
@@ -526,6 +526,42 @@ namespace AZ
|
||||
return m_editContext;
|
||||
}
|
||||
|
||||
auto SerializeContext::RegisterType(const AZ::TypeId& typeId, AZ::SerializeContext::ClassData&& classData, CreateAnyFunc createAnyFunc) -> ClassBuilder
|
||||
{
|
||||
auto [typeToClassIter, inserted] = m_uuidMap.try_emplace(typeId, AZStd::move(classData));
|
||||
m_classNameToUuid.emplace(AZ::Crc32(typeToClassIter->second.m_name), typeId);
|
||||
m_uuidAnyCreationMap.emplace(typeId, createAnyFunc);
|
||||
|
||||
return ClassBuilder(this, typeToClassIter);
|
||||
}
|
||||
|
||||
bool SerializeContext::UnregisterType(const AZ::TypeId& typeId)
|
||||
{
|
||||
if (auto typeToClassIter = m_uuidMap.find(typeId); typeToClassIter != m_uuidMap.end())
|
||||
{
|
||||
ClassData& classData = typeToClassIter->second;
|
||||
RemoveClassData(&classData);
|
||||
|
||||
auto [classNameRangeFirst, classNameRangeLast] = m_classNameToUuid.equal_range(Crc32(classData.m_name));
|
||||
while (classNameRangeFirst != classNameRangeLast)
|
||||
{
|
||||
if (classNameRangeFirst->second == typeId)
|
||||
{
|
||||
classNameRangeFirst = m_classNameToUuid.erase(classNameRangeFirst);
|
||||
}
|
||||
else
|
||||
{
|
||||
++classNameRangeFirst;
|
||||
}
|
||||
}
|
||||
m_uuidAnyCreationMap.erase(typeId);
|
||||
m_uuidMap.erase(typeToClassIter);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ClassDeprecate
|
||||
// [11/8/2012]
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace AZ
|
||||
typedef AZStd::unordered_map<Uuid, ClassData> UuidToClassMap;
|
||||
|
||||
/// If registerIntegralTypes is true we will register the default serializer for all integral types.
|
||||
SerializeContext(bool registerIntegralTypes = true, bool createEditContext = false);
|
||||
explicit SerializeContext(bool registerIntegralTypes = true, bool createEditContext = false);
|
||||
virtual ~SerializeContext();
|
||||
|
||||
/// Deleting copy ctor because we own unique_ptr's of IDataContainers
|
||||
@@ -185,6 +185,17 @@ namespace AZ
|
||||
template<typename EnumType>
|
||||
EnumBuilder Enum(IObjectFactory* factory);
|
||||
|
||||
//! Function Pointer which is used to construct an AZStd::any for a registered type using the Serialize Context
|
||||
using CreateAnyFunc = AZStd::any(*)(SerializeContext*);
|
||||
//! Allows registration of a TypeId without the need to supply a C++ type
|
||||
//! If the type is not already registered, then the ClassData is moved into the SerializeContext
|
||||
//! internal structure
|
||||
ClassBuilder RegisterType(const AZ::TypeId& typeId, AZ::SerializeContext::ClassData&& classData,
|
||||
CreateAnyFunc createAnyFunc = [](SerializeContext*) -> AZStd::any { return {}; });
|
||||
|
||||
//! Unregister a type from the SerializeContext and removes all internal mappings
|
||||
//! @return true if the type was previously registered
|
||||
bool UnregisterType(const AZ::TypeId& typeId);
|
||||
// Helper method that gets the generic info of ValueType and calls Reflect on it, should it exist
|
||||
template <class ValueType>
|
||||
void RegisterGenericType();
|
||||
@@ -1055,7 +1066,6 @@ namespace AZ
|
||||
AZStd::any CreateAny(const Uuid& classId);
|
||||
|
||||
/// Register GenericClassInfo with the SerializeContext
|
||||
using CreateAnyFunc = AZStd::any(*)(SerializeContext*);
|
||||
void RegisterGenericClassInfo(const AZ::Uuid& typeId, GenericClassInfo* genericClassInfo, const CreateAnyFunc& createAnyFunc);
|
||||
|
||||
/// Unregisters all GenericClassInfo instances registered in the current module and deletes the GenericClassInfo instances
|
||||
@@ -1091,11 +1101,6 @@ namespace AZ
|
||||
const TypeId& GetUnderlyingTypeId(const TypeId& enumTypeId) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Generic enumerate function can can take both 'const void*' and 'void*' data pointer types.
|
||||
*/
|
||||
template<class PtrType, class EnumType>
|
||||
bool EnumerateInstanceTempl(PtrType ptr, const Uuid& classId, EnumType beginElemCB, const SerializeContext::EndElemEnumCB& endElemCB, const ClassData* classData, const char* elementName, const ClassElement* classElement);
|
||||
|
||||
/// Enumerate function called to enumerate an azrtti hierarchy
|
||||
static void EnumerateBaseRTTIEnumCallback(const Uuid& id, void* userData);
|
||||
|
||||
@@ -1059,6 +1059,15 @@ namespace AZ
|
||||
}
|
||||
|
||||
u64 fileSize = file.Length();
|
||||
if (fileSize == 0)
|
||||
{
|
||||
AZ_Warning("Settings Registry", false, R"(Registry file "%s" is 0 bytes in length. There is no nothing to merge)", path);
|
||||
pointer.Create(m_settings, m_settings.GetAllocator())
|
||||
.SetObject()
|
||||
.AddMember(StringRef("Error"), StringRef("registry file is 0 bytes."), m_settings.GetAllocator())
|
||||
.AddMember(StringRef("Path"), Value(path, m_settings.GetAllocator()), m_settings.GetAllocator());
|
||||
return false;
|
||||
}
|
||||
scratchBuffer.clear();
|
||||
scratchBuffer.resize_no_construct(fileSize + 1);
|
||||
if (file.Read(fileSize, scratchBuffer.data()) != fileSize)
|
||||
@@ -1076,8 +1085,17 @@ namespace AZ
|
||||
jsonPatch.ParseInsitu<flags>(scratchBuffer.data());
|
||||
if (jsonPatch.HasParseError())
|
||||
{
|
||||
AZ_Error("Settings Registry", false, R"(Unable to parse registry file "%s" due to json error "%s" at offset %llu.)",
|
||||
path, GetParseError_En(jsonPatch.GetParseError()), jsonPatch.GetErrorOffset());
|
||||
if (jsonPatch.GetParseError() == rapidjson::kParseErrorDocumentEmpty)
|
||||
{
|
||||
AZ_Warning("Settings Registry", false, R"(Unable to parse registry file "%s" due to json error "%s" at offset %llu.)",
|
||||
path, GetParseError_En(jsonPatch.GetParseError()), jsonPatch.GetErrorOffset());
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Settings Registry", false, R"(Unable to parse registry file "%s" due to json error "%s" at offset %llu.)", path,
|
||||
GetParseError_En(jsonPatch.GetParseError()), jsonPatch.GetErrorOffset());
|
||||
}
|
||||
|
||||
pointer.Create(m_settings, m_settings.GetAllocator()).SetObject()
|
||||
.AddMember(StringRef("Error"), StringRef("Unable to parse registry file due to invalid json."), m_settings.GetAllocator())
|
||||
.AddMember(StringRef("Path"), Value(path, m_settings.GetAllocator()), m_settings.GetAllocator())
|
||||
|
||||
@@ -211,6 +211,8 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
|
||||
void MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(SettingsRegistryInterface& registry, AZStd::string_view targetName)
|
||||
{
|
||||
registry.Set(BuildTargetNameKey, targetName);
|
||||
|
||||
// Add specializations to the target registry based on the name of the Build System Target
|
||||
auto targetSpecialization = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%.*s",
|
||||
SpecializationsRootKey, aznumeric_cast<int>(targetName.size()), targetName.data());
|
||||
@@ -305,7 +307,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
// Iterate over the line and escape the '~' and '/' values
|
||||
AZStd::fixed_string<ConfigBufferMaxSize> escapedLine = EncodeLineForJsonPointer<ConfigBufferMaxSize>(line,
|
||||
configParserSettings.m_commandLineSettings.m_delimiterFunc);
|
||||
|
||||
|
||||
registry.MergeCommandLineArgument(escapedLine, currentJsonPointerPath, configParserSettings.m_commandLineSettings);
|
||||
|
||||
// Skip past the newline character if found
|
||||
@@ -410,6 +412,13 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
#endif
|
||||
registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
|
||||
|
||||
// check for a default write storage path, fall back to the cache root if not
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> devWriteStorage = Utils::GetDevWriteStoragePath();
|
||||
registry.Set(FilePathKey_DevWriteStorage,
|
||||
devWriteStorage.has_value() ?
|
||||
devWriteStorage.value() :
|
||||
path.LexicallyNormal().Native());
|
||||
|
||||
// Cache game folder - corresponds to the @assets@ alias
|
||||
AZStd::to_lower(projectPathValue.begin(), projectPathValue.end());
|
||||
path /= projectPathValue;
|
||||
@@ -614,7 +623,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
switchKey += '/';
|
||||
switchKey += commandOption;
|
||||
size_t switchKeyRootSize = switchKey.size();
|
||||
// Associate an empty array with the commandOption by default
|
||||
// Associate an empty array with the commandOption by default
|
||||
rapidjson::Document commandSwitchDocument;
|
||||
rapidjson::Pointer pointer(switchKey.c_str(), switchKey.length());
|
||||
pointer.Set(commandSwitchDocument, rapidjson::Value(rapidjson::kArrayType));;
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace AZ::IO
|
||||
namespace AZ::SettingsRegistryMergeUtils
|
||||
{
|
||||
inline static constexpr char OrganizationRootKey[] = "/Amazon";
|
||||
inline static constexpr char BuildTargetNameKey[] = "/Amazon/AzCore/Settings/BuildTargetName";
|
||||
inline static constexpr char SpecializationsRootKey[] = "/Amazon/AzCore/Settings/Specializations";
|
||||
inline static constexpr char BootstrapSettingsRootKey[] = "/Amazon/AzCore/Bootstrap";
|
||||
inline static constexpr char GemListRootKey[] = "/Amazon/AzCore/Gems";
|
||||
@@ -40,12 +41,14 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
inline static constexpr char FilePathKey_SourceGameFolder[] = "/Amazon/AzCore/Runtime/FilePaths/SourceGameFolder";
|
||||
//! Stores the filename of the Game Project Directory which is equivalent to the project name
|
||||
inline static constexpr char FilePathKey_SourceGameName[] = "/Amazon/AzCore/Runtime/FilePaths/SourceGameName";
|
||||
//! Development write storage path may be considered temporary or cache storage on some platforms
|
||||
inline static constexpr char FilePathKey_DevWriteStorage[] = "/Amazon/AzCore/Runtime/FilePaths/DevWriteStorage";
|
||||
|
||||
//! Root key for where command line are stored at witin the settings registry
|
||||
inline static constexpr char CommandLineRootKey[] = "/Amazon/AzCore/Runtime/CommandLine";
|
||||
//! Root key for command line switches(arguments that start with "-" or "--")
|
||||
inline static constexpr char CommandLineSwitchRootKey[] = "/Amazon/AzCore/Runtime/CommandLine/Switches";
|
||||
//! Root key for command line positional arguments
|
||||
//! Root key for command line positional arguments
|
||||
inline static constexpr char CommandLineMiscValuesRootKey[] = "/Amazon/AzCore/Runtime/CommandLine/MiscValues";
|
||||
|
||||
//! Examines the Settings Registry for a "/Amazon/CommandLine/Switches/app-root" key
|
||||
@@ -174,7 +177,7 @@ namespace AZ::SettingsRegistryMergeUtils
|
||||
//! Determines if a PrettyWriter should be used when dumping the Settings Registry
|
||||
bool m_prettifyOutput{};
|
||||
//! Include filter which is used to indicate which paths of the Settings Registry
|
||||
//! should be traversed.
|
||||
//! should be traversed.
|
||||
//! If the include filter is empty then all paths underneath the JSON pointer path are included
|
||||
//! otherwise the include filter invoked and if it returns true does it proceed with traversal continues down the path
|
||||
AZStd::function<bool(AZStd::string_view path)> m_includeFilter;
|
||||
|
||||
@@ -18,15 +18,38 @@
|
||||
|
||||
namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
{
|
||||
static void RegisterScriptProxyForNotify(SettingsRegistryScriptProxy& settingsRegistryProxy)
|
||||
{
|
||||
if (settingsRegistryProxy.IsValid())
|
||||
{
|
||||
auto ForwardSettingsUpdateToProxyEvent = [&settingsRegistryProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
|
||||
{
|
||||
if (settingsRegistryProxy.m_notifyEventProxy)
|
||||
{
|
||||
settingsRegistryProxy.m_notifyEventProxy->m_scriptNotifyEvent.Signal(path);
|
||||
}
|
||||
};
|
||||
// Register the forwarding function with the BehaviorContext
|
||||
settingsRegistryProxy.m_notifyEventProxy->m_settingsUpdatedHandler =
|
||||
settingsRegistryProxy.m_settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
|
||||
}
|
||||
}
|
||||
|
||||
SettingsRegistryScriptProxy::SettingsRegistryScriptProxy() = default;
|
||||
SettingsRegistryScriptProxy::SettingsRegistryScriptProxy(AZStd::shared_ptr<AZ::SettingsRegistryInterface> settingsRegistry)
|
||||
: m_settingsRegistry(AZStd::move(settingsRegistry))
|
||||
{}
|
||||
, m_notifyEventProxy(AZStd::make_shared<NotifyEventProxy>())
|
||||
{
|
||||
RegisterScriptProxyForNotify(*this);
|
||||
}
|
||||
|
||||
// Raw AZ::SettingsRegistryInterface pointer is not owned by the proxy, so it's deleter is a no-op
|
||||
SettingsRegistryScriptProxy::SettingsRegistryScriptProxy(AZ::SettingsRegistryInterface* const settingsRegistry)
|
||||
: m_settingsRegistry(settingsRegistry, [](AZ::SettingsRegistryInterface*) {})
|
||||
{}
|
||||
, m_notifyEventProxy(AZStd::make_shared<NotifyEventProxy>())
|
||||
{
|
||||
RegisterScriptProxyForNotify(*this);
|
||||
}
|
||||
|
||||
// SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid
|
||||
bool SettingsRegistryScriptProxy::IsValid() const
|
||||
@@ -69,23 +92,23 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
// Reflect Specializations structure
|
||||
auto SpecializationsAppend = [](SpecializationsProxy* tagInst, AZStd::string_view specialization)
|
||||
{
|
||||
return tagInst->m_specializations->Append(specialization);
|
||||
return tagInst && tagInst->m_specializations->Append(specialization);
|
||||
};
|
||||
auto SpecializationsContains = [](SpecializationsProxy* tagInst, AZStd::string_view specialization)
|
||||
{
|
||||
return tagInst->m_specializations->Contains(specialization);
|
||||
return tagInst && tagInst->m_specializations->Contains(specialization);
|
||||
};
|
||||
auto SpecializationsGetPriority = [](SpecializationsProxy* tagInst, AZStd::string_view specialization)
|
||||
{
|
||||
return tagInst->m_specializations->GetPriority(specialization);
|
||||
return tagInst ? tagInst->m_specializations->GetPriority(specialization) : 0U;
|
||||
};
|
||||
auto SpecializationsGetCount = [](SpecializationsProxy* tagInst)
|
||||
{
|
||||
return tagInst->m_specializations->GetCount();
|
||||
return tagInst ? tagInst->m_specializations->GetCount() : 0U;
|
||||
};
|
||||
auto SpecializationsGetSpecialization = [](SpecializationsProxy* tagInst, size_t index)
|
||||
{
|
||||
return tagInst->m_specializations->GetSpecialization(index);
|
||||
return tagInst ? tagInst->m_specializations->GetSpecialization(index) : AZStd::string_view{};
|
||||
};
|
||||
behaviorContext.Class<SpecializationsProxy>("Specializations")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
|
||||
@@ -121,7 +144,8 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto MergeSettings = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonData,
|
||||
AZ::SettingsRegistryInterface::Format format) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->MergeSettings(jsonData, format);
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->MergeSettings(jsonData, format);
|
||||
};
|
||||
// Set a default value for the Setting Registry Merge Format parameter
|
||||
// This allows the function to be called from the BehaviorContext using only the json data parameter
|
||||
@@ -132,7 +156,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto MergeSettingsFile = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view filePath,
|
||||
AZStd::string_view jsonRootKey, AZ::SettingsRegistryInterface::Format format) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid()
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->MergeSettingsFile(filePath, format, jsonRootKey);
|
||||
};
|
||||
using MergeSettingsFileFunctionTraits = AZStd::function_traits<decltype(MergeSettingsFile)>;
|
||||
@@ -153,7 +177,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto MergeSettingsFolder = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view folderPath,
|
||||
const Internal::SpecializationsProxy& specProxy, AZStd::string_view platform) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid()
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->MergeSettingsFolder(folderPath, *specProxy.m_specializations, platform);
|
||||
};
|
||||
using MergeSettingsFolderFunctionTraits = AZStd::function_traits<decltype(MergeSettingsFolder)>;
|
||||
@@ -173,24 +197,29 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
|
||||
auto SetBool = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath, bool boolValue) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, boolValue);
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, boolValue);
|
||||
};
|
||||
auto SetInt = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath, AZ::s64 intValue) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, intValue);
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, intValue);
|
||||
};
|
||||
auto SetUint = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath, AZ::u64 uintValue) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, uintValue);
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, uintValue);
|
||||
};
|
||||
auto SetFloat = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath, double floatValue) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, floatValue);
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, floatValue);
|
||||
};
|
||||
auto SetString = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath, AZStd::string_view stringValue)
|
||||
-> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, stringValue);
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Set(jsonPath, stringValue);
|
||||
};
|
||||
|
||||
// Query functors
|
||||
@@ -200,8 +229,9 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
AZStd::string outputString;
|
||||
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings{ true };
|
||||
AZ::IO::ByteContainerStream outputStream(&outputString);
|
||||
if (settingsRegistryProxy->IsValid() && AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(
|
||||
*settingsRegistryProxy->m_settingsRegistry, jsonPath, outputStream, dumperSettings))
|
||||
if (settingsRegistryProxy && settingsRegistryProxy->IsValid() &&
|
||||
AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*settingsRegistryProxy->m_settingsRegistry,
|
||||
jsonPath, outputStream, dumperSettings))
|
||||
{
|
||||
return outputString;
|
||||
}
|
||||
@@ -212,7 +242,8 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto GetBool = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath) -> AZStd::optional<bool>
|
||||
{
|
||||
bool boolValue{};
|
||||
if (settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Get(boolValue, jsonPath))
|
||||
if (settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Get(boolValue, jsonPath))
|
||||
{
|
||||
return boolValue;
|
||||
}
|
||||
@@ -222,7 +253,8 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto GetInt = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath) -> AZStd::optional<s64>
|
||||
{
|
||||
AZ::s64 intValue{};
|
||||
if (settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Get(intValue, jsonPath))
|
||||
if (settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Get(intValue, jsonPath))
|
||||
{
|
||||
return intValue;
|
||||
}
|
||||
@@ -232,7 +264,8 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto GetUint = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath) -> AZStd::optional<u64>
|
||||
{
|
||||
AZ::u64 uintValue{};
|
||||
if (settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Get(uintValue, jsonPath))
|
||||
if (settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Get(uintValue, jsonPath))
|
||||
{
|
||||
return uintValue;
|
||||
}
|
||||
@@ -242,7 +275,8 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto GetFloat = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath) -> AZStd::optional<double>
|
||||
{
|
||||
double floatValue{};
|
||||
if (settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Get(floatValue, jsonPath))
|
||||
if (settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Get(floatValue, jsonPath))
|
||||
{
|
||||
return floatValue;
|
||||
}
|
||||
@@ -252,7 +286,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
auto GetString = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath) -> AZStd::optional<AZStd::string>
|
||||
{
|
||||
AZStd::string stringValue;
|
||||
if (settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Get(stringValue, jsonPath))
|
||||
if (settingsRegistryProxy && settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Get(stringValue, jsonPath))
|
||||
{
|
||||
return stringValue;
|
||||
}
|
||||
@@ -263,7 +297,23 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
// SettingsRegistry::Remove wrapper
|
||||
auto RemoveKey = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy, AZStd::string_view jsonPath) -> bool
|
||||
{
|
||||
return settingsRegistryProxy->IsValid() && settingsRegistryProxy->m_settingsRegistry->Remove(jsonPath);
|
||||
return settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_settingsRegistry->Remove(jsonPath);
|
||||
};
|
||||
|
||||
// Reflect function which returns the ScriptNotifyEvent from the SettingsRegistryScriptProxy
|
||||
// in order to trigger the ScriptCanvas feature of allowing creation of an AZ EventHandler
|
||||
// from a BehaviorMethod node which returns an AZ::Event pointer
|
||||
auto GetScriptNotifyEvent = [](Internal::SettingsRegistryScriptProxy* settingsRegistryProxy)
|
||||
-> Internal::SettingsRegistryScriptProxy::ScriptNotifyEvent*
|
||||
{
|
||||
if (settingsRegistryProxy && settingsRegistryProxy->IsValid()
|
||||
&& settingsRegistryProxy->m_notifyEventProxy)
|
||||
{
|
||||
return &settingsRegistryProxy->m_notifyEventProxy->m_scriptNotifyEvent;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
auto settingsRegistryClassBuilder = behaviorContext.Class<Internal::SettingsRegistryScriptProxy>(InterfaceClassName);
|
||||
@@ -287,6 +337,16 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
->Method("GetString", GetString)
|
||||
->Method("RemoveKey", RemoveKey)
|
||||
;
|
||||
|
||||
// Create the BehaviorAzEventDescription needed to reflect the
|
||||
// GetNotifyEvent method to the BehaviorContext without errors
|
||||
AZ::BehaviorAzEventDescription scriptNotifyEventDesc;
|
||||
scriptNotifyEventDesc.m_eventName = "SettingsRegistry Notify Event";
|
||||
scriptNotifyEventDesc.m_parameterNames.push_back("Json Path");
|
||||
|
||||
settingsRegistryClassBuilder->Method("GetNotifyEvent", GetScriptNotifyEvent)
|
||||
->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move(scriptNotifyEventDesc))
|
||||
;
|
||||
}
|
||||
|
||||
void ReflectSettingsRegistryCreateMethod(AZ::BehaviorContext& behaviorContext)
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class BehaviorContext;
|
||||
class SettingsRegistryInterface;
|
||||
}
|
||||
|
||||
|
||||
namespace AZ::SettingsRegistryScriptUtils
|
||||
{
|
||||
//! Reflects the SettingsRegistryInterface class to the BehaviorContext.
|
||||
@@ -34,6 +36,18 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
AZ_CLASS_ALLOCATOR(SettingsRegistryScriptProxy, AZ::SystemAllocator, 0);
|
||||
AZ_TYPE_INFO(SettingsRegistryScriptProxy, "{795C80A0-D243-473B-972A-C32CA487BAA5}");
|
||||
|
||||
// NotifyEventProxy is used to forward an update to an entry in the SettingsRegistry
|
||||
// using the RegisterNotifier function to the BehaviorContext in order to create
|
||||
// a behavior method that returns an AZ::Event reference or pointer
|
||||
// This allows ScriptCanvas to create an AZ Event Handler using the information
|
||||
// reflected to the BehaviorMethod
|
||||
using ScriptNotifyEvent = AZ::Event<AZStd::string_view>;
|
||||
struct NotifyEventProxy
|
||||
{
|
||||
ScriptNotifyEvent m_scriptNotifyEvent;
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_settingsUpdatedHandler;
|
||||
};
|
||||
|
||||
SettingsRegistryScriptProxy();
|
||||
// Stores a SettingsRegistryInterface which will use the provided shared_ptr deleter when the reference count hits zero
|
||||
SettingsRegistryScriptProxy(AZStd::shared_ptr<AZ::SettingsRegistryInterface> settingsRegistry);
|
||||
@@ -44,5 +58,6 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
|
||||
bool IsValid() const;
|
||||
|
||||
AZStd::shared_ptr<AZ::SettingsRegistryInterface> m_settingsRegistry;
|
||||
AZStd::shared_ptr<NotifyEventProxy> m_notifyEventProxy;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
AZ_TYPE_SAFE_INTEGRAL(HashValue32, uint32_t);
|
||||
AZ_TYPE_SAFE_INTEGRAL(HashValue64, uint64_t);
|
||||
AZ_TYPE_SAFE_INTEGRAL(HashValue32, u32);
|
||||
AZ_TYPE_SAFE_INTEGRAL(HashValue64, u64);
|
||||
|
||||
//! Hashes a contiguous array of bytes starting at buffer and ending at buffer + length
|
||||
//! @param[in] buffer pointer to the memory to be hashed
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <AzCore/PlatformDef.h>
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
@@ -44,7 +45,7 @@ namespace AZ
|
||||
//! has closed the message box.
|
||||
void NativeErrorMessageBox(const char* title, const char* message);
|
||||
|
||||
//! Enum used for the GetExecutablePath return type which indicates
|
||||
//! Enum used for the GetExecutablePath return type which indicates
|
||||
//! whether the function returned with a success value or a specific error
|
||||
enum class ExecutablePathResult : int8_t
|
||||
{
|
||||
@@ -66,14 +67,14 @@ namespace AZ
|
||||
//! Retrieves the path to the application executable
|
||||
//! @param exeStorageBuffer output buffer which is used to store the executable path within
|
||||
//! @param exeStorageSize size of the exeStorageBuffer
|
||||
//! @returns a struct that indicates if the executable path was able to be stored within the executableBuffer
|
||||
//! @returns a struct that indicates if the executable path was able to be stored within the executableBuffer
|
||||
//! as well as if the executable path contains the executable filename or the executable directory
|
||||
GetExecutablePathReturnType GetExecutablePath(char* exeStorageBuffer, size_t exeStorageSize);
|
||||
|
||||
//! Retrieves the directory of the application executable
|
||||
//! @param exeStorageBuffer output buffer which is used to store the executable path within
|
||||
//! @param exeStorageSize size of the exeStorageBuffer
|
||||
//! @returns a result object that indicates if the executable directory was able to be stored within the buffer
|
||||
//! @returns a result object that indicates if the executable directory was able to be stored within the buffer
|
||||
ExecutablePathResult GetExecutableDirectory(char* exeStorageBuffer, size_t exeStorageSize);
|
||||
|
||||
//! Retrieves the App root path to use on the current platform
|
||||
@@ -81,6 +82,10 @@ namespace AZ
|
||||
//! on the location of the bootstrap.cfg file
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> GetDefaultAppRootPath();
|
||||
|
||||
//! Retrieves the development write storage path to use on the current platform, may be considered
|
||||
//! temporary or cache storage
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath();
|
||||
|
||||
// Attempts the supplied path to an absolute path.
|
||||
//! Returns nullopt if path cannot be converted to an absolute path
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path);
|
||||
|
||||
@@ -51,6 +51,8 @@ set(FILES
|
||||
Component/EntityId.h
|
||||
Component/EntityIdSerializer.cpp
|
||||
Component/EntityIdSerializer.h
|
||||
Component/EntitySerializer.cpp
|
||||
Component/EntitySerializer.h
|
||||
Component/EntityUtils.cpp
|
||||
Component/EntityUtils.h
|
||||
Component/NamedEntityId.cpp
|
||||
@@ -73,9 +75,6 @@ set(FILES
|
||||
Console/ILogger.h
|
||||
Console/LoggerSystemComponent.cpp
|
||||
Console/LoggerSystemComponent.h
|
||||
Prefab/PrefabAsset.h
|
||||
Prefab/PrefabBus.h
|
||||
Prefab/PrefabComponent.h
|
||||
Slice/SliceAsset.cpp
|
||||
Slice/SliceAsset.h
|
||||
Slice/SliceAssetHandler.cpp
|
||||
@@ -96,10 +95,13 @@ set(FILES
|
||||
Debug/AssetTracking.h
|
||||
Debug/AssetTrackingTypesImpl.h
|
||||
Debug/AssetTrackingTypes.h
|
||||
Debug/LocalFileEventLogger.h
|
||||
Debug/LocalFileEventLogger.cpp
|
||||
Debug/FrameProfiler.h
|
||||
Debug/FrameProfilerBus.h
|
||||
Debug/FrameProfilerComponent.cpp
|
||||
Debug/FrameProfilerComponent.h
|
||||
Debug/IEventLogger.h
|
||||
Debug/ProfileModuleInit.cpp
|
||||
Debug/ProfileModuleInit.h
|
||||
Debug/Profiler.cpp
|
||||
@@ -274,7 +276,7 @@ set(FILES
|
||||
Math/Internal/SimdMathCommon_neonDouble.inl
|
||||
Math/Internal/SimdMathCommon_neonQuad.inl
|
||||
Math/Internal/SimdMathCommon_simd.inl
|
||||
Math/Internal/SimdMathCommon_sse.inl
|
||||
Math/Internal/SimdMathCommon_sse.inl
|
||||
Math/Internal/VectorConversions.inl
|
||||
Math/Internal/VertexContainer.inl
|
||||
Math/InterpolationSample.h
|
||||
@@ -286,7 +288,7 @@ set(FILES
|
||||
Math/MathReflection.h
|
||||
Math/MathScriptHelpers.cpp
|
||||
Math/MathScriptHelpers.h
|
||||
Math/MathUtils.cpp
|
||||
Math/MathUtils.cpp
|
||||
Math/MathUtils.h
|
||||
Math/MathVectorSerializer.h
|
||||
Math/MathVectorSerializer.cpp
|
||||
|
||||
@@ -988,6 +988,8 @@ namespace AZStd
|
||||
return ++result;
|
||||
}
|
||||
|
||||
using std::binary_search;
|
||||
|
||||
// todo search_n
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ namespace AZStd
|
||||
: any(alloc)
|
||||
{
|
||||
static_assert(std::is_copy_constructible<decay_t<ValueType>>::value
|
||||
|| (std::is_rvalue_reference<ValueType>::value && std::is_move_constructible<decay_t<ValueType>>::value), "ValueType must be copy constructible or a movable rvalue ref.");
|
||||
|| (std::is_rvalue_reference<ValueType&&>::value && std::is_move_constructible<decay_t<ValueType>>::value), "ValueType must be copy constructible or a movable rvalue ref.");
|
||||
|
||||
// Initialize typeinfo from the type given
|
||||
m_typeInfo = create_template_type_info<decay_t<ValueType>>();
|
||||
|
||||
@@ -196,6 +196,54 @@ namespace AZStd
|
||||
return m_tree.node_handle_insert_unique(hint, AZStd::move(nodeHandle));
|
||||
}
|
||||
|
||||
//! C++17 insert_or_assign function assigns the element to the mapped_type if the key exist in the container
|
||||
//! Otherwise a new value is inserted into the container
|
||||
template <typename M>
|
||||
pair<iterator, bool> insert_or_assign(const key_type& key, M&& value)
|
||||
{
|
||||
return m_tree.insert_or_assign_unique(key, AZStd::forward<M>(value));
|
||||
}
|
||||
template <typename M>
|
||||
pair<iterator, bool> insert_or_assign(key_type&& key, M&& value)
|
||||
{
|
||||
return m_tree.insert_or_assign_unique(AZStd::move(key), AZStd::forward<M>(value));
|
||||
}
|
||||
template <typename M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& key, M&& value)
|
||||
{
|
||||
return m_tree.insert_or_assign_unique(hint, key, AZStd::forward<M>(value));
|
||||
}
|
||||
template <typename M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& key, M&& value)
|
||||
{
|
||||
return m_tree.insert_or_assign_unique(hint, AZStd::move(key), AZStd::forward<M>(value));
|
||||
}
|
||||
|
||||
//! C++17 try_emplace function that does nothing to the arguments if the key exist in the container,
|
||||
//! otherwise it constructs the value type as if invoking
|
||||
//! value_type(AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<KeyType>(key)),
|
||||
//! AZStd::forward_as_tuple(AZStd::forward<Args>(args)...))
|
||||
template <typename... Args>
|
||||
pair<iterator, bool> try_emplace(const key_type& key, Args&&... arguments)
|
||||
{
|
||||
return m_tree.try_emplace_unique(key, AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
pair<iterator, bool> try_emplace(key_type&& key, Args&&... arguments)
|
||||
{
|
||||
return m_tree.try_emplace_unique(AZStd::move(key), AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& key, Args&&... arguments)
|
||||
{
|
||||
return m_tree.try_emplace_unique(hint, key, AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& key, Args&&... arguments)
|
||||
{
|
||||
return m_tree.try_emplace_unique(hint, AZStd::move(key), AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
|
||||
node_type extract(const key_type& key)
|
||||
{
|
||||
return m_tree.template node_handle_extract<node_type>(key);
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace AZStd
|
||||
void push(value_type&& value) { m_container.push_back(AZStd::move(value)); AZStd::push_heap(m_container.begin(), m_container.end(), m_comp); }
|
||||
template<class Args>
|
||||
void emplace(Args&& args) { m_container.emplace_back(AZStd::forward<Args>(args)); AZStd::push_heap(m_container.begin(), m_container.end(), m_comp); }
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_continer); AZStd::swap(m_comp, rhs.m_comp); }
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_container); AZStd::swap(m_comp, rhs.m_comp); }
|
||||
|
||||
AZ_FORCE_INLINE Container& get_container() { return m_container; }
|
||||
AZ_FORCE_INLINE const Container& get_container() const { return m_container; }
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#ifndef AZSTD_RED_BLACK_TREE_H
|
||||
#define AZSTD_RED_BLACK_TREE_H
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/allocator.h>
|
||||
#include <AzCore/std/allocator_traits.h>
|
||||
@@ -18,6 +17,7 @@
|
||||
#include <AzCore/std/createdestroy.h>
|
||||
#include <AzCore/std/functional_basic.h>
|
||||
#include <AzCore/std/typetraits/alignment_of.h>
|
||||
#include <AzCore/std/tuple.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
|
||||
// Allow us to save up to 25% of the node overhead. And yes the code is faster too.
|
||||
@@ -730,7 +730,7 @@ namespace AZStd
|
||||
}
|
||||
return result;
|
||||
}
|
||||
iterator insert_equal(iterator insertPos, const value_type& value)
|
||||
iterator insert_equal(const iterator insertPos, const value_type& value)
|
||||
{
|
||||
return insert_equal_node(insertPos, create_node(value));
|
||||
}
|
||||
@@ -752,6 +752,18 @@ namespace AZStd
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
AZStd::pair<iterator, bool> try_emplace_unique(ComparableToKey&& key, Args&&... arguments);
|
||||
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
iterator try_emplace_unique(const_iterator hint, ComparableToKey&& key, Args&&... arguments);
|
||||
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
AZStd::pair<iterator, bool> insert_or_assign_unique(ComparableToKey&& key, MappedType&& value);
|
||||
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
iterator insert_or_assign_unique(const_iterator hint, ComparableToKey&& key, MappedType&& value);
|
||||
|
||||
//! Returns an insert_return_type with the members initialized as follows: if nodeHandle is empty, inserted is false, position is end(), and node is empty.
|
||||
//! Otherwise if the insertion took place, inserted is true, position points to the inserted element, and node is empty.
|
||||
//! If the insertion failed, inserted is false, node has the previous value of nodeHandle, and position points to an element with a key equivalent to nodeHandle.key().
|
||||
@@ -2077,8 +2089,71 @@ namespace AZStd
|
||||
return NodeHandle{ nodeToExtract, get_allocator() };
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
auto rbtree<Traits>::try_emplace_unique(ComparableToKey&& key, Args&&... arguments) -> AZStd::pair<iterator, bool>
|
||||
{
|
||||
// Check if the key has a corresponding node in the container
|
||||
iterator insertIter = lower_bound(key);
|
||||
if (insertIter.m_node != &m_head && !m_keyEq(key, insertIter->first))
|
||||
{
|
||||
return { insertIter, false };
|
||||
}
|
||||
|
||||
#endif // AZSTD_RED_BLACK_TREE_H
|
||||
#pragma once
|
||||
base_node_ptr_type newNode = create_node(AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<Args>(arguments)...));
|
||||
return { insert_unique_node(insertIter, newNode), true };
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
auto rbtree<Traits>::try_emplace_unique(const_iterator hint, ComparableToKey&& key, Args&&... arguments) -> iterator
|
||||
{
|
||||
// Check if the key has a corresponding node in the container
|
||||
iterator insertIter = lower_bound(key);
|
||||
if (insertIter.m_node != &m_head && !m_keyEq(key, insertIter->first))
|
||||
{
|
||||
return insertIter;
|
||||
}
|
||||
|
||||
base_node_ptr_type newNode = create_node(AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<Args>(arguments)...));
|
||||
return insert_unique_node(hint, newNode);
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
auto rbtree<Traits>::insert_or_assign_unique(ComparableToKey&& key, MappedType&& value) -> AZStd::pair<iterator, bool>
|
||||
{
|
||||
// Check if the key has a corresponding node in the container
|
||||
iterator insertIter = lower_bound(key);
|
||||
if (insertIter.m_node != &m_head && !m_keyEq(key, insertIter->first))
|
||||
{
|
||||
// Update the mapped element if the key has been found
|
||||
insertIter->second = AZStd::forward<MappedType>(value);
|
||||
return { insertIter, false };
|
||||
}
|
||||
|
||||
base_node_ptr_type newNode = create_node(AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<MappedType>(value)));
|
||||
return { insert_unique_node(insertIter, newNode), true };
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
auto rbtree<Traits>::insert_or_assign_unique(const_iterator hint, ComparableToKey&& key, MappedType&& value) -> iterator
|
||||
{
|
||||
// Check if the key has a corresponding node in the container
|
||||
iterator insertIter = lower_bound(key);
|
||||
if (insertIter.m_node != &m_head && !m_keyEq(key, insertIter->first))
|
||||
{
|
||||
// Update the mapped element if the key has been found
|
||||
insertIter->second = AZStd::forward<MappedType>(value);
|
||||
return insertIter;
|
||||
}
|
||||
|
||||
base_node_ptr_type newNode = create_node(AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<MappedType>(value)));
|
||||
return insert_unique_node(hint, newNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +236,54 @@ namespace AZStd
|
||||
return base_type::node_handle_insert(hint, AZStd::move(nodeHandle));
|
||||
}
|
||||
|
||||
//! C++17 insert_or_assign function assigns the element to the mapped_type if the key exist in the container
|
||||
//! Otherwise a new value is inserted into the container
|
||||
template <typename M>
|
||||
pair_iter_bool insert_or_assign(const key_type& key, M&& value)
|
||||
{
|
||||
return base_type::insert_or_assign_transparent(key, AZStd::forward<M>(value));
|
||||
}
|
||||
template <typename M>
|
||||
pair_iter_bool insert_or_assign(key_type&& key, M&& value)
|
||||
{
|
||||
return base_type::insert_or_assign_transparent(AZStd::move(key), AZStd::forward<M>(value));
|
||||
}
|
||||
template <typename M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& key, M&& value)
|
||||
{
|
||||
return base_type::insert_or_assign_transparent(hint, key, AZStd::forward<M>(value));
|
||||
}
|
||||
template <typename M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& key, M&& value)
|
||||
{
|
||||
return base_type::insert_or_assign_transparent(hint, AZStd::move(key), AZStd::forward<M>(value));
|
||||
}
|
||||
|
||||
//! C++17 try_emplace function that does nothing to the arguments if the key exist in the container,
|
||||
//! otherwise it constructs the value type as if invoking
|
||||
//! value_type(AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<KeyType>(key)),
|
||||
//! AZStd::forward_as_tuple(AZStd::forward<Args>(args)...))
|
||||
template <typename... Args>
|
||||
pair_iter_bool try_emplace(const key_type& key, Args&&... arguments)
|
||||
{
|
||||
return base_type::try_emplace_transparent(key, AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
pair_iter_bool try_emplace(key_type&& key, Args&&... arguments)
|
||||
{
|
||||
return base_type::try_emplace_transparent(AZStd::move(key), AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& key, Args&&... arguments)
|
||||
{
|
||||
return base_type::try_emplace_transparent(hint, key, AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& key, Args&&... arguments)
|
||||
{
|
||||
return base_type::try_emplace_transparent(hint, AZStd::move(key), AZStd::forward<Args>(arguments)...);
|
||||
}
|
||||
|
||||
node_type extract(const key_type& key)
|
||||
{
|
||||
return base_type::template node_handle_extract<node_type>(key);
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#ifndef AZSTD_HASH_TABLE_H
|
||||
#define AZSTD_HASH_TABLE_H 1
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Math/MathUtils.h>
|
||||
#include <AzCore/std/containers/node_handle.h>
|
||||
@@ -20,6 +19,7 @@
|
||||
#include <AzCore/std/containers/fixed_list.h>
|
||||
|
||||
#include <AzCore/std/hash.h>
|
||||
#include <AzCore/std/tuple.h>
|
||||
#include <AzCore/std/utils.h>
|
||||
#include <AzCore/std/functional_basic.h>
|
||||
#include <AzCore/std/allocator_ref.h>
|
||||
@@ -685,14 +685,6 @@ namespace AZStd
|
||||
}
|
||||
}
|
||||
|
||||
//AZ_FORCE_INLINE void erase(const key_type *first, const key_type *last)
|
||||
//{
|
||||
// // erase all that match array of keys [first, last)
|
||||
// //_DEBUG_RANGE(first, last);
|
||||
// for (; first != last; ++first)
|
||||
// erase(*first);
|
||||
//}
|
||||
|
||||
AZ_FORCE_INLINE void clear()
|
||||
{
|
||||
// erase all
|
||||
@@ -959,28 +951,6 @@ namespace AZStd
|
||||
protected:
|
||||
AZ_FORCE_INLINE size_type bucket_from_hash(const size_type key) const { return key % m_data.get_num_buckets(); }
|
||||
|
||||
// FOR SLIST VERSION
|
||||
/*AZ_FORCE_INLINE iterator find_previous(list_type& list, vector_value_type* buckets, iterator& iter, size_type bucketIndex)
|
||||
{
|
||||
if( iter != list.begin() )
|
||||
{
|
||||
while(bucketIndex>0)
|
||||
{
|
||||
--bucketIndex;
|
||||
const vector_value_type& bucket = buckets[bucketIndex];
|
||||
if(bucket.first > 0)
|
||||
{
|
||||
iterator iter = bucket.second;
|
||||
for(size_type i = 0; i < (bucket.first-1); ++i, ++iter);
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list.before_begin();
|
||||
}*/
|
||||
|
||||
|
||||
void copy(const this_type& rhs)
|
||||
{
|
||||
// copy entire hash table
|
||||
@@ -1105,6 +1075,18 @@ namespace AZStd
|
||||
return (pair_iter_bool(insertPos, true)); // return iterator for new element
|
||||
}
|
||||
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
pair_iter_bool try_emplace_transparent(ComparableToKey&& key, Args&&... arguments);
|
||||
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
iterator try_emplace_transparent(const_iterator hint, ComparableToKey&& key, Args&&... arguments);
|
||||
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
pair_iter_bool insert_or_assign_transparent(ComparableToKey&& key, MappedType&& value);
|
||||
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
iterator insert_or_assign_transparent(const_iterator hint, ComparableToKey&& key, MappedType&& value);
|
||||
|
||||
storage_type m_data;
|
||||
key_eq m_keyEqual;
|
||||
hasher m_hasher;
|
||||
@@ -1202,7 +1184,97 @@ namespace AZStd
|
||||
|
||||
return m_data.m_list.unlink(removePos);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // AZSTD_HASH_TABLE_H
|
||||
#pragma once
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
inline auto hash_table<Traits>::try_emplace_transparent(ComparableToKey&& key, Args&&... arguments) -> pair_iter_bool
|
||||
{
|
||||
// Check if the key has a corresponding node in the container
|
||||
if (iterator findIter = find(key); findIter != m_data.m_list.end())
|
||||
{
|
||||
return { findIter, false };
|
||||
}
|
||||
|
||||
size_type bucketIndex = bucket_from_hash(m_hasher(key));
|
||||
auto& [numElements, bucketFrontIter] = m_data.buckets()[bucketIndex];
|
||||
|
||||
// advance to the end of the bucket and piecewise construct the arguments at before that iterator
|
||||
iterator insertIter = AZStd::next(bucketFrontIter, numElements);
|
||||
|
||||
if (numElements == 0)
|
||||
{
|
||||
// No elements in the bucket
|
||||
// emplace the element at the beginning of the list and update the bucket front iterator to point to it
|
||||
insertIter = m_data.m_list.emplace(m_data.m_list.begin(), AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<Args>(arguments)...));
|
||||
bucketFrontIter = insertIter;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The returned iterator from list::emplace is pointing at the newly inserted element
|
||||
insertIter = m_data.m_list.emplace(insertIter, AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<Args>(arguments)...));
|
||||
}
|
||||
|
||||
// Update the number of elements in the bucket using the numElements reference variable
|
||||
++numElements;
|
||||
|
||||
m_data.rehash_if_needed(this);
|
||||
|
||||
return { insertIter, true };
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename... Args>
|
||||
inline auto hash_table<Traits>::try_emplace_transparent(const_iterator, ComparableToKey&& key, Args&&... arguments) -> iterator
|
||||
{
|
||||
return try_emplace_transparent(AZStd::forward<ComparableToKey>(key), AZStd::forward<Args>(arguments)...).first;
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
inline auto hash_table<Traits>::insert_or_assign_transparent(ComparableToKey&& key, MappedType&& value) -> pair_iter_bool
|
||||
{
|
||||
// Check if the key has a corresponding node in the container
|
||||
if (iterator findIter = find(key); findIter != m_data.m_list.end())
|
||||
{
|
||||
// Update the mapped element if the key has been found
|
||||
findIter->second = AZStd::forward<MappedType>(value);
|
||||
return { findIter, false };
|
||||
}
|
||||
|
||||
size_type bucketIndex = bucket_from_hash(m_hasher(key));
|
||||
auto& [numElements, bucketFrontIter] = m_data.buckets()[bucketIndex];
|
||||
|
||||
// advance to the end of the bucket and piecewise construct the arguments at before that iterator
|
||||
iterator insertIter = AZStd::next(bucketFrontIter, numElements);
|
||||
|
||||
if (numElements == 0)
|
||||
{
|
||||
// No elements in the bucket
|
||||
// emplace the element at the beginning of the list and update the bucket front iterator to point to it
|
||||
insertIter = m_data.m_list.emplace(m_data.m_list.begin(), AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<MappedType>(value)));
|
||||
bucketFrontIter = insertIter;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The returned iterator from list::emplace is pointing at the newly inserted element
|
||||
insertIter = m_data.m_list.emplace(insertIter, AZStd::piecewise_construct, AZStd::forward_as_tuple(AZStd::forward<ComparableToKey>(key)),
|
||||
AZStd::forward_as_tuple(AZStd::forward<MappedType>(value)));
|
||||
}
|
||||
// Update the number of elements in the bucket using the numElements reference variable
|
||||
++numElements;
|
||||
|
||||
m_data.rehash_if_needed(this);
|
||||
|
||||
return { insertIter, true };
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
template <typename ComparableToKey, typename MappedType>
|
||||
inline auto hash_table<Traits>::insert_or_assign_transparent(const_iterator, ComparableToKey&& key, MappedType&& value) -> iterator
|
||||
{
|
||||
return insert_or_assign_transparent(AZStd::forward<ComparableToKey>(key), AZStd::forward<MappedType>(value)).first;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,4 +39,32 @@ namespace AZStd
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
// ref: https://en.cppreference.com/w/cpp/algorithm/inner_product
|
||||
template<class InputIt1, class InputIt2, class T>
|
||||
constexpr T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init)
|
||||
{
|
||||
while (first1 != last1)
|
||||
{
|
||||
init = AZStd::move(init) + *first1 * *first2;
|
||||
++first1;
|
||||
++first2;
|
||||
}
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
// ref: https://en.cppreference.com/w/cpp/algorithm/inner_product
|
||||
template<class InputIt1, class InputIt2, class T, class BinaryOperation1, class BinaryOperation2>
|
||||
constexpr T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init, BinaryOperation1 op1, BinaryOperation2 op2)
|
||||
{
|
||||
while (first1 != last1)
|
||||
{
|
||||
init = op1(AZStd::move(init), op2(*first1, *first2));
|
||||
++first1;
|
||||
++first2;
|
||||
}
|
||||
|
||||
return init;
|
||||
}
|
||||
} // namespace AZStd
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
|
||||
#include <AzCore/Android/JNI/Internal/ClassName.h>
|
||||
#include <AzCore/Android/Utils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Utils
|
||||
@@ -55,6 +58,12 @@ namespace AZ
|
||||
return appRoot ? AZStd::make_optional<AZStd::fixed_string<MaxPathLength>>(appRoot) : AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
|
||||
{
|
||||
const char* writeStorage = AZ::Android::Utils::GetAppPublicStoragePath();
|
||||
return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
{
|
||||
AZStd::fixed_string<MaxPathLength> absolutePath;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
|
||||
@@ -169,19 +169,20 @@ namespace Platform
|
||||
{
|
||||
if (s.st_mode & S_IWUSR)
|
||||
{
|
||||
// file is already writeable
|
||||
return true;
|
||||
}
|
||||
return chmod(sourceFileName, permissions | S_IWUSR) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s.st_mode & S_IRWXG)
|
||||
if (s.st_mode & S_IWUSR)
|
||||
{
|
||||
return chmod(sourceFileName, permissions & ~(S_IWUSR)) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// file is already writeable
|
||||
// file is already read-only
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
@@ -51,6 +52,11 @@ namespace AZ
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZStd::fixed_string<MaxPathLength>> ConvertToAbsolutePath(AZStd::string_view path)
|
||||
{
|
||||
AZStd::fixed_string<MaxPathLength> absolutePath;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/optional.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
|
||||
@@ -48,5 +49,10 @@ namespace AZ
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,9 @@ namespace AZ::Utils
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,29 @@ namespace AZ::Utils
|
||||
const char* pathToResources = [[[NSBundle mainBundle] resourcePath] UTF8String];
|
||||
return AZStd::fixed_string<MaxPathLength>::format("%s/assets", pathToResources);
|
||||
}
|
||||
|
||||
AZStd::optional<AZ::IO::FixedMaxPathString> GetDevWriteStoragePath()
|
||||
{
|
||||
NSArray* appSupportDirectoryPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
if ([appSupportDirectoryPaths count] == 0)
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
NSString* appSupportDir = static_cast<NSString*>([appSupportDirectoryPaths objectAtIndex:0]);
|
||||
if (!appSupportDir)
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
const char* src = [appSupportDir UTF8String];
|
||||
const size_t srcLen = strlen(src);
|
||||
if (srcLen > MaxPathLength - 1)
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
return AZStd::make_optional<AZ::IO::FixedMaxPathString>(src);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1600,6 +1600,148 @@ namespace UnitTest
|
||||
EXPECT_TRUE(test_adl.empty());
|
||||
}
|
||||
|
||||
TEST_F(HashedContainers, UnorderedMapTryEmplace_DoesNotConstruct_OnExistingKey)
|
||||
{
|
||||
static int s_tryEmplaceConstructorCallCount;
|
||||
s_tryEmplaceConstructorCallCount = 0;
|
||||
struct TryEmplaceConstructorCalls
|
||||
{
|
||||
TryEmplaceConstructorCalls()
|
||||
{
|
||||
++s_tryEmplaceConstructorCallCount;
|
||||
}
|
||||
TryEmplaceConstructorCalls(int value)
|
||||
: m_value{ value }
|
||||
{
|
||||
++s_tryEmplaceConstructorCallCount;
|
||||
}
|
||||
TryEmplaceConstructorCalls(const TryEmplaceConstructorCalls&)
|
||||
{
|
||||
++s_tryEmplaceConstructorCallCount;
|
||||
}
|
||||
|
||||
int m_value{};
|
||||
};
|
||||
using TryEmplaceTestMap = AZStd::unordered_map<int, TryEmplaceConstructorCalls>;
|
||||
TryEmplaceTestMap testContainer;
|
||||
|
||||
// try_emplace move key
|
||||
AZStd::pair<TryEmplaceTestMap::iterator, bool> emplacePairIter = testContainer.try_emplace(1, 5);
|
||||
EXPECT_EQ(1, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_TRUE(emplacePairIter.second);
|
||||
EXPECT_EQ(5, emplacePairIter.first->second.m_value);
|
||||
|
||||
// try_emplace copy key
|
||||
int testKey = 3;
|
||||
emplacePairIter = testContainer.try_emplace(testKey, 72);
|
||||
EXPECT_EQ(2, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_TRUE(emplacePairIter.second);
|
||||
EXPECT_EQ(72, emplacePairIter.first->second.m_value);
|
||||
|
||||
// invoke try_emplace with hint and move key
|
||||
TryEmplaceTestMap::iterator emplaceIter = testContainer.try_emplace(testContainer.end(), 5, 4092);
|
||||
EXPECT_EQ(3, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_EQ(4092, emplaceIter->second.m_value);
|
||||
|
||||
// invoke try_emplace with hint and copy key
|
||||
testKey = 48;
|
||||
emplaceIter = testContainer.try_emplace(testContainer.end(), testKey, 824);
|
||||
EXPECT_EQ(4, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_EQ(824, emplaceIter->second.m_value);
|
||||
|
||||
// Since the key of '1' exist, nothing should be constructed
|
||||
emplacePairIter = testContainer.try_emplace(1, -6354);
|
||||
EXPECT_EQ(4, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_FALSE(emplacePairIter.second);
|
||||
EXPECT_EQ(5, emplacePairIter.first->second.m_value);
|
||||
}
|
||||
|
||||
TEST_F(HashedContainers, UnorderedMapTryEmplace_DoesNotMoveValue_OnExistingKey)
|
||||
{
|
||||
AZStd::unordered_map<int, AZStd::unique_ptr<int>> testMap;
|
||||
auto testPtr = AZStd::make_unique<int>(5);
|
||||
auto [emplaceIter, inserted] = testMap.try_emplace(1, AZStd::move(testPtr));
|
||||
EXPECT_TRUE(inserted);
|
||||
EXPECT_EQ(nullptr, testPtr);
|
||||
|
||||
testPtr = AZStd::make_unique<int>(7000);
|
||||
auto [emplaceIter2, inserted2] = testMap.try_emplace(1, AZStd::move(testPtr));
|
||||
EXPECT_FALSE(inserted2);
|
||||
ASSERT_NE(nullptr, testPtr);
|
||||
EXPECT_EQ(7000, *testPtr);
|
||||
}
|
||||
|
||||
TEST_F(HashedContainers, UnorderedMapInsertOrAssign_PerformsAssignment_OnExistingKey)
|
||||
{
|
||||
static int s_tryInsertOrAssignConstructorCalls;
|
||||
static int s_tryInsertOrAssignAssignmentCalls;
|
||||
s_tryInsertOrAssignConstructorCalls = 0;
|
||||
s_tryInsertOrAssignAssignmentCalls = 0;
|
||||
struct InsertOrAssignInitCalls
|
||||
{
|
||||
InsertOrAssignInitCalls()
|
||||
{
|
||||
++s_tryInsertOrAssignConstructorCalls;
|
||||
}
|
||||
InsertOrAssignInitCalls(int value)
|
||||
: m_value{ value }
|
||||
{
|
||||
++s_tryInsertOrAssignConstructorCalls;
|
||||
}
|
||||
InsertOrAssignInitCalls(const InsertOrAssignInitCalls& other)
|
||||
: m_value{ other.m_value }
|
||||
{
|
||||
++s_tryInsertOrAssignConstructorCalls;
|
||||
}
|
||||
|
||||
InsertOrAssignInitCalls& operator=(int value)
|
||||
{
|
||||
m_value = value;
|
||||
++s_tryInsertOrAssignAssignmentCalls;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int m_value{};
|
||||
};
|
||||
using InsertOrAssignTestMap = AZStd::unordered_map<int, InsertOrAssignInitCalls>;
|
||||
InsertOrAssignTestMap testContainer;
|
||||
|
||||
// insert_or_assign move key
|
||||
AZStd::pair<InsertOrAssignTestMap::iterator, bool> insertOrAssignPairIter = testContainer.insert_or_assign(1, 5);
|
||||
EXPECT_EQ(1, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_TRUE(insertOrAssignPairIter.second);
|
||||
EXPECT_EQ(5, insertOrAssignPairIter.first->second.m_value);
|
||||
|
||||
// insert_or_assign copy key
|
||||
int testKey = 3;
|
||||
insertOrAssignPairIter = testContainer.insert_or_assign(testKey, 72);
|
||||
EXPECT_EQ(2, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_TRUE(insertOrAssignPairIter.second);
|
||||
EXPECT_EQ(72, insertOrAssignPairIter.first->second.m_value);
|
||||
|
||||
// invoke insert_or_assign with hint and move key
|
||||
InsertOrAssignTestMap::iterator insertOrAssignIter = testContainer.insert_or_assign(testContainer.end(), 5, 4092);
|
||||
EXPECT_EQ(3, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_EQ(4092, insertOrAssignIter->second.m_value);
|
||||
|
||||
// invoke insert_or_assign with hint and copy key
|
||||
testKey = 48;
|
||||
insertOrAssignIter = testContainer.insert_or_assign(testContainer.end(), testKey, 824);
|
||||
EXPECT_EQ(4, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_EQ(824, insertOrAssignIter->second.m_value);
|
||||
|
||||
// Since the key of '1' exist, only an assignment should take place
|
||||
insertOrAssignPairIter = testContainer.insert_or_assign(1, -6354);
|
||||
EXPECT_EQ(4, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(1, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_FALSE(insertOrAssignPairIter.second);
|
||||
EXPECT_EQ(-6354, insertOrAssignPairIter.first->second.m_value);
|
||||
}
|
||||
|
||||
template <typename ContainerType>
|
||||
class HashedMapDifferentAllocatorFixture
|
||||
: public AllocatorsFixture
|
||||
|
||||
@@ -10,14 +10,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/numeric.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/numeric.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class AccumulateFixture
|
||||
: public AllocatorsTestFixture
|
||||
class AccumulateFixture : public AllocatorsTestFixture
|
||||
{
|
||||
};
|
||||
|
||||
@@ -25,31 +24,76 @@ namespace UnitTest
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
AZStd::vector<int> numbers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
AZStd::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
const int total = AZStd::accumulate(AZStd::cbegin(numbers), AZStd::cend(numbers), 0);
|
||||
EXPECT_THAT(total, Eq(55));
|
||||
}
|
||||
|
||||
TEST_F(AccumulateFixture, AccumulateWithBinaryOperator)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
|
||||
const AZStd::vector<int> numbers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
const AZStd::vector<int> evenNumbers =
|
||||
AZStd::accumulate(
|
||||
AZStd::cbegin(numbers), AZStd::cend(numbers), AZStd::vector<int>{},
|
||||
[](AZStd::vector<int> acc, const int number)
|
||||
const AZStd::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
const AZStd::vector<int> evenNumbers = AZStd::accumulate(
|
||||
AZStd::cbegin(numbers), AZStd::cend(numbers), AZStd::vector<int>{}, [](AZStd::vector<int> acc, const int number) {
|
||||
if (number % 2 == 0)
|
||||
{
|
||||
if (number % 2 == 0)
|
||||
{
|
||||
acc.push_back(number);
|
||||
}
|
||||
acc.push_back(number);
|
||||
}
|
||||
|
||||
return acc;
|
||||
});
|
||||
return acc;
|
||||
});
|
||||
|
||||
EXPECT_THAT(evenNumbers.size(), Eq(5));
|
||||
EXPECT_THAT(evenNumbers, ElementsAre(2, 4, 6, 8, 10));
|
||||
}
|
||||
|
||||
class InnerProductFixture : public AllocatorsTestFixture
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(InnerProductFixture, InnerProductWithoutBinaryOperator)
|
||||
{
|
||||
using ::testing::Eq;
|
||||
|
||||
AZStd::vector<int> numbers1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
AZStd::vector<int> numbers2{2, 4, 6, 8, 10, 12, 14, 16, 18, 20};
|
||||
|
||||
const int total = AZStd::inner_product(AZStd::cbegin(numbers1), AZStd::cend(numbers1), AZStd::cbegin(numbers2), 0);
|
||||
EXPECT_THAT(total, Eq(770));
|
||||
}
|
||||
|
||||
TEST_F(InnerProductFixture, InnerProductWithBinaryOperator)
|
||||
{
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
|
||||
const AZStd::vector<int> number_values{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
const AZStd::vector<AZStd::string> number_names{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"};
|
||||
|
||||
struct NumberLabel
|
||||
{
|
||||
int m_value;
|
||||
AZStd::string m_name;
|
||||
};
|
||||
|
||||
const AZStd::vector<NumberLabel> numberLabels = AZStd::inner_product(
|
||||
AZStd::cbegin(number_values), AZStd::cend(number_values), AZStd::cbegin(number_names), AZStd::vector<NumberLabel>{},
|
||||
[](AZStd::vector<NumberLabel> acc, const NumberLabel& numberLabel) {
|
||||
acc.push_back(numberLabel);
|
||||
return acc;
|
||||
},
|
||||
[](const int value, const AZStd::string& label) {
|
||||
return NumberLabel{value, label};
|
||||
});
|
||||
|
||||
EXPECT_THAT(numberLabels.size(), Eq(10));
|
||||
|
||||
for (size_t i = 0; i < numberLabels.size(); ++i)
|
||||
{
|
||||
EXPECT_THAT(numberLabels[i].m_value, Eq(number_values[i]));
|
||||
EXPECT_THAT(numberLabels[i].m_name, Eq(number_names[i]));
|
||||
}
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
@@ -1353,6 +1353,149 @@ namespace UnitTest
|
||||
EXPECT_EQ(1, uniqueMap.count(4));
|
||||
}
|
||||
|
||||
TEST_F(Tree_Map, MapTryEmplace_DoesNotConstruct_OnExistingKey)
|
||||
{
|
||||
static int s_tryEmplaceConstructorCallCount;
|
||||
s_tryEmplaceConstructorCallCount = 0;
|
||||
struct TryEmplaceConstructorCalls
|
||||
{
|
||||
TryEmplaceConstructorCalls()
|
||||
{
|
||||
++s_tryEmplaceConstructorCallCount;
|
||||
}
|
||||
TryEmplaceConstructorCalls(int value)
|
||||
: m_value{ value }
|
||||
{
|
||||
++s_tryEmplaceConstructorCallCount;
|
||||
}
|
||||
TryEmplaceConstructorCalls(const TryEmplaceConstructorCalls& other)
|
||||
: m_value{ other.m_value }
|
||||
{
|
||||
++s_tryEmplaceConstructorCallCount;
|
||||
}
|
||||
|
||||
int m_value{};
|
||||
};
|
||||
using TryEmplaceTestMap = AZStd::map<int, TryEmplaceConstructorCalls>;
|
||||
TryEmplaceTestMap testContainer;
|
||||
|
||||
// try_emplace move key
|
||||
AZStd::pair<TryEmplaceTestMap::iterator, bool> emplacePairIter = testContainer.try_emplace(1, 5);
|
||||
EXPECT_EQ(1, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_TRUE(emplacePairIter.second);
|
||||
EXPECT_EQ(5, emplacePairIter.first->second.m_value);
|
||||
|
||||
// try_emplace copy key
|
||||
int testKey = 3;
|
||||
emplacePairIter = testContainer.try_emplace(testKey, 72);
|
||||
EXPECT_EQ(2, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_TRUE(emplacePairIter.second);
|
||||
EXPECT_EQ(72, emplacePairIter.first->second.m_value);
|
||||
|
||||
// invoke try_emplace with hint and move key
|
||||
TryEmplaceTestMap::iterator emplaceIter = testContainer.try_emplace(testContainer.end(), 5, 4092);
|
||||
EXPECT_EQ(3, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_EQ(4092, emplaceIter->second.m_value);
|
||||
|
||||
// invoke try_emplace with hint and copy key
|
||||
testKey = 48;
|
||||
emplaceIter = testContainer.try_emplace(testContainer.end(), testKey, 824);
|
||||
EXPECT_EQ(4, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_EQ(824, emplaceIter->second.m_value);
|
||||
|
||||
// Since the key of '1' exist, nothing should be constructed
|
||||
emplacePairIter = testContainer.try_emplace(1, -6354);
|
||||
EXPECT_EQ(4, s_tryEmplaceConstructorCallCount);
|
||||
EXPECT_FALSE(emplacePairIter.second);
|
||||
EXPECT_EQ(5, emplacePairIter.first->second.m_value);
|
||||
}
|
||||
|
||||
TEST_F(Tree_Map, MapTryEmplace_DoesNotMoveValue_OnExistingKey)
|
||||
{
|
||||
AZStd::unordered_map<int, AZStd::unique_ptr<int>> testMap;
|
||||
auto testPtr = AZStd::make_unique<int>(5);
|
||||
auto [emplaceIter, inserted] = testMap.try_emplace(1, AZStd::move(testPtr));
|
||||
EXPECT_TRUE(inserted);
|
||||
EXPECT_EQ(nullptr, testPtr);
|
||||
|
||||
testPtr = AZStd::make_unique<int>(7000);
|
||||
auto [emplaceIter2, inserted2] = testMap.try_emplace(1, AZStd::move(testPtr));
|
||||
EXPECT_FALSE(inserted2);
|
||||
ASSERT_NE(nullptr, testPtr);
|
||||
EXPECT_EQ(7000, *testPtr);
|
||||
}
|
||||
|
||||
TEST_F(Tree_Map, MapInsertOrAssign_PerformsAssignment_OnExistingKey)
|
||||
{
|
||||
static int s_tryInsertOrAssignConstructorCalls;
|
||||
static int s_tryInsertOrAssignAssignmentCalls;
|
||||
s_tryInsertOrAssignConstructorCalls = 0;
|
||||
s_tryInsertOrAssignAssignmentCalls = 0;
|
||||
struct InsertOrAssignInitCalls
|
||||
{
|
||||
InsertOrAssignInitCalls()
|
||||
{
|
||||
++s_tryInsertOrAssignConstructorCalls;
|
||||
}
|
||||
InsertOrAssignInitCalls(int value)
|
||||
: m_value{ value }
|
||||
{
|
||||
++s_tryInsertOrAssignConstructorCalls;
|
||||
}
|
||||
InsertOrAssignInitCalls(const InsertOrAssignInitCalls& other)
|
||||
: m_value{ other.m_value }
|
||||
{
|
||||
++s_tryInsertOrAssignConstructorCalls;
|
||||
}
|
||||
|
||||
InsertOrAssignInitCalls& operator=(int value)
|
||||
{
|
||||
m_value = value;
|
||||
++s_tryInsertOrAssignAssignmentCalls;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int m_value{};
|
||||
};
|
||||
using InsertOrAssignTestMap = AZStd::map<int, InsertOrAssignInitCalls>;
|
||||
InsertOrAssignTestMap testContainer;
|
||||
|
||||
// insert_or_assign move key
|
||||
AZStd::pair<InsertOrAssignTestMap::iterator, bool> insertOrAssignPairIter = testContainer.insert_or_assign(1, 5);
|
||||
EXPECT_EQ(1, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_TRUE(insertOrAssignPairIter.second);
|
||||
EXPECT_EQ(5, insertOrAssignPairIter.first->second.m_value);
|
||||
|
||||
// insert_or_assign copy key
|
||||
int testKey = 3;
|
||||
insertOrAssignPairIter = testContainer.insert_or_assign(testKey, 72);
|
||||
EXPECT_EQ(2, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_TRUE(insertOrAssignPairIter.second);
|
||||
EXPECT_EQ(72, insertOrAssignPairIter.first->second.m_value);
|
||||
|
||||
// invoke insert_or_assign with hint and move key
|
||||
InsertOrAssignTestMap::iterator insertOrAssignIter = testContainer.insert_or_assign(testContainer.end(), 5, 4092);
|
||||
EXPECT_EQ(3, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_EQ(4092, insertOrAssignIter->second.m_value);
|
||||
|
||||
// invoke insert_or_assign with hint and copy key
|
||||
testKey = 48;
|
||||
insertOrAssignIter = testContainer.insert_or_assign(testContainer.end(), testKey, 824);
|
||||
EXPECT_EQ(4, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(0, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_EQ(824, insertOrAssignIter->second.m_value);
|
||||
|
||||
// Since the key of '1' exist, only an assignment should take place
|
||||
insertOrAssignPairIter = testContainer.insert_or_assign(1, -6354);
|
||||
EXPECT_EQ(4, s_tryInsertOrAssignConstructorCalls);
|
||||
EXPECT_EQ(1, s_tryInsertOrAssignAssignmentCalls);
|
||||
EXPECT_FALSE(insertOrAssignPairIter.second);
|
||||
EXPECT_EQ(-6354, insertOrAssignPairIter.first->second.m_value);
|
||||
}
|
||||
|
||||
template <typename ContainerType>
|
||||
class TreeMapDifferentAllocatorFixture
|
||||
: public AllocatorsFixture
|
||||
|
||||
@@ -646,7 +646,11 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
#if AZ_TRAIT_DISABLE_ASSET_JOB_PARALLEL_TESTS
|
||||
TEST_F(Parallel_Thread, DISABLED_Test)
|
||||
#else
|
||||
TEST_F(Parallel_Thread, Test)
|
||||
#endif // AZ_TRAIT_DISABLE_ASSET_JOB_PARALLEL_TESTS
|
||||
{
|
||||
run();
|
||||
}
|
||||
|
||||
@@ -835,11 +835,11 @@ namespace UnitTest
|
||||
m_testAssetManager->SetParallelDependentLoadingEnabled(true);
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
TEST_F(AssetJobsFloodTest, DISABLED_LoadTest_SameAsset_DifferentFilters)
|
||||
#else
|
||||
TEST_F(AssetJobsFloodTest, LoadTest_SameAsset_DifferentFilters)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
{
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
|
||||
|
||||
@@ -1188,11 +1188,11 @@ namespace UnitTest
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
|
||||
#else
|
||||
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
{
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
|
||||
// Setup has already created/destroyed assets
|
||||
@@ -1229,11 +1229,11 @@ namespace UnitTest
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
|
||||
#else
|
||||
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
{
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
|
||||
// Setup has already created/destroyed assets
|
||||
@@ -1268,11 +1268,11 @@ namespace UnitTest
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
|
||||
#else
|
||||
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
|
||||
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
|
||||
{
|
||||
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
|
||||
// Setup has already created/destroyed assets
|
||||
|
||||
@@ -808,6 +808,13 @@ namespace UnitTest
|
||||
EXPECT_NE(assets.find(MyAsset1Id), assets.end());
|
||||
|
||||
AssetManager::Instance().ResumeAssetRelease();
|
||||
|
||||
// Sleep to allow for the assets to release
|
||||
int retryCount = 100;
|
||||
while ((--retryCount>0) && assets.size() > 0)
|
||||
{
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
EXPECT_EQ(assets.size(), 0);
|
||||
}
|
||||
|
||||
@@ -517,6 +517,104 @@ namespace UnitTest
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(0);
|
||||
}
|
||||
|
||||
TEST_F(BehaviorContextTestFixture, MethodWhichReturnsAzEvent_WithNoAzBehaviorAzEventDescription_FailsValidation)
|
||||
{
|
||||
using TestAzEvent = AZ::Event<float>;
|
||||
auto TestMethodWhichReturnsAzEvent = [](TestAzEvent& testEvent) -> TestAzEvent&
|
||||
{
|
||||
return testEvent;
|
||||
};
|
||||
|
||||
UnitTest::TestRunner::Instance().StartAssertTests();
|
||||
// Test reflecting function which returns AZ::Event
|
||||
m_behaviorContext.Method("TestMethodWhichReturnsAzEvent", TestMethodWhichReturnsAzEvent);
|
||||
int numErrors = UnitTest::TestRunner::Instance().StopAssertTests();
|
||||
EXPECT_EQ(1, numErrors);
|
||||
}
|
||||
|
||||
TEST_F(BehaviorContextTestFixture, MethodWhichReturnsAzEvent_WithEmptyEventName_FailsValidation)
|
||||
{
|
||||
using TestAzEvent = AZ::Event<float>;
|
||||
auto TestMethodWhichReturnsAzEvent = [](TestAzEvent& testEvent) -> TestAzEvent&
|
||||
{
|
||||
return testEvent;
|
||||
};
|
||||
|
||||
// Test reflecting function which returns AZ::Event
|
||||
AZ::BehaviorAzEventDescription behaviorEventDesc;
|
||||
// m_eventName member is not set, validation should fail
|
||||
behaviorEventDesc.m_parameterNames.push_back("Scale");
|
||||
|
||||
UnitTest::TestRunner::Instance().StartAssertTests();
|
||||
m_behaviorContext.Method("TestMethodWhichReturnsAzEvent", TestMethodWhichReturnsAzEvent)
|
||||
->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move(behaviorEventDesc));
|
||||
int numErrors = UnitTest::TestRunner::Instance().StopAssertTests();
|
||||
EXPECT_EQ(1, numErrors);
|
||||
}
|
||||
|
||||
TEST_F(BehaviorContextTestFixture, MethodWhichReturnsAzEvent_WithParameterNameWhichIsEmpty_FailsValidation)
|
||||
{
|
||||
using TestAzEvent = AZ::Event<float>;
|
||||
auto TestMethodWhichReturnsAzEvent = [](TestAzEvent& testEvent) -> TestAzEvent&
|
||||
{
|
||||
return testEvent;
|
||||
};
|
||||
|
||||
// Test reflecting function which returns AZ::Event
|
||||
AZ::BehaviorAzEventDescription behaviorEventDesc;
|
||||
behaviorEventDesc.m_eventName = "TestAzEvent";
|
||||
behaviorEventDesc.m_parameterNames.push_back(""); // Parameter name is empty, validation should fail
|
||||
|
||||
UnitTest::TestRunner::Instance().StartAssertTests();
|
||||
m_behaviorContext.Method("TestMethodWhichReturnsAzEvent", TestMethodWhichReturnsAzEvent)
|
||||
->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move(behaviorEventDesc));
|
||||
int numErrors = UnitTest::TestRunner::Instance().StopAssertTests();
|
||||
EXPECT_EQ(1, numErrors);
|
||||
}
|
||||
|
||||
TEST_F(BehaviorContextTestFixture, MethodWhichReturnsAzEvent_WithMismatchNumberOfParameters_FailsValidation)
|
||||
{
|
||||
using TestAzEvent = AZ::Event<float>;
|
||||
auto TestMethodWhichReturnsAzEvent = [](TestAzEvent& testEvent) -> TestAzEvent&
|
||||
{
|
||||
return testEvent;
|
||||
};
|
||||
|
||||
// Test reflecting function which returns AZ::Event
|
||||
AZ::BehaviorAzEventDescription behaviorEventDesc;
|
||||
behaviorEventDesc.m_eventName = "TestAzEvent";
|
||||
// The AZ Event accepts one parameters.
|
||||
// Two parameter names are being added here
|
||||
behaviorEventDesc.m_parameterNames.push_back("Scale");
|
||||
behaviorEventDesc.m_parameterNames.push_back("Size");
|
||||
|
||||
UnitTest::TestRunner::Instance().StartAssertTests();
|
||||
m_behaviorContext.Method("TestMethodWhichReturnsAzEvent", TestMethodWhichReturnsAzEvent)
|
||||
->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move(behaviorEventDesc));
|
||||
int numErrors = UnitTest::TestRunner::Instance().StopAssertTests();
|
||||
EXPECT_LE(1, numErrors);
|
||||
}
|
||||
|
||||
TEST_F(BehaviorContextTestFixture, MethodWhichReturnsAzEvent_WithCompleteAzBehaviorAzEventDescriptionription_PassesValidation)
|
||||
{
|
||||
using TestAzEvent = AZ::Event<float>;
|
||||
auto TestMethodWhichReturnsAzEvent = [](TestAzEvent& testEvent) -> TestAzEvent&
|
||||
{
|
||||
return testEvent;
|
||||
};
|
||||
|
||||
// Test reflecting function which returns AZ::Event
|
||||
AZ::BehaviorAzEventDescription behaviorEventDesc;
|
||||
behaviorEventDesc.m_eventName = "TestAzEvent";
|
||||
behaviorEventDesc.m_parameterNames.push_back("Scale");
|
||||
|
||||
UnitTest::TestRunner::Instance().StartAssertTests();
|
||||
m_behaviorContext.Method("TestMethodWhichReturnsAzEvent", TestMethodWhichReturnsAzEvent)
|
||||
->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move(behaviorEventDesc));
|
||||
int numErrors = UnitTest::TestRunner::Instance().StopAssertTests();
|
||||
EXPECT_EQ(0, numErrors);
|
||||
}
|
||||
|
||||
class ClassWithEnumClass
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -1336,7 +1336,11 @@ namespace UnitTest
|
||||
size_t m_numThreads;
|
||||
};
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_FRAMEPROFILER_TEST
|
||||
TEST_F(FrameProfilerComponentTest, DISABLED_Test)
|
||||
#else
|
||||
TEST_F(FrameProfilerComponentTest, Test)
|
||||
#endif
|
||||
{
|
||||
run();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* 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 <limits>
|
||||
#include <AzCore/Debug/LocalFileEventLogger.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
namespace AZ::Debug
|
||||
{
|
||||
class LocalFileEventLoggerTest
|
||||
: public UnitTest::AllocatorsFixture
|
||||
{
|
||||
public:
|
||||
inline static constexpr EventNameHash MessageId = EventNameHash("Message");
|
||||
inline static constexpr const char* LogFileName = "TestLog.azel";
|
||||
};
|
||||
|
||||
TEST_F(LocalFileEventLoggerTest, RecordEvent_SingleString_WrittenToLog)
|
||||
{
|
||||
constexpr const char* message = "Hello world";
|
||||
|
||||
LocalFileEventLogger realLogger;
|
||||
|
||||
AZ::Test::ScopedAutoTempDirectory tempDir;
|
||||
auto logFilePath = tempDir.Resolve(LogFileName);
|
||||
|
||||
auto logger = Interface<IEventLogger>::Get();
|
||||
ASSERT_NE(logger, nullptr);
|
||||
|
||||
realLogger.Start(logFilePath.c_str());
|
||||
logger->RecordStringEvent(MessageId, message);
|
||||
realLogger.Stop();
|
||||
|
||||
EventLogReader reader;
|
||||
ASSERT_TRUE(reader.ReadLog(logFilePath.c_str()));
|
||||
|
||||
EXPECT_EQ(reader.GetEventName(), PrologEventHash);
|
||||
|
||||
ASSERT_TRUE(reader.Next());
|
||||
EXPECT_EQ(reader.GetEventName(), MessageId);
|
||||
EXPECT_STREQ(reader.GetString().data(), message);
|
||||
|
||||
EXPECT_FALSE(reader.Next());
|
||||
}
|
||||
|
||||
TEST_F(LocalFileEventLoggerTest, RecordEvent_SeveralStrings_WrittenToLog)
|
||||
{
|
||||
constexpr const char* messages[] = {
|
||||
"Hello world",
|
||||
"And goodbye",
|
||||
"It has been a long and strange journey"
|
||||
};
|
||||
|
||||
LocalFileEventLogger realLogger;
|
||||
|
||||
AZ::Test::ScopedAutoTempDirectory tempDir;
|
||||
auto logFilePath = tempDir.Resolve(LogFileName);
|
||||
|
||||
auto logger = Interface<IEventLogger>::Get();
|
||||
ASSERT_NE(logger, nullptr);
|
||||
|
||||
realLogger.Start(logFilePath.c_str());
|
||||
for (auto message : messages)
|
||||
{
|
||||
logger->RecordStringEvent(MessageId, message);
|
||||
}
|
||||
realLogger.Stop();
|
||||
|
||||
EventLogReader reader;
|
||||
ASSERT_TRUE(reader.ReadLog(logFilePath.c_str()));
|
||||
|
||||
EXPECT_EQ(reader.GetEventName(), PrologEventHash);
|
||||
|
||||
for (auto message : messages)
|
||||
{
|
||||
ASSERT_TRUE(reader.Next());
|
||||
EXPECT_EQ(reader.GetEventName(), MessageId);
|
||||
EXPECT_STREQ(reader.GetString().data(), message);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(reader.Next());
|
||||
}
|
||||
|
||||
TEST_F(LocalFileEventLoggerTest, RecordEvent_StringsFromMultipleThreads_WrittenToLog)
|
||||
{
|
||||
constexpr const char* messages[] = {
|
||||
"Hello world",
|
||||
"And goodbye"
|
||||
};
|
||||
|
||||
constexpr size_t totalThreads = 4;
|
||||
|
||||
AZ::Test::ScopedAutoTempDirectory tempDir;
|
||||
LocalFileEventLogger realLogger;
|
||||
|
||||
auto logFilePath = tempDir.Resolve(LogFileName);
|
||||
|
||||
realLogger.Start(logFilePath.c_str());
|
||||
|
||||
AZStd::atomic_bool startLogging = false;
|
||||
|
||||
AZStd::thread threads[totalThreads];
|
||||
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
|
||||
{
|
||||
threads[threadIndex] = AZStd::thread([&startLogging, &messages]()
|
||||
{
|
||||
while (!startLogging)
|
||||
{
|
||||
AZStd::this_thread::yield();
|
||||
}
|
||||
|
||||
auto logger = Interface<IEventLogger>::Get();
|
||||
ASSERT_NE(logger, nullptr);
|
||||
|
||||
for (auto message : messages)
|
||||
{
|
||||
logger->RecordStringEvent(MessageId, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
startLogging = true;
|
||||
|
||||
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
|
||||
{
|
||||
threads[threadIndex].join();
|
||||
}
|
||||
|
||||
realLogger.Stop();
|
||||
|
||||
EventLogReader reader;
|
||||
ASSERT_TRUE(reader.ReadLog(logFilePath.c_str()));
|
||||
|
||||
uint64_t threadIds[totalThreads]{ 0 };
|
||||
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
|
||||
{
|
||||
EXPECT_EQ(reader.GetEventName(), PrologEventHash);
|
||||
threadIds[threadIndex] = reader.GetThreadId();
|
||||
|
||||
for (size_t otherThreadIndex = 0; otherThreadIndex < threadIndex; ++otherThreadIndex)
|
||||
{
|
||||
EXPECT_NE(threadIds[threadIndex], threadIds[otherThreadIndex]);
|
||||
}
|
||||
|
||||
for (auto message : messages)
|
||||
{
|
||||
ASSERT_TRUE(reader.Next());
|
||||
EXPECT_EQ(reader.GetEventName(), MessageId);
|
||||
EXPECT_STREQ(reader.GetString().data(), message);
|
||||
}
|
||||
|
||||
reader.Next();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LocalFileEventLoggerTest, RecordEvent_BufferGetsWrittenWhenFull_WrittenToLog)
|
||||
{
|
||||
constexpr EventNameHash largeBlockId = EventNameHash("Large block");
|
||||
constexpr size_t largeBlockSizeOffset = 14;
|
||||
constexpr size_t largeBlockSize = AZStd::numeric_limits<uint16_t>::max() - largeBlockSizeOffset;
|
||||
struct LargeBlock
|
||||
{
|
||||
char m_block[largeBlockSize];
|
||||
};
|
||||
|
||||
constexpr const char* message = "The message after the large block.";
|
||||
|
||||
LocalFileEventLogger realLogger;
|
||||
|
||||
AZ::Test::ScopedAutoTempDirectory tempDir;
|
||||
auto logFilePath = tempDir.Resolve(LogFileName);
|
||||
|
||||
auto logger = Interface<IEventLogger>::Get();
|
||||
ASSERT_NE(logger, nullptr);
|
||||
|
||||
realLogger.Start(logFilePath.c_str());
|
||||
|
||||
LargeBlock& block = logger->RecordEventBegin<LargeBlock>(largeBlockId);
|
||||
logger->RecordEventEnd();
|
||||
|
||||
logger->RecordStringEvent(MessageId, message);
|
||||
realLogger.Stop();
|
||||
|
||||
EventLogReader reader;
|
||||
ASSERT_TRUE(reader.ReadLog(logFilePath.c_str()));
|
||||
EXPECT_EQ(reader.GetEventName(), PrologEventHash);
|
||||
|
||||
ASSERT_TRUE(reader.Next());
|
||||
EXPECT_EQ(reader.GetEventName(), largeBlockId);
|
||||
EXPECT_EQ(reader.GetEventSize(), largeBlockSize);
|
||||
|
||||
ASSERT_TRUE(reader.Next());
|
||||
EXPECT_EQ(reader.GetEventName(), PrologEventHash); // Another prolog as a new cache block started.
|
||||
|
||||
ASSERT_TRUE(reader.Next());
|
||||
EXPECT_EQ(reader.GetEventName(), MessageId);
|
||||
EXPECT_STREQ(reader.GetString().data(), message);
|
||||
|
||||
EXPECT_FALSE(reader.Next());
|
||||
}
|
||||
|
||||
TEST_F(LocalFileEventLoggerTest, Flush_DuringMultipleRecords_FlushDoesNotDeadlock)
|
||||
{
|
||||
constexpr size_t totalThreads = 8;
|
||||
constexpr size_t recordsPerThreadCount = 2000;
|
||||
constexpr size_t recordsYieldCount = totalThreads * 1000;
|
||||
|
||||
constexpr const char* message = "This is a threaded message test.";
|
||||
|
||||
LocalFileEventLogger realLogger;
|
||||
|
||||
AZ::Test::ScopedAutoTempDirectory tempDir;
|
||||
auto logFilePath = tempDir.Resolve(LogFileName);
|
||||
|
||||
realLogger.Start(logFilePath.c_str());
|
||||
|
||||
AZStd::atomic_int totalRecordsWritten = 0;
|
||||
AZStd::atomic_bool startLogging = false;
|
||||
|
||||
AZStd::thread threads[totalThreads];
|
||||
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
|
||||
{
|
||||
threads[threadIndex] = AZStd::thread([&startLogging, &totalRecordsWritten, &message, recordsPerThreadCount]()
|
||||
{
|
||||
while (!startLogging)
|
||||
{
|
||||
AZStd::this_thread::yield();
|
||||
}
|
||||
|
||||
auto logger = Interface<IEventLogger>::Get();
|
||||
ASSERT_NE(logger, nullptr);
|
||||
|
||||
for (size_t recordCount = 0; recordCount < recordsPerThreadCount; ++recordCount)
|
||||
{
|
||||
logger->RecordStringEvent(MessageId, message);
|
||||
++totalRecordsWritten;
|
||||
}
|
||||
});
|
||||
}
|
||||
startLogging = true;
|
||||
|
||||
while (totalRecordsWritten < recordsYieldCount)
|
||||
{
|
||||
AZStd::this_thread::yield();
|
||||
}
|
||||
|
||||
realLogger.Flush();
|
||||
|
||||
for (size_t threadIndex = 0; threadIndex < totalThreads; ++threadIndex)
|
||||
{
|
||||
threads[threadIndex].join();
|
||||
}
|
||||
|
||||
realLogger.Stop();
|
||||
}
|
||||
} // namespace AZ::Debug
|
||||
@@ -496,6 +496,67 @@ namespace SettingsRegistryScriptUtilsTests
|
||||
AZStd::visit(ExpectedValueVisitor, testParam.m_expectedValue);
|
||||
}
|
||||
|
||||
TEST_P(SettingsRegistryBehaviorContextParamFixture, GetNotifyEvent_AllowsRegistrationOfAzEventHandler_Succeeds)
|
||||
{
|
||||
auto&& testParam = GetParam();
|
||||
|
||||
bool updateNotifySent{};
|
||||
|
||||
// Set the expected value within the SettingsRegistry
|
||||
auto ExpectedValueVisitor = [this, jsonPath = testParam.m_jsonPointerPath, setMethodName = testParam.m_setMethodName,
|
||||
&updateNotifySent](auto&& value)
|
||||
{
|
||||
using ValueType = AZStd::remove_cvref_t<decltype(value)>;
|
||||
const auto classIter = m_behaviorContext->m_classes.find(SettingsRegistryScriptClassName);
|
||||
ASSERT_NE(m_behaviorContext->m_classes.end(), classIter);
|
||||
AZ::BehaviorClass* settingsRegistryInterfaceClass = classIter->second;
|
||||
ASSERT_NE(nullptr, settingsRegistryInterfaceClass);
|
||||
// Lookup the SettingsRegistry Proxy GetNotifyEvent
|
||||
auto foundIt = settingsRegistryInterfaceClass->m_methods.find("GetNotifyEvent");
|
||||
ASSERT_NE(settingsRegistryInterfaceClass->m_methods.end(), foundIt);
|
||||
// Create local settings registry proxy object
|
||||
AZ::SettingsRegistryScriptUtils::Internal::SettingsRegistryScriptProxy settingsRegistryObject(m_registry.get());
|
||||
|
||||
// Register a notification call back
|
||||
AZ::SettingsRegistryScriptUtils::Internal::SettingsRegistryScriptProxy::ScriptNotifyEvent::Handler scriptNotifyHandler(
|
||||
[&updateNotifySent, jsonPath](AZStd::string_view path)
|
||||
{
|
||||
if (path == jsonPath)
|
||||
{
|
||||
updateNotifySent = true;
|
||||
}
|
||||
});
|
||||
AZ::SettingsRegistryScriptUtils::Internal::SettingsRegistryScriptProxy::ScriptNotifyEvent* scriptNotifyEvent{};
|
||||
EXPECT_TRUE(foundIt->second->InvokeResult(scriptNotifyEvent, &settingsRegistryObject));
|
||||
ASSERT_NE(nullptr, scriptNotifyEvent);
|
||||
|
||||
// connect the scriptNotifyHandler to the settings registry script proxy event
|
||||
scriptNotifyHandler.Connect(*scriptNotifyEvent);
|
||||
|
||||
// Find Reflected SettingsRegistryInterface Set* Method
|
||||
foundIt = settingsRegistryInterfaceClass->m_methods.find(setMethodName);
|
||||
ASSERT_NE(settingsRegistryInterfaceClass->m_methods.end(), foundIt);
|
||||
|
||||
// Invoke Set* method
|
||||
bool setResult{};
|
||||
EXPECT_TRUE(foundIt->second->InvokeResult(setResult, &settingsRegistryObject, jsonPath, value));
|
||||
EXPECT_TRUE(setResult);
|
||||
|
||||
// Check value set through the BehaviorContext against the Settings Registry instance
|
||||
|
||||
// SettingsRegistryInterface::Get() can store the string result in an AZStd::fixed_string/AZStd::string
|
||||
// So the AZStd::string_view is mapped to an AZStd::fixed_string for the purpose of calling Get()
|
||||
using GetValueType = AZStd::conditional_t<AZStd::is_same_v<AZStd::string_view, ValueType>,
|
||||
AZ::SettingsRegistryInterface::FixedValueString, ValueType>;
|
||||
GetValueType outputValue{};
|
||||
EXPECT_TRUE(m_registry->Get(outputValue, jsonPath));
|
||||
EXPECT_EQ(value, outputValue);
|
||||
};
|
||||
|
||||
AZStd::visit(ExpectedValueVisitor, testParam.m_expectedValue);
|
||||
EXPECT_TRUE(updateNotifySent);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
SettingsRegistryBehaviorContextGetFunctions,
|
||||
SettingsRegistryBehaviorContextParamFixture,
|
||||
|
||||
@@ -73,6 +73,7 @@ set(FILES
|
||||
UUIDTests.cpp
|
||||
XML.cpp
|
||||
Debug/AssetTracking.cpp
|
||||
Debug/LocalFileEventLoggerTests.cpp
|
||||
Debug/Trace.cpp
|
||||
Name/NameJsonSerializerTests.cpp
|
||||
Name/NameTests.cpp
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#include <AzFramework/Archive/ArchiveFileIO.h>
|
||||
#include <AzFramework/Script/ScriptRemoteDebugging.h>
|
||||
#include <AzFramework/Script/ScriptComponent.h>
|
||||
#include <AzFramework/Spawnable/SpawnableSystemComponent.h>
|
||||
#include <AzFramework/StreamingInstall/StreamingInstall.h>
|
||||
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
|
||||
#include <AzFramework/Viewport/CameraState.h>
|
||||
@@ -407,6 +408,7 @@ namespace AzFramework
|
||||
azrtti_typeid<AzFramework::InputSystemComponent>(),
|
||||
azrtti_typeid<AzFramework::DrillerNetworkAgentComponent>(),
|
||||
azrtti_typeid<AzFramework::StreamingInstall::StreamingInstallSystemComponent>(),
|
||||
azrtti_typeid<AzFramework::SpawnableSystemComponent>(),
|
||||
AZ::Uuid("{624a7be2-3c7e-4119-aee2-1db2bdb6cc89}"), // ScriptDebugAgent
|
||||
});
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <AzFramework/Scene/SceneSystemComponent.h>
|
||||
#include <AzFramework/Script/ScriptComponent.h>
|
||||
#include <AzFramework/Script/ScriptRemoteDebugging.h>
|
||||
#include <AzFramework/Spawnable/SpawnableSystemComponent.h>
|
||||
#include <AzFramework/StreamingInstall/StreamingInstall.h>
|
||||
#include <AzFramework/TargetManagement/TargetManagementComponent.h>
|
||||
#include <AzFramework/Visibility/OctreeSystemComponent.h>
|
||||
@@ -63,6 +64,7 @@ namespace AzFramework
|
||||
AzFramework::AzFrameworkConfigurationSystemComponent::CreateDescriptor(),
|
||||
|
||||
AzFramework::OctreeSystemComponent::CreateDescriptor(),
|
||||
AzFramework::SpawnableSystemComponent::CreateDescriptor(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ namespace AzFramework
|
||||
{
|
||||
Scene* scene = createSceneOutcome.GetValue();
|
||||
bool success = false;
|
||||
EntityContextId gameEntityContextId;
|
||||
EntityContextId gameEntityContextId = EntityContextId::CreateNull();
|
||||
GameEntityContextRequestBus::BroadcastResult(gameEntityContextId, &GameEntityContextRequests::GetGameEntityContextId);
|
||||
|
||||
if (!gameEntityContextId.IsNull())
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include <AzFramework/Components/NonUniformScaleComponent.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Math/ToString.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
@@ -33,7 +35,6 @@ namespace AzFramework
|
||||
|
||||
void NonUniformScaleComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("SkyCloudService"));
|
||||
incompatible.push_back(AZ_CRC_CE("DebugDrawObbService"));
|
||||
incompatible.push_back(AZ_CRC_CE("DebugDrawService"));
|
||||
incompatible.push_back(AZ_CRC_CE("EMotionFXActorService"));
|
||||
@@ -49,8 +50,6 @@ namespace AzFramework
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXShapeColliderService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXCharacterControllerService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PhysXRagdollService"));
|
||||
incompatible.push_back(AZ_CRC_CE("TouchBendingPhysicsService"));
|
||||
incompatible.push_back(AZ_CRC_CE("WaterVolumeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("WhiteBoxService"));
|
||||
incompatible.push_back(AZ_CRC_CE("NavigationAreaService"));
|
||||
incompatible.push_back(AZ_CRC_CE("GeometryService"));
|
||||
@@ -58,12 +57,9 @@ namespace AzFramework
|
||||
incompatible.push_back(AZ_CRC_CE("CompoundShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("CylinderShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("DiskShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("FixedVertexContainerService"));
|
||||
incompatible.push_back(AZ_CRC_CE("PolygonPrismShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SphereShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("SplineService"));
|
||||
incompatible.push_back(AZ_CRC_CE("TubeShapeService"));
|
||||
incompatible.push_back(AZ_CRC_CE("VariableVertexContainerService"));
|
||||
}
|
||||
|
||||
void NonUniformScaleComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
@@ -88,7 +84,17 @@ namespace AzFramework
|
||||
|
||||
void NonUniformScaleComponent::SetScale(const AZ::Vector3& scale)
|
||||
{
|
||||
m_scale = scale;
|
||||
if (scale.GetMinElement() >= AZ::MinNonUniformScale)
|
||||
{
|
||||
m_scale = scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::Vector3 clampedScale = scale.GetMax(AZ::Vector3(AZ::MinNonUniformScale));
|
||||
AZ_Warning("Non-uniform Scale Component", false, "SetScale value was clamped from %s to %s for entity %s",
|
||||
AZ::ToString(scale).c_str(), AZ::ToString(clampedScale).c_str(), GetEntity()->GetName().c_str());
|
||||
m_scale = clampedScale;
|
||||
}
|
||||
m_scaleChangedEvent.Signal(m_scale);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,25 +17,23 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzFramework/Network/NetBindingHandlerBus.h>
|
||||
#include <AzFramework/Network/NetBindingSystemBus.h>
|
||||
#include <AzFramework/Network/NetworkContext.h>
|
||||
|
||||
#include <GridMate/Replica/ReplicaChunk.h>
|
||||
#include <GridMate/Replica/ReplicaFunctions.h>
|
||||
#include <GridMate/Replica/DataSet.h>
|
||||
#include <GridMate/Serialize/CompressionMarshal.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class BehaviorTransformNotificationBusHandler : public TransformNotificationBus::Handler, public AZ::BehaviorEBusHandler
|
||||
class BehaviorTransformNotificationBusHandler
|
||||
: public TransformNotificationBus::Handler
|
||||
, public AZ::BehaviorEBusHandler
|
||||
{
|
||||
public:
|
||||
AZ_EBUS_BEHAVIOR_BINDER(BehaviorTransformNotificationBusHandler, "{9CEF4DAB-F359-4A3E-9856-7780281E0DAA}", AZ::SystemAllocator
|
||||
, OnTransformChanged
|
||||
, OnParentChanged
|
||||
, OnChildAdded
|
||||
, OnChildRemoved
|
||||
AZ_EBUS_BEHAVIOR_BINDER
|
||||
(
|
||||
BehaviorTransformNotificationBusHandler,
|
||||
"{9CEF4DAB-F359-4A3E-9856-7780281E0DAA}",
|
||||
AZ::SystemAllocator,
|
||||
OnTransformChanged,
|
||||
OnParentChanged,
|
||||
OnChildAdded,
|
||||
OnChildRemoved
|
||||
);
|
||||
|
||||
void OnTransformChanged(const Transform& localTM, const Transform& worldTM) override
|
||||
@@ -80,109 +78,10 @@ namespace AZ
|
||||
new(self) TransformConfig();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
//=========================================================================
|
||||
// TransformReplicaChunk
|
||||
// [3/9/2016]
|
||||
//=========================================================================
|
||||
class TransformReplicaChunk
|
||||
: public GridMate::ReplicaChunkBase
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(TransformReplicaChunk, AZ::SystemAllocator, 0);
|
||||
|
||||
static const char* GetChunkName() { return "TransformReplicaChunk"; }
|
||||
|
||||
TransformReplicaChunk()
|
||||
: m_parentId("ParentId")
|
||||
, m_localTranslation("LocalTranslationData")
|
||||
, m_localRotation("LocalRotationData")
|
||||
, m_localScale("LocalScaleData")
|
||||
{
|
||||
m_localTranslation.GetThrottler().SetThreshold(AZ::Vector3(0.005f, 0.005f, 0.005f));
|
||||
m_localScale.GetThrottler().SetThreshold(AZ::Vector3(0.001f, 0.001f, 0.001f));
|
||||
}
|
||||
|
||||
bool IsReplicaMigratable() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetInitialTM(const AZ::Transform& t)
|
||||
{
|
||||
m_initialWorldTM = t;
|
||||
}
|
||||
|
||||
void SetLocalTM(const AZ::Transform& t)
|
||||
{
|
||||
m_localScale.Set(t.GetScale());
|
||||
m_localTranslation.Set(t.GetTranslation());
|
||||
m_localRotation.Set(t.GetRotation());
|
||||
}
|
||||
|
||||
AZ::Transform GetLocalTransform() const
|
||||
{
|
||||
AZ::Transform newXform;
|
||||
newXform.SetTranslation(m_localTranslation.Get());
|
||||
newXform.SetRotation(m_localRotation.Get());
|
||||
newXform.SetScale(m_localScale.Get());
|
||||
return newXform;
|
||||
}
|
||||
|
||||
unsigned int GetLocalTime()
|
||||
{
|
||||
return GetReplicaManager()->GetTime().m_localTime;
|
||||
}
|
||||
|
||||
// parentId (can have no parent)
|
||||
DataSet<AZ::u64>::BindInterface<TransformComponent, &TransformComponent::OnNewNetParentData> m_parentId;
|
||||
|
||||
// transform
|
||||
DataSet<AZ::Vector3, GridMate::Marshaler<AZ::Vector3>, GridMate::EpsilonThrottle<AZ::Vector3>>::BindInterface<TransformComponent, &TransformComponent::OnNewPositionData> m_localTranslation;
|
||||
DataSet<AZ::Quaternion, GridMate::Marshaler<AZ::Quaternion>, GridMate::BasicThrottle<AZ::Quaternion>>::BindInterface<TransformComponent, &TransformComponent::OnNewRotationData> m_localRotation;
|
||||
DataSet<AZ::Vector3, GridMate::Marshaler<AZ::Vector3>, GridMate::EpsilonThrottle<AZ::Vector3>>::BindInterface<TransformComponent, &TransformComponent::OnNewScaleData> m_localScale;
|
||||
|
||||
AZ::Transform m_initialWorldTM;
|
||||
|
||||
class Descriptor
|
||||
: public ExternalChunkDescriptor<TransformReplicaChunk>
|
||||
{
|
||||
public:
|
||||
ReplicaChunkBase* CreateFromStream(UnmarshalContext& context) override
|
||||
{
|
||||
// Pre/Post construct allow DataSets and RPCs to bind to the chunk.
|
||||
TransformReplicaChunk* transformChunk = aznew TransformReplicaChunk;
|
||||
context.m_iBuf->Read(transformChunk->m_initialWorldTM);
|
||||
return transformChunk;
|
||||
}
|
||||
|
||||
void DiscardCtorStream(UnmarshalContext& context) override
|
||||
{
|
||||
AZ::Transform discard;
|
||||
context.m_iBuf->Read(discard);
|
||||
}
|
||||
|
||||
void MarshalCtorData(ReplicaChunkBase* chunk, WriteBuffer& wb) override
|
||||
{
|
||||
TransformReplicaChunk* transformChunk = static_cast<TransformReplicaChunk*>(chunk);
|
||||
TransformComponent* transformComponent = static_cast<TransformComponent*>(transformChunk->GetHandler());
|
||||
if (transformComponent)
|
||||
{
|
||||
wb.Write(transformComponent->GetWorldTM());
|
||||
}
|
||||
else
|
||||
{
|
||||
wb.Write(transformChunk->m_initialWorldTM);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//=========================================================================
|
||||
bool TransformComponentVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
|
||||
{
|
||||
if (classElement.GetVersion() < 3)
|
||||
@@ -204,28 +103,10 @@ namespace AzFramework
|
||||
// future re-additions of it won't remove it (as long as they bump the version number.)
|
||||
classElement.RemoveElementByName(AZ_CRC("InterpolateScale", 0x9d00b831));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// TransformComponent
|
||||
// [8/9/2013]
|
||||
//=========================================================================
|
||||
TransformComponent::TransformComponent()
|
||||
: m_parentTM(nullptr)
|
||||
, m_parentActive(false)
|
||||
, m_onNewParentKeepWorldTM(true)
|
||||
, m_parentActivationTransformMode(ParentActivationTransformMode::MaintainOriginalRelativeTransform)
|
||||
, m_isStatic(false)
|
||||
, m_interpolatePosition(AZ::InterpolationMode::NoInterpolation)
|
||||
, m_interpolateRotation(AZ::InterpolationMode::NoInterpolation)
|
||||
{
|
||||
m_localTM = AZ::Transform::CreateIdentity();
|
||||
m_worldTM = AZ::Transform::CreateIdentity();
|
||||
}
|
||||
|
||||
TransformComponent::TransformComponent(const TransformComponent& copy)
|
||||
: m_localTM(copy.m_localTM)
|
||||
, m_worldTM(copy.m_worldTM)
|
||||
@@ -235,89 +116,9 @@ namespace AzFramework
|
||||
, m_notificationBus(nullptr)
|
||||
, m_onNewParentKeepWorldTM(copy.m_onNewParentKeepWorldTM)
|
||||
, m_parentActivationTransformMode(copy.m_parentActivationTransformMode)
|
||||
, m_replicaChunk(nullptr)
|
||||
, m_isStatic(copy.m_isStatic)
|
||||
, m_interpolatePosition(copy.m_interpolatePosition)
|
||||
, m_interpolateRotation(copy.m_interpolateRotation)
|
||||
, m_netTargetTranslation()
|
||||
, m_netTargetRotation()
|
||||
, m_netTargetScale(copy.m_netTargetScale)
|
||||
{
|
||||
CreateSamples();
|
||||
if (copy.m_netTargetTranslation)
|
||||
{
|
||||
m_netTargetTranslation->SetNewTarget(copy.m_netTargetTranslation->GetTargetValue(), copy.m_netTargetTranslation->GetTargetTimestamp());
|
||||
}
|
||||
if (copy.m_netTargetRotation)
|
||||
{
|
||||
m_netTargetRotation->SetNewTarget(copy.m_netTargetRotation->GetTargetValue(), copy.m_netTargetRotation->GetTargetTimestamp());
|
||||
}
|
||||
|
||||
SetSyncEnabled(copy.m_isSyncEnabled);
|
||||
}
|
||||
|
||||
|
||||
void TransformComponent::CreateTranslationSample()
|
||||
{
|
||||
switch(m_interpolatePosition)
|
||||
{
|
||||
case AZ::InterpolationMode::LinearInterpolation:
|
||||
m_netTargetTranslation = AZStd::make_unique<AZ::LinearlyInterpolatedSample<AZ::Vector3>>();
|
||||
break;
|
||||
case AZ::InterpolationMode::NoInterpolation:
|
||||
default:
|
||||
m_netTargetTranslation = AZStd::make_unique<AZ::UninterpolatedSample<AZ::Vector3>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::CreateRotationSample()
|
||||
{
|
||||
switch (m_interpolateRotation)
|
||||
{
|
||||
case AZ::InterpolationMode::LinearInterpolation:
|
||||
m_netTargetRotation = AZStd::make_unique<AZ::LinearlyInterpolatedSample<AZ::Quaternion>>();
|
||||
break;
|
||||
case AZ::InterpolationMode::NoInterpolation:
|
||||
default:
|
||||
m_netTargetRotation = AZStd::make_unique<AZ::UninterpolatedSample<AZ::Quaternion>>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::CreateSamples()
|
||||
{
|
||||
if (m_netTargetTranslation)
|
||||
{
|
||||
auto target = m_netTargetTranslation->GetTargetValue();
|
||||
auto timeStamp = m_netTargetTranslation->GetTargetTimestamp();
|
||||
|
||||
CreateTranslationSample();
|
||||
|
||||
m_netTargetTranslation->SetNewTarget(target, timeStamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateTranslationSample();
|
||||
}
|
||||
|
||||
if (m_netTargetRotation)
|
||||
{
|
||||
auto target = m_netTargetRotation->GetTargetValue();
|
||||
auto timeStamp = m_netTargetRotation->GetTargetTimestamp();
|
||||
|
||||
CreateRotationSample();
|
||||
|
||||
m_netTargetRotation->SetNewTarget(target, timeStamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateRotationSample();
|
||||
}
|
||||
}
|
||||
|
||||
TransformComponent::~TransformComponent()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
bool TransformComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
|
||||
@@ -328,9 +129,6 @@ namespace AzFramework
|
||||
m_worldTM = config->m_worldTransform;
|
||||
m_parentId = config->m_parentId;
|
||||
m_parentActivationTransformMode = config->m_parentActivationTransformMode;
|
||||
SetSyncEnabled(config->m_netSyncEnabled);
|
||||
m_interpolatePosition = config->m_interpolatePosition;
|
||||
m_interpolateRotation = config->m_interpolateRotation;
|
||||
m_isStatic = config->m_isStatic;
|
||||
return true;
|
||||
}
|
||||
@@ -345,9 +143,6 @@ namespace AzFramework
|
||||
config->m_worldTransform = m_worldTM;
|
||||
config->m_parentId = m_parentId;
|
||||
config->m_parentActivationTransformMode = m_parentActivationTransformMode;
|
||||
config->m_netSyncEnabled = IsSyncEnabled();
|
||||
config->m_interpolatePosition = m_interpolatePosition;
|
||||
config->m_interpolateRotation = m_interpolateRotation;
|
||||
config->m_isStatic = m_isStatic;
|
||||
return true;
|
||||
}
|
||||
@@ -366,8 +161,11 @@ namespace AzFramework
|
||||
void TransformComponent::Deactivate()
|
||||
{
|
||||
EBUS_EVENT_ID(m_parentId, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId());
|
||||
|
||||
UnbindFromNetwork();
|
||||
auto parentTransform = AZ::TransformBus::FindFirstHandler(m_parentId);
|
||||
if (parentTransform)
|
||||
{
|
||||
parentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Removed, GetEntityId());
|
||||
}
|
||||
|
||||
m_notificationBus = nullptr;
|
||||
if (m_parentId.IsValid())
|
||||
@@ -379,12 +177,31 @@ namespace AzFramework
|
||||
AZ::TransformBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void TransformComponent::BindTransformChangedEventHandler(AZ::TransformChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_transformChangedEvent);
|
||||
}
|
||||
|
||||
void TransformComponent::BindParentChangedEventHandler(AZ::ParentChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_parentChangedEvent);
|
||||
}
|
||||
|
||||
void TransformComponent::BindChildChangedEventHandler(AZ::ChildChangedEvent::Handler& handler)
|
||||
{
|
||||
handler.Connect(m_childChangedEvent);
|
||||
}
|
||||
|
||||
void TransformComponent::NotifyChildChangedEvent(AZ::ChildChangeType changeType, AZ::EntityId entityId)
|
||||
{
|
||||
m_childChangedEvent.Signal(changeType, entityId);
|
||||
}
|
||||
|
||||
void TransformComponent::SetLocalTM(const AZ::Transform& tm)
|
||||
{
|
||||
if (AreMoveRequestsAllowed())
|
||||
{
|
||||
SetLocalTMImpl(tm);
|
||||
UpdateReplicaChunk();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,28 +210,17 @@ namespace AzFramework
|
||||
if (AreMoveRequestsAllowed())
|
||||
{
|
||||
SetWorldTMImpl(tm);
|
||||
UpdateReplicaChunk();
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::SetParent(AZ::EntityId id)
|
||||
{
|
||||
if (!IsNetworkControlled())
|
||||
{
|
||||
SetParentImpl(id, true);
|
||||
|
||||
UpdateReplicaChunk();
|
||||
}
|
||||
SetParentImpl(id, true);
|
||||
}
|
||||
|
||||
void TransformComponent::SetParentRelative(AZ::EntityId id)
|
||||
{
|
||||
if (!IsNetworkControlled())
|
||||
{
|
||||
SetParentImpl(id, m_isStatic);
|
||||
|
||||
UpdateReplicaChunk();
|
||||
}
|
||||
SetParentImpl(id, m_isStatic);
|
||||
}
|
||||
|
||||
void TransformComponent::SetWorldTranslation(const AZ::Vector3& newPosition)
|
||||
@@ -860,267 +666,6 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
void TransformComponent::OnEntityActivated(const AZ::EntityId& parentEntityId)
|
||||
{
|
||||
OnEntityActivatedImpl(parentEntityId);
|
||||
UpdateReplicaChunk();
|
||||
}
|
||||
|
||||
void TransformComponent::OnEntityDeactivated(const AZ::EntityId& parentEntityId)
|
||||
{
|
||||
if (!IsNetworkControlled())
|
||||
{
|
||||
OnEntityDeactivateImpl(parentEntityId);
|
||||
UpdateReplicaChunk();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this transform is network controlled, then the localTM is updated by the network,
|
||||
// so update m_parentTM and compute worldTM instead.
|
||||
AZ_Assert(parentEntityId == m_parentId, "We expect to receive notifications only from the current parent!");
|
||||
m_parentTM = nullptr;
|
||||
m_parentActive = false;
|
||||
ComputeWorldTM();
|
||||
}
|
||||
}
|
||||
|
||||
GridMate::ReplicaChunkPtr TransformComponent::GetNetworkBinding()
|
||||
{
|
||||
TransformReplicaChunk* replicaChunk = GridMate::CreateReplicaChunk<TransformReplicaChunk>();
|
||||
replicaChunk->SetHandler(this);
|
||||
m_replicaChunk = replicaChunk;
|
||||
|
||||
UpdateReplicaChunk();
|
||||
|
||||
return m_replicaChunk;
|
||||
}
|
||||
|
||||
void TransformComponent::SetNetworkBinding(GridMate::ReplicaChunkPtr replicaChunk)
|
||||
{
|
||||
AZ_Assert(m_replicaChunk == nullptr, "Being bound to two ReplicaChunks");
|
||||
|
||||
bool isTransformChunk = replicaChunk != nullptr;
|
||||
|
||||
AZ_Assert(isTransformChunk, "Being bound to invalid chunk type");
|
||||
if (isTransformChunk)
|
||||
{
|
||||
replicaChunk->SetHandler(this);
|
||||
m_replicaChunk = replicaChunk;
|
||||
|
||||
TransformReplicaChunk* transformReplicaChunk = static_cast<TransformReplicaChunk*>(m_replicaChunk.get());
|
||||
|
||||
m_parentId = AZ::EntityId(transformReplicaChunk->m_parentId.Get());
|
||||
|
||||
m_worldTM = transformReplicaChunk->m_initialWorldTM;
|
||||
m_localTM = transformReplicaChunk->GetLocalTransform();
|
||||
|
||||
CreateSamples();
|
||||
|
||||
m_netTargetTranslation->SetNewTarget(
|
||||
transformReplicaChunk->m_localTranslation.Get(),
|
||||
transformReplicaChunk->m_localTranslation.GetLastUpdateTime());
|
||||
m_netTargetRotation->SetNewTarget(
|
||||
transformReplicaChunk->m_localRotation.Get(),
|
||||
transformReplicaChunk->m_localRotation.GetLastUpdateTime());
|
||||
m_netTargetScale = transformReplicaChunk->m_localScale.Get();
|
||||
|
||||
if (HasAnyInterpolation())
|
||||
{
|
||||
// only connect if interpolation was selected for either position or rotation
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
}
|
||||
}
|
||||
|
||||
m_onNewParentKeepWorldTM = false;
|
||||
}
|
||||
|
||||
void TransformComponent::UnbindFromNetwork()
|
||||
{
|
||||
if (HasAnyInterpolation())
|
||||
{
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
if (m_replicaChunk)
|
||||
{
|
||||
m_replicaChunk->SetHandler(nullptr);
|
||||
m_replicaChunk = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::OnNewNetTransformData(const AZ::Transform& transform, const GridMate::TimeContext& /*tc*/)
|
||||
{
|
||||
SetLocalTMImpl(transform);
|
||||
}
|
||||
|
||||
void TransformComponent::OnNewNetParentData(const AZ::u64& parentId, const GridMate::TimeContext& /*tc*/)
|
||||
{
|
||||
SetParentImpl(AZ::EntityId(parentId), false);
|
||||
}
|
||||
|
||||
bool TransformComponent::IsNetworkControlled() const
|
||||
{
|
||||
return m_replicaChunk && m_replicaChunk->GetReplica() && !m_replicaChunk->IsMaster();
|
||||
}
|
||||
|
||||
bool TransformComponent::IsPositionInterpolated()
|
||||
{
|
||||
return m_interpolatePosition != AZ::InterpolationMode::NoInterpolation;
|
||||
}
|
||||
|
||||
bool TransformComponent::IsRotationInterpolated()
|
||||
{
|
||||
return m_interpolateRotation != AZ::InterpolationMode::NoInterpolation;
|
||||
}
|
||||
|
||||
bool TransformComponent::HasAnyInterpolation()
|
||||
{
|
||||
return IsPositionInterpolated() || IsRotationInterpolated();
|
||||
}
|
||||
|
||||
void TransformComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*currentTime*/)
|
||||
{
|
||||
if (GetEntity() && GetEntity()->GetState() == AZ::Entity::State::Active)
|
||||
{
|
||||
if (m_replicaChunk && m_replicaChunk->IsProxy())
|
||||
{
|
||||
const unsigned int localTime = m_replicaChunk->GetReplicaManager()->GetTime().m_localTime;
|
||||
const AZ::Transform newXform = GetInterpolatedTransform(localTime);
|
||||
SetLocalTMImpl(newXform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Transform TransformComponent::GetInterpolatedTransform(unsigned localTime)
|
||||
{
|
||||
const AZ::Vector3 newTranslation = m_netTargetTranslation->GetInterpolatedValue(localTime);
|
||||
const AZ::Quaternion newRotation = m_netTargetRotation->GetInterpolatedValue(localTime);
|
||||
AZ::Transform newXform = AZ::Transform::CreateFromQuaternionAndTranslation(newRotation, newTranslation);
|
||||
newXform.MultiplyByScale(m_netTargetScale);
|
||||
|
||||
return newXform;
|
||||
}
|
||||
|
||||
void TransformComponent::OnNewPositionData(const AZ::Vector3& translation, const GridMate::TimeContext& tc)
|
||||
{
|
||||
m_netTargetTranslation->SetNewTarget(translation, tc.m_realTime);
|
||||
if (!HasAnyInterpolation())
|
||||
{
|
||||
unsigned int localTime = m_replicaChunk->GetReplicaManager()->GetTime().m_localTime;
|
||||
AZ::Transform newXform = GetInterpolatedTransform(localTime);
|
||||
SetLocalTMImpl(newXform);
|
||||
}
|
||||
};
|
||||
|
||||
void TransformComponent::OnNewRotationData(const AZ::Quaternion& rotation, const GridMate::TimeContext& tc)
|
||||
{
|
||||
m_netTargetRotation->SetNewTarget(rotation, tc.m_realTime);
|
||||
if (!HasAnyInterpolation())
|
||||
{
|
||||
unsigned int localTime = m_replicaChunk->GetReplicaManager()->GetTime().m_localTime;
|
||||
AZ::Transform newXform = GetInterpolatedTransform(localTime);
|
||||
SetLocalTMImpl(newXform);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::OnNewScaleData(const AZ::Vector3& scale, const GridMate::TimeContext& /*tc*/)
|
||||
{
|
||||
// no interpolation of scale by design, very unlikely somebody needs it
|
||||
m_netTargetScale = scale;
|
||||
}
|
||||
|
||||
void TransformComponent::UpdateReplicaChunk()
|
||||
{
|
||||
if (!IsNetworkControlled() && m_replicaChunk)
|
||||
{
|
||||
TransformReplicaChunk* transformReplicaChunk = static_cast<TransformReplicaChunk*>(m_replicaChunk.get());
|
||||
transformReplicaChunk->SetLocalTM(GetLocalTM());
|
||||
transformReplicaChunk->m_parentId.Set(static_cast<AZ::u64>(GetParentId()));
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::SetParentImpl(AZ::EntityId parentId, bool isKeepWorldTM)
|
||||
{
|
||||
if (parentId == GetEntityId())
|
||||
{
|
||||
AZ_Warning("TransformComponent", false, "An entity can not be set as its own parent.");
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId oldParent = m_parentId;
|
||||
if (m_parentId.IsValid())
|
||||
{
|
||||
AZ::TransformNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TransformHierarchyInformationBus::Handler::BusDisconnect();
|
||||
AZ::EntityBus::Handler::BusDisconnect();
|
||||
m_parentActive = false;
|
||||
}
|
||||
|
||||
m_parentId = parentId;
|
||||
if (m_parentId.IsValid())
|
||||
{
|
||||
AZ::Entity* parentEntity = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(parentEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_parentId);
|
||||
m_parentActive = parentEntity && (parentEntity->GetState() == AZ::Entity::State::Active);
|
||||
|
||||
m_onNewParentKeepWorldTM = isKeepWorldTM;
|
||||
|
||||
AZ::TransformNotificationBus::Handler::BusConnect(m_parentId);
|
||||
AZ::TransformHierarchyInformationBus::Handler::BusConnect(m_parentId);
|
||||
AZ::EntityBus::Handler::BusConnect(m_parentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_parentTM = nullptr;
|
||||
|
||||
if (isKeepWorldTM)
|
||||
{
|
||||
SetWorldTM(m_worldTM);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLocalTM(m_localTM);
|
||||
}
|
||||
|
||||
if (oldParent.IsValid())
|
||||
{
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
|
||||
}
|
||||
}
|
||||
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnParentChanged, oldParent, parentId);
|
||||
|
||||
if (oldParent != parentId) // Don't send removal notification while activating.
|
||||
{
|
||||
EBUS_EVENT_ID(oldParent, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId());
|
||||
}
|
||||
|
||||
EBUS_EVENT_ID(parentId, AZ::TransformNotificationBus, OnChildAdded, GetEntityId());
|
||||
}
|
||||
|
||||
void TransformComponent::SetLocalTMImpl(const AZ::Transform& tm)
|
||||
{
|
||||
m_localTM = tm;
|
||||
ComputeWorldTM(); // We can user dirty flags and compute it later on demand
|
||||
}
|
||||
|
||||
void TransformComponent::SetWorldTMImpl(const AZ::Transform& tm)
|
||||
{
|
||||
m_worldTM = tm;
|
||||
ComputeLocalTM(); // We can user dirty flags and compute it later on demand
|
||||
}
|
||||
|
||||
void TransformComponent::OnTransformChangedImpl(const AZ::Transform& /*parentLocalTM*/, const AZ::Transform& parentWorldTM)
|
||||
{
|
||||
// Called when our parent transform changes
|
||||
// Ignore the event until we've already derived our local transform.
|
||||
if (m_parentTM)
|
||||
{
|
||||
m_worldTM = parentWorldTM * m_localTM;
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::OnEntityActivatedImpl(const AZ::EntityId& parentEntityId)
|
||||
{
|
||||
AZ_Assert(parentEntityId == m_parentId, "We expect to receive notifications only from the current parent!");
|
||||
|
||||
@@ -1171,15 +716,109 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::OnEntityDeactivateImpl(const AZ::EntityId& parentEntityId)
|
||||
void TransformComponent::OnEntityDeactivated([[maybe_unused]] const AZ::EntityId& parentEntityId)
|
||||
{
|
||||
(void)parentEntityId;
|
||||
AZ_Assert(parentEntityId == m_parentId, "We expect to receive notifications only from the current parent!");
|
||||
m_parentTM = nullptr;
|
||||
m_parentActive = false;
|
||||
ComputeLocalTM();
|
||||
}
|
||||
|
||||
void TransformComponent::SetParentImpl(AZ::EntityId parentId, bool isKeepWorldTM)
|
||||
{
|
||||
if (parentId == GetEntityId())
|
||||
{
|
||||
AZ_Warning("TransformComponent", false, "An entity can not be set as its own parent.");
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::EntityId oldParent = m_parentId;
|
||||
if (m_parentId.IsValid())
|
||||
{
|
||||
AZ::TransformNotificationBus::Handler::BusDisconnect();
|
||||
AZ::TransformHierarchyInformationBus::Handler::BusDisconnect();
|
||||
AZ::EntityBus::Handler::BusDisconnect();
|
||||
m_parentActive = false;
|
||||
}
|
||||
|
||||
m_parentId = parentId;
|
||||
if (m_parentId.IsValid())
|
||||
{
|
||||
AZ::Entity* parentEntity = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(parentEntity, &AZ::ComponentApplicationBus::Events::FindEntity, m_parentId);
|
||||
m_parentActive = parentEntity && (parentEntity->GetState() == AZ::Entity::State::Active);
|
||||
|
||||
m_onNewParentKeepWorldTM = isKeepWorldTM;
|
||||
|
||||
AZ::TransformNotificationBus::Handler::BusConnect(m_parentId);
|
||||
AZ::TransformHierarchyInformationBus::Handler::BusConnect(m_parentId);
|
||||
AZ::EntityBus::Handler::BusConnect(m_parentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_parentTM = nullptr;
|
||||
|
||||
if (isKeepWorldTM)
|
||||
{
|
||||
SetWorldTM(m_worldTM);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLocalTM(m_localTM);
|
||||
}
|
||||
|
||||
if (oldParent.IsValid())
|
||||
{
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
|
||||
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
|
||||
}
|
||||
}
|
||||
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnParentChanged, oldParent, parentId);
|
||||
m_parentChangedEvent.Signal(oldParent, parentId);
|
||||
|
||||
if (oldParent != parentId) // Don't send removal notification while activating.
|
||||
{
|
||||
EBUS_EVENT_ID(oldParent, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId());
|
||||
auto oldParentTransform = AZ::TransformBus::FindFirstHandler(oldParent);
|
||||
if (oldParentTransform)
|
||||
{
|
||||
oldParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Removed, GetEntityId());
|
||||
}
|
||||
}
|
||||
|
||||
EBUS_EVENT_ID(parentId, AZ::TransformNotificationBus, OnChildAdded, GetEntityId());
|
||||
auto newParentTransform = AZ::TransformBus::FindFirstHandler(parentId);
|
||||
if (newParentTransform)
|
||||
{
|
||||
newParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Added, GetEntityId());
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::SetLocalTMImpl(const AZ::Transform& tm)
|
||||
{
|
||||
m_localTM = tm;
|
||||
ComputeWorldTM(); // We can user dirty flags and compute it later on demand
|
||||
}
|
||||
|
||||
void TransformComponent::SetWorldTMImpl(const AZ::Transform& tm)
|
||||
{
|
||||
m_worldTM = tm;
|
||||
ComputeLocalTM(); // We can user dirty flags and compute it later on demand
|
||||
}
|
||||
|
||||
void TransformComponent::OnTransformChangedImpl(const AZ::Transform& /*parentLocalTM*/, const AZ::Transform& parentWorldTM)
|
||||
{
|
||||
// Called when our parent transform changes
|
||||
// Ignore the event until we've already derived our local transform.
|
||||
if (m_parentTM)
|
||||
{
|
||||
m_worldTM = parentWorldTM * m_localTM;
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
|
||||
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
|
||||
}
|
||||
}
|
||||
|
||||
void TransformComponent::ComputeLocalTM()
|
||||
{
|
||||
if (m_parentTM)
|
||||
@@ -1192,6 +831,7 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
|
||||
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
|
||||
}
|
||||
|
||||
void TransformComponent::ComputeWorldTM()
|
||||
@@ -1206,15 +846,11 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnTransformChanged, m_localTM, m_worldTM);
|
||||
m_transformChangedEvent.Signal(m_localTM, m_worldTM);
|
||||
}
|
||||
|
||||
bool TransformComponent::AreMoveRequestsAllowed() const
|
||||
{
|
||||
if (IsNetworkControlled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow static transform to be moved while entity is activated.
|
||||
// But do allow a static transform to be moved when the entity is deactivated.
|
||||
if (m_isStatic && m_entity && (m_entity->GetState() > AZ::Entity::State::Init))
|
||||
@@ -1248,7 +884,7 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection);
|
||||
if(behaviorContext)
|
||||
if (behaviorContext)
|
||||
{
|
||||
behaviorContext->EBus<AZ::TransformNotificationBus>("TransformNotificationBus")->
|
||||
Handler<AZ::BehaviorTransformNotificationBusHandler>();
|
||||
@@ -1396,26 +1032,8 @@ namespace AzFramework
|
||||
->Property("parentActivationTransformMode",
|
||||
[](AZ::TransformConfig* config) { return (int&)(config->m_parentActivationTransformMode); },
|
||||
[](AZ::TransformConfig* config, const int& i) { config->m_parentActivationTransformMode = (AZ::TransformConfig::ParentActivationTransformMode)i; })
|
||||
->Property("netSyncEnabled", BehaviorValueProperty(&AZ::TransformConfig::m_netSyncEnabled))
|
||||
->Property("interpolatePosition",
|
||||
[](AZ::TransformConfig* config) { return (int&)(config->m_interpolatePosition); },
|
||||
[](AZ::TransformConfig* config, const int& i) { config->m_interpolatePosition = (AZ::InterpolationMode)i; })
|
||||
->Property("interpolateRotation",
|
||||
[](AZ::TransformConfig* config) { return (int&)(config->m_interpolateRotation); },
|
||||
[](AZ::TransformConfig* config, const int& i) { config->m_interpolateRotation = (AZ::InterpolationMode)i; })
|
||||
->Property("isStatic", BehaviorValueProperty(&AZ::TransformConfig::m_isStatic))
|
||||
;
|
||||
}
|
||||
|
||||
NetworkContext* netContext = azrtti_cast<NetworkContext*>(reflection);
|
||||
if (netContext)
|
||||
{
|
||||
netContext->Class<TransformComponent>()
|
||||
->Chunk<TransformReplicaChunk, TransformReplicaChunk::Descriptor>()
|
||||
->Field("ParentId", &TransformReplicaChunk::m_parentId)
|
||||
->Field("LocalTranslationData", &TransformReplicaChunk::m_localTranslation)
|
||||
->Field("LocalRotationData", &TransformReplicaChunk::m_localRotation)
|
||||
->Field("LocalScaleData", &TransformReplicaChunk::m_localScale);
|
||||
}
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
@@ -29,26 +29,20 @@ namespace AzToolsFramework
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
class TransformReplicaChunk;
|
||||
class GameEntityContextComponent;
|
||||
|
||||
/// @deprecated Use AZ::TransformConfig
|
||||
using TransformComponentConfiguration = AZ::TransformConfig;
|
||||
|
||||
//! Fundamental component that describes the entity in 3D space.
|
||||
//! It is net-bindable. Only local transform is synchronized, so when
|
||||
//! parented, the parent must properly synchronize its transform as well.
|
||||
class TransformComponent
|
||||
: public AZ::Component
|
||||
, public AZ::EntityBus::Handler
|
||||
, public AZ::TransformBus::Handler
|
||||
, public AZ::TransformNotificationBus::Handler
|
||||
, public AZ::EntityBus::Handler
|
||||
, public AZ::TickBus::Handler
|
||||
, private AZ::TransformHierarchyInformationBus::Handler
|
||||
, public NetBindable
|
||||
{
|
||||
friend class TransformReplicaChunk;
|
||||
|
||||
public:
|
||||
AZ_COMPONENT(TransformComponent, AZ::TransformComponentTypeId, NetBindable, AZ::TransformInterface);
|
||||
|
||||
@@ -56,11 +50,15 @@ namespace AzFramework
|
||||
|
||||
using ParentActivationTransformMode = AZ::TransformConfig::ParentActivationTransformMode;
|
||||
|
||||
TransformComponent();
|
||||
TransformComponent() = default;
|
||||
TransformComponent(const TransformComponent& copy);
|
||||
virtual ~TransformComponent();
|
||||
~TransformComponent() override = default;
|
||||
|
||||
// TransformBus events (publicly accessible)
|
||||
void BindTransformChangedEventHandler(AZ::TransformChangedEvent::Handler& handler) override;
|
||||
void BindParentChangedEventHandler(AZ::ParentChangedEvent::Handler& handler) override;
|
||||
void BindChildChangedEventHandler(AZ::ChildChangedEvent::Handler& handler) override;
|
||||
void NotifyChildChangedEvent(AZ::ChildChangeType changeType, AZ::EntityId entityId) override;
|
||||
//! Returns true if the tm was set to the local transform.
|
||||
const AZ::Transform& GetLocalTM() override { return m_localTM; }
|
||||
//! Returns true if the tm was set to the world transform.
|
||||
@@ -115,8 +113,6 @@ namespace AzFramework
|
||||
float GetLocalY() override;
|
||||
float GetLocalZ() override;
|
||||
|
||||
bool IsPositionInterpolated() override;
|
||||
|
||||
// Rotation modifiers
|
||||
void SetRotation(const AZ::Vector3& eulerAnglesRadian) override;
|
||||
void SetRotationQuaternion(const AZ::Quaternion& quaternion) override;
|
||||
@@ -148,8 +144,6 @@ namespace AzFramework
|
||||
AZ::Vector3 GetLocalRotation() override;
|
||||
AZ::Quaternion GetLocalRotationQuaternion() override;
|
||||
|
||||
bool IsRotationInterpolated() override;
|
||||
|
||||
// Scale Modifiers
|
||||
void SetScale(const AZ::Vector3& scale) override;
|
||||
void SetScaleX(float scaleX) override;
|
||||
@@ -187,28 +181,6 @@ namespace AzFramework
|
||||
void OnEntityDeactivated(const AZ::EntityId& parentEntityId) override;
|
||||
//! @}
|
||||
|
||||
//! Methods implementing NetBindable.
|
||||
//! @{
|
||||
GridMate::ReplicaChunkPtr GetNetworkBinding() override;
|
||||
void SetNetworkBinding(GridMate::ReplicaChunkPtr chunk) override;
|
||||
void UnbindFromNetwork() override;
|
||||
|
||||
//! Called by the net chunk when new transform data arrives from the network.
|
||||
void OnNewNetTransformData(const AZ::Transform& transform, const GridMate::TimeContext& tc);
|
||||
|
||||
//! Called by the net chunk when new parent id arrives from the network.
|
||||
void OnNewNetParentData(const AZ::u64& parentId, const GridMate::TimeContext& tc);
|
||||
|
||||
//! Returns true if this instance is non-authoritative.
|
||||
bool IsNetworkControlled() const;
|
||||
|
||||
//! Triggers an update of the chunk data. Should only be called on the authoritative instance.
|
||||
void UpdateReplicaChunk();
|
||||
//! @}
|
||||
|
||||
// AZ::TickBus
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Actual Implementation Functions
|
||||
// They are protected so we can gate them when network-controlled
|
||||
@@ -216,8 +188,6 @@ namespace AzFramework
|
||||
void SetLocalTMImpl(const AZ::Transform& tm);
|
||||
void SetWorldTMImpl(const AZ::Transform& tm);
|
||||
void OnTransformChangedImpl(const AZ::Transform& parentLocalTM, const AZ::Transform& parentWorldTM);
|
||||
void OnEntityActivatedImpl(const AZ::EntityId& parentEntityId);
|
||||
void OnEntityDeactivateImpl(const AZ::EntityId& parentEntityId);
|
||||
void ComputeLocalTM();
|
||||
void ComputeWorldTM();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -228,44 +198,31 @@ namespace AzFramework
|
||||
// TransformHierarchyInformationBus
|
||||
void GatherChildren(AZStd::vector<AZ::EntityId>& children) override;
|
||||
|
||||
//! Feedback from corresponding replica chunk.
|
||||
//! @{
|
||||
void OnNewPositionData(const AZ::Vector3&, const GridMate::TimeContext&);
|
||||
void OnNewRotationData(const AZ::Quaternion&, const GridMate::TimeContext&);
|
||||
void OnNewScaleData(const AZ::Vector3&, const GridMate::TimeContext&);
|
||||
//! @}
|
||||
|
||||
/// \ref ComponentDescriptor::GetProvidedServices
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
|
||||
/// \ref ComponentDescriptor::Reflect
|
||||
static void Reflect(AZ::ReflectContext* reflection);
|
||||
|
||||
AZ::Transform m_localTM; ///< Local transform relative to parent transform (same as worldTM if no parent).
|
||||
AZ::Transform m_worldTM; ///< World transform including parent transform (same as localTM if no parent).
|
||||
AZ::EntityId m_parentId; ///< If valid, this transform is parented to m_parentId.
|
||||
AZ::TransformInterface* m_parentTM; ///< Cached - pointer to parent transform, to avoid extra calls. Valid only when if it's present.
|
||||
bool m_parentActive; ///< Keeps track of the state of the parent entity.
|
||||
AZ::TransformNotificationBus::BusPtr m_notificationBus; ///< Cached bus pointer to the notification bus.
|
||||
bool m_onNewParentKeepWorldTM; ///< If set, recompute localTM instead of worldTM when parent becomes active.
|
||||
ParentActivationTransformMode m_parentActivationTransformMode;
|
||||
GridMate::ReplicaChunkPtr m_replicaChunk;
|
||||
bool m_isStatic; ///< If true, the transform is static and doesn't move while entity is active.
|
||||
AZ::InterpolationMode m_interpolatePosition; ///< Interpolation mode for net-synced position updates.
|
||||
AZ::InterpolationMode m_interpolateRotation; ///< Interpolation mode for net-synced rotation updates.
|
||||
AZ::TransformChangedEvent m_transformChangedEvent; ///< Event used to signal when a transform changes.
|
||||
AZ::ParentChangedEvent m_parentChangedEvent; ///< Event used to signal when a transforms parent changes.
|
||||
AZ::ChildChangedEvent m_childChangedEvent; ///< Event used to signal when a transform has a child entity added or removed.
|
||||
|
||||
private:
|
||||
AZ::Transform m_localTM = AZ::Transform::CreateIdentity(); ///< Local transform relative to parent transform (same as worldTM if no parent).
|
||||
AZ::Transform m_worldTM = AZ::Transform::CreateIdentity(); ///< World transform including parent transform (same as localTM if no parent).
|
||||
|
||||
bool HasAnyInterpolation();
|
||||
AZ::EntityId m_parentId; ///< If valid, this transform is parented to m_parentId.
|
||||
AZ::TransformInterface* m_parentTM = nullptr; ///< Cached - pointer to parent transform, to avoid extra calls. Valid only when if it's present.
|
||||
AZ::TransformNotificationBus::BusPtr m_notificationBus; ///< Cached bus pointer to the notification bus.
|
||||
ParentActivationTransformMode m_parentActivationTransformMode = ParentActivationTransformMode::MaintainOriginalRelativeTransform;
|
||||
bool m_parentActive = false; ///< Keeps track of the state of the parent entity.
|
||||
bool m_onNewParentKeepWorldTM = true; ///< If set, recompute localTM instead of worldTM when parent becomes active.
|
||||
bool m_isStatic = false; ///< If true, the transform is static and doesn't move while entity is active.
|
||||
|
||||
void CreateSamples();
|
||||
void CreateTranslationSample();
|
||||
void CreateRotationSample();
|
||||
|
||||
AZ::Transform GetInterpolatedTransform(unsigned int localTime);
|
||||
|
||||
AZStd::unique_ptr<AZ::Sample<AZ::Vector3>> m_netTargetTranslation;
|
||||
AZStd::unique_ptr<AZ::Sample<AZ::Quaternion>> m_netTargetRotation;
|
||||
AZ::Vector3 m_netTargetScale;
|
||||
//! @deprecated
|
||||
//! @{
|
||||
AZ::InterpolationMode m_interpolatePosition = AZ::InterpolationMode::NoInterpolation;
|
||||
AZ::InterpolationMode m_interpolateRotation = AZ::InterpolationMode::NoInterpolation;
|
||||
//! @}
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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/Engine/Engine.h>
|
||||
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
namespace Engine
|
||||
{
|
||||
AZ::IO::FixedMaxPath FindEngineRoot(AZStd::string_view searchPath)
|
||||
{
|
||||
// File to locate
|
||||
const char engineRootMarker[] = "engine.json";
|
||||
|
||||
AZ::IO::FixedMaxPath currentSearchPath{searchPath};
|
||||
if (currentSearchPath.empty())
|
||||
{
|
||||
char executablePath[AZ_MAX_PATH_LEN];
|
||||
AZ::Utils::GetExecutablePathReturnType result = AZ::Utils::GetExecutablePath(executablePath, AZ_MAX_PATH_LEN);
|
||||
currentSearchPath = executablePath;
|
||||
}
|
||||
do
|
||||
{
|
||||
currentSearchPath = currentSearchPath.ParentPath();
|
||||
if (AZ::IO::SystemFile::Exists((currentSearchPath / engineRootMarker).c_str()))
|
||||
{
|
||||
return currentSearchPath;
|
||||
}
|
||||
} while (currentSearchPath.ParentPath() != currentSearchPath);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} // AzFramework
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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/IO/Path/Path.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
namespace Engine
|
||||
{
|
||||
// Helper to attempt to locate the engine root by searching up the directory tree. If no search path is
|
||||
// provided the current executable path is used
|
||||
AZ::IO::FixedMaxPath FindEngineRoot(AZStd::string_view searchPath = {});
|
||||
} // Engine
|
||||
} // AzFramework
|
||||
@@ -93,6 +93,8 @@ namespace AzFramework
|
||||
virtual void SetEntitiesRemovedCallback(OnEntitiesRemovedCallback onEntitiesRemovedCallback) = 0;
|
||||
virtual void SetValidateEntitiesCallback(ValidateEntitiesCallback validateEntitiesCallback) = 0;
|
||||
|
||||
bool m_shouldAssertForLegacySlicesUsage = false;
|
||||
|
||||
protected:
|
||||
OnEntitiesAddedCallback m_entitiesAddedCallback;
|
||||
OnEntitiesRemovedCallback m_entitiesRemovedCallback;
|
||||
|
||||
@@ -19,6 +19,19 @@ namespace AzFramework
|
||||
using EntityContextId = AZ::Uuid;
|
||||
using EntityList = AZStd::vector<AZ::Entity*>;
|
||||
|
||||
class EntityOwnershipService;
|
||||
|
||||
class EntityOwnershipServiceInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(EntityOwnershipServiceInterface, "{6490E958-5DF5-45CF-9A25-D857DB0C67DB}");
|
||||
|
||||
EntityOwnershipServiceInterface() = default;
|
||||
virtual ~EntityOwnershipServiceInterface() = default;
|
||||
|
||||
virtual AZStd::unique_ptr<EntityOwnershipService> CreateEntityOwnershipService() = 0;
|
||||
};
|
||||
|
||||
class EntityOwnershipServiceNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
/*
|
||||
* 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/Math/Aabb.h>
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
constexpr const char * const AZ_TOUCH_BENDING_WINDOW = "AzTouchBending";
|
||||
|
||||
// Bone orientation
|
||||
//
|
||||
// _ TOP Point Z+ up
|
||||
// | | ^
|
||||
// | | |
|
||||
// |*| (0,0,0) X- <------|--------> X+
|
||||
// | | |
|
||||
// |_| BOTTOM Point |
|
||||
// Z-
|
||||
|
||||
/// SpinePoint contains properties of mass, thickness, damping and stiffness
|
||||
/// for the bone that will be made. The SpinePoint is the BOTTOM point
|
||||
/// of a Bone.
|
||||
struct SpinePoint
|
||||
{
|
||||
///mass in Kg.
|
||||
float m_mass;
|
||||
|
||||
///If you imagine the Bone to be a cylinder, this is its radius in meters.
|
||||
float m_thickness;
|
||||
|
||||
///A value from 0.0 to 1.0. 0.0 means no damping, lots of back and forth movement around its original pose.
|
||||
///1.0 means maximum damping, the segment will quickly converge back to its original pose.
|
||||
float m_damping;
|
||||
|
||||
///A value from 0.0 to 1.0. 0.0 means no stiffness, the segment will look like a sad willow,
|
||||
///It would never return to its original pose.
|
||||
float m_stiffness;
|
||||
|
||||
///Position is in Model Space.
|
||||
AZ::Vector3 m_position;
|
||||
};
|
||||
|
||||
struct Spine
|
||||
{
|
||||
///Index of parent spine. -1 if no parent.
|
||||
int m_parentSpineIndex;
|
||||
|
||||
///Index of the point within the parent spine array of segments.
|
||||
///-1 if no parent.
|
||||
int m_parentPointIndex;
|
||||
|
||||
///Array of segments.
|
||||
AZStd::vector<SpinePoint> m_points;
|
||||
};
|
||||
|
||||
typedef void* SpineTreeIDType;
|
||||
|
||||
///SpineTree is an archetype. This is basically the AzFramework version
|
||||
///of CStatObj.SSpine.
|
||||
struct SpineTree
|
||||
{
|
||||
///Unique Identifier Of this SpineTree.
|
||||
SpineTreeIDType m_spineTreeId;
|
||||
|
||||
///A SpineTree ALWAYS contains at least one spine.
|
||||
AZStd::vector<Spine> m_spines;
|
||||
|
||||
///Helper method.
|
||||
size_t CalculateTotalNumberOfBones() const
|
||||
{
|
||||
size_t numberOfBones = 0;
|
||||
for (const Spine& spine : m_spines)
|
||||
{
|
||||
numberOfBones += spine.m_points.size() - 1;
|
||||
}
|
||||
return numberOfBones;
|
||||
}
|
||||
};
|
||||
|
||||
///The Engine side of Touch bending uses this as an opaque handle.
|
||||
///Only the TouchBending Gem knows what's inside.
|
||||
///This handle corresponds one-to-one with a unique Vegetation Render Node instance.
|
||||
struct TouchBendingTriggerHandle;
|
||||
|
||||
///The Engine side of Touch bending uses this as an opaque handle.
|
||||
///Only the TouchBending Gem knows what's inside.
|
||||
///This handle corresponds one-to-one with a unique CStatObjFoliage instance.
|
||||
struct TouchBendingSkeletonHandle;
|
||||
|
||||
///Used by TouchBending Gem to talk back with the Engine.
|
||||
class ITouchBendingCallback
|
||||
{
|
||||
public:
|
||||
ITouchBendingCallback() = default;
|
||||
virtual ~ITouchBendingCallback() = default;
|
||||
|
||||
/** @brief Checks if a render node is within e_CullVegActivation radius from the camera
|
||||
*
|
||||
* @param privateData Pointer to the Render Node inside the Engine that represents
|
||||
* the touch bendable entity. From the point of view of the TouchBending Gem this
|
||||
* is an opaque pointer, but from the point of view of the engine this is a
|
||||
* CVegetation render node.
|
||||
* @returns Returns a non-zero SpineTreeIDType if the Render Node is within
|
||||
* e_CullVegActivation radius from the center of the main camera.
|
||||
* Otherwise returns zero.
|
||||
*/
|
||||
virtual SpineTreeIDType CheckDistanceToCamera(const void* privateData) = 0;
|
||||
|
||||
/** @brief Builds a SpineTree archetype object using its SpineTreeIDType.
|
||||
*
|
||||
* \p privateData is a CVegetation*
|
||||
* \p spineTreeId is a CStatObj*
|
||||
*
|
||||
* @param privateData Pointer to the Render Node inside the Engine that represents
|
||||
* the touch bendable entity.
|
||||
* @param spineTreeId Spine Tree Archetype Identifier as given previously by the Engine.
|
||||
* @param spineTreeOut Output SpineTree archetype object.
|
||||
* @returns TRUE if such \p spineTreeId is valid and a SpineTree archetype was successfully built.
|
||||
* Otherwise returns FALSE.
|
||||
*/
|
||||
virtual bool BuildSpineTree(const void* privateData, SpineTreeIDType spineTreeId, SpineTree& spineTreeOut) = 0;
|
||||
|
||||
/** TouchBending Gem calls this to notify the Engine that a unique PhysicalizedSkeleton instance was built
|
||||
* on behalf of \p privateData.
|
||||
*
|
||||
* The Engine uses this event to build a CStatObjFoliage to keep track of active touch bendable objects.
|
||||
* The Engine will keep CStatObjFoliage alive as long as it is touched or for a specific lifetime in seconds
|
||||
* defined by the CVar e_FoliageBranchesTimeout.
|
||||
*
|
||||
* @param privateData Pointer to the Render Node inside the Engine that represents
|
||||
* the touch bendable entity.
|
||||
* @param skeletonHandle Opaque pointer that the CStatObjFoliage must keep a copy to. The engine
|
||||
* should should use it later when calling *Skeleton*() named methods of the TouchBendingBus.
|
||||
* @returns true if the CStatObjFoliage was created successfully. It may return false only for cases where the CStatObj
|
||||
* was removed and CStatObjFoliage can only be created if CStatObj is not null.
|
||||
*/
|
||||
virtual bool OnPhysicalizedTouchBendingSkeleton(const void* privateData, TouchBendingSkeletonHandle* skeletonHandle) = 0;
|
||||
}; //class ITouchBendingCallback
|
||||
|
||||
|
||||
//Exact same memory format as QuatTS
|
||||
//CStatObjFoliage::ComputeSkinningTransformations() uses:
|
||||
//QuatTS.q[x,y,z] as TOP joint position.
|
||||
//QuatTS.t[x,y,z] as BOTTOM joint position.
|
||||
//QuatTS.s CStatObjFoliage::GetSkinningData() reads this value for the first bone of each spine
|
||||
// as marker for valid data, if less than zero, the spine is skipped by the Skinning code.
|
||||
// A bone has two joints, TOP and BOTTOM:
|
||||
//
|
||||
// _ TOP Z+ up
|
||||
// | | ^
|
||||
// | | |
|
||||
// |*| (0,0,0) X- <------|--------> X+
|
||||
// | | |
|
||||
// |_| BOTTOM |
|
||||
// Z-
|
||||
struct JointPositions
|
||||
{
|
||||
float m_TopJointLocation[3]; //Equivalent to QuatTS.q.xyz (ijk)
|
||||
float m_qw; //Equivalent to QuatTS.q.w
|
||||
float m_BottomJointLocation[3]; //Equivalent to QuatTS.t
|
||||
float m_hasNewData; //Equivalent to QuatTS.s (See description above about CStatObjFoliage::GetSkinningData()).
|
||||
};
|
||||
|
||||
/**
|
||||
* Replacement of CryPhysics Touch Bending simulation.
|
||||
*/
|
||||
class TouchBendingRequest
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(TouchBendingRequest, "{4E9DE1BE-F0C7-47E7-B315-9302F62D044C}");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual ~TouchBendingRequest() = default;
|
||||
|
||||
/// If the EBUS implementation (aka TouchBending Gem) returns TRUE
|
||||
/// all of the Physics simulation for Touch Bendable CVegetation is done
|
||||
/// by the TouchBending Gem with PhysX. If it returns FALSE the engine
|
||||
/// will default to CryPhysics.
|
||||
virtual bool IsTouchBendingEnabled() const = 0;
|
||||
|
||||
/** @brief Creates a TouchBending Trigger with a simple trigger box.
|
||||
*
|
||||
* Initially a TouchBending Trigger is nothing more than a trigger volume. There's
|
||||
* no skeleton, etc. It will serve as the trigger, that when touched, builds a unique physicalized
|
||||
* skeleton with the same amount of bones as a SpineTree. Recall that SpineTree is an archetype.
|
||||
* The TouchBending Gem Builds a TouchBendingSkeletonHandle based on a SpineTree when TouchBendingTriggerHandle is touched.
|
||||
*
|
||||
* When the User adds an object via the Vegetation panel of the "Terrain Tool" UI
|
||||
* this method will be called by the engine.
|
||||
*
|
||||
* If the User has enabled the Dynamic Vegetation Gem this method can be called
|
||||
* at runtime as CVegetation nodes appear within the Camera Frustum.
|
||||
*
|
||||
* @param worldTransform This transform includes the scale factor. It is the position of the root
|
||||
* of the CVegetation node.
|
||||
* @param worldAabb Axis Aligned Bounding Box in world coordinates of the CVegetation node.
|
||||
* @param callback The engine gives this callback to the TouchBending Gem for further communication.
|
||||
* @param callbackPrivateData The Engine gives this opaque handle to TouchBending Gem so the Gem it can properly address it
|
||||
* address the right Render Node instance when using the \p callback.
|
||||
* @returns An opaque handle of a TouchBending Trigger Instance created by the TouchBending Gem.
|
||||
*/
|
||||
virtual TouchBendingTriggerHandle* CreateTouchBendingTrigger(const AZ::Transform& worldTransform,
|
||||
const AZ::Aabb&worldAabb, ITouchBendingCallback* callback, const void * callbackPrivateData) = 0;
|
||||
|
||||
/** @brief Used by the engine to notify TouchBending Gem about the visibility status of the physicalized skeleton.
|
||||
*
|
||||
* @param skeletonHandle Opaque pointer to the physicalized skeleton created by TouchBending Gem.
|
||||
* @param isVisible if TRUE the engine finds out that the skeleton is visible. If FALSE the engine calculated
|
||||
* that the skeleton is either totally outside of the Camera Frustum or its distance
|
||||
* from the camera exceeds the CVAR e_CullVegActivation.
|
||||
* @param skeletonBoneCountOut It is the responsibility of the TouchBending Gem to fill this out
|
||||
* with the number of bones available for skinning.
|
||||
* @param triggerTouchCountOut It is the responsibility of the TouchBending Gem to fill this out
|
||||
* with the number of objects that are touching the touch bending trigger.
|
||||
* @returns void
|
||||
*/
|
||||
virtual void SetTouchBendingSkeletonVisibility(Physics::TouchBendingSkeletonHandle* skeletonHandle,
|
||||
bool isVisible, AZ::u32& skeletonBoneCountOut, AZ::u32& triggerTouchCountOut) = 0;
|
||||
|
||||
/** @brief The engine calls this when it is deleting the Render Node.
|
||||
*
|
||||
* When the User deletes an object via the Vegetation panel of the Rollup Bar (Legacy) UI
|
||||
* this method will be called by the engine.
|
||||
*
|
||||
* If the User has enabled the Dynamic Vegetation Gem this method can be called
|
||||
* at runtime as CVegetation nodes disappear from the Camera Frustum.
|
||||
*
|
||||
* @param handle Opaque handle of the TouchBending trigger instance as created by the
|
||||
* TouchBending Gem.
|
||||
* @returns void
|
||||
*/
|
||||
virtual void DeleteTouchBendingTrigger(TouchBendingTriggerHandle* handle) = 0;
|
||||
|
||||
|
||||
/** @brief The engine calls this to destroy a physicalized skeleton.
|
||||
*
|
||||
* The touch bending trigger remains active.
|
||||
* This means that in the future something may touch the trigger
|
||||
* and the skeleton is created again.
|
||||
*
|
||||
* @param skeletonHandle Opaque handle of the TouchBending Skeleton as created by the
|
||||
* TouchBending Gem. The skeleton will be removed from the Physics World.
|
||||
* @returns
|
||||
*/
|
||||
virtual void DephysicalizeTouchBendingSkeleton(TouchBendingSkeletonHandle* skeletonHandle) = 0;
|
||||
|
||||
|
||||
/** Reads the current position of the pair-of-joints per bone of the Skeleton into the \p jointPositions
|
||||
* buffer.
|
||||
*
|
||||
* @param skeletonHandle Opaque handle of the physicalized skeleton instance as created by the
|
||||
* TouchBending Gem.
|
||||
* @param jointPositions Buffer where the Top and Bottom Joint positions for each bone
|
||||
* is written to. Please read the documentation of "struct JointPositions" for clarification.
|
||||
* @returns void
|
||||
*/
|
||||
virtual void ReadJointPositionsOfSkeleton(TouchBendingSkeletonHandle* skeletonHandle, JointPositions* jointPositions) = 0;
|
||||
};
|
||||
using TouchBendingBus = AZ::EBus<TouchBendingRequest>;
|
||||
|
||||
/// A helper method to test if there's a Gem implementing the TouchBendingBus.
|
||||
AZ_INLINE bool IsTouchBendingEnabled()
|
||||
{
|
||||
bool isEnabled = false;
|
||||
TouchBendingBus::BroadcastResult(isEnabled, &TouchBendingBus::Events::IsTouchBendingEnabled);
|
||||
return isEnabled;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformXenia, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
|
||||
static const char* PlatformNames[PlatformId::NumPlatformIds] = { PlatformPC, PlatformES3, PlatformIOS, PlatformOSX, PlatformProvo, PlatformSalem, PlatformJasper, PlatformServer, PlatformAll, PlatformAllClient };
|
||||
|
||||
const char* PlatformIdToPalFolder(AzFramework::PlatformId platform)
|
||||
{
|
||||
@@ -35,8 +35,6 @@ namespace AzFramework
|
||||
return "iOS";
|
||||
case AzFramework::OSX:
|
||||
return "Mac";
|
||||
case AzFramework::XENIA:
|
||||
return "Xenia";
|
||||
case AzFramework::PROVO:
|
||||
return "Provo";
|
||||
case AzFramework::SALEM:
|
||||
@@ -84,10 +82,6 @@ namespace AzFramework
|
||||
{
|
||||
return PlatformSalem;
|
||||
}
|
||||
else if (osPlatform == PlatformCodeNameXenia)
|
||||
{
|
||||
return PlatformCodeNameXenia;
|
||||
}
|
||||
else if (osPlatform == PlatformCodeNameJasper)
|
||||
{
|
||||
return PlatformJasper;
|
||||
@@ -220,9 +214,6 @@ namespace AzFramework
|
||||
case PlatformId::OSX:
|
||||
platformCodes.emplace_back(PlatformCodeNameMac);
|
||||
break;
|
||||
case PlatformId::XENIA:
|
||||
platformCodes.emplace_back(PlatformCodeNameXenia);
|
||||
break;
|
||||
case PlatformId::PROVO:
|
||||
platformCodes.emplace_back(PlatformCodeNameProvo);
|
||||
break;
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace AzFramework
|
||||
constexpr char PlatformES3[] = "es3";
|
||||
constexpr char PlatformIOS[] = "ios";
|
||||
constexpr char PlatformOSX[] = "osx_gl";
|
||||
constexpr char PlatformXenia[] = "xenia";
|
||||
constexpr char PlatformProvo[] = "provo";
|
||||
constexpr char PlatformSalem[] = "salem";
|
||||
constexpr char PlatformJasper[] = "jasper";
|
||||
@@ -39,7 +38,6 @@ namespace AzFramework
|
||||
constexpr char PlatformCodeNameAndroid[] = "Android";
|
||||
constexpr char PlatformCodeNameiOS[] = "iOS";
|
||||
constexpr char PlatformCodeNameMac[] = "Mac";
|
||||
constexpr char PlatformCodeNameXenia[] = "Xenia";
|
||||
constexpr char PlatformCodeNameProvo[] = "Provo";
|
||||
constexpr char PlatformCodeNameSalem[] = "Salem";
|
||||
constexpr char PlatformCodeNameJasper[] = "Jasper";
|
||||
@@ -57,7 +55,6 @@ namespace AzFramework
|
||||
ES3,
|
||||
IOS,
|
||||
OSX,
|
||||
XENIA,
|
||||
PROVO,
|
||||
SALEM,
|
||||
JASPER,
|
||||
@@ -68,7 +65,7 @@ namespace AzFramework
|
||||
// Add new platforms above this
|
||||
NumPlatformIds
|
||||
);
|
||||
constexpr int NumClientPlatforms = 8;
|
||||
constexpr int NumClientPlatforms = 7;
|
||||
constexpr int NumPlatforms = NumClientPlatforms + 1; // 1 "Server" platform currently
|
||||
enum class PlatformFlags : AZ::u32
|
||||
{
|
||||
@@ -77,7 +74,6 @@ namespace AzFramework
|
||||
Platform_ES3 = 1 << PlatformId::ES3,
|
||||
Platform_IOS = 1 << PlatformId::IOS,
|
||||
Platform_OSX = 1 << PlatformId::OSX,
|
||||
Platform_XENIA = 1 << PlatformId::XENIA,
|
||||
Platform_PROVO = 1 << PlatformId::PROVO,
|
||||
Platform_SALEM = 1 << PlatformId::SALEM,
|
||||
Platform_JASPER = 1 << PlatformId::JASPER,
|
||||
@@ -89,7 +85,7 @@ namespace AzFramework
|
||||
// A special platform that will always correspond to all non-server platforms, even if new ones are added
|
||||
Platform_ALL_CLIENT = 1ULL << 31,
|
||||
|
||||
AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_XENIA | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
|
||||
AllNamedPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER | Platform_SERVER,
|
||||
};
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(PlatformFlags);
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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/ProjectManager/ProjectManager.h>
|
||||
#include <AzFramework/Engine/Engine.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
namespace ProjectManager
|
||||
{
|
||||
// Check if any project path appears to have been provided on the command line
|
||||
bool HasCommandLineProjectName(const int argc, char* argv[])
|
||||
{
|
||||
constexpr int numOptionPrefixes = 3;
|
||||
static const char* optionPrefixes[numOptionPrefixes] = { "/", "--", "-" };
|
||||
constexpr int numOptionNames = 2;
|
||||
static const char* optionNames[numOptionNames] = { "projectpath", R"(regset="/Amazon/AzCore/Bootstrap/sys_game_folder)" };
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
int thisPrefix = 0;
|
||||
for (; thisPrefix < numOptionPrefixes; ++thisPrefix)
|
||||
{
|
||||
if (strncmp(argv[i], optionPrefixes[thisPrefix], strlen(optionPrefixes[thisPrefix])) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the argument doesn't start with any of our switch start parameters, this isn't an argument giving us a project
|
||||
if (thisPrefix == numOptionPrefixes)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// We compare the portion of the string after our prefix
|
||||
int startIndex = strlen(optionPrefixes[thisPrefix]);
|
||||
// If the whole argument was just one of the prefixes, this also isn't what we were looking for
|
||||
if (startIndex == strlen(argv[i]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int switchNum = 0;
|
||||
for (; switchNum < numOptionNames; ++switchNum)
|
||||
{
|
||||
// Start the string comparison at startIndex for each string - after the option indicator
|
||||
if (azstrnicmp(&argv[i][startIndex], optionNames[switchNum], strlen(optionNames[switchNum])) == 0)
|
||||
{
|
||||
int expectedOptionLength = strlen(optionNames[switchNum]) + startIndex;
|
||||
// The option is what we're looking for if it had a space after it (it was the whole argument) or it has an equals next
|
||||
if (strlen(argv[i]) == (expectedOptionLength) || ((strlen(argv[i]) > expectedOptionLength ) && argv[i][expectedOptionLength] == '='))
|
||||
{
|
||||
// We found one of the acceptable arguments
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Check for a project name, if not found, attempt to launch project manager and shut down
|
||||
ProjectPathCheckResult CheckProjectPathProvided(const int argc, char* argv[])
|
||||
{
|
||||
// If we were able to locate a path to a project, we're done
|
||||
if (HasProjectName(argc, argv))
|
||||
{
|
||||
return ProjectPathCheckResult::ProjectPathFound;
|
||||
}
|
||||
|
||||
if (LaunchProjectManager())
|
||||
{
|
||||
AZ_TracePrintf("ProjectManager", "Project Manager launched successfully, requesting exit.");
|
||||
return ProjectPathCheckResult::ProjectManagerLaunched;
|
||||
}
|
||||
AZ_Error("ProjectManager", false, "Project Manager failed to launch and no project selected!");
|
||||
return ProjectPathCheckResult::ProjectManagerLaunchFailed;
|
||||
}
|
||||
|
||||
} // ProjectManager
|
||||
|
||||
bool ProjectManager::HasProjectName(const int argc, char* argv[])
|
||||
{
|
||||
return HasCommandLineProjectName(argc, argv) || HasBootstrapProjectName();
|
||||
}
|
||||
|
||||
// This is a transition method until all projects/tests/etc have been converted to expect and use the projectpath parameter
|
||||
// If bootstrap.cfg exists and has a valid project name we should assume we're launching using it
|
||||
// After that time it can be removed
|
||||
bool ProjectManager::HasBootstrapProjectName(AZStd::string_view projectFolder)
|
||||
{
|
||||
AZ::IO::FixedMaxPath enginePath = Engine::FindEngineRoot(projectFolder);
|
||||
if (enginePath.empty())
|
||||
{
|
||||
AZ_Warning("ProjectManager", false, "Couldn't find engine root");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bootstrapPath = enginePath / "bootstrap.cfg";
|
||||
|
||||
if (!AZ::IO::SystemFile::Exists(bootstrapPath.c_str()))
|
||||
{
|
||||
AZ_Warning("ProjectManager", false, "No bootstrap file found at %s", bootstrapPath.c_str());
|
||||
return false;
|
||||
}
|
||||
AZStd::fixed_string< MaxBootstrapFileSize> bootstrapString;
|
||||
auto fileSize = AZ::IO::SystemFile::Length(bootstrapPath.c_str());
|
||||
if (fileSize >= MaxBootstrapFileSize)
|
||||
{
|
||||
AZ_Warning("ProjectManager", false, "Bootstrap read size at %s is %zu", bootstrapPath.c_str(), fileSize);
|
||||
bootstrapString.resize_no_construct(MaxBootstrapFileSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
bootstrapString.resize_no_construct(fileSize);
|
||||
}
|
||||
AZ::IO::SystemFile::SizeType bytesRead = AZ::IO::SystemFile::Read(bootstrapPath.c_str(), bootstrapString.data(), MaxBootstrapFileSize - 1);
|
||||
if (bytesRead == (MaxBootstrapFileSize - 1))
|
||||
{
|
||||
AZ_Warning("ProjectManager", false, "Bootstrap read size at %s was %zu", bootstrapPath.c_str(), bytesRead);
|
||||
}
|
||||
if (!ContentHasProjectName(bootstrapString))
|
||||
{
|
||||
AZ_TracePrintf("ProjectManager", "Bootstrap at %s did not contain project name", bootstrapPath.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is a transition method until all projects/tests/etc have been converted to expect and use the projectpath parameter
|
||||
// If bootstrap.cfg exists and has a valid project name we should assume we're launching using it
|
||||
// After that time it can be removed
|
||||
bool ProjectManager::ContentHasProjectName(AZStd::fixed_string< MaxBootstrapFileSize>& bootstrapString)
|
||||
{
|
||||
static const char* const projectKey = "sys_game_folder";
|
||||
size_t searchStart = bootstrapString.find(projectKey);
|
||||
while (searchStart != bootstrapString.npos)
|
||||
{
|
||||
// Once we've found the key we need to search the line forward and backwards. Commented out lines shouldn't count
|
||||
// and if there's no value after the equals then it's also not set
|
||||
auto checkPos = searchStart;
|
||||
// We're at the start already if this is position 0
|
||||
bool foundLineStart = checkPos == 0;
|
||||
if (checkPos)
|
||||
{
|
||||
--checkPos;
|
||||
}
|
||||
while (checkPos > 0 && bootstrapString[checkPos] != '-')
|
||||
{
|
||||
if (bootstrapString[checkPos] == '\n')
|
||||
{
|
||||
// Looks like a valid key
|
||||
foundLineStart = true;
|
||||
break;
|
||||
}
|
||||
if (!std::isspace(bootstrapString[checkPos]))
|
||||
{
|
||||
// This appears to be some other character appearing before our key, this isn't valid
|
||||
break;
|
||||
}
|
||||
--checkPos;
|
||||
}
|
||||
if (!foundLineStart)
|
||||
{
|
||||
// Commented line or other content preceding our key, keep searching
|
||||
searchStart = bootstrapString.find(projectKey, searchStart + 1);
|
||||
continue;
|
||||
}
|
||||
checkPos = searchStart + strlen(projectKey);
|
||||
bool foundEquals = false;
|
||||
while (checkPos < bootstrapString.length())
|
||||
{
|
||||
if (bootstrapString[checkPos] == '\n')
|
||||
{
|
||||
// We've reached the end of the line and didn't find anything that seems to be a value for our key
|
||||
break;
|
||||
}
|
||||
if (std::isspace(bootstrapString[checkPos]))
|
||||
{
|
||||
// Whitespace - keep searching back
|
||||
++checkPos;
|
||||
continue;
|
||||
}
|
||||
if (bootstrapString[checkPos] == '=')
|
||||
{
|
||||
foundEquals = true;
|
||||
++checkPos;
|
||||
continue;
|
||||
}
|
||||
if (foundEquals)
|
||||
{
|
||||
auto nameEnd = bootstrapString.find_first_of(" \n", checkPos);
|
||||
if (nameEnd == bootstrapString.npos)
|
||||
{
|
||||
// End of content, this is valid
|
||||
nameEnd = bootstrapString.length();
|
||||
}
|
||||
constexpr size_t nameMax = 100;
|
||||
if (nameEnd - checkPos > nameMax)
|
||||
{
|
||||
AZ_Warning("ProjectManager", false, "Project name exceeded %zu characters (%zu)", nameMax, nameEnd - checkPos);
|
||||
return false;
|
||||
}
|
||||
AZStd::fixed_string<nameMax + 1> projectName(&bootstrapString[checkPos], nameEnd - checkPos);
|
||||
AZ_TracePrintf("ProjectManager", "Found project name of %s", projectName.c_str());
|
||||
// This is not a space, we've found our key, and we've found some sort of non space entry, we count this as "it looks like we have a value entered"
|
||||
return true;
|
||||
}
|
||||
// there was some other content on this line after our key before the equals that was not a space, this isn't our key
|
||||
searchStart = bootstrapString.find(projectKey, searchStart + 1);
|
||||
break;
|
||||
}
|
||||
searchStart = bootstrapString.find(projectKey, searchStart + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // AzFramework
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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/IO/SystemFile.h>
|
||||
#include <AzCore/std/string/fixed_string.h>
|
||||
#include <AzCore/std/string/string_view.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
namespace ProjectManager
|
||||
{
|
||||
constexpr AZ::IO::SystemFile::SizeType MaxBootstrapFileSize = 1024 * 10;
|
||||
|
||||
// Check if any project name can be found anywhere
|
||||
bool HasProjectName(const int argc, char* argv[]);
|
||||
// Check if any project name can be found on the command line
|
||||
bool HasCommandLineProjectName(const int argc, char* argv[]);
|
||||
// Check if a relative project is being used through bootstrap
|
||||
bool HasBootstrapProjectName(AZStd::string_view projectFolder = {});
|
||||
// Search content for project name key
|
||||
bool ContentHasProjectName(AZStd::fixed_string< MaxBootstrapFileSize>& bootstrapString);
|
||||
enum class ProjectPathCheckResult
|
||||
{
|
||||
ProjectManagerLaunchFailed = -1,
|
||||
ProjectManagerLaunched = 0,
|
||||
ProjectPathFound = 1
|
||||
};
|
||||
// Check for a project name, if not found, attempts to launch project manager and returns false
|
||||
ProjectPathCheckResult CheckProjectPathProvided(const int argc, char* argv[]);
|
||||
// Attempt to Launch the project manager. Requires locating the engine root, project manager script, and python.
|
||||
bool LaunchProjectManager();
|
||||
}
|
||||
} // AzFramework
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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/RTTI/ReflectContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
Spawnable::Spawnable(const AZ::Data::AssetId& id)
|
||||
: AZ::Data::AssetData(id)
|
||||
{
|
||||
}
|
||||
|
||||
Spawnable::Spawnable(Spawnable&& other)
|
||||
: m_entities(AZStd::move(other.m_entities))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Spawnable& Spawnable::operator=(Spawnable&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
m_entities = AZStd::move(other.m_entities);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Spawnable::EntityList& Spawnable::GetEntities() const
|
||||
{
|
||||
return m_entities;
|
||||
}
|
||||
|
||||
Spawnable::EntityList& Spawnable::GetEntities()
|
||||
{
|
||||
return m_entities;
|
||||
}
|
||||
|
||||
bool Spawnable::IsEmpty() const
|
||||
{
|
||||
return m_entities.empty();
|
||||
}
|
||||
|
||||
SpawnableMetaData& Spawnable::GetMetaData()
|
||||
{
|
||||
return m_metaData;
|
||||
}
|
||||
|
||||
const SpawnableMetaData& Spawnable::GetMetaData() const
|
||||
{
|
||||
return m_metaData;
|
||||
}
|
||||
|
||||
void Spawnable::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
|
||||
{
|
||||
serializeContext->Class<Spawnable, AZ::Data::AssetData>()->Version(1)
|
||||
->Field("Meta data", &Spawnable::m_metaData)
|
||||
->Field("Entities", &Spawnable::m_entities);
|
||||
}
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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/Entity.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzFramework/Spawnable/SpawnableMetaData.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
class ReflectContext;
|
||||
|
||||
class Spawnable final
|
||||
: public AZ::Data::AssetData
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(Spawnable, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AzFramework::Spawnable, "{855E3021-D305-4845-B284-20C3F7FDF16B}", AZ::Data::AssetData);
|
||||
|
||||
using EntityList = AZStd::vector<AZStd::unique_ptr<AZ::Entity>>;
|
||||
|
||||
inline static constexpr const char* FileExtension = "spawnable";
|
||||
|
||||
Spawnable() = default;
|
||||
explicit Spawnable(const AZ::Data::AssetId& id);
|
||||
Spawnable(const Spawnable& rhs) = delete;
|
||||
Spawnable(Spawnable&& other);
|
||||
~Spawnable() override = default;
|
||||
|
||||
Spawnable& operator=(const Spawnable& rhs) = delete;
|
||||
Spawnable& operator=(Spawnable&& other);
|
||||
|
||||
const EntityList& GetEntities() const;
|
||||
EntityList& GetEntities();
|
||||
bool IsEmpty() const;
|
||||
|
||||
SpawnableMetaData& GetMetaData();
|
||||
const SpawnableMetaData& GetMetaData() const;
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
private:
|
||||
SpawnableMetaData m_metaData;
|
||||
|
||||
// Container for keeping all entities of the prefab the Spawnable was created from.
|
||||
// Includes both direct and nested entities of the prefab.
|
||||
EntityList m_entities;
|
||||
};
|
||||
|
||||
using SpawnableList = AZStd::vector<Spawnable>;
|
||||
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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/Serialization/Utils.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
|
||||
#include <AzFramework/Spawnable/Spawnable.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
SpawnableAssetHandler::SpawnableAssetHandler()
|
||||
{
|
||||
AZ::AssetTypeInfoBus::MultiHandler::BusConnect(AZ::AzTypeInfo<Spawnable>::Uuid());
|
||||
}
|
||||
|
||||
SpawnableAssetHandler::~SpawnableAssetHandler()
|
||||
{
|
||||
AZ::AssetTypeInfoBus::MultiHandler::BusDisconnect();
|
||||
}
|
||||
|
||||
AZ::Data::AssetPtr SpawnableAssetHandler::CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type)
|
||||
{
|
||||
AZ_Assert(type == AZ::AzTypeInfo<Spawnable>::Uuid(),
|
||||
"Asset handler for Spawnable was given a type that's not a Spawnable: %s", type.ToString<AZStd::string>().c_str());
|
||||
return aznew Spawnable(id);
|
||||
}
|
||||
|
||||
void SpawnableAssetHandler::DestroyAsset(AZ::Data::AssetPtr ptr)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
void SpawnableAssetHandler::GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes)
|
||||
{
|
||||
assetTypes.push_back(AZ::AzTypeInfo<Spawnable>::Uuid());
|
||||
}
|
||||
|
||||
auto SpawnableAssetHandler::LoadAssetData(
|
||||
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
|
||||
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
|
||||
const AZ::Data::AssetFilterCB& assetLoadFilterCB) -> LoadResult
|
||||
{
|
||||
Spawnable* spawnable = asset.GetAs<Spawnable>();
|
||||
AZ_Assert(spawnable, "Loaded asset data handed to the SpawnableAssetHandler didn't contain a Spawanble.");
|
||||
|
||||
AZ::ObjectStream::FilterDescriptor filter(assetLoadFilterCB);
|
||||
if (AZ::Utils::LoadObjectFromStreamInPlace(*stream, *spawnable, nullptr /*SerializeContext*/, filter))
|
||||
{
|
||||
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Spawnable", false, "Failed to deserialize asset %s.", asset->GetId().ToString<AZStd::string>().c_str());
|
||||
return AZ::Data::AssetHandler::LoadResult::Error;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Data::AssetType SpawnableAssetHandler::GetAssetType() const
|
||||
{
|
||||
return AZ::AzTypeInfo<Spawnable>::Uuid();
|
||||
}
|
||||
|
||||
const char* SpawnableAssetHandler::GetAssetTypeDisplayName() const
|
||||
{
|
||||
return "Spawnable";
|
||||
}
|
||||
|
||||
const char* SpawnableAssetHandler::GetGroup() const
|
||||
{
|
||||
return "Prefab";
|
||||
}
|
||||
|
||||
const char* SpawnableAssetHandler::GetBrowserIcon() const
|
||||
{
|
||||
return "Editor/Icons/Components/Viewport/EntityInSlice.png";
|
||||
}
|
||||
|
||||
void SpawnableAssetHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions)
|
||||
{
|
||||
extensions.push_back(Spawnable::FileExtension);
|
||||
}
|
||||
} // namespace AzFramework
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetTypeInfoBus.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
class SpawnableAssetHandler final
|
||||
: public AZ::Data::AssetHandler
|
||||
, public AZ::AssetTypeInfoBus::MultiHandler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(SpawnableAssetHandler, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AZ::SpawnableAssetHandler, "{BF6E2D17-87C9-4BB1-A205-3656CF6D551D}", AZ::Data::AssetHandler);
|
||||
|
||||
SpawnableAssetHandler();
|
||||
~SpawnableAssetHandler() override;
|
||||
|
||||
//
|
||||
// AssetHandler
|
||||
//
|
||||
|
||||
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override;
|
||||
void DestroyAsset(AZ::Data::AssetPtr ptr) override;
|
||||
void GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) override;
|
||||
|
||||
|
||||
//
|
||||
// AssetTypeInfoBus
|
||||
//
|
||||
|
||||
AZ::Data::AssetType GetAssetType() const override;
|
||||
const char* GetAssetTypeDisplayName() const override;
|
||||
const char* GetGroup() const override;
|
||||
const char* GetBrowserIcon() const override;
|
||||
void GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) override;
|
||||
|
||||
protected:
|
||||
LoadResult LoadAssetData(
|
||||
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
|
||||
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
|
||||
const AZ::Data::AssetFilterCB& assetLoadFilterCB) override;
|
||||
};
|
||||
} // namespace AzFramework
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user