merge from main

This commit is contained in:
greerdv
2021-05-19 12:14:25 +01:00
11816 changed files with 189923 additions and 1002401 deletions
-1
View File
@@ -1 +0,0 @@
*.xml
@@ -417,4 +417,4 @@ namespace AZ
return true;
}
}
}
}
@@ -219,4 +219,4 @@ namespace AZ
bool m_isRunning; //!< Internal flag indicating if the application is running, mainly used to determine if we shoudl be blocking on the event pump while paused
};
} // namespace Android
} // namespace AZ
} // namespace AZ
@@ -485,4 +485,4 @@ namespace AZ { namespace Android
} // namespace AZ
#include <AzCore/Android/JNI/Internal/Object_impl.h>
#include <AzCore/Android/JNI/Internal/Object_impl.h>
@@ -18,6 +18,7 @@
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Preprocessor/Enum.h>
#include <AzCore/std/containers/bitset.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/string_view.h>
@@ -216,16 +217,14 @@ namespace AZ
/**
* Setting for each reference (Asset<T>) to control loading of referenced assets during serialization.
*/
enum class AssetLoadBehavior : u8
{
PreLoad = 0, ///< Serializer will "Pre load" dependencies, asset containers may load in parallel but will not signal AssetReady
QueueLoad = 1, ///< Serializer will queue an asynchronous load of the referenced asset and return the object to the user. User code should use the \ref AZ::Data::AssetBus to monitor for when it's ready.
NoLoad = 2, ///< Serializer will load reference information, but asset loading will be left to the user. User code should call Asset<T>::QueueLoad and use the \ref AZ::Data::AssetBus to monitor for when it's ready.
///< AssetContainers will skip NoLoad dependencies
AZ_ENUM_WITH_UNDERLYING_TYPE(AssetLoadBehavior, u8,
(PreLoad, 0), ///< Serializer will "Pre load" dependencies, asset containers may load in parallel but will not signal AssetReady
(QueueLoad, 1), ///< Serializer will queue an asynchronous load of the referenced asset and return the object to the user. User code should use the \ref AZ::Data::AssetBus to monitor for when it's ready.
(NoLoad, 2), ///< Serializer will load reference information, but asset loading will be left to the user. User code should call Asset<T>::QueueLoad and use the \ref AZ::Data::AssetBus to monitor for when it's ready.
///< AssetContainers will skip NoLoad dependencies
Count,
Default = QueueLoad,
};
(Default, QueueLoad)
);
struct AssetFilterInfo
{
@@ -308,6 +307,8 @@ namespace AZ
Asset(AssetLoadBehavior loadBehavior = AssetLoadBehavior::Default);
/// Create an asset from a valid asset data (created asset), might not be loaded or currently loading.
Asset(AssetData* assetData, AssetLoadBehavior loadBehavior);
/// Create an asset from a valid asset data (created asset) and set the asset id for both, might not be loaded or currently loading.
Asset(const AZ::Data::AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior);
/// Initialize asset pointer with id, type, and hint. No data construction will occur until QueueLoad is called.
Asset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint = AZStd::string());
@@ -788,6 +789,18 @@ namespace AZ
SetData(assetData);
}
//=========================================================================
template<class T>
Asset<T>::Asset(const AssetId& id, AssetData* assetData, AssetLoadBehavior loadBehavior)
: m_assetId(id)
, m_assetType(azrtti_typeid<T>())
, m_loadBehavior(loadBehavior)
{
AZ_Assert(!assetData->m_assetId.IsValid(), "Asset data already has an ID set.");
assetData->m_assetId = id;
SetData(assetData);
}
//=========================================================================
template<class T>
Asset<T>::Asset(const AssetId& id, const AZ::Data::AssetType& type, const AZStd::string& hint)
@@ -1104,8 +1117,11 @@ namespace AZ
// If we either already had valid asset data, or just created it via FindOrCreateAsset, try to queue the load.
if (m_assetData && m_assetData->GetId().IsValid())
{
// Only try to queue if the asset isn't already loading or loaded.
if (m_assetData->GetStatus() == AZ::Data::AssetData::AssetStatus::NotLoaded)
// Try to queue if the asset isn't already loading or loaded.
// Also try to queue if the asset *is* already loading or loaded, but we're the only one with a strong reference
// (i.e. use count == 1), because that means it was in the process of being garbage-collected.
if ((m_assetData->GetStatus() == AZ::Data::AssetData::AssetStatus::NotLoaded) ||
(m_assetData->GetUseCount() == 1))
{
*this = AssetInternal::GetAsset(m_assetData->GetId(), m_assetData->GetType(), loadBehavior, loadParams);
}
@@ -1219,6 +1235,7 @@ namespace AZ
} // namespace ProductDependencyInfo
} // namespace Data
AZ_TYPE_INFO_SPECIALIZE(Data::AssetLoadBehavior, "{DAF9ECED-FEF3-4D7A-A220-8CFD6A5E6DA1}");
AZ_TYPE_INFO_TEMPLATE_WITH_NAME(AZ::Data::Asset, "Asset", "{C891BF19-B60C-45E2-BFD0-027D15DDC939}", AZ_TYPE_INFO_CLASS);
} // namespace AZ
@@ -239,8 +239,13 @@ namespace AZ
return;
}
CheckReady();
m_initComplete = true;
// *After* setting initComplete to true, check to see if the assets are already ready.
// This check needs to wait until after setting initComplete because if they *are* ready, we want the final call to
// RemoveWaitingAsset to trigger the OnAssetContainerReady/Canceled event. If we call CheckReady() *before* setting
// initComplete, if all the assets are ready, the event will never get triggered.
CheckReady();
}
bool AssetContainer::IsReady() const
@@ -255,7 +260,7 @@ namespace AZ
bool AssetContainer::IsValid() const
{
return (m_containerAssetId.IsValid() && m_initComplete);
return (m_containerAssetId.IsValid() && m_initComplete && m_rootAsset);
}
void AssetContainer::CheckReady()
@@ -264,13 +269,13 @@ namespace AZ
{
for (auto& [assetId, dependentAsset] : m_dependencies)
{
if (dependentAsset->IsReady())
if (dependentAsset->IsReady() || dependentAsset->IsError())
{
HandleReadyAsset(dependentAsset);
}
}
}
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady())
if (auto asset = m_rootAsset.GetStrongReference(); asset.IsReady() || asset.IsError())
{
HandleReadyAsset(asset);
}
@@ -341,6 +346,7 @@ namespace AZ
void AssetContainer::OnAssetError(Asset<AssetData> asset)
{
AZ_Warning("AssetContainer", false, "Error loading asset %s", asset->GetId().ToString<AZStd::string>().c_str());
HandleReadyAsset(asset);
}
@@ -366,7 +372,10 @@ namespace AZ
auto remainingPreloadIter = m_preloadList.find(waiterId);
if (remainingPreloadIter == m_preloadList.end())
{
AZ_Warning("AssetContainer", !m_initComplete, "Couldn't find waiting list for %s", waiterId.ToString<AZStd::string>().c_str());
// If we got here without an entry on the preload list, it probably means this asset was triggered to load multiple
// times, some with dependencies and some without. To ensure that we don't disturb the loads that expect the
// dependencies, just silently return and don't treat the asset as finished loading. We'll rely on the other load
// to send an OnAssetReady() whenever its expected dependencies are met.
return;
}
if (!remainingPreloadIter->second.erase(preloadID))
@@ -487,10 +496,10 @@ namespace AZ
m_waitingCount -= 1;
disconnectEbus = true;
if (m_waitingAssets.empty())
{
allReady = true;
}
}
if (m_waitingAssets.empty())
{
allReady = true;
}
}
@@ -501,8 +510,15 @@ namespace AZ
}
}
if (allReady && m_initComplete)
// If there are no assets left to be loaded, trigger the final AssetContainer notification (ready or canceled).
// We guard against prematurely sending it (m_initComplete) because it's possible for assets to get removed from our waiting
// list *while* we're still building up the list, so the list would appear to be empty too soon.
// We also guard against sending it multiple times (m_finalNotificationSent), because in some error conditions, it may be
// possible to try to remove the same asset multiple times, which if it's the last asset, it could trigger multiple
// notifications.
if (allReady && m_initComplete && !m_finalNotificationSent)
{
m_finalNotificationSent = true;
if (m_rootAsset)
{
AssetManagerBus::Broadcast(&AssetManagerBus::Events::OnAssetContainerReady, this);
@@ -610,7 +626,12 @@ namespace AZ
}
for(auto& thisList : preloadList)
{
m_preloadList[thisList.first].insert(thisList.second.begin(), thisList.second.end());
// Only save the entry to the final preload list if it has at least one dependent asset still remaining after
// the checks above.
if (!thisList.second.empty())
{
m_preloadList[thisList.first].insert(thisList.second.begin(), thisList.second.end());
}
}
}
}
@@ -137,6 +137,7 @@ namespace AZ
AZStd::atomic_int m_invalidDependencies{ 0 };
AZStd::unordered_set<AZ::Data::AssetId> m_unloadedDependencies;
AZStd::atomic_bool m_initComplete{ false };
AZStd::atomic_bool m_finalNotificationSent{false};
mutable AZStd::recursive_mutex m_preloadMutex;
// AssetId -> List of assets it is still waiting on
@@ -208,6 +208,7 @@ namespace AZ::Data
void AssetDataStream::Close()
{
AZ_Assert(m_isOpen, "Attempting to close a stream that hasn't been opened.");
AZ_Assert(m_curReadRequest == nullptr, "Attempting to close a stream with a read request in flight.");
// Destroy the asset buffer and unlock the allocator, so the allocator itself knows that it is no longer needed.
if (m_buffer != m_preloadedData.data())
@@ -222,6 +223,16 @@ namespace AZ::Data
AZ_PROFILE_INTERVAL_END(AZ::Debug::ProfileCategory::AzCore, this);
}
void AssetDataStream::RequestCancel()
{
AZStd::scoped_lock<AZStd::mutex> lock(m_readRequestMutex);
if (m_curReadRequest)
{
auto streamer = Interface<IO::IStreamer>::Get();
m_curReadRequest = streamer->Cancel(m_curReadRequest);
}
}
void AssetDataStream::Seek(AZ::IO::OffsetType bytes, AZ::IO::GenericStream::SeekMode mode)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzCore);
@@ -82,6 +82,10 @@ namespace AZ::Data
//! Gets the size of data loaded (so far).
size_t GetLoadedSize() const { return m_loadedSize; }
//! Request a cancellation of any current IO streamer requests.
//! Note: This is asynchronous and not guaranteed to cancel if the request is already in-process.
void RequestCancel();
private:
//! Perform any operations needed by all variants of Open()
void OpenInternal(size_t assetSize, const char* streamName);
@@ -28,6 +28,8 @@ namespace AZ::Data::AssetInternal
class WeakAsset
{
public:
static constexpr bool EnableAssetCancellation = false;
WeakAsset() = default;
WeakAsset(AssetData* assetData, AssetLoadBehavior assetReferenceLoadBehavior);
@@ -111,7 +113,14 @@ namespace AZ::Data::AssetInternal
// - If the left and right sides are the same, clearing the right side's reference means one less reference will exist
if (m_assetData)
{
m_assetData->ReleaseWeak();
if constexpr (EnableAssetCancellation)
{
m_assetData->ReleaseWeak();
}
else
{
m_assetData->Release();
}
}
m_assetData = AZStd::move(rhs.m_assetData);
rhs.m_assetData = nullptr;
@@ -141,13 +150,27 @@ namespace AZ::Data::AssetInternal
if (assetData)
{
assetData->AcquireWeak();
if constexpr (EnableAssetCancellation)
{
assetData->AcquireWeak();
}
else
{
assetData->Acquire();
}
m_assetId = assetData->GetId();
}
if (m_assetData)
{
m_assetData->ReleaseWeak();
if constexpr (EnableAssetCancellation)
{
m_assetData->ReleaseWeak();
}
else
{
m_assetData->Release();
}
}
m_assetData = assetData;
@@ -70,6 +70,17 @@ namespace AZ
}
}
{
const AZ::Data::AssetLoadBehavior autoLoadBehavior = instance->GetAutoLoadBehavior();
const AZ::Data::AssetLoadBehavior defaultAutoLoadBehavior = defaultInstance ?
defaultInstance->GetAutoLoadBehavior() : AZ::Data::AssetLoadBehavior::Default;
result.Combine(
ContinueStoringToJsonObjectField(outputValue, "loadBehavior",
&autoLoadBehavior, &defaultAutoLoadBehavior,
azrtti_typeid<Data::AssetLoadBehavior>(), context));
}
{
ScopedContextPath subPathHint(context, "m_assetHint");
const AZStd::string* hint = &instance->GetHint();
@@ -100,14 +111,28 @@ namespace AZ
AssetId id;
JSR::ResultCode result(JSR::Tasks::ReadField);
SerializedAssetTracker* assetTracker =
context.GetMetadata().Find<SerializedAssetTracker>();
{
Data::AssetLoadBehavior loadBehavior = instance->GetAutoLoadBehavior();
result =
ContinueLoadingFromJsonObjectField(&loadBehavior,
azrtti_typeid<Data::AssetLoadBehavior>(),
inputValue, "loadBehavior", context);
instance->SetAutoLoadBehavior(loadBehavior);
}
auto it = inputValue.FindMember("assetId");
if (it != inputValue.MemberEnd())
{
ScopedContextPath subPath(context, "assetId");
result = ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context);
result.Combine(ContinueLoading(&id, azrtti_typeid<AssetId>(), it->value, context));
if (!id.m_guid.IsNull())
{
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), AssetLoadBehavior::NoLoad);
*instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior());
result.Combine(context.Report(result, "Successfully created Asset<T> with id."));
@@ -142,6 +167,11 @@ namespace AZ
"The asset hint is missing for Asset<T>, so it will be left empty."));
}
if (assetTracker)
{
assetTracker->AddAsset(*instance);
}
bool success = result.GetOutcome() <= JSR::Outcomes::PartialSkip;
bool defaulted = result.GetOutcome() == JSR::Outcomes::DefaultsUsed || result.GetOutcome() == JSR::Outcomes::PartialDefaults;
AZStd::string_view message =
@@ -150,5 +180,20 @@ namespace AZ
"Not enough information was available to create an instance of Asset<T> or data was corrupted.";
return context.Report(result, message);
}
void SerializedAssetTracker::AddAsset(Asset<AssetData>& asset)
{
m_serializedAssets.emplace_back(asset);
}
const AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets() const
{
return m_serializedAssets;
}
AZStd::vector<Asset<AssetData>>& SerializedAssetTracker::GetTrackedAssets()
{
return m_serializedAssets;
}
} // namespace Data
} // namespace AZ
@@ -13,6 +13,7 @@
#pragma once
#include <AzCore/Memory/Memory.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
namespace AZ
@@ -37,5 +38,18 @@ namespace AZ
private:
JsonSerializationResult::Result LoadAsset(void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context);
};
class SerializedAssetTracker final
{
public:
AZ_RTTI(SerializedAssetTracker, "{1E067091-8C0A-44B1-A455-6E97663F6963}");
void AddAsset(Asset<AssetData>& asset);
AZStd::vector<Asset<AssetData>>& GetTrackedAssets();
const AZStd::vector<Asset<AssetData>>& GetTrackedAssets() const;
private:
AZStd::vector<Asset<AssetData>> m_serializedAssets;
};
} // namespace Data
} // namespace AZ
@@ -1454,32 +1454,48 @@ namespace AZ
//=========================================================================
void AssetManager::ReloadAssetFromData(const Asset<AssetData>& asset)
{
AZ_Assert(asset.Get(), "Asset data for reload is missing.");
AZStd::scoped_lock<AZStd::recursive_mutex> assetLock(m_assetMutex);
AZ_Assert(m_assets.find(asset.GetId()) != m_assets.end(), "Unable to reload asset %s because its not in the AssetManager's asset list.", asset.ToString<AZStd::string>().c_str());
AZ_Assert(m_assets.find(asset.GetId()) == m_assets.end() || asset->RTTI_GetType() == m_assets.find(asset.GetId())->second->RTTI_GetType(),
"New and old data types are mismatched!");
bool shouldAssignAssetData = false;
auto found = m_assets.find(asset.GetId());
if ((found == m_assets.end()) || (asset->RTTI_GetType() != found->second->RTTI_GetType()))
{
return; // this will just lead to crashes down the line and the above asserts cover this.
}
AZ_Assert(asset.Get(), "Asset data for reload is missing.");
AZStd::scoped_lock<AZStd::recursive_mutex> assetLock(m_assetMutex);
AZ_Assert(
m_assets.find(asset.GetId()) != m_assets.end(),
"Unable to reload asset %s because it's not in the AssetManager's asset list.", asset.ToString<AZStd::string>().c_str());
AZ_Assert(
m_assets.find(asset.GetId()) == m_assets.end() ||
asset->RTTI_GetType() == m_assets.find(asset.GetId())->second->RTTI_GetType(),
"New and old data types are mismatched!");
AssetData* newData = asset.Get();
if (found->second != newData)
{
// Notify users that we are about to change asset
AssetBus::Event(asset.GetId(), &AssetBus::Events::OnAssetPreReload, asset);
// Resolve the asset handler and account for the new asset instance.
auto found = m_assets.find(asset.GetId());
if ((found == m_assets.end()) || (asset->RTTI_GetType() != found->second->RTTI_GetType()))
{
AssetHandlerMap::iterator handlerIt = m_handlers.find(newData->GetType());
AZ_Assert(handlerIt != m_handlers.end(), "No handler was registered for this asset [type:%s id:%s]!",
newData->GetType().ToString<AZ::OSString>().c_str(), newData->GetId().ToString<AZ::OSString>().c_str());
return; // this will just lead to crashes down the line and the above asserts cover this.
}
AssetData* newData = asset.Get();
if (found->second != newData)
{
// Notify users that we are about to change asset
AssetBus::Event(asset.GetId(), &AssetBus::Events::OnAssetPreReload, asset);
// Resolve the asset handler and account for the new asset instance.
{
AssetHandlerMap::iterator handlerIt = m_handlers.find(newData->GetType());
AZ_Assert(
handlerIt != m_handlers.end(), "No handler was registered for this asset [type:%s id:%s]!",
newData->GetType().ToString<AZ::OSString>().c_str(), newData->GetId().ToString<AZ::OSString>().c_str());
}
shouldAssignAssetData = true;
}
}
// We specifically perform this outside of the m_assetMutex lock so that the lock isn't held at the point that
// OnAssetReload is triggered inside of AssignAssetData. Otherwise, we open up a high potential for deadlocks.
if (shouldAssignAssetData)
{
AssignAssetData(asset);
}
}
@@ -2144,7 +2160,7 @@ namespace AZ
if (curIter != m_assetContainers.end())
{
auto newRef = curIter->second.lock();
if (newRef)
if (newRef && newRef->IsValid())
{
return newRef;
}
@@ -13,7 +13,7 @@
#include <AzCore/Asset/AssetJsonSerializer.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Preprocessor/EnumReflectUtils.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Asset/AssetManager.h>
@@ -24,6 +24,11 @@
namespace AZ
{
namespace Data
{
AZ_ENUM_DEFINE_REFLECT_UTILITIES(AssetLoadBehavior);
}
//=========================================================================
// AssetDatabaseComponent
// [6/25/2012]
@@ -99,6 +104,8 @@ namespace AZ
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
AZ::Data::AssetLoadBehaviorReflect(*serializeContext);
serializeContext->RegisterGenericType<Data::Asset<Data::AssetData>>();
serializeContext->Class<AssetManagerComponent, AZ::Component>()
@@ -19,7 +19,6 @@
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/NativeUI/NativeUISystemComponent.h>
#include <AzCore/Script/ScriptSystemComponent.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/Slice/SliceSystemComponent.h>
@@ -43,7 +42,6 @@ namespace AZ
AssetManagerComponent::CreateDescriptor(),
UserSettingsComponent::CreateDescriptor(),
Debug::FrameProfilerComponent::CreateDescriptor(),
NativeUI::NativeUISystemComponent::CreateDescriptor(),
SliceComponent::CreateDescriptor(),
SliceSystemComponent::CreateDescriptor(),
SliceMetadataInfoComponent::CreateDescriptor(),
@@ -14,6 +14,7 @@
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Math/Sfmt.h>
#include <AzCore/Math/Crc.h>
@@ -173,7 +174,11 @@ namespace AZ
//=========================================================================
void ComponentDescriptor::ReleaseDescriptor()
{
EBUS_EVENT(ComponentApplicationBus, UnregisterComponentDescriptor, this);
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
if (componentApplication != nullptr)
{
componentApplication->UnregisterComponentDescriptor(this);
}
delete this;
}
} // namespace AZ
@@ -28,6 +28,8 @@
#include <AzCore/Memory/AllocatorManager.h>
#include <AzCore/Memory/MallocSchema.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/Utils.h>
@@ -424,7 +426,7 @@ namespace AZ
// Now that the Allocators are initialized, the Command Line parameters can be parsed
m_commandLine.Parse(m_argC, m_argV);
ParseCommandLine(m_commandLine);
SettingsRegistryMergeUtils::ParseCommandLine(m_commandLine);
// Create the settings registry and register it with the AZ interface system
// This is done after the AppRoot has been calculated so that the Bootstrap.cfg
@@ -475,7 +477,7 @@ namespace AZ
m_console = AZ::Interface<AZ::IConsole>::Get();
if (m_console == nullptr)
{
m_console = aznew AZ::Console();
m_console = aznew AZ::Console(*m_settingsRegistry);
AZ::Interface<AZ::IConsole>::Register(m_console);
m_ownsConsole = true;
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
@@ -524,13 +526,50 @@ namespace AZ
// are destroyed
m_commandLine = {};
m_entityAddedEvent.DisconnectAllHandlers();
m_entityRemovedEvent.DisconnectAllHandlers();
m_entityActivatedEvent.DisconnectAllHandlers();
m_entityDeactivatedEvent.DisconnectAllHandlers();
DestroyAllocator();
}
void ReportBadEngineRoot()
{
AZStd::string errorMessage = {"Unable to determine a valid path to the engine.\n"
"Check parameters such as --project-path and --engine-path and make sure they are valid.\n"};
if (auto registry = AZ::SettingsRegistry::Get(); registry != nullptr)
{
AZ::SettingsRegistryInterface::FixedValueString filePathErrorStr;
if (registry->Get(filePathErrorStr, AZ::SettingsRegistryMergeUtils::FilePathKey_ErrorText); !filePathErrorStr.empty())
{
errorMessage += "Additional Info:\n";
errorMessage += filePathErrorStr.c_str();
}
}
if (auto nativeUI = AZ::Interface<AZ::NativeUI::NativeUIRequests>::Get(); nativeUI != nullptr)
{
nativeUI->DisplayOkDialog("O3DE Fatal Error", errorMessage.c_str(), false);
}
else
{
AZ_Error("ComponentApplication", false, "O3DE Fatal Error: %s\n", errorMessage.c_str());
}
}
Entity* ComponentApplication::Create(const Descriptor& descriptor, const StartupParameters& startupParameters)
{
AZ_Assert(!m_isStarted, "Component application already started!");
if (m_engineRoot.empty())
{
ReportBadEngineRoot();
return nullptr;
}
m_startupParameters = startupParameters;
m_descriptor = descriptor;
@@ -871,73 +910,55 @@ namespace AZ
}
}
void ComponentApplication::ParseCommandLine(const AZ::CommandLine& commandLine)
{
struct OptionKeyToRegsetKey
{
AZStd::string_view m_optionKey;
AZStd::string m_regsetKey;
};
// Provide overrides for the engine root, the project root and the project cache root
AZStd::array commandOptions = {
OptionKeyToRegsetKey{ "engine-path", AZStd::string::format("%s/engine_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) },
OptionKeyToRegsetKey{ "project-path", AZStd::string::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) },
OptionKeyToRegsetKey{ "project-cache-path", AZStd::string::format("%s/project_cache_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) }
};
AZStd::fixed_vector<AZStd::string, commandOptions.size()> overrideArgs;
for (auto&& [optionKey, regsetKey] : commandOptions)
{
if (size_t optionCount = commandLine.GetNumSwitchValues(optionKey); optionCount > 0)
{
// Use the last supplied command option value to override previous values
auto overrideArg = AZStd::string::format(R"(--regset="%s=%s")", regsetKey.c_str(),
commandLine.GetSwitchValue(optionKey, optionCount - 1).c_str());
overrideArgs.emplace_back(AZStd::move(overrideArg));
}
}
if (!overrideArgs.empty())
{
// Dump the input command line, add the additional option overrides
// and Parse the new command line into the Component Application command line
AZ::CommandLine::ParamContainer commandLineArgs;
commandLine.Dump(commandLineArgs);
commandLineArgs.insert(commandLineArgs.end(), AZStd::make_move_iterator(overrideArgs.begin()),
AZStd::make_move_iterator(overrideArgs.end()));
m_commandLine.Parse(commandLineArgs);
}
}
void ComponentApplication::MergeSettingsToRegistry(SettingsRegistryInterface& registry)
{
SettingsRegistryInterface::Specializations specializations;
SetSettingsRegistrySpecializations(specializations);
AZStd::vector<char> scratchBuffer;
// Retrieves the list gem module build targets that the active project depends on
SettingsRegistryMergeUtils::MergeSettingsToRegistry_TargetBuildDependencyRegistry(registry,
AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
#if defined(AZ_DEBUG_BUILD) || defined(AZ_PROFILE_BUILD)
// In development builds apply the o3de registry and the command line to allow early overrides. This will
// allow developers to override things like default paths or Asset Processor connection settings. Any additional
// values will be replaced by later loads, so this step will happen again at the end of loading.
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
// Project User Registry is merged after the command line here to allow make sure the any command line override of the project path
// is used for merging the project's user registry
SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
#endif
//! Retrieves the list gem targets that the project has load dependencies on
//! This populates the /Amazon/Gems/<GemName>/SourcePaths array entries which is required
//! by the MergeSettingsToRegistry_GemRegistry() function below to locate the gem's root folder
//! and merge in the gem's registry files.
//! But when running from a pre-built app from the O3DE SDK(Editor/AssetProcessor), the projects binary
//! directory is needed in order to located the load dependency registry files
//! That project binary folder is generated with the <ProjectRoot>/user/Registry when CMake is configured
//! for the project
//! Therefore the order of merging must be as follows
//! 1. MergeSettingsToRegistry_ProjectUserRegistry - Populates the /Amazon/Project/Settings/Build/project_build_path
//! which contains the path to the project binary directory
//! 2. MergeSettingsToRegistry_TargetBuildDependencyRegistry - Loads the cmake_dependencies.<project_name>.<application_name>.setreg
//! file from the locations in order of
//! 1. <executable_directory>/Registry
//! 2. <cache_root>/Registry
//! 3. <project_build_path>/bin/$<CONFIG>/Registry
//! 3. MergeSettingsToRegistry_GemRegistries - Merges the settings registry files from each gem's <GemRoot>/Registry directory
SettingsRegistryMergeUtils::MergeSettingsToRegistry_TargetBuildDependencyRegistry(registry,
AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_EngineRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_GemRegistries(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
#if defined(AZ_DEBUG_BUILD) || defined(AZ_PROFILE_BUILD)
SettingsRegistryMergeUtils::MergeSettingsToRegistry_O3deUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, false);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectUserRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(registry, m_commandLine, true);
#endif
// Update the Runtime file paths in case the "{BootstrapSettingsRootKey}/assets" key was overriden by a setting registry
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(registry);
}
void ComponentApplication::SetSettingsRegistrySpecializations(SettingsRegistryInterface::Specializations& specializations)
@@ -986,6 +1007,26 @@ namespace AZ
handler.Connect(m_entityRemovedEvent);
}
void ComponentApplication::RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler)
{
handler.Connect(m_entityActivatedEvent);
}
void ComponentApplication::RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler)
{
handler.Connect(m_entityDeactivatedEvent);
}
void ComponentApplication::SignalEntityActivated(AZ::Entity* entity)
{
m_entityActivatedEvent.Signal(entity);
}
void ComponentApplication::SignalEntityDeactivated(AZ::Entity* entity)
{
m_entityDeactivatedEvent.Signal(entity);
}
//=========================================================================
// AddEntity
// [5/30/2012]
@@ -1285,7 +1326,7 @@ namespace AZ
// Add all auto loadable non-asset gems to the list of gem modules to load
if (!moduleLoadData.m_autoLoad)
{
break;
continue;
}
for (AZ::OSString& dynamicLibraryPath : moduleLoadData.m_dynamicLibraryPaths)
{
@@ -204,6 +204,10 @@ namespace AZ
void UnregisterComponentDescriptor(const ComponentDescriptor* descriptor) override final;
void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler& handler) override final;
void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler& handler) override final;
void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler) override final;
void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler) override final;
void SignalEntityActivated(Entity* entity) override final;
void SignalEntityDeactivated(Entity* entity) override final;
bool AddEntity(Entity* entity) override;
bool RemoveEntity(Entity* entity) override;
bool DeleteEntity(const EntityId& id) override;
@@ -328,9 +332,6 @@ namespace AZ
/// Create the drillers
void CreateDrillers();
/// Parse ComponentApplication specific command line arguments
void ParseCommandLine(const AZ::CommandLine& commandLine);
virtual void MergeSettingsToRegistry(SettingsRegistryInterface& registry);
//! Sets the specializations that will be used when loading the Settings Registry. Extend this in derived
@@ -385,6 +386,8 @@ namespace AZ
AZStd::unique_ptr<SettingsRegistryInterface> m_settingsRegistry;
EntityAddedEvent m_entityAddedEvent;
EntityRemovedEvent m_entityRemovedEvent;
EntityAddedEvent m_entityActivatedEvent;
EntityRemovedEvent m_entityDeactivatedEvent;
AZ::IConsole* m_console{};
Descriptor m_descriptor;
bool m_isStarted{ false };
@@ -72,6 +72,8 @@ namespace AZ
using EntityAddedEvent = AZ::Event<AZ::Entity*>;
using EntityRemovedEvent = AZ::Event<AZ::Entity*>;
using EntityActivatedEvent = AZ::Event<AZ::Entity*>;
using EntityDeactivatedEvent = AZ::Event<AZ::Entity*>;
//! Interface that components can use to make requests of the main application.
class ComponentApplicationRequests
@@ -102,6 +104,22 @@ namespace AZ
//! @param handler the event handler to signal.
virtual void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler& handler) = 0;
//! Registers an event handler that will be signalled whenever an entity is added.
//! @param handler the event handler to signal.
virtual void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler& handler) = 0;
//! Registers an event handler that will be signalled whenever an entity is removed.
//! @param handler the event handler to signal.
virtual void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler& handler) = 0;
//! Signals that the provided entity has been activated.
//! @param entity the entity being activated.
virtual void SignalEntityActivated(AZ::Entity* entity) = 0;
//! Signals that the provided entity has been deactivated.
//! @param entity the entity being deactivated.
virtual void SignalEntityDeactivated(AZ::Entity* entity) = 0;
//! Adds an entity to the application's registry.
//! Calling Init() on an entity automatically performs this operation.
//! @param entity A pointer to the entity to add to the application's registry.
@@ -112,7 +112,11 @@ namespace AZ
{
EBUS_EVENT(EntitySystemBus, OnEntityDestruction, m_id);
EBUS_EVENT_ID(m_id, EntityBus, OnEntityDestruction, m_id);
EBUS_EVENT(ComponentApplicationBus, RemoveEntity, this);
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
if (componentApplication != nullptr)
{
componentApplication->RemoveEntity(this);
}
m_stateEvent.Signal(State::Init, State::Destroying);
}
@@ -216,12 +220,22 @@ namespace AZ
EBUS_EVENT_ID(m_id, EntityBus, OnEntityActivated, m_id);
EBUS_EVENT(EntitySystemBus, OnEntityActivated, m_id);
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
if (componentApplication != nullptr)
{
componentApplication->SignalEntityActivated(this);
}
}
void Entity::Deactivate()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzCore);
AZ::ComponentApplicationRequests* componentApplication = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
if (componentApplication != nullptr)
{
componentApplication->SignalEntityDeactivated(this);
}
EBUS_EVENT_ID(m_id, EntityBus, OnEntityDeactivated, m_id);
EBUS_EVENT(EntitySystemBus, OnEntityDeactivated, m_id);
@@ -87,4 +87,4 @@ namespace AZ
size_t m_nextBlockSize;
unsigned int m_compressedBufferIndex;
};
};
};
+176 -38
View File
@@ -13,7 +13,9 @@
#include <AzCore/Console/Console.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/Json/JsonSerializationSettings.h>
#include <AzCore/Settings/CommandLine.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/IO/FileIO.h>
@@ -43,6 +45,12 @@ namespace AZ
{
}
Console::Console(AZ::SettingsRegistryInterface& settingsRegistryInterface)
: Console()
{
RegisterCommandInvokerWithSettingsRegistry(settingsRegistryInterface);
}
Console::~Console()
{
// on console destruction relink the console functors back to the deferred head
@@ -111,51 +119,51 @@ namespace AZ
void Console::ExecuteConfigFile(AZStd::string_view configFileName)
{
IO::FixedMaxPath filePathFixed = configFileName;
if (AZ::IO::FileIOBase* fileIOBase = AZ::IO::FileIOBase::GetInstance())
auto settingsRegistry = AZ::SettingsRegistry::Get();
// If the config file is a settings registry file use the SettingsRegistryInterface MergeSettingsFile function
// otherwise use the SettingsRegistryMergeUtils MergeSettingsToRegistry_ConfigFile function to merge an INI-style
// file to the settings registry
AZ::IO::PathView configFile(configFileName);
if (configFile.Extension() == ".setreg")
{
fileIOBase->ResolvePath(filePathFixed, configFileName);
settingsRegistry->MergeSettingsFile(configFile.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch);
}
IO::SystemFile file;
if (!file.Open(filePathFixed.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
else if (configFile.Extension() == ".setregpatch")
{
AZLOG_ERROR("Failed to load '%s'. File could not be opened.", filePathFixed.c_str());
return;
settingsRegistry->MergeSettingsFile(configFile.Native(), AZ::SettingsRegistryInterface::Format::JsonPatch);
}
const IO::SizeType length = file.Length();
if (length == 0)
else
{
AZLOG_ERROR("Failed to load '%s'. File is empty.", filePathFixed.c_str());
return;
}
file.Seek(0, IO::SystemFile::SF_SEEK_BEGIN);
AZStd::string fileBuffer;
fileBuffer.resize(length);
IO::SizeType bytesRead = file.Read(length, fileBuffer.data());
file.Close();
// Resize again just in case bytesRead is less than length for some reason
fileBuffer.resize(bytesRead);
AZLOG_INFO("Loading config file %s", filePathFixed.c_str());
AZStd::vector<AZStd::string_view> separatedCommands;
auto BreakCommandsByLine = [&separatedCommands](AZStd::string_view token)
{
separatedCommands.emplace_back(token);
};
StringFunc::TokenizeVisitor(fileBuffer, BreakCommandsByLine, "\n\r");
for (const auto& commandView : separatedCommands)
{
ConsoleCommandContainer commandArgsView;
auto ConvertCommandStringToArray = [&commandArgsView](AZStd::string_view token)
AZ::SettingsRegistryMergeUtils::ConfigParserSettings configParserSettings;
configParserSettings.m_registryRootPointerPath = "/Amazon/AzCore/Runtime/ConsoleCommands";
configParserSettings.m_commandLineSettings.m_delimiterFunc = [](AZStd::string_view line)
{
commandArgsView.emplace_back(token);
SettingsRegistryInterface::CommandLineArgumentSettings::JsonPathValue pathValue;
AZStd::string_view parsedLine = line;
// Splits the line based on the <equal> or <colon>
if (auto path = AZ::StringFunc::TokenizeNext(parsedLine, "=:"); path.has_value())
{
pathValue.m_path = AZ::StringFunc::StripEnds(*path);
pathValue.m_value = AZ::StringFunc::StripEnds(parsedLine);
}
// If the value is empty, then the line either contained an equal sign followed only by whitespace or the line was empty
// 1. line="testInit=", pathValue.m_path="testInit", pathValue.m_value=""
// 2. line="testInit 1", pathValue.m_path="testInit 1", pathValue.m_value=""
// Therefore the path is split the path on whitespace in order to retrieve a value
if (pathValue.m_value.empty())
{
parsedLine = pathValue.m_path;
if (auto path = AZ::StringFunc::TokenizeNext(parsedLine, " \t"); path.has_value())
{
pathValue.m_path = AZ::StringFunc::StripEnds(*path);
pathValue.m_value = AZ::StringFunc::StripEnds(parsedLine);
}
}
return pathValue;
};
constexpr AZStd::string_view commandSeparators = " =";
StringFunc::TokenizeVisitor(commandView, ConvertCommandStringToArray, commandSeparators);
PerformCommand(commandArgsView, ConsoleSilentMode::NotSilent, ConsoleInvokedFrom::AzConsole, ConsoleFunctorFlags::Null, ConsoleFunctorFlags::Null);
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ConfigFile(*settingsRegistry, configFile.Native(), configParserSettings);
}
}
@@ -447,4 +455,134 @@ namespace AZ
return result;
}
struct ConsoleCommandKeyNotificationHandler
{
ConsoleCommandKeyNotificationHandler(AZ::SettingsRegistryInterface& registry, Console& console)
: m_settingsRegistry(registry)
, m_console(console)
{
}
// Responsible for using the Json Serialization Issue Callback system
// to determine when a JSON Patch or JSON Merge Patch modifies a value
// at a path underneath the IConsole::ConsoleRootCommandKey JSON pointer
JsonSerializationResult::ResultCode operator()(AZStd::string_view message,
JsonSerializationResult::ResultCode result, AZStd::string_view path)
{
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (result.GetTask() == JsonSerializationResult::Tasks::Merge
&& result.GetProcessing() == JsonSerializationResult::Processing::Completed
&& inputKey.IsRelativeTo(consoleRootCommandKey))
{
if (auto type = m_settingsRegistry.GetType(path); type != SettingsRegistryInterface::Type::NoType)
{
operator()(path, type);
}
}
// This is the default issue reporting, that logs using the warning category
if (result.GetProcessing() != JsonSerializationResult::Processing::Completed)
{
scratchBuffer.append(message.begin(), message.end());
scratchBuffer.append("\n Reason: ");
result.AppendToString(scratchBuffer, path);
scratchBuffer.append(".");
AZ_Warning("JSON Serialization", false, "%s", scratchBuffer.c_str());
scratchBuffer.clear();
}
return result;
}
void operator()(AZStd::string_view path, SettingsRegistryInterface::Type type)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
AZ::IO::PathView consoleRootCommandKey{ IConsole::ConsoleRootCommandKey, AZ::IO::PosixPathSeparator };
AZ::IO::PathView inputKey{ path, AZ::IO::PosixPathSeparator };
if (inputKey.IsRelativeTo(consoleRootCommandKey))
{
FixedValueString command = inputKey.LexicallyRelative(consoleRootCommandKey).Native();
ConsoleCommandContainer commandArgs;
// Argument string which stores the value from the Settings Registry long enough
// to pass into the PerformCommand. The ConsoleCommandContainer stores string_views
// and therefore doesn't own the memory.
FixedValueString commandArgString;
if (type == SettingsRegistryInterface::Type::String)
{
if (m_settingsRegistry.Get(commandArgString, path))
{
auto ConvertCommandArgumentToArray = [&commandArgs](AZStd::string_view token)
{
commandArgs.emplace_back(token);
};
constexpr AZStd::string_view commandSeparators = " \t\n\r";
StringFunc::TokenizeVisitor(commandArgString, ConvertCommandArgumentToArray, commandSeparators);
}
}
else if (type == SettingsRegistryInterface::Type::Boolean)
{
bool commandArgBool{};
if (m_settingsRegistry.Get(commandArgBool, path))
{
commandArgString = commandArgBool ? "true" : "false";
commandArgs.emplace_back(commandArgString);
}
}
else if (type == SettingsRegistryInterface::Type::Integer)
{
// Try converting to a signed 64-bit number first and then an unsigned 64-bit number
AZ::s64 commandArgInt{};
AZ::u64 commandArgUInt{};
if (m_settingsRegistry.Get(commandArgInt, path))
{
AZStd::to_string(commandArgString, commandArgInt);
commandArgs.emplace_back(commandArgString);
}
else if (m_settingsRegistry.Get(commandArgUInt, path))
{
AZStd::to_string(commandArgString, commandArgUInt);
commandArgs.emplace_back(commandArgString);
}
}
else if (type == SettingsRegistryInterface::Type::FloatingPoint)
{
double commandArgFloat{};
if (m_settingsRegistry.Get(commandArgFloat, path))
{
AZStd::to_string(commandArgString, commandArgFloat);
commandArgs.emplace_back(commandArgString);
}
}
CVarFixedString commandTrace(command);
for (AZStd::string_view commandArg : commandArgs)
{
commandTrace.push_back(' ');
commandTrace += commandArg;
}
m_console.PerformCommand(command, commandArgs, ConsoleSilentMode::NotSilent, ConsoleInvokedFrom::AzConsole, ConsoleFunctorFlags::Null, ConsoleFunctorFlags::Null);
}
}
AZ::Console& m_console;
AZ::SettingsRegistryInterface& m_settingsRegistry;
AZStd::string scratchBuffer;
};
void Console::RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry)
{
// Make sure the there is a JSON object at the path of AZ::IConsole::ConsoleRootCommandKey
// So that JSON Patch is able to add values underneath that object (JSON Patch doesn't create intermediate objects)
settingsRegistry.MergeSettings(R"({ "Amazon": { "AzCore": { "Runtime": { "ConsoleCommands": {} } }}})",
SettingsRegistryInterface::Format::JsonMergePatch);
m_consoleCommandKeyHandler = settingsRegistry.RegisterNotifier(ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this });
JsonApplyPatchSettings applyPatchSettings;
applyPatchSettings.m_reporting = ConsoleCommandKeyNotificationHandler{ settingsRegistry, *this };
settingsRegistry.SetApplyPatchSettings(applyPatchSettings);
}
}
@@ -14,6 +14,7 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/containers/unordered_map.h>
@@ -29,6 +30,9 @@ namespace AZ
AZ_CLASS_ALLOCATOR(Console, AZ::OSAllocator, 0);
Console();
//! Constructor overload which registers a notifier with the Settings Registry that will execute
//! a console command whenever a key is set under the AZ::IConsole::ConsoleCommandRootKey JSON object
explicit Console(AZ::SettingsRegistryInterface& settingsRegistry);
~Console() override;
//! IConsole interface
@@ -67,6 +71,7 @@ namespace AZ
void RegisterFunctor(ConsoleFunctorBase* functor) override;
void UnregisterFunctor(ConsoleFunctorBase* functor) override;
void LinkDeferredFunctors(ConsoleFunctorBase*& deferredHead) override;
void RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry) override;
//! @}
private:
@@ -96,6 +101,7 @@ namespace AZ
ConsoleFunctorBase* m_head;
using CommandMap = AZStd::unordered_map<CVarFixedString, AZStd::vector<ConsoleFunctorBase*>>;
CommandMap m_commands;
AZ::SettingsRegistryInterface::NotifyEventHandler m_consoleCommandKeyHandler;
friend class ConsoleFunctorBase;
};
@@ -148,7 +148,15 @@ namespace AZ
{
AZ::CVarFixedString convertCandidate{ arguments.front() };
char* endPtr = nullptr;
MAX_TYPE value = static_cast<MAX_TYPE>(strtoll(convertCandidate.c_str(), &endPtr, 0));
MAX_TYPE value;
if constexpr (AZStd::is_unsigned_v<MAX_TYPE>)
{
value = aznumeric_cast<MAX_TYPE>(strtoull(convertCandidate.c_str(), &endPtr, 0));
}
else
{
value = aznumeric_cast<MAX_TYPE>(strtoll(convertCandidate.c_str(), &endPtr, 0));
}
if (endPtr == convertCandidate.c_str())
{
@@ -22,8 +22,10 @@
namespace AZ
{
class SettingsRegistryInterface;
class CommandLine;
//! @class IConsole
//! A simple console class for providing text based variable and process interaction.
class IConsole
@@ -33,6 +35,8 @@ namespace AZ
using FunctorVisitor = AZStd::function<void(ConsoleFunctorBase*)>;
inline static constexpr AZStd::string_view ConsoleRootCommandKey = "/Amazon/AzCore/Runtime/ConsoleCommands";
IConsole() = default;
virtual ~IConsole() = default;
@@ -145,6 +149,12 @@ namespace AZ
//! Returns the AZ::Event<> invoked whenever a console command could not be found.
DispatchCommandNotFoundEvent& GetDispatchCommandNotFoundEvent();
//! Register a notification event handler with the Settings Registry
//! That is responsible for updating console commands whenever
//! a key is found underneath the "/Amazon/AzCore/Runtime/ConsoleCommands" JSON entry
//! @param Settings Registry reference to register notifier with
virtual void RegisterCommandInvokerWithSettingsRegistry(AZ::SettingsRegistryInterface& settingsRegistry) = 0;
AZ_DISABLE_COPY_MOVE(IConsole);
protected:
@@ -126,9 +126,11 @@ namespace AZ
char buffer[MaxLogBufferSize];
const AZStd::size_t length = azvsnprintf(buffer, MaxLogBufferSize, format, args);
buffer[AZStd::min<AZStd::size_t>(length, MaxLogBufferSize - 2)] = '\n';
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 1)] = '\0';
m_logEvent.Signal(level, buffer, file, function, line);
// Force a new-line before calling the AZ::Debug::Trace functions, as they assume a newline is present
buffer[AZStd::min<AZStd::size_t>(length + 1, MaxLogBufferSize - 2)] = '\n';
switch (level)
{
case LogLevel::Warn:
@@ -142,8 +144,6 @@ namespace AZ
AZ::Debug::Trace::Output("Logger", buffer);
break;
}
m_logEvent.Signal(level, buffer, file, function, line);
}
void LoggerSystemComponent::SetLevel(const AZ::ConsoleCommandContainer& arguments)
@@ -82,4 +82,4 @@ namespace AZ
#define AZ_TRACE_INSTANT_THREAD_CATEGORY(name, category) \
EBUS_QUEUE_EVENT(AZ::Debug::EventTraceDrillerBus, RecordInstantThread, name, category, AZStd::this_thread::get_id(), AZStd::GetTimeNowMicroSecond())
#define AZ_TRACE_INSTANT_THREAD(name) AZ_TRACE_INSTANT_THREAD_CATEGORY(name, "")
#define AZ_TRACE_INSTANT_THREAD(name) AZ_TRACE_INSTANT_THREAD_CATEGORY(name, "")
@@ -64,4 +64,4 @@ namespace AZ
} // namespace AZ
#endif // AZCORE_FRAME_PROFILER_H
#pragma once
#pragma once
@@ -39,4 +39,4 @@ namespace AZ
} // namespace AZ
#endif // AZCORE_FRAME_PROFILER_BUS_H
#pragma once
#pragma once
@@ -46,4 +46,4 @@ namespace AZ
}
#endif // AZCORE_PROFILER_DRILLER_BUS_H
#pragma once
#pragma once
@@ -233,6 +233,14 @@ namespace AZ
AZ_Assert(handler->m_event == this, "Entry event does not match");
handler->Disconnect();
}
// Free up any owned memory
AZStd::vector<Handler*> freeHandlers;
m_handlers.swap(freeHandlers);
AZStd::vector<Handler*> freeAdds;
m_addList.swap(freeAdds);
AZStd::stack<size_t> freeFree;
m_freeList.swap(freeFree);
}
@@ -194,4 +194,4 @@ namespace AZ
}
};
}
}
}
@@ -112,4 +112,4 @@ namespace AZ
return m_offsetEnd;
}
} // namespace IO
} // namesapce AZ
} // namesapce AZ
@@ -71,4 +71,4 @@ namespace AZ
u64 m_offsetEnd : 63;
};
} // namespace IO
} // namesapce AZ
} // namesapce AZ
+1 -1
View File
@@ -24,4 +24,4 @@
#if AZ_TRAIT_JSON_CLANG_IGNORE_UNKNOWN_WARNING && defined(AZ_COMPILER_CLANG)
#pragma clang diagnostic pop
#endif
#endif
@@ -46,4 +46,4 @@ namespace AZ
}
#endif
#pragma once
#pragma once
@@ -68,4 +68,4 @@ namespace AZ
}
#endif
#pragma once
#pragma once
@@ -70,4 +70,4 @@ namespace AZ
}
#endif
#pragma once
#pragma once
+1 -1
View File
@@ -36,4 +36,4 @@ namespace AZ
}
#endif
#pragma once
#pragma once
@@ -96,4 +96,4 @@ namespace AZ
}
#endif
#pragma once
#pragma once
@@ -135,4 +135,4 @@ namespace AZ
}
#endif
#pragma once
#pragma once
@@ -296,4 +296,4 @@ namespace AZ
m_updateCallback(index);
}
}
}
}
@@ -164,4 +164,4 @@ namespace AZ
return GetTargetValue();
}
};
}
}
@@ -0,0 +1,485 @@
/*
* 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/Math/MathMatrixSerializer.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/string/osstring.h>
#include <AzCore/Casting/numeric_cast.h>
namespace AZ::JsonMathMatrixSerializerInternal
{
template<typename MatrixType, size_t RowCount, size_t ColumnCount>
JsonSerializationResult::Result LoadArray(MatrixType& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
constexpr size_t ElementCount = RowCount * ColumnCount;
static_assert(ElementCount == 9 || ElementCount == 12 || ElementCount == 16,
"MathMatrixSerializer only support Matrix3x3, Matrix3x4 and Matrix4x4.");
rapidjson::SizeType arraySize = inputValue.Size();
if (arraySize < ElementCount)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Not enough numbers in JSON array to load math matrix from.");
}
AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid<float>());
if (!floatSerializer)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the JSON float serializer.");
}
constexpr const char* names[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"};
float values[ElementCount];
for (int i = 0; i < ElementCount; ++i)
{
ScopedContextPath subPath(context, names[i]);
JSR::Result intermediate = floatSerializer->Load(values + i, azrtti_typeid<float>(), inputValue[i], context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
}
size_t valueIndex = 0;
for (size_t r = 0; r < RowCount; ++r)
{
for (size_t c = 0; c < ColumnCount; ++c)
{
output.SetElement(aznumeric_caster(r), aznumeric_caster(c), values[valueIndex++]);
}
}
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Success, "Successfully read math matrix.");
}
JsonSerializationResult::Result LoadFloatFromObject(
float& output,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context,
const char* name,
const char* altName)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
AZ::BaseJsonSerializer* floatSerializer = context.GetRegistrationContext()->GetSerializerForType(azrtti_typeid<float>());
if (!floatSerializer)
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Catastrophic, "Failed to find the json float serializer.");
}
const char* nameUsed = name;
JSR::ResultCode result(JSR::Tasks::ReadField);
auto iterator = inputValue.FindMember(rapidjson::StringRef(name));
if (iterator == inputValue.MemberEnd())
{
nameUsed = altName;
iterator = inputValue.FindMember(rapidjson::StringRef(altName));
if (iterator == inputValue.MemberEnd())
{
// field not found so leave default value
result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed));
nameUsed = nullptr;
}
}
if (nameUsed)
{
ScopedContextPath subPath(context, nameUsed);
JSR::Result intermediate = floatSerializer->Load(&output, azrtti_typeid<float>(), iterator->value, context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
else
{
result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
}
return context.Report(result, "Successfully read float.");
}
JsonSerializationResult::Result LoadVector3FromObject(
Vector3& output,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context,
AZStd::fixed_vector<AZStd::string_view, 6> names)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
constexpr size_t ElementCount = 3; // Vector3
JSR::ResultCode result(JSR::Tasks::ReadField);
float values[ElementCount];
for (int i = 0; i < ElementCount; ++i)
{
values[i] = output.GetElement(i);
auto name = names[i * 2];
auto altName = names[(i * 2) + 1];
JSR::Result intermediate = LoadFloatFromObject(values[i], inputValue, context, name.data(), altName.data());
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
else
{
result.Combine(JSR::ResultCode(JSR::Tasks::ReadField, JSR::Outcomes::Success));
}
}
for (int i = 0; i < ElementCount; ++i)
{
output.SetElement(i, values[i]);
}
return context.Report(result, "Successfully read math matrix.");
}
JsonSerializationResult::Result LoadQuaternionAndScale(
AZ::Quaternion& quaternion,
float& scale,
const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
JSR::ResultCode result(JSR::Tasks::ReadField);
scale = 1.0f;
JSR::Result intermediateScale = LoadFloatFromObject(scale, inputValue, context, "scale", "Scale");
if (intermediateScale.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediateScale;
}
result.Combine(intermediateScale);
if (AZ::IsClose(scale, 0.0f))
{
result.Combine({ JSR::Tasks::ReadField, JSR::Outcomes::Unsupported });
return context.Report(result, "Scale can not be zero.");
}
AZ::Vector3 degreesRollPitchYaw = AZ::Vector3::CreateZero();
JSR::Result intermediateDegrees = LoadVector3FromObject(degreesRollPitchYaw, inputValue, context, { "roll", "Roll", "pitch", "Pitch", "yaw", "Yaw" });
if (intermediateDegrees.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediateDegrees;
}
result.Combine(intermediateDegrees);
// the quaternion should be equivalent to a series of rotations in the order z, then y, then x
const AZ::Vector3 eulerRadians = AZ::Vector3DegToRad(degreesRollPitchYaw);
quaternion = AZ::Quaternion::CreateRotationX(eulerRadians.GetX()) *
AZ::Quaternion::CreateRotationY(eulerRadians.GetY()) *
AZ::Quaternion::CreateRotationZ(eulerRadians.GetZ());
return context.Report(result, "Successfully read math yaw, pitch, roll, and scale.");
}
template<typename MatrixType>
JsonSerializationResult::Result LoadObject(MatrixType& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
output = MatrixType::CreateIdentity();
JSR::ResultCode result(JSR::Tasks::ReadField);
float scale;
AZ::Quaternion rotation;
JSR::Result intermediate = LoadQuaternionAndScale(rotation, scale, inputValue, context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
result.Combine(intermediate);
AZ::Vector3 translation = AZ::Vector3::CreateZero();
JSR::Result intermediateTranslation = LoadVector3FromObject(translation, inputValue, context, { "x", "X", "y", "Y", "z", "Z" });
if (intermediateTranslation.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediateTranslation;
}
result.Combine(intermediateTranslation);
// composed a matrix by rotation, then scale, then translation
auto matrix = MatrixType::CreateFromQuaternion(rotation);
matrix.MultiplyByScale(Vector3{ scale });
matrix.SetTranslation(translation);
if (matrix == MatrixType::CreateIdentity())
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity matrix for empty object.");
}
output = matrix;
return context.Report(result, "Successfully read math matrix.");
}
template<>
JsonSerializationResult::Result LoadObject<Matrix3x3>(Matrix3x3& output, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
output = Matrix3x3::CreateIdentity();
JSR::ResultCode result(JSR::Tasks::ReadField);
float scale;
AZ::Quaternion rotation;
JSR::Result intermediate = LoadQuaternionAndScale(rotation, scale, inputValue, context);
if (intermediate.GetResultCode().GetProcessing() != JSR::Processing::Completed)
{
return intermediate;
}
result.Combine(intermediate);
// composed a matrix by rotation then scale
auto matrix = Matrix3x3::CreateFromQuaternion(rotation);
matrix.MultiplyByScale(Vector3{ scale });
if (matrix == Matrix3x3::CreateIdentity())
{
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed, "Using identity matrix for empty object.");
}
output = matrix;
return context.Report(result, "Successfully read math matrix.");
}
template<typename MatrixType, size_t RowCount, size_t ColumnCount>
JsonSerializationResult::Result Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
constexpr size_t ElementCount = RowCount * ColumnCount;
static_assert(ElementCount == 9 || ElementCount == 12 || ElementCount == 16,
"MathMatrixSerializer only support Matrix3x3, Matrix3x4 and Matrix4x4.");
AZ_Assert(azrtti_typeid<MatrixType>() == outputValueTypeId,
"Unable to deserialize Matrix%zux%zu to json because the provided type is %s",
RowCount, ColumnCount, outputValueTypeId.ToString<OSString>().c_str());
AZ_UNUSED(outputValueTypeId);
MatrixType* matrix = reinterpret_cast<MatrixType*>(outputValue);
AZ_Assert(matrix, "Output value for JsonMatrix%zux%zuSerializer can't be null.", RowCount, ColumnCount);
switch (inputValue.GetType())
{
case rapidjson::kArrayType:
return LoadArray<MatrixType, RowCount, ColumnCount>(*matrix, inputValue, context);
case rapidjson::kObjectType:
return LoadObject<MatrixType>(*matrix, inputValue, context);
case rapidjson::kStringType:
[[fallthrough]];
case rapidjson::kNumberType:
[[fallthrough]];
case rapidjson::kNullType:
[[fallthrough]];
case rapidjson::kFalseType:
[[fallthrough]];
case rapidjson::kTrueType:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported,
"Unsupported type. Math matrix can only be read from arrays or objects.");
default:
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unknown,
"Unknown json type encountered in math matrix.");
}
}
template<typename MatrixType>
AZ::Quaternion CreateQuaternion(const MatrixType& matrix);
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix3x3>(const AZ::Matrix3x3& matrix)
{
return Quaternion::CreateFromMatrix3x3(matrix);
}
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix3x4>(const AZ::Matrix3x4& matrix)
{
return Quaternion::CreateFromMatrix3x4(matrix);
}
template<>
AZ::Quaternion CreateQuaternion<AZ::Matrix4x4>(const AZ::Matrix4x4& matrix)
{
return Quaternion::CreateFromMatrix4x4(matrix);
}
template<typename MatrixType>
JsonSerializationResult::Result StoreRotationAndScale(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
AZ_UNUSED(valueTypeId);
const MatrixType* matrix = reinterpret_cast<const MatrixType*>(inputValue);
AZ_Assert(matrix, "Input value for JsonMatrixSerializer can't be null.");
const MatrixType* defaultMatrix = reinterpret_cast<const MatrixType*>(defaultValue);
if (!context.ShouldKeepDefaults() && defaultMatrix && *matrix == *defaultMatrix)
{
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default math Matrix used.");
}
MatrixType matrixToExport = *matrix;
AZ::Vector3 scale = matrixToExport.ExtractScale();
AZ::Quaternion rotation = CreateQuaternion(matrixToExport);
auto degrees = rotation.GetEulerDegrees();
outputValue.AddMember(rapidjson::StringRef("roll"), degrees.GetX(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("pitch"), degrees.GetY(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("yaw"), degrees.GetZ(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("scale"), scale.GetX(), context.GetJsonAllocator());
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Math Matrix successfully stored.");
}
template<typename MatrixType>
JsonSerializationResult::Result StoreTranslation(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult; // Used remove name conflicts in AzCore in uber builds.
AZ_UNUSED(valueTypeId);
const MatrixType* matrix = reinterpret_cast<const MatrixType*>(inputValue);
AZ_Assert(matrix, "Input value for JsonMatrixSerializer can't be null.");
const MatrixType* defaultMatrix = reinterpret_cast<const MatrixType*>(defaultValue);
if (!context.ShouldKeepDefaults() && defaultMatrix && *matrix == *defaultMatrix)
{
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "Default math Matrix used.");
}
auto translation = matrix->GetTranslation();
outputValue.AddMember(rapidjson::StringRef("x"), translation.GetX(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("y"), translation.GetY(), context.GetJsonAllocator());
outputValue.AddMember(rapidjson::StringRef("z"), translation.GetZ(), context.GetJsonAllocator());
return context.Report(JSR::Tasks::WriteValue, JSR::Outcomes::Success, "Math Matrix successfully stored.");
}
}
namespace AZ
{
// Matrix3x3
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x3Serializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonMatrix3x3Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x3, 3, 3>(
outputValue,
outputValueTypeId,
inputValue,
context);
}
JsonSerializationResult::Result JsonMatrix3x3Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
outputValue.SetObject();
return JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x3>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
}
// Matrix3x4
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix3x4Serializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonMatrix3x4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix3x4, 3, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
}
JsonSerializationResult::Result JsonMatrix3x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix3x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
result.GetResultCode().Combine(resultTranslation);
return result;
}
// Matrix4x4
AZ_CLASS_ALLOCATOR_IMPL(JsonMatrix4x4Serializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonMatrix4x4Serializer::Load(void* outputValue, const Uuid& outputValueTypeId,
const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
return JsonMathMatrixSerializerInternal::Load<Matrix4x4, 4, 4>(
outputValue,
outputValueTypeId,
inputValue,
context);
}
JsonSerializationResult::Result JsonMatrix4x4Serializer::Store(rapidjson::Value& outputValue, const void* inputValue,
const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context)
{
outputValue.SetObject();
auto result = JsonMathMatrixSerializerInternal::StoreRotationAndScale<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
auto resultTranslation = JsonMathMatrixSerializerInternal::StoreTranslation<Matrix4x4>(
outputValue,
inputValue,
defaultValue,
valueTypeId,
context);
result.GetResultCode().Combine(resultTranslation);
return result;
}
}
@@ -0,0 +1,54 @@
/*
* 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/Serialization/Json/BaseJsonSerializer.h>
namespace AZ
{
class JsonMatrix3x3Serializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix3x3Serializer, "{8C76CD6A-8576-4604-A746-CF7A7F20F366}", 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;
};
class JsonMatrix3x4Serializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix3x4Serializer, "{E801333B-4AF1-4F43-976C-579670B02DC5}", 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;
};
class JsonMatrix4x4Serializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(JsonMatrix4x4Serializer, "{46E888FC-248A-4910-9221-4E101A10AEA1}", 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;
};
}
@@ -24,6 +24,7 @@
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/MathMatrixSerializer.h>
#include <AzCore/Math/MathVectorSerializer.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/ColorSerializer.h>
@@ -366,6 +367,9 @@ namespace AZ
{
context.Serializer<JsonColorSerializer>()->HandlesType<Color>();
context.Serializer<JsonUuidSerializer>()->HandlesType<Uuid>();
context.Serializer<JsonMatrix3x3Serializer>()->HandlesType<Matrix3x3>();
context.Serializer<JsonMatrix3x4Serializer>()->HandlesType<Matrix3x4>();
context.Serializer<JsonMatrix4x4Serializer>()->HandlesType<Matrix4x4>();
context.Serializer<JsonVector2Serializer>()->HandlesType<Vector2>();
context.Serializer<JsonVector3Serializer>()->HandlesType<Vector3>();
context.Serializer<JsonVector4Serializer>()->HandlesType<Vector4>();
+10 -9
View File
@@ -13,16 +13,17 @@
#pragma once
#include <AzCore/base.h>
#include <AzCore/std/math.h>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_integral.h>
#include <AzCore/std/typetraits/is_signed.h>
#include <AzCore/std/typetraits/is_unsigned.h>
#include <AzCore/std/utils.h>
#include <math.h>
#include <float.h>
#include <limits>
#include <cmath>
#include <math.h>
#include <utility>
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_integral.h>
// We have a separate inline define for math functions.
// The performance of these functions is very sensitive to inlining, and some compilers don't deal well with this.
@@ -308,12 +309,12 @@ namespace AZ
AZ_MATH_INLINE bool IsClose(float a, float b, float tolerance = Constants::Tolerance)
{
return (fabsf(a - b) <= tolerance);
return (AZStd::abs(a - b) <= tolerance);
}
AZ_MATH_INLINE bool IsClose(double a, double b, double tolerance = Constants::Tolerance)
{
return (fabs(a - b) <= tolerance);
return (AZStd::abs(a - b) <= tolerance);
}
//! Returns x >= 0.0f ? 1.0f : -1.0f.
@@ -402,12 +403,12 @@ namespace AZ
AZ_MATH_INLINE float GetAbs(float a)
{
return fabsf(a);
return AZStd::abs(a);
}
AZ_MATH_INLINE double GetAbs(double a)
{
return std::abs(a);
return AZStd::abs(a);
}
AZ_MATH_INLINE float GetMod(float a, float b)
@@ -441,7 +442,7 @@ namespace AZ
template<typename T>
AZ_MATH_INLINE bool IsCloseMag(T x, T y, T epsilonValue = std::numeric_limits<T>::epsilon())
{
return (std::fabs(x - y) <= epsilonValue * GetMax<T>(GetMax<T>(T(1.0), std::fabs(x)), std::fabs(y)));
return (AZStd::abs(x - y) <= epsilonValue * GetMax<T>(GetMax<T>(T(1.0), AZStd::abs(x)), AZStd::abs(y)));
}
//! ClampIfCloseMag(x, y, epsilon) returns y when x and y are within epsilon of each other (taking magnitude into account). Otherwise returns x.
+38 -13
View File
@@ -142,27 +142,44 @@ namespace AZ
void SetBasis(const Vector3& basisX, const Vector3& basisY, const Vector3& basisZ);
//! @}
Matrix3x3 operator*(const Matrix3x3& rhs) const;
//! Calculates (this->GetTranspose() * rhs).
Matrix3x3 TransposedMultiply(const Matrix3x3& rhs) const;
//! Post-multiplies the matrix by a vector.
Vector3 operator*(const Vector3& rhs) const;
Matrix3x3 operator+(const Matrix3x3& rhs) const;
Matrix3x3 operator-(const Matrix3x3& rhs) const;
Matrix3x3 operator*(float multiplier) const;
Matrix3x3 operator/(float divisor) const;
Matrix3x3 operator-() const;
Matrix3x3& operator*=(const Matrix3x3& rhs);
//! Operator for matrix-matrix addition.
//! @{
[[nodiscard]] Matrix3x3 operator+(const Matrix3x3& rhs) const;
Matrix3x3& operator+=(const Matrix3x3& rhs);
//! @}
//! Operator for matrix-matrix substraction.
//! @{
[[nodiscard]] Matrix3x3 operator-(const Matrix3x3& rhs) const;
Matrix3x3& operator-=(const Matrix3x3& rhs);
//! @}
//! Operator for matrix-matrix multiplication.
//! @{
[[nodiscard]] Matrix3x3 operator*(const Matrix3x3& rhs) const;
Matrix3x3& operator*=(const Matrix3x3& rhs);
//! @}
//! Operator for multiplying all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix3x3 operator*(float multiplier) const;
Matrix3x3& operator*=(float multiplier);
//! @}
//! Operator for dividing all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix3x3 operator/(float divisor) const;
Matrix3x3& operator/=(float divisor);
//! @}
//! Operator for negating all matrix's elements
[[nodiscard]] Matrix3x3 operator-() const;
bool operator==(const Matrix3x3& rhs) const;
bool operator!=(const Matrix3x3& rhs) const;
@@ -187,7 +204,10 @@ namespace AZ
//! @}
//! Gets the scale part of the transformation, i.e. the length of the scale components.
Vector3 RetrieveScale() const;
[[nodiscard]] Vector3 RetrieveScale() const;
//! Gets the squared scale part of the transformation (the squared length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScaleSq() const;
//! Gets the scale part of the transformation as in RetrieveScale, and also removes this scaling from the matrix.
Vector3 ExtractScale();
@@ -195,6 +215,9 @@ namespace AZ
//! Quick multiplication by a scale matrix, equivalent to m*=Matrix3x3::CreateScale(scale).
void MultiplyByScale(const Vector3& scale);
//! Returns a matrix with the reciprocal scale, keeping the same rotation and translation.
[[nodiscard]] Matrix3x3 GetReciprocalScaled() const;
//! Polar decomposition, M=U*H, U is orthogonal (unitary) and H is symmetric (hermitian).
//! This function returns the orthogonal part only
Matrix3x3 GetPolarDecomposition() const;
@@ -241,7 +264,9 @@ namespace AZ
//! Note that this is not the usual multiplication order for transformations.
Vector3& operator*=(Vector3& lhs, const Matrix3x3& rhs);
//! Pre-multiplies the matrix by a scalar.
Matrix3x3 operator*(float lhs, const Matrix3x3& rhs);
}
} // namespace AZ
#include <AzCore/Math/Matrix3x3.inl>
+84 -57
View File
@@ -392,14 +392,6 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(const Matrix3x3& rhs) const
{
Matrix3x3 result;
Simd::Vec3::Mat3x3Multiply(GetSimdValues(), rhs.GetSimdValues(), result.GetSimdValues());
return result;
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::TransposedMultiply(const Matrix3x3& rhs) const
{
Matrix3x3 result;
@@ -416,51 +408,12 @@ namespace AZ
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator+(const Matrix3x3& rhs) const
{
return Matrix3x3(Simd::Vec3::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
, Simd::Vec3::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
, Simd::Vec3::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue()));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-(const Matrix3x3& rhs) const
{
return Matrix3x3(Simd::Vec3::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
, Simd::Vec3::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue())
, Simd::Vec3::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue()));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(float multiplier) const
{
const Simd::Vec3::FloatType mulVec = Simd::Vec3::Splat(multiplier);
return Matrix3x3(Simd::Vec3::Mul(m_rows[0].GetSimdValue(), mulVec)
, Simd::Vec3::Mul(m_rows[1].GetSimdValue(), mulVec)
, Simd::Vec3::Mul(m_rows[2].GetSimdValue(), mulVec));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator/(float divisor) const
{
const Simd::Vec3::FloatType divVec = Simd::Vec3::Splat(divisor);
return Matrix3x3(Simd::Vec3::Div(m_rows[0].GetSimdValue(), divVec)
, Simd::Vec3::Div(m_rows[1].GetSimdValue(), divVec)
, Simd::Vec3::Div(m_rows[2].GetSimdValue(), divVec));
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-() const
{
const Simd::Vec3::FloatType zeroVec = Simd::Vec3::ZeroFloat();
return Matrix3x3(Simd::Vec3::Sub(zeroVec, m_rows[0].GetSimdValue())
, Simd::Vec3::Sub(zeroVec, m_rows[1].GetSimdValue())
, Simd::Vec3::Sub(zeroVec, m_rows[2].GetSimdValue()));
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator*=(const Matrix3x3& rhs)
{
*this = *this * rhs;
return *this;
return Matrix3x3
(
Simd::Vec3::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec3::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec3::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
);
}
@@ -471,6 +424,17 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-(const Matrix3x3& rhs) const
{
return Matrix3x3
(
Simd::Vec3::Sub(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec3::Sub(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec3::Sub(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator-=(const Matrix3x3& rhs)
{
*this = *this - rhs;
@@ -478,6 +442,33 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(const Matrix3x3& rhs) const
{
Matrix3x3 result;
Simd::Vec3::Mat3x3Multiply(GetSimdValues(), rhs.GetSimdValues(), result.GetSimdValues());
return result;
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator*=(const Matrix3x3& rhs)
{
*this = *this * rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator*(float multiplier) const
{
const Simd::Vec3::FloatType mulVec = Simd::Vec3::Splat(multiplier);
return Matrix3x3
(
Simd::Vec3::Mul(m_rows[0].GetSimdValue(), mulVec),
Simd::Vec3::Mul(m_rows[1].GetSimdValue(), mulVec),
Simd::Vec3::Mul(m_rows[2].GetSimdValue(), mulVec)
);
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator*=(float multiplier)
{
*this = *this * multiplier;
@@ -485,6 +476,18 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator/(float divisor) const
{
const Simd::Vec3::FloatType divVec = Simd::Vec3::Splat(divisor);
return Matrix3x3
(
Simd::Vec3::Div(m_rows[0].GetSimdValue(), divVec),
Simd::Vec3::Div(m_rows[1].GetSimdValue(), divVec),
Simd::Vec3::Div(m_rows[2].GetSimdValue(), divVec)
);
}
AZ_MATH_INLINE Matrix3x3& Matrix3x3::operator/=(float divisor)
{
*this = *this / divisor;
@@ -492,6 +495,18 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::operator-() const
{
const Simd::Vec3::FloatType zeroVec = Simd::Vec3::ZeroFloat();
return Matrix3x3
(
Simd::Vec3::Sub(zeroVec, m_rows[0].GetSimdValue()),
Simd::Vec3::Sub(zeroVec, m_rows[1].GetSimdValue()),
Simd::Vec3::Sub(zeroVec, m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE bool Matrix3x3::operator==(const Matrix3x3& rhs) const
{
return (Simd::Vec3::CmpAllEq(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue())
@@ -552,6 +567,12 @@ namespace AZ
}
AZ_MATH_INLINE Vector3 Matrix3x3::RetrieveScaleSq() const
{
return Vector3(GetBasisX().GetLengthSq(), GetBasisY().GetLengthSq(), GetBasisZ().GetLengthSq());
}
AZ_MATH_INLINE Vector3 Matrix3x3::ExtractScale()
{
const Vector3 x = GetBasisX();
@@ -584,6 +605,14 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x3 Matrix3x3::GetReciprocalScaled() const
{
Matrix3x3 result = *this;
result.MultiplyByScale(RetrieveScaleSq().GetReciprocal());
return result;
}
AZ_MATH_INLINE void Matrix3x3::GetPolarDecomposition(Matrix3x3* orthogonalOut, Matrix3x3* symmetricOut) const
{
*orthogonalOut = GetPolarDecomposition();
@@ -679,8 +708,6 @@ namespace AZ
AZ_MATH_INLINE Matrix3x3 operator*(float lhs, const Matrix3x3& rhs)
{
const Simd::Vec3::FloatType lhsVec = Simd::Vec3::Splat(lhs);
const Simd::Vec3::FloatType* rows = rhs.GetSimdValues();
return Matrix3x3(Simd::Vec3::Mul(lhsVec, rows[0]), Simd::Vec3::Mul(lhsVec, rows[1]), Simd::Vec3::Mul(lhsVec, rows[2]));
return rhs * lhs;
}
}
} // namespace AZ
+40 -3
View File
@@ -225,11 +225,38 @@ namespace AZ
//! Sets the three basis vectors and the translation.
void SetBasisAndTranslation(const Vector3& basisX, const Vector3& basisY, const Vector3& basisZ, const Vector3& translation);
//! Operator for matrix-matrix multiplication.
[[nodiscard]] Matrix3x4 operator*(const Matrix3x4& rhs) const;
//! Operator for matrix-matrix addition.
//! @{
[[nodiscard]] Matrix3x4 operator+(const Matrix3x4& rhs) const;
Matrix3x4& operator+=(const Matrix3x4& rhs);
//! @}
//! Compound assignment operator for matrix-matrix multiplication.
//! Operator for matrix-matrix substraction.
//! @{
[[nodiscard]] Matrix3x4 operator-(const Matrix3x4& rhs) const;
Matrix3x4& operator-=(const Matrix3x4& rhs);
//! @}
//! Operator for matrix-matrix multiplication.
//! @{
[[nodiscard]] Matrix3x4 operator*(const Matrix3x4& rhs) const;
Matrix3x4& operator*=(const Matrix3x4& rhs);
//! @}
//! Operator for multiplying all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix3x4 operator*(float multiplier) const;
Matrix3x4& operator*=(float multiplier);
//! @}
//! Operator for dividing all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix3x4 operator/(float divisor) const;
Matrix3x4& operator/=(float divisor);
//! @}
//! Operator for negating all matrix's elements
[[nodiscard]] Matrix3x4 operator-() const;
//! Operator for transforming a Vector3.
[[nodiscard]] Vector3 operator*(const Vector3& rhs) const;
@@ -274,12 +301,18 @@ namespace AZ
//! Gets the scale part of the transformation (the length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScale() const;
//! Gets the squared scale part of the transformation (the squared length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScaleSq() const;
//! Gets the scale part of the transformation as in RetrieveScale, and also removes this scaling from the matrix.
Vector3 ExtractScale();
//! Multiplies the basis vectors of the matrix by the elements of the scale specified.
void MultiplyByScale(const Vector3& scale);
//! Returns a matrix with the reciprocal scale, keeping the same rotation and translation.
[[nodiscard]] Matrix3x4 GetReciprocalScaled() const;
//! Tests if the 3x3 part of the matrix is orthogonal.
bool IsOrthogonal(float tolerance = Constants::Tolerance) const;
@@ -335,6 +368,10 @@ namespace AZ
Vector4 m_rows[RowCount];
};
//! Pre-multiplies the matrix by a scalar.
Matrix3x4 operator*(float lhs, const Matrix3x4& rhs);
} // namespace AZ
#include <AzCore/Math/Matrix3x4.inl>
@@ -472,6 +472,42 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator+(const Matrix3x4& rhs) const
{
return Matrix3x4
(
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())
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator+=(const Matrix3x4& rhs)
{
*this = *this + rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator-(const Matrix3x4& rhs) const
{
return Matrix3x4
(
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())
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator-=(const Matrix3x4& rhs)
{
*this = *this - rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(const Matrix3x4& rhs) const
{
Matrix3x4 result;
@@ -487,6 +523,56 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(float multiplier) const
{
const Simd::Vec4::FloatType mulVec = Simd::Vec4::Splat(multiplier);
return Matrix3x4
(
Simd::Vec4::Mul(m_rows[0].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[1].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[2].GetSimdValue(), mulVec)
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator*=(float multiplier)
{
*this = *this * multiplier;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator/(float divisor) const
{
const Simd::Vec4::FloatType divVec = Simd::Vec4::Splat(divisor);
return Matrix3x4
(
Simd::Vec4::Div(m_rows[0].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[1].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[2].GetSimdValue(), divVec)
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator/=(float divisor)
{
*this = *this / divisor;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator-() const
{
const Simd::Vec4::FloatType zeroVec = Simd::Vec4::ZeroFloat();
return Matrix3x4
(
Simd::Vec4::Sub(zeroVec, m_rows[0].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[1].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE Vector3 Matrix3x4::operator*(const Vector3& rhs) const
{
return Vector3
@@ -583,6 +669,12 @@ namespace AZ
}
AZ_MATH_INLINE Vector3 Matrix3x4::RetrieveScaleSq() const
{
return Vector3(GetColumn(0).GetLengthSq(), GetColumn(1).GetLengthSq(), GetColumn(2).GetLengthSq());
}
AZ_MATH_INLINE Vector3 Matrix3x4::ExtractScale()
{
const Vector3 scale = RetrieveScale();
@@ -600,6 +692,14 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::GetReciprocalScaled() const
{
Matrix3x4 result = *this;
result.MultiplyByScale(RetrieveScaleSq().GetReciprocal());
return result;
}
AZ_MATH_INLINE void Matrix3x4::Orthogonalize()
{
*this = GetOrthogonalized();
@@ -660,4 +760,10 @@ namespace AZ
{
return reinterpret_cast<Simd::Vec4::FloatType*>(m_rows);
}
AZ_MATH_INLINE Matrix3x4 operator*(float lhs, const Matrix3x4& rhs)
{
return rhs * lhs;
}
} // namespace AZ
+39 -5
View File
@@ -171,14 +171,38 @@ namespace AZ
void SetTranslation(const Vector3& v);
//! @}
Matrix4x4 operator+(const Matrix4x4& rhs) const;
//! Operator for matrix-matrix addition.
//! @{
[[nodiscard]] Matrix4x4 operator+(const Matrix4x4& rhs) const;
Matrix4x4& operator+=(const Matrix4x4& rhs);
//! @}
Matrix4x4 operator-(const Matrix4x4& rhs) const;
//! Operator for matrix-matrix substraction.
//! @{
[[nodiscard]] Matrix4x4 operator-(const Matrix4x4& rhs) const;
Matrix4x4& operator-=(const Matrix4x4& rhs);
//! @}
Matrix4x4 operator*(const Matrix4x4& rhs) const;
//! Operator for matrix-matrix multiplication.
//! @{
[[nodiscard]] Matrix4x4 operator*(const Matrix4x4& rhs) const;
Matrix4x4& operator*=(const Matrix4x4& rhs);
//! @}
//! Operator for multiplying all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix4x4 operator*(float multiplier) const;
Matrix4x4& operator*=(float multiplier);
//! @}
//! Operator for dividing all matrix's elements with a scalar
//! @{
[[nodiscard]] Matrix4x4 operator/(float divisor) const;
Matrix4x4& operator/=(float divisor);
//! @}
//! Operator for negating all matrix's elements
[[nodiscard]] Matrix4x4 operator-() const;
//! Post-multiplies the matrix by a vector.
//! Assumes that the w-component of the Vector3 is 1.0.
@@ -222,7 +246,10 @@ namespace AZ
//! @}
//! Gets the scale part of the transformation, i.e. the length of the scale components.
Vector3 RetrieveScale() const;
[[nodiscard]] Vector3 RetrieveScale() const;
//! Gets the squared scale part of the transformation (the squared length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScaleSq() const;
//! Gets the scale part of the transformation as in RetrieveScale, and also removes this scaling from the matrix.
Vector3 ExtractScale();
@@ -230,6 +257,9 @@ namespace AZ
//! Quick multiplication by a scale matrix, equivalent to m*=Matrix4x4::CreateScale(scale).
void MultiplyByScale(const Vector3& scale);
//! Returns a matrix with the reciprocal scale, keeping the same rotation and translation.
[[nodiscard]] Matrix4x4 GetReciprocalScaled() const;
bool IsClose(const Matrix4x4& rhs, float tolerance = Constants::Tolerance) const;
bool operator==(const Matrix4x4& rhs) const;
@@ -270,6 +300,10 @@ namespace AZ
//! Pre-multiplies the matrix by a vector in-place.
//! Note that this is not the usual multiplication order for transformations.
Vector4& operator*=(Vector4& lhs, const Matrix4x4& rhs);
}
//! Pre-multiplies the matrix by a scalar.
Matrix4x4 operator*(float lhs, const Matrix4x4& rhs);
} // namespace AZ
#include <AzCore/Math/Matrix4x4.inl>
+92 -15
View File
@@ -480,20 +480,12 @@ 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()));
(
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)
@@ -502,6 +494,18 @@ namespace AZ
return *this;
}
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;
@@ -523,6 +527,59 @@ namespace AZ
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator*(float multiplier) const
{
const Simd::Vec4::FloatType mulVec = Simd::Vec4::Splat(multiplier);
return Matrix4x4
(
Simd::Vec4::Mul(m_rows[0].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[1].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[2].GetSimdValue(), mulVec),
Simd::Vec4::Mul(m_rows[3].GetSimdValue(), mulVec)
);
}
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator*=(float multiplier)
{
*this = *this * multiplier;
return *this;
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator/(float divisor) const
{
const Simd::Vec4::FloatType divVec = Simd::Vec4::Splat(divisor);
return Matrix4x4
(
Simd::Vec4::Div(m_rows[0].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[1].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[2].GetSimdValue(), divVec),
Simd::Vec4::Div(m_rows[3].GetSimdValue(), divVec)
);
}
AZ_MATH_INLINE Matrix4x4& Matrix4x4::operator/=(float divisor)
{
*this = *this / divisor;
return *this;
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::operator-() const
{
const Simd::Vec4::FloatType zeroVec = Simd::Vec4::ZeroFloat();
return Matrix4x4
(
Simd::Vec4::Sub(zeroVec, m_rows[0].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[1].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[2].GetSimdValue()),
Simd::Vec4::Sub(zeroVec, m_rows[3].GetSimdValue())
);
}
AZ_MATH_INLINE Vector3 Matrix4x4::operator*(const Vector3& rhs) const
{
return Vector3(Simd::Vec4::Mat4x4TransformPoint3(GetSimdValues(), rhs.GetSimdValue()));
@@ -595,6 +652,12 @@ namespace AZ
}
AZ_MATH_INLINE Vector3 Matrix4x4::RetrieveScaleSq() const
{
return Vector3(GetBasisX().GetLengthSq(), GetBasisY().GetLengthSq(), GetBasisZ().GetLengthSq());
}
AZ_MATH_INLINE Vector3 Matrix4x4::ExtractScale()
{
Vector4 x = GetBasisX();
@@ -619,6 +682,14 @@ namespace AZ
}
AZ_MATH_INLINE Matrix4x4 Matrix4x4::GetReciprocalScaled() const
{
Matrix4x4 result = *this;
result.MultiplyByScale(RetrieveScaleSq().GetReciprocal());
return result;
}
AZ_MATH_INLINE bool Matrix4x4::IsClose(const Matrix4x4& rhs, float tolerance) const
{
const Simd::Vec4::FloatType vecTolerance = Simd::Vec4::Splat(tolerance);
@@ -702,4 +773,10 @@ namespace AZ
lhs = lhs * rhs;
return lhs;
}
}
AZ_MATH_INLINE Matrix4x4 operator*(float lhs, const Matrix4x4& rhs)
{
return rhs * lhs;
}
} // namespace AZ
@@ -67,4 +67,4 @@ namespace AZ
//! Transforms a position by a matrix. This function can be used with any generic cases which include projection matrices.
Vector3 MatrixTransformPosition(const Matrix4x4& matrix, const Vector3& inPosition);
} // namespace AZ
} // namespace AZ
@@ -110,4 +110,4 @@ namespace AZ
} // namespace AZ
#include <AzCore/Math/Internal/VertexContainer.inl>
#include <AzCore/Math/Internal/VertexContainer.inl>
@@ -172,4 +172,4 @@ namespace AZ
template<>
inline AZ::Vector3 AdaptVertexOut<AZ::Vector2>(const AZ::Vector2& vector) { return Vector2ToVector3(vector); }
} // namespace AZ
} // namespace AZ
@@ -103,7 +103,7 @@ namespace AZ
}
}
/// Returns a pointer to the beginning of master vector of SmallAllocationGroups.
/// Returns a pointer to the beginning of vector of SmallAllocationGroups.
SmallAllocationGroup* ArrayHead()
{
return this - m_index;
@@ -169,7 +169,7 @@ namespace AZ
return m_marker == MARKER;
}
/// Returns the master index of the SmallAllocationGroup containing this allocation
/// Returns the index of the SmallAllocationGroup containing this allocation
uint32_t GetSmallAllocationIndex() const
{
return (uint32_t)(m_data & 0xFFFFFFFF);
@@ -31,4 +31,4 @@ namespace AZ
return modulePath;
}
} // namespace Internal
} // namespace AZ
} // namespace AZ
@@ -15,45 +15,49 @@
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
namespace AZ
namespace AZ::NativeUI
{
namespace NativeUI
enum AssertAction
{
enum AssertAction
{
IGNORE_ASSERT = 0,
IGNORE_ALL_ASSERTS,
BREAK,
NONE,
};
IGNORE_ASSERT = 0,
IGNORE_ALL_ASSERTS,
BREAK,
NONE,
};
class NativeUIRequests
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
using MutexType = AZStd::recursive_mutex;
class NativeUIRequests
{
public:
AZ_RTTI(NativeUIRequests, "{48361EE6-C1E7-4965-A13A-7425B2691817}");
virtual ~NativeUIRequests() = default;
// Waits for user to select an option before execution continues
// Returns the option string selected by the user
virtual AZStd::string DisplayBlockingDialog(const AZStd::string& /*title*/, const AZStd::string& /*message*/, const AZStd::vector<AZStd::string>& /*options*/) const { return ""; };
// Waits for user to select an option before execution continues
// Returns the option string selected by the user
virtual AZStd::string DisplayBlockingDialog(const AZStd::string& /*title*/, const AZStd::string& /*message*/, const AZStd::vector<AZStd::string>& /*options*/) const { return ""; };
// Waits for user to select an option ('Ok' or optionally 'Cancel') before execution continues
// Returns the option string selected by the user
virtual AZStd::string DisplayOkDialog(const AZStd::string& /*title*/, const AZStd::string& /*message*/, bool /*showCancel*/) const { return ""; };
// Waits for user to select an option ('Ok' or optionally 'Cancel') before execution continues
// Returns the option string selected by the user
virtual AZStd::string DisplayOkDialog(const AZStd::string& /*title*/, const AZStd::string& /*message*/, bool /*showCancel*/) const { return ""; };
// Waits for user to select an option ('Yes', 'No' or optionally 'Cancel') before execution continues
// Returns the option string selected by the user
virtual AZStd::string DisplayYesNoDialog(const AZStd::string& /*title*/, const AZStd::string& /*message*/, bool /*showCancel*/) const { return ""; };
// Waits for user to select an option ('Yes', 'No' or optionally 'Cancel') before execution continues
// Returns the option string selected by the user
virtual AZStd::string DisplayYesNoDialog(const AZStd::string& /*title*/, const AZStd::string& /*message*/, bool /*showCancel*/) const { return ""; };
// Displays an assert dialog box
// Returns the action selected by the user
virtual AssertAction DisplayAssertDialog(const AZStd::string& /*message*/) const { return AssertAction::NONE; };
};
// Displays an assert dialog box
// Returns the action selected by the user
virtual AssertAction DisplayAssertDialog(const AZStd::string& /*message*/) const { return AssertAction::NONE; };
};
using NativeUIRequestBus = AZ::EBus<NativeUIRequests>;
}
}
class NativeUIEBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
using MutexType = AZStd::recursive_mutex;
};
using NativeUIRequestBus = AZ::EBus<NativeUIRequests, NativeUIEBusTraits>;
} // namespace AZ::NativeUI
@@ -15,50 +15,19 @@
#include <AzCore/NativeUI/NativeUISystemComponent.h>
namespace AZ
namespace AZ::NativeUI
{
using namespace AZ::NativeUI;
void NativeUISystemComponent::Reflect(AZ::ReflectContext* context)
NativeUISystem::NativeUISystem()
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<NativeUISystemComponent, AZ::Component>()
->Version(0)
;
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<NativeUISystemComponent>("NativeUI", "Adds basic support for native (platform specific) UI dialog boxes")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
NativeUIRequestBus::Handler::BusConnect();
}
void NativeUISystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
NativeUISystem::~NativeUISystem()
{
provided.push_back(AZ_CRC("NativeUIService", 0x8ec25f87));
NativeUIRequestBus::Handler::BusDisconnect();
}
void NativeUISystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("NativeUIService", 0x8ec25f87));
}
void NativeUISystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
(void)required;
}
void NativeUISystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
(void)dependent;
}
AssertAction NativeUISystemComponent::DisplayAssertDialog(const AZStd::string& message) const
AssertAction NativeUISystem::DisplayAssertDialog(const AZStd::string& message) const
{
static const char* buttonNames[3] = { "Ignore", "Ignore All", "Break" };
AZStd::vector<AZStd::string> options;
@@ -80,7 +49,7 @@ namespace AZ
return AssertAction::NONE;
}
AZStd::string NativeUISystemComponent::DisplayOkDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const
AZStd::string NativeUISystem::DisplayOkDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const
{
AZStd::vector<AZStd::string> options;
@@ -93,7 +62,7 @@ namespace AZ
return DisplayBlockingDialog(title, message, options);
}
AZStd::string NativeUISystemComponent::DisplayYesNoDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const
AZStd::string NativeUISystem::DisplayYesNoDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const
{
AZStd::vector<AZStd::string> options;
@@ -106,18 +75,4 @@ namespace AZ
return DisplayBlockingDialog(title, message, options);
}
void NativeUISystemComponent::Init()
{
}
void NativeUISystemComponent::Activate()
{
NativeUIRequestBus::Handler::BusConnect();
}
void NativeUISystemComponent::Deactivate()
{
NativeUIRequestBus::Handler::BusDisconnect();
}
}
} // namespace AZ::NativeUI
@@ -15,40 +15,24 @@
#include <AzCore/Component/Component.h>
#include <AzCore/NativeUI/NativeUIRequests.h>
namespace AZ
namespace AZ::NativeUI
{
namespace NativeUI
class NativeUISystem
: public NativeUIRequestBus::Handler
{
class NativeUISystemComponent
: public AZ::Component
, public NativeUIRequestBus::Handler
{
public:
AZ_COMPONENT(NativeUISystemComponent, "{E996C058-4AFE-4C8C-816F-98D864D8576D}");
public:
AZ_RTTI(NativeUISystem, "{FF534B2C-11BE-4DEA-A5B7-A4FA96FE1EDE}", NativeUIRequests);
AZ_CLASS_ALLOCATOR(NativeUISystem, AZ::OSAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
NativeUISystem();
~NativeUISystem() override;
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
////////////////////////////////////////////////////////////////////////
// NativeUIRequestBus interface implementation
AZStd::string DisplayBlockingDialog(const AZStd::string& title, const AZStd::string& message, const AZStd::vector<AZStd::string>& options) const override;
AZStd::string DisplayOkDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const override;
AZStd::string DisplayYesNoDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const override;
AssertAction DisplayAssertDialog(const AZStd::string& message) const override;
////////////////////////////////////////////////////////////////////////
protected:
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
};
}
}
////////////////////////////////////////////////////////////////////////
// NativeUIRequestBus interface implementation
AZStd::string DisplayBlockingDialog(const AZStd::string& title, const AZStd::string& message, const AZStd::vector<AZStd::string>& options) const override;
AZStd::string DisplayOkDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const override;
AZStd::string DisplayYesNoDialog(const AZStd::string& title, const AZStd::string& message, bool showCancel) const override;
AssertAction DisplayAssertDialog(const AZStd::string& message) const override;
////////////////////////////////////////////////////////////////////////
};
} // namespace AZ::NativeUI
@@ -113,4 +113,4 @@
#define AZCG_Unpack_98(x, ...) AZCG_Unpack_1(x) AZCG_Unpack_97(__VA_ARGS__)
#define AZCG_Unpack_99(x, ...) AZCG_Unpack_1(x) AZCG_Unpack_98(__VA_ARGS__)
#define AZCG_Unpack(...) AZ_MACRO_SPECIALIZE(AZCG_Unpack_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
#define AZCG_Paste(x) x
#define AZCG_Paste(x) x
@@ -816,7 +816,7 @@ namespace AZ
template<size_t Index>
static void ReflectUnpackMethodFold(BehaviorContext::ClassBuilder<ContainerType>& builder)
{
AZStd::string methodName = AZStd::string::format("Get%ld", Index);
const AZStd::string methodName = AZStd::string::format("Get%zu", Index);
builder->Method(methodName.data(), [](ContainerType& value) { return AZStd::get<Index>(value); })
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::ScriptCanvasAttributes::TupleGetFunctionIndex, Index)
@@ -26,4 +26,4 @@ namespace AZ
void Activate() override { }
void Deactivate() override { }
};
}
}
@@ -79,4 +79,4 @@ namespace AZ
AZ_TYPE_INFO_SPECIALIZE(Script::Attributes::OperatorType, "{26B98C03-7E07-4E3E-9E31-03DA2168E896}");
AZ_TYPE_INFO_SPECIALIZE(Script::Attributes::StorageType, "{57FED71F-B590-4002-9599-A48CB50B0F8E}");
}
}
@@ -32,4 +32,4 @@ namespace AZ
};
typedef AZ::EBus<BehaviorObjectSignalsInterface> BehaviorObjectSignals;
}
}
@@ -138,4 +138,4 @@ namespace AZ
m_currentlyProcessingTypeIds.pop_back();
}
}
}
}
@@ -208,4 +208,4 @@ namespace AZ
}
}
}
}
}
@@ -1411,4 +1411,4 @@ namespace AZ
m_value = entityProperty->m_value;
}
}
}
}
@@ -38,4 +38,4 @@ namespace AZ
};
typedef AZ::EBus<ScriptPropertyWatcherInterface> ScriptPropertyWatcherBus;
}
}
@@ -699,6 +699,10 @@ Data::AssetHandler::LoadResult ScriptSystemComponent::LoadAssetData(
script->m_scriptBuffer.resize(scriptDataLength);
stream->Read(scriptDataLength, script->m_scriptBuffer.data());
// Clear cached references in the event of a successful load. This function has to be queued on
// AssetBus where NotifyAssetReloaded is also queued, to ensure its execution before NotifyAssetReloaded
Data::AssetBus::QueueFunction(&ScriptSystemComponent::ClearAssetReferences, this, asset.GetId());
return Data::AssetHandler::LoadResult::LoadComplete;
}
@@ -853,7 +857,7 @@ const char* ScriptSystemComponent::GetGroup() const
const char* AZ::ScriptSystemComponent::GetBrowserIcon() const
{
return "Editor/Icons/Components/LuaScript.svg";
return "Icons/Components/LuaScript.svg";
}
AZ::Uuid AZ::ScriptSystemComponent::GetComponentTypeId() const
@@ -47,4 +47,4 @@ namespace AZ
}
}
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
#endif // #if !defined(AZCORE_EXCLUDE_LUA)
@@ -24,9 +24,7 @@ namespace AZ
AZ_TYPE_INFO_SPECIALIZE(AZStd::chrono::system_clock::time_point, "{5C48FD59-7267-405D-9C06-1EA31379FE82}");
/**
* Wrapper that reflects a AZStd::chrono::system_clock::time_point to script.
*/
//! Wrapper that reflects a AZStd::chrono::system_clock::time_point to script.
class ScriptTimePoint
{
public:
@@ -38,33 +36,45 @@ namespace AZ
explicit ScriptTimePoint(AZStd::chrono::system_clock::time_point timePoint)
: m_timePoint(timePoint) {}
AZStd::string ToString() const
{
return AZStd::string::format("Time %llu", m_timePoint.time_since_epoch().count());
}
//! Formats the time point in a string formatted as: "Time <seconds since epoch>".
AZStd::string ToString() const;
const AZStd::chrono::system_clock::time_point& Get() { return m_timePoint; }
//! Returns the time point.
const AZStd::chrono::system_clock::time_point& Get() const;
// Returns the time point in seconds
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 seconds
double GetSeconds() const;
// Returns the time point in milliseconds
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();
}
//! Returns the time point in milliseconds
double GetMilliseconds() const;
static void Reflect(ReflectContext* reflection);
protected:
AZStd::chrono::system_clock::time_point m_timePoint;
};
inline AZStd::string ScriptTimePoint::ToString() const
{
return AZStd::string::format("Time %llu", m_timePoint.time_since_epoch().count());
}
inline const AZStd::chrono::system_clock::time_point& ScriptTimePoint::Get() const
{
return m_timePoint;
}
inline double ScriptTimePoint::GetSeconds() const
{
typedef AZStd::chrono::duration<double> double_seconds;
return AZStd::chrono::duration_cast<double_seconds>(m_timePoint.time_since_epoch()).count();
}
inline double ScriptTimePoint::GetMilliseconds() const
{
typedef AZStd::chrono::duration<double, AZStd::milli> double_ms;
return AZStd::chrono::duration_cast<double_ms>(m_timePoint.time_since_epoch()).count();
}
}
@@ -287,4 +287,4 @@ namespace AZ
return nullptr;
}
}
}
@@ -53,6 +53,10 @@ namespace AZ
//! RemoveableByUser : A bool which determines if the component can be removed by the user.
//! Setting this to false prevents the user from removing this component. Default behavior is removeable by user.
const static AZ::Crc32 RemoveableByUser = AZ_CRC("RemoveableByUser", 0x32c7fd50);
//! An int which, if specified, causes a component to be forced to a particular position in the sorted list of
//! components on an entity, and prevents dragging or moving operations which would affect that position.
const static AZ::Crc32 FixedComponentListIndex = AZ_CRC_CE("FixedComponentListIndex");
const static AZ::Crc32 AppearsInAddComponentMenu = AZ_CRC("AppearsInAddComponentMenu", 0x53790e31);
const static AZ::Crc32 ForceAutoExpand = AZ_CRC("ForceAutoExpand", 0x1a5c79d2); // Ignores expansion state set by user, enforces expansion.
const static AZ::Crc32 AutoExpand = AZ_CRC("AutoExpand", 0x306ff5c0); // Expands automatically unless user changes expansion state.
@@ -118,6 +122,7 @@ namespace AZ
const static AZ::Crc32 StringLineEditingCompleteNotify = AZ_CRC("StringLineEditingCompleteNotify", 0x139e5fa9);
const static AZ::Crc32 NameLabelOverride = AZ_CRC("NameLabelOverride", 0x9ff79cab);
const static AZ::Crc32 AssetPickerTitle = AZ_CRC_CE("AssetPickerTitle");
const static AZ::Crc32 ChildNameLabelOverride = AZ_CRC("ChildNameLabelOverride", 0x73dd2909);
//! Container attribute that is used to override labels for its elements given the index of the element
const static AZ::Crc32 IndexedChildNameLabelOverride = AZ_CRC("IndexedChildNameLabelOverride", 0x5f313ac2);
@@ -74,7 +74,7 @@ namespace AZ
"Unable to retrieve the correct container information for AZStd::array instance.");
}
Flags flags = Flags::None;
ContinuationFlags flags = ContinuationFlags::None;
Uuid elementTypeId = Uuid::CreateNull();
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
{
@@ -82,7 +82,7 @@ namespace AZ
elementTypeId = genericClassElement->m_typeId;
if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
flags = Flags::ResolvePointer;
flags = ContinuationFlags::ResolvePointer;
}
return false;
};
@@ -161,7 +161,7 @@ namespace AZ
"Not enough entries in JSON array to load an AZStd::array from.");
}
Flags flags = Flags::None;
ContinuationFlags flags = ContinuationFlags::None;
Uuid elementTypeId = Uuid::CreateNull();
auto typeEnumCallback = [&elementTypeId, &flags](const Uuid&, const SerializeContext::ClassElement* genericClassElement)
{
@@ -169,7 +169,7 @@ namespace AZ
elementTypeId = genericClassElement->m_typeId;
if (genericClassElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
flags = Flags::ResolvePointer;
flags = ContinuationFlags::ResolvePointer;
}
return false;
};
@@ -208,22 +208,28 @@ namespace AZ
// BaseJsonSerializer
//
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context, Flags flags)
BaseJsonSerializer::OperationFlags BaseJsonSerializer::GetOperationsFlags() const
{
return flags & Flags::ResolvePointer ?
JsonDeserializer::LoadToPointer(object, typeId, value, context) :
JsonDeserializer::Load(object, typeId, value, context);
return OperationFlags::None;
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(rapidjson::Value& output, const void* object,
const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags)
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoading(
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
{
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
: JsonDeserializer::Load(object, typeId, value, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context,
ContinuationFlags flags)
{
using namespace JsonSerializationResult;
if (flags & Flags::ReplaceDefault && !context.ShouldKeepDefaults())
if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults())
{
if (flags & Flags::ResolvePointer)
if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer)
{
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context);
}
@@ -248,7 +254,7 @@ namespace AZ
}
}
return flags & Flags::ResolvePointer ?
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ?
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) :
JsonSerializer::Store(output, object, defaultObject, typeId, context);
}
@@ -265,8 +271,9 @@ namespace AZ
return JsonSerializer::StoreTypeName(output, typeId, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value,
rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags)
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueLoadingFromJsonObjectField(
void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName,
JsonDeserializerContext& context, ContinuationFlags flags)
{
using namespace JsonSerializationResult;
@@ -291,7 +298,7 @@ namespace AZ
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoringToJsonObjectField(rapidjson::Value& output,
rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context, Flags flags)
const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags)
{
using namespace JsonSerializationResult;
@@ -161,13 +161,19 @@ namespace AZ
public:
AZ_RTTI(BaseJsonSerializer, "{7291FFDC-D339-40B5-BB26-EA067A327B21}");
enum Flags
enum class ContinuationFlags
{
None = 0, //! No extra flags.
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1 //! The default value provided for storing will be replaced with a newly created one.
};
enum class OperationFlags
{
None = 0, //! No flags that control how the custom json serializer is used.
ManualDefault = 1 << 0 //! Even if an (explicit) default is found the custom json serializer will still be called.
};
virtual ~BaseJsonSerializer() = default;
//! Transforms the data from the rapidjson Value to outputValue, if the conversion is possible and supported.
@@ -180,6 +186,9 @@ namespace AZ
virtual JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) = 0;
//! Returns the operation flags which tells the Json Serialization how this custom json serializer can be used.
virtual OperationFlags GetOperationsFlags() const;
protected:
//! Continues loading of a (sub)value. Use this function to load member variables for instance. This is more optimal than
//! directly calling the json serialization.
@@ -187,8 +196,9 @@ namespace AZ
//! @param typeId Type id of the object passed in.
//! @param value The value in the JSON document where the deserializer will start reading data from.
//! @param context The context used during deserialization. Use the value passed in from Load.
JsonSerializationResult::ResultCode ContinueLoading(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueLoading(
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context,
ContinuationFlags flags = ContinuationFlags::None);
//! Continues storing of a (sub)value. Use this function to store member variables for instance. This is more optimal than
//! directly calling the json serialization.
@@ -200,8 +210,9 @@ namespace AZ
//! the settings.
//! @param typeId The type id of the object and default object.
//! @param context The context used during serialization. Use the value passed in from Store.
JsonSerializationResult::ResultCode ContinueStoring(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueStoring(
rapidjson::Value& output, const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context,
ContinuationFlags flags = ContinuationFlags::None);
//! Retrieves the type id from a json object or json string.
//! @param typeId The retrieved type id.
@@ -222,12 +233,14 @@ namespace AZ
const Uuid& typeId, JsonSerializerContext& context);
//! Helper function similar to ContinueLoading, but loads the data as a member of 'value' rather than 'value' itself, if it exists.
JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField(void* object, const Uuid& typeId, const rapidjson::Value& value,
rapidjson::Value::StringRefType memberName, JsonDeserializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueLoadingFromJsonObjectField(
void* object, const Uuid& typeId, const rapidjson::Value& value, rapidjson::Value::StringRefType memberName,
JsonDeserializerContext& context, ContinuationFlags flags = ContinuationFlags::None);
//! Helper function similar to ContinueStoring, but stores the data as a member of 'output' rather than overwriting 'output'.
JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField(rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName,
const void* object, const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context, Flags flags = Flags::None);
JsonSerializationResult::ResultCode ContinueStoringToJsonObjectField(
rapidjson::Value& output, rapidjson::Value::StringRefType newMemberName, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context, ContinuationFlags flags = ContinuationFlags::None);
//! Checks if a value is an explicit default. This useful for containers where not storing anything as a default would mean
//! a slot wouldn't be used so something has to be added to represent the fully default target.
@@ -238,6 +251,7 @@ namespace AZ
rapidjson::Value GetExplicitDefault();
};
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::Flags)
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::ContinuationFlags)
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::BaseJsonSerializer::OperationFlags)
} // namespace AZ
@@ -75,9 +75,10 @@ namespace AZ
auto elementCallback = [this, &array, &retVal, &index, &context]
(void* elementPtr, const Uuid& elementId, const SerializeContext::ClassData*, const SerializeContext::ClassElement* classElement)
{
Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
flags |= Flags::ReplaceDefault;
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
flags |= ContinuationFlags::ReplaceDefault;
ScopedContextPath subPath(context, index);
index++;
@@ -161,8 +162,9 @@ namespace AZ
container->EnumTypes(typeEnumCallback);
AZ_Assert(classElement, "No class element found for the type in the basic container.");
Flags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
ContinuationFlags flags = classElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
const size_t capacity = container->IsFixedCapacity() ? container->Capacity(outputValue) : std::numeric_limits<size_t>::max();
@@ -10,91 +10,75 @@
*
*/
#include "ByteStreamSerializer.h"
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/Json/ByteStreamSerializer.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/StringFunc/StringFunc.h>
namespace AZ
{
namespace ByteSerializerInternal
{
static JsonSerializationResult::Result Load(void* outputValue, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
{
using JsonSerializationResult::Outcomes;
using JsonSerializationResult::Tasks;
AZ_Assert(outputValue, "Expected a valid pointer to load from json value.");
switch (inputValue.GetType())
{
case rapidjson::kStringType: {
JsonByteStream buffer;
if (AZ::StringFunc::Base64::Decode(buffer, inputValue.GetString(), inputValue.GetStringLength()))
{
JsonByteStream* valAsByteStream = static_cast<JsonByteStream*>(outputValue);
*valAsByteStream = AZStd::move(buffer);
return context.Report(Tasks::ReadField, Outcomes::Success, "Successfully read ByteStream.");
}
return context.Report(Tasks::ReadField, Outcomes::Invalid, "Decode of Base64 encoded ByteStream failed.");
}
case rapidjson::kArrayType:
case rapidjson::kObjectType:
case rapidjson::kNullType:
case rapidjson::kFalseType:
case rapidjson::kTrueType:
case rapidjson::kNumberType:
return context.Report(
Tasks::ReadField, Outcomes::Unsupported,
"Unsupported type. ByteStream values cannot be read from arrays, objects, nulls, booleans or numbers.");
default:
return context.Report(Tasks::ReadField, Outcomes::Unknown, "Unknown json type encountered for ByteStream value.");
}
}
static JsonSerializationResult::Result StoreWithDefault(
rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, JsonSerializerContext& context)
{
using JsonSerializationResult::Outcomes;
using JsonSerializationResult::Tasks;
const JsonByteStream& valAsByteStream = *static_cast<const JsonByteStream*>(inputValue);
if (context.ShouldKeepDefaults() || !defaultValue || (valAsByteStream != *static_cast<const JsonByteStream*>(defaultValue)))
{
const auto base64ByteStream = AZ::StringFunc::Base64::Encode(valAsByteStream.data(), valAsByteStream.size());
outputValue.SetString(base64ByteStream.c_str(), base64ByteStream.size(), context.GetJsonAllocator());
return context.Report(Tasks::WriteValue, Outcomes::Success, "ByteStream successfully stored.");
}
return context.Report(Tasks::WriteValue, Outcomes::DefaultsUsed, "Default ByteStream used.");
}
} // namespace ByteSerializerInternal
AZ_CLASS_ALLOCATOR_IMPL(JsonByteStreamSerializer, SystemAllocator, 0);
JsonSerializationResult::Result JsonByteStreamSerializer::Load(
void* outputValue, [[maybe_unused]] const Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
JsonDeserializerContext& context)
{
using JsonSerializationResult::Outcomes;
using JsonSerializationResult::Tasks;
AZ_Assert(
azrtti_typeid<JsonByteStream>() == outputValueTypeId,
"Unable to deserialize AZStd::vector<AZ::u8>> to json because the provided type is %s",
outputValueTypeId.ToString<AZStd::string>().c_str());
AZ_Assert(outputValue, "Expected a valid pointer to load from json value.");
return ByteSerializerInternal::Load(outputValue, inputValue, context);
switch (inputValue.GetType())
{
case rapidjson::kStringType: {
JsonByteStream buffer;
if (AZ::StringFunc::Base64::Decode(buffer, inputValue.GetString(), inputValue.GetStringLength()))
{
JsonByteStream* valAsByteStream = static_cast<JsonByteStream*>(outputValue);
*valAsByteStream = AZStd::move(buffer);
return context.Report(Tasks::ReadField, Outcomes::Success, "Successfully read ByteStream.");
}
return context.Report(Tasks::ReadField, Outcomes::Invalid, "Decode of Base64 encoded ByteStream failed.");
}
case rapidjson::kArrayType:
case rapidjson::kObjectType:
case rapidjson::kNullType:
case rapidjson::kFalseType:
case rapidjson::kTrueType:
case rapidjson::kNumberType:
return context.Report(
Tasks::ReadField, Outcomes::Unsupported,
"Unsupported type. ByteStream values cannot be read from arrays, objects, nulls, booleans or numbers.");
default:
return context.Report(Tasks::ReadField, Outcomes::Unknown, "Unknown json type encountered for ByteStream value.");
}
}
JsonSerializationResult::Result JsonByteStreamSerializer::Store(
rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, [[maybe_unused]] const Uuid& valueTypeId,
JsonSerializerContext& context)
{
using JsonSerializationResult::Outcomes;
using JsonSerializationResult::Tasks;
AZ_Assert(
azrtti_typeid<JsonByteStream>() == valueTypeId,
"Unable to serialize AZStd::vector<AZ::u8> to json because the provided type is %s",
valueTypeId.ToString<AZStd::string>().c_str());
return ByteSerializerInternal::StoreWithDefault(outputValue, inputValue, defaultValue, context);
const JsonByteStream& valAsByteStream = *static_cast<const JsonByteStream*>(inputValue);
if (context.ShouldKeepDefaults() || !defaultValue || (valAsByteStream != *static_cast<const JsonByteStream*>(defaultValue)))
{
const auto base64ByteStream = AZ::StringFunc::Base64::Encode(valAsByteStream.data(), valAsByteStream.size());
outputValue.SetString(base64ByteStream.c_str(), base64ByteStream.size(), context.GetJsonAllocator());
return context.Report(Tasks::WriteValue, Outcomes::Success, "ByteStream successfully stored.");
}
return context.Report(Tasks::WriteValue, Outcomes::DefaultsUsed, "Default ByteStream used.");
}
} // namespace AZ
@@ -22,6 +22,19 @@
namespace AZ
{
JsonSerializationResult::ResultCode JsonDeserializer::DeserializerDefaultCheck(BaseJsonSerializer* serializer, void* object,
const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
bool isExplicitDefault = IsExplicitDefault(value);
bool manuallyDefaults = (serializer->GetOperationsFlags() & BaseJsonSerializer::OperationFlags::ManualDefault) ==
BaseJsonSerializer::OperationFlags::ManualDefault;
return !isExplicitDefault || (isExplicitDefault && manuallyDefaults)
? serializer->Load(object, typeId, value, context)
: context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
JsonSerializationResult::ResultCode JsonDeserializer::Load(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context)
{
@@ -33,17 +46,12 @@ namespace AZ
"Target object for Json Serialization is pointing to nothing during loading.");
}
if (IsExplicitDefault(value))
{
return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
{
return serializer->Load(object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
}
const SerializeContext::ClassData* classData = context.GetSerializeContext()->FindClassData(typeId);
if (!classData)
{
@@ -56,9 +64,14 @@ namespace AZ
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
if (serializer)
{
return serializer->Load(object, typeId, value, context);
return DeserializerDefaultCheck(serializer, object, typeId, value, context);
}
}
if (IsExplicitDefault(value))
{
return context.Report(Tasks::ReadField, Outcomes::DefaultsUsed, "Value has an explicit default.");
}
if (classData->m_azRtti && (classData->m_azRtti->GetTypeTraits() & AZ::TypeTraits::is_enum) == AZ::TypeTraits::is_enum)
{
@@ -97,7 +110,8 @@ namespace AZ
return context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
AZStd::string::format("Failed to retrieve rtti information for %s.", classData->m_name));
}
AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch during deserialization of a json file. (%s vs %s)");
AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch during deserialization of a json file. (%s vs %s)",
classData->m_azRtti->GetTypeId().ToString<AZStd::string>().c_str(), typeId.ToString<AZStd::string>().c_str());
void** objectPtr = reinterpret_cast<void**>(object);
bool isNull = *objectPtr == nullptr;
@@ -512,27 +526,24 @@ namespace AZ
if (*object)
{
const AZ::Uuid& actualClassId = rtti.GetActualUuid(*object);
if (actualClassId != objectType)
const SerializeContext::ClassData* actualClassData = context.GetSerializeContext()->FindClassData(actualClassId);
if (!actualClassData)
{
const SerializeContext::ClassData* actualClassData = context.GetSerializeContext()->FindClassData(actualClassId);
if (!actualClassData)
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
AZStd::string::format("Unable to find serialization information for type %s.", actualClassId.ToString<AZStd::string>().c_str()));
return ResolvePointerResult::FullyProcessed;
}
status = context.Report(Tasks::RetrieveInfo, Outcomes::Unknown,
AZStd::string::format("Unable to find serialization information for type %s.", actualClassId.ToString<AZStd::string>().c_str()));
return ResolvePointerResult::FullyProcessed;
}
if (actualClassData->m_factory)
{
actualClassData->m_factory->Destroy(*object);
*object = nullptr;
}
else
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Catastrophic,
"Unable to find the factory needed to clear out the default value.");
return ResolvePointerResult::FullyProcessed;
}
if (actualClassData->m_factory)
{
actualClassData->m_factory->Destroy(*object);
*object = nullptr;
}
else
{
status = context.Report(Tasks::RetrieveInfo, Outcomes::Catastrophic,
"Unable to find the factory needed to clear out the default value.");
return ResolvePointerResult::FullyProcessed;
}
}
status = ResultCode(Tasks::ReadField, Outcomes::Success);
@@ -113,5 +113,13 @@ namespace AZ
//! Checks if a value is an explicit default. This means the value is an object with no members.
static bool IsExplicitDefault(const rapidjson::Value& value);
private:
static JsonSerializationResult::ResultCode DeserializerDefaultCheck(
BaseJsonSerializer* serializer,
void* object,
const Uuid& typeId,
const rapidjson::Value& value,
JsonDeserializerContext& context);
};
} // namespace AZ
@@ -11,13 +11,17 @@
*/
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/JSON/stringbuffer.h>
#include <AzCore/Serialization/Json/JsonMerger.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/StackedString.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/osstring.h>
namespace AZ
{
using ReporterString = AZStd::fixed_string<1024>;
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings)
@@ -105,8 +109,7 @@ namespace AZ
}
else
{
AZ::OSString message = AZ::OSString::format(R"(Unknown operation "%.*s".)",
aznumeric_cast<int>(operationName.length()), operationName.data());
auto message = ReporterString::format(R"(Unknown operation "%.*s".)", AZ_STRING_ARG(operationName));
return settings.m_reporting(message.c_str(), ResultCode(Tasks::Merge, Outcomes::Unknown), element);
}
@@ -131,6 +134,14 @@ namespace AZ
JsonSerializationResult::ResultCode JsonMerger::ApplyMergePatch(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings)
{
StackedString element(StackedString::Format::JsonPointer);
return ApplyMergePatchInternal(target, allocator, patch, settings, element);
}
JsonSerializationResult::ResultCode JsonMerger::ApplyMergePatchInternal(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings, StackedString& element)
{
using namespace JsonSerializationResult;
@@ -150,14 +161,18 @@ namespace AZ
{
if (targetField != target.MemberEnd())
{
result.Combine(ApplyMergePatch(targetField->value, allocator, field.value, settings));
ScopedStackedString fieldNameScope{ element,
AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
result.Combine(ApplyMergePatchInternal(targetField->value, allocator, field.value, settings, element));
}
else
{
rapidjson::Value name;
name.CopyFrom(field.name, allocator, true);
rapidjson::Value value;
result.Combine(ApplyMergePatch(value, allocator, field.value, settings));
ScopedStackedString fieldNameScope{ element,
AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
result.Combine(ApplyMergePatchInternal(value, allocator, field.value, settings, element));
target.AddMember(AZStd::move(name), AZStd::move(value), allocator);
}
}
@@ -165,7 +180,14 @@ namespace AZ
{
if (targetField != target.MemberEnd())
{
ScopedStackedString fieldNameScope{ element,
AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
AZStd::string_view jsonPath = element.Get();
target.RemoveMember(targetField);
result.Combine(settings.m_reporting(ReporterString::format(
R"(Successfully removed member from "%.*s" using JSON Merge Patch)", AZ_STRING_ARG(jsonPath)),
ResultCode(Tasks::Merge, Outcomes::Success), element));
}
}
else
@@ -173,6 +195,12 @@ namespace AZ
if (targetField != target.MemberEnd())
{
targetField->value.CopyFrom(field.value, allocator, true);
ScopedStackedString fieldNameScope{ element, AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
AZStd::string_view jsonPath = element.Get();
result.Combine(settings.m_reporting(ReporterString::format(
R"(Successfully updated JSON field "%.*s" using JSON Merge Patch)", AZ_STRING_ARG(jsonPath)),
ResultCode(Tasks::Merge, Outcomes::Success), element));
}
else
{
@@ -181,6 +209,12 @@ namespace AZ
name.CopyFrom(field.name, allocator, true);
value.CopyFrom(field.value, allocator, true);
target.AddMember(AZStd::move(name), AZStd::move(value), allocator);
ScopedStackedString fieldNameScope{ element, AZStd::string_view(field.name.GetString(), field.name.GetStringLength()) };
AZStd::string_view jsonPath = element.Get();
result.Combine(settings.m_reporting(ReporterString::format(
R"(Successfully added JSON field "%.*s" using JSON Merge Patch)", AZ_STRING_ARG(jsonPath)),
ResultCode(Tasks::Merge, Outcomes::Success), element));
}
}
}
@@ -190,7 +224,7 @@ namespace AZ
target.CopyFrom(patch, allocator, true);
}
result.Combine(settings.m_reporting("Successfully applied patch to target using JSON Merge Patch.",
ResultCode(Tasks::Merge, Outcomes::Success), StackedString(StackedString::Format::JsonPointer)));
ResultCode(Tasks::Merge, Outcomes::Success), element));
return result;
}
@@ -268,9 +302,11 @@ namespace AZ
const rapidjson::Pointer::Token* const tokens = path.GetTokens();
if (path.GetTokenCount() == 0)
{
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
target = AZStd::move(newValue);
return settings.m_reporting(R"(Successfully applied "add" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
rapidjson::Pointer parent = rapidjson::Pointer(tokens, path.GetTokenCount() - 1);
@@ -342,8 +378,10 @@ namespace AZ
ResultCode(Tasks::Merge, Outcomes::TypeMismatch), element);
}
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
return settings.m_reporting(R"(Successfully applied "add" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch_Remove(rapidjson::Value& target, const rapidjson::Pointer& path,
@@ -393,8 +431,10 @@ namespace AZ
ResultCode(Tasks::Merge, Outcomes::TypeMismatch), element);
}
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
return settings.m_reporting(R"(Successfully applied "remove" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch_Replace(rapidjson::Value& target,
@@ -420,8 +460,10 @@ namespace AZ
memberValue->CopyFrom(value->value, allocator);
rapidjson::StringBuffer pointerPathString;
path.Stringify(pointerPathString);
return settings.m_reporting(R"(Successfully applied "replace" operation.)",
ResultCode(Tasks::Merge, Outcomes::Success), element);
ResultCode(Tasks::Merge, Outcomes::Success), pointerPathString.GetString());
}
JsonSerializationResult::ResultCode JsonMerger::ApplyPatch_Move(rapidjson::Value& target,
@@ -42,6 +42,11 @@ namespace AZ
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings);
//! Implementation of the JSON Merge Patch algorithm: https://tools.ietf.org/html/rfc7386
static JsonSerializationResult::ResultCode ApplyMergePatchInternal(rapidjson::Value& target,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch,
JsonApplyPatchSettings& settings, StackedString& element);
//! Function to create JSON Merge Patches: https://tools.ietf.org/html/rfc7386
static JsonSerializationResult::ResultCode CreateMergePatch(rapidjson::Value& patch,
rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& source,
@@ -38,6 +38,20 @@ namespace AZ
};
//! Core class to handle serialization to and from json documents.
//! The Json Serialization works by taking a default constructed object and then apply the information found in the JSON document
//! on top of that object. This allows the Json Serialization to avoid storing default values and helps guarantee that the final
//! object is in a valid state even if non-fatal issues are encountered.
//! Note on containers: Containers such as vector or map are always considered to be empty even if there's entries in the provided
//! default object. During deserialization entries will be appended to any existing values. A flag is provided to automatically
//! clear containers during deserialization.
//! Note on maps: If the key for map containers such as unordered_map can be interpret as a string the Json Serialization will use
//! a JSON Object to store the data in instead of an array with key/value objects.
//! Note on pointers: The Json Serialization assumes that are always constructed, so a default JSON value of "{}" is interpret as
//! creating a new default instance even if the default value is a null pointer. A JSON Null needs to be explicitly stored in
//! the JSON Document in order to default or explicitly set a pointer to null.
//! Note on pointer memory: Objects created/destroyed by the Json Serialization for pointers require that the AZ_CLASS_ALLOCATOR is
//! declared and the object is created using aznew or memory is allocated using azmalloc. Without these the application may
//! crash if the Json Serialization tries to create or destroy an object pointed to by a pointer.
class JsonSerialization final
{
public:
@@ -104,13 +104,12 @@ namespace AZ
auto serializer = context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId);
if (serializer)
{
if (storeTypeId == StoreTypeId::Yes)
ResultCode result = serializer->Store(node, object, defaultObject, classData.m_typeId, context);
if (storeTypeId == StoreTypeId::Yes && result.GetProcessing() != Processing::Halted)
{
return context.Report(Tasks::WriteValue, Outcomes::Catastrophic,
"Unable to store type information in a JSON Serializer primitive.");
result.Combine(InsertTypeId(node, classData, context));
}
return serializer->Store(node, object, defaultObject, classData.m_typeId, context);
return result;
}
if (classData.m_azRtti && (classData.m_azRtti->GetTypeTraits() & AZ::TypeTraits::is_enum) == AZ::TypeTraits::is_enum)
@@ -128,13 +127,12 @@ namespace AZ
serializer = context.GetRegistrationContext()->GetSerializerForType(classData.m_azRtti->GetGenericTypeId());
if (serializer)
{
if (storeTypeId == StoreTypeId::Yes)
ResultCode result = serializer->Store(node, object, defaultObject, classData.m_typeId, context);
if (storeTypeId == StoreTypeId::Yes && result.GetProcessing() != Processing::Halted)
{
return context.Report(Tasks::WriteValue, Outcomes::Catastrophic,
"Unable to store type information in a JSON Serializer primitive.");
result.Combine(InsertTypeId(node, classData, context));
}
return serializer->Store(node, object, defaultObject, classData.m_typeId, context);
return result;
}
}
@@ -149,6 +147,7 @@ namespace AZ
ResultCode result(Tasks::WriteValue);
if (storeTypeId == StoreTypeId::Yes)
{
// Not using InsertTypeId here to avoid needing to create the temporary value and swap it in that call.
node.AddMember(rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier),
StoreTypeName(classData, context), context.GetJsonAllocator());
result = ResultCode(Tasks::WriteValue, Outcomes::Success);
@@ -569,6 +568,31 @@ namespace AZ
}
}
JsonSerializationResult::ResultCode JsonSerializer::InsertTypeId(
rapidjson::Value& output, const SerializeContext::ClassData& classData, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
if (output.IsObject())
{
rapidjson::Value insertedObject(rapidjson::kObjectType);
insertedObject.AddMember(
rapidjson::StringRef(JsonSerialization::TypeIdFieldIdentifier), StoreTypeName(classData, context),
context.GetJsonAllocator());
for (auto& element : output.GetObject())
{
insertedObject.AddMember(AZStd::move(element.name), AZStd::move(element.value), context.GetJsonAllocator());
}
output = AZStd::move(insertedObject);
return ResultCode(Tasks::WriteValue, Outcomes::Success);
}
else
{
return context.Report(Tasks::WriteValue, Outcomes::Catastrophic, "Only able to store type information in a JSON Object.");
}
}
rapidjson::Value JsonSerializer::GetExplicitDefault()
{
return rapidjson::Value(rapidjson::kObjectType);
@@ -80,6 +80,9 @@ namespace AZ
static JsonSerializationResult::ResultCode StoreTypeName(rapidjson::Value& output,
const Uuid& typeId, JsonSerializerContext& context);
static JsonSerializationResult::ResultCode InsertTypeId(
rapidjson::Value& output, const SerializeContext::ClassData& classData, JsonSerializerContext& context);
static rapidjson::Value GetExplicitDefault();
};
} // namespace AZ
@@ -215,10 +215,10 @@ namespace AZ
// Load key
void* keyAddress = pairContainer->GetElementByIndex(address, pairElement, 0);
AZ_Assert(keyAddress, "Element reserved for associative container, but unable to retrieve address of the key.");
Flags keyLoadFlags = Flags::None;
ContinuationFlags keyLoadFlags = ContinuationFlags::None;
if (keyElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
keyLoadFlags = Flags::ResolvePointer;
keyLoadFlags = ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(keyAddress) = nullptr;
}
JSR::ResultCode keyResult = ContinueLoading(keyAddress, keyElement->m_typeId, key, context, keyLoadFlags);
@@ -231,10 +231,10 @@ namespace AZ
// Load value
void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1);
AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value.");
Flags valueLoadFlags = Flags::None;
ContinuationFlags valueLoadFlags = ContinuationFlags::None;
if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER)
{
valueLoadFlags = Flags::ResolvePointer;
valueLoadFlags = ContinuationFlags::ResolvePointer;
*reinterpret_cast<void**>(valueAddress) = nullptr;
}
JSR::ResultCode valueResult = ContinueLoading(valueAddress, valueElement->m_typeId, value, context, valueLoadFlags);
@@ -23,13 +23,6 @@ namespace AZ
{
namespace JSR = JsonSerializationResult;
if (IsExplicitDefault(inputValue))
{
// Do nothing if the input is an explicit default.
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::DefaultsUsed,
"Default value for smart pointer requested so no change was made.");
}
const SerializeContext::ClassData* containerClass = context.GetSerializeContext()->FindClassData(outputValueTypeId);
if (!containerClass)
{
@@ -89,7 +82,7 @@ namespace AZ
{
// If the target type is the same as the type already stored in the smart pointer than no new
// instance is created and the existing instance will be updated with the data in the json document.
result = ContinueLoading(instance, elementClassId, inputValue, context, Flags::ResolvePointer);
result = ContinueLoading(instance, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer);
return false;
}
}
@@ -100,7 +93,7 @@ namespace AZ
// the wrong address. In these cases explicitly reset the smart pointer. This will erase the existing
// data but that's fine as it's not being used.
void* element = nullptr;
result = ContinueLoading(&element, elementClassId, inputValue, context, Flags::ResolvePointer);
result = ContinueLoading(&element, elementClassId, inputValue, context, ContinuationFlags::ResolvePointer);
if (result.GetProcessing() != JSR::Processing::Halted && result.GetProcessing() != JSR::Processing::Altered)
{
void* elementPtr = container->ReserveElement(instance, nullptr);
@@ -153,8 +146,7 @@ namespace AZ
if (defaultValue)
{
bool typesMatch = false;
auto defaultInputCallback = [&defaultValue, &inputPtrType, &typesMatch]
auto defaultInputCallback = [&defaultValue]
(void* elementPtr, const Uuid&, const SerializeContext::ClassData*, const SerializeContext::ClassElement*)
{
defaultValue = elementPtr;
@@ -163,13 +155,14 @@ namespace AZ
container->EnumElements(const_cast<void*>(defaultValue), defaultInputCallback);
}
JSR::ResultCode result = ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, Flags::ResolvePointer);
if (result.GetOutcome() == JSR::Outcomes::DefaultsUsed)
{
outputValue = GetExplicitDefault();
return context.Report(result, "Smart pointer used all defaults.");
}
JSR::ResultCode result =
ContinueStoring(outputValue, inputValue, defaultValue, inputPtrType, context, ContinuationFlags::ResolvePointer);
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted ?
"Successfully processed smart pointer." : "A problem occurred while processing a smart pointer.");
}
BaseJsonSerializer::OperationFlags JsonSmartPointerSerializer::GetOperationsFlags() const
{
return OperationFlags::ManualDefault;
}
} // namespace AZ
@@ -28,5 +28,7 @@ namespace AZ
JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue,
const Uuid& valueTypeId, JsonSerializerContext& context) override;
OperationFlags GetOperationsFlags() const override;
};
} // namespace AZ
@@ -99,8 +99,9 @@ namespace AZ
ScopedContextPath subPath(context, i);
Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
JSR::ResultCode result = ContinueStoring(elementValues[i], elementAddress, defaultElementAddress,
classElements[i]->m_typeId, context, flags);
@@ -179,8 +180,9 @@ namespace AZ
void* elementAddress = container->GetElementByIndex(outputValue, nullptr, i);
AZ_Assert(elementAddress, "Address of AZStd::pair or AZStd::tuple element %zu could not be retrieved.", i);
Flags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER ?
Flags::ResolvePointer : Flags::None;
ContinuationFlags flags = classElements[i]->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER
? ContinuationFlags::ResolvePointer
: ContinuationFlags::None;
while (arrayIndex < inputValue.Size())
{
@@ -14,8 +14,6 @@
#include <limits>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Memory/OSAllocator.h>
#include <AzCore/Memory/SystemAllocator.h>
@@ -88,4 +88,28 @@ namespace AZ
{
return index < m_names.size() ? m_names[index] : AZStd::string_view();
}
SettingsRegistryInterface::CommandLineArgumentSettings::CommandLineArgumentSettings()
{
m_delimiterFunc = [](AZStd::string_view line) -> JsonPathValue
{
constexpr AZStd::string_view CommandLineArgumentDelimiters{ "=:" };
JsonPathValue pathValue;
pathValue.m_value = line;
// Splits the line on the first delimiter and stores that in the pathValue.m_path variable
// The StringFunc::TokenizeNext function updates the pathValue.m_value parameter in place
// to contain all the text after the first delimiter
// So if pathValue.m_value="foo = Hello Ice Cream=World:17", the call to TokenizeNext would
// split the value as follows
// pathValue.m_path = "foo"
// pathValue.m_value = "Hello Ice Cream=World:17"
if (auto path = AZ::StringFunc::TokenizeNext(pathValue.m_value, CommandLineArgumentDelimiters); path.has_value())
{
pathValue.m_path = AZ::StringFunc::StripEnds(*path);
}
pathValue.m_value = AZ::StringFunc::StripEnds(pathValue.m_value);
return pathValue;
};
}
} // namespace AZ
@@ -26,6 +26,7 @@
namespace AZ
{
struct JsonApplyPatchSettings;
//! The Settings Registry is the central storage for global settings. Having application-wide settings
//! stored in a central location allows different tools such as command lines, consoles, configuration
//! files, etc. to work in a universal way.
@@ -256,25 +257,24 @@ namespace AZ
//! Remove the value at the provided path
//! @param path The path to a value that should be removed
//! @return Whether or not the value was stored at the provided path. An invalid path will return false;
//! @return Whether or not the path was found and removed. An invalid path will return false;
virtual bool Remove(AZStd::string_view path) = 0;
//! Structure which contains configuration settings for how to parse a single command line argument
//! It supports supplying a functor for determining if a character is a delimiter
//! It supports supplying a functor for splitting a line into JSON path and JSON value
struct CommandLineArgumentSettings
{
inline static constexpr AZStd::string_view CommandLineArgumentDelimiters{ "=:"};
CommandLineArgumentSettings()
struct JsonPathValue
{
m_delimiterFunc = [](const char delimiter) -> bool
{
return CommandLineArgumentDelimiters.find_first_of(delimiter) != AZStd::string_view::npos;
};
}
//! Callback function which is invoked to determine whether a delimiter has been found
//! return value of true indicates that a delimiter has been found
using DelimiterFunc = AZStd::function<bool(const char delimiter)>;
AZStd::string_view m_path;
AZStd::string_view m_value;
};
CommandLineArgumentSettings();
//! Callback function which is invoked to determine how to split a command line argument
//! into a JSON path and a JSON value
using DelimiterFunc = AZStd::function<JsonPathValue(AZStd::string_view line)>;
DelimiterFunc m_delimiterFunc;
};
//! Merges a single command line argument into the settings registry. Command line arguments
@@ -322,6 +322,14 @@ namespace AZ
//! @return True if the registry folder was successfully merged, otherwise false.
virtual bool MergeSettingsFolder(AZStd::string_view path, const Specializations& specializations,
AZStd::string_view platform = {}, AZStd::string_view rootKey = "", AZStd::vector<char>* scratchBuffer = nullptr) = 0;
//! Stores the settings structure which is used when merging settings to the Settings Registry
//! using JSON Merge Patch or JSON Merge Patch.
//! The settings contain an issue reporting callback which can be used to track patching process.
//! Potential application of the reporting callback could be to update a UI whenever a key receives an updated value
//! @param applyPatchSettings The ApplyPatchSettings which are using during JSON Merging
virtual void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings) = 0;
virtual void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings) = 0;
};
inline SettingsRegistryInterface::Visitor::~Visitor() = default;
@@ -11,6 +11,7 @@
*/
#include <cctype>
#include <cerrno>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/JSON/error/en.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
@@ -419,9 +420,6 @@ namespace AZ
bool SettingsRegistryImpl::MergeCommandLineArgument(AZStd::string_view argument, AZStd::string_view rootKey,
const CommandLineArgumentSettings& commandLineSettings)
{
const char* front = argument.begin();
const char* back = argument.end();
if (!commandLineSettings.m_delimiterFunc)
{
AZ_Error("SettingsRegistry", false,
@@ -429,87 +427,40 @@ namespace AZ
aznumeric_cast<int>(argument.size()), argument.data());
return false;
}
const char* split = AZStd::find_if(front, back, commandLineSettings.m_delimiterFunc);
if (split == front || // There is no key
split == (back-1) || // There is no value
split == back) // Split character not found.
auto [key, value] = commandLineSettings.m_delimiterFunc(argument);
if (key.empty())
{
// They key where to set the JSON value cannot be empty
// The value of the JSON can be though
// This is so that a key can be set to empty string using "/KeyPath="
return false;
}
const char* keyStart = front;
while (std::isspace(*keyStart)) // This is safe because it will eventually stop on =
// Prepend the rootKey as an anchor to the argument key
SettingsRegistryInterface::FixedValueString keyPath{ rootKey.ends_with('/')
? rootKey.substr(0, rootKey.size() - 1)
: rootKey };
// Append the JSON reference token prefix of '/' to the keyPath
if (!key.starts_with('/'))
{
keyStart++;
keyPath.push_back('/');
}
if (keyStart == split) // Key is just white spaces
if ((key.size() + keyPath.size()) > keyPath.max_size())
{
// The key portion is longer than the FixedValueString max size that can be stored
// This limitation is arbitrary, if an AZStd::string is used or if the C++17 std::to_chars
// function is used, there wouldn't need to be a limitation
return false;
}
const char* keyEnd = split;
while (std::isspace(*--keyEnd));
keyEnd++;
keyPath += key;
key = keyPath;
char buffer[MaxJsonPathLength];
AZStd::string_view key;
bool keyHasDivider = *keyStart == '/';
if (!rootKey.empty())
if (value.empty())
{
bool rootKeyHasDivider = (rootKey[rootKey.length() - 1]) == '/';
size_t count;
if (!rootKeyHasDivider && !keyHasDivider)
{
count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "%.*s/%.*s",
aznumeric_cast<int>(rootKey.length()), rootKey.data(),
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
}
else if (rootKeyHasDivider && keyHasDivider)
{
count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "%.*s%.*s",
aznumeric_cast<int>(rootKey.length()) - 1, rootKey.data(),
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
}
else
{
count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "%.*s%.*s",
aznumeric_cast<int>(rootKey.length()), rootKey.data(),
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
}
if (count >= AZ_ARRAY_SIZE(buffer) - 1)
{
return false;
}
key = AZStd::string_view(buffer, count);
}
else if (!keyHasDivider)
{
size_t count = azsnprintf(buffer, AZ_ARRAY_SIZE(buffer), "/%.*s",
aznumeric_cast<int>(keyEnd - keyStart), keyStart);
if (count >= AZ_ARRAY_SIZE(buffer) - 1)
{
return false;
}
key = AZStd::string_view(buffer, count);
}
else
{
key = AZStd::string_view(keyStart, keyEnd);
return Set(key, value);
}
const char* valueStart = split + 1;
while (std::isspace(*valueStart) && valueStart < back)
{
valueStart++;
}
if (valueStart == back)
{
return false; // The value is empty
}
const char* valueEnd = back;
while (std::isspace(*(--valueEnd)));
valueEnd++;
AZStd::string_view value(valueStart, valueEnd);
if (value == "true")
{
return Set(key, true);
@@ -519,23 +470,35 @@ namespace AZ
return Set(key, false);
}
if (value.length() - 1 >= MaxCommandLineArgumentLength)
SettingsRegistryInterface::FixedValueString valueString;
if (value.size() > valueString.max_size())
{
// The value portion is longer than the FixedValueString max size that can be stored
// This limitation is arbitrary, if an AZStd::string is used or if the C++17 std::to_chars
// function is used, there wouldn't need to be a limitation
return false;
}
char argumentString[MaxCommandLineArgumentLength];
snprintf(argumentString, AZ_ARRAY_SIZE(argument), "%.*s", aznumeric_cast<int>(value.length()), value.data());
char* argumentStringEnd = argumentString + value.length();
valueString = value;
const char* valueStringEnd = valueString.c_str() + valueString.size();
errno = 0;
char* convertEnd = nullptr;
s64 intValue = strtoll(argumentString, &convertEnd, 0);
if (convertEnd == argumentStringEnd)
s64 intValue = strtoll(valueString.c_str(), &convertEnd, 0);
if (errno != ERANGE && convertEnd == valueStringEnd)
{
return Set(key, intValue);
}
errno = 0;
convertEnd = nullptr;
double floatingPointValue = strtod(argumentString, &convertEnd);
if (convertEnd == argumentStringEnd)
u64 uintValue = strtoull(valueString.c_str(), &convertEnd, 0);
if (errno != ERANGE && convertEnd == valueStringEnd)
{
return Set(key, uintValue);
}
errno = 0;
convertEnd = nullptr;
double floatingPointValue = strtod(valueString.c_str(), &convertEnd);
if (errno != ERANGE && convertEnd == valueStringEnd)
{
return Set(key, floatingPointValue);
}
@@ -611,7 +574,7 @@ namespace AZ
}
else
{
if (MaxFilePathLength < path.length() + 1)
if (AZ::IO::MaxPathLength < path.length() + 1)
{
AZ_Error("Settings Registry", false,
R"(Path "%.*s" is too long. Either make sure that the provided path is terminated or use a shorter path.)",
@@ -623,10 +586,8 @@ namespace AZ
.AddMember(StringRef("Path"), AZStd::move(pathValue), m_settings.GetAllocator());
return false;
}
char filePath[MaxFilePathLength];
azstrncpy(filePath, AZ_ARRAY_SIZE(filePath), path.data(), path.length());
filePath[path.length()] = 0;
result = MergeSettingsFileInternal(filePath, format, rootKey, *scratchBuffer);
AZ::IO::FixedMaxPathString filePath(path);
result = MergeSettingsFileInternal(filePath.c_str(), format, rootKey, *scratchBuffer);
}
scratchBuffer->clear();
@@ -660,7 +621,7 @@ namespace AZ
additionalSpaceRequired += AZ_ARRAY_SIZE(PlatformFolder) + platform.length() + 2; // +2 for the two slashes.
}
if (path.length() + additionalSpaceRequired > MaxFilePathLength)
if (path.length() + additionalSpaceRequired > AZ::IO::MaxPathLength)
{
AZ_Error("Settings Registry", false, "Folder path for the Setting Registry is too long: %.*s",
static_cast<int>(path.size()), path.data());
@@ -673,7 +634,7 @@ namespace AZ
RegistryFileList fileList;
scratchBuffer->clear();
AZStd::fixed_string<MaxFilePathLength> folderPath{ path };
AZ::IO::FixedMaxPathString folderPath{ path };
constexpr AZStd::string_view pathSeparators{ AZ_CORRECT_AND_WRONG_DATABASE_SEPARATOR };
if (pathSeparators.find_first_of(folderPath.back()) == AZStd::string_view::npos)
{
@@ -926,7 +887,7 @@ namespace AZ
// Sort by the name first so the registry file gets applied with all its specializations.
if (lhs.m_tags[0] != rhs.m_tags[0])
{
return strcmp(lhs.m_relativePath, rhs.m_relativePath) < 0;
return lhs.m_relativePath < rhs.m_relativePath;
}
// Then sort by size first so the files with the fewest specializations get applied first.
@@ -956,14 +917,14 @@ namespace AZ
}
collisionFound = true;
AZ_Error("Settings Registry", false, R"(Two registry files point to the same specialization: "%s" and "%s")",
lhs.m_relativePath, rhs.m_relativePath);
AZ_Error("Settings Registry", false, R"(Two registry files in "%.*s" point to the same specialization: "%s" and "%s")",
AZ_STRING_ARG(folderPath), lhs.m_relativePath.c_str(), rhs.m_relativePath.c_str());
historyPointer.Create(m_settings, m_settings.GetAllocator()).SetObject()
.AddMember(StringRef("Error"), StringRef("Too many files in registry folder."), m_settings.GetAllocator())
.AddMember(StringRef("Path"),
Value(folderPath.data(), aznumeric_caster(folderPath.length()), m_settings.GetAllocator()), m_settings.GetAllocator())
.AddMember(StringRef("File1"), Value(lhs.m_relativePath, m_settings.GetAllocator()), m_settings.GetAllocator())
.AddMember(StringRef("File2"), Value(rhs.m_relativePath, m_settings.GetAllocator()), m_settings.GetAllocator());
.AddMember(StringRef("File1"), Value(lhs.m_relativePath.c_str(), m_settings.GetAllocator()), m_settings.GetAllocator())
.AddMember(StringRef("File2"), Value(rhs.m_relativePath.c_str(), m_settings.GetAllocator()), m_settings.GetAllocator());
return false;
}
@@ -1036,9 +997,9 @@ namespace AZ
// thats the name tag.
AZStd::sort(AZStd::next(output.m_tags.begin()), output.m_tags.end());
if (filePathSize < AZ_ARRAY_SIZE(output.m_relativePath))
if (filePathSize < output.m_relativePath.max_size())
{
azstrcpy(output.m_relativePath, AZ_ARRAY_SIZE(output.m_relativePath), filename);
output.m_relativePath = filename;
return true;
}
else
@@ -1145,7 +1106,7 @@ namespace AZ
JsonSerializationResult::ResultCode mergeResult(JsonSerializationResult::Tasks::Merge);
if (rootKey.empty())
{
mergeResult = JsonSerialization::ApplyPatch(m_settings, m_settings.GetAllocator(), jsonPatch, mergeApproach);
mergeResult = JsonSerialization::ApplyPatch(m_settings, m_settings.GetAllocator(), jsonPatch, mergeApproach, m_applyPatchSettings);
}
else
{
@@ -1153,7 +1114,7 @@ namespace AZ
if (root.IsValid())
{
Value& rootValue = root.Create(m_settings, m_settings.GetAllocator());
mergeResult = JsonSerialization::ApplyPatch(rootValue, m_settings.GetAllocator(), jsonPatch, mergeApproach);
mergeResult = JsonSerialization::ApplyPatch(rootValue, m_settings.GetAllocator(), jsonPatch, mergeApproach, m_applyPatchSettings);
}
else
{
@@ -1180,4 +1141,13 @@ namespace AZ
return true;
}
void SettingsRegistryImpl::SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings)
{
m_applyPatchSettings = applyPatchSettings;
}
void SettingsRegistryImpl::GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings)
{
applyPatchSettings = m_applyPatchSettings;
}
} // namespace AZ
@@ -14,6 +14,7 @@
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/pointer.h>
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Settings/SettingsRegistry.h>
@@ -35,9 +36,6 @@ namespace AZ
AZ_CLASS_ALLOCATOR(SettingsRegistryImpl, AZ::OSAllocator, 0);
AZ_RTTI(AZ::SettingsRegistryImpl, "{E9C34190-F888-48CA-83C9-9F24B4E21D72}", AZ::SettingsRegistryInterface);
static constexpr size_t MaxFilePathLength = AZ_MAX_PATH_LEN;
static constexpr size_t MaxJsonPathLength = 1024;
static constexpr size_t MaxCommandLineArgumentLength = 1024;
static constexpr size_t MaxRegistryFolderEntries = 128;
SettingsRegistryImpl();
@@ -80,11 +78,14 @@ namespace AZ
bool MergeSettingsFolder(AZStd::string_view path, const Specializations& specializations,
AZStd::string_view platform, AZStd::string_view rootKey = "", AZStd::vector<char>* scratchBuffer = nullptr) override;
void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& applyPatchSettings) override;
void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& applyPatchSettings) override;
private:
using TagList = AZStd::fixed_vector<size_t, Specializations::MaxCount + 1>;
struct RegistryFile
{
char m_relativePath[MaxFilePathLength]{ 0 };
AZ::IO::FixedMaxPathString m_relativePath;
TagList m_tags;
bool m_isPatch{ false };
bool m_isPlatformFile{ false };
@@ -109,5 +110,6 @@ namespace AZ
rapidjson::Document m_settings;
JsonSerializerSettings m_serializationSettings;
JsonDeserializerSettings m_deserializationSettings;
JsonApplyPatchSettings m_applyPatchSettings;
};
} // namespace AZ
@@ -32,17 +32,12 @@
namespace AZ::Internal
{
AZ::SettingsRegistryInterface::FixedValueString GetEngineMonikerForProject(
SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath)
SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectJsonPath)
{
// projectPath needs to be an absolute path here.
using namespace AZ::SettingsRegistryMergeUtils;
bool projectJsonMerged = false;
auto projectJsonPath = projectPath / "project.json";
if (AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
{
projectJsonMerged = settingsRegistry.MergeSettingsFile(
projectJsonPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ProjectSettingsRootKey);
}
bool projectJsonMerged = settingsRegistry.MergeSettingsFile(
projectJsonPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, ProjectSettingsRootKey);
AZ::SettingsRegistryInterface::FixedValueString engineMoniker;
if (projectJsonMerged)
@@ -105,12 +100,12 @@ namespace AZ::Internal
const auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
AZStd::set<AZ::IO::FixedMaxPath> projectPathsNotFound;
for (EngineInfo& engineInfo : pathVisitor.m_enginePaths)
{
AZ::IO::FixedMaxPath engineSettingsPath{engineInfo.m_path};
engineSettingsPath /= "engine.json";
if (AZ::IO::SystemFile::Exists(engineSettingsPath.c_str()))
if (auto engineSettingsPath = AZ::IO::FixedMaxPath{engineInfo.m_path} / "engine.json";
AZ::IO::SystemFile::Exists(engineSettingsPath.c_str()))
{
if (settingsRegistry.MergeSettingsFile(
engineSettingsPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineSettingsRootKey))
@@ -119,12 +114,61 @@ namespace AZ::Internal
}
}
auto engineMoniker = Internal::GetEngineMonikerForProject(settingsRegistry, engineInfo.m_path / projectPath);
if (!engineMoniker.empty() && engineMoniker == engineInfo.m_moniker)
if (auto projectJsonPath = (engineInfo.m_path / projectPath / "project.json").LexicallyNormal();
AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
{
engineRoot = engineInfo.m_path;
break;
if (auto engineMoniker = Internal::GetEngineMonikerForProject(settingsRegistry, projectJsonPath);
!engineMoniker.empty() && engineMoniker == engineInfo.m_moniker)
{
engineRoot = engineInfo.m_path;
break;
}
}
else
{
projectPathsNotFound.insert(projectJsonPath);
}
// Continue looking for candidates, remove the previous engine and project settings that were merged above.
settingsRegistry.Remove(ProjectSettingsRootKey);
settingsRegistry.Remove(EngineSettingsRootKey);
}
if (engineRoot.empty())
{
AZStd::string errorStr;
if (!projectPathsNotFound.empty())
{
// This case is usually encountered when a project path is given as a relative path,
// which is assumed to be relative to an engine root.
// When no project.json files are found this way, dump this error message about
// which project paths were checked.
AZStd::string projectPathsTested;
for (const auto& path : projectPathsNotFound)
{
projectPathsTested.append(AZStd::string::format(" %s\n", path.c_str()));
}
errorStr = AZStd::string::format("No valid project was found at these locations:\n%s"
"Please supply a valid --project-path to the application.",
projectPathsTested.c_str());
}
else
{
// The other case is that a project.json was found, but after checking all the registered engines
// none of them matched the engine moniker.
AZStd::string enginePathsChecked;
for (const auto& engineInfo : pathVisitor.m_enginePaths)
{
enginePathsChecked.append(AZStd::string::format(" %s (%s)\n", engineInfo.m_path.c_str(), engineInfo.m_moniker.c_str()));
}
errorStr = AZStd::string::format(
"No engine was found in o3de_manifest.json with a name that matches the one set in the project.json.\n"
"Engines that were checked:\n%s"
"Please check that your engine and project have both been registered with scripts/o3de.py.", enginePathsChecked.c_str()
);
}
settingsRegistry.Set(FilePathKey_ErrorText, errorStr.c_str());
}
}
@@ -158,7 +202,7 @@ namespace AZ::Internal
return {};
}
void InjectSettingToCommandLineFront(AZ::SettingsRegistryInterface& settingsRegistry,
void InjectSettingToCommandLineBack(AZ::SettingsRegistryInterface& settingsRegistry,
AZStd::string_view path, AZStd::string_view value)
{
AZ::CommandLine commandLine;
@@ -168,7 +212,7 @@ namespace AZ::Internal
auto projectPathOverride = AZStd::string::format(R"(--regset="%.*s=%.*s")",
aznumeric_cast<int>(path.size()), path.data(), aznumeric_cast<int>(value.size()), value.data());
paramContainer.emplace(paramContainer.begin(), AZStd::move(projectPathOverride));
paramContainer.emplace(paramContainer.end(), AZStd::move(projectPathOverride));
commandLine.Parse(paramContainer);
AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine);
}
@@ -197,8 +241,8 @@ namespace AZ::SettingsRegistryMergeUtils
if (!engineRoot.empty())
{
settingsRegistry.Set(engineRootKey, engineRoot.Native());
// Inject the engine root into the front of the command line settings
Internal::InjectSettingToCommandLineFront(settingsRegistry, engineRootKey, engineRoot.Native());
// Inject the engine root at the end of the command line settings
Internal::InjectSettingToCommandLineBack(settingsRegistry, engineRootKey, engineRoot.Native());
return engineRoot;
}
}
@@ -244,8 +288,8 @@ namespace AZ::SettingsRegistryMergeUtils
if (!projectRoot.empty())
{
settingsRegistry.Set(projectRootKey, projectRoot.c_str());
// Inject the project root into the front of the command line settings
Internal::InjectSettingToCommandLineFront(settingsRegistry, projectRootKey, projectRoot.Native());
// Inject the project root at the end of the command line settings
Internal::InjectSettingToCommandLineBack(settingsRegistry, projectRootKey, projectRoot.Native());
return projectRoot;
}
}
@@ -296,46 +340,6 @@ namespace AZ::SettingsRegistryMergeUtils
return sectionName;
}
// Encodes a key, value delimited line such that the entire "key" can be stored as a single
// JSON Pointer key by escaping the tilde(~) and forward slash(/)
template<size_t BufferSize>
static AZStd::fixed_string<BufferSize> EncodeLineForJsonPointer(AZStd::string_view token,
const AZ::SettingsRegistryInterface::CommandLineArgumentSettings::DelimiterFunc& delimiterFunc)
{
if (!delimiterFunc)
{
// Since the delimiter function is not valid, return the token unchanged
return AZStd::fixed_string<BufferSize>{ token };
}
// Iterate over the line and escape the '~' and '/' values
AZStd::fixed_string<BufferSize> encodedToken;
size_t chIndex = 0;
for (; chIndex < token.size(); ++chIndex)
{
const char ch = token[chIndex];
if (delimiterFunc(ch))
{
// If the delimiter is found, this indicates that the end of the key has been found
break;
}
switch (ch)
{
case '~':
encodedToken += "~0";
break;
case '/':
encodedToken += "~1";
break;
default:
encodedToken += ch;
}
}
// Copy over the rest of the post delimited line to the encoded token
encodedToken.append(token.data() + chIndex, token.data() + token.size());
return encodedToken;
}
void QuerySpecializationsFromRegistry(SettingsRegistryInterface& registry, SettingsRegistryInterface::Specializations& specializations)
{
// Append any specializations stored in the registry
@@ -455,14 +459,7 @@ namespace AZ::SettingsRegistryMergeUtils
}
}
// Check if the "key" portion of the line has '~' or '/' as the SettingsRegistry uses JSON Pointer
// to set the "value" portion. Those characters need to be escaped with ~0 and ~1 respectively
// to allow them to be embedded in a single json key
// 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);
registry.MergeCommandLineArgument(line, currentJsonPointerPath, configParserSettings.m_commandLineSettings);
// Skip past the newline character if found
frontIter = lineEndIter + (foundNewLine ? 1 : 0);
@@ -555,6 +552,34 @@ namespace AZ::SettingsRegistryMergeUtils
? devWriteStorage.value()
: projectUserPath.Native());
// Set the project in-memory build path if the ProjectBuildPath key has been supplied
if (AZ::IO::FixedMaxPath projectBuildPath; registry.Get(projectBuildPath.Native(), ProjectBuildPath))
{
registry.Remove(FilePathKey_ProjectBuildPath);
registry.Remove(FilePathKey_ProjectConfigurationBinPath);
AZ::IO::FixedMaxPath buildConfigurationPath = normalizedProjectPath / projectBuildPath;
if (IO::SystemFile::Exists(buildConfigurationPath.c_str()))
{
registry.Set(FilePathKey_ProjectBuildPath, buildConfigurationPath.LexicallyNormal().Native());
}
// Add the specific build configuration paths to the Settings Registry
// First try <project-build-path>/bin/$<CONFIG> and if that path doesn't exist
// try <project-build-path>/bin/$<PLATFORM>/$<CONFIG>
buildConfigurationPath /= "bin";
if (IO::SystemFile::Exists((buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
{
registry.Set(FilePathKey_ProjectConfigurationBinPath,
(buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native());
}
else if (IO::SystemFile::Exists((buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
{
registry.Set(FilePathKey_ProjectConfigurationBinPath,
(buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native());
}
}
// Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
auto projectNameKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
@@ -645,6 +670,14 @@ namespace AZ::SettingsRegistryMergeUtils
mergePath /= SettingsRegistryInterface::RegistryFolder;
registry.MergeSettingsFolder(mergePath.Native(), specializations, platform, "", scratchBuffer);
}
AZ::IO::FixedMaxPath projectBinPath;
if (registry.Get(projectBinPath.Native(), FilePathKey_ProjectConfigurationBinPath))
{
// Append the project build path path to the project root
projectBinPath /= SettingsRegistryInterface::RegistryFolder;
registry.MergeSettingsFolder(projectBinPath.Native(), specializations, platform, "", scratchBuffer);
}
}
void MergeSettingsToRegistry_EngineRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
@@ -654,7 +687,6 @@ namespace AZ::SettingsRegistryMergeUtils
if (registry.Get(engineRootPath, FilePathKey_EngineRootFolder))
{
AZ::IO::FixedMaxPath mergePath{ AZStd::move(engineRootPath) };
mergePath /= "Engine";
mergePath /= SettingsRegistryInterface::RegistryFolder;
registry.MergeSettingsFolder(mergePath.Native(), specializations, platform, "", scratchBuffer);
}
@@ -875,6 +907,50 @@ namespace AZ::SettingsRegistryMergeUtils
return true;
}
void ParseCommandLine(AZ::CommandLine& commandLine)
{
struct OptionKeyToRegsetKey
{
AZStd::string_view m_optionKey;
AZStd::string m_regsetKey;
};
// Provide overrides for the engine root, the project root and the project cache root
AZStd::array commandOptions = {
OptionKeyToRegsetKey{
"engine-path", AZStd::string::format("%s/engine_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)},
OptionKeyToRegsetKey{
"project-path", AZStd::string::format("%s/project_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)},
OptionKeyToRegsetKey{
"project-cache-path",
AZStd::string::format("%s/project_cache_path", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey)},
OptionKeyToRegsetKey{"project-build-path", ProjectBuildPath} };
AZStd::fixed_vector<AZStd::string, commandOptions.size()> overrideArgs;
for (auto&& [optionKey, regsetKey] : commandOptions)
{
if (size_t optionCount = commandLine.GetNumSwitchValues(optionKey); optionCount > 0)
{
// Use the last supplied command option value to override previous values
auto overrideArg = AZStd::string::format(
R"(--regset="%s=%s")", regsetKey.c_str(), commandLine.GetSwitchValue(optionKey, optionCount - 1).c_str());
overrideArgs.emplace_back(AZStd::move(overrideArg));
}
}
if (!overrideArgs.empty())
{
// Dump the input command line, add the additional option overrides
// and Parse the new command line args (write back) into the input command line.
AZ::CommandLine::ParamContainer commandLineArgs;
commandLine.Dump(commandLineArgs);
commandLineArgs.insert(
commandLineArgs.end(), AZStd::make_move_iterator(overrideArgs.begin()), AZStd::make_move_iterator(overrideArgs.end()));
commandLine.Parse(commandLineArgs);
}
}
bool DumpSettingsRegistryToStream(SettingsRegistryInterface& registry, AZStd::string_view key,
AZ::IO::GenericStream& stream, const DumperSettings& dumperSettings)
{
@@ -52,9 +52,20 @@ namespace AZ::SettingsRegistryMergeUtils
//! project settings can be stored
inline static constexpr char FilePathKey_ProjectUserPath[] = "/Amazon/AzCore/Runtime/FilePaths/SourceProjectUserPath";
//! User facing key which represents the root of a project cmake build tree. i.e the ${CMAKE_BINARY_DIR}
//! A relative path is taking relative to the *project* root, NOT *engine* root.
inline constexpr AZStd::string_view ProjectBuildPath = "/Amazon/Project/Settings/Build/project_build_path";
//! In-Memory only key which stores an absolute path to the project build directory
inline constexpr AZStd::string_view FilePathKey_ProjectBuildPath = "/Amazon/AzCore/Runtime/FilePaths/ProjectBuildPath";
//! In-Memory only key which stores the configuration directory containing the built binaries
inline constexpr AZStd::string_view FilePathKey_ProjectConfigurationBinPath = "/Amazon/AzCore/Runtime/FilePaths/ProjectConfigurationBinPath";
//! 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";
//! Stores error text regarding engine boot sequence when engine and project roots cannot be determined
inline static constexpr char FilePathKey_ErrorText[] = "/Amazon/AzCore/Runtime/FilePaths/ErrorText";
//! Root key for where command line are stored at within the settings registry
inline static constexpr char CommandLineRootKey[] = "/Amazon/AzCore/Runtime/CommandLine";
//! Key set to trigger a notification that the CommandLine has been stored within the settings registry
@@ -125,7 +136,7 @@ namespace AZ::SettingsRegistryMergeUtils
//! Callback function that is after a has been filtered through the CommentPrefixFunc
//! to determine if the text matches a section header
//! returns a view of the section name if the line contains a section
//! Otherwise an empty view is returend
//! Otherwise an empty view is returned
using SectionHeaderFunc = AZStd::function<AZStd::string_view(AZStd::string_view line)>;
//! Root JSON pointer path to place all key=values pairs of configuration data within
@@ -219,6 +230,9 @@ namespace AZ::SettingsRegistryMergeUtils
//! into the AZ::CommandLine instance
bool GetCommandLineFromRegistry(SettingsRegistryInterface& registry, AZ::CommandLine& commandLine);
//! Parse a CommandLine and transform certain options into formal "regset" options
void ParseCommandLine(AZ::CommandLine& commandLine);
//! Structure for configuring how values should be dumped from the Settings Registry
struct DumperSettings
{
@@ -139,4 +139,4 @@ namespace AZ
/// @deprecated Use SliceBus.
using PrefabBus = SliceBus;
} // namespace AZ
} // namespace AZ
@@ -1740,7 +1740,10 @@ namespace AZ
if (!iter->IsInstantiated())
{
#if defined(AZ_ENABLE_TRACING)
Data::Asset<SliceAsset> thisAsset = Data::AssetManager::Instance().FindAsset(GetMyAsset()->GetId(), AZ::Data::AssetLoadBehavior::Default);
Data::Asset<SliceAsset> thisAsset = GetMyAsset()
? Data::Asset<SliceAsset>(Data::AssetManager::Instance().FindAsset(
GetMyAsset()->GetId(), AZ::Data::AssetLoadBehavior::Default))
: Data::Asset<SliceAsset>();
AZ_Warning("Slice", false, "Removing %d instances of slice asset %s from parent asset %s due to failed instantiation. "
"Saving parent asset will result in loss of slice data.",
iter->GetInstances().size(),

Some files were not shown because too many files have changed in this diff Show More