Merge branch 'main' into non-uniform-scale-manipulators
This commit is contained in:
@@ -477,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());
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
@@ -260,21 +261,20 @@ namespace AZ
|
||||
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
|
||||
|
||||
@@ -340,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
|
||||
@@ -499,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);
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace AZ
|
||||
MOCK_METHOD5(
|
||||
MergeSettingsFolder,
|
||||
bool(AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector<char>*));
|
||||
|
||||
MOCK_METHOD1(SetApplyPatchSettings, void(const JsonApplyPatchSettings&));
|
||||
MOCK_METHOD1(GetApplyPatchSettings, void(JsonApplyPatchSettings&));
|
||||
};
|
||||
} // namespace AZ
|
||||
|
||||
|
||||
@@ -13,24 +13,25 @@
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Console/Console.h>
|
||||
|
||||
#include <AzCore/Settings/SettingsRegistryImpl.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
using namespace UnitTest;
|
||||
|
||||
AZ_CVAR(bool, testBool, false, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(char, testChar, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int8_t, testInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int16_t, testInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int32_t, testInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int64_t, testInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(uint8_t, testUInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(bool, testBool, false, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(char, testChar, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int8_t, testInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int16_t, testInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int32_t, testInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(int64_t, testInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(uint8_t, testUInt8, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(uint16_t, testUInt16, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(uint32_t, testUInt32, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(uint64_t, testUInt64, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, testFloat, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(double, testDouble, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(float, testFloat, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
AZ_CVAR(double, testDouble, 0, nullptr, ConsoleFunctorFlags::Null, "");
|
||||
|
||||
AZ_CVAR(AZ::CVarFixedString, testString, "default", nullptr, ConsoleFunctorFlags::Null, "");
|
||||
|
||||
@@ -189,7 +190,7 @@ namespace AZ
|
||||
|
||||
TEST_F(ConsoleTests, CVar_GetSetTest_Vector2)
|
||||
{
|
||||
testVec2 = AZ::Vector2{ 0.0f, 0.0f};
|
||||
testVec2 = AZ::Vector2{ 0.0f, 0.0f };
|
||||
TestCVarHelper(testVec2, "testVec2", "testVec2 1 1", "testVec2 asdf", AZ::Vector2(100, 100), AZ::Vector2(0, 0), AZ::Vector2(1, 1));
|
||||
}
|
||||
|
||||
@@ -350,3 +351,245 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace ConsoleSettingsRegistryTests
|
||||
{
|
||||
//! ConfigFile MergeUtils Test
|
||||
struct ConfigFileParams
|
||||
{
|
||||
AZStd::string_view m_testConfigFileName;
|
||||
AZStd::string_view m_testConfigContents;
|
||||
};
|
||||
class ConsoleSettingsRegistryFixture
|
||||
: public UnitTest::ScopedAllocatorSetupFixture
|
||||
, public ::testing::WithParamInterface<ConfigFileParams>
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
m_registry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
|
||||
// Store off the old global settings registry to restore after each test
|
||||
m_oldSettingsRegistry = AZ::SettingsRegistry::Get();
|
||||
if (m_oldSettingsRegistry != nullptr)
|
||||
{
|
||||
AZ::SettingsRegistry::Unregister(m_oldSettingsRegistry);
|
||||
}
|
||||
AZ::SettingsRegistry::Register(m_registry.get());
|
||||
|
||||
// Create a TestFile in the Test Directory
|
||||
m_testFolder = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / "ConsoleTestFolder";
|
||||
auto configFileParams = GetParam();
|
||||
CreateTestFile(m_testFolder / configFileParams.m_testConfigFileName, configFileParams.m_testConfigContents);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
// Remove the Test Directory
|
||||
DeleteFolderRecursive(m_testFolder);
|
||||
|
||||
// Restore the old global settings registry
|
||||
AZ::SettingsRegistry::Unregister(m_registry.get());
|
||||
if (m_oldSettingsRegistry != nullptr)
|
||||
{
|
||||
AZ::SettingsRegistry::Register(m_oldSettingsRegistry);
|
||||
m_oldSettingsRegistry = {};
|
||||
}
|
||||
m_registry.reset();
|
||||
}
|
||||
|
||||
void TestClassFunc(const AZ::ConsoleCommandContainer& someStrings)
|
||||
{
|
||||
m_stringArgCount = someStrings.size();
|
||||
}
|
||||
|
||||
AZ_CONSOLEFUNC(ConsoleSettingsRegistryFixture, TestClassFunc, AZ::ConsoleFunctorFlags::Null, "");
|
||||
|
||||
static void DeleteFolderRecursive(const AZ::IO::PathView& path)
|
||||
{
|
||||
auto callback = [&path](AZStd::string_view filename, bool isFile) -> bool
|
||||
{
|
||||
if (isFile)
|
||||
{
|
||||
auto filePath = AZ::IO::FixedMaxPath(path) / filename;
|
||||
AZ::IO::SystemFile::Delete(filePath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filename != "." && filename != "..")
|
||||
{
|
||||
auto folderPath = AZ::IO::FixedMaxPath(path) / filename;
|
||||
DeleteFolderRecursive(folderPath);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto searchPath = AZ::IO::FixedMaxPath(path) / "*";
|
||||
AZ::IO::SystemFile::FindFiles(searchPath.c_str(), callback);
|
||||
AZ::IO::SystemFile::DeleteDir(AZ::IO::FixedMaxPathString(path.Native()).c_str());
|
||||
}
|
||||
|
||||
static bool CreateTestFile(const AZ::IO::FixedMaxPath& testPath, AZStd::string_view content)
|
||||
{
|
||||
AZ::IO::SystemFile file;
|
||||
if (!file.Open(testPath.c_str(), AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE
|
||||
| AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
|
||||
{
|
||||
AZ_Assert(false, "Unable to open test file for writing: %s", testPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.Write(content.data(), content.size()) != content.size())
|
||||
{
|
||||
AZ_Assert(false, "Unable to write content to test file: %s", testPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t m_stringArgCount{};
|
||||
AZStd::unique_ptr<AZ::SettingsRegistryInterface> m_registry;
|
||||
AZ::IO::FixedMaxPath m_testFolder;
|
||||
|
||||
private:
|
||||
AZ::SettingsRegistryInterface* m_oldSettingsRegistry{};
|
||||
};
|
||||
|
||||
static bool s_consoleFreeFunctionInvoked = false;
|
||||
static void TestSettingsRegistryFreeFunc(const AZ::ConsoleCommandContainer& someStrings)
|
||||
{
|
||||
EXPECT_TRUE(someStrings.empty());
|
||||
s_consoleFreeFunctionInvoked = true;
|
||||
}
|
||||
|
||||
AZ_CONSOLEFREEFUNC(TestSettingsRegistryFreeFunc, AZ::ConsoleFunctorFlags::Null, "");
|
||||
|
||||
TEST_P(ConsoleSettingsRegistryFixture, Console_AbleToLoadSettingsFile_Successfully)
|
||||
{
|
||||
AZ::Console testConsole(*m_registry);
|
||||
testConsole.LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
|
||||
AZ::Interface<AZ::IConsole>::Register(&testConsole);
|
||||
AZ_CVAR_SCOPED(int32_t, testInit, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "");
|
||||
s_consoleFreeFunctionInvoked = false;
|
||||
testInit = {};
|
||||
AZ::testChar = {};
|
||||
AZ::testBool = {};
|
||||
AZ::testInt8 = {};
|
||||
AZ::testInt16 = {};
|
||||
AZ::testInt32 = {};
|
||||
AZ::testInt64 = {};
|
||||
AZ::testUInt8 = {};
|
||||
AZ::testUInt16 = {};
|
||||
AZ::testUInt32 = {};
|
||||
AZ::testUInt64 = {};
|
||||
AZ::testFloat= {};
|
||||
AZ::testDouble = {};
|
||||
AZ::testString = {};
|
||||
|
||||
auto configFileParams = GetParam();
|
||||
auto testFilePath = m_testFolder / configFileParams.m_testConfigFileName;
|
||||
EXPECT_TRUE(AZ::IO::SystemFile::Exists(testFilePath.c_str()));
|
||||
testConsole.ExecuteConfigFile(testFilePath.Native());
|
||||
EXPECT_TRUE(s_consoleFreeFunctionInvoked);
|
||||
EXPECT_EQ(3, testInit);
|
||||
EXPECT_TRUE(static_cast<bool>(AZ::testBool));
|
||||
EXPECT_EQ('Q', AZ::testChar);
|
||||
EXPECT_EQ(24, AZ::testInt8);
|
||||
EXPECT_EQ(-32, AZ::testInt16);
|
||||
EXPECT_EQ(41, AZ::testInt32);
|
||||
EXPECT_EQ(-51, AZ::testInt64);
|
||||
EXPECT_EQ(3, AZ::testUInt8);
|
||||
EXPECT_EQ(5, AZ::testUInt16);
|
||||
EXPECT_EQ(6, AZ::testUInt32);
|
||||
EXPECT_EQ(0xFFFF'FFFF'FFFF'FFFF, AZ::testUInt64);
|
||||
EXPECT_FLOAT_EQ(1.0f, AZ::testFloat);
|
||||
EXPECT_DOUBLE_EQ(2, AZ::testDouble);
|
||||
EXPECT_STREQ("Stable", static_cast<AZ::CVarFixedString>(AZ::testString).c_str());
|
||||
EXPECT_EQ(3, m_stringArgCount);
|
||||
AZ::Interface<AZ::IConsole>::Unregister(&testConsole);
|
||||
}
|
||||
|
||||
|
||||
static constexpr AZStd::string_view UserINIStyleContent =
|
||||
R"(
|
||||
testInit = 3
|
||||
testBool true
|
||||
testChar Q
|
||||
testInt8 24
|
||||
testInt16 -32
|
||||
testInt32 41
|
||||
testInt64 -51
|
||||
testUInt8 3
|
||||
testUInt16 5
|
||||
testUInt32 6
|
||||
testUInt64 18446744073709551615
|
||||
testFloat 1.0
|
||||
testDouble 2
|
||||
testString Stable
|
||||
ConsoleSettingsRegistryFixture.testClassFunc Foo Bar Baz
|
||||
TestSettingsRegistryFreeFunc
|
||||
)";
|
||||
|
||||
static constexpr AZStd::string_view UserJsonMergePatchContent =
|
||||
R"(
|
||||
{
|
||||
"Amazon": {
|
||||
"AzCore": {
|
||||
"Runtime": {
|
||||
"ConsoleCommands": {
|
||||
"testInit": 3,
|
||||
"testBool": true,
|
||||
"testChar": "Q",
|
||||
"testInt8": 24,
|
||||
"testInt16": -32,
|
||||
"testInt32": 41,
|
||||
"testInt64": -51,
|
||||
"testUInt8": 3,
|
||||
"testUInt16": 5,
|
||||
"testUInt32": 6,
|
||||
"testUInt64": 18446744073709551615,
|
||||
"testFloat": 1.0,
|
||||
"testDouble": 2,
|
||||
"testString": "Stable",
|
||||
"ConsoleSettingsRegistryFixture.testClassFunc": "Foo Bar Baz",
|
||||
"TestSettingsRegistryFreeFunc": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
static constexpr AZStd::string_view UserJsonPatchContent =
|
||||
R"(
|
||||
[
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInit", "value": 3 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testBool", "value": true },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testChar", "value": "Q" },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt8", "value": 24 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt16", "value": -32 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt32", "value": 41 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testInt64", "value": -51 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt8", "value": 3 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt16", "value": 5 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt32", "value": 6 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testUInt64", "value": 18446744073709551615 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testFloat", "value": 1.0 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testDouble", "value": 2 },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/testString", "value": "Stable" },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/ConsoleSettingsRegistryFixture.testClassFunc", "value": "Foo Bar Baz" },
|
||||
{ "op": "add", "path": "/Amazon/AzCore/Runtime/ConsoleCommands/TestSettingsRegistryFreeFunc", "value": "" }
|
||||
]
|
||||
)";
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ExecuteCommandFromSettingsFile,
|
||||
ConsoleSettingsRegistryFixture,
|
||||
::testing::Values(
|
||||
ConfigFileParams{"user.cfg", UserINIStyleContent},
|
||||
ConfigFileParams{"user.setreg", UserJsonMergePatchContent},
|
||||
ConfigFileParams{"user.setregpatch", UserJsonPatchContent}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace SettingsRegistryConsoleUtilsTests
|
||||
{
|
||||
constexpr const char* settingsKey = "/TestKey";
|
||||
constexpr const char* expectedValue = "TestValue";
|
||||
AZ::Console testConsole;
|
||||
AZ::Console testConsole(*m_registry);
|
||||
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
|
||||
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
|
||||
EXPECT_TRUE(testConsole.PerformCommand(AZ::SettingsRegistryConsoleUtils::SettingsRegistrySet, { settingsKey, expectedValue }));
|
||||
@@ -69,7 +69,7 @@ namespace SettingsRegistryConsoleUtilsTests
|
||||
{
|
||||
constexpr const char* settingsKey = "/TestKey";
|
||||
constexpr const char* expectedValue = "TestValue";
|
||||
AZ::Console testConsole;
|
||||
AZ::Console testConsole(*m_registry);
|
||||
|
||||
// Scopes the console functor handle so that it destructs and unregisters the console functors
|
||||
{
|
||||
@@ -89,7 +89,7 @@ namespace SettingsRegistryConsoleUtilsTests
|
||||
constexpr const char* settingsKey2 = "/TestKey2";
|
||||
constexpr const char* expectedValue = R"(TestValue)";
|
||||
constexpr const char* expectedValue2 = R"(Hello World)";
|
||||
AZ::Console testConsole;
|
||||
AZ::Console testConsole(*m_registry);
|
||||
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
|
||||
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace SettingsRegistryConsoleUtilsTests
|
||||
constexpr const char* settingsKey2 = "/TestKey2";
|
||||
constexpr const char* expectedValue = R"(TestValue)";
|
||||
constexpr const char* expectedValue2 = R"(Hello World)";
|
||||
AZ::Console testConsole;
|
||||
AZ::Console testConsole(*m_registry);
|
||||
|
||||
// Add settings to settings registry
|
||||
EXPECT_TRUE(m_registry->Set(settingsKey, expectedValue));
|
||||
@@ -137,7 +137,7 @@ namespace SettingsRegistryConsoleUtilsTests
|
||||
constexpr const char* settingsKey2 = "/TestKey2";
|
||||
constexpr const char* expectedValue = R"(TestValue)";
|
||||
constexpr const char* expectedValue2 = R"(Hello World)";
|
||||
AZ::Console testConsole;
|
||||
AZ::Console testConsole(*m_registry);
|
||||
|
||||
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
|
||||
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
|
||||
@@ -195,7 +195,7 @@ namespace SettingsRegistryConsoleUtilsTests
|
||||
constexpr const char* SettingsKey2 = "TestKey2";
|
||||
constexpr const char* ExpectedValue = R"(TestValue)";
|
||||
constexpr const char* ExpectedValue2 = R"(Hello World)";
|
||||
AZ::Console testConsole;
|
||||
AZ::Console testConsole(*m_registry);
|
||||
|
||||
AZ::SettingsRegistryConsoleUtils::ConsoleFunctorHandle handle{
|
||||
AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_registry, testConsole) };
|
||||
|
||||
@@ -1228,27 +1228,33 @@ namespace SettingsRegistryTests
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLong_ReturnsFalse)
|
||||
{
|
||||
AZStd::string argument = AZStd::string::format("Te%*cst=Value", aznumeric_cast<int>(AZ::SettingsRegistryImpl::MaxJsonPathLength), ' ');
|
||||
constexpr int LongKeySize = 1024;
|
||||
AZStd::string argument = AZStd::string::format("Te%*cst=Value", LongKeySize, ' ');
|
||||
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyIsTooLongWithDivider_ReturnsFalse)
|
||||
{
|
||||
AZStd::string argument = AZStd::string::format("/Te%*cst=Value", aznumeric_cast<int>(AZ::SettingsRegistryImpl::MaxJsonPathLength), ' ');
|
||||
constexpr int LongKeySize = 1024;
|
||||
AZStd::string argument = AZStd::string::format("/Te%*cst=Value", LongKeySize, ' ');
|
||||
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, "/Path", {}));
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsTooLong_ReturnsFalse)
|
||||
{
|
||||
AZStd::string argument = AZStd::string::format("Test=Val%*cue", aznumeric_cast<int>(AZ::SettingsRegistryImpl::MaxCommandLineArgumentLength), ' ');
|
||||
constexpr int LongValueSize = 1024;
|
||||
AZStd::string argument = AZStd::string::format("Test=Val%*cue", LongValueSize, ' ');
|
||||
EXPECT_FALSE(m_registry->MergeCommandLineArgument(argument, {}, {}));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType("/Test"));
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingValue_ReturnsFalse)
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingValue_ReturnsEmptyString)
|
||||
{
|
||||
EXPECT_FALSE(m_registry->MergeCommandLineArgument("Test=", {}, {}));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::NoType, m_registry->GetType("/Test"));
|
||||
EXPECT_TRUE(m_registry->MergeCommandLineArgument("Test=", {}, {}));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Test"));
|
||||
AZ::SettingsRegistryInterface::FixedValueString value;
|
||||
EXPECT_TRUE(m_registry->Get(value, "/Test"));
|
||||
EXPECT_TRUE(value.empty());
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_MissingKey_ReturnsFalse)
|
||||
@@ -1271,9 +1277,13 @@ namespace SettingsRegistryTests
|
||||
EXPECT_FALSE(m_registry->MergeCommandLineArgument(" =Value", {}, {}));
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsSpaces_ReturnsFalse)
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_ValueIsSpaces_ReturnsEmptyString)
|
||||
{
|
||||
EXPECT_FALSE(m_registry->MergeCommandLineArgument("Key= ", {}, {}));
|
||||
EXPECT_TRUE(m_registry->MergeCommandLineArgument("Key= ", {}, {}));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType("/Key"));
|
||||
AZ::SettingsRegistryInterface::FixedValueString value;
|
||||
EXPECT_TRUE(m_registry->Get(value, "/Key"));
|
||||
EXPECT_TRUE(value.empty());
|
||||
}
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeCommandLineArgument_KeyAndValueAreSpaces_ReturnsFalse)
|
||||
@@ -1367,9 +1377,8 @@ namespace SettingsRegistryTests
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeSettingsFile_PathAsSubStringThatsTooLong_ReturnsFalse)
|
||||
{
|
||||
char path[AZ::SettingsRegistryImpl::MaxFilePathLength + 1];
|
||||
memset(path, '1', sizeof(path));
|
||||
AZStd::string_view subPath(path, AZ::SettingsRegistryImpl::MaxFilePathLength);
|
||||
constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, '1');
|
||||
const AZStd::string_view subPath(path);
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
bool result = m_registry->MergeSettingsFile(subPath, AZ::SettingsRegistryInterface::Format::JsonMergePatch, {}, nullptr);
|
||||
@@ -1719,8 +1728,7 @@ namespace SettingsRegistryTests
|
||||
|
||||
TEST_F(SettingsRegistryTest, MergeSettingsFolder_PathTooLong_ReportsErrorAndReturnsFalse)
|
||||
{
|
||||
char path[AZ::SettingsRegistryImpl::MaxFilePathLength + 1]{};
|
||||
memset(path, 'a', AZ_ARRAY_SIZE(path));
|
||||
constexpr AZStd::fixed_string<AZ::IO::MaxPathLength + 1> path(AZ::IO::MaxPathLength + 1, 'a');
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
bool result = m_registry->MergeSettingsFolder(path, { "editor", "test" }, {}, nullptr);
|
||||
@@ -1741,7 +1749,7 @@ namespace SettingsRegistryTests
|
||||
m_testFolder->push_back(AZ_CORRECT_DATABASE_SEPARATOR);
|
||||
*m_testFolder += AZ::SettingsRegistryInterface::RegistryFolder;
|
||||
bool result = m_registry->MergeSettingsFolder(*m_testFolder, { "editor", "test" }, {}, nullptr);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
|
||||
EXPECT_GT(::UnitTest::TestRunner::Instance().StopAssertTests(), 0);
|
||||
EXPECT_FALSE(result);
|
||||
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/0")); // Folder and specialization settings.
|
||||
@@ -1751,11 +1759,5 @@ namespace SettingsRegistryTests
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/Path"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/File1"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/1/File2"));
|
||||
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::Object, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/Error"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/Path"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/File1"));
|
||||
EXPECT_EQ(AZ::SettingsRegistryInterface::Type::String, m_registry->GetType(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/2/File2"));
|
||||
}
|
||||
} // namespace SettingsRegistryTests
|
||||
|
||||
@@ -236,8 +236,6 @@ namespace AzFramework
|
||||
// Archive classes relies on the FileIOBase DirectInstance to close
|
||||
// files properly
|
||||
m_directFileIO.reset();
|
||||
|
||||
// The AZ::Console skips destruction and always leaks to allow it to be used in static memory
|
||||
}
|
||||
|
||||
void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters)
|
||||
|
||||
@@ -40,17 +40,18 @@ namespace AzFramework
|
||||
//! Standard parameters for drawing text on screen
|
||||
struct TextDrawParameters
|
||||
{
|
||||
ViewportId m_drawViewportId = InvalidViewportId; //! Viewport to draw into
|
||||
AZ::Vector3 m_position; //! world space position for 3d draws, screen space x,y,depth for 2d.
|
||||
AZ::Color m_color = AZ::Colors::White; //! Color to draw the text
|
||||
AZ::Vector2 m_scale = AZ::Vector2(1.0f); //! font scale
|
||||
TextHorizontalAlignment m_hAlign = TextHorizontalAlignment::Left; //! Horizontal text alignment
|
||||
TextVerticalAlignment m_vAlign = TextVerticalAlignment::Top; //! Vertical text alignment
|
||||
bool m_monospace = false; //! disable character proportional spacing
|
||||
bool m_depthTest = false; //! Test character against the depth buffer
|
||||
bool m_virtual800x600ScreenSize = true; //! Text placement and size are scaled relative to a virtual 800x600 resolution
|
||||
bool m_scaleWithWindow = false; //! Font gets bigger as the window gets bigger
|
||||
bool m_multiline = true; //! text respects ascii newline characters
|
||||
ViewportId m_drawViewportId = InvalidViewportId; //!< Viewport to draw into
|
||||
AZ::Vector3 m_position; //!< world space position for 3d draws, screen space x,y,depth for 2d.
|
||||
AZ::Color m_color = AZ::Colors::White; //!< Color to draw the text
|
||||
AZ::Vector2 m_scale = AZ::Vector2(1.0f); //!< font scale
|
||||
float m_lineSpacing; //!< Spacing between new lines, as a percentage of m_scale.
|
||||
TextHorizontalAlignment m_hAlign = TextHorizontalAlignment::Left; //!< Horizontal text alignment
|
||||
TextVerticalAlignment m_vAlign = TextVerticalAlignment::Top; //!< Vertical text alignment
|
||||
bool m_monospace = false; //!< disable character proportional spacing
|
||||
bool m_depthTest = false; //!< Test character against the depth buffer
|
||||
bool m_virtual800x600ScreenSize = true; //!< Text placement and size are scaled relative to a virtual 800x600 resolution
|
||||
bool m_scaleWithWindow = false; //!< Font gets bigger as the window gets bigger
|
||||
bool m_multiline = true; //!< text respects ascii newline characters
|
||||
};
|
||||
|
||||
class FontDrawInterface
|
||||
@@ -63,10 +64,13 @@ namespace AzFramework
|
||||
|
||||
virtual void DrawScreenAlignedText2d(
|
||||
const TextDrawParameters& params,
|
||||
const AZStd::string_view& string) = 0;
|
||||
AZStd::string_view text) = 0;
|
||||
virtual void DrawScreenAlignedText3d(
|
||||
const TextDrawParameters& params,
|
||||
const AZStd::string_view& string) = 0;
|
||||
AZStd::string_view text) = 0;
|
||||
virtual AZ::Vector2 GetTextSize(
|
||||
const TextDrawParameters& params,
|
||||
AZStd::string_view text) = 0;
|
||||
};
|
||||
|
||||
class FontQueryInterface
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
*/
|
||||
// Description : Ebus for querying thermal information of the device.
|
||||
|
||||
#pragma once
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
|
||||
// The different types of temperature sensor that could be available on the device.
|
||||
enum class ThermalSensorType : int
|
||||
{
|
||||
CPU = 0,
|
||||
GPU,
|
||||
Battery,
|
||||
Count
|
||||
};
|
||||
|
||||
// Handles requests for thermal information
|
||||
class ThermalInfoHandler : public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
|
||||
virtual ~ThermalInfoHandler() = default;
|
||||
|
||||
/**
|
||||
* Returns the current temperature of a specific sensor in Celcius degrees.
|
||||
* If the type of sensor is not available on the device it returns 0.
|
||||
*
|
||||
* \param sensor The sensor type.
|
||||
*/
|
||||
virtual float GetSensorTemp(ThermalSensorType sensor) = 0;
|
||||
|
||||
/**
|
||||
* Returns the temperature that is considered as overheating for a specific sensor.
|
||||
* The value returned is in Celcius degrees.
|
||||
*
|
||||
* \param sensor The sensor type.
|
||||
*/
|
||||
virtual float GetSensorOverheatingTemp(ThermalSensorType sensor) = 0;
|
||||
};
|
||||
|
||||
using ThermalInfoRequestsBus = AZ::EBus<ThermalInfoHandler>;
|
||||
|
||||
|
||||
@@ -296,6 +296,7 @@ set(FILES
|
||||
Spawnable/SpawnableSystemComponent.cpp
|
||||
Terrain/TerrainDataRequestBus.h
|
||||
Terrain/TerrainDataRequestBus.cpp
|
||||
Thermal/ThermalInfo.h
|
||||
Platform/PlatformDefaults.h
|
||||
Windowing/WindowBus.h
|
||||
Windowing/NativeWindow.cpp
|
||||
|
||||
+6
@@ -13,6 +13,7 @@
|
||||
#include <AzFramework/API/ApplicationAPI_Platform.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
|
||||
#include <AzFramework/Thermal/ThermalInfo_Android.h>
|
||||
|
||||
#include <AzCore/Android/AndroidEnv.h>
|
||||
#include <AzCore/Android/JNI/Object.h>
|
||||
@@ -95,6 +96,7 @@ namespace AzFramework
|
||||
private:
|
||||
AndroidEventDispatcher* m_eventDispatcher;
|
||||
ApplicationLifecycleEvents::Event m_lastEvent;
|
||||
AZStd::unique_ptr<ThermalInfoHandler> m_thermalInfoHandler;
|
||||
|
||||
AZStd::atomic<bool> m_requestResponseReceived;
|
||||
AZStd::unique_ptr<AZ::Android::JNI::Object> m_lumberyardActivity;
|
||||
@@ -125,6 +127,10 @@ namespace AzFramework
|
||||
AndroidLifecycleEvents::Bus::Handler::BusConnect();
|
||||
AndroidAppRequests::Bus::Handler::BusConnect();
|
||||
PermissionRequestResultNotification::Bus::Handler::BusConnect();
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
m_thermalInfoHandler = AZStd::make_unique<ThermalInfoAndroidHandler>();
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
#include "ThermalInfo_Android.h"
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
ThermalInfoAndroidHandler::ThermalInfoAndroidHandler()
|
||||
{
|
||||
static_assert(AZ_ARRAY_SIZE(m_temperatureFiles) == static_cast<int>(ThermalSensorType::Count), "Thermal count does not match temperature array size");
|
||||
ThermalInfoRequestsBus::Handler::BusConnect();
|
||||
memset(m_temperatureFiles, 0, sizeof(m_temperatureFiles));
|
||||
|
||||
const int sensorCount = static_cast<int>(ThermalSensorType::Count);
|
||||
const char* sensorTypes[sensorCount] = { "cpu", "gpu", "battery" };
|
||||
const int maxStringLen = 128;
|
||||
char tempString[maxStringLen];
|
||||
const char* thermalPath = "/sys/class/thermal";
|
||||
// List the elements from the thermal folder to get the thermal_zones available on the device
|
||||
DIR* directory = opendir(thermalPath);
|
||||
if (directory)
|
||||
{
|
||||
struct dirent* item;
|
||||
const char* thermalPrefix = "thermal_zone";
|
||||
// List all items of the directory and find the one that start with thermal_zone (thermal_zone0, thermal_zone1, etc)
|
||||
while ((item = readdir(directory)) != nullptr)
|
||||
{
|
||||
if (strncmp(item->d_name, thermalPrefix, strlen(thermalPrefix)) == 0)
|
||||
{
|
||||
// Try to deduce the type of sensor. For this we read the "type" file of the thermal zone.
|
||||
// This "type" is a string set by the manufacturer, so it can be anything.
|
||||
AZStd::string path = AZStd::string::format("%s/%s/type", thermalPath, item->d_name);
|
||||
FILE* sensorTypeFile = fopen(path.c_str(), "r");
|
||||
if (sensorTypeFile)
|
||||
{
|
||||
if (fscanf(sensorTypeFile, "%s", tempString))
|
||||
{
|
||||
for (int i = 0; i < sensorCount; ++i)
|
||||
{
|
||||
if (m_temperatureFiles[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t foundPos = AzFramework::StringFunc::Find(tempString, sensorTypes[i]);
|
||||
if (foundPos != AZStd::string::npos)
|
||||
{
|
||||
path = AZStd::string::format("%s/%s/temp", thermalPath, item->d_name);
|
||||
m_temperatureFiles[i] = fopen(path.c_str(), "r");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(sensorTypeFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(directory);
|
||||
}
|
||||
|
||||
int cpuSensorIndex = static_cast<int>(ThermalSensorType::CPU);
|
||||
if (!m_temperatureFiles[cpuSensorIndex])
|
||||
{
|
||||
// If we didn't find the CPU sensor just assume it's the first one.
|
||||
AZStd::string path = AZStd::string::format("%s/thermal_zone0/temp", thermalPath);
|
||||
m_temperatureFiles[cpuSensorIndex] = fopen(path.c_str(), "r");
|
||||
}
|
||||
}
|
||||
|
||||
ThermalInfoAndroidHandler::~ThermalInfoAndroidHandler()
|
||||
{
|
||||
ThermalInfoRequestsBus::Handler::BusDisconnect();
|
||||
for (int i = 0; i < static_cast<int>(ThermalSensorType::Count); ++i)
|
||||
{
|
||||
if (m_temperatureFiles[i])
|
||||
{
|
||||
fclose(m_temperatureFiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float ThermalInfoAndroidHandler::GetSensorTemp(ThermalSensorType sensor)
|
||||
{
|
||||
FILE* tempFile = m_temperatureFiles[static_cast<int>(sensor)];
|
||||
if (!tempFile)
|
||||
{
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
fseek(tempFile, 0, SEEK_SET);
|
||||
float temperature = 0.f;
|
||||
fscanf(tempFile, "%f", &temperature);
|
||||
temperature /= 1000.0f;
|
||||
return temperature;
|
||||
}
|
||||
|
||||
float ThermalInfoAndroidHandler::GetSensorOverheatingTemp(ThermalSensorType sensor)
|
||||
{
|
||||
const int overheatingTemperatures[static_cast<int>(ThermalSensorType::Count)] =
|
||||
{
|
||||
70, // CPU
|
||||
70, // GPU
|
||||
40 // Battery
|
||||
};
|
||||
|
||||
return overheatingTemperatures[static_cast<int>(sensor)];
|
||||
}
|
||||
|
||||
#endif // !defined(AZ_RELEASE_BUILD)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if !defined(AZ_RELEASE_BUILD)
|
||||
#include <AzFramework/Thermal/ThermalInfo.h>
|
||||
|
||||
class ThermalInfoAndroidHandler : public ThermalInfoRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
ThermalInfoAndroidHandler();
|
||||
~ThermalInfoAndroidHandler() override;
|
||||
|
||||
float GetSensorTemp(ThermalSensorType sensor) override;
|
||||
float GetSensorOverheatingTemp(ThermalSensorType sensor) override;
|
||||
|
||||
private:
|
||||
FILE* m_temperatureFiles[static_cast<int>(ThermalSensorType::Count)];
|
||||
};
|
||||
#endif // !defined(AZ_RELEASE_BUILD)
|
||||
@@ -36,4 +36,6 @@ set(FILES
|
||||
AzFramework/Process/ProcessCommon.h
|
||||
AzFramework/Process/ProcessWatcher_Android.cpp
|
||||
AzFramework/Process/ProcessCommunicator_Android.cpp
|
||||
AzFramework/Thermal/ThermalInfo_Android.cpp
|
||||
AzFramework/Thermal/ThermalInfo_Android.h
|
||||
)
|
||||
|
||||
@@ -22,7 +22,9 @@ namespace AzNetworking
|
||||
AZ::HashValue32 HashSerializer::GetHash() const
|
||||
{
|
||||
// Just truncate the upper bits
|
||||
return static_cast<AZ::HashValue32>(m_hash);
|
||||
const AZ::HashValue32 lower = static_cast<AZ::HashValue32>(m_hash);
|
||||
const AZ::HashValue32 upper = static_cast<AZ::HashValue32>(m_hash >> 32);
|
||||
return lower ^ upper;
|
||||
}
|
||||
|
||||
SerializerMode HashSerializer::GetSerializerMode() const
|
||||
|
||||
@@ -56,6 +56,6 @@ namespace AzNetworking
|
||||
|
||||
private:
|
||||
|
||||
AZ::HashValue64 m_hash;
|
||||
AZ::HashValue64 m_hash = AZ::HashValue64{ 0 };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -159,6 +159,8 @@ namespace AzQtComponents
|
||||
// Timer for updating our hovered drop zone opacity
|
||||
QObject::connect(m_dropZoneHoverFadeInTimer, &QTimer::timeout, this, &FancyDocking::onDropZoneHoverFadeInUpdate);
|
||||
m_dropZoneHoverFadeInTimer->setInterval(g_FancyDockingConstants.dropZoneHoverFadeUpdateIntervalMS);
|
||||
QIcon dragIcon = QIcon(QStringLiteral(":/Cursors/Grabbing.svg"));
|
||||
m_dragCursor = QCursor(dragIcon.pixmap(16), 5, 2);
|
||||
}
|
||||
|
||||
FancyDocking::~FancyDocking()
|
||||
@@ -1884,6 +1886,8 @@ namespace AzQtComponents
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(m_dragCursor);
|
||||
|
||||
QPoint relativePressPos = pressPos;
|
||||
|
||||
// If we are dragging a floating window, we need to grab a reference to its
|
||||
@@ -3565,6 +3569,11 @@ namespace AzQtComponents
|
||||
*/
|
||||
void FancyDocking::clearDraggingState()
|
||||
{
|
||||
if (QApplication::overrideCursor())
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
m_ghostWidget->hide();
|
||||
|
||||
// Release the mouse and keyboard from our main window since we grab them when we start dragging
|
||||
|
||||
@@ -266,6 +266,8 @@ namespace AzQtComponents
|
||||
|
||||
QString m_floatingWindowIdentifierPrefix;
|
||||
QString m_tabContainerIdentifierPrefix;
|
||||
|
||||
QCursor m_dragCursor;
|
||||
};
|
||||
|
||||
} // namespace AzQtComponents
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <QAction>
|
||||
#include <QActionEvent>
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIcon>
|
||||
#include <QLayout>
|
||||
@@ -419,6 +420,14 @@ namespace AzQtComponents
|
||||
// a mouse move. The paint handler updates the close button's visibility
|
||||
setAttribute(Qt::WA_Hover);
|
||||
AzQtComponents::Style::addClass(this, g_emptyStyleClass);
|
||||
|
||||
QIcon icon = QIcon(QStringLiteral(":/Cursors/Grab_release.svg"));
|
||||
m_hoverCursor = QCursor(icon.pixmap(16), 5, 2);
|
||||
|
||||
icon = QIcon(QStringLiteral(":/Cursors/Grabbing.svg"));
|
||||
m_dragCursor = QCursor(icon.pixmap(16), 5, 2);
|
||||
|
||||
this->setCursor(m_hoverCursor);
|
||||
}
|
||||
|
||||
void TabBar::setHandleOverflow(bool handleOverflow)
|
||||
@@ -455,6 +464,11 @@ namespace AzQtComponents
|
||||
{
|
||||
if (mouseEvent->buttons() & Qt::LeftButton)
|
||||
{
|
||||
if (!QApplication::overrideCursor() || *QApplication::overrideCursor() != m_dragCursor)
|
||||
{
|
||||
QApplication::setOverrideCursor(m_dragCursor);
|
||||
}
|
||||
|
||||
m_lastMousePress = mouseEvent->pos();
|
||||
}
|
||||
|
||||
@@ -469,6 +483,7 @@ namespace AzQtComponents
|
||||
// selected tab is moved around. The close button is not explicitly rendered for the
|
||||
// moved tab during this operation. We need to make sure not to set it visible again
|
||||
// while the tab is moving. This flag makes sure it happens.
|
||||
|
||||
m_movingTab = true;
|
||||
}
|
||||
|
||||
@@ -479,6 +494,13 @@ namespace AzQtComponents
|
||||
|
||||
void TabBar::mouseReleaseEvent(QMouseEvent* mouseEvent)
|
||||
{
|
||||
// Ensure we don't reset the cursor in the case of a dummy event being sent from DockTabWidget to trigger the animation.
|
||||
Qt::MouseButtons realButtons = QApplication::mouseButtons();
|
||||
if (QApplication::overrideCursor() && !(realButtons & Qt::LeftButton))
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
if (m_movingTab && !(mouseEvent->buttons() & Qt::LeftButton))
|
||||
{
|
||||
// When a moving tab is released, there is a short animation to put the moving tab
|
||||
@@ -632,13 +654,7 @@ namespace AzQtComponents
|
||||
{
|
||||
QPoint p = tabRect(i).topLeft();
|
||||
|
||||
int rightPadding = g_closeButtonPadding;
|
||||
if (m_overflowing == Overflowing)
|
||||
{
|
||||
rightPadding = 0;
|
||||
}
|
||||
|
||||
p.setX(p.x() + tabRect(i).width() - rightPadding - g_closeButtonWidth);
|
||||
p.setX(p.x() + tabRect(i).width() - g_closeButtonPadding - g_closeButtonWidth);
|
||||
p.setY(p.y() + 1 + (tabRect(i).height() - g_closeButtonWidth) / 2);
|
||||
tabBtn->move(p);
|
||||
}
|
||||
|
||||
@@ -203,6 +203,9 @@ namespace AzQtComponents
|
||||
bool m_movingTab = false;
|
||||
QPoint m_lastMousePress;
|
||||
|
||||
QCursor m_dragCursor;
|
||||
QCursor m_hoverCursor;
|
||||
|
||||
void resetOverflow();
|
||||
void overflowIfNeeded();
|
||||
void showCloseButtonAt(int index);
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Cursor-_x2F_-Grab-release">
|
||||
<g id="Group" transform="translate(3.366335, 2.000000)">
|
||||
<g id="Path">
|
||||
<g>
|
||||
<path id="path-1" d="M2.4,1v7.5H2.2C1.3,7.9,0,7.8-1.1,8.2l-0.3,0.1c-0.9,0.4-1.3,1.5-0.9,2.4l1.6,4c0.3,0.8,1.1,1.8,1.7,2.5
|
||||
l0.5,0.4c0.4,0.3,0.7,0.7,1.1,0.9l0.1,0.1L3,18.8l0.3,0.3c0.1,0.3,0.3,0.4,0.3,0.4v0.1l0,0V22h7l0.5-0.9l0.5,0.9h3.3v-2.5l0,0
|
||||
v-0.1l0.1-0.3l0.1-0.3l0.1-0.3l0.1-0.4l0.3-0.5l0.3-0.5l0.4-0.8l0.1-0.3c0.5-0.5,0.9-1.8,0.9-2.6V7.6c0-1.2-0.4-3.8-2.2-3.8
|
||||
c-1.1,0-1.3,0.5-1.6,0.8c-0.1-0.3-0.4-1.5-1.7-1.6c-1.1,0-1.6,0.5-1.7,0.9C10,3.5,9.7,2.5,8.4,2.5C7.6,2.5,7.4,2.6,7.1,3
|
||||
C6.8,2.5,6.1-2,4.6-2C3.3-2,2.4,0,2.4,1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path id="path-1_1_" d="M2.4,1v7.5H2.2C1.3,7.9,0,7.8-1.1,8.2l-0.3,0.1c-0.9,0.4-1.3,1.5-0.9,2.4l1.6,4c0.3,0.8,1.1,1.8,1.7,2.5
|
||||
l0.5,0.4c0.4,0.3,0.7,0.7,1.1,0.9l0.1,0.1L3,18.8l0.3,0.3c0.1,0.1,0.3,0.3,0.3,0.3v0.3l0,0V22h7l0.5-0.9l0.5,0.9h3.3v-2.5l0,0
|
||||
v-0.1l0.1-0.3l0.1-0.3l0.1-0.3l0.1-0.4l0.3-0.5l0.3-0.5l0.4-0.8l0.1-0.3c0.5-0.5,0.9-1.8,0.9-2.6V7.6c0-1.2-0.4-3.8-2.2-3.8
|
||||
c-1.1,0-1.3,0.5-1.6,0.8c-0.1-0.3-0.4-1.5-1.7-1.6c-1.1,0-1.6,0.5-1.7,0.9C10,3.5,9.7,2.5,8.4,2.5C8,2.5,7.6,3,7.1,3.1
|
||||
c0,0.3,0.3-2.9-0.3-3.8C6.6-1.1,6.1-2,4.6-2C3.3-2,2.4,0,2.4,1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="Path_1_" class="st0" d="M7.1,6.4V5.3c0-0.5,0.4-1.5,1.3-1.5s1.2,0.9,1.2,1.6c0,2,0,0.5,0,1.3h0.8c0-2.8,0-0.4,0-0.9
|
||||
c0-0.8,0.5-1.3,1.3-1.3S13,5.1,13,5.9c0,0.5,0,0.9,0,0.9h0.8V6.4c0-0.7,0.3-1.2,1.1-1.2c0.9,0,1.2,0.7,1.3,1.7v1.3v5
|
||||
c0,0.7-0.3,1.5-0.5,2.1l-0.1,0.3l-0.4,0.8L14.8,17l-0.3,0.4l-0.3,0.4L14,18.2c-0.3,0.5-0.4,0.9-0.4,1.3v0.1v1.2h-1.2l-1.2-2.5
|
||||
l-1.3,2.5h-5v-1.2c0-0.4-0.4-0.8-0.8-1.5l-0.4-0.4l-0.4-0.4l-0.1-0.1L2.9,17l-0.3-0.3L2,16.1c-0.4-0.4-1.1-0.9-1.3-1.5l-0.3-0.4
|
||||
l-1.5-4c-0.1-0.3,0-0.7,0.1-0.8c0,0,0,0,0.1-0.1l0.3-0.1c1.1-0.4,2.2,0.1,2.9,1.1l0.1,0.1l1.1,1.7V1c0-0.7,0.7-1.3,1.3-1.3
|
||||
s1.2,0.7,1.2,1.2c0,3.6,0,5.4,0,5.5H7.1z"/>
|
||||
<polygon id="Path_2_" points="12.4,10.9 13.7,10.9 13.7,15.8 12.4,15.8 "/>
|
||||
<polygon id="Path_3_" points="9.9,10.9 11.2,10.9 11.2,15.8 9.9,15.8 "/>
|
||||
<polygon id="Path_4_" points="7.4,10.9 8.7,10.9 8.7,15.8 7.4,15.8 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M5.3,4.2V2.2c0-0.6,0.2-1.1,0.6-1.6C6.3,0.1,6.9,0,7.4,0l0,0c1,0,1.7,0.5,2.4,1.1l0.2,0.2l0,0l0.2-0.2
|
||||
c0.5,0,0.8-0.2,1.1-0.2h0.2c1,0,1.7,0.5,2.4,1.1l0.2,0.2l0,0L14,2c0.3-0.2,0.5-0.3,0.8-0.3H15c1.1,0,1.9,0.5,2.5,1.1L17.7,3l0,0
|
||||
l0.2-0.2c0.5-0.2,0.8-0.3,1.1-0.3h0.2c1,0,1.7,0.5,2.4,1.1c0.6,0.6,1,1.6,1,2.5l0,0v7.5c0,1-0.5,2.5-1,3.3c0,0-2.2,3.3-2.2,4.5l0,0
|
||||
V24h-2.9l-0.6-2.2L14.7,24H6.9v-2.4c0-1.1-4.8-4.3-4.8-4.3c-1-0.6-1.6-2.1-1.6-3.2l0,0V8.4c0-1.1,0.5-2.1,1.1-2.9
|
||||
C2.6,4.6,4,4.1,5.3,4.2L5.3,4.2z"/>
|
||||
<g>
|
||||
<path d="M7.4,1C7.9,1,8.5,1.2,9,1.8l0.2,0.2l0.7,0.7L10.5,2C10.7,2,10.8,2,11,1.9c0.1,0,0.2,0,0.2,0h0.2c0.6,0,1.1,0.3,1.7,0.8
|
||||
l0.2,0.2l0.7,0.7l0.7-0.7l0.1-0.1c0,0,0.1,0,0.1-0.1c0,0,0.1,0,0.1-0.1H15c0.7,0,1.3,0.3,1.8,0.8L17,3.7l0.7,0.7l0.7-0.7
|
||||
c0.1,0,0.1,0,0.2-0.1c0.1-0.1,0.4-0.1,0.4-0.1h0.2c0.6,0,1.1,0.3,1.7,0.8c0.4,0.4,0.7,1.1,0.7,1.8v7.5c0,0.7-0.4,2.2-0.8,2.8
|
||||
c-0.7,1.1-2.4,3.7-2.4,5V23h-1.1l-0.4-1.5L16,19l-1.1,2.3L14.1,23H7.9v-1.4c0-0.4,0-1.6-5.2-5.1C2.1,16,1.5,15,1.5,14.1V8.4
|
||||
c0-0.7,0.3-1.5,0.9-2.2c0.6-0.6,1.5-1,2.4-1c0.1,0,0.2,0,0.4,0l1.1,0.1V4.2V2.2c0-0.4,0.1-0.6,0.3-0.9l0.1-0.1l0.1-0.1
|
||||
C6.8,1,7.1,1,7.4,1 M14.9,2.7L14.9,2.7L14.9,2.7 M7.4,0C6.9,0,6.3,0.1,5.9,0.6c-0.5,0.5-0.6,1-0.6,1.6v2.1c-0.2,0-0.3,0-0.5,0
|
||||
c-1.1,0-2.3,0.5-3.2,1.3C1,6.3,0.5,7.3,0.5,8.4v5.7c0,1.1,0.6,2.5,1.6,3.2c0,0,4.8,3.2,4.8,4.3V24h7.8l1.1-2.2l0.6,2.2h2.9v-2.5
|
||||
c0-1.1,2.2-4.5,2.2-4.5c0.5-0.8,1-2.4,1-3.3V6.1c0-1-0.3-1.9-1-2.5c-0.6-0.6-1.4-1.1-2.4-1.1H19c-0.3,0-0.6,0.2-1.1,0.3L17.7,3
|
||||
l-0.2-0.2c-0.6-0.6-1.4-1.1-2.5-1.1h-0.2c-0.3,0-0.5,0.2-0.8,0.3l-0.2,0.2L13.7,2c-0.6-0.6-1.4-1.1-2.4-1.1h-0.2
|
||||
c-0.3,0-0.6,0.2-1.1,0.2L9.9,1.2L9.8,1.1C9.1,0.4,8.3,0,7.4,0L7.4,0z"/>
|
||||
</g>
|
||||
<polygon class="st1" points="10.9,10.4 12.5,10.4 12.5,16.8 10.9,16.8 "/>
|
||||
<polygon class="st1" points="14,10.4 15.6,10.4 15.6,16.8 14,16.8 "/>
|
||||
<polygon class="st1" points="17.2,10.4 18.8,10.4 18.8,16.8 17.2,16.8 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -637,5 +637,7 @@
|
||||
</qresource>
|
||||
<qresource prefix="/Cursors">
|
||||
<file alias="Pointer.svg">img/UI20/Cursors/Pointer.svg</file>
|
||||
<file alias="Grab_release.svg">img/UI20/Cursors/Grab_release.svg</file>
|
||||
<file alias="Grabbing.svg">img/UI20/Cursors/Grabbing.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -199,6 +199,43 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetTemplateSourcePaths(const PrefabDomValue& prefabDom, AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths)
|
||||
{
|
||||
PrefabDomValueConstReference findSourceResult = PrefabDomUtils::FindPrefabDomValue(prefabDom, PrefabDomUtils::SourceName);
|
||||
if (!findSourceResult.has_value() || !(findSourceResult->get().IsString()) ||
|
||||
findSourceResult->get().GetStringLength() == 0)
|
||||
{
|
||||
AZ_Assert(
|
||||
false,
|
||||
"PrefabDomUtils::GetDependentTemplatePath - Source value of prefab in the provided DOM is not a valid string.");
|
||||
return;
|
||||
}
|
||||
|
||||
templateSourcePaths.emplace(findSourceResult->get().GetString());
|
||||
PrefabDomValueConstReference instancesReference = GetInstancesValue(prefabDom);
|
||||
if (instancesReference.has_value())
|
||||
{
|
||||
const PrefabDomValue& instances = instancesReference->get();
|
||||
|
||||
for (PrefabDomValue::ConstMemberIterator instanceIterator = instances.MemberBegin();
|
||||
instanceIterator != instances.MemberEnd(); ++instanceIterator)
|
||||
{
|
||||
GetTemplateSourcePaths(instanceIterator->value, templateSourcePaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrefabDomValueConstReference GetInstancesValue(const PrefabDomValue& prefabDom)
|
||||
{
|
||||
PrefabDomValueConstReference findInstancesResult = FindPrefabDomValue(prefabDom, PrefabDomUtils::InstancesName);
|
||||
if (!findInstancesResult.has_value() || !(findInstancesResult->get().IsObject()))
|
||||
{
|
||||
return AZStd::nullopt;
|
||||
}
|
||||
|
||||
return findInstancesResult->get();
|
||||
}
|
||||
|
||||
void PrintPrefabDomValue(
|
||||
[[maybe_unused]] const AZStd::string_view printMessage,
|
||||
[[maybe_unused]] const PrefabDomValue& prefabDomValue)
|
||||
|
||||
@@ -100,6 +100,20 @@ namespace AzToolsFramework
|
||||
.Append(instanceName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a set of all the template source paths in the given dom.
|
||||
* @param prefabDom The DOM to get the template source paths from.
|
||||
* @param[out] templateSourcePaths The set of template source paths to populate.
|
||||
*/
|
||||
void GetTemplateSourcePaths(const PrefabDomValue& prefabDom, AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
|
||||
|
||||
/**
|
||||
* Gets the instances DOM value from the given prefab DOM.
|
||||
*
|
||||
* @return the instances DOM value or AZStd::nullopt if it instances can't be found.
|
||||
*/
|
||||
PrefabDomValueConstReference GetInstancesValue(const PrefabDomValue& prefabDom);
|
||||
|
||||
/**
|
||||
* Prints the contents of the given prefab DOM value to the debug output console in a readable format.
|
||||
* @param printMessage The message that will be printed before printing the PrefabDomValue
|
||||
|
||||
@@ -210,25 +210,30 @@ namespace AzToolsFramework
|
||||
auto relativePath = m_prefabLoaderInterface->GetRelativePathToProject(filePath);
|
||||
Prefab::TemplateId templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath);
|
||||
|
||||
// If the template isn't currently loaded, there's no way for it to be in the hierarchy so we just skip the check.
|
||||
if (templateId != Prefab::InvalidTemplateId && IsPrefabInInstanceAncestorHierarchy(templateId, instanceToParentUnder->get()))
|
||||
if (templateId == InvalidTemplateId)
|
||||
{
|
||||
return AZ::Failure(
|
||||
AZStd::string::format(
|
||||
"Instantiate Prefab operation aborted - Cyclical dependency detected\n(%s depends on %s).",
|
||||
relativePath.Native().c_str(),
|
||||
instanceToParentUnder->get().GetTemplateSourcePath().Native().c_str()
|
||||
)
|
||||
);
|
||||
// Load the template from the file
|
||||
templateId = m_prefabLoaderInterface->LoadTemplateFromFile(filePath);
|
||||
AZ_Assert(templateId != InvalidTemplateId, "Template with source path %s couldn't be loaded correctly.", filePath);
|
||||
}
|
||||
|
||||
|
||||
const PrefabDom& templateDom = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
|
||||
AZStd::unordered_set<AZ::IO::Path> templatePaths;
|
||||
PrefabDomUtils::GetTemplateSourcePaths(templateDom, templatePaths);
|
||||
|
||||
if (IsCyclicalDependencyFound(instanceToParentUnder->get(), templatePaths))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"Instantiate Prefab operation aborted - Cyclical dependency detected\n(%s depends on %s).",
|
||||
relativePath.Native().c_str(), instanceToParentUnder->get().GetTemplateSourcePath().Native().c_str()));
|
||||
}
|
||||
|
||||
{
|
||||
// Initialize Undo Batch object
|
||||
ScopedUndoBatch undoBatch("Instantiate Prefab");
|
||||
|
||||
PrefabDom instanceToParentUnderDomBeforeCreate;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(
|
||||
instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get());
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get());
|
||||
|
||||
// Instantiate the Prefab
|
||||
auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder);
|
||||
@@ -242,8 +247,7 @@ namespace AzToolsFramework
|
||||
PrefabUndoHelpers::UpdatePrefabInstance(
|
||||
instanceToParentUnder->get(), "Update prefab instance", instanceToParentUnderDomBeforeCreate, undoBatch.GetUndoBatch());
|
||||
|
||||
CreateLink({}, instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(),
|
||||
undoBatch.GetUndoBatch(), parent);
|
||||
CreateLink({}, instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), parent);
|
||||
AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId();
|
||||
|
||||
// Apply position
|
||||
@@ -296,17 +300,17 @@ namespace AzToolsFramework
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
bool PrefabPublicHandler::IsPrefabInInstanceAncestorHierarchy(TemplateId prefabTemplateId, InstanceOptionalConstReference instance)
|
||||
bool PrefabPublicHandler::IsCyclicalDependencyFound(
|
||||
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths)
|
||||
{
|
||||
InstanceOptionalConstReference currentInstance = instance;
|
||||
|
||||
while (currentInstance.has_value())
|
||||
{
|
||||
if (currentInstance->get().GetTemplateId() == prefabTemplateId)
|
||||
if (templateSourcePaths.contains(currentInstance->get().GetTemplateSourcePath()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
currentInstance = currentInstance->get().GetParentInstance();
|
||||
}
|
||||
|
||||
@@ -1003,5 +1007,5 @@ namespace AzToolsFramework
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
@@ -107,13 +107,15 @@ namespace AzToolsFramework
|
||||
const AZStd::vector<AZ::EntityId>& entityIds, EntityList& inputEntityList, EntityList& topLevelEntities,
|
||||
AZ::EntityId& commonRootEntityId, InstanceOptionalReference& commonRootEntityOwningInstance);
|
||||
|
||||
/* Detects whether an instance of prefabTemplateId is present in the hierarchy of ancestors of instance.
|
||||
/* Checks whether the template source path of any of the ancestors in the instance hierarchy matches with one of the
|
||||
* paths provided in a set.
|
||||
*
|
||||
* \param prefabTemplateId The template id to test for
|
||||
* \param instance The instance whose ancestor hierarchy prefabTemplateId will be tested against.
|
||||
* \return true if an instance of the template of id prefabTemplateId could be found in the ancestor hierarchy of instance, false otherwise.
|
||||
* \param instance The instance whose ancestor hierarchy the provided set of template source paths will be tested against.
|
||||
* \param templateSourcePaths The template source paths provided to be checked against the instance ancestor hierarchy.
|
||||
* \return true if any of the template source paths could be found in the ancestor hierarchy of instance, false otherwise.
|
||||
*/
|
||||
bool IsPrefabInInstanceAncestorHierarchy(TemplateId prefabTemplateId, InstanceOptionalConstReference instance);
|
||||
bool IsCyclicalDependencyFound(
|
||||
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
|
||||
|
||||
static Instance* GetParentInstance(Instance* instance);
|
||||
static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant);
|
||||
@@ -129,5 +131,5 @@ namespace AzToolsFramework
|
||||
|
||||
uint64_t m_newEntityCounter = 1;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+17
-1
@@ -254,7 +254,7 @@ namespace AzToolsFramework
|
||||
m_localTransformDirty = true;
|
||||
m_worldTransformDirty = true;
|
||||
|
||||
if (GetEntity())
|
||||
if (const AZ::Entity* entity = GetEntity())
|
||||
{
|
||||
SetDirty();
|
||||
|
||||
@@ -273,6 +273,22 @@ namespace AzToolsFramework
|
||||
{
|
||||
boundsUnion->OnTransformUpdated(GetEntity());
|
||||
}
|
||||
// Fire a property changed notification for this component
|
||||
if (const AZ::Component* component = entity->FindComponent<Components::TransformComponent>())
|
||||
{
|
||||
PropertyEditorEntityChangeNotificationBus::Event(
|
||||
GetEntityId(), &PropertyEditorEntityChangeNotifications::OnEntityComponentPropertyChanged, component->GetId());
|
||||
}
|
||||
|
||||
// Refresh the property editor if we're selected
|
||||
bool selected = false;
|
||||
ToolsApplicationRequestBus::BroadcastResult(
|
||||
selected, &AzToolsFramework::ToolsApplicationRequests::IsSelected, GetEntityId());
|
||||
if (selected)
|
||||
{
|
||||
ToolsApplicationEvents::Bus::Broadcast(
|
||||
&ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user