diff --git a/Assets/Editor/Prefabs/Default_Level.prefab b/Assets/Editor/Prefabs/Default_Level.prefab
index d02d669f53..0267131fa0 100644
--- a/Assets/Editor/Prefabs/Default_Level.prefab
+++ b/Assets/Editor/Prefabs/Default_Level.prefab
@@ -185,7 +185,7 @@
{
"id": {
"materialAssetId": {
- "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
+ "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
@@ -197,7 +197,7 @@
"id": {
"lodIndex": 0,
"materialAssetId": {
- "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
+ "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 803645540
}
}
diff --git a/Assets/Engine/Engine_Dependencies.xml b/Assets/Engine/Engine_Dependencies.xml
index 0d6541e18e..b969b3bc97 100644
--- a/Assets/Engine/Engine_Dependencies.xml
+++ b/Assets/Engine/Engine_Dependencies.xml
@@ -1,16 +1,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
diff --git a/Assets/Engine/SeedAssetList.seed b/Assets/Engine/SeedAssetList.seed
index aafbffbe8f..18ccd19dc3 100644
--- a/Assets/Engine/SeedAssetList.seed
+++ b/Assets/Engine/SeedAssetList.seed
@@ -1,621 +1,260 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py
index ee8e177bbb..303a012cda 100755
--- a/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py
+++ b/AutomatedTesting/Gem/PythonTests/assetpipeline/asset_processor_tests/asset_processor_batch_dependency_tests.py
@@ -47,82 +47,6 @@ class TestsAssetProcessorBatch_DependenycyTests(object):
"""
AssetProcessorBatch Dependency tests
"""
-
- @pytest.mark.test_case_id("C16877166")
- @pytest.mark.BAT
- @pytest.mark.assetpipeline
- # fmt:off
- def test_WindowsMacPlatforms_RunAPBatch_NotMissingDependency(self, ap_setup_fixture, asset_processor,
- workspace):
- # fmt:on
- """
- Engine Schema
- This test case has a conditional scenario depending on the existence of surfacetypes.xml in a project.
- Some projects have this file and others do not. Run the conditional scenario depending on the existence
- of the file in the project
- libs/materialeffects/surfacetypes.xml is listed as an entry engine_dependencies.xml
- libs/materialeffects/surfacetypes.xml is not listed as a missing dependency
- in the 'assetprocessorbatch' console output
-
- Test Steps:
- 1. Assets are pre-processed
- 2. Verify that engine_dependencies.xml exists
- 3. Verify engine_dependencies.xml has surfacetypes.xml present
- 4. Run Missing Dependency scanner against the engine_dependenciese.xml
- 5. Verify that Surfacetypes.xml is NOT in the missing depdencies output
- 6. Add the schema file which allows our xml parser to understand dependencies for our engine_dependencies file
- 7. Process assets
- 8. Run Missing Dependency scanner against the engine_dependenciese.xml
- 9. Verify that surfacetypes.xml is in the missing dependencies out
- """
-
- env = ap_setup_fixture
- BATCH_LOG_PATH = env["ap_batch_log_file"]
- asset_processor.create_temp_asset_root()
- asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "engine_dependencies.xml"))
- asset_processor.add_scan_folder(os.path.join("Assets", "Engine"))
- asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Libs", "MaterialEffects", "surfacetypes.xml"))
-
- # Precondition: Assets are all processed
- asset_processor.batch_process()
-
- DEPENDENCIES_PATH = os.path.join(asset_processor.temp_project_cache(), "engine_dependencies.xml")
- assert os.path.exists(DEPENDENCIES_PATH), "The engine_dependencies.xml does not exist."
- surfacetypes_in_dependencies = False
- surfacetypes_missing_logline = False
-
- # Read engine_dependencies.xml to see if surfacetypes.xml is present
- with open(DEPENDENCIES_PATH, "r") as dependencies_file:
- for line in dependencies_file.readlines():
- if "surfacetypes.xml" in line:
- surfacetypes_in_dependencies = True
- logger.info("Surfacetypes.xml was listed in the engine_dependencies.xml file.")
- break
-
- if not surfacetypes_in_dependencies:
- logger.info("Surfacetypes.xml was not listed in the engine_dependencies.xml file.")
-
- _, output = asset_processor.batch_process(capture_output=True,
- extra_params="--dsp=%engine_dependencies.xml")
- log = APOutputParser(output)
- for _ in log.get_lines(run=-1, contains=["surfacetypes.xml", "Missing"]):
- surfacetypes_missing_logline = True
-
- assert surfacetypes_missing_logline, "Surfacetypes.xml not seen in the batch log as missing."
-
- # Add the schema file which allows our xml parser to understand dependencies for our engine_dependencies file
- asset_processor.add_relative_source_asset(os.path.join("Assets", "Engine", "Schema", "enginedependency.xmlschema"))
- asset_processor.batch_process()
-
- _, output = asset_processor.batch_process(capture_output=True,
- extra_params="--dsp=%engine_dependencies.xml")
- log = APOutputParser(output)
- surfacetypes_missing_logline = False
- for _ in log.get_lines(run=-1, contains=["surfacetypes.xml", "Missing"]):
- surfacetypes_missing_logline = True
-
- assert not surfacetypes_missing_logline, "Surfacetypes.xml not seen in the batch log as missing."
-
schemas = [
("C16877167", ".ent"),
("C16877168", "Environment.xml"),
diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp
index e4138da932..5cffedd774 100644
--- a/Code/Editor/CryEdit.cpp
+++ b/Code/Editor/CryEdit.cpp
@@ -45,6 +45,7 @@ AZ_POP_DISABLE_WARNING
// AzCore
#include
+#include
#include
#include
#include
@@ -1686,6 +1687,11 @@ bool CCryEditApp::InitInstance()
return false;
}
+ if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get())
+ {
+ AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})");
+ }
+
// Process some queued events come from system init
// Such as asset catalog loaded notification.
// There are some systems need to load configurations from assets for post initialization but before loading level
diff --git a/Code/Editor/EditorViewportWidget.cpp b/Code/Editor/EditorViewportWidget.cpp
index c7814cc842..8e6983a9d7 100644
--- a/Code/Editor/EditorViewportWidget.cpp
+++ b/Code/Editor/EditorViewportWidget.cpp
@@ -43,10 +43,11 @@
// AzToolsFramework
#include
+#include
+#include
#include
#include
#include
-#include
// AtomToolsFramework
#include
@@ -1032,6 +1033,7 @@ void EditorViewportWidget::ConnectViewportInteractionRequestBus()
AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusConnect(GetViewportId());
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusConnect(GetViewportId());
m_viewportUi.ConnectViewportUiBus(GetViewportId());
+ AzFramework::ViewportBorderRequestBus::Handler::BusConnect(GetViewportId());
AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusConnect();
}
@@ -1040,6 +1042,7 @@ void EditorViewportWidget::DisconnectViewportInteractionRequestBus()
{
AzFramework::InputSystemCursorConstraintRequestBus::Handler::BusDisconnect();
+ AzFramework::ViewportBorderRequestBus::Handler::BusDisconnect();
m_viewportUi.DisconnectViewportUiBus();
AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler::BusDisconnect();
AzToolsFramework::ViewportInteraction::MainEditorViewportInteractionRequestBus::Handler::BusDisconnect();
@@ -1124,7 +1127,9 @@ void EditorViewportWidget::OnTitleMenu(QMenu* menu)
action = menu->addAction(tr("Create camera entity from current view"));
connect(action, &QAction::triggered, this, &EditorViewportWidget::OnMenuCreateCameraEntityFromCurrentView);
- if (!gameEngine || !gameEngine->IsLevelLoaded())
+ const auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get();
+ if (!gameEngine || !gameEngine->IsLevelLoaded() ||
+ (prefabEditorEntityOwnershipInterface && !prefabEditorEntityOwnershipInterface->IsRootPrefabAssigned()))
{
action->setEnabled(false);
action->setToolTip(tr(AZ::ViewportHelpers::TextCantCreateCameraNoLevel));
@@ -2636,4 +2641,25 @@ void EditorViewportWidget::StopFullscreenPreview()
// Show the main window
MainWindow::instance()->show();
}
+
+AZStd::optional EditorViewportWidget::GetViewportBorderPadding() const
+{
+ if (auto viewportEditorModeTracker = AZ::Interface::Get())
+ {
+ auto viewportEditorModes = viewportEditorModeTracker->GetViewportEditorModes({ AzToolsFramework::GetEntityContextId() });
+ if (viewportEditorModes->IsModeActive(AzToolsFramework::ViewportEditorMode::Focus) ||
+ viewportEditorModes->IsModeActive(AzToolsFramework::ViewportEditorMode::Component))
+ {
+ AzFramework::ViewportBorderPadding viewportBorderPadding = {};
+ viewportBorderPadding.m_top = AzToolsFramework::ViewportUi::ViewportUiTopBorderSize;
+ viewportBorderPadding.m_left = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize;
+ viewportBorderPadding.m_right = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize;
+ viewportBorderPadding.m_bottom = AzToolsFramework::ViewportUi::ViewportUiLeftRightBottomBorderSize;
+ return viewportBorderPadding;
+ }
+ }
+
+ return AZStd::nullopt;
+}
+
#include
diff --git a/Code/Editor/EditorViewportWidget.h b/Code/Editor/EditorViewportWidget.h
index 68ea48c7f5..01f6068d56 100644
--- a/Code/Editor/EditorViewportWidget.h
+++ b/Code/Editor/EditorViewportWidget.h
@@ -38,6 +38,7 @@
#include
#include
+#include
// forward declarations.
class CBaseObject;
@@ -86,6 +87,7 @@ AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
class SANDBOX_API EditorViewportWidget final
: public QtViewport
+ , public AzFramework::ViewportBorderRequestBus::Handler
, private IEditorNotifyListener
, private IUndoManagerListener
, private Camera::EditorCameraRequestBus::Handler
@@ -120,6 +122,9 @@ public:
void SetFOV(float fov) override;
float GetFOV() const override;
+ // AzFramework::ViewportBorderRequestBus overrides ...
+ AZStd::optional GetViewportBorderPadding() const override;
+
private:
////////////////////////////////////////////////////////////////////////
// Private types ...
diff --git a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
index 5b849dcbe7..82d9f9ede2 100644
--- a/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
+++ b/Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -642,6 +643,9 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
AzToolsFramework::EntityIdList selected;
GetSelectedOrHighlightedEntities(selected);
+ bool prefabSystemEnabled = false;
+ AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
+
QAction* action = nullptr;
// when nothing is selected, entity is created at root level
@@ -658,18 +662,20 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con
// when a single entity is selected, entity is created as its child
else if (selected.size() == 1)
{
- action = menu->addAction(QObject::tr("Create entity"));
- QObject::connect(
- action, &QAction::triggered, action,
- [selected]
- {
- EBUS_EVENT(AzToolsFramework::EditorRequests::Bus, CreateNewEntityAsChild, selected.front());
- });
+ auto containerEntityInterface = AZ::Interface::Get();
+ if (!prefabSystemEnabled || (containerEntityInterface && containerEntityInterface->IsContainerOpen(selected.front())))
+ {
+ action = menu->addAction(QObject::tr("Create entity"));
+ QObject::connect(
+ action, &QAction::triggered, action,
+ [selected]
+ {
+ AzToolsFramework::EditorRequestBus::Broadcast(&AzToolsFramework::EditorRequestBus::Handler::CreateNewEntityAsChild, selected.front());
+ }
+ );
+ }
}
- bool prefabSystemEnabled = false;
- AzFramework::ApplicationRequests::Bus::BroadcastResult(prefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
-
if (!prefabSystemEnabled)
{
menu->addSeparator();
diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h
index f8e263d6b0..f76ea19589 100644
--- a/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h
+++ b/Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h
@@ -65,8 +65,35 @@ namespace AZ
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides - Application is a singleton
- static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
- typedef AZStd::recursive_mutex MutexType;
+ static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+ using MutexType = AZStd::recursive_mutex;
+
+ static constexpr bool EnableEventQueue = true;
+ using EventQueueMutexType = AZStd::mutex;
+ struct PostThreadDispatchInvoker
+ {
+ ~PostThreadDispatchInvoker();
+ };
+
+ template
+ struct ThreadDispatchLockGuard
+ {
+ ThreadDispatchLockGuard(DispatchMutex& contextMutex)
+ : m_lock{ contextMutex }
+ {}
+ ThreadDispatchLockGuard(DispatchMutex& contextMutex, AZStd::adopt_lock_t adopt_lock)
+ : m_lock{ contextMutex, adopt_lock }
+ {}
+ ThreadDispatchLockGuard(const ThreadDispatchLockGuard&) = delete;
+ ThreadDispatchLockGuard& operator=(const ThreadDispatchLockGuard&) = delete;
+ private:
+ PostThreadDispatchInvoker m_threadPolicyInvoker;
+ using LockType = AZStd::conditional_t, AZStd::scoped_lock>;
+ LockType m_lock;
+ };
+
+ template
+ using DispatchLockGuard = ThreadDispatchLockGuard;
//////////////////////////////////////////////////////////////////////////
virtual ~AssetCatalogRequests() = default;
@@ -200,6 +227,17 @@ namespace AZ
using AssetCatalogRequestBus = AZ::EBus;
+ inline AssetCatalogRequests::PostThreadDispatchInvoker::~PostThreadDispatchInvoker()
+ {
+ if (!AssetCatalogRequestBus::IsInDispatchThisThread())
+ {
+ if (AssetCatalogRequestBus::QueuedEventCount())
+ {
+ AssetCatalogRequestBus::ExecuteQueuedEvents();
+ }
+ }
+ }
+
/*
* Events that AssetManager listens for
*/
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
index ba66d56379..ad7e44c2ae 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
@@ -14,6 +14,7 @@
#include
#include
+#include
#include
#include
@@ -44,8 +45,6 @@
#include
#include
-#include
-#include
#include
#include
@@ -215,12 +214,6 @@ namespace AZ
// Update old Project path before attempting to merge in new Settings Registry values in order to prevent recursive calls
m_oldProjectPath = newProjectPath;
- // Merge the project.json file into settings registry under ProjectSettingsRootKey path.
- AZ::IO::FixedMaxPath projectMetadataFile{ AZ::SettingsRegistryMergeUtils::FindEngineRoot(m_registry) / newProjectPath };
- projectMetadataFile /= "project.json";
- m_registry.MergeSettingsFile(projectMetadataFile.Native(),
- AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
-
// Update all the runtime file paths based on the new "project_path" value.
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry);
}
@@ -506,6 +499,16 @@ namespace AZ
SettingsRegistryMergeUtils::MergeSettingsToRegistry_CommandLine(*m_settingsRegistry, m_commandLine, executeRegDumpCommands);
SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*m_settingsRegistry);
+ // The /O3DE/Application/LifecycleEvents array contains a valid set of lifecycle events
+ // Those lifecycle events are normally read from the /Registry
+ // which isn't merged until ComponentApplication::Create invokes MergeSettingsToRegistry
+ // So pre-populate the valid lifecycle even entries
+ ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SystemAllocatorCreated");
+ ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "SettingsRegistryAvailable");
+ ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "ConsoleAvailable");
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorCreated", R"({})");
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryAvailable", R"({})");
+
// Create the Module Manager
m_moduleManager = AZStd::make_unique();
@@ -520,6 +523,7 @@ namespace AZ
m_ownsConsole = true;
m_console->LinkDeferredFunctors(AZ::ConsoleFunctorBase::GetDeferredHead());
m_settingsRegistryConsoleFunctors = AZ::SettingsRegistryConsoleUtils::RegisterAzConsoleCommands(*m_settingsRegistry, *m_console);
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleAvailable", R"({})");
}
}
@@ -551,6 +555,7 @@ namespace AZ
{
AZ::Interface::Unregister(m_console);
delete m_console;
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})");
}
m_moduleManager.reset();
@@ -558,6 +563,8 @@ namespace AZ
if (AZ::SettingsRegistry::Get() == m_settingsRegistry.get())
{
SettingsRegistry::Unregister(m_settingsRegistry.get());
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SettingsRegistryUnavailable", R"({})");
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "SystemAllocatorPendingDestruction", R"({})");
}
m_settingsRegistry.reset();
@@ -672,6 +679,8 @@ namespace AZ
ReflectionEnvironment::GetReflectionManager()->Reflect(azrtti_typeid(this), [this](ReflectContext* context) {Reflect(context); });
RegisterCoreComponents();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerAvailable", R"({})");
+
TickBus::AllowFunctionQueuing(true);
SystemTickBus::AllowFunctionQueuing(true);
@@ -691,6 +700,7 @@ namespace AZ
// Load the actual modules
LoadModules();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsLoaded", R"({})");
// Execute user.cfg after modules have been loaded but before processing any command-line overrides
AZ::IO::FixedMaxPath platformCachePath;
@@ -756,12 +766,14 @@ namespace AZ
m_entities.rehash(0); // force free all memory
DestroyReflectionManager();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerUnavailable", R"({})");
static_cast(m_settingsRegistry.get())->ClearNotifiers();
static_cast(m_settingsRegistry.get())->ClearMergeEvents();
// Uninit and unload any dynamic modules.
m_moduleManager->UnloadModules();
+ ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})");
NameDictionary::Destroy();
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
index f2b1bb8905..6df93aff4e 100644
--- a/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
@@ -175,6 +175,8 @@ namespace AZ
bool m_loadDynamicModules = true;
//! Used by test fixtures to ensure reflection occurs to edit context.
bool m_createEditContext = false;
+ //! Indicates whether the AssetCatalog.xml should be loaded by default in Application::StartCommon
+ bool m_loadAssetCatalog = true;
};
ComponentApplication();
@@ -356,7 +358,7 @@ namespace AZ
/// Calculates the root directory of the engine.
void CalculateEngineRoot();
- /// Calculates the directory where the bootstrap.cfg file resides.
+ /// Deprecated: The term "AppRoot" has no meaning
void CalculateAppRoot();
template
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp
new file mode 100644
index 0000000000..8e15871bc0
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+
+namespace AZ::ComponentApplicationLifecycle
+{
+ bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Type = SettingsRegistryInterface::Type;
+ FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
+ eventRegistrationKey += '/';
+ eventRegistrationKey += eventName;
+ return settingsRegistry.GetType(eventRegistrationKey) == Type::Object;
+ }
+
+ bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue)
+ {
+ using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+ using Format = AZ::SettingsRegistryInterface::Format;
+
+ if (!ValidateEvent(settingsRegistry, eventName))
+ {
+ AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot signal event %.*s. Name does is not a field of object "%.*s".)"
+ R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")"
+ " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
+ return false;
+ }
+ auto eventRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
+ AZ_STRING_ARG(eventName));
+
+ return settingsRegistry.MergeSettings(eventValue, Format::JsonMergePatch, eventRegistrationKey);
+ }
+
+ bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Format = AZ::SettingsRegistryInterface::Format;
+
+ if (!ValidateEvent(settingsRegistry, eventName))
+ {
+ FixedValueString eventRegistrationKey{ ApplicationLifecycleEventRegistrationKey };
+ eventRegistrationKey += '/';
+ eventRegistrationKey += eventName;
+ return settingsRegistry.MergeSettings(R"({})", Format::JsonMergePatch, eventRegistrationKey);
+ }
+
+ return true;
+ }
+
+ bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
+ AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName, bool autoRegisterEvent)
+ {
+ using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+ using Type = AZ::SettingsRegistryInterface::Type;
+ using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler;
+
+ // Some systems may attempt to register a handler before the settings registry has been loaded
+ // If so, this flag lets them automatically register an event if it hasn't yet been registered.
+ // RegisterEvent calls validate event.
+ if ((!autoRegisterEvent && !ValidateEvent(settingsRegistry, eventName)) ||
+ (autoRegisterEvent && !RegisterEvent(settingsRegistry, eventName)))
+ {
+ AZ_Warning(
+ "ComponentApplicationLifecycle", false,
+ R"(Cannot register event %.*s. Name is not a field of object "%.*s".)"
+ R"( Please make sure the entry exists in the '/Registry/application_lifecycle_events.setreg")"
+ " or in *.setreg within the project", AZ_STRING_ARG(eventName), AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey));
+ return false;
+ }
+ auto eventNameRegistrationKey = FixedValueString::format("%.*s/%.*s", AZ_STRING_ARG(ApplicationLifecycleEventRegistrationKey),
+ AZ_STRING_ARG(eventName));
+ auto lifecycleCallback = [callback = AZStd::move(callback), eventNameRegistrationKey](AZStd::string_view path, Type type)
+ {
+ if (path == eventNameRegistrationKey)
+ {
+ callback(path, type);
+ }
+ };
+
+ handler = NotifyEventHandler(AZStd::move(lifecycleCallback));
+ settingsRegistry.RegisterNotifier(handler);
+
+ return true;
+ }
+}
diff --git a/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h
new file mode 100644
index 0000000000..6f07c0da3f
--- /dev/null
+++ b/Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace AZ::ComponentApplicationLifecycle
+{
+ //! Root Key where lifecycle events should be registered under
+ inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Application/LifecycleEvents";
+
+
+ //! Validates that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
+ //! @param settingsRegistry registry where @eventName will be searched
+ //! @param eventName name of key that validated that exists as an element in the ApplicationLifecycleEventRegistrationKey array
+ //! @return true if the @eventName was found in the ApplicationLifecycleEventRegistrationKey array
+ bool ValidateEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
+
+ //! Wrapper around setting a value underneath the ApplicationLifecycleEventRegistrationKey
+ //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array
+ //! It then appends the @eventName to the ApplicationLifecycleEventRegistrationKey merges the @eventValue into
+ //! the SettingsRegistry at that key
+ //! NOTE: This function should only be invoked from ComponentApplication and its derived classes
+ //! @param settingsRegistry registry where eventName should be set
+ //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to signal
+ //! @param eventValue JSON Object that will be merged into the SettingsRegistry at /
+ //! @return true if the eventValue was successfully merged at the /
+ bool SignalEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName, AZStd::string_view eventValue);
+
+ //! Register that the event @eventName is stored in the array at ApplicationLifecycleEventRegistrationKey
+ //! @param settingsRegistry registry where @eventName will be searched
+ //! @param eventName name of key that will be stored in the ApplicationLifecycleEventRegistrationKey array
+ //! @return true if the event passed validation or the eventName was stored in the ApplicationLifecycleEventRegistrationKey array
+ bool RegisterEvent(AZ::SettingsRegistryInterface& settingsRegistry, AZStd::string_view eventName);
+
+ //! Wrapper around registering the NotifyEventHandler with the SettingsRegistry for the specified event
+ //! It validates if the @eventName is is part of the ApplicationLifecycleEventRegistrationKey array and if
+ //! so moves the @callback into @handler and then registers the handler with the SettingsRegistry NotifyEvent
+ //! @param settingsRegistry registry where handler will be registered
+ //! @param handler handler where callback will be moved into and then registered with the SettingsRegistry
+ //! if the specified @eventName passes validation
+ //! @param callback will be moved into the handler if the specified @eventName is valid
+ //! @param eventName name of key underneath the ApplicationLifecycleEventRegistrationKey to register
+ //! @param autoRegisterEvent automatically register this event if it hasn't been registered yet. This is useful
+ //! when registering a handler before the settings registry has been loaded.
+ //! @return true if the handler was registered with the SettingsRegistry NotifyEvent
+ bool RegisterHandler(AZ::SettingsRegistryInterface& settingsRegistry, AZ::SettingsRegistryInterface::NotifyEventHandler& handler,
+ AZ::SettingsRegistryInterface::NotifyCallback callback, AZStd::string_view eventName, bool autoRegisterEvent = false);
+}
diff --git a/Code/Framework/AzCore/AzCore/Console/IConsole.h b/Code/Framework/AzCore/AzCore/Console/IConsole.h
index 73d17ac65a..aef163d22b 100644
--- a/Code/Framework/AzCore/AzCore/Console/IConsole.h
+++ b/Code/Framework/AzCore/AzCore/Console/IConsole.h
@@ -262,6 +262,6 @@ static constexpr AZ::ThreadSafety ConsoleThreadSafety<_TYPE, std::enable_if_t Functor##_FUNCTION(#_FUNCTION, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
+ inline AZ::ConsoleFunctor Functor##_FUNCTION(_NAME, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
#define AZ_CONSOLEFREEFUNC(...) AZ_MACRO_SPECIALIZE(AZ_CONSOLEFREEFUNC_, AZ_VA_NUM_ARGS(__VA_ARGS__), (__VA_ARGS__))
diff --git a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp
index 1fdeafa309..cbf3ce5243 100644
--- a/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp
+++ b/Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp
@@ -10,16 +10,13 @@
#include
#include
-#include
-#include
#include
#include
#include
-#include
#include
+#include
+#include
#include
-#include
-#include
#include
#include
@@ -221,11 +218,16 @@ namespace AZ
}
}
+ AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
+ const char* comma = "";
// For all system components, deactivate
for (auto componentIt = m_systemComponents.rbegin(); componentIt != m_systemComponents.rend(); ++componentIt)
{
ModuleEntity::DeactivateComponent(**componentIt);
+ componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, (*componentIt)->RTTI_GetTypeName());
+ comma = ", ";
}
+ componentNamesArray += R"(]})";
// For all modules that we created an entity for, set them to "Init" (meaning not Activated)
for (auto& moduleData : m_ownedModules)
@@ -239,6 +241,13 @@ namespace AZ
// Since the system components have been deactivated clear out the vector.
m_systemComponents.clear();
+
+ // Signal that the System Components have deactivated
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsDeactivated", componentNamesArray);
+ }
+
}
//=========================================================================
@@ -284,7 +293,11 @@ namespace AZ
{
// Split the tag list
AZStd::vector tagList;
- AZStd::tokenize(tags, ",", tagList);
+ auto TokenizeTags = [&tagList](AZStd::string_view token)
+ {
+ tagList.push_back(token);
+ };
+ AZ::StringFunc::TokenizeVisitor(tags, TokenizeTags, ',');
m_systemComponentTags.resize(tagList.size());
AZStd::transform(tagList.begin(), tagList.end(), m_systemComponentTags.begin(), [](const AZStd::string_view& tag)
@@ -737,11 +750,17 @@ namespace AZ
}
}
+ AZStd::string componentNamesArray = R"({ "SystemComponents":[)";
+ const char* comma = "";
// Activate the entities in the appropriate order
for (Component* component : componentsToActivate)
{
ModuleEntity::ActivateComponent(*component);
+
+ componentNamesArray += AZStd::string::format(R"(%s"%s")", comma, component->RTTI_GetTypeName());
+ comma = ", ";
}
+ componentNamesArray += R"(]})";
// Done activating; set state to active
for (auto& moduleData : modulesToInit)
@@ -755,5 +774,12 @@ namespace AZ
// Save the activated components for deactivation later
m_systemComponents.insert(m_systemComponents.end(), componentsToActivate.begin(), componentsToActivate.end());
+
+ // Signal that the System Components are activated
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "SystemComponentsActivated",
+ componentNamesArray);
+ }
}
} // namespace AZ
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
index 36f66312d8..3668ab14fd 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
@@ -29,6 +29,8 @@
namespace AZ::Internal
{
+ static constexpr const char* ProductCacheDirectoryName = "Cache";
+
AZ::SettingsRegistryInterface::FixedValueString GetEngineMonikerForProject(
SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectJsonPath)
{
@@ -228,19 +230,20 @@ namespace AZ::Internal
namespace AZ::SettingsRegistryMergeUtils
{
- constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Settings/Internal/engine_root_scan_up_path" };
- constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Settings/Internal/project_root_scan_up_path" };
-
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry)
{
+ static constexpr AZStd::string_view InternalScanUpEngineRootKey{ "/O3DE/Runtime/Internal/engine_root_scan_up_path" };
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Type = SettingsRegistryInterface::Type;
+
AZ::IO::FixedMaxPath engineRoot;
// This is the 'external' engine root key, as in passed from command-line or .setreg files.
- auto engineRootKey = SettingsRegistryInterface::FixedValueString::format("%s/engine_path", BootstrapSettingsRootKey);
+ constexpr auto engineRootKey = FixedValueString(BootstrapSettingsRootKey) + "/engine_path";
// Step 1 Run the scan upwards logic once to find the location of the engine.json if it exist
// Once this step is run the {InternalScanUpEngineRootKey} is set in the Settings Registry
// to have this scan logic only run once InternalScanUpEngineRootKey the supplied registry
- if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == SettingsRegistryInterface::Type::NoType)
+ if (settingsRegistry.GetType(InternalScanUpEngineRootKey) == Type::NoType)
{
// We can scan up from exe directory to find engine.json, use that for engine root if it exists.
engineRoot = Internal::ScanUpRootLocator("engine.json");
@@ -283,14 +286,18 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry)
{
+ static constexpr AZStd::string_view InternalScanUpProjectRootKey{ "/O3DE/Runtime/Internal/project_root_scan_up_path" };
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ using Type = SettingsRegistryInterface::Type;
+
AZ::IO::FixedMaxPath projectRoot;
- const auto projectRootKey = SettingsRegistryInterface::FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
+ constexpr auto projectRootKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
- // Step 1 Run the scan upwards logic once to find the location of the project.json if it exist
+ // Step 1 Run the scan upwards logic once to find the location of the closest ancestor project.json
// Once this step is run the {InternalScanUpProjectRootKey} is set in the Settings Registry
// to have this scan logic only run once for the supplied registry
// SettingsRegistryInterface::GetType is used to check if a key is set
- if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == SettingsRegistryInterface::Type::NoType)
+ if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == Type::NoType)
{
projectRoot = Internal::ScanUpRootLocator("project.json");
// Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry
@@ -305,19 +312,129 @@ namespace AZ::SettingsRegistryMergeUtils
}
// Step 2 Check the project-path key
- // This is the project path root key, as in passed from command-line or .setreg files.
- if (settingsRegistry.Get(projectRoot.Native(), projectRootKey))
+ // This is the project path root key, as passed from command-line or *.setreg files.
+ settingsRegistry.Get(projectRoot.Native(), projectRootKey);
+ return projectRoot;
+ }
+
+ //! The algorithm that is used to find the project cache is as follows
+ //! 1. The "{BootstrapSettingsRootKey}/project_cache_path" is checked for the path
+ //! 2. Otherwise append the ProductCacheDirectoryName constant to the
+ static AZ::IO::FixedMaxPath FindProjectCachePath(SettingsRegistryInterface& settingsRegistry, const AZ::IO::FixedMaxPath& projectPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+
+ constexpr auto projectCachePathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_cache_path";
+
+ // Step 1 Check the project-cache-path key
+ if (AZ::IO::FixedMaxPath projectCachePath; settingsRegistry.Get(projectCachePath.Native(), projectCachePathKey))
{
- return projectRoot;
+ return projectCachePath;
}
- // Step 3 Check for a "Cache" directory by scanning upwards from the executable directory
- if (auto candidateRoot = Internal::ScanUpRootLocator("Cache");
- !candidateRoot.empty() && AZ::IO::SystemFile::IsDirectory(candidateRoot.c_str()))
+ // Step 2 Append the "Cache" directory to the project-path
+ return projectPath / Internal::ProductCacheDirectoryName;
+ }
+
+ //! Set the user directory with the provided path or using /user as default
+ static AZ::IO::FixedMaxPath FindProjectUserPath(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+
+ // User: root - same as the @user@ alias, this is the starting path for transient data and log files.
+ constexpr auto projectUserPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_user_path";
+
+ // Step 1 Check the project-user-path key
+ if (AZ::IO::FixedMaxPath projectUserPath; settingsRegistry.Get(projectUserPath.Native(), projectUserPathKey))
{
- projectRoot = AZStd::move(candidateRoot);
+ return projectUserPath;
+ }
+
+ // Step 2 Append the "User" directory to the project-path
+ return projectPath / "user";
+ }
+
+ //! Set the log directory using the settings registry path or using /log as default
+ static AZ::IO::FixedMaxPath FindProjectLogPath(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectUserPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+
+ // User: root - same as the @log@ alias, this is the starting path for transient data and log files.
+ constexpr auto projectLogPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_log_path";
+
+ // Step 1 Check the project-user-path key
+ if (AZ::IO::FixedMaxPath projectLogPath; settingsRegistry.Get(projectLogPath.Native(), projectLogPathKey))
+ {
+ return projectLogPath;
+ }
+
+ // Step 2 Append the "Log" directory to the project-user-path
+ return projectUserPath / "log";
+ }
+
+ // check for a default write storage path, fall back to the if not
+ static AZ::IO::FixedMaxPath FindDevWriteStoragePath(const AZ::IO::FixedMaxPath& projectUserPath)
+ {
+ AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath();
+ return devWriteStorage.has_value() ? *devWriteStorage : projectUserPath;
+ }
+
+ // check for the project build path, which is a relative path from the project root
+ // that specifies where the build directory is located
+ static void SetProjectBuildPath(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectPath)
+ {
+ if (AZ::IO::FixedMaxPath projectBuildPath; settingsRegistry.Get(projectBuildPath.Native(), ProjectBuildPath))
+ {
+ settingsRegistry.Remove(FilePathKey_ProjectBuildPath);
+ settingsRegistry.Remove(FilePathKey_ProjectConfigurationBinPath);
+ AZ::IO::FixedMaxPath buildConfigurationPath = (projectPath / projectBuildPath).LexicallyNormal();
+ if (IO::SystemFile::Exists(buildConfigurationPath.c_str()))
+ {
+ settingsRegistry.Set(FilePathKey_ProjectBuildPath, buildConfigurationPath.Native());
+ }
+
+ // Add the specific build configuration paths to the Settings Registry
+ // First try /bin/$ and if that path doesn't exist
+ // try /bin/$/$
+ buildConfigurationPath /= "bin";
+ if (IO::SystemFile::Exists((buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
+ {
+ settingsRegistry.Set(FilePathKey_ProjectConfigurationBinPath,
+ (buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).Native());
+ }
+ else if (IO::SystemFile::Exists((buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
+ {
+ settingsRegistry.Set(FilePathKey_ProjectConfigurationBinPath,
+ (buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).Native());
+ }
+ }
+ }
+
+ // Sets the project name within the Settings Registry by looking up the "project_name"
+ // within the project.json file
+ static void SetProjectName(SettingsRegistryInterface& settingsRegistry,
+ const AZ::IO::FixedMaxPath& projectPath)
+ {
+ using FixedValueString = SettingsRegistryInterface::FixedValueString;
+ // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
+ constexpr auto projectNameKey = FixedValueString(ProjectSettingsRootKey) + "/project_name";
+
+ // Read the project name from the project.json file if it exists
+ if (AZ::IO::FixedMaxPath projectJsonPath = projectPath / "project.json";
+ AZ::IO::SystemFile::Exists(projectJsonPath.c_str()))
+ {
+ settingsRegistry.MergeSettingsFile(projectJsonPath.Native(),
+ AZ::SettingsRegistryInterface::Format::JsonMergePatch, AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey);
+ }
+ // If a project name isn't set the default will be set to the final path segment of the project path
+ if (FixedValueString projectName; !settingsRegistry.Get(projectName, projectNameKey))
+ {
+ projectName = projectPath.Filename().Native();
+ settingsRegistry.Set(projectNameKey, projectName);
}
- return projectRoot;
}
AZStd::string_view ConfigParserSettings::DefaultCommentPrefixFilter(AZStd::string_view line)
@@ -397,7 +514,7 @@ namespace AZ::SettingsRegistryMergeUtils
bool MergeSettingsToRegistry_ConfigFile(SettingsRegistryInterface& registry, AZStd::string_view filePath,
const ConfigParserSettings& configParserSettings)
{
- auto configPath = FindEngineRoot(registry) / filePath;
+ auto configPath = FindProjectRoot(registry) / filePath;
IO::FileReader configFile;
bool configFileOpened{};
switch (configParserSettings.m_fileReaderClass)
@@ -542,19 +659,77 @@ namespace AZ::SettingsRegistryMergeUtils
void MergeSettingsToRegistry_AddRuntimeFilePaths(SettingsRegistryInterface& registry)
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
- // Binary folder
- AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
- registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
- // Engine root folder - corresponds to the @engroot@ and @engroot@ aliases
+ // Binary folder - corresponds to the @exefolder@ alias
+ AZ::IO::FixedMaxPath exePath = AZ::Utils::GetExecutableDirectory();
+ registry.Set(FilePathKey_BinaryFolder, exePath.LexicallyNormal().Native());
+
+ // Project path - corresponds to the @projectroot@ alias
+ // NOTE: We make the project-path in the BootstrapSettingsRootKey absolute first
+
+ AZ::IO::FixedMaxPath projectPath = FindProjectRoot(registry);
+ if (constexpr auto projectPathKey = FixedValueString(BootstrapSettingsRootKey) + "/project_path";
+ !projectPath.empty())
+ {
+ if (projectPath.IsRelative())
+ {
+ if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectPath.Native());
+ projectAbsPath.has_value())
+ {
+ projectPath = AZStd::move(*projectAbsPath);
+ }
+ }
+
+ projectPath = projectPath.LexicallyNormal();
+ AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(projectPath.c_str()),
+ R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to a valid absolute path?)"
+ , projectPath.c_str(), AZ_STRING_ARG(projectPathKey));
+
+ registry.Set(FilePathKey_ProjectPath, projectPath.Native());
+ }
+ else
+ {
+ AZ_TracePrintf("SettingsRegistryMergeUtils",
+ R"(Project path isn't set in the Settings Registry at "%.*s".)"
+ " Project-related filepaths will be set relative to the executable directory\n",
+ AZ_STRING_ARG(projectPathKey));
+ registry.Set(FilePathKey_ProjectPath, exePath.Native());
+ }
+
+ // Engine root folder - corresponds to the @engroot@ alias
AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
- registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
+ if (!engineRoot.empty())
+ {
+ if (engineRoot.IsRelative())
+ {
+ if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native());
+ engineRootAbsPath.has_value())
+ {
+ engineRoot = AZStd::move(*engineRootAbsPath);
+ }
+ }
+
+ engineRoot = engineRoot.LexicallyNormal();
+ registry.Set(FilePathKey_EngineRootFolder, engineRoot.Native());
+ }
- auto projectPathKey = FixedValueString::format("%s/project_path", BootstrapSettingsRootKey);
- SettingsRegistryInterface::FixedValueString projectPathValue;
- if (registry.Get(projectPathValue, projectPathKey))
+ // Cache folder
+ AZ::IO::FixedMaxPath projectCachePath = FindProjectCachePath(registry, projectPath).LexicallyNormal();
+ if (!projectCachePath.empty())
{
- // Cache folder
+ if (projectCachePath.IsRelative())
+ {
+ if (auto projectCacheAbsPath = AZ::Utils::ConvertToAbsolutePath(projectCachePath.Native());
+ projectCacheAbsPath.has_value())
+ {
+ projectCachePath = AZStd::move(*projectCacheAbsPath);
+ }
+ }
+
+ projectCachePath = projectCachePath.LexicallyNormal();
+ registry.Set(FilePathKey_CacheProjectRootFolder, projectCachePath.Native());
+
+ // Cache/ folder
// Get the name of the asset platform assigned by the bootstrap. First check for platform version such as "windows_assets"
// and if that's missing just get "assets".
FixedValueString assetPlatform;
@@ -570,118 +745,67 @@ namespace AZ::SettingsRegistryMergeUtils
assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
}
- // Project path - corresponds to the @projectroot@ alias
- // NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded.
- path = engineRoot / projectPathValue;
-
- AZ_Warning("SettingsRegistryMergeUtils", AZ::IO::SystemFile::Exists(path.c_str()),
- R"(Project path "%s" does not exist. Is the "%.*s" registry setting set to valid absolute path?)"
- , path.c_str(), aznumeric_cast(projectPathKey.size()), projectPathKey.data());
-
- AZ::IO::FixedMaxPath normalizedProjectPath = path.LexicallyNormal();
- registry.Set(FilePathKey_ProjectPath, normalizedProjectPath.Native());
-
- // Set the user directory with the provided path or using project/user as default
- auto projectUserPathKey = FixedValueString::format("%s/project_user_path", BootstrapSettingsRootKey);
- AZ::IO::FixedMaxPath projectUserPath;
- if (!registry.Get(projectUserPath.Native(), projectUserPathKey))
+ // Make sure the asset platform is set before setting cache path for the asset platform.
+ if (!assetPlatform.empty())
{
- projectUserPath = (normalizedProjectPath / "user").LexicallyNormal();
+ registry.Set(FilePathKey_CacheRootFolder, (projectCachePath / assetPlatform).Native());
}
- registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
+ }
- // Set the log directory with the provided path or using project/user/log as default
- auto projectLogPathKey = FixedValueString::format("%s/project_log_path", BootstrapSettingsRootKey);
- AZ::IO::FixedMaxPath projectLogPath;
- if (!registry.Get(projectLogPath.Native(), projectLogPathKey))
+ // User folder
+ AZ::IO::FixedMaxPath projectUserPath = FindProjectUserPath(registry, projectPath);
+ if (!projectUserPath.empty())
+ {
+ if (projectUserPath.IsRelative())
{
- projectLogPath = (projectUserPath / "log").LexicallyNormal();
+ if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native());
+ projectUserAbsPath.has_value())
+ {
+ projectUserPath = AZStd::move(*projectUserAbsPath);
+ }
}
- registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native());
- // check for a default write storage path, fall back to the project's user/ directory if not
- AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath();
- registry.Set(FilePathKey_DevWriteStorage, devWriteStorage.has_value()
- ? devWriteStorage.value()
- : projectUserPath.Native());
+ projectUserPath = projectUserPath.LexicallyNormal();
+ registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native());
+ }
- // Set the project in-memory build path if the ProjectBuildPath key has been supplied
- if (AZ::IO::FixedMaxPath projectBuildPath; registry.Get(projectBuildPath.Native(), ProjectBuildPath))
+ // Log folder
+ if (AZ::IO::FixedMaxPath projectLogPath = FindProjectLogPath(registry, projectUserPath); !projectLogPath.empty())
+ {
+ if (projectLogPath.IsRelative())
{
- registry.Remove(FilePathKey_ProjectBuildPath);
- registry.Remove(FilePathKey_ProjectConfigurationBinPath);
- AZ::IO::FixedMaxPath buildConfigurationPath = normalizedProjectPath / projectBuildPath;
- if (IO::SystemFile::Exists(buildConfigurationPath.c_str()))
- {
- registry.Set(FilePathKey_ProjectBuildPath, buildConfigurationPath.LexicallyNormal().Native());
- }
-
- // Add the specific build configuration paths to the Settings Registry
- // First try /bin/$ and if that path doesn't exist
- // try /bin/$/$
- buildConfigurationPath /= "bin";
- if (IO::SystemFile::Exists((buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
- {
- registry.Set(FilePathKey_ProjectConfigurationBinPath,
- (buildConfigurationPath / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native());
- }
- else if (IO::SystemFile::Exists((buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).c_str()))
+ if (auto projectLogAbsPath = AZ::Utils::ConvertToAbsolutePath(projectLogPath.Native()))
{
- registry.Set(FilePathKey_ProjectConfigurationBinPath,
- (buildConfigurationPath / AZ_TRAIT_OS_PLATFORM_CODENAME / AZ_BUILD_CONFIGURATION_TYPE).LexicallyNormal().Native());
+ projectLogPath = AZStd::move(*projectLogAbsPath);
}
-
}
- // Project name - if it was set via merging project.json use that value, otherwise use the project path's folder name.
- auto projectNameKey =
- AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::ProjectSettingsRootKey)
- + "/project_name";
-
- AZ::SettingsRegistryInterface::FixedValueString projectName;
- if (!registry.Get(projectName, projectNameKey))
- {
- projectName = path.Filename().Native();
- registry.Set(projectNameKey, projectName);
- }
+ projectLogPath = projectLogPath.LexicallyNormal();
+ registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native());
+ }
- // Cache folders - sets up various paths in registry for the cache.
- // Make sure the asset platform is set before setting these cache paths.
- if (!assetPlatform.empty())
+ // Developer Write Storage folder
+ if (AZ::IO::FixedMaxPath devWriteStoragePath = FindDevWriteStoragePath(projectUserPath); !devWriteStoragePath.empty())
+ {
+ if (devWriteStoragePath.IsRelative())
{
- // Cache: project root - no corresponding fileIO alias, but this is where the asset database lives.
- // A registry override is accepted using the "project_cache_path" key.
- auto projectCacheRootOverrideKey = FixedValueString::format("%s/project_cache_path", BootstrapSettingsRootKey);
- // Clear path to make sure that the `project_cache_path` value isn't concatenated to the project path
- path.clear();
- if (registry.Get(path.Native(), projectCacheRootOverrideKey))
- {
- registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
- path /= assetPlatform;
- registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
- }
- else
+ if (auto devWriteStorageAbsPath = AZ::Utils::ConvertToAbsolutePath(devWriteStoragePath.Native()))
{
- // Cache: root - same as the @products@ alias, this is the starting path for cache files.
- path = normalizedProjectPath / "Cache";
- registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
- path /= assetPlatform;
- registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
+ devWriteStoragePath = AZStd::move(*devWriteStorageAbsPath);
}
}
- }
- else
- {
- // Set the default ProjectUserPath to the /user directory
- registry.Set(FilePathKey_ProjectUserPath, (engineRoot / "user").LexicallyNormal().Native());
- AZ_TracePrintf("SettingsRegistryMergeUtils",
- R"(Project path isn't set in the Settings Registry at "%.*s". Project-related filepaths will not be set)" "\n",
- aznumeric_cast(projectPathKey.size()), projectPathKey.data());
+
+ devWriteStoragePath = devWriteStoragePath.LexicallyNormal();
+ registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native());
}
+ // Set the project in-memory build path if the ProjectBuildPath key has been supplied
+ SetProjectBuildPath(registry, projectPath);
+ // Set the project name using the "project_name" key
+ SetProjectName(registry, projectPath);
+
#if !AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
// Setup the cache, user, and log paths to platform specific locations when running on non-host platforms
- path = engineRoot;
if (AZStd::optional nonHostCacheRoot = Utils::GetDefaultAppRootPath();
nonHostCacheRoot)
{
@@ -690,25 +814,25 @@ namespace AZ::SettingsRegistryMergeUtils
}
else
{
- registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
- registry.Set(FilePathKey_CacheRootFolder, path.LexicallyNormal().Native());
+ registry.Set(FilePathKey_CacheProjectRootFolder, projectPath.Native());
+ registry.Set(FilePathKey_CacheRootFolder, projectPath.Native());
}
if (AZStd::optional devWriteStorage = Utils::GetDevWriteStoragePath();
devWriteStorage)
{
- const AZ::IO::FixedMaxPath devWriteStoragePath(*devWriteStorage);
- registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectUserPath, (devWriteStoragePath / "user").LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectLogPath, (devWriteStoragePath / "user/log").LexicallyNormal().Native());
+ const auto devWriteStoragePath = AZ::IO::PathView(*devWriteStorage).LexicallyNormal();
+ registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native());
+ registry.Set(FilePathKey_ProjectUserPath, (devWriteStoragePath / "user").Native());
+ registry.Set(FilePathKey_ProjectLogPath, (devWriteStoragePath / "user" / "log").Native());
}
else
{
- registry.Set(FilePathKey_DevWriteStorage, path.LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectUserPath, (path / "user").LexicallyNormal().Native());
- registry.Set(FilePathKey_ProjectLogPath, (path / "user/log").LexicallyNormal().Native());
- }
-#endif // AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
+ registry.Set(FilePathKey_DevWriteStorage, projectPath.Native());
+ registry.Set(FilePathKey_ProjectUserPath, (projectPath / "user").Native());
+ registry.Set(FilePathKey_ProjectLogPath, (projectPath / "user" / "log").Native());
}
+#endif // AZ_TRAIT_OS_IS_HOST_OS_PLATFORM
+}
void MergeSettingsToRegistry_TargetBuildDependencyRegistry(SettingsRegistryInterface& registry, const AZStd::string_view platform,
const SettingsRegistryInterface::Specializations& specializations, AZStd::vector* scratchBuffer)
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
index daa64c0343..56eec91813 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h
@@ -87,9 +87,9 @@ namespace AZ::SettingsRegistryMergeUtils
AZ::IO::FixedMaxPath FindEngineRoot(SettingsRegistryInterface& settingsRegistry);
//! The algorithm that is used to find the project root is as follows
- //! 1. The first time this function is it performs a upward scan for a project.json file from
- //! the executable directory and if found stores that path to an internal key.
- //! In the same step it injects the path into the front of list of command line parameters
+ //! 1. The first time this function runs it performs an upward scan for a "project.json" file from
+ //! the executable directory and stores that path into an internal key.
+ //! In the same step it injects the path into the back of the command line parameters
//! using the --regset="{BootstrapSettingsRootKey}/project_path=" value
//! 2. Next the "{BootstrapSettingsRootKey}/project_path" is checked to see if it has a project path set
//!
diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp
index c32591874b..5cd36785dc 100644
--- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp
+++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp
@@ -15,20 +15,20 @@
namespace AZ::SettingsRegistryScriptUtils::Internal
{
- static void RegisterScriptProxyForNotify(SettingsRegistryScriptProxy& settingsRegistryProxy)
+ static void RegisterScriptProxyForNotify(SettingsRegistryInterface* settingsRegistry,
+ SettingsRegistryScriptProxy::NotifyEventProxy* notifyEventProxy)
{
- if (settingsRegistryProxy.IsValid())
+ if (settingsRegistry != nullptr)
{
- auto ForwardSettingsUpdateToProxyEvent = [&settingsRegistryProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
+ auto ForwardSettingsUpdateToProxyEvent = [notifyEventProxy](AZStd::string_view path, AZ::SettingsRegistryInterface::Type)
{
- if (settingsRegistryProxy.m_notifyEventProxy)
+ if (notifyEventProxy)
{
- settingsRegistryProxy.m_notifyEventProxy->m_scriptNotifyEvent.Signal(path);
+ notifyEventProxy->m_scriptNotifyEvent.Signal(path);
}
};
// Register the forwarding function with the BehaviorContext
- settingsRegistryProxy.m_notifyEventProxy->m_settingsUpdatedHandler =
- settingsRegistryProxy.m_settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
+ notifyEventProxy->m_settingsUpdatedHandler = settingsRegistry->RegisterNotifier(ForwardSettingsUpdateToProxyEvent);
}
}
@@ -37,7 +37,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
: m_settingsRegistry(AZStd::move(settingsRegistry))
, m_notifyEventProxy(AZStd::make_shared())
{
- RegisterScriptProxyForNotify(*this);
+ RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
}
// Raw AZ::SettingsRegistryInterface pointer is not owned by the proxy, so it's deleter is a no-op
@@ -45,7 +45,7 @@ namespace AZ::SettingsRegistryScriptUtils::Internal
: m_settingsRegistry(settingsRegistry, [](AZ::SettingsRegistryInterface*) {})
, m_notifyEventProxy(AZStd::make_shared())
{
- RegisterScriptProxyForNotify(*this);
+ RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
}
// SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid
diff --git a/Code/Framework/AzCore/AzCore/azcore_files.cmake b/Code/Framework/AzCore/AzCore/azcore_files.cmake
index 41229429f2..0c5a360844 100644
--- a/Code/Framework/AzCore/AzCore/azcore_files.cmake
+++ b/Code/Framework/AzCore/AzCore/azcore_files.cmake
@@ -41,6 +41,8 @@ set(FILES
Component/ComponentApplication.cpp
Component/ComponentApplication.h
Component/ComponentApplicationBus.h
+ Component/ComponentApplicationLifecycle.cpp
+ Component/ComponentApplicationLifecycle.h
Component/ComponentBus.cpp
Component/ComponentBus.h
Component/ComponentExport.h
diff --git a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
index f8d0ea8bb1..323e834413 100644
--- a/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Application/Application.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -120,6 +121,11 @@ namespace AzFramework
m_archiveFileIO = AZStd::make_unique(m_archive.get());
AZ::IO::FileIOBase::SetInstance(m_archiveFileIO.get());
SetFileIOAliases();
+ // The FileIOAvailable event needs to be registered here as this event is sent out
+ // before the settings registry has merged the .setreg files from the
+ // (That happens in MergeSettingsToRegistry
+ AZ::ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "FileIOAvailable");
+ AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOAvailable", R"({})");
}
if (auto nativeUI = AZ::Interface::Get(); nativeUI == nullptr)
@@ -172,6 +178,8 @@ namespace AzFramework
// Archive classes relies on the FileIOBase DirectInstance to close
// files properly
m_directFileIO.reset();
+
+ AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOUnavailable", R"({})");
}
void Application::Start(const Descriptor& descriptor, const StartupParameters& startupParameters)
@@ -196,7 +204,24 @@ namespace AzFramework
systemEntity->Activate();
AZ_Assert(systemEntity->GetState() == AZ::Entity::State::Active, "System Entity failed to activate.");
- m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active);
+ if (m_isStarted = (systemEntity->GetState() == AZ::Entity::State::Active); m_isStarted)
+ {
+ if (m_startupParameters.m_loadAssetCatalog)
+ {
+ // Start Monitoring Asset changes over the network and load the AssetCatalog
+ auto StartMonitoringAssetsAndLoadCatalog = [this](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
+ {
+ if (AZ::IO::FixedMaxPath assetCatalogPath;
+ m_settingsRegistry->Get(assetCatalogPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
+ {
+ assetCatalogPath /= "assetcatalog.xml";
+ assetCatalogRequests->LoadCatalog(assetCatalogPath.c_str());
+ }
+ };
+ using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
+ AssetCatalogBus::Broadcast(AZStd::move(StartMonitoringAssetsAndLoadCatalog));
+ }
+ }
}
void Application::PreModuleLoad()
@@ -210,6 +235,17 @@ namespace AzFramework
{
if (m_isStarted)
{
+ if (m_startupParameters.m_loadAssetCatalog)
+ {
+ // Stop Monitoring Assets changes
+ auto StopMonitoringAssets = [](AZ::Data::AssetCatalogRequests* assetCatalogRequests)
+ {
+ assetCatalogRequests->StopMonitoringAssets();
+ };
+ using AssetCatalogBus = AZ::Data::AssetCatalogRequestBus;
+ AssetCatalogBus::Broadcast(AZStd::move(StopMonitoringAssets));
+ }
+
ApplicationLifecycleEvents::Bus::Broadcast(&ApplicationLifecycleEvents::OnApplicationAboutToStop);
m_pimpl.reset();
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
index cd30b753b5..c1c5775958 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
@@ -12,6 +12,7 @@
#include
#include
+#include
#include
#include
#include
@@ -363,6 +364,23 @@ namespace AZ::IO
, m_mainThreadId{ AZStd::this_thread::get_id() }
{
CompressionBus::Handler::BusConnect();
+
+ // If the settings registry is not available at this point,
+ // then something catastrophic has happened in the application startup.
+ // That should have been caught and messaged out earlier in startup.
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ // Automatically register the event if it's not registered, because
+ // this system is initialized before the settings registry has loaded the event list.
+ AZ::ComponentApplicationLifecycle::RegisterHandler(
+ *settingsRegistry, m_componentApplicationLifecycleHandler,
+ [this](AZStd::string_view /*path*/, AZ::SettingsRegistryInterface::Type /*type*/)
+ {
+ OnSystemEntityActivated();
+ },
+ "SystemComponentsActivated",
+ /*autoRegisterEvent*/ true);
+ }
}
//////////////////////////////////////////////////////////////////////////
@@ -1175,13 +1193,20 @@ namespace AZ::IO
}
}
- auto bundleManifest = GetBundleManifest(desc.pZip);
AZStd::shared_ptr bundleCatalog;
+ auto bundleManifest = GetBundleManifest(desc.pZip);
if (bundleManifest)
{
bundleCatalog = GetBundleCatalog(desc.pZip, bundleManifest->GetCatalogName());
}
+ // If this archive is loaded before the serialize context is available, then the manifest and catalog will need to be loaded later.
+ if (!bundleManifest || !bundleCatalog)
+ {
+ m_archivesWithCatalogsToLoad.push_back(
+ ArchivesWithCatalogsToLoad(szFullPath, szBindRoot, flags, nextBundle, desc.m_strFileName));
+ }
+
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
@@ -1219,12 +1244,17 @@ namespace AZ::IO
m_levelOpenEvent.Signal(levelDirs);
}
- AZ::IO::ArchiveNotificationBus::Broadcast([](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName,
- AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle, AZStd::shared_ptr bundleCatalog)
+ if (bundleManifest && bundleCatalog)
{
- archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog);
- }, desc.m_strFileName.c_str(), bundleManifest, nextBundle, bundleCatalog);
-
+ AZ::IO::ArchiveNotificationBus::Broadcast(
+ [](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName,
+ AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle,
+ AZStd::shared_ptr bundleCatalog)
+ {
+ archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog);
+ },
+ desc.m_strFileName.c_str(), bundleManifest, nextBundle, bundleCatalog);
+ }
return true;
}
@@ -2138,7 +2168,7 @@ namespace AZ::IO
}
currentDirPattern = currentDir + AZ_FILESYSTEM_SEPARATOR_WILDCARD;
- currentFilePattern = currentDir + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "levels.pak";
+ currentFilePattern = currentDir + AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING + "level.pak";
ZipDir::FileEntry* fileEntry = findFile.FindExact(currentFilePattern.c_str());
if (fileEntry)
@@ -2175,4 +2205,36 @@ namespace AZ::IO
return catalogInfo;
}
+
+ void Archive::OnSystemEntityActivated()
+ {
+ for (const auto& archiveInfo : m_archivesWithCatalogsToLoad)
+ {
+ AZStd::intrusive_ptr archive =
+ OpenArchive(archiveInfo.m_fullPath, archiveInfo.m_bindRoot, archiveInfo.m_flags, nullptr);
+ if (!archive)
+ {
+ continue;
+ }
+
+ ZipDir::CachePtr pZip = static_cast(archive.get())->GetCache();
+
+ AZStd::shared_ptr bundleCatalog;
+ auto bundleManifest = GetBundleManifest(pZip);
+ if (bundleManifest)
+ {
+ bundleCatalog = GetBundleCatalog(pZip, bundleManifest->GetCatalogName());
+ }
+
+ AZ::IO::ArchiveNotificationBus::Broadcast(
+ [](AZ::IO::ArchiveNotifications* archiveNotifications, const char* bundleName,
+ AZStd::shared_ptr bundleManifest, const AZ::IO::FixedMaxPath& nextBundle,
+ AZStd::shared_ptr bundleCatalog)
+ {
+ archiveNotifications->BundleOpened(bundleName, bundleManifest, nextBundle.c_str(), bundleCatalog);
+ },
+ archiveInfo.m_strFileName.c_str(), bundleManifest, archiveInfo.m_nextBundle, bundleCatalog);
+ }
+ m_archivesWithCatalogsToLoad.clear();
+ }
}
diff --git a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h
index f08d90a66e..279702b433 100644
--- a/Code/Framework/AzFramework/AzFramework/Archive/Archive.h
+++ b/Code/Framework/AzFramework/AzFramework/Archive/Archive.h
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -271,6 +272,11 @@ namespace AZ::IO
ZipDir::CachePtr* pZip = {}) const;
private:
+ // Archives can't be fully mounted until the system entity has been activated,
+ // because mounting them requires the BundlingSystemComponent and the serialization system
+ // to both be available.
+ void OnSystemEntityActivated();
+
bool OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view pName, AZStd::intrusive_ptr pData = nullptr, bool addLevels = true);
bool OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, AZStd::vector* pFullPaths = nullptr, bool addLevels = true);
@@ -313,6 +319,8 @@ namespace AZ::IO
mutable AZStd::shared_mutex m_csZips;
ZipArray m_arrZips;
+ AZ::SettingsRegistryInterface::NotifyEventHandler m_componentApplicationLifecycleHandler;
+
//////////////////////////////////////////////////////////////////////////
// Opened files collector.
//////////////////////////////////////////////////////////////////////////
@@ -339,5 +347,34 @@ namespace AZ::IO
// [LYN-2376] Remove once legacy slice support is removed
LevelPackOpenEvent m_levelOpenEvent;
LevelPackCloseEvent m_levelCloseEvent;
+
+ // If pak files are loaded before the serialization and bundling system
+ // are ready to go, their asset catalogs can't be loaded.
+ // In this case, cache information about those archives,
+ // and attempt to load the catalogs later, when the required systems are enabled.
+ struct ArchivesWithCatalogsToLoad
+ {
+ ArchivesWithCatalogsToLoad(
+ AZStd::string_view fullPath,
+ AZStd::string_view bindRoot,
+ int flags,
+ AZ::IO::PathView nextBundle,
+ AZ::IO::Path strFileName)
+ : m_fullPath(fullPath)
+ , m_bindRoot(bindRoot)
+ , m_flags(flags)
+ , m_nextBundle(nextBundle)
+ , m_strFileName(strFileName)
+ {
+ }
+
+ AZ::IO::Path m_strFileName;
+ AZStd::string m_fullPath;
+ AZStd::string m_bindRoot;
+ AZ::IO::PathView m_nextBundle;
+ int m_flags;
+ };
+
+ AZStd::vector m_archivesWithCatalogsToLoad;
};
}
diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
index e6b8211c28..3d76bcefc4 100644
--- a/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
@@ -565,7 +565,7 @@ namespace AzFramework
if (!bytes.empty())
{
- AZStd::shared_ptr < AzFramework::AssetRegistry> prevRegistry;
+ AZStd::shared_ptr prevRegistry;
if (!m_initialized)
{
// First time initialization may have updates already processed which we want to apply
@@ -589,7 +589,6 @@ namespace AzFramework
AZ_TracePrintf("AssetCatalog", "Loaded registry containing %u assets.\n", m_registry->m_assetIdToInfo.size());
// It's currently possible in tools for us to have received updates from AP which were applied before the catalog was ready to load
- // due to CryPak and CrySystem coming online later than our components
if (!m_initialized)
{
ApplyDeltaCatalog(prevRegistry);
@@ -611,12 +610,13 @@ namespace AzFramework
// the mutex. If the listener tries to perform a blocking asset load via GetAsset() / BlockUntilLoadComplete(), the spawned asset
// thread will make a call to the AssetCatalogRequestBus and block on the held mutex. This would cause a deadlock, since the listener
// won't free the mutex until the load is complete.
- // So instead, queue the notification until the next tick, so that it doesn't occur within the AssetCatalogRequestBus mutex, and also
+ // So instead, queue the notification until after the AssetCatalogRequestBus mutex is unlocked for the current thread, and also
// so that the entire AssetCatalog initialization is complete.
- AZ::TickBus::QueueFunction([catalogRegistryString = AZStd::string(catalogRegistryFile)]()
- {
- AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
- });
+ auto OnCatalogLoaded = [catalogRegistryString = AZStd::string(catalogRegistryFile)]()
+ {
+ AssetCatalogEventBus::Broadcast(&AssetCatalogEventBus::Events::OnCatalogLoaded, catalogRegistryString.c_str());
+ };
+ AZ::Data::AssetCatalogRequestBus::QueueFunction(AZStd::move(OnCatalogLoaded));
}
}
@@ -978,6 +978,7 @@ namespace AzFramework
AZStd::lock_guard lock(m_registryMutex);
m_registry->Clear();
+ m_initialized = false;
}
diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
index f9eefa7639..26a22f4625 100644
--- a/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
+++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
@@ -61,6 +61,7 @@ namespace AzFramework
//=========================================================================
void AssetRegistry::Clear()
{
+ m_assetDependencies = {};
m_assetIdToInfo = AssetIdToInfoMap();
m_assetPathToId = AssetPathToIdMap();
}
diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp
index f820ee56ed..abbb9ec2f1 100644
--- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp
+++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp
@@ -10,11 +10,11 @@
#include
#include
#include
+#include
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -89,19 +89,19 @@ namespace AzFramework
bool FileTagManager::Save(FileTagType fileTagType, const AZStd::string& destinationFilePath = AZStd::string())
{
AzFramework::FileTag::FileTagAsset* fileTagAsset = GetFileTagAsset(fileTagType);
- AZStd::string filePathToSave = destinationFilePath;
+ AZ::IO::Path filePathToSave = destinationFilePath;
if (filePathToSave.empty())
{
filePathToSave = FileTagQueryManager::GetDefaultFileTagFilePath(fileTagType);
}
- if (!AzFramework::StringFunc::EndsWith(filePathToSave, AzFramework::FileTag::FileTagAsset::Extension()))
+ if (!filePathToSave.Extension().Native().ends_with(AzFramework::FileTag::FileTagAsset::Extension()))
{
AZ_Error("FileTag", false, "Unable to save tag file (%s). Invalid file extension, file tag can only have (%s) extension.\n", filePathToSave.c_str(), AzFramework::FileTag::FileTagAsset::Extension());
return false;
}
- return AZ::Utils::SaveObjectToFile(filePathToSave, AZ::DataStream::StreamType::ST_XML, fileTagAsset);
+ return AZ::Utils::SaveObjectToFile(filePathToSave.Native(), AZ::DataStream::StreamType::ST_XML, fileTagAsset);
}
AZ::Outcome FileTagManager::AddTagsInternal(AZStd::string filePath, FileTagType fileTagType, AZStd::vector fileTags, AzFramework::FileTag::FilePatternType filePatternType)
@@ -239,17 +239,22 @@ namespace AzFramework
QueryFileTagsEventBus::Handler::BusDisconnect();
}
- AZStd::string FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
+ AZ::IO::Path FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType fileTagType)
{
- auto destinationFilePath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / EngineAssetSourceRelPath;
+ AZ::IO::Path destinationFilePath;
+ if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+ {
+ settingsRegistry->Get(destinationFilePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
+ }
+ destinationFilePath /= EngineAssetSourceRelPath;
destinationFilePath /= fileTagType == FileTagType::Exclude ? ExcludeFileName : IncludeFileName;
destinationFilePath.ReplaceExtension(AzFramework::FileTag::FileTagAsset::Extension());
- return destinationFilePath.String();
+ return destinationFilePath;
}
bool FileTagQueryManager::Load(const AZStd::string& filePath)
{
- AZStd::string fileToLoad = filePath;
+ AZ::IO::Path fileToLoad = filePath;
if (fileToLoad.empty())
{
fileToLoad = GetDefaultFileTagFilePath(m_fileTagType);
diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h
index be3d6e6d08..d2dee3f629 100644
--- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h
+++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
namespace AzFramework
@@ -88,7 +89,7 @@ namespace AzFramework
/////////////////////////////////////////////////////////////////////////
- static AZStd::string GetDefaultFileTagFilePath(FileTagType fileTagType);
+ static AZ::IO::Path GetDefaultFileTagFilePath(FileTagType fileTagType);
protected:
diff --git a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp
index 1e03a6df0d..1d09500415 100644
--- a/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp
+++ b/Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp
@@ -16,6 +16,7 @@
#include
#include
+#include
#include
namespace AzFramework
@@ -66,7 +67,8 @@ namespace AzFramework
m_excludeFileQueryManager.reset(aznew FileTagQueryManager(FileTagType::Exclude));
if (!m_excludeFileQueryManager.get()->Load())
{
- AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n", FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
+ AZ_Error("FileTagQueryComponent", false, "Not able to load default exclude file (%s). Please make sure that it exists on disk.\n",
+ FileTagQueryManager::GetDefaultFileTagFilePath(FileTagType::Exclude).c_str());
}
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h
index 444173f773..131ecc0c57 100644
--- a/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h
+++ b/Code/Framework/AzFramework/AzFramework/Viewport/ViewportBus.h
@@ -8,8 +8,9 @@
#pragma once
-#include
#include
+#include
+#include
namespace AZ
{
@@ -20,18 +21,15 @@ namespace AZ
namespace AzFramework
{
- class ViewportRequests
- : public AZ::EBusTraits
+ class ViewportRequests : public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
- static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = ViewportId;
static void Reflect(AZ::ReflectContext* context);
- virtual ~ViewportRequests() {}
-
//! Gets the current camera's world to view matrix.
virtual const AZ::Matrix4x4& GetCameraViewMatrix() const = 0;
//! Sets the current camera's world to view matrix.
@@ -44,8 +42,36 @@ namespace AzFramework
virtual AZ::Transform GetCameraTransform() const = 0;
//! Convenience method, sets the camera's world to view matrix from this AZ::Transform.
virtual void SetCameraTransform(const AZ::Transform& transform) = 0;
+
+ protected:
+ ~ViewportRequests() = default;
};
using ViewportRequestBus = AZ::EBus;
-} //namespace AzFramework
+ //! The additional padding around the viewport when a viewport border is active.
+ struct ViewportBorderPadding
+ {
+ float m_top;
+ float m_bottom;
+ float m_left;
+ float m_right;
+ };
+
+ //! For performing queries about the state of the viewport border.
+ class ViewportBorderRequests : public AZ::EBusTraits
+ {
+ public:
+ static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+ using BusIdType = ViewportId;
+
+ //! Returns if a viewport border is in effect and what the current dimensions (padding) of the border are.
+ virtual AZStd::optional GetViewportBorderPadding() const = 0;
+
+ protected:
+ ~ViewportBorderRequests() = default;
+ };
+
+ using ViewportBorderRequestBus = AZ::EBus;
+} // namespace AzFramework
diff --git a/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp b/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp
index 6cde5b5e84..aa1a27f9b0 100644
--- a/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp
+++ b/Code/Framework/AzFramework/Tests/ArchiveCompressionTests.cpp
@@ -41,7 +41,9 @@ namespace UnitTest
auto projectPathKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
- registry->Set(projectPathKey, "AutomatedTesting");
+ AZ::IO::FixedMaxPath enginePath;
+ registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
+ registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
m_application->Start({});
diff --git a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp
index 37babb49a8..ff0e3ab724 100644
--- a/Code/Framework/AzFramework/Tests/ArchiveTests.cpp
+++ b/Code/Framework/AzFramework/Tests/ArchiveTests.cpp
@@ -45,7 +45,9 @@ namespace UnitTest
auto projectPathKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
- registry->Set(projectPathKey, "AutomatedTesting");
+ AZ::IO::FixedMaxPath enginePath;
+ registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
+ registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
m_application->Start({});
diff --git a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp
index 8cdc54f34f..8a24d164cd 100644
--- a/Code/Framework/AzFramework/Tests/AssetCatalog.cpp
+++ b/Code/Framework/AzFramework/Tests/AssetCatalog.cpp
@@ -305,10 +305,14 @@ namespace UnitTest
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
auto projectPathKey =
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
- registry->Set(projectPathKey, "AutomatedTesting");
+ AZ::IO::FixedMaxPath enginePath;
+ registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
+ registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
- m_app->Start(desc);
+ AZ::ComponentApplication::StartupParameters startupParameters;
+ startupParameters.m_loadAssetCatalog = false;
+ m_app->Start(desc, startupParameters);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
diff --git a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp
index 0cce93d751..36acf2b063 100644
--- a/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp
+++ b/Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp
@@ -45,6 +45,13 @@ namespace AzGameFramework
enginePakPath = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / "engine.pak";
m_archive->OpenPack("@products@", enginePakPath.Native());
}
+
+ // By default, load all archives in the products folder.
+ // If you want to adjust this for your project, make sure that the archive containing
+ // the bootstrap for the settings registry is still loaded here, and any archives containing
+ // assets used early in startup, like default shaders, are loaded here.
+ constexpr AZStd::string_view paksFolder = "@products@/*.pak"; // (@products@ assumed)
+ m_archive->OpenPacks(paksFolder);
}
GameApplication::~GameApplication()
@@ -80,9 +87,9 @@ namespace AzGameFramework
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_ProjectRegistry(registry, AZ_TRAIT_OS_PLATFORM_CODENAME, specializations, &scratchBuffer);
#endif
- // Used the lowercase the platform name since the bootstrap.game...setreg is being loaded
+ // Used the lowercase the platform name since the bootstrap.game..setreg is being loaded
// from the asset cache root where all the files are in lowercased from regardless of the filesystem case-sensitivity
- static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE "." AZ_TRAIT_OS_PLATFORM_CODENAME_LOWER ".setreg";
+ static constexpr char filename[] = "bootstrap.game." AZ_BUILD_CONFIGURATION_TYPE ".setreg";
AZ::IO::FixedMaxPath cacheRootPath;
if (registry.Get(cacheRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h
index 2d7d9dc511..67bf64d224 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntityInterface.h
@@ -58,6 +58,10 @@ namespace AzToolsFramework
//! @return The highest closed entity container id if any, or entityId otherwise.
virtual AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const = 0;
+ //! Triggers the OnContainerEntityStatusChanged notifications for all registered containers,
+ //! allowing listeners to update correctly.
+ virtual void RefreshAllContainerEntities(AzFramework::EntityContextId entityContextId) const = 0;
+
//! Clears all open state information for Container Entities for the EntityContextId provided.
//! Used when context is switched, for example in the case of a new root prefab being loaded
//! in place of an old one.
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp
index 0a27a5cb90..78cf84c6a1 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.cpp
@@ -142,6 +142,15 @@ namespace AzToolsFramework
Clear(editorEntityContextId);
}
+ void ContainerEntitySystemComponent::RefreshAllContainerEntities([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
+ {
+ for (AZ::EntityId containerEntityId : m_containers)
+ {
+ ContainerEntityNotificationBus::Broadcast(
+ &ContainerEntityNotificationBus::Events::OnContainerEntityStatusChanged, containerEntityId, m_openContainers.contains(containerEntityId));
+ }
+ }
+
ContainerEntityOperationResult ContainerEntitySystemComponent::Clear(AzFramework::EntityContextId entityContextId)
{
// We don't yet support multiple entity contexts, so only clear the default.
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h
index 7a11e05096..68153a77cb 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h
@@ -47,6 +47,7 @@ namespace AzToolsFramework
ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) override;
bool IsContainerOpen(AZ::EntityId entityId) const override;
AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const override;
+ void RefreshAllContainerEntities(AzFramework::EntityContextId entityContextId) const override;
ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) override;
bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const override;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp
index 5ba7382831..8098727177 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp
@@ -86,6 +86,45 @@ namespace AzToolsFramework::Prefab
return AZ::Success();
}
+ PrefabFocusOperationResult PrefabFocusHandler::FocusOnParentOfFocusedPrefab(
+ [[maybe_unused]] AzFramework::EntityContextId entityContextId)
+ {
+ // If only one instance is in the hierarchy, this operation is invalid
+ size_t hierarchySize = m_instanceFocusHierarchy.size();
+ if (hierarchySize <= 1)
+ {
+ return AZ::Failure(
+ AZStd::string("Prefab Focus Handler: Could not complete FocusOnParentOfFocusedPrefab operation while focusing on the root."));
+ }
+
+ // Retrieve parent of currently focused prefab.
+ InstanceOptionalReference parentInstance = m_instanceFocusHierarchy[hierarchySize - 2];
+
+ // Use container entity of parent Instance for focus operations.
+ AZ::EntityId entityId = parentInstance->get().GetContainerEntityId();
+
+ // Initialize Undo Batch object
+ ScopedUndoBatch undoBatch("Edit Prefab");
+
+ // Clear selection
+ {
+ const EntityIdList selectedEntities = EntityIdList{};
+ auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection");
+ selectionUndo->SetParent(undoBatch.GetUndoBatch());
+ ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
+ }
+
+ // Edit Prefab
+ {
+ auto editUndo = aznew PrefabFocusUndo("Edit Prefab");
+ editUndo->Capture(entityId);
+ editUndo->SetParent(undoBatch.GetUndoBatch());
+ FocusOnPrefabInstanceOwningEntityId(entityId);
+ }
+
+ return AZ::Success();
+ }
+
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
{
if (index < 0 || index >= m_instanceFocusHierarchy.size())
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h
index 9decaed1ec..75b9666389 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.h
@@ -50,6 +50,7 @@ namespace AzToolsFramework::Prefab
// PrefabFocusPublicInterface overrides ...
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
+ PrefabFocusOperationResult FocusOnParentOfFocusedPrefab(AzFramework::EntityContextId entityContextId) override;
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
AZ::EntityId GetFocusedPrefabContainerEntityId(AzFramework::EntityContextId entityContextId) const override;
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h
index 5bd4c6b0f6..2fc9ef6b9a 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusPublicInterface.h
@@ -30,6 +30,9 @@ namespace AzToolsFramework::Prefab
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
+ //! Set the focused prefab instance to the parent of the currently focused prefab instance. Supports undo/redo.
+ virtual PrefabFocusOperationResult FocusOnParentOfFocusedPrefab(AzFramework::EntityContextId entityContextId) = 0;
+
//! Set the focused prefab instance to the instance at position index of the current path. Supports undo/redo.
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
index 84fe476fb3..5ac9f772dc 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp
@@ -12,11 +12,12 @@
#include
#include
+#include
#include
#include
#include
-#include
#include
+#include
#include
#include
#include
@@ -565,6 +566,7 @@ namespace AzToolsFramework
parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
}
+ // If the parent entity isn't owned by a prefab instance, bail.
InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId);
if (!owningInstanceOfParentEntity)
{
@@ -572,6 +574,14 @@ namespace AzToolsFramework
"Cannot add entity because the owning instance of parent entity with id '%llu' could not be found.",
static_cast(parentId)));
}
+
+ // If the parent entity is a closed container, bail.
+ if (auto containerEntityInterface = AZ::Interface::Get(); !containerEntityInterface->IsContainerOpen(parentId))
+ {
+ return AZ::Failure(AZStd::string::format(
+ "Cannot add entity because the parent entity (id '%llu') is a closed container entity.",
+ static_cast(parentId)));
+ }
EntityAlias entityAlias = Instance::GenerateEntityAlias();
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
index 434a1d8303..a68a72f00a 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp
@@ -43,6 +43,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -764,10 +765,21 @@ namespace AzToolsFramework
return canHandleData;
}
- bool EntityOutlinerListModel::CanDropMimeDataAssets(const QMimeData* data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex& /*parent*/) const
+ bool EntityOutlinerListModel::CanDropMimeDataAssets(
+ const QMimeData* data,
+ [[maybe_unused]] Qt::DropAction action,
+ [[maybe_unused]] int row,
+ [[maybe_unused]] int column,
+ const QModelIndex& parent) const
{
- using namespace AzToolsFramework;
-
+ // Disable dropping assets on closed container entities.
+ AZ::EntityId parentId = GetEntityFromIndex(parent);
+ if (auto containerEntityInterface = AZ::Interface::Get();
+ !containerEntityInterface->IsContainerOpen(parentId))
+ {
+ return false;
+ }
+
if (data->hasFormat(AssetBrowser::AssetBrowserEntry::GetMimeType()))
{
return DecodeAssetMimeData(data);
@@ -788,8 +800,15 @@ namespace AzToolsFramework
return false;
}
+ // If the parent entity is a closed container, bail.
+ if (auto containerEntityInterface = AZ::Interface::Get();
+ !containerEntityInterface->IsContainerOpen(assignParentId))
+ {
+ return false;
+ }
+
// Source Files
- if (sourceFiles.size() > 0)
+ if (!sourceFiles.empty())
{
// Get position (center of viewport). If no viewport is available, (0,0,0) will be used.
AZ::Vector3 viewportCenterPosition = AZ::Vector3::CreateZero();
@@ -973,6 +992,12 @@ namespace AzToolsFramework
return false;
}
+ // If the new parent is a closed container, bail.
+ if (auto containerEntityInterface = AZ::Interface::Get(); !containerEntityInterface->IsContainerOpen(newParentId))
+ {
+ return false;
+ }
+
// Ignore entities not owned by the editor context. It is assumed that all entities belong
// to the same context since multiple selection doesn't span across views.
for (const AZ::EntityId& entityId : selectedEntityIds)
@@ -2155,20 +2180,9 @@ namespace AzToolsFramework
void EntityOutlinerItemDelegate::PaintAncestorForegrounds(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
- // Go through ancestors and add them to the stack
- AZStd::stack handlerStack;
-
+ // Ancestor foregrounds are painted on top of the childrens'.
for (QModelIndex ancestorIndex = index.parent(); ancestorIndex.isValid(); ancestorIndex = ancestorIndex.parent())
{
- handlerStack.push(ancestorIndex);
- }
-
- // Apply the ancestor overrides from top to bottom
- while (!handlerStack.empty())
- {
- QModelIndex ancestorIndex = handlerStack.top();
- handlerStack.pop();
-
AZ::EntityId ancestorEntityId(ancestorIndex.data(EntityOutlinerListModel::EntityIdRole).value());
auto ancestorUiHandler = m_editorEntityFrameworkInterface->GetHandler(ancestorEntityId);
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp
index bb6bdf0ebd..b34bbff298 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.cpp
@@ -8,6 +8,7 @@
#include
+#include
#include
#include
@@ -92,4 +93,16 @@ namespace AzToolsFramework
painter->drawLine(rect.bottomLeft(), rect.bottomRight());
painter->restore();
}
+
+ bool LevelRootUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const
+ {
+ if (auto prefabFocusPublicInterface = AZ::Interface::Get();
+ !prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
+ {
+ prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
+ }
+
+ // Don't propagate event.
+ return true;
+ }
}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h
index 1e485572b8..3f7f56670e 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/LevelRootUiHandler.h
@@ -33,6 +33,7 @@ namespace AzToolsFramework
bool CanToggleLockVisibility(AZ::EntityId entityId) const override;
bool CanRename(AZ::EntityId entityId) const override;
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
private:
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
index ae0d18b077..aa6d82e634 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#include
#include
@@ -54,6 +55,7 @@
#include
#include
#include
+#include
#include
#include
@@ -61,6 +63,8 @@ namespace AzToolsFramework
{
namespace Prefab
{
+ AzFramework::EntityContextId PrefabIntegrationManager::s_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
+
ContainerEntityInterface* PrefabIntegrationManager::s_containerEntityInterface = nullptr;
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr;
@@ -136,6 +140,9 @@ namespace AzToolsFramework
return;
}
+ // Get EditorEntityContextId
+ EditorEntityContextRequestBus::BroadcastResult(s_editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
+
// Initialize Editor functionality for the Prefab Focus Handler
auto prefabFocusInterface = AZ::Interface::Get();
prefabFocusInterface->InitializeEditorInterfaces();
@@ -145,10 +152,16 @@ namespace AzToolsFramework
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
AZ::Interface::Register(this);
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension);
+ EditorEntityContextNotificationBus::Handler::BusConnect();
+
+ InitializeShortcuts();
}
PrefabIntegrationManager::~PrefabIntegrationManager()
{
+ UninitializeShortcuts();
+
+ EditorEntityContextNotificationBus::Handler::BusDisconnect();
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect();
AZ::Interface::Unregister(this);
PrefabInstanceContainerNotificationBus::Handler::BusDisconnect();
@@ -161,6 +174,74 @@ namespace AzToolsFramework
PrefabUserSettings::Reflect(context);
}
+ void PrefabIntegrationManager::InitializeShortcuts()
+ {
+ // Open/Edit Prefab (+)
+ // We also support = to enable easier editing on compact US keyboards.
+ {
+ m_actions.emplace_back(AZStd::make_unique(nullptr));
+
+ m_actions.back()->setShortcuts({ QKeySequence(Qt::Key_Plus), QKeySequence(Qt::Key_Equal) });
+ m_actions.back()->setText("Open/Edit Prefab");
+ m_actions.back()->setStatusTip("Edit the prefab in focus mode.");
+
+ QObject::connect(
+ m_actions.back().get(), &QAction::triggered, m_actions.back().get(),
+ []
+ {
+ AzToolsFramework::EntityIdList selectedEntities;
+ AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
+ selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
+
+ if (selectedEntities.size() != 1)
+ {
+ return;
+ }
+
+ AZ::EntityId selectedEntity = selectedEntities[0];
+
+ if (!s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
+ {
+ return;
+ }
+
+ if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
+ {
+ ContextMenu_EditPrefab(selectedEntity);
+ }
+ });
+
+ EditorActionRequestBus::Broadcast(
+ &EditorActionRequests::AddActionViaBusCrc, AZ_CRC_CE("com.o3de.action.editortransform.prefabopen"),
+ m_actions.back().get());
+ }
+
+ // Close Prefab (-)
+ {
+ m_actions.emplace_back(AZStd::make_unique(nullptr));
+
+ m_actions.back()->setShortcuts({ QKeySequence(Qt::Key_Minus) });
+ m_actions.back()->setText("Close Prefab");
+ m_actions.back()->setStatusTip("Close focus mode for this prefab and move one level up.");
+
+ QObject::connect(
+ m_actions.back().get(), &QAction::triggered, m_actions.back().get(),
+ []
+ {
+ ContextMenu_ClosePrefab();
+ });
+
+ EditorActionRequestBus::Broadcast(
+ &EditorActionRequests::AddActionViaBusCrc, AZ_CRC_CE("com.o3de.action.editortransform.prefabclose"),
+ m_actions.back().get());
+ }
+ }
+
+ void PrefabIntegrationManager::UninitializeShortcuts()
+ {
+ m_actions.clear();
+ }
+
int PrefabIntegrationManager::GetMenuPosition() const
{
return aznumeric_cast(EditorContextMenuOrdering::MIDDLE);
@@ -181,16 +262,13 @@ namespace AzToolsFramework
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
- auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
- EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
-
// Create Prefab
{
if (!selectedEntities.empty())
{
// Hide if the only selected entity is the Focused Instance Container
if (selectedEntities.size() > 1 ||
- selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))
+ selectedEntities[0] != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId))
{
bool layerInSelection = false;
@@ -254,17 +332,30 @@ namespace AzToolsFramework
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
{
- // Edit Prefab
if (!s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
{
- QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
+ // Edit Prefab
+ QAction* editAction = menu->addAction(QObject::tr("Open/Edit Prefab"));
+ editAction->setShortcut(QKeySequence(Qt::Key_Plus));
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
QObject::connect(editAction, &QAction::triggered, editAction, [selectedEntity] {
ContextMenu_EditPrefab(selectedEntity);
});
-
- itemWasShown = true;
+ }
+ else
+ {
+ // Close Prefab
+ QAction* closeAction = menu->addAction(QObject::tr("Close Prefab"));
+ closeAction->setShortcut(QKeySequence(Qt::Key_Minus));
+ closeAction->setToolTip(QObject::tr("Close focus mode for this prefab and move one level up."));
+
+ QObject::connect(
+ closeAction, &QAction::triggered, closeAction,
+ []
+ {
+ ContextMenu_ClosePrefab();
+ });
}
// Save Prefab
@@ -279,9 +370,9 @@ namespace AzToolsFramework
QObject::connect(saveAction, &QAction::triggered, saveAction, [selectedEntity] {
ContextMenu_SavePrefab(selectedEntity);
});
-
- itemWasShown = true;
}
+
+ itemWasShown = true;
}
}
}
@@ -295,7 +386,8 @@ namespace AzToolsFramework
QObject::connect(deleteAction, &QAction::triggered, deleteAction, [] { ContextMenu_DeleteSelected(); });
if (selectedEntities.empty() ||
- (selectedEntities.size() == 1 && selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId)))
+ (selectedEntities.size() == 1 &&
+ selectedEntities[0] == s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId)))
{
deleteAction->setDisabled(true);
}
@@ -306,7 +398,7 @@ namespace AzToolsFramework
AZ::EntityId selectedEntityId = selectedEntities[0];
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntityId) &&
- selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))
+ selectedEntityId != s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId))
{
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
QObject::connect(
@@ -334,6 +426,24 @@ namespace AzToolsFramework
}
}
+ void PrefabIntegrationManager::OnStartPlayInEditorBegin()
+ {
+ // Focus on the root prefab (AZ::EntityId() will default to it)
+ s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId());
+ }
+
+ void PrefabIntegrationManager::OnStopPlayInEditor()
+ {
+ // Refresh all containers when leaving Game Mode to ensure everything is synced.
+ QTimer::singleShot(
+ 0,
+ [&]()
+ {
+ s_containerEntityInterface->RefreshAllContainerEntities(s_editorEntityContextId);
+ }
+ );
+ }
+
void PrefabIntegrationManager::ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities)
{
// Save a reference to our currently active window since it will be
@@ -343,12 +453,9 @@ namespace AzToolsFramework
const AZStd::string prefabFilesPath = "@projectroot@/Prefabs";
// Remove focused instance container entity if it's part of the list
- auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
- EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
-
auto focusedContainerIter = AZStd::find(
selectedEntities.begin(), selectedEntities.end(),
- s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId));
+ s_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(s_editorEntityContextId));
if (focusedContainerIter != selectedEntities.end())
{
selectedEntities.erase(focusedContainerIter);
@@ -500,6 +607,11 @@ namespace AzToolsFramework
}
}
+ void PrefabIntegrationManager::ContextMenu_ClosePrefab()
+ {
+ s_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(s_editorEntityContextId);
+ }
+
void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
{
s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity);
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h
index e8c10c150a..808a0c2408 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -56,6 +57,7 @@ namespace AzToolsFramework
, public PrefabInstanceContainerNotificationBus::Handler
, public PrefabIntegrationInterface
, public QObject
+ , private EditorEntityContextNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(PrefabIntegrationManager, AZ::SystemAllocator, 0);
@@ -76,6 +78,10 @@ namespace AzToolsFramework
// EntityOutlinerSourceDropHandlingBus overrides ...
void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override;
+ // EditorEntityContextNotificationBus overrides ...
+ void OnStartPlayInEditorBegin() override;
+ void OnStopPlayInEditor() override;
+
// PrefabInstanceContainerNotificationBus overrides ...
void OnPrefabComponentActivate(AZ::EntityId entityId) override;
void OnPrefabComponentDeactivate(AZ::EntityId entityId) override;
@@ -96,11 +102,16 @@ namespace AzToolsFramework
static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities);
static void ContextMenu_InstantiatePrefab();
static void ContextMenu_InstantiateProceduralPrefab();
+ static void ContextMenu_ClosePrefab();
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
static void ContextMenu_DeleteSelected();
static void ContextMenu_DetachPrefab(AZ::EntityId containerEntity);
+ // Shortcut setup handlers
+ void InitializeShortcuts();
+ void UninitializeShortcuts();
+
// Prompt and resolve dialogs
static bool QueryUserForPrefabSaveLocation(
const AZStd::string& suggestedName, const char* initialTargetDirectory, AZ::u32 prefabUserSettingsId, QWidget* activeWindow,
@@ -140,7 +151,10 @@ namespace AzToolsFramework
AZStd::unique_ptr ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference);
void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog);
+ AZStd::vector> m_actions;
+
static const AZStd::string s_prefabFileExtension;
+ static AzFramework::EntityContextId s_editorEntityContextId;
static ContainerEntityInterface* s_containerEntityInterface;
static EditorEntityUiInterface* s_editorEntityUiInterface;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp
index 447f94fc15..8b56b26508 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp
@@ -21,6 +21,8 @@
namespace AzToolsFramework
{
+ AzFramework::EntityContextId PrefabUiHandler::s_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
+
const QColor PrefabUiHandler::m_backgroundColor = QColor("#444444");
const QColor PrefabUiHandler::m_backgroundHoverColor = QColor("#5A5A5A");
const QColor PrefabUiHandler::m_backgroundSelectedColor = QColor("#656565");
@@ -47,6 +49,9 @@ namespace AzToolsFramework
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusPublicInterface on PrefabUiHandler construction.");
return;
}
+
+ // Get EditorEntityContextId
+ EditorEntityContextRequestBus::BroadcastResult(s_editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
}
QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
@@ -180,7 +185,7 @@ namespace AzToolsFramework
painter->restore();
}
- void PrefabUiHandler::PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
+ void PrefabUiHandler::PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const
{
if (!painter)
@@ -425,19 +430,23 @@ namespace AzToolsFramework
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
- auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
- EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
-
- // Go one level up.
- int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(editorEntityContextId);
- m_prefabFocusPublicInterface->FocusOnPathIndex(editorEntityContextId, length - 2);
+ // Close this prefab and focus on the parent
+ m_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(s_editorEntityContextId);
}
}
bool PrefabUiHandler::OnEntityDoubleClick(AZ::EntityId entityId) const
{
- // Focus on this prefab
- m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
+ if (!m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
+ {
+ // Focus on this prefab
+ m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
+ }
+ else
+ {
+ // Close this prefab and focus on the parent
+ m_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(s_editorEntityContextId);
+ }
// Don't propagate event.
return true;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h
index 6c78afc5b7..bb1c646dbe 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h
@@ -10,6 +10,8 @@
#include
+#include
+
namespace AzToolsFramework
{
@@ -34,9 +36,12 @@ namespace AzToolsFramework
QString GenerateItemTooltip(AZ::EntityId entityId) const override;
QIcon GenerateItemIcon(AZ::EntityId entityId) const override;
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
- void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
- const QModelIndex& descendantIndex) const override;
void PaintItemForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ void PaintDescendantForeground(
+ QPainter* painter,
+ const QStyleOptionViewItem& option,
+ const QModelIndex& index,
+ const QModelIndex& descendantIndex) const override;
bool OnOutlinerItemClick(const QPoint& position, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void OnOutlinerItemCollapse(const QModelIndex& index) const override;
bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
@@ -49,6 +54,8 @@ namespace AzToolsFramework
static QModelIndex GetLastVisibleChild(const QModelIndex& parent);
static QModelIndex Internal_GetLastVisibleChild(const QAbstractItemModel* model, const QModelIndex& index);
+ static AzFramework::EntityContextId s_editorEntityContextId;
+
static constexpr int m_prefabCapsuleRadius = 6;
static constexpr int m_prefabBorderThickness = 2;
static const QColor m_backgroundColor;
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp
index 52cf3279a4..920e99665d 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.cpp
@@ -59,12 +59,11 @@ namespace AzToolsFramework::Prefab
connect(m_backButton, &QToolButton::clicked, this,
[&]()
{
- if (int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
- {
- m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
- }
+ m_prefabFocusPublicInterface->FocusOnParentOfFocusedPrefab(m_editorEntityContextId);
}
);
+
+ m_backButton->setToolTip("Up one level (-)");
}
void PrefabViewportFocusPathHandler::OnPrefabFocusChanged()
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp
index 43e2a6793f..c53aa66d81 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -19,7 +20,6 @@
namespace AzToolsFramework::ViewportUi::Internal
{
const static int HighlightBorderSize = 5;
- const static int TopHighlightBorderSize = 25;
const static char* HighlightBorderColor = "#4A90E2";
static void UnparentWidgets(ViewportUiElementIdInfoLookup& viewportUiElementIdInfoLookup)
@@ -61,7 +61,7 @@ namespace AzToolsFramework::ViewportUi::Internal
, m_uiOverlay(parent)
, m_fullScreenLayout(&m_uiOverlay)
, m_uiOverlayLayout()
- , m_componentModeBorderText(&m_uiOverlay)
+ , m_viewportBorderText(&m_uiOverlay)
{
}
@@ -221,11 +221,11 @@ namespace AzToolsFramework::ViewportUi::Internal
AZStd::shared_ptr ViewportUiDisplay::GetViewportUiElement(ViewportUiElementId elementId)
{
- auto element = m_viewportUiElements.find(elementId);
- if (element != m_viewportUiElements.end())
+ if (auto element = m_viewportUiElements.find(elementId); element != m_viewportUiElements.end())
{
return element->second.m_widget;
}
+
return nullptr;
}
@@ -287,27 +287,30 @@ namespace AzToolsFramework::ViewportUi::Internal
{
return element.IsValid() && element.m_widget->isVisible();
}
+
return false;
}
void ViewportUiDisplay::CreateViewportBorder(const AZStd::string& borderTitle)
{
const AZStd::string styleSheet = AZStd::string::format(
- "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, TopHighlightBorderSize,
+ "border: %dpx solid %s; border-top: %dpx solid %s;", HighlightBorderSize, HighlightBorderColor, ViewportUiTopBorderSize,
HighlightBorderColor);
m_uiOverlay.setStyleSheet(styleSheet.c_str());
m_uiOverlayLayout.setContentsMargins(
- HighlightBorderSize + ViewportUiOverlayMargin, TopHighlightBorderSize + ViewportUiOverlayMargin,
+ HighlightBorderSize + ViewportUiOverlayMargin, ViewportUiTopBorderSize + ViewportUiOverlayMargin,
HighlightBorderSize + ViewportUiOverlayMargin, HighlightBorderSize + ViewportUiOverlayMargin);
- m_componentModeBorderText.setVisible(true);
- m_componentModeBorderText.setText(borderTitle.c_str());
+ m_viewportBorderText.setVisible(true);
+ m_viewportBorderText.setText(borderTitle.c_str());
}
void ViewportUiDisplay::RemoveViewportBorder()
{
- m_componentModeBorderText.setVisible(false);
+ m_viewportBorderText.setVisible(false);
m_uiOverlay.setStyleSheet("border: none;");
- m_uiOverlayLayout.setMargin(ViewportUiOverlayMargin);
+ m_uiOverlayLayout.setContentsMargins(
+ ViewportUiOverlayMargin, ViewportUiOverlayMargin + ViewportUiOverlayTopMarginPadding, ViewportUiOverlayMargin,
+ ViewportUiOverlayMargin);
}
void ViewportUiDisplay::PositionViewportUiElementFromWorldSpace(ViewportUiElementId elementId, const AZ::Vector3& pos)
@@ -359,10 +362,10 @@ namespace AzToolsFramework::ViewportUi::Internal
// format the label which will appear on top of the highlight border
AZStd::string styleSheet = AZStd::string::format("background-color: %s; border: none;", HighlightBorderColor);
- m_componentModeBorderText.setStyleSheet(styleSheet.c_str());
- m_componentModeBorderText.setFixedHeight(TopHighlightBorderSize);
- m_componentModeBorderText.setVisible(false);
- m_fullScreenLayout.addWidget(&m_componentModeBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter);
+ m_viewportBorderText.setStyleSheet(styleSheet.c_str());
+ m_viewportBorderText.setFixedHeight(ViewportUiTopBorderSize);
+ m_viewportBorderText.setVisible(false);
+ m_fullScreenLayout.addWidget(&m_viewportBorderText, 0, 0, Qt::AlignTop | Qt::AlignHCenter);
}
void ViewportUiDisplay::PrepareWidgetForViewportUi(QPointer widget)
@@ -395,14 +398,14 @@ namespace AzToolsFramework::ViewportUi::Internal
void ViewportUiDisplay::UpdateUiOverlayGeometry()
{
- // add the component mode border region if visible
+ // add the viewport border region if visible
QRegion region;
- if (m_componentModeBorderText.isVisible())
+ if (m_viewportBorderText.isVisible())
{
// get the border region by taking the entire region and subtracting the non-border area
region += m_uiOverlay.rect();
region -= QRect(
- QPoint(m_uiOverlay.rect().left() + HighlightBorderSize, m_uiOverlay.rect().top() + TopHighlightBorderSize),
+ QPoint(m_uiOverlay.rect().left() + HighlightBorderSize, m_uiOverlay.rect().top() + ViewportUiTopBorderSize),
QPoint(m_uiOverlay.rect().right() - HighlightBorderSize, m_uiOverlay.rect().bottom() - HighlightBorderSize));
}
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h
index 5020241815..32b746a1ac 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplay.h
@@ -113,7 +113,7 @@ namespace AzToolsFramework::ViewportUi::Internal
QWidget m_uiOverlay; //!< The UI Overlay which displays Viewport UI Elements.
QGridLayout m_fullScreenLayout; //!< The layout which extends across the full screen.
ViewportUiDisplayLayout m_uiOverlayLayout; //!< The layout used for optionally anchoring Viewport UI Elements.
- QLabel m_componentModeBorderText; //!< The text used for the Component Mode border.
+ QLabel m_viewportBorderText; //!< The text used for the viewport border.
QWidget* m_renderOverlay;
QPointer m_fullScreenWidget; //!< Reference to the widget attached to m_fullScreenLayout if any.
diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h
index 4a44f07491..335f664094 100644
--- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h
+++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportUi/ViewportUiDisplayLayout.h
@@ -14,13 +14,20 @@
#include
#include
-namespace AzToolsFramework::ViewportUi::Internal
+namespace AzToolsFramework::ViewportUi
{
- // margin for the Viewport UI Overlay in pixels
+ //! Margin for the Viewport UI Overlay (in pixels)
constexpr int ViewportUiOverlayMargin = 5;
- // padding to make space for ImGui
+ //! Padding to make space for ImGui (in pixels)
constexpr int ViewportUiOverlayTopMarginPadding = 20;
+ //! Size of the top viewport border (in pixels)
+ constexpr int ViewportUiTopBorderSize = 25;
+ //! Size of the left, right and bottom viewport border (in pixels)
+ constexpr int ViewportUiLeftRightBottomBorderSize = 5;
+} // namespace AzToolsFramework::ViewportUi
+namespace AzToolsFramework::ViewportUi::Internal
+{
//! QGridLayout implementation that uses a grid of QVBox/QHBoxLayouts internally to stack widgets.
class ViewportUiDisplayLayout : public QGridLayout
{
diff --git a/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp b/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp
index ad80b5c39d..af0ae9addb 100644
--- a/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp
+++ b/Code/Framework/AzToolsFramework/Tests/AssetFileInfoListComparison.cpp
@@ -57,9 +57,7 @@ namespace UnitTest
ArgumentContainer argContainer{ {} };
// Append Command Line override for the Project Cache Path
- auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", m_tempDir.GetDirectory());
- auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" };
- argContainer.push_back(projectCachePathOverride.data());
+ auto projectPathOverride = FixedValueString::format(R"(--project-path="%s")", m_tempDir.GetDirectory());
argContainer.push_back(projectPathOverride.data());
m_application = new ToolsTestApplication("AssetFileInfoListComparisonTest", aznumeric_caster(argContainer.size()), argContainer.data());
AzToolsFramework::AssetSeedManager assetSeedManager;
@@ -100,7 +98,7 @@ namespace UnitTest
m_application->Start(AzFramework::Application::Descriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
- // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
+ // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
@@ -223,7 +221,7 @@ namespace UnitTest
// AssetFileInfo should contain {2*, 4*, 5}
AzToolsFramework::AssetFileInfoList assetFileInfoList;
-
+
ASSERT_TRUE(AZ::Utils::LoadObjectFromFileInPlace(TempFiles[FileIndex::ResultAssetFileInfoList], assetFileInfoList)) << "Unable to read the asset file info list.\n";
EXPECT_EQ(assetFileInfoList.m_fileInfoList.size(), 3);
@@ -256,7 +254,7 @@ namespace UnitTest
}
}
- // Verifying that correct assetId are present in the assetFileInfo list
+ // Verifying that correct assetId are present in the assetFileInfo list
AZStd::unordered_set expectedAssetIds{ m_assets[2], m_assets[4], m_assets[5] };
for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList)
@@ -298,7 +296,7 @@ namespace UnitTest
{
firstAssetIdToAssetFileInfoMap[assetFileInfo.m_assetId] = AZStd::move(assetFileInfo);
}
-
+
AzToolsFramework::AssetFileInfoList secondAssetFileInfoList;
ASSERT_TRUE(AZ::Utils::LoadObjectFromFileInPlace(TempFiles[FileIndex::SecondAssetFileInfoList], secondAssetFileInfoList)) << "Unable to read the asset file info list.\n";
@@ -315,7 +313,7 @@ namespace UnitTest
auto foundSecond = secondAssetIdToAssetFileInfoMap.find(assetFileInfo.m_assetId);
if (foundSecond != secondAssetIdToAssetFileInfoMap.end())
{
- // Even if the asset Id is present in both the AssetFileInfo List, it should match the file hash from the second AssetFileInfo list
+ // Even if the asset Id is present in both the AssetFileInfo List, it should match the file hash from the second AssetFileInfo list
for (int idx = 0; idx < AzToolsFramework::AssetFileInfo::s_arraySize; idx++)
{
if (foundSecond->second.m_hash[idx] != assetFileInfo.m_hash[idx])
@@ -343,7 +341,7 @@ namespace UnitTest
}
}
- // Verifying that correct assetId are present in the assetFileInfo list
+ // Verifying that correct assetId are present in the assetFileInfo list
AZStd::unordered_set expectedAssetIds{ m_assets[0], m_assets[1], m_assets[2], m_assets[3], m_assets[4], m_assets[5] };
for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList)
@@ -403,7 +401,7 @@ namespace UnitTest
}
}
- // Verifying that correct assetId are present in the assetFileInfo list
+ // Verifying that correct assetId are present in the assetFileInfo list
AZStd::unordered_set