IN THIS ARTICLE
Settings Registry API Examples
Details the available set of APIs provided by the Settings Registry. Examples are provided for the ability to query settings, using both the query and visitor APIs. How to define and update settings using the setter APIs.
Settings can be merged from files or in-memory JSON documents using the merge API. Finally, notifications of when settings have been modified or removed are available using the notification API.
Query API
The
query API supports directly querying the types of bool
, int64_t
, double
, AZStd::string
, AZStd::fixed_string
, and any object reflected to the SerializeContext
. The getter method of querying objects from the SerializeContext
is safe to use, but it should be respected as an implementation detail of the Settings Registry. The SettingsRegistryInterface::GetObject
interface will remain stable, but no assumptions should be made on how objects are serialized.
The following example demonstrates the query API for builtin settings types (bool
, AZ::s64
, AZ::u64
double
, (fixed_)string
):
// Setting FileIOBase alias based on values within the Settings Registry
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString
if (FixedValueString pathAlias;
m_settingsRegistry->Get(pathAlias, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
{
fileIoBase->SetAlias("@products@", pathAlias.c_str());
}
if (AZStd::string pathAlias;
m_settingsRegistry->Get(pathAlias, AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
{
fileIoBase->SetAlias("@projectroot@", pathAlias.c_str());
}
if (AZ::s64 intValue;
m_settingsRegistry->Get(pathAlias, "/O3DE/Int64Value"))
{
// Do stuff with int value
}
Load C++ class objects with the query API
To load C++ class objects using the Settings Registry, the type itself must be reflected to the SerializeContext
to use the query API. Instead of calling the SettingsRegistryInterface::Get
method for querying built-in JSON types, the SettingsRegistry::GetObject
function is used.
The following example shows how to use the SettingsRegistryInterface::GetObject
function to load the SettingsRegistry into an AzPhysics::SceneConfiguration
.
// Make sure the AzPhysics::SceneConfiguration class is reflected
void SceneConfiguration::Reflect(AZ::ReflectContext* context)
{
Physics::WorldConfiguration::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SceneConfiguration>()
->Version(1)
->Field("LegacyConfig", &SceneConfiguration::m_legacyConfiguration)
->Field("LegacyId", &SceneConfiguration::m_legacyId)
->Field("Name", &SceneConfiguration::m_sceneName)
;
}
}
// ... Later on the SceneConfiguration class can be loaded from the SettingsRegistry using the `GetObject` function
AzPhysics::SceneConfiguration sceneConfig;
bool configurationRead = false;
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
configurationRead = settingsRegistry->GetObject(sceneConfig, "/Amazon/Gems/PhysX/DefaultSceneConfiguration");
}
Visitor API
The
visitor API supports recursively visiting the JSON object and array children starting at a specified JSON pointer. The visitor API allows for processing each child field which isn’t available when using the GetObject
API. It also provides type flexibility when dealing with JSON data. A visitor for the Settings Registry must implement the AZ::SettingsRegistryInterface::Visitor
class. An instance of that class can be supplied to the AZ::SettingsRegistryInterface::Visit()
method.
The visitor is recommended when serializing in/out complex objects that don’t have any SerializeContext
reflection. Since it isn’t tied to the SerializeContext
, the same logic could be used to read objects from the Settings Registry using the JSON facilities available within other programming languages such as Python, JavaScript, or C#.
The following example demonstrates using the visitor API to gather active gem info populated under the settings object at “/O3DE/Gems”. This is done by the GetGemsInfo function.
// Queries the Settings Registry to get the list active gems targets and source paths
auto GemSettingsVisitor = [&gemInfoList](const AZ::SettingsRegistryInterface::VisitArgs& gemVisitArgs)
{
auto FindGemInfoByName = [&gemVisitArgs](const GemInfo& gemInfo)
{
return gemVisitArgs.m_fieldName == gemInfo.m_gemName;
};
auto gemInfoFoundIter = AZStd::ranges::find_if(gemInfoList, FindGemInfoByName);
GemInfo& gemInfo = gemInfoFoundIter != gemInfoList.end() ? *gemInfoFoundIter : gemInfoList.emplace_back(gemVisitArgs.m_fieldName);
// Read the Gem Target Name into target Name field
auto VisitGemTargets = [&gemInfo](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
{
// Assume the fieldName is the name of the target in this case
gemInfo.m_gemTargetNames.emplace_back(visitArgs.m_fieldName);
return AZ::SettingsRegistryInterface::VisitResponse::Skip;
};
AZ::SettingsRegistryVisitorUtils::VisitObject(gemVisitArgs.m_registry, VisitGemTargets,
FixedValueString::format("%.*s/Targets", AZ_STRING_ARG(gemVisitArgs.m_jsonKeyPath)));
// Visit the "SourcePath" array fields of the gem to populate the Gem Absolute Source Paths array
const auto gemPathKey = FixedValueString::format("%s/%.*s/Path",
AZ::SettingsRegistryMergeUtils::ManifestGemsRootKey, AZ_STRING_ARG(gemVisitArgs.m_fieldName));
if (AZ::IO::Path gemRootPath; gemVisitArgs.m_registry.Get(gemRootPath.Native(), gemPathKey))
{
gemInfo.m_absoluteSourcePaths.emplace_back(gemRootPath);
}
return AZ::SettingsRegistryInterface::VisitResponse::Skip;
};
// Visit the "/O3DE/Gems" setting object to query the data for all active gems
AZ::SettingsRegistryVisitorUtils::VisitObject(settingsRegistry, GemSettingsVisitor,
AZ::SettingsRegistryMergeUtils::ActiveGemsRootKey);
The
visitor utilities API can simplify visiting the direct children fields of a JSON object. These classes and functions are located in SettingsRegistryVisitorUtils.h
.
For example, consider the following JSON array:
{
"O3DE": {
"Array" : [
1,
2
]
}
}
The fields of the array can be visited using the SettingsRegistryVisitorUtils
as in the following example:
struct AppendArrayVisitor
: AZ::SettingsRegistryVisitorUtils::ArrayVisitor
{
CustomArrayVisitor() = default;
AZ::SettingsRegistryInterface::VisitResponse Visit(const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
{
if (int value; visitArgs.m_registry.Get(value, visitArgs.m_jsonKeyPath))
{
m_array.push_back(value);
}
return AZ::SettingsRegistryInterface::VisitResponse::Skip;
}
AZStd::vector<int> m_array;
};
// ...
settingsRegistry->Visit(AppendArrayVisitor, "/O3DE/Array");
The same approach can be used to visit the fields of an object. Consider the following JSON object:
{
"O3DE": {
"Object": {
"Field1": 1,
"Field2": 2
}
}
}
You can use the helper visitor callback function, as in the following example:
AZStd::unordered_map<AZStd::string, int> fieldToIntMap;
auto AppendObjectFields = [&fieldToIntMap](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
{
if (int value{}; visitArgs.m_registry.Get(value, visitArgs.m_jsonKeyPath))
{
fieldToIntMap.emplace(visitArgs.m_fieldName, value);
}
return AZ::SettingsRegistryInterface::VisitResponse::Skip;
};
// ...
if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
settingsRegistry != nullptr)
{
AZ::SettingsRegistryVisitorUtils::VisitObject(*settingsRegistry, AppendObjectFields, "/O3DE/Object");
}
Value setter API
The
value setter API is used to set values within the Settings Registry for bool
, int64_t
, double
, and AZStd::string_view
types, or any type that is reflected to the SerializeContext
.
The Settings Registry SettingsRegistryInterface::Set
function can be used to store supported types such as bool
, AZ::s64/AZ::u64
, double
, and AZStd::string_view
. The following example shows how to add several directories to the Settings Registry as well as plain values:
// Executable folder - corresponds to the @exefolder@ alias
AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
registry.Set("/O3DE/Runtime/FilePaths/BinaryFolder", path.Native());
// Engine root folder - corresponds to the @engroot@ alias
AZStd::string engineRoot = AZ::Utils::GetEnginePath();
registry.Set("/O3DE/Runtime/FilePaths/EngineRootFolder", engineRoot);
// Retrieve the "project_path" and set the Runtime FilePaths to the ProjectPath
AZ::SettingsRegistryInterface::FixedValueString projectPath = AZ::Utils::GetProjectPath();
registry.Set("/O3DE/Runtime/FilePaths/ProjectPath", projectPath);
// Add a bool value
registry.Set("/O3DE/BoolValue", true);
// Add a double value
registry.Set("/O3DE/DoubleValue1", 1.0);
registry.Set("/O3DE/DoubleValue2", 2.0f); // float promoted to double
// Add a signed integer value
registry.Set("/O3DE/SignedInt", 1LL);
// Add an unsigned integer value
registry.Set("/O3DE/UnsignedInt", 2ULL);
Store a C++ Class Object with the value setter API
The value setter API allows transforming a C++ object into JSON data through the SettingsRegistryInterface:SetObject
. There is a prerequisite that the C++ object must be reflected to the SerializeContext
as in the following example:
// Make sure the AzPhysics::SceneConfiguration class is reflected
void SceneConfiguration::Reflect(AZ::ReflectContext* context)
{
Physics::WorldConfiguration::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SceneConfiguration>()
->Version(1)
->Field("LegacyConfig", &SceneConfiguration::m_legacyConfiguration)
->Field("LegacyId", &SceneConfiguration::m_legacyId)
->Field("Name", &SceneConfiguration::m_sceneName)
;
}
}
// ... Later, the SceneConfiguration class can be stored in the Settings Registry
AzPhysics::SceneConfiguration sceneConfig;
sceneConfig.m_sceneName = "PhysX Scene 1";
bool configurationStored = false;
AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
if (settingsRegistry)
{
configurationStored = settingsRegistry->SetObject("/Amazon/Gems/PhysX/DefaultSceneConfiguration", sceneConfig);
}
Value notifier API
The
value notifier API notifies when a key at a JSON pointer within the SettingsRegistry has been modified. The SettingsRegistryInterface::RegisterNotifier
function allows users to register a callback with the Settings Registry that is invoked when the value of a JSON key is set.
The following example demonstrates logging each time the Asset Cache directory has been updated in the Settings Registry:
SettingsRegistry& registry = *AZ::SettingsRegistry::Get();
AZ::SettingsRegistryInterface::NotifyCallback assetCacheChangedCB =
[®istry](const AZ::SettingsRegistryInterface::NotifyArgs& notifyArgs)
{
if (notifyArgs.m_jsonKeyPath == "/O3DE/Runtime/FilePaths/CacheRootFolder"
&& notifyArgs.m_type == SettingsRegistryInterface::Type::String)
{
AZ::IO::FixedMaxPath cacheRootPath;
if (registry.Get(cacheRootPath.Native(), notifyArgs.m_jsonKeyPath))
{
AZ_TracePrintf("Settings Registry Tracking", "The Asset Cache path has changed to %s", cacheRootPath.c_str());
}
}
};
{
// The SettingsRegistryInterface::RegisterNotifier function returns an AZ event handler that needs to be stored for as long
// as desired to receive the notification events
AZ::SettingsRegistryInterface::NotifyEventHandler assetChangedHandler = registry.RegisterNotifier(AZStd::move(assetCacheChangedCB));
// ...
registry.Set("/O3DE/Runtime/FilePaths/CacheRootFolder", "/home/testuser/TestCache");
}
// When the assetChangedHandler scope ends, it will unregister from the Settings Registry Notifier event
// If the handler is desired to persist, it can be stored in a member variable to extend its lifetime
Merge API
The most complex part of the Settings Registry is the merge API . There are three primary types of input that can be supplied to the Settings Registry for merging.
- Input with the syntax <JSONPointerPath>=<value> - This is treated as if command line parameters are being merged.
- An in-memory JSON document
- A path to a file/directory containing
.setreg
/.setregpatch
files
The order in which settings are merged to the registry is important. Keys that are merged later have precedence over keys seen merged earlier. Suppose there is streamer.setreg
that has the content at key “/O3DE/AzFramework/MyArray” such as the following example:
{
"O3DE":
{
"AzFramework":
{
"MyArray":
[
"String1",
"String3",
"String5",
],
"MyObject":
{
"AssetKey": "{80CCDA30-1F70-4982-ADE9-62D3DEE332D4}"
}
}
}
}
Suppose there is a streamer.editor.setreg
with the same “/O3DE/AzFramework/MyArray” key, such as the following example:
{
"O3DE":
{
"AzFramework":
{
"MyArray":
[
true,
false,
75,
"StringX",
"InnerObject":
{
"MyKey": "PathKey"
}
]
}
}
}
If the preceding streamer.editor.setreg
is merged, the settings registry contains the following keys and values:
"/O3DE/AzFramework/MyArray/0" = true
"/O3DE/AzFramework/MyArray/1" = false
"/O3DE/AzFramework/MyArray/2" = 75
"/O3DE/AzFramework/MyArray/3" = "StringX"
"/O3DE/AzFramework/MyArray/4/InnerObject/MyKey" = "PathKey"
"/O3DE/AzFramework/MyObject/AssetKey" = "{80CCDA30-1F70-4982-ADE9-62D3DEE332D4}"
The values that are associated with the JSON array underneath the “/O3DE/AzFramework/MyArray” key from the merger of the streamer.setreg
file have been updated with the values of the streamer.editor.setreg
file.
Merge Settings Registry files from directories
Merging a specific .setreg
or .setregpatch
file or merging a JSON document requires only specifying the file name or JSON content. Merging a directory containing .setreg
or .setregpatch
files requires not only the directory name but also specifying a list of tags known as specializations to the merge API.
The following example contains the specializations of automatedtesting
, automatedtesting_gamelauncher
, game
and the current build configuration tag as part of its name (debug, profile, release):
SettingsRegistryInterface::Specializations specializations{"automatedtesting", "automatedtesting_gamelauncher", "game", AZ_BUILD_CONFIGURATION_TYPE };
AZ::IO::FixedMaxPath mergePath = Internal::GetExecutableDirectory();
if (!mergePath.empty())
{
registry.MergeSettingsFolder((mergePath / SettingsRegistryInterface::RegistryFolder).Native(),
specializations, AZ_TRAIT_OS_PLATFORM_CODENAME);
}
// Look within the Cache Root directory for target build dependency .setreg files
AZ::SettingsRegistryInterface::FixedValueString cacheRootPath;
if (registry.Get(cacheRootPath, FilePathKey_CacheRootFolder))
{
mergePath = AZStd::move(cacheRootPath);
mergePath /= SettingsRegistryInterface::RegistryFolder;
registry.MergeSettingsFolder(mergePath.Native(), specializations, platform);
}
The details of specializations are explained in the following sections on Auxiliary APIs.
Auxiliary APIs
The Settings Registry merge utilities are a set of functions located in AzCore/Settings/SettingsRegistryMergeUtils.h
which contains implementation of merging of common files, directory locations, and the command line. It also contains functions for querying and setting a specialization within the Settings Registry underneath the key of “/Amazon/AzCore/Settings/Specializations”. Specializations can be used for filtering which *.setreg
files to merge when SettingsInterface::MergeSettingsFolder
is invoked.
The Settings Registry Merge Utilities also contains a function for dumping JSON values at a specific JSON pointer path to an AZ::IO::GenericStream
.
The following list provides links to the various auxiliary APIs in AzCore/Settings/SettingsRegistryMergeUtils.h
:
O3DE Manifest utils API
The Settings Registry Merge Utilities also provides the O3DE Manifest Utils API , which is a collection of helper functions for visiting the set of active Gems as well as recursively visiting the O3DE manifest “external_subdirectories” to determine the set of all registered Gems.