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/PythonCoverage/gem.json b/AutomatedTesting/Gem/PythonCoverage/gem.json index 39e327b5e3..b99ce0daad 100644 --- a/AutomatedTesting/Gem/PythonCoverage/gem.json +++ b/AutomatedTesting/Gem/PythonCoverage/gem.json @@ -2,6 +2,7 @@ "gem_name": "PythonCoverage", "display_name": "PythonCoverage", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "A tool for generating gem coverage for Python tests.", 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/AutomatedTesting/Gem/gem.json b/AutomatedTesting/Gem/gem.json index 6c8c7829ce..df197df09d 100644 --- a/AutomatedTesting/Gem/gem.json +++ b/AutomatedTesting/Gem/gem.json @@ -2,10 +2,13 @@ "gem_name": "AutomatedTesting", "display_name": "AutomatedTesting", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "Project Gem for customizing the AutomatedTesting project functionality.", - "canonical_tags": ["Gem"], + "canonical_tags": [ + "Gem" + ], "user_tags": [], "icon_path": "preview.png", "requirements": "" 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 b16758a07e..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(); @@ -2638,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/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp index ad5e57479b..8ba152a9f9 100644 --- a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.cpp @@ -10,6 +10,8 @@ #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB #include +#include +#include #endif namespace Editor @@ -23,16 +25,34 @@ namespace Editor return nullptr; } + xcb_connection_t* EditorQtApplicationXcb::GetXcbConnectionFromQt() + { + QPlatformNativeInterface* native = platformNativeInterface(); + AZ_Warning("EditorQtApplicationXcb", native, "Unable to retrieve the native platform interface"); + if (!native) + { + return nullptr; + } + return reinterpret_cast(native->nativeResourceForIntegration(QByteArray("connection"))); + } + + void EditorQtApplicationXcb::OnStartPlayInEditor() + { + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(GetXcbConnectionFromQt(), true); + } + + void EditorQtApplicationXcb::OnStopPlayInEditor() + { + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(GetXcbConnectionFromQt(), false); + } + bool EditorQtApplicationXcb::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*) { if (GetIEditor()->IsInGameMode()) { #ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB - // We need to handle RAW Input events in a separate loop. This is a workaround to enable XInput2 RAW Inputs using Editor mode. - // TODO To have this call here might be not be perfect. - AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::PollSpecialEvents); - - // Now handle the rest of the events. AzFramework::XcbEventHandlerBus::Broadcast( &AzFramework::XcbEventHandler::HandleXcbEvent, static_cast(message)); #endif diff --git a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h index 8c145c3aa7..109ae1742b 100644 --- a/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h +++ b/Code/Editor/Platform/Linux/Editor/Core/QtEditorApplication_linux.h @@ -6,19 +6,35 @@ * */ +#if !defined(Q_MOC_RUN) #include +#include +#endif + +using xcb_connection_t = struct xcb_connection_t; namespace Editor { - class EditorQtApplicationXcb : public EditorQtApplication + class EditorQtApplicationXcb + : public EditorQtApplication + , public AzToolsFramework::EditorEntityContextNotificationBus::Handler { Q_OBJECT public: EditorQtApplicationXcb(int& argc, char** argv) : EditorQtApplication(argc, argv) { + // Connect bus to listen for OnStart/StopPlayInEditor events + AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect(); } + xcb_connection_t* GetXcbConnectionFromQt(); + + /////////////////////////////////////////////////////////////////////// + // AzToolsFramework::EditorEntityContextNotificationBus overrides + void OnStartPlayInEditor() override; + void OnStopPlayInEditor() override; + // QAbstractNativeEventFilter: bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; }; 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/Platform/Common/Xcb/AzFramework/XcbApplication.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp index f57f4a89ac..780e1e72fe 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbApplication.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace AzFramework { //////////////////////////////////////////////////////////////////////////////////////////////// @@ -34,6 +36,31 @@ namespace AzFramework return m_xcbConnection.get(); } + void SetEnableXInput(xcb_connection_t* connection, bool enable) override + { + struct Mask + { + xcb_input_event_mask_t head; + xcb_input_xi_event_mask_t mask; + }; + const Mask mask { + /*.head=*/{ + /*.device_id=*/XCB_INPUT_DEVICE_ALL_MASTER, + /*.mask_len=*/1 + }, + /*.mask=*/ enable ? + (xcb_input_xi_event_mask_t)(XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE) : + (xcb_input_xi_event_mask_t)XCB_NONE + }; + + const xcb_setup_t* xcbSetup = xcb_get_setup(connection); + const xcb_screen_t* xcbScreen = xcb_setup_roots_iterator(xcbSetup).data; + + xcb_input_xi_select_events(connection, xcbScreen->root, 1, &mask.head); + + xcb_flush(connection); + } + private: XcbUniquePtr m_xcbConnection = nullptr; }; diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h index daa5bf35af..ca7ce06e6c 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbConnectionManager.h @@ -24,6 +24,9 @@ namespace AzFramework virtual ~XcbConnectionManager() = default; virtual xcb_connection_t* GetXcbConnection() const = 0; + + //! Enables/Disables XInput Raw Input events. + virtual void SetEnableXInput(xcb_connection_t* connection, bool enable) = 0; }; class XcbConnectionManagerBusTraits diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h index 251342093a..f32e45ed99 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbEventHandler.h @@ -23,9 +23,6 @@ namespace AzFramework virtual ~XcbEventHandler() = default; virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0; - - // ATTN This is used as a workaround for RAW Input events when using the Editor. - virtual void PollSpecialEvents(){}; }; class XcbEventHandlerBusTraits : public AZ::EBusTraits diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp index 56f21e6533..c3b7a97ccf 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.cpp @@ -13,21 +13,68 @@ namespace AzFramework { - xcb_window_t GetSystemCursorFocusWindow() + xcb_window_t GetSystemCursorFocusWindow(xcb_connection_t* connection) { void* systemCursorFocusWindow = nullptr; AzFramework::InputSystemCursorConstraintRequestBus::BroadcastResult( systemCursorFocusWindow, &AzFramework::InputSystemCursorConstraintRequests::GetSystemCursorConstraintWindow); - if (!systemCursorFocusWindow) + if (systemCursorFocusWindow) { - return XCB_NONE; + return static_cast(reinterpret_cast(systemCursorFocusWindow)); } - // TODO Clang compile error because cast .... loses information. On GNU/Linux HWND is void* and on 64-bit - // machines its obviously 64 bit but we receive the window id from m_renderOverlay.winId() which is xcb_window_t 32-bit. + // EWMH-compliant window managers set the "_NET_ACTIVE_WINDOW" property + // of the X server's root window to the currently active window. This + // retrieves value of that property. - return static_cast(reinterpret_cast(systemCursorFocusWindow)); + // Get the atom for the _NET_ACTIVE_WINDOW property + constexpr int propertyNameLength = 18; + xcb_generic_error_t* error = nullptr; + XcbStdFreePtr activeWindowAtom {xcb_intern_atom_reply( + connection, + xcb_intern_atom(connection, /*only_if_exists=*/ 1, propertyNameLength, "_NET_ACTIVE_WINDOW"), + &error + )}; + if (!activeWindowAtom || error) + { + if (error) + { + AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code); + free(error); + } + return XCB_WINDOW_NONE; + } + + // Get the root window + const xcb_window_t rootWId = xcb_setup_roots_iterator(xcb_get_setup(connection)).data->root; + + // Fetch the value of the root window's _NET_ACTIVE_WINDOW property + XcbStdFreePtr property {xcb_get_property_reply( + connection, + xcb_get_property( + /*c=*/connection, + /*_delete=*/ 0, + /*window=*/rootWId, + /*property=*/activeWindowAtom->atom, + /*type=*/XCB_ATOM_WINDOW, + /*long_offset=*/0, + /*long_length=*/1 + ), + &error + )}; + + if (!property || error) + { + if (error) + { + AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code); + free(error); + } + return XCB_WINDOW_NONE; + } + + return *static_cast(xcb_get_property_value(property.get())); } xcb_connection_t* XcbInputDeviceMouse::s_xcbConnection = nullptr; @@ -39,8 +86,7 @@ namespace AzFramework : InputDeviceMouse::Implementation(inputDevice) , m_systemCursorState(SystemCursorState::Unknown) , m_systemCursorPositionNormalized(0.5f, 0.5f) - , m_prevConstraintWindow(XCB_NONE) - , m_focusWindow(XCB_NONE) + , m_focusWindow(XCB_WINDOW_NONE) , m_cursorShown(true) { XcbEventHandlerBus::Handler::BusConnect(); @@ -57,14 +103,14 @@ namespace AzFramework InputDeviceMouse::Implementation* XcbInputDeviceMouse::Create(InputDeviceMouse& inputDevice) { - auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + const auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); if (!interface) { AZ_Warning("XcbInput", false, "XCB interface not available"); return nullptr; } - s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection(); + s_xcbConnection = interface->GetXcbConnection(); if (!s_xcbConnection) { AZ_Warning("XcbInput", false, "XCB connection not available"); @@ -126,7 +172,7 @@ namespace AzFramework // Get window information. const XcbStdFreePtr xcbGeometryReply{ xcb_get_geometry_reply( - s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) }; if (!xcbGeometryReply) { @@ -137,7 +183,7 @@ namespace AzFramework xcb_translate_coordinates(s_xcbConnection, window, s_xcbScreen->root, 0, 0); const XcbStdFreePtr xkbTranslateCoordReply{ xcb_translate_coordinates_reply( - s_xcbConnection, translate_coord, NULL) }; + s_xcbConnection, translate_coord, nullptr) }; if (!xkbTranslateCoordReply) { @@ -173,11 +219,11 @@ namespace AzFramework for (const auto& barrier : m_activeBarriers) { xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked( - s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, NULL); - const XcbStdFreePtr xkbError{ xcb_request_check(s_xcbConnection, cookie) }; + s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, nullptr); + const XcbStdFreePtr xcbError{ xcb_request_check(s_xcbConnection, cookie) }; AZ_Warning( - "XcbInput", !xkbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0, + "XcbInput", !xcbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0, barrier.x1, barrier.y1); } } @@ -207,7 +253,7 @@ namespace AzFramework const xcb_xfixes_query_version_cookie_t query_cookie = xcb_xfixes_query_version(s_xcbConnection, 5, 0); - xcb_generic_error_t* error = NULL; + xcb_generic_error_t* error = nullptr; const XcbStdFreePtr xkbQueryRequestReply{ xcb_xfixes_query_version_reply( s_xcbConnection, query_cookie, &error) }; @@ -244,7 +290,7 @@ namespace AzFramework const xcb_input_xi_query_version_cookie_t query_version_cookie = xcb_input_xi_query_version(s_xcbConnection, 2, 2); - xcb_generic_error_t* error = NULL; + xcb_generic_error_t* error = nullptr; const XcbStdFreePtr xkbQueryRequestReply{ xcb_input_xi_query_version_reply( s_xcbConnection, query_version_cookie, &error) }; @@ -268,40 +314,13 @@ namespace AzFramework return m_xInputInitialized; } - void XcbInputDeviceMouse::SetEnableXInput(bool enable) - { - struct - { - xcb_input_event_mask_t head; - int mask; - } mask; - - mask.head.deviceid = XCB_INPUT_DEVICE_ALL; - mask.head.mask_len = 1; - - if (enable) - { - mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | - XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS | - XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE; - } - else - { - mask.mask = XCB_NONE; - } - - xcb_input_xi_select_events(s_xcbConnection, s_xcbScreen->root, 1, &mask.head); - - xcb_flush(s_xcbConnection); - } - void XcbInputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState) { if (systemCursorState != m_systemCursorState) { m_systemCursorState = systemCursorState; - m_focusWindow = GetSystemCursorFocusWindow(); + m_focusWindow = GetSystemCursorFocusWindow(s_xcbConnection); HandleCursorState(m_focusWindow, systemCursorState); } @@ -309,52 +328,10 @@ namespace AzFramework void XcbInputDeviceMouse::HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState) { - bool confined = false, cursorShown = true; - switch (systemCursorState) - { - case SystemCursorState::ConstrainedAndHidden: - { - //!< Constrained to the application's main window and hidden - confined = true; - cursorShown = false; - } - break; - case SystemCursorState::ConstrainedAndVisible: - { - //!< Constrained to the application's main window and visible - confined = true; - } - break; - case SystemCursorState::UnconstrainedAndHidden: - { - //!< Free to move outside the main window but hidden while inside - cursorShown = false; - } - break; - case SystemCursorState::UnconstrainedAndVisible: - { - //!< Free to move outside the application's main window and visible - } - case SystemCursorState::Unknown: - default: - break; - } - - // ATTN GetSystemCursorFocusWindow when getting out of the play in editor will return XCB_NONE - // We need however the window id to reset the cursor. - if (XCB_NONE == window && (confined || cursorShown)) - { - // Reuse the previous window to reset states. - window = m_prevConstraintWindow; - m_prevConstraintWindow = XCB_NONE; - } - else - { - // Remember the window we used to modify cursor and barrier states. - m_prevConstraintWindow = window; - } - - SetEnableXInput(!cursorShown); + const bool confined = (systemCursorState == SystemCursorState::ConstrainedAndHidden) || + (systemCursorState == SystemCursorState::ConstrainedAndVisible); + const bool cursorShown = (systemCursorState == SystemCursorState::ConstrainedAndVisible) || + (systemCursorState == SystemCursorState::UnconstrainedAndVisible); CreateBarriers(window, confined); ShowCursor(window, cursorShown); @@ -368,26 +345,26 @@ namespace AzFramework void XcbInputDeviceMouse::SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized) { // TODO Basically not done at all. Added only the basic functions needed. - const XcbStdFreePtr xkbGeometryReply{ xcb_get_geometry_reply( - s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + const XcbStdFreePtr xcbGeometryReply{ xcb_get_geometry_reply( + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) }; - if (!xkbGeometryReply) + if (!xcbGeometryReply) { return; } - const int16_t x = static_cast(positionNormalized.GetX() * xkbGeometryReply->width); - const int16_t y = static_cast(positionNormalized.GetY() * xkbGeometryReply->height); + const int16_t x = static_cast(positionNormalized.GetX() * xcbGeometryReply->width); + const int16_t y = static_cast(positionNormalized.GetY() * xcbGeometryReply->height); - xcb_warp_pointer(s_xcbConnection, XCB_NONE, window, 0, 0, 0, 0, x, y); + xcb_warp_pointer(s_xcbConnection, XCB_WINDOW_NONE, window, 0, 0, 0, 0, x, y); xcb_flush(s_xcbConnection); } void XcbInputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) { - const xcb_window_t window = GetSystemCursorFocusWindow(); - if (XCB_NONE == window) + const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection); + if (XCB_WINDOW_NONE == window) { return; } @@ -401,7 +378,7 @@ namespace AzFramework const xcb_query_pointer_cookie_t pointer = xcb_query_pointer(s_xcbConnection, window); - const XcbStdFreePtr xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, NULL) }; + const XcbStdFreePtr xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, nullptr) }; if (!xkbQueryPointerReply) { @@ -409,7 +386,7 @@ namespace AzFramework } const XcbStdFreePtr xkbGeometryReply{ xcb_get_geometry_reply( - s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) }; + s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) }; if (!xkbGeometryReply) { @@ -429,8 +406,8 @@ namespace AzFramework AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalized() const { - const xcb_window_t window = GetSystemCursorFocusWindow(); - if (XCB_NONE == window) + const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection); + if (XCB_WINDOW_NONE == window) { return AZ::Vector2::CreateZero(); } @@ -455,11 +432,11 @@ namespace AzFramework cookie = xcb_xfixes_hide_cursor_checked(s_xcbConnection, window); } - const XcbStdFreePtr xkbError{ xcb_request_check(s_xcbConnection, cookie) }; + const XcbStdFreePtr xcbError{ xcb_request_check(s_xcbConnection, cookie) }; - if (xkbError) + if (xcbError) { - AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xkbError->error_code); + AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xcbError->error_code); return; } @@ -500,14 +477,6 @@ namespace AzFramework } } - void XcbInputDeviceMouse::HandlePointerMotionEvents(const xcb_generic_event_t* event) - { - const xcb_input_motion_event_t* mouseMotionEvent = reinterpret_cast(event); - - m_systemCursorPosition[0] = mouseMotionEvent->event_x; - m_systemCursorPosition[1] = mouseMotionEvent->event_y; - } - void XcbInputDeviceMouse::HandleRawInputEvents(const xcb_ge_generic_event_t* event) { const xcb_ge_generic_event_t* genericEvent = reinterpret_cast(event); @@ -552,78 +521,20 @@ namespace AzFramework } } - void XcbInputDeviceMouse::PollSpecialEvents() - { - while (xcb_generic_event_t* genericEvent = xcb_poll_for_queued_event(s_xcbConnection)) - { - // TODO Is the following correct? If we are showing the cursor, don't poll RAW Input events. - switch (genericEvent->response_type & ~0x80) - { - case XCB_GE_GENERIC: - { - const xcb_ge_generic_event_t* geGenericEvent = reinterpret_cast(genericEvent); - - // Only handle raw inputs if we have focus. - // Handle Raw Input events first. - if ((geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) || - (geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || - (geGenericEvent->event_type == XCB_INPUT_RAW_MOTION)) - { - HandleRawInputEvents(geGenericEvent); - - free(genericEvent); - } - } - break; - } - } - } - void XcbInputDeviceMouse::HandleXcbEvent(xcb_generic_event_t* event) { switch (event->response_type & ~0x80) { - // QT5 is using by default XInput which means we do need to check for XCB_GE_GENERIC event to parse all mouse related events. + // XInput raw events are sent from the server as a XCB_GE_GENERIC + // event. A XCB_GE_GENERIC event is typecast to a + // xcb_ge_generic_event_t, which is distinct from a + // xcb_generic_event_t, and exists so that X11 extensions can extend + // the event emission beyond the size that a normal X11 event could + // contain. case XCB_GE_GENERIC: { const xcb_ge_generic_event_t* genericEvent = reinterpret_cast(event); - - // Handling RAW Inputs here works in GameMode but not in Editor mode because QT is - // not handling RAW input events and passing to. - if (!m_cursorShown) - { - // Handle Raw Input events first. - if ((genericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) || - (genericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || (genericEvent->event_type == XCB_INPUT_RAW_MOTION)) - { - HandleRawInputEvents(genericEvent); - } - } - else - { - switch (genericEvent->event_type) - { - case XCB_INPUT_BUTTON_PRESS: - { - const xcb_input_button_press_event_t* mouseButtonEvent = - reinterpret_cast(genericEvent); - HandleButtonPressEvents(mouseButtonEvent->detail, true); - } - break; - case XCB_INPUT_BUTTON_RELEASE: - { - const xcb_input_button_release_event_t* mouseButtonEvent = - reinterpret_cast(genericEvent); - HandleButtonPressEvents(mouseButtonEvent->detail, false); - } - break; - case XCB_INPUT_MOTION: - { - HandlePointerMotionEvents(event); - } - break; - } - } + HandleRawInputEvents(genericEvent); } break; case XCB_FOCUS_IN: @@ -634,6 +545,9 @@ namespace AzFramework m_focusWindow = focusInEvent->event; HandleCursorState(m_focusWindow, m_systemCursorState); } + + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(interface->GetXcbConnection(), true); } break; case XCB_FOCUS_OUT: @@ -644,7 +558,10 @@ namespace AzFramework ProcessRawEventQueues(); ResetInputChannelStates(); - m_focusWindow = XCB_NONE; + m_focusWindow = XCB_WINDOW_NONE; + + auto* interface = AzFramework::XcbConnectionManagerInterface::Get(); + interface->SetEnableXInput(interface->GetXcbConnection(), false); } break; } diff --git a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h index 106d204ca9..a69a8a9ec5 100644 --- a/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h +++ b/Code/Framework/AzFramework/Platform/Common/Xcb/AzFramework/XcbInputDeviceMouse.h @@ -65,9 +65,6 @@ namespace AzFramework //! \ref AzFramework::InputDeviceMouse::Implementation::TickInputDevice void TickInputDevice() override; - //! This method is called by the Editor to accommodate some events with the Editor. Never called in Game mode. - void PollSpecialEvents() override; - //! Handle X11 events. void HandleXcbEvent(xcb_generic_event_t* event) override; @@ -77,9 +74,6 @@ namespace AzFramework //! Initialize XInput extension. Used for raw input during confinement and showing/hiding the cursor. static bool InitializeXInput(); - //! Enables/Disables XInput Raw Input events. - void SetEnableXInput(bool enable); - //! Create barriers. void CreateBarriers(xcb_window_t window, bool create); @@ -98,9 +92,6 @@ namespace AzFramework //! Handle button press/release events. void HandleButtonPressEvents(uint32_t detail, bool pressed); - //! Handle motion notify events. - void HandlePointerMotionEvents(const xcb_generic_event_t* event); - //! Will set cursor states and confinement modes. void HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState); @@ -160,7 +151,6 @@ namespace AzFramework AZ::Vector2 m_cursorHiddenPosition; AZ::Vector2 m_systemCursorPositionNormalized; - uint32_t m_systemCursorPosition[MAX_XI_RAW_AXIS]; static xcb_connection_t* s_xcbConnection; static xcb_screen_t* s_xcbScreen; @@ -171,9 +161,6 @@ namespace AzFramework //! Will be true if the xinput2 extension could be initialized. static bool m_xInputInitialized; - //! The window that had focus - xcb_window_t m_prevConstraintWindow; - //! The current window that has focus xcb_window_t m_focusWindow; diff --git a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp index c6f481b65b..a3381dabb8 100644 --- a/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp +++ b/Code/Framework/AzFramework/Platform/Mac/AzFramework/Asset/AssetSystemComponentHelper_Mac.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -24,14 +26,20 @@ namespace AzFramework::AssetSystem::Platform AZ::IO::FixedMaxPath assetProcessorPath{ executableDirectory }; // In Mac the Editor and game is within a bundle, so the path to the sibling app // has to go up from the Contents/MacOS folder the binary is in - assetProcessorPath /= "../../../AssetProcessor.app"; + assetProcessorPath /= "../../../AssetProcessor.app/Contents/MacOS/AssetProcessor"; assetProcessorPath = assetProcessorPath.LexicallyNormal(); if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { - // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. - assetProcessorPath = - AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.app"; + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + // Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure. + assetProcessorPath = AZ::IO::FixedMaxPath{ engineRoot } / installedBinariesPath / "AssetProcessor.app/Contents/MacOS/AssetProcessor"; + } + } if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str())) { @@ -39,23 +47,21 @@ namespace AzFramework::AssetSystem::Platform } } - auto fullLaunchCommand = AZ::IO::FixedMaxPathString::format(R"(open -g "%s" --args --start-hidden)", assetProcessorPath.c_str()); + AZStd::string commandLineParams; // Add the engine path to the launch command if not empty if (!engineRoot.empty()) { - fullLaunchCommand += R"( --engine-path=")"; - fullLaunchCommand += engineRoot; - fullLaunchCommand += '"'; + commandLineParams += AZStd::string::format("\"--engine-path=\"%s\"\"", engineRoot.data()); } - // Add the active project path to the launch command if not empty if (!projectPath.empty()) { - fullLaunchCommand += R"( --project-path=")"; - fullLaunchCommand += projectPath; - fullLaunchCommand += '"'; + commandLineParams += AZStd::string::format(" \"--regset=/Amazon/AzCore/Bootstrap/project_path=\"%s\"\"", projectPath.data()); } - return system(fullLaunchCommand.c_str()) == 0; + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; + processLaunchInfo.m_processExecutableString = AZStd::move(assetProcessorPath.Native()); + processLaunchInfo.m_commandlineParameters = commandLineParams; + return AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo); } } 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/AzFramework/Tests/Platform/Common/Xcb/Actions.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h index 1650ff4f8d..e86904efa3 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/Actions.h @@ -11,6 +11,13 @@ #include #include +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_0_VALUE_PARAMS()) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{}; + return value; +} ACTION_TEMPLATE(ReturnMalloc, HAS_1_TEMPLATE_PARAMS(typename, T), AND_1_VALUE_PARAMS(p0)) { @@ -25,3 +32,38 @@ ACTION_TEMPLATE(ReturnMalloc, *value = T{ p0, p1 }; return value; } +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_3_VALUE_PARAMS(p0, p1, p2)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_4_VALUE_PARAMS(p0, p1, p2, p3)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3, p4 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3, p4, p5 }; + return value; +} +ACTION_TEMPLATE(ReturnMalloc, + HAS_1_TEMPLATE_PARAMS(typename, T), + AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) { + T* value = static_cast(malloc(sizeof(T))); + *value = T{ p0, p1, p2, p3, p4, p5, p6 }; + return value; +} diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp index b15809a4c6..a19642e388 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.cpp @@ -32,6 +32,82 @@ xcb_generic_error_t* xcb_request_check(xcb_connection_t* c, xcb_void_cookie_t co { return MockXcbInterface::Instance()->xcb_request_check(c, cookie); } +const xcb_setup_t* xcb_get_setup(xcb_connection_t *c) +{ + return MockXcbInterface::Instance()->xcb_get_setup(c); +} +xcb_screen_iterator_t xcb_setup_roots_iterator(const xcb_setup_t* R) +{ + return MockXcbInterface::Instance()->xcb_setup_roots_iterator(R); +} +const xcb_query_extension_reply_t* xcb_get_extension_data(xcb_connection_t* c, xcb_extension_t* ext) +{ + return MockXcbInterface::Instance()->xcb_get_extension_data(c, ext); +} +int xcb_flush(xcb_connection_t *c) +{ + return MockXcbInterface::Instance()->xcb_flush(c); +} +xcb_query_pointer_cookie_t xcb_query_pointer(xcb_connection_t* c, xcb_window_t window) +{ + return MockXcbInterface::Instance()->xcb_query_pointer(c, window); +} +xcb_query_pointer_reply_t* xcb_query_pointer_reply(xcb_connection_t* c, xcb_query_pointer_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_query_pointer_reply(c, cookie, e); +} +xcb_get_geometry_cookie_t xcb_get_geometry(xcb_connection_t* c, xcb_drawable_t drawable) +{ + return MockXcbInterface::Instance()->xcb_get_geometry(c, drawable); +} +xcb_get_geometry_reply_t* xcb_get_geometry_reply(xcb_connection_t* c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_get_geometry_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_warp_pointer( + xcb_connection_t* c, + xcb_window_t src_window, + xcb_window_t dst_window, + int16_t src_x, + int16_t src_y, + uint16_t src_width, + uint16_t src_height, + int16_t dst_x, + int16_t dst_y) +{ + return MockXcbInterface::Instance()->xcb_warp_pointer(c, src_window, dst_window, src_x, src_y, src_width, src_height, dst_x, dst_y); +} +xcb_intern_atom_cookie_t xcb_intern_atom(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name) +{ + return MockXcbInterface::Instance()->xcb_intern_atom(c, only_if_exists, name_len, name); +} +xcb_intern_atom_reply_t* xcb_intern_atom_reply(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_intern_atom_reply(c, cookie, e); +} +xcb_get_property_cookie_t xcb_get_property( + xcb_connection_t* c, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length) +{ + return MockXcbInterface::Instance()->xcb_get_property(c, _delete, window, property, type, long_offset, long_length); +} +xcb_get_property_reply_t* xcb_get_property_reply(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_get_property_reply(c, cookie, e); +} +void* xcb_get_property_value(const xcb_get_property_reply_t* R) +{ + return MockXcbInterface::Instance()->xcb_get_property_value(R); +} +uint32_t xcb_generate_id(xcb_connection_t *c) +{ + return MockXcbInterface::Instance()->xcb_generate_id(c); +} // ---------------------------------------------------------------------------- // xcb-xkb @@ -116,4 +192,76 @@ xkb_state_component xkb_state_update_mask( state, depressed_mods, latched_mods, locked_mods, depressed_layout, latched_layout, locked_layout); } +// ---------------------------------------------------------------------------- +// xcb-xfixes +xcb_xfixes_query_version_cookie_t xcb_xfixes_query_version( + xcb_connection_t* c, uint32_t client_major_version, uint32_t client_minor_version) +{ + return MockXcbInterface::Instance()->xcb_xfixes_query_version(c, client_major_version, client_minor_version); +} +xcb_xfixes_query_version_reply_t* xcb_xfixes_query_version_reply( + xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_xfixes_query_version_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_xfixes_show_cursor_checked(xcb_connection_t* c, xcb_window_t window) +{ + return MockXcbInterface::Instance()->xcb_xfixes_show_cursor_checked(c, window); +} +xcb_void_cookie_t xcb_xfixes_hide_cursor_checked(xcb_connection_t* c, xcb_window_t window) +{ + return MockXcbInterface::Instance()->xcb_xfixes_hide_cursor_checked(c, window); +} +xcb_void_cookie_t xcb_xfixes_delete_pointer_barrier_checked(xcb_connection_t* c, xcb_xfixes_barrier_t barrier) +{ + return MockXcbInterface::Instance()->xcb_xfixes_delete_pointer_barrier_checked(c, barrier); +} +xcb_translate_coordinates_cookie_t xcb_translate_coordinates(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y) +{ + return MockXcbInterface::Instance()->xcb_translate_coordinates(c, src_window, dst_window, src_x, src_y); +} +xcb_translate_coordinates_reply_t* xcb_translate_coordinates_reply(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_translate_coordinates_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_xfixes_create_pointer_barrier_checked( + xcb_connection_t* c, + xcb_xfixes_barrier_t barrier, + xcb_window_t window, + uint16_t x1, + uint16_t y1, + uint16_t x2, + uint16_t y2, + uint32_t directions, + uint16_t num_devices, + const uint16_t* devices) +{ + return MockXcbInterface::Instance()->xcb_xfixes_create_pointer_barrier_checked(c, barrier, window, x1, y1, x2, y2, directions, num_devices, devices); +} + +// ---------------------------------------------------------------------------- +// xcb-xinput +xcb_input_xi_query_version_cookie_t xcb_input_xi_query_version(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version) +{ + return MockXcbInterface::Instance()->xcb_input_xi_query_version(c, major_version, minor_version); +} +xcb_input_xi_query_version_reply_t* xcb_input_xi_query_version_reply( + xcb_connection_t* c, xcb_input_xi_query_version_cookie_t cookie, xcb_generic_error_t** e) +{ + return MockXcbInterface::Instance()->xcb_input_xi_query_version_reply(c, cookie, e); +} +xcb_void_cookie_t xcb_input_xi_select_events( + xcb_connection_t* c, xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t* masks) +{ + return MockXcbInterface::Instance()->xcb_input_xi_select_events(c, window, num_mask, masks); +} +int xcb_input_raw_button_press_axisvalues_length (const xcb_input_raw_button_press_event_t *R) +{ + return MockXcbInterface::Instance()->xcb_input_raw_button_press_axisvalues_length(R); +} +xcb_input_fp3232_t* xcb_input_raw_button_press_axisvalues_raw(const xcb_input_raw_button_press_event_t* R) +{ + return MockXcbInterface::Instance()->xcb_input_raw_button_press_axisvalues_raw(R); +} + } diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h index b57751344e..c554993110 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/MockXcbInterface.h @@ -18,6 +18,8 @@ #undef explicit #include #include +#include +#include #include "Printers.h" @@ -62,6 +64,37 @@ public: MOCK_CONST_METHOD1(xcb_disconnect, void(xcb_connection_t* c)); MOCK_CONST_METHOD1(xcb_poll_for_event, xcb_generic_event_t*(xcb_connection_t* c)); MOCK_CONST_METHOD2(xcb_request_check, xcb_generic_error_t*(xcb_connection_t* c, xcb_void_cookie_t cookie)); + MOCK_CONST_METHOD1(xcb_get_setup, const xcb_setup_t*(xcb_connection_t *c)); + MOCK_CONST_METHOD1(xcb_setup_roots_iterator, xcb_screen_iterator_t(const xcb_setup_t* R)); + MOCK_CONST_METHOD2(xcb_get_extension_data, const xcb_query_extension_reply_t*(xcb_connection_t* c, xcb_extension_t* ext)); + MOCK_CONST_METHOD1(xcb_flush, int(xcb_connection_t *c)); + MOCK_CONST_METHOD2(xcb_query_pointer, xcb_query_pointer_cookie_t(xcb_connection_t* c, xcb_window_t window)); + MOCK_CONST_METHOD3(xcb_query_pointer_reply, xcb_query_pointer_reply_t*(xcb_connection_t* c, xcb_query_pointer_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD2(xcb_get_geometry, xcb_get_geometry_cookie_t(xcb_connection_t* c, xcb_drawable_t drawable)); + MOCK_CONST_METHOD3(xcb_get_geometry_reply, xcb_get_geometry_reply_t*(xcb_connection_t* c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD9(xcb_warp_pointer, xcb_void_cookie_t( + xcb_connection_t* c, + xcb_window_t src_window, + xcb_window_t dst_window, + int16_t src_x, + int16_t src_y, + uint16_t src_width, + uint16_t src_height, + int16_t dst_x, + int16_t dst_y)); + MOCK_CONST_METHOD4(xcb_intern_atom, xcb_intern_atom_cookie_t(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name)); + MOCK_CONST_METHOD3(xcb_intern_atom_reply, xcb_intern_atom_reply_t*(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD7(xcb_get_property, xcb_get_property_cookie_t( + xcb_connection_t* c, + uint8_t _delete, + xcb_window_t window, + xcb_atom_t property, + xcb_atom_t type, + uint32_t long_offset, + uint32_t long_length)); + MOCK_CONST_METHOD3(xcb_get_property_reply, xcb_get_property_reply_t*(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD1(xcb_get_property_value, void*(const xcb_get_property_reply_t* R)); + MOCK_CONST_METHOD1(xcb_generate_id, uint32_t(xcb_connection_t *c)); // xcb-xkb MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor)); @@ -83,6 +116,33 @@ public: MOCK_CONST_METHOD4(xkb_state_key_get_utf8, int(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size)); MOCK_CONST_METHOD7(xkb_state_update_mask, xkb_state_component(xkb_state* state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout)); + // xcb-xfixes + MOCK_CONST_METHOD3(xcb_xfixes_query_version, xcb_xfixes_query_version_cookie_t(xcb_connection_t* c, uint32_t client_major_version, uint32_t client_minor_version)); + MOCK_CONST_METHOD3(xcb_xfixes_query_version_reply, xcb_xfixes_query_version_reply_t*(xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD2(xcb_xfixes_show_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window)); + MOCK_CONST_METHOD2(xcb_xfixes_hide_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window)); + MOCK_CONST_METHOD2(xcb_xfixes_delete_pointer_barrier_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_xfixes_barrier_t barrier)); + MOCK_CONST_METHOD5(xcb_translate_coordinates, xcb_translate_coordinates_cookie_t(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y)); + MOCK_CONST_METHOD3(xcb_translate_coordinates_reply, xcb_translate_coordinates_reply_t*(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD10(xcb_xfixes_create_pointer_barrier_checked, xcb_void_cookie_t( + xcb_connection_t* c, + xcb_xfixes_barrier_t barrier, + xcb_window_t window, + uint16_t x1, + uint16_t y1, + uint16_t x2, + uint16_t y2, + uint32_t directions, + uint16_t num_devices, + const uint16_t* devices)); + + // xcb-xinput + MOCK_CONST_METHOD3(xcb_input_xi_query_version, xcb_input_xi_query_version_cookie_t(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version)); + MOCK_CONST_METHOD3(xcb_input_xi_query_version_reply, xcb_input_xi_query_version_reply_t*(xcb_connection_t* c, xcb_input_xi_query_version_cookie_t cookie, xcb_generic_error_t** e)); + MOCK_CONST_METHOD4(xcb_input_xi_select_events, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t* masks)); + MOCK_CONST_METHOD1(xcb_input_raw_button_press_axisvalues_length, int(const xcb_input_raw_button_press_event_t* R)); + MOCK_CONST_METHOD1(xcb_input_raw_button_press_axisvalues_raw, xcb_input_fp3232_t*(const xcb_input_raw_button_press_event_t* R)); + private: static inline MockXcbInterface* self = nullptr; }; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h index 8e9b008fc1..2a63a5f158 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbBaseTestFixture.h @@ -22,6 +22,12 @@ namespace AzFramework public: void SetUp() override; + template + static xcb_generic_event_t MakeEvent(T event) + { + return *reinterpret_cast(&event); + } + protected: testing::NiceMock m_interface; xcb_connection_t m_connection{}; diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp index 76d00eda50..7209875235 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceKeyboardTests.cpp @@ -21,12 +21,6 @@ #include "XcbBaseTestFixture.h" #include "XcbTestApplication.h" -template -xcb_generic_event_t MakeEvent(T event) -{ - return *reinterpret_cast(&event); -} - namespace AzFramework { // Sets up default behavior for mock keyboard responses to xcb methods diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp new file mode 100644 index 0000000000..05784462d7 --- /dev/null +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/XcbInputDeviceMouseTests.cpp @@ -0,0 +1,545 @@ +/* + * 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 + +#include + +#include +#include + +#include "XcbBaseTestFixture.h" +#include "XcbTestApplication.h" +#include "Matchers.h" +#include "Actions.h" + +namespace AzFramework +{ + // Sets up default behavior for mock keyboard responses to xcb methods + class XcbInputDeviceMouseTests + : public XcbBaseTestFixture + { + public: + void SetUp() override + { + using testing::Eq; + using testing::Field; + using testing::Return; + using testing::StrEq; + using testing::_; + + XcbBaseTestFixture::SetUp(); + + ON_CALL(m_interface, xcb_get_setup(&m_connection)) + .WillByDefault(Return(&s_xcbSetup)); + ON_CALL(m_interface, xcb_setup_roots_iterator(&s_xcbSetup)) + .WillByDefault(Return(xcb_screen_iterator_t{&s_xcbScreen})); + + ON_CALL(m_interface, xcb_get_extension_data(&m_connection, &xcb_xfixes_id)) + .WillByDefault(Return(&s_xfixesExtensionReply)); + ON_CALL(m_interface, xcb_xfixes_query_version_reply(&m_connection, _, _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_XFIXES_QUERY_VERSION, + /*pad0=*/(uint8_t)0, + /*sequence=*/(uint16_t)1, + /*length=*/0u, + /*major_version=*/5u, + /*minor_version=*/0u + )); + + ON_CALL(m_interface, xcb_get_extension_data(&m_connection, &xcb_input_id)) + .WillByDefault(Return(&s_xfixesExtensionReply)); + ON_CALL(m_interface, xcb_input_xi_query_version_reply(&m_connection, _, _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_INPUT_XI_QUERY_VERSION, + /*pad0=*/(uint8_t)0, + /*sequence=*/(uint16_t)1, + /*length=*/0u, + /*major_version=*/(uint16_t)2, + /*minor_version=*/(uint16_t)2 + )); + + // Set the default focus window + EXPECT_CALL(m_interface, xcb_intern_atom(&m_connection, 1, 18, StrEq("_NET_ACTIVE_WINDOW"))) + .WillRepeatedly(Return(xcb_intern_atom_cookie_t{/*.sequence=*/ 1})); + ON_CALL(m_interface, xcb_intern_atom_reply(&m_connection, Field(&xcb_intern_atom_cookie_t::sequence, Eq(1)), _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_INTERN_ATOM, + /*pad0=*/(uint8_t)0, + /*sequence=*/(uint16_t)1, + /*length=*/0u, + /*xcb_atom_t=*/s_netActiveWindowAtom + )); + ON_CALL(m_interface, xcb_get_property(&m_connection, 0, s_rootWindow, s_netActiveWindowAtom, XCB_ATOM_WINDOW, 0, 1)) + .WillByDefault(Return(xcb_get_property_cookie_t{/*.sequence=*/ s_getActiveWindowPropertySequence})); + ON_CALL(m_interface, xcb_get_property_reply(&m_connection, Field(&xcb_get_property_cookie_t::sequence, Eq(s_getActiveWindowPropertySequence)), _)) + .WillByDefault(ReturnMalloc( + /*response_type=*/(uint8_t)XCB_GET_PROPERTY, + /*format=*/(uint8_t)0, + /*sequence=*/(uint16_t)s_getActiveWindowPropertySequence, + /*length=*/0u, + /*type=*/XCB_ATOM_WINDOW, + /*bytes_after=*/0u, + /*value_len=*/1u + )); + ON_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence)))) + .WillByDefault(Return(const_cast(&s_nullWindow))); + + ON_CALL(m_interface, xcb_get_geometry(&m_connection, _)) + .WillByDefault(Return(xcb_get_geometry_cookie_t{/*.sequence=*/1})); + ON_CALL(m_interface, xcb_get_geometry_reply(&m_connection, Field(&xcb_get_geometry_cookie_t::sequence, Eq(1)), _)) + .WillByDefault(ReturnMalloc(s_defaultWindowGeometry)); + } + + void PumpApplication() + { + m_application.PumpSystemEventLoopUntilEmpty(); + m_application.TickSystem(); + m_application.Tick(); + } + + protected: + static constexpr inline uint8_t s_xinputMajorOpcode = 131; + static constexpr inline xcb_window_t s_rootWindow = 1; + static constexpr inline xcb_window_t s_nullWindow = XCB_WINDOW_NONE; + static constexpr inline xcb_input_device_id_t s_virtualCorePointerId = 2; + static constexpr inline xcb_input_device_id_t s_physicalPointerDeviceId = 3; + static constexpr inline uint16_t s_screenWidthInPixels = 3840; + static constexpr inline uint16_t s_screenHeightInPixels = 2160; + static constexpr inline uint16_t s_getActiveWindowPropertySequence = 2160; + static constexpr inline xcb_atom_t s_netActiveWindowAtom = 1; + static constexpr inline xcb_setup_t s_xcbSetup{ + /*.status=*/1, + /*.pad0=*/0, + /*.protocol_major_version=*/11, + /*.protocol_minor_version=*/0, + }; + static inline xcb_screen_t s_xcbScreen{ + /*.root=*/s_rootWindow, + /*.default_colormap=*/32, + /*.white_pixel=*/16777215, + /*.black_pixel=*/0, + /*.current_input_masks=*/0, + /*.width_in_pixels=*/s_screenWidthInPixels, + /*.height_in_pixels=*/s_screenHeightInPixels, + /*.width_in_millimeters=*/602, + /*.height_in_millimeters=*/341, + }; + static constexpr inline xcb_query_extension_reply_t s_xfixesExtensionReply{ + /*.response_type=*/XCB_QUERY_EXTENSION, + /*.pad0=*/0, + /*.sequence=*/1, + /*.length=*/0, + /*.present=*/1, + }; + static constexpr inline xcb_query_extension_reply_t s_xinputExtensionReply{ + /*.response_type=*/XCB_QUERY_EXTENSION, + /*.pad0=*/0, + /*.sequence=*/1, + /*.length=*/0, + /*.present=*/1, + /*.major_opcode=*/s_xinputMajorOpcode, + }; + static constexpr inline xcb_get_geometry_reply_t s_defaultWindowGeometry{ + /*.response_type=*/XCB_GET_GEOMETRY, + /*.depth=*/0, + /*.sequence=*/1, + /*.length=*/0, + /*.root=*/s_rootWindow, + /*.x=*/100, + /*.y=*/100, + /*.width=*/100, + /*.height=*/100, + /*.border_width=*/3, + /*.pad0[2]=*/{}, + }; + XcbTestApplication m_application{ + /*enabledGamepadsCount=*/0, + /*keyboardEnabled=*/false, + /*motionEnabled=*/false, + /*mouseEnabled=*/true, + /*touchEnabled=*/false, + /*virtualKeyboardEnabled=*/false + }; + }; + + struct MouseButtonTestData + { + xcb_button_index_t m_button; + }; + + class XcbInputDeviceMouseButtonTests + : public XcbInputDeviceMouseTests + , public testing::WithParamInterface + { + public: + static InputChannelId GetInputChannelIdForButton(const xcb_button_index_t button) + { + switch (button) + { + case XCB_BUTTON_INDEX_1: + return InputDeviceMouse::Button::Left; + case XCB_BUTTON_INDEX_2: + return InputDeviceMouse::Button::Right; + case XCB_BUTTON_INDEX_3: + return InputDeviceMouse::Button::Middle; + } + return InputChannelId{}; + } + + AZStd::array GetIdleChannelIdsForButton(const xcb_button_index_t button) + { + switch (button) + { + case XCB_BUTTON_INDEX_1: + return { InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_2: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_3: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_4: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other2 }; + case XCB_BUTTON_INDEX_5: + return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1 }; + } + return AZStd::array(); + } + }; + + TEST_P(XcbInputDeviceMouseButtonTests, ButtonInputChannelsUpdateStateFromXcbEvents) + { + using testing::Each; + using testing::Eq; + using testing::NotNull; + using testing::Property; + using testing::Return; + + // Set the expectations for the events that will be generated + // nullptr entries represent when the event queue is empty, and will cause + // PumpSystemEventLoopUntilEmpty to return + // + // Event pointers are freed by the calling code, so these actions + // malloc new copies + // + // The xcb mouse does not react to the `XCB_BUTTON_PRESS` / + // `XCB_BUTTON_RELEASE` events, but it will still receive those events + // from the X server. + EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_input_raw_button_press_event_t{ + /*response_type=*/XCB_GE_GENERIC, + /*extension=*/s_xinputMajorOpcode, + /*sequence=*/4, + /*length=*/2, + /*event_type=*/XCB_INPUT_RAW_BUTTON_PRESS, + /*deviceid=*/s_virtualCorePointerId, + /*time=*/3984920, + /*detail=*/GetParam().m_button, + /*sourceid=*/s_physicalPointerDeviceId, + /*valuators_len=*/2, + /*flags=*/0, + /*pad0[4]=*/{}, + /*full_sequence=*/4 + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_button_press_event_t{ + /*response_type=*/XCB_BUTTON_PRESS, + /*detail=*/static_cast(GetParam().m_button), + /*sequence=*/4, + /*time=*/3984920, + /*root=*/s_rootWindow, + /*event=*/119537664, + /*child=*/0, + /*root_x=*/55, + /*root_y=*/1099, + /*event_x=*/55, + /*event_y=*/55, + /*state=*/0, + /*same_screen=*/1 + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_input_raw_button_release_event_t{ + /*response_type=*/XCB_GE_GENERIC, + /*extension=*/s_xinputMajorOpcode, + /*sequence=*/4, + /*length=*/2, + /*event_type=*/XCB_INPUT_RAW_BUTTON_RELEASE, + /*deviceid=*/s_virtualCorePointerId, + /*time=*/3984964, + /*detail=*/GetParam().m_button, + /*sourceid=*/s_physicalPointerDeviceId, + /*valuators_len=*/2, + /*flags=*/0, + /*pad0[4]=*/{}, + /*full_sequence=*/4 + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_button_release_event_t{ + /*response_type=*/XCB_BUTTON_RELEASE, + /*detail=*/static_cast(GetParam().m_button), + /*sequence=*/4, + /*time=*/3984964, + /*root=*/s_rootWindow, + /*event=*/119537664, + /*child=*/0, + /*root_x=*/55, + /*root_y=*/1099, + /*event_x=*/55, + /*event_y=*/55, + /*state=*/XCB_KEY_BUT_MASK_BUTTON_1, + /*same_screen=*/1 + }))) + .WillOnce(Return(nullptr)) + ; + + m_application.Start(); + InputSystemCursorRequestBus::Event( + InputDeviceMouse::Id, + &InputSystemCursorRequests::SetSystemCursorState, + SystemCursorState::ConstrainedAndHidden); + + const InputChannel* activeButtonChannel = InputChannelRequests::FindInputChannel(GetInputChannelIdForButton(GetParam().m_button)); + const auto inactiveButtonChannels = [this]() + { + const auto inactiveButtonChannelIds = GetIdleChannelIdsForButton(GetParam().m_button); + AZStd::array channels{}; + AZStd::transform(begin(inactiveButtonChannelIds), end(inactiveButtonChannelIds), begin(channels), [](const InputChannelId& id) + { + return InputChannelRequests::FindInputChannel(id); + }); + return channels; + }(); + + ASSERT_TRUE(activeButtonChannel); + ASSERT_THAT(inactiveButtonChannels, Each(NotNull())); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Began)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Updated)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Ended)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + + PumpApplication(); + + EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle)))); + } + + INSTANTIATE_TEST_CASE_P( + AllButtons, + XcbInputDeviceMouseButtonTests, + testing::Values( + MouseButtonTestData{ XCB_BUTTON_INDEX_1 }, + MouseButtonTestData{ XCB_BUTTON_INDEX_2 }, + MouseButtonTestData{ XCB_BUTTON_INDEX_3 } + // XCB_BUTTON_INDEX_4 and XCB_BUTTON_INDEX_5 map to positive and + // negative scroll wheel events, which are handled as motion events + ) + ); + + TEST_F(XcbInputDeviceMouseTests, MovementInputChannelsUpdateStateFromXcbEvents) + { + using testing::Each; + using testing::Eq; + using testing::FloatEq; + using testing::NotNull; + using testing::Property; + using testing::Return; + + // Set the expectations for the events that will be generated + // nullptr entries represent when the event queue is empty, and will cause + // PumpSystemEventLoopUntilEmpty to return + // + // Event pointers are freed by the calling code, so these actions + // malloc new copies + // + // The xcb mouse does not react to the `XCB_MOTION_NOTIFY` event, but + // it will still receive it from the X server. + EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_input_raw_motion_event_t{ + /*response_type=*/XCB_GE_GENERIC, + /*extension=*/s_xinputMajorOpcode, + /*sequence=*/5, + /*length=*/10, + /*event_type=*/XCB_INPUT_RAW_MOTION, + /*deviceid=*/s_virtualCorePointerId, + /*time=*/0, // use the time value to identify each event + /*detail=*/XCB_MOTION_NORMAL, + /*sourceid=*/s_physicalPointerDeviceId, + /*valuators_len=*/2, // number of axes that have values for this event + /*flags=*/0, + /*pad0[4]=*/{}, + /*full_sequence=*/5, + }))) + .WillOnce(Return(nullptr)) + .WillOnce(ReturnMalloc(MakeEvent(xcb_motion_notify_event_t{ + /*response_type=*/XCB_MOTION_NOTIFY, + /*detail=*/XCB_MOTION_NORMAL, + /*sequence=*/5, + /*time=*/1, // use the time value to identify each event + /*root=*/s_rootWindow, + /*event=*/127926272, + /*child=*/0, + /*root_x=*/95, + /*root_y=*/1079, + /*event_x=*/95, + /*event_y=*/20, + /*state=*/0, + /*same_screen=*/1, + }))) + .WillOnce(Return(nullptr)) + ; + + AZStd::array axisValues + { + xcb_input_fp3232_t{ /*.integral=*/ 1, /*.fraction=*/0 }, // x motion + xcb_input_fp3232_t{ /*.integral=*/ 2, /*.fraction=*/0 } // y motion + }; + + EXPECT_CALL(m_interface, xcb_input_raw_button_press_axisvalues_length(testing::Field(&xcb_input_raw_button_press_event_t::time, 0))) + .WillRepeatedly(testing::Return(2)); // x and y axis + EXPECT_CALL(m_interface, xcb_input_raw_button_press_axisvalues_raw(testing::Field(&xcb_input_raw_button_press_event_t::time, 0))) + .WillRepeatedly(testing::Return(axisValues.data())); // x and y axis + + m_application.Start(); + InputSystemCursorRequestBus::Event( + InputDeviceMouse::Id, + &InputSystemCursorRequests::SetSystemCursorState, + SystemCursorState::ConstrainedAndHidden); + + const InputChannel* xMotionChannel = InputChannelRequests::FindInputChannel(InputDeviceMouse::Movement::X); + const InputChannel* yMotionChannel = InputChannelRequests::FindInputChannel(InputDeviceMouse::Movement::Y); + ASSERT_TRUE(xMotionChannel); + ASSERT_TRUE(yMotionChannel); + + EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Idle)); + EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f)); + EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f)); + + PumpApplication(); + + EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Began)); + EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Began)); + EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(1.0f)); + EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(2.0f)); + + PumpApplication(); + + EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Ended)); + EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Ended)); + EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f)); + EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f)); + } + + struct GetCursorPositionParam + { + int16_t m_x; + int16_t m_y; + }; + + class XcbGetSystemCursorPositionTests + : public XcbInputDeviceMouseTests + , public testing::WithParamInterface + { + }; + + TEST_P(XcbGetSystemCursorPositionTests, GetSystemCursorPositionNormalizedReturnsCorrectValue) + { + using testing::Eq; + using testing::Field; + using testing::Return; + using testing::_; + + xcb_window_t focusWindow = 42; + const xcb_query_pointer_reply_t queryPointerReply{ + /*.response_type=*/XCB_QUERY_POINTER, + /*.same_screen=*/1, + /*.sequence=*/0, + /*.length=*/1, + /*.root=*/s_rootWindow, + /*.child=*/focusWindow, + /*.root_x=*/static_cast(GetParam().m_x + s_defaultWindowGeometry.x), + /*.root_y=*/static_cast(GetParam().m_y + s_defaultWindowGeometry.y), + /*.win_x=*/GetParam().m_x, + /*.win_y=*/GetParam().m_y, + /*.mask=*/{}, + /*.pad0[2]=*/{}, + }; + + // Querying the root window's pointer gives its absolute value + const xcb_query_pointer_reply_t rootWindowQueryPointerReply{ + /*.response_type=*/XCB_QUERY_POINTER, + /*.same_screen=*/1, + /*.sequence=*/0, + /*.length=*/1, + /*.root=*/s_rootWindow, + /*.child=*/s_rootWindow, + /*.root_x=*/static_cast(GetParam().m_x + s_defaultWindowGeometry.x), + /*.root_y=*/static_cast(GetParam().m_y + s_defaultWindowGeometry.y), + /*.win_x=*/static_cast(GetParam().m_x + s_defaultWindowGeometry.x), + /*.win_y=*/static_cast(GetParam().m_y + s_defaultWindowGeometry.y), + /*.mask=*/{}, + /*.pad0[2]=*/{}, + }; + + EXPECT_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence)))) + .WillRepeatedly(Return(&focusWindow)); + + EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, focusWindow)) + .WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/1})); + EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 1), _)) + .WillRepeatedly(ReturnMalloc(queryPointerReply)); + + EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, s_rootWindow)) + .WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/2})); + EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 2), _)) + .WillRepeatedly(ReturnMalloc(rootWindowQueryPointerReply)); + + m_application.Start(); + InputSystemCursorRequestBus::Event( + InputDeviceMouse::Id, + &InputSystemCursorRequests::SetSystemCursorState, + SystemCursorState::ConstrainedAndHidden); + + AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero(); + InputSystemCursorRequestBus::EventResult( + systemCursorPositionNormalized, + InputDeviceMouse::Id, + &InputSystemCursorRequests::GetSystemCursorPositionNormalized); + + EXPECT_THAT(systemCursorPositionNormalized, ::testing::AllOf( + testing::Property(&AZ::Vector2::GetX, testing::FloatEq(static_cast(GetParam().m_x) / s_defaultWindowGeometry.width)), + testing::Property(&AZ::Vector2::GetY, testing::FloatEq(static_cast(GetParam().m_y) / s_defaultWindowGeometry.height)) + )); + } + + INSTANTIATE_TEST_CASE_P( + AllPointerPositions, + XcbGetSystemCursorPositionTests, + testing::Values( + // Default mocked window geometry sets width and height to 100, all + // parameter values should be within [0, 100) + GetCursorPositionParam{ 50, 50 }, + GetCursorPositionParam{ 25, 25 }, + GetCursorPositionParam{ 0, 100 } + ) + ); +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake index 147fd2bfe1..7da00fa18c 100644 --- a/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake +++ b/Code/Framework/AzFramework/Tests/Platform/Common/Xcb/azframework_xcb_tests_files.cmake @@ -17,5 +17,6 @@ set(FILES XcbBaseTestFixture.cpp XcbBaseTestFixture.h XcbInputDeviceKeyboardTests.cpp + XcbInputDeviceMouseTests.cpp XcbTestApplication.h ) 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/UI/Outliner/EntityOutlinerListModel.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp index 13ec27c1b8..a68a72f00a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Outliner/EntityOutlinerListModel.cpp @@ -2180,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/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index 951b876347..aa6d82e634 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -151,6 +152,7 @@ namespace AzToolsFramework PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); + EditorEntityContextNotificationBus::Handler::BusConnect(); InitializeShortcuts(); } @@ -159,6 +161,7 @@ namespace AzToolsFramework { UninitializeShortcuts(); + EditorEntityContextNotificationBus::Handler::BusDisconnect(); AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); @@ -423,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 diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index a8d325c4cf..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; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp index 8bd1b7db04..8b56b26508 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.cpp @@ -185,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) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h index 3627449ab4..bb1c646dbe 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabUiHandler.h @@ -36,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; 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 expectedAssetIds{ m_assets[1], m_assets[2], m_assets[3], m_assets[4] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -462,7 +460,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[5] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -493,7 +491,7 @@ namespace UnitTest EXPECT_EQ(assetFileInfoList.m_fileInfoList.size(), 5); - // 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] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -601,7 +599,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] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) @@ -625,12 +623,12 @@ namespace UnitTest AssetFileInfoListComparison::ComparisonData filePatternComparisonData(AssetFileInfoListComparison::ComparisonType::FilePattern,"$1", "Asset[0-3].txt", AssetFileInfoListComparison::FilePatternType::Regex); filePatternComparisonData.m_firstInput = TempFiles[FileIndex::FirstAssetFileInfoList]; assetFileInfoListComparison.AddComparisonStep(filePatternComparisonData); - + AzToolsFramework::AssetFileInfoListComparison::ComparisonData deltaComparisonData(AzToolsFramework::AssetFileInfoListComparison::ComparisonType::Delta, TempFiles[FileIndex::ResultAssetFileInfoList]); deltaComparisonData.m_firstInput = "$1"; deltaComparisonData.m_secondInput = TempFiles[FileIndex::SecondAssetFileInfoList]; assetFileInfoListComparison.AddComparisonStep(deltaComparisonData); - + ASSERT_TRUE(assetFileInfoListComparison.CompareAndSaveResults().IsSuccess()) << "Multiple Comparison Operation( FilePattern + Delta ) failed.\n"; // Output of the FilePattern Operation should be {0,1,2,3} // Output of the Delta Operation should be {2*,4*,5} @@ -666,7 +664,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) @@ -738,7 +736,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[4], m_assets[5] }; for (const AzToolsFramework::AssetFileInfo& assetFileInfo : assetFileInfoList.m_fileInfoList) diff --git a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp index 827737f561..f04a0642d1 100644 --- a/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp +++ b/Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp @@ -63,10 +63,8 @@ namespace UnitTest ArgumentContainer argContainer{ {} }; // Append Command Line override for the Project Cache Path - AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; - auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); - auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; - argContainer.push_back(projectCachePathOverride.data()); + auto cacheProjectRootFolder = AZ::IO::Path{ m_tempDir.GetDirectory() } / "Cache"; + auto projectPathOverride = FixedValueString::format(R"(--project-path="%s")", m_tempDir.GetDirectory()); argContainer.push_back(projectPathOverride.data()); m_application = new ToolsTestApplication("AssetSeedManagerTest", aznumeric_caster(argContainer.size()), argContainer.data()); m_assetSeedManager = new AzToolsFramework::AssetSeedManager(); diff --git a/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp b/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp index e416efc5c6..9d6c8a4bff 100644 --- a/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp +++ b/Code/Framework/AzToolsFramework/Tests/ComponentAddRemove.cpp @@ -572,7 +572,9 @@ 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); AzFramework::Application::Descriptor descriptor; diff --git a/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp b/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp index 54132b974e..dbe3963069 100644 --- a/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp +++ b/Code/Framework/AzToolsFramework/Tests/GenericComponentWrapperTest.cpp @@ -59,7 +59,9 @@ protected: 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(AZ::ComponentApplication::Descriptor()); @@ -184,7 +186,9 @@ public: 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(AzFramework::Application::Descriptor()); diff --git a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp index 9c226cbb1b..e58e347b2a 100644 --- a/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp @@ -44,10 +44,8 @@ namespace UnitTest ArgumentContainer argContainer{ {} }; // Append Command Line override for the Project Cache Path - AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; - auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); - auto projectPathOverride = FixedValueString{ R"(--project-path=AutomatedTesting)" }; - argContainer.push_back(projectCachePathOverride.data()); + auto cacheProjectRootFolder = AZ::IO::Path{ m_tempDir.GetDirectory() } / "Cache"; + auto projectPathOverride = FixedValueString::format(R"(--project-path="%s")", m_tempDir.GetDirectory()); argContainer.push_back(projectPathOverride.data()); m_application = new ToolsTestApplication("AddressedAssetCatalogManager", aznumeric_caster(argContainer.size()), argContainer.data()); @@ -195,10 +193,7 @@ namespace UnitTest ArgumentContainer argContainer{ {} }; // Append Command Line override for the Project Cache Path - AZ::IO::Path cacheProjectRootFolder{ m_tempDir.GetDirectory() }; - auto projectCachePathOverride = FixedValueString::format(R"(--project-cache-path="%s")", cacheProjectRootFolder.c_str()); - 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("MessageTest", aznumeric_caster(argContainer.size()), argContainer.data()); diff --git a/Code/Framework/AzToolsFramework/Tests/Slices.cpp b/Code/Framework/AzToolsFramework/Tests/Slices.cpp index 1e849de620..9b546357cd 100644 --- a/Code/Framework/AzToolsFramework/Tests/Slices.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Slices.cpp @@ -1059,7 +1059,9 @@ 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(AzFramework::Application::Descriptor()); diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 0ef9cdfc3b..0f832ff1a5 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -664,6 +665,8 @@ namespace O3DELauncher systemInitParams.pSystem = CreateSystemInterface(systemInitParams); #endif // !defined(AZ_MONOLITHIC_BUILD) + AZ::ComponentApplicationLifecycle::SignalEvent(*settingsRegistry, "LegacySystemInterfaceCreated", R"({})"); + ReturnCode status = ReturnCode::Success; if (systemInitParams.pSystem) diff --git a/Code/Legacy/CrySystem/IDebugCallStack.cpp b/Code/Legacy/CrySystem/IDebugCallStack.cpp index deb9c11581..a02658d34a 100644 --- a/Code/Legacy/CrySystem/IDebugCallStack.cpp +++ b/Code/Legacy/CrySystem/IDebugCallStack.cpp @@ -242,7 +242,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...) va_end(ArgList); AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle; - AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle); + AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\error.log", AZ::IO::GetOpenModeFromStringMode("a+t"), fileHandle); if (fileHandle != AZ::IO::InvalidHandle) { AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, szBuffer, strlen(szBuffer)); @@ -254,7 +254,7 @@ void IDebugCallStack::WriteLineToLog(const char* format, ...) ////////////////////////////////////////////////////////////////////////// void IDebugCallStack::StartMemLog() { - AZ::IO::FileIOBase::GetDirectInstance()->Open("@Log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle); + AZ::IO::FileIOBase::GetDirectInstance()->Open("@log@\\memallocfile.log", AZ::IO::OpenMode::ModeWrite, m_memAllocFileHandle); assert(m_memAllocFileHandle != AZ::IO::InvalidHandle); } diff --git a/Code/Legacy/CrySystem/System.h b/Code/Legacy/CrySystem/System.h index 015b09a69b..a3a0f12278 100644 --- a/Code/Legacy/CrySystem/System.h +++ b/Code/Legacy/CrySystem/System.h @@ -590,7 +590,7 @@ public: bool InitVTuneProfiler(); - void OpenBasicPaks(); + void OpenPlatformPaks(); void OpenLanguagePak(const char* sLanguage); void OpenLanguageAudioPak(const char* sLanguage); void GetLocalizedPath(const char* sLanguage, AZStd::string& sLocalizedPath); diff --git a/Code/Legacy/CrySystem/SystemInit.cpp b/Code/Legacy/CrySystem/SystemInit.cpp index ee1c498a9a..ac0683cf1b 100644 --- a/Code/Legacy/CrySystem/SystemInit.cpp +++ b/Code/Legacy/CrySystem/SystemInit.cpp @@ -649,7 +649,7 @@ bool CSystem::InitFileSystem_LoadEngineFolders(const SSystemInitParams&) auto projectName = AZ::Utils::GetProjectName(); AZ_Printf(AZ_TRACE_SYSTEM_WINDOW, "Project Name: %s\n", projectName.empty() ? "None specified" : projectName.c_str()); - OpenBasicPaks(); + OpenPlatformPaks(); // Load game-specific folder. LoadConfiguration("game.cfg"); @@ -786,29 +786,19 @@ void CSystem::InitLocalization() OpenLanguageAudioPak(language.c_str()); } -void CSystem::OpenBasicPaks() +void CSystem::OpenPlatformPaks() { - static bool bBasicPaksLoaded = false; - if (bBasicPaksLoaded) + static bool bPlatformPaksLoaded = false; + if (bPlatformPaksLoaded) { return; } - bBasicPaksLoaded = true; - - // open pak files - constexpr AZStd::string_view paksFolder = "@products@/*.pak"; // (@products@ assumed) - m_env.pCryPak->OpenPacks(paksFolder); - - InlineInitializationProcessing("CSystem::OpenBasicPaks OpenPacks( paksFolder.c_str() )"); + bPlatformPaksLoaded = true; ////////////////////////////////////////////////////////////////////////// // Open engine packs ////////////////////////////////////////////////////////////////////////// - const char* const assetsDir = "@products@"; - - // After game paks to have same search order as with files on disk - m_env.pCryPak->OpenPack(assetsDir, "engine.pak"); #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION SYSTEMINIT_CPP_SECTION_15 @@ -816,6 +806,7 @@ void CSystem::OpenBasicPaks() #endif #ifdef AZ_PLATFORM_ANDROID + const char* const assetsDir = "@products@"; // Load Android Obb files if available const char* obbStorage = AZ::Android::Utils::GetObbStoragePath(); AZStd::string mainObbPath = AZStd::move(AZStd::string::format("%s/%s", obbStorage, AZ::Android::Utils::GetObbFileName(true))); @@ -824,7 +815,7 @@ void CSystem::OpenBasicPaks() m_env.pCryPak->OpenPack(assetsDir, patchObbPath.c_str()); #endif //AZ_PLATFORM_ANDROID - InlineInitializationProcessing("CSystem::OpenBasicPaks OpenPacks( Engine... )"); + InlineInitializationProcessing("CSystem::OpenPlatformPaks OpenPacks( Engine... )"); } ////////////////////////////////////////////////////////////////////////// @@ -1328,7 +1319,7 @@ AZ_POP_DISABLE_WARNING ////////////////////////////////////////////////////////////////////////// // Open basic pak files after intro movie playback started ////////////////////////////////////////////////////////////////////////// - OpenBasicPaks(); + OpenPlatformPaks(); ////////////////////////////////////////////////////////////////////////// // AUDIO diff --git a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp index 0d915dcc49..07eae67a81 100644 --- a/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp +++ b/Code/Tools/AssetBundler/tests/applicationManagerTests.cpp @@ -66,7 +66,9 @@ namespace AssetBundler } 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); diff --git a/Code/Tools/AssetBundler/tests/tests_main.cpp b/Code/Tools/AssetBundler/tests/tests_main.cpp index 21a6bf8a6f..86432d730e 100644 --- a/Code/Tools/AssetBundler/tests/tests_main.cpp +++ b/Code/Tools/AssetBundler/tests/tests_main.cpp @@ -106,7 +106,9 @@ namespace AssetBundler } 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); AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath(); diff --git a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp index c65ab24aed..305578219f 100644 --- a/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp +++ b/Code/Tools/AssetProcessor/native/InternalBuilders/SettingsRegistryBuilder.cpp @@ -159,6 +159,7 @@ namespace AssetProcessor builderDesc.m_busId = m_builderId; builderDesc.m_createJobFunction = AZStd::bind(&SettingsRegistryBuilder::CreateJobs, this, AZStd::placeholders::_1, AZStd::placeholders::_2); builderDesc.m_processJobFunction = AZStd::bind(&SettingsRegistryBuilder::ProcessJob, this, AZStd::placeholders::_1, AZStd::placeholders::_2); + builderDesc.m_version = 1; AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDesc); @@ -259,6 +260,11 @@ namespace AssetProcessor scratchBuffer.reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer; AZStd::fixed_vector platformCodes; AzFramework::PlatformHelper::AppendPlatformCodeNames(platformCodes, request.m_platformInfo.m_identifier); + AZ_Assert(platformCodes.size() <= 1, "A one-to-one mapping of asset type platform identifier" + " to platform codename is required in the SettingsRegistryBuilder." + " The bootstrap.game is now only produced per build configuration and doesn't take into account" + " different platforms names"); + const AZStd::string& assetPlatformIdentifier = request.m_jobDescription.GetPlatformIdentifier(); // Determines the suffix that will be used for the launcher based on processing server vs non-server assets const char* launcherType = assetPlatformIdentifier != AzFramework::PlatformHelper::GetPlatformName(AzFramework::PlatformId::SERVER) @@ -293,9 +299,9 @@ namespace AssetProcessor outputBuffer.Reserve(512 * 1024); // Reserve 512kb to avoid repeatedly resizing the buffer; SettingsExporter exporter(outputBuffer, excludes); - for (AZStd::string_view platform : platformCodes) + if (!platformCodes.empty()) { - AZ::u32 productSubID = static_cast(AZStd::hash{}(platform)); // Deliberately ignoring half the bits. + AZStd::string_view platform = platformCodes.front(); for (size_t i = 0; i < AZStd::size(specializations); ++i) { const AZ::SettingsRegistryInterface::Specializations& specialization = specializations[i]; @@ -337,7 +343,7 @@ namespace AssetProcessor // The purpose of this section is to copy the Gem's SourcePaths from the Global Settings Registry // the local SettingsRegistry. The reason this is needed is so that the call to // `MergeSettingsToRegistry_GemRegistries` below is able to locate each gem's "/Registry" folder - // that will be merged into the bootstrap.game...setreg file + // that will be merged into the bootstrap.game..setreg file // This is used by the GameLauncher applications to read from a single merged .setreg file // containing the settings needed to run a game/simulation without have access to the source code base registry AZStd::vector gemInfos; @@ -407,9 +413,8 @@ namespace AssetProcessor return; } - outputPath += specialization.GetSpecialization(0); // Append configuration - outputPath += '.'; - outputPath += platform; + AZStd::string_view specializationString(specialization.GetSpecialization(0)); + outputPath += specializationString; // Append configuration outputPath += ".setreg"; AZ::IO::SystemFile file; @@ -426,7 +431,11 @@ namespace AssetProcessor } file.Close(); - response.m_outputProducts.emplace_back(outputPath, m_assetType, productSubID + aznumeric_cast(i)); + const AZ::u32 hashedSpecialization = static_cast(AZStd::hash{}(specializationString)); + AZ_Assert(hashedSpecialization != 0, "Product ID generation failed for specialization %.*s." + " This can result in a product ID collision with other builders for this asset.", + AZ_STRING_ARG(specializationString)); + response.m_outputProducts.emplace_back(outputPath, m_assetType, hashedSpecialization); response.m_outputProducts.back().m_dependenciesHandled = true; outputPath.erase(extensionOffset); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp index 2a1c4e4755..5c449e2764 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetCatalog/AssetCatalogUnitTests.cpp @@ -128,7 +128,9 @@ namespace AssetProcessor settingsRegistry->Set(cacheRootKey, m_data->m_temporarySourceDir.absoluteFilePath("Cache").toUtf8().constData()); auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); AssetUtilities::ComputeProjectCacheRoot(m_data->m_cacheRootDir); QString normalizedCacheRoot = AssetUtilities::NormalizeDirectoryPath(m_data->m_cacheRootDir.absolutePath()); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index 516f6beb3c..6a0da96151 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -107,12 +107,13 @@ namespace AssetProcessorMessagesTests AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey }; constexpr AZ::SettingsRegistryInterface::FixedValueString projectPathKey{ bootstrapKey + "/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); // Force the branch token into settings registry before starting the application manager. // This avoids writing the asset_processor.setreg file which can cause fileIO errors. - const AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath(); constexpr AZ::SettingsRegistryInterface::FixedValueString branchTokenKey{ bootstrapKey + "/assetProcessor_branch_token" }; AZStd::string token; AZ::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp index 3249b7e396..2629f7b956 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp @@ -71,12 +71,13 @@ namespace AssetProcessor auto registry = AZ::SettingsRegistry::Get(); auto bootstrapKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey); auto projectPathKey = bootstrapKey + "/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); // Forcing the branch token into settings registry before starting the application manager. // This avoids writing the asset_processor.setreg file which can cause fileIO errors. - AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath(); auto branchTokenKey = bootstrapKey + "/assetProcessor_branch_token"; AZStd::string token; AzFramework::StringFunc::AssetPath::CalculateBranchToken(enginePath.c_str(), token); diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h index 258268c182..719b04610c 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.h @@ -50,7 +50,9 @@ namespace AssetProcessor + "/project_path"; if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); } } diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index da6e995c97..f8d758e092 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -192,7 +192,9 @@ void AssetProcessorManagerTest::SetUp() registry->Set(cacheRootKey, tempPath.absoluteFilePath("Cache").toUtf8().constData()); 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_data->m_databaseLocationListener.BusConnect(); @@ -4137,11 +4139,19 @@ struct LockedFileTest switch (message.GetMessageType()) { case SourceFileNotificationMessage::MessageType: - if (const auto sourceFileMessage = azrtti_cast(&message); - sourceFileMessage != nullptr && sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved - && m_callback) + if (const auto sourceFileMessage = azrtti_cast(&message); sourceFileMessage != nullptr && + sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved) { - m_callback(); + // The File Remove message will occur before an attempt to delete the file + // Wait for more than 1 File Remove message. + // This indicates the AP has attempted to delete the file once, failed to do so and is now retrying + ++m_deleteCounter; + + if(m_deleteCounter > 1 && m_callback) + { + m_callback(); + m_callback = {}; // Unset it to be safe, we only intend to run the callback once + } } break; default: @@ -4165,6 +4175,7 @@ struct LockedFileTest ModtimeScanningTest::TearDown(); } + AZStd::atomic_int m_deleteCounter{ 0 }; AZStd::function m_callback; }; @@ -4204,6 +4215,10 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeleteFails) TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased) { + // This test is intended to verify the AP will successfully retry deleting a source asset + // when one of its product assets is locked temporarily + // We'll lock the file by holding it open + auto theFile = m_data->m_absolutePath[1].toUtf8(); const char* theFileString = theFile.constData(); auto [sourcePath, productPath] = *m_data->m_productPaths.find(theFileString); @@ -4216,19 +4231,22 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased) ASSERT_GT(m_data->m_productPaths.size(), 0); QFile product(productPath); + // Open the file and keep it open to lock it + // We'll start a thread later to unlock the file + // This will allow us to test how AP handles trying to delete a locked file ASSERT_TRUE(product.open(QIODevice::ReadOnly)); // Check if we can delete the file now, if we can't, proceed with the test // If we can, it means the OS running this test doesn't lock open files so there's nothing to test if (!AZ::IO::SystemFile::Delete(productPath.toUtf8().constData())) { - AZStd::thread workerThread; + m_deleteCounter = 0; - m_callback = [&product, &workerThread]() { - workerThread = AZStd::thread([&product]() { - AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(60)); - product.close(); - }); + // Set up a callback which will fire after at least 1 retry + // Unlock the file at that point so AP can successfully delete it + m_callback = [&product]() + { + product.close(); }; QMetaObject::invokeMethod( @@ -4238,8 +4256,9 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased) EXPECT_FALSE(QFile::exists(productPath)); EXPECT_EQ(m_data->m_deletedSources.size(), 1); - - workerThread.join(); + + EXPECT_GT(m_deleteCounter, 1); // Make sure the AP tried more than once to delete the file + m_errorAbsorber->ExpectAsserts(0); } else { diff --git a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp index 0c362ac829..4dd7e184e9 100644 --- a/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp +++ b/Code/Tools/BundleLauncher/O3DE_SDK_Launcher.cpp @@ -49,6 +49,12 @@ int main(int argc, char* argv[]) AZStd::unique_ptr shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); shellProcess->WaitForProcessToExit(120); shellProcess.reset(); + + parameters = AZStd::string::format("-c \"%s/scripts/o3de.sh register --this-engine\"", enginePath.c_str()); + shellProcessLaunch.m_commandlineParameters = parameters; + shellProcess.reset(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE)); + shellProcess->WaitForProcessToExit(120); + shellProcess.reset(); AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de"; AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; diff --git a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp index 1a3b86d34b..d5a344b686 100644 --- a/Code/Tools/DeltaCataloger/Tests/tests_main.cpp +++ b/Code/Tools/DeltaCataloger/Tests/tests_main.cpp @@ -45,7 +45,9 @@ protected: 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); AZ::ComponentApplication::Descriptor desc; diff --git a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp index 0d66009d90..e901d807b4 100644 --- a/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp +++ b/Code/Tools/ProjectManager/Platform/Linux/ProjectUtils_linux.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -94,5 +96,10 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + return AZ::Utils::GetExecutableDirectory(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp index e36f6cd0c8..b768200398 100644 --- a/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp +++ b/Code/Tools/ProjectManager/Platform/Mac/ProjectUtils_mac.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -104,5 +107,35 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath editorPath{ executableDirectory }; + editorPath /= "../../../Editor.app/Contents/MacOS"; + editorPath = editorPath.LexicallyNormal(); + if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str())) + { + if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) + { + if (AZ::IO::FixedMaxPath installedBinariesPath; + settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder)) + { + if (AZ::IO::FixedMaxPath engineRootFolder; + settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder)) + { + editorPath = engineRootFolder / installedBinariesPath / "Editor.app/Contents/MacOS"; + } + } + } + + if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str())) + { + AZ_Error("ProjectManager", false, "Unable to find the Editor app bundle!"); + } + } + + return editorPath; + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp index 831529d5e4..871f8e9567 100644 --- a/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp +++ b/Code/Tools/ProjectManager/Platform/Windows/ProjectUtils_windows.cpp @@ -14,6 +14,8 @@ #include #include +#include + namespace O3DE::ProjectManager { namespace ProjectUtils @@ -139,5 +141,10 @@ namespace O3DE::ProjectManager QProcessEnvironment::systemEnvironment(), QObject::tr("Running get_python script...")); } + + AZ::IO::FixedMaxPath GetEditorDirectory() + { + return AZ::Utils::GetExecutableDirectory(); + } } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 8c6d40505a..12cce5a4ea 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -81,6 +81,8 @@ namespace O3DE::ProjectManager DownloadStatus m_downloadStatus = UnknownDownloadStatus; QStringList m_features; QString m_requirement; + QString m_licenseText; + QString m_licenseLink; QString m_directoryLink; QString m_documentationLink; QString m_version = "Unknown Version"; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp index 3d9a8f6c86..b0b8cca29a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.cpp @@ -52,6 +52,22 @@ namespace O3DE::ProjectManager Update(selectedIndices[0]); } + void SetLabelElidedText(QLabel* label, QString text) + { + QFontMetrics nameFontMetrics(label->font()); + int labelWidth = label->width(); + + // Don't elide if the widgets are sized too small (sometimes occurs when loading gem catalog) + if (labelWidth > 100) + { + label->setText(nameFontMetrics.elidedText(text, Qt::ElideRight, labelWidth)); + } + else + { + label->setText(text); + } + } + void GemInspector::Update(const QModelIndex& modelIndex) { if (!modelIndex.isValid()) @@ -59,38 +75,52 @@ namespace O3DE::ProjectManager m_mainWidget->hide(); } - m_nameLabel->setText(m_model->GetDisplayName(modelIndex)); - m_creatorLabel->setText(m_model->GetCreator(modelIndex)); + SetLabelElidedText(m_nameLabel, m_model->GetDisplayName(modelIndex)); + SetLabelElidedText(m_creatorLabel, m_model->GetCreator(modelIndex)); m_summaryLabel->setText(m_model->GetSummary(modelIndex)); m_summaryLabel->adjustSize(); + m_licenseLinkLabel->setText(m_model->GetLicenseText(modelIndex)); + m_licenseLinkLabel->SetUrl(m_model->GetLicenseLink(modelIndex)); + m_directoryLinkLabel->SetUrl(m_model->GetDirectoryLink(modelIndex)); m_documentationLinkLabel->SetUrl(m_model->GetDocLink(modelIndex)); if (m_model->HasRequirement(modelIndex)) { - m_reqirementsIconLabel->show(); - m_reqirementsTitleLabel->show(); - m_reqirementsTextLabel->show(); + m_requirementsIconLabel->show(); + m_requirementsTitleLabel->show(); + m_requirementsTextLabel->show(); + m_requirementsMainSpacer->changeSize(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed); - m_reqirementsTitleLabel->setText("Requirement"); - m_reqirementsTextLabel->setText(m_model->GetRequirement(modelIndex)); + m_requirementsTitleLabel->setText(tr("Requirement")); + m_requirementsTextLabel->setText(m_model->GetRequirement(modelIndex)); } else { - m_reqirementsIconLabel->hide(); - m_reqirementsTitleLabel->hide(); - m_reqirementsTextLabel->hide(); + m_requirementsIconLabel->hide(); + m_requirementsTitleLabel->hide(); + m_requirementsTextLabel->hide(); + m_requirementsMainSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); } // Depending gems - m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGemNames(modelIndex)); + QStringList dependingGems = m_model->GetDependingGemNames(modelIndex); + if (!dependingGems.isEmpty()) + { + m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGems); + m_dependingGems->show(); + } + else + { + m_dependingGems->hide(); + } // Additional information - m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); - m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); - m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex))); + m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex))); + m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex))); + m_binarySizeLabel->setText(tr("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex))); m_mainWidget->adjustSize(); m_mainWidget->show(); @@ -108,35 +138,51 @@ namespace O3DE::ProjectManager { // Gem name, creator and summary m_nameLabel = CreateStyledLabel(m_mainLayout, 18, s_headerColor); - m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_headerColor); + m_creatorLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor); m_mainLayout->addSpacing(5); // TODO: QLabel seems to have issues determining the right sizeHint() for our font with the given font size. // This results into squeezed elements in the layout in case the text is a little longer than a sentence. - m_summaryLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); + m_summaryLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor); m_mainLayout->addWidget(m_summaryLabel); m_summaryLabel->setWordWrap(true); m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); m_summaryLabel->setOpenExternalLinks(true); m_mainLayout->addSpacing(5); + // License + { + QHBoxLayout* licenseHLayout = new QHBoxLayout(); + licenseHLayout->setMargin(0); + licenseHLayout->setAlignment(Qt::AlignLeft); + m_mainLayout->addLayout(licenseHLayout); + + QLabel* licenseLabel = CreateStyledLabel(licenseHLayout, s_baseFontSize, s_headerColor); + licenseLabel->setText(tr("License: ")); + + m_licenseLinkLabel = new LinkLabel("", QUrl(), s_baseFontSize); + licenseHLayout->addWidget(m_licenseLinkLabel); + + licenseHLayout->addStretch(); + + m_mainLayout->addSpacing(5); + } + // Directory and documentation links { QHBoxLayout* linksHLayout = new QHBoxLayout(); linksHLayout->setMargin(0); m_mainLayout->addLayout(linksHLayout); - QSpacerItem* spacerLeft = new QSpacerItem(0, 0, QSizePolicy::Expanding); - linksHLayout->addSpacerItem(spacerLeft); + linksHLayout->addStretch(); - m_directoryLinkLabel = new LinkLabel("View in Directory"); + m_directoryLinkLabel = new LinkLabel(tr("View in Directory")); linksHLayout->addWidget(m_directoryLinkLabel); linksHLayout->addWidget(new QLabel("|")); - m_documentationLinkLabel = new LinkLabel("Read Documentation"); + m_documentationLinkLabel = new LinkLabel(tr("Read Documentation")); linksHLayout->addWidget(m_documentationLinkLabel); - QSpacerItem* spacerRight = new QSpacerItem(0, 0, QSizePolicy::Expanding); - linksHLayout->addSpacerItem(spacerRight); + linksHLayout->addStretch(); m_mainLayout->addSpacing(8); } @@ -144,34 +190,35 @@ namespace O3DE::ProjectManager // Separating line QFrame* hLine = new QFrame(); hLine->setFrameShape(QFrame::HLine); - hLine->setStyleSheet("color: #666666;"); + hLine->setObjectName("horizontalSeparatingLine"); m_mainLayout->addWidget(hLine); m_mainLayout->addSpacing(10); // Requirements - m_reqirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor); + m_requirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor); - QHBoxLayout* requrementsLayout = new QHBoxLayout(); - requrementsLayout->setAlignment(Qt::AlignTop); - requrementsLayout->setMargin(0); - requrementsLayout->setSpacing(0); + QHBoxLayout* requirementsLayout = new QHBoxLayout(); + requirementsLayout->setAlignment(Qt::AlignTop); + requirementsLayout->setMargin(0); + requirementsLayout->setSpacing(0); - m_reqirementsIconLabel = new QLabel(); - m_reqirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24)); - requrementsLayout->addWidget(m_reqirementsIconLabel); + m_requirementsIconLabel = new QLabel(); + m_requirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24)); + requirementsLayout->addWidget(m_requirementsIconLabel); - m_reqirementsTextLabel = GemInspector::CreateStyledLabel(requrementsLayout, 10, s_textColor); - m_reqirementsTextLabel->setWordWrap(true); - m_reqirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_reqirementsTextLabel->setOpenExternalLinks(true); + m_requirementsTextLabel = GemInspector::CreateStyledLabel(requirementsLayout, 10, s_textColor); + m_requirementsTextLabel->setWordWrap(true); + m_requirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_requirementsTextLabel->setOpenExternalLinks(true); - QSpacerItem* reqirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding); - requrementsLayout->addSpacerItem(reqirementsSpacer); + QSpacerItem* requirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding); + requirementsLayout->addSpacerItem(requirementsSpacer); - m_mainLayout->addLayout(requrementsLayout); + m_mainLayout->addLayout(requirementsLayout); - m_mainLayout->addSpacing(20); + m_requirementsMainSpacer = new QSpacerItem(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed); + m_mainLayout->addSpacerItem(m_requirementsMainSpacer); // Depending gems m_dependingGems = new GemsSubWidget(); @@ -181,10 +228,10 @@ namespace O3DE::ProjectManager // Additional information QLabel* additionalInfoLabel = CreateStyledLabel(m_mainLayout, 14, s_headerColor); - additionalInfoLabel->setText("Additional Information"); + additionalInfoLabel->setText(tr("Additional Information")); - m_versionLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); - m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); - m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor); + m_versionLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor); + m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor); + m_binarySizeLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index 38285577fd..c6548527ab 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -16,7 +16,7 @@ #include #include -#include +#include #endif QT_FORWARD_DECLARE_CLASS(QVBoxLayout) @@ -36,6 +36,9 @@ namespace O3DE::ProjectManager void Update(const QModelIndex& modelIndex); static QLabel* CreateStyledLabel(QLayout* layout, int fontSize, const QString& colorCodeString); + // Fonts + inline constexpr static int s_baseFontSize = 12; + // Colors inline constexpr static const char* s_headerColor = "#FFFFFF"; inline constexpr static const char* s_textColor = "#DDDDDD"; @@ -57,13 +60,15 @@ namespace O3DE::ProjectManager QLabel* m_nameLabel = nullptr; QLabel* m_creatorLabel = nullptr; QLabel* m_summaryLabel = nullptr; + LinkLabel* m_licenseLinkLabel = nullptr; LinkLabel* m_directoryLinkLabel = nullptr; LinkLabel* m_documentationLinkLabel = nullptr; // Requirements - QLabel* m_reqirementsTitleLabel = nullptr; - QLabel* m_reqirementsIconLabel = nullptr; - QLabel* m_reqirementsTextLabel = nullptr; + QLabel* m_requirementsTitleLabel = nullptr; + QLabel* m_requirementsIconLabel = nullptr; + QLabel* m_requirementsTextLabel = nullptr; + QSpacerItem* m_requirementsMainSpacer = nullptr; // Depending and conflicting gems GemsSubWidget* m_dependingGems = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index e3eb95cb34..7c217db65b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -59,6 +59,8 @@ namespace O3DE::ProjectManager item->setData(gemInfo.m_path, RolePath); item->setData(gemInfo.m_requirement, RoleRequirement); item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus); + item->setData(gemInfo.m_licenseText, RoleLicenseText); + item->setData(gemInfo.m_licenseLink, RoleLicenseLink); appendRow(item); @@ -249,6 +251,16 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleRequirement).toString(); } + QString GemModel::GetLicenseText(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleLicenseText).toString(); + } + + QString GemModel::GetLicenseLink(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleLicenseLink).toString(); + } + GemModel* GemModel::GetSourceModel(QAbstractItemModel* model) { GemSortFilterProxyModel* proxyModel = qobject_cast(model); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index 9fac47d18e..e548d9d3f7 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -48,7 +48,9 @@ namespace O3DE::ProjectManager RoleTypes, RolePath, RoleRequirement, - RoleDownloadStatus + RoleDownloadStatus, + RoleLicenseText, + RoleLicenseLink }; void AddGem(const GemInfo& gemInfo); @@ -75,6 +77,8 @@ namespace O3DE::ProjectManager static QStringList GetFeatures(const QModelIndex& modelIndex); static QString GetPath(const QModelIndex& modelIndex); static QString GetRequirement(const QModelIndex& modelIndex); + static QString GetLicenseText(const QModelIndex& modelIndex); + static QString GetLicenseLink(const QModelIndex& modelIndex); static GemModel* GetSourceModel(QAbstractItemModel* model); static const GemModel* GetSourceModel(const QAbstractItemModel* model); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp index d065ab59f8..6655aef86d 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.cpp @@ -99,7 +99,7 @@ namespace O3DE::ProjectManager m_nameLabel->setObjectName("gemRepoInspectorNameLabel"); m_mainLayout->addWidget(m_nameLabel); - m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(""), 12, this); + m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(), 12, this); m_mainLayout->addWidget(m_repoLinkLabel); m_mainLayout->addSpacing(5); diff --git a/Code/Tools/ProjectManager/Source/ProjectUtils.h b/Code/Tools/ProjectManager/Source/ProjectUtils.h index 1fdf76913e..890d50d2de 100644 --- a/Code/Tools/ProjectManager/Source/ProjectUtils.h +++ b/Code/Tools/ProjectManager/Source/ProjectUtils.h @@ -14,6 +14,7 @@ #include #include +#include #include namespace O3DE::ProjectManager @@ -67,7 +68,8 @@ namespace O3DE::ProjectManager AZ::Outcome GetProjectBuildPath(const QString& projectPath); AZ::Outcome OpenCMakeGUI(const QString& projectPath); AZ::Outcome RunGetPythonScript(const QString& enginePath); - + + AZ::IO::FixedMaxPath GetEditorDirectory(); } // namespace ProjectUtils } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index cf42da88fa..f86a689e59 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -392,11 +392,11 @@ namespace O3DE::ProjectManager { if (!WarnIfInBuildQueue(projectPath)) { - AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory(); + AZ::IO::FixedMaxPath executableDirectory = ProjectUtils::GetEditorDirectory(); AZStd::string executableFilename = "Editor"; AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION); auto cmdPath = AZ::IO::FixedMaxPathString::format( - "%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), + "%s --regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str()); AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 740393fec0..905139a4f2 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -709,6 +709,8 @@ namespace O3DE::ProjectManager gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", ""); gemInfo.m_creator = Py_To_String_Optional(data, "origin", ""); gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", ""); + gemInfo.m_licenseText = Py_To_String_Optional(data, "license", "Unspecified License"); + gemInfo.m_licenseLink = Py_To_String_Optional(data, "license_url", ""); if (gemInfo.m_creator.contains("Open 3D Engine")) { diff --git a/Code/Tools/SerializeContextTools/SliceConverter.cpp b/Code/Tools/SerializeContextTools/SliceConverter.cpp index 6cfe072f80..d0528a9cad 100644 --- a/Code/Tools/SerializeContextTools/SliceConverter.cpp +++ b/Code/Tools/SerializeContextTools/SliceConverter.cpp @@ -81,8 +81,6 @@ namespace AZ // Load the asset catalog so that we can find any nested assets successfully. We also need to tick the tick bus // so that the OnCatalogLoaded event gets processed now, instead of during application shutdown. - AZ::Data::AssetCatalogRequestBus::Broadcast( - &AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); application.Tick(); AZStd::string logggingScratchBuffer; diff --git a/Gems/AWSClientAuth/gem.json b/Gems/AWSClientAuth/gem.json index 75c07d025b..d03b86fb45 100644 --- a/Gems/AWSClientAuth/gem.json +++ b/Gems/AWSClientAuth/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSClientAuth", "display_name": "AWS Client Authorization", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "AWS Client Auth provides client authentication and AWS authorization solution.", diff --git a/Gems/AWSCore/gem.json b/Gems/AWSCore/gem.json index 1bb9da9192..9fb974ccf4 100644 --- a/Gems/AWSCore/gem.json +++ b/Gems/AWSCore/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSCore", "display_name": "AWS Core", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Core Gem provides basic shared AWS functionality such as AWS SDK initialization and client configuration, and is automatically added when selecting any AWS feature Gem.", diff --git a/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py b/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py index 59f763498f..72c9d8ea43 100644 --- a/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py +++ b/Gems/AWSGameLift/cdk/aws_gamelift/fleet_configurations.py @@ -44,7 +44,7 @@ FLEET_CONFIGURATIONS = [ 'build_path': '', # (Conditional) The operating system that the game server binaries are built to run on. # This parameter is required if the parameter build_path is defined. - # Choose from AMAZON_LINUX, AMAZON_LINUX or WINDOWS_2012. + # Choose from AMAZON_LINUX or WINDOWS_2012. 'operating_system': 'WINDOWS_2012' }, # (Optional) Information about the use of a TLS/SSL certificate for a fleet. diff --git a/Gems/AWSGameLift/gem.json b/Gems/AWSGameLift/gem.json index a0d7f62cd1..3fe1f15c3d 100644 --- a/Gems/AWSGameLift/gem.json +++ b/Gems/AWSGameLift/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSGameLift", "display_name": "AWS GameLift", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS GameLift Gem provides a framework to extend O3DE networking layer to work with GameLift resources via GameLift server and client SDK.", diff --git a/Gems/AWSMetrics/gem.json b/Gems/AWSMetrics/gem.json index df16890012..59eae8f3c2 100644 --- a/Gems/AWSMetrics/gem.json +++ b/Gems/AWSMetrics/gem.json @@ -2,6 +2,7 @@ "gem_name": "AWSMetrics", "display_name": "AWS Metrics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Amazon Web Services, Inc.", "type": "Code", "summary": "The AWS Metrics Gem provides a solution for AWS metrics submission and analytics.", diff --git a/Gems/Achievements/gem.json b/Gems/Achievements/gem.json index bd643f471a..8180584500 100644 --- a/Gems/Achievements/gem.json +++ b/Gems/Achievements/gem.json @@ -2,6 +2,7 @@ "gem_name": "Achievements", "display_name": "Achievements", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Achievements Gem provides a target platform agnostic interface for retrieving achievement details and unlocking achievements.", diff --git a/Gems/AssetMemoryAnalyzer/gem.json b/Gems/AssetMemoryAnalyzer/gem.json index 902102fa27..443eeced18 100644 --- a/Gems/AssetMemoryAnalyzer/gem.json +++ b/Gems/AssetMemoryAnalyzer/gem.json @@ -2,6 +2,7 @@ "gem_name": "AssetMemoryAnalyzer", "display_name": "Asset Memory Analyzer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Memory Analyzer Gem provides tools to profile asset memory usage in Open 3D Engine through ImGUI (Immediate Mode Graphical User Interface).", diff --git a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h index 456afd0006..c4872ab425 100644 --- a/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h +++ b/Gems/AssetValidation/Code/Tests/AssetValidationTestShared.h @@ -150,7 +150,7 @@ struct AssetValidationTest auto projectPathKey = AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; - m_registry.Set(projectPathKey, "AutomatedTesting"); + m_registry.Set(projectPathKey, (AZ::IO::FixedMaxPath(GetEngineRoot()) / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(m_registry); // Set the engine root to the temporary directory and re-update the runtime file paths diff --git a/Gems/AssetValidation/gem.json b/Gems/AssetValidation/gem.json index 1e57f60dc4..55cdffb9f3 100644 --- a/Gems/AssetValidation/gem.json +++ b/Gems/AssetValidation/gem.json @@ -2,6 +2,7 @@ "gem_name": "AssetValidation", "display_name": "Asset Validation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Asset Validation Gem provides seed-related commands to ensure assets have valid seeds for asset bundling.", diff --git a/Gems/Atom/Asset/ImageProcessingAtom/gem.json b/Gems/Atom/Asset/ImageProcessingAtom/gem.json index 1424841256..4fd437d298 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/gem.json +++ b/Gems/Atom/Asset/ImageProcessingAtom/gem.json @@ -2,6 +2,7 @@ "gem_name": "ImageProcessingAtom", "display_name": "Atom Image Processing", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Asset/Shader/gem.json b/Gems/Atom/Asset/Shader/gem.json index 6d5e9f4dbe..9f59c65f78 100644 --- a/Gems/Atom/Asset/Shader/gem.json +++ b/Gems/Atom/Asset/Shader/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomShader", "display_name": "Atom Shader Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index 3f5761270a..c232d4bba7 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -132,7 +133,6 @@ namespace AZ m_createDefaultScene = false; } - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); TickBus::Handler::BusConnect(); // Listen for window system requests (e.g. requests for default window handle) @@ -143,6 +143,20 @@ namespace AZ Render::Bootstrap::DefaultWindowBus::Handler::BusConnect(); Render::Bootstrap::RequestBus::Handler::BusConnect(); + + // If the settings registry isn't available, something earlier in startup will report that failure. + 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*/) + { + Initialize(); + }, + "LegacySystemInterfaceCreated"); + } } void BootstrapSystemComponent::Deactivate() @@ -153,7 +167,6 @@ namespace AZ AzFramework::WindowSystemRequestBus::Handler::BusDisconnect(); AzFramework::WindowSystemNotificationBus::Handler::BusDisconnect(); TickBus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); m_brdfTexture = nullptr; RemoveRenderPipeline(); @@ -164,14 +177,14 @@ namespace AZ m_windowHandle = nullptr; } - void BootstrapSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/) + void BootstrapSystemComponent::Initialize() { - if (m_isAssetCatalogLoaded) + if (m_isInitialized) { return; } - m_isAssetCatalogLoaded = true; + m_isInitialized = true; if (!RPI::RPISystemInterface::Get()->IsInitialized()) { @@ -216,7 +229,7 @@ namespace AZ { m_windowHandle = windowHandle; - if (m_isAssetCatalogLoaded) + if (m_isInitialized) { CreateWindowContext(); if (m_createDefaultScene) diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h index 566d19b1a4..74679f5753 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h @@ -8,10 +8,10 @@ #pragma once #include -#include #include +#include +#include -#include #include #include #include @@ -29,7 +29,6 @@ #include #include - namespace AZ { namespace Render @@ -40,7 +39,6 @@ namespace AZ : public Component , public TickBus::Handler , public AzFramework::WindowNotificationBus::Handler - , public AzFramework::AssetCatalogEventBus::Handler , public AzFramework::WindowSystemNotificationBus::Handler , public AzFramework::WindowSystemRequestBus::Handler , public Render::Bootstrap::DefaultWindowBus::Handler @@ -82,13 +80,12 @@ namespace AZ void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; int GetTickOrder() override; - // AzFramework::AssetCatalogEventBus::Handler overrides ... - void OnCatalogLoaded(const char* catalogFile) override; - // AzFramework::WindowSystemNotificationBus::Handler overrides ... void OnWindowCreated(AzFramework::NativeWindowHandle windowHandle) override; private: + void Initialize(); + void CreateDefaultRenderPipeline(); void CreateDefaultScene(); void DestroyDefaultScene(); @@ -105,7 +102,7 @@ namespace AZ RPI::ScenePtr m_defaultScene = nullptr; AZStd::shared_ptr m_defaultFrameworkScene = nullptr; - bool m_isAssetCatalogLoaded = false; + bool m_isInitialized = false; // The id of the render pipeline created by this component RPI::RenderPipelineId m_renderPipelineId; @@ -119,6 +116,8 @@ namespace AZ // Maps AZ scenes to RPI scene weak pointers to allow looking up a ScenePtr instead of a raw Scene* AZStd::unordered_map> m_azSceneToAtomSceneMap; + + AZ::SettingsRegistryInterface::NotifyEventHandler m_componentApplicationLifecycleHandler; }; } // namespace Bootstrap } // namespace Render diff --git a/Gems/Atom/Bootstrap/gem.json b/Gems/Atom/Bootstrap/gem.json index 5e98a1887d..df615366da 100644 --- a/Gems/Atom/Bootstrap/gem.json +++ b/Gems/Atom/Bootstrap/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_Bootstrap", "display_name": "Atom Bootstrap", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Component/DebugCamera/gem.json b/Gems/Atom/Component/DebugCamera/gem.json index 586cb37058..8eab74f41d 100644 --- a/Gems/Atom/Component/DebugCamera/gem.json +++ b/Gems/Atom/Component/DebugCamera/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_Component_DebugCamera", "display_name": "Atom Debug Camera Component", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Feature/Common/gem.json b/Gems/Atom/Feature/Common/gem.json index 36a61fbbbb..84ab07a9f9 100644 --- a/Gems/Atom/Feature/Common/gem.json +++ b/Gems/Atom/Feature/Common/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_Feature_Common", "display_name": "Atom Feature Common", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/DX12/gem.json b/Gems/Atom/RHI/DX12/gem.json index eb876a1b9f..868acd43db 100644 --- a/Gems/Atom/RHI/DX12/gem.json +++ b/Gems/Atom/RHI/DX12/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_DX12", "display_name": "Atom RHI DX12", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/Metal/gem.json b/Gems/Atom/RHI/Metal/gem.json index 8da6bfabec..6e983ba505 100644 --- a/Gems/Atom/RHI/Metal/gem.json +++ b/Gems/Atom/RHI/Metal/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_Metal", "display_name": "Atom RHI Metal", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/Null/gem.json b/Gems/Atom/RHI/Null/gem.json index 0870efaae7..e9f22c5fcb 100644 --- a/Gems/Atom/RHI/Null/gem.json +++ b/Gems/Atom/RHI/Null/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_Null", "display_name": "Atom RHI Null", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/Vulkan/gem.json b/Gems/Atom/RHI/Vulkan/gem.json index 81e8dd22ea..1dfeb9eafa 100644 --- a/Gems/Atom/RHI/Vulkan/gem.json +++ b/Gems/Atom/RHI/Vulkan/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI_Vulkan", "display_name": "Atom RHI Vulkan", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RHI/gem.json b/Gems/Atom/RHI/gem.json index 5f916e5224..7b64476c65 100644 --- a/Gems/Atom/RHI/gem.json +++ b/Gems/Atom/RHI/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_RHI", "display_name": "Atom RHI", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/RPI/gem.json b/Gems/Atom/RPI/gem.json index b5a6fd5a1a..885f150508 100644 --- a/Gems/Atom/RPI/gem.json +++ b/Gems/Atom/RPI/gem.json @@ -3,6 +3,7 @@ "display_name": "Atom API", "summary": "", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "canonical_tags": [ diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp index e8ec77e7bd..ba3bfe4718 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp @@ -175,8 +175,6 @@ namespace AtomToolsFramework AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast( &AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized); - AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequestBus::Events::LoadCatalog, "@products@/assetcatalog.xml"); - if (!AZ::RPI::RPISystemInterface::Get()->IsInitialized()) { AZ::RPI::RPISystemInterface::Get()->InitializeSystemAssets(); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp index fc5b31297a..d46e7b27be 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.cpp @@ -47,30 +47,27 @@ namespace AtomToolsFramework void PreviewRendererSystemComponent::Activate() { - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect(); PreviewRendererSystemRequestBus::Handler::BusConnect(); + + AZ::TickBus::QueueFunction( + [this]() + { + if (!m_previewRenderer) + { + m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer( + "PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline")); + } + }); } void PreviewRendererSystemComponent::Deactivate() { PreviewRendererSystemRequestBus::Handler::BusDisconnect(); AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect(); - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); m_previewRenderer.reset(); } - void PreviewRendererSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile) - { - AZ::TickBus::QueueFunction([this](){ - if (!m_previewRenderer) - { - m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer( - "PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline")); - } - }); - } - void PreviewRendererSystemComponent::OnApplicationAboutToStop() { m_previewRenderer.reset(); diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h index 8110d84794..0b145bbdf1 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/PreviewRenderer/PreviewRendererSystemComponent.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include #include #include @@ -19,7 +18,6 @@ namespace AtomToolsFramework //! System component that manages a global PreviewRenderer. class PreviewRendererSystemComponent final : public AZ::Component - , public AzFramework::AssetCatalogEventBus::Handler , public AzFramework::ApplicationLifecycleEvents::Bus::Handler , public PreviewRendererSystemRequestBus::Handler { @@ -38,9 +36,6 @@ namespace AtomToolsFramework void Deactivate() override; private: - // AzFramework::AssetCatalogEventBus::Handler overrides ... - void OnCatalogLoaded(const char* catalogFile) override; - // AzFramework::ApplicationLifecycleEvents overrides... void OnApplicationAboutToStop() override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/gem.json b/Gems/Atom/Tools/AtomToolsFramework/gem.json index 2b0380bdae..de2a9e06f2 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/gem.json +++ b/Gems/Atom/Tools/AtomToolsFramework/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomToolsFramework", "display_name": "Atom Tools Framework", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/Atom/Tools/MaterialEditor/gem.json b/Gems/Atom/Tools/MaterialEditor/gem.json index 85ff434eab..807e3fa65f 100644 --- a/Gems/Atom/Tools/MaterialEditor/gem.json +++ b/Gems/Atom/Tools/MaterialEditor/gem.json @@ -2,6 +2,7 @@ "gem_name": "MaterialEditor", "display_name": "Atom Material Editor", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "Editor for creating, modifying, and previewing materials", diff --git a/Gems/Atom/gem.json b/Gems/Atom/gem.json index 1f5e4a37f3..0f409ad993 100644 --- a/Gems/Atom/gem.json +++ b/Gems/Atom/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom", "display_name": "Atom Renderer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom Renderer Gem provides Atom Renderer and its associated tools (such as Material Editor), utilites, libraries, and interfaces.", diff --git a/Gems/AtomContent/ReferenceMaterials/gem.json b/Gems/AtomContent/ReferenceMaterials/gem.json index f697d50bc4..b75b01e1ae 100644 --- a/Gems/AtomContent/ReferenceMaterials/gem.json +++ b/Gems/AtomContent/ReferenceMaterials/gem.json @@ -5,8 +5,15 @@ "origin": "https://github.com/aws-lumberyard-dev/o3de.git", "type": "Asset", "summary": "Atom Asset Gem with a library of reference materials for StandardPBR (and others in the future)", - "canonical_tags": ["Gem"], - "user_tags": ["Assets", "PBR", "Materials"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Assets", + "PBR", + "Materials" + ], "icon_path": "preview.png", - "dependencies": [] + "dependencies": [], + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt" } diff --git a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab index b5aed9d14b..92874574e4 100644 --- a/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab +++ b/Gems/AtomContent/Sponza/Assets/Prefabs/test_sponza_material_conversion.prefab @@ -581,7 +581,7 @@ { "id": { "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -593,7 +593,7 @@ "id": { "lodIndex": 0, "materialAssetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", "subId": 803645540 } } @@ -608,10 +608,10 @@ "Configuration": { "ModelAsset": { "assetId": { - "guid": "{935F694A-8639-515B-8133-81CDC7948E5B}", - "subId": 277333723 + "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}", + "subId": 277889906 }, - "assetHint": "objects/groudplane/groundplane_521x521m.azmodel" + "assetHint": "objects/groudplane/groundplane_512x512m.azmodel" } } } diff --git a/Gems/AtomContent/Sponza/gem.json b/Gems/AtomContent/Sponza/gem.json index 64de0e5da0..68749cd5f4 100644 --- a/Gems/AtomContent/Sponza/gem.json +++ b/Gems/AtomContent/Sponza/gem.json @@ -2,6 +2,7 @@ "gem_name": "Sponza", "display_name": "Sponza", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "A standard test scene for Global Illumination (forked from crytek sponza scene)", diff --git a/Gems/AtomContent/gem.json b/Gems/AtomContent/gem.json index 1bffe7d989..400e897a3b 100644 --- a/Gems/AtomContent/gem.json +++ b/Gems/AtomContent/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomContent", "display_name": "Atom Content", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Atom Content Gem provides assets for Atom Renderer and a modified version of the Pixar Look Development Studio.", diff --git a/Gems/AtomLyIntegration/AtomBridge/gem.json b/Gems/AtomLyIntegration/AtomBridge/gem.json index 1d48d8be61..e8ca413023 100644 --- a/Gems/AtomLyIntegration/AtomBridge/gem.json +++ b/Gems/AtomLyIntegration/AtomBridge/gem.json @@ -2,6 +2,7 @@ "gem_name": "Atom_AtomBridge", "display_name": "Atom Bridge", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomFont/gem.json b/Gems/AtomLyIntegration/AtomFont/gem.json index ed5b488de7..8907ec0979 100644 --- a/Gems/AtomLyIntegration/AtomFont/gem.json +++ b/Gems/AtomLyIntegration/AtomFont/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomFont", "display_name": "Atom Font", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json index 564eeedee2..e188ba0cb8 100644 --- a/Gems/AtomLyIntegration/AtomImGuiTools/gem.json +++ b/Gems/AtomLyIntegration/AtomImGuiTools/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomImGuiTools", "display_name": "Atom ImGui", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json index a2a7ba4b77..5a0763e0da 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayIcons/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomViewportDisplayIcons", "display_name": "Atom Viewport Display Icons", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json index be5cc96f95..639d93d301 100644 --- a/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json +++ b/Gems/AtomLyIntegration/AtomViewportDisplayInfo/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomViewportDisplayInfo", "display_name": "Atom Viewport Display Info", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice index b4c9eac10f..e0c7c9e456 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice +++ b/Gems/AtomLyIntegration/CommonFeatures/Assets/LevelAssets/default.slice @@ -836,7 +836,7 @@ - + diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp index ebc79abca5..dfb6cf6946 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/CoreLights/DiskLightDelegate.cpp @@ -47,53 +47,60 @@ namespace AZ::Render return m_shapeBus->GetRadius() * GetTransform().GetUniformScale(); } - void DiskLightDelegate::DrawDebugDisplay(const Transform& transform, const Color& /*color*/, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const + void DiskLightDelegate::DrawDebugDisplay(const Transform& transform, [[maybe_unused]]const Color&, AzFramework::DebugDisplayRequests& debugDisplay, bool isSelected) const { - if (isSelected) + debugDisplay.PushMatrix(transform); + const float radius = GetConfig()->m_attenuationRadius; + const float shapeRadius = m_shapeBus->GetRadius(); + + auto DrawConicalFrustum = [&debugDisplay](uint32_t numRadiusLines, const Color& color, float brightness, float topRadius, float bottomRadius, float height) { - debugDisplay.PushMatrix(transform); - float radius = GetConfig()->m_attenuationRadius; + const Color displayColor = Color(color.GetAsVector3() * brightness); + debugDisplay.SetColor(displayColor); + debugDisplay.DrawWireDisk(Vector3(0.0, 0.0, height), Vector3::CreateAxisZ(), bottomRadius); - if (GetConfig()->m_enableShutters) + for (uint32_t i = 0; i < numRadiusLines; ++i) { + float radiusLineAngle = float(i) / numRadiusLines * Constants::TwoPi; + float cosAngle = cos(radiusLineAngle); + float sinAngle = sin(radiusLineAngle); + debugDisplay.DrawLine( + Vector3(cosAngle * topRadius, sinAngle * topRadius, 0), + Vector3(cosAngle * bottomRadius,sinAngle * bottomRadius, height) + ); + } + }; - float innerRadians = DegToRad(GetConfig()->m_innerShutterAngleDegrees); - float outerRadians = DegToRad(GetConfig()->m_outerShutterAngleDegrees); - - // Draw a cone using the cone angle and attenuation radius - innerRadians = GetMin(innerRadians, outerRadians); - float coneRadiusInner = sin(innerRadians) * radius; - float coneHeightInner = cos(innerRadians) * radius; - float coneRadiusOuter = sin(outerRadians) * radius; - float coneHeightOuter = cos(outerRadians) * radius; - - auto DrawConicalFrustum = [&debugDisplay](uint32_t numRadiusLines, float topRadius, float bottomRadius, float height, float brightness) - { - debugDisplay.SetColor(Color(brightness, brightness, brightness, 1.0f)); - debugDisplay.DrawWireDisk(Vector3(0.0, 0.0, height), Vector3::CreateAxisZ(), bottomRadius); - - for (uint32_t i = 0; i < numRadiusLines; ++i) - { - float radiusLineAngle = float(i) / numRadiusLines * Constants::TwoPi; - debugDisplay.DrawLine( - Vector3(cos(radiusLineAngle) * topRadius, sin(radiusLineAngle) * topRadius, 0), - Vector3(cos(radiusLineAngle) * bottomRadius, sin(radiusLineAngle) * bottomRadius, height) - ); - } - }; - - DrawConicalFrustum(16, m_shapeBus->GetRadius(), m_shapeBus->GetRadius() + coneRadiusInner, coneHeightInner, 1.0f); - DrawConicalFrustum(16, m_shapeBus->GetRadius(), m_shapeBus->GetRadius() + coneRadiusOuter, coneHeightOuter, 0.65f); + const Color coneColor = isSelected ? Color::CreateOne() : Color(0.0f, 0.75f, 0.75f, 1.0); + const uint32_t innerConeLines = 8; + float innerRadians, outerRadians; + if (GetConfig()->m_enableShutters) + { // With shutters enabled, draw inner and outer debug display frustums + innerRadians = DegToRad(GetConfig()->m_innerShutterAngleDegrees); + outerRadians = DegToRad(GetConfig()->m_outerShutterAngleDegrees); - } - else - { - debugDisplay.DrawWireDisk(Vector3::CreateZero(), Vector3::CreateAxisZ(), radius); - debugDisplay.DrawArc(Vector3::CreateZero(), radius, 270.0f, 180.0f, 3.0f, 0); - debugDisplay.DrawArc(Vector3::CreateZero(), radius, 0.0f, 180.0f, 3.0f, 1); - } - debugDisplay.PopMatrix(); + // Draw a cone using the cone angle and attenuation radius + innerRadians = GetMin(innerRadians, outerRadians); + + float coneRadiusOuter = sin(outerRadians) * radius; + float coneHeightOuter = cos(outerRadians) * radius; + + // Outer cone frustum 'faded' debug cone + const uint32_t outerConeLines = 9; + DrawConicalFrustum(outerConeLines, coneColor, 0.75f, shapeRadius, shapeRadius + coneRadiusOuter, coneHeightOuter); + } + else + { // Generic debug display frustum + const float coneAngle = 25.0f; + innerRadians = DegToRad(coneAngle); // 25 degrees debug display } + + // Inner cone frustum + float coneRadiusInner = sin(innerRadians) * radius; + float coneHeightInner = cos(innerRadians) * radius; + DrawConicalFrustum(innerConeLines, coneColor, 1.0f, shapeRadius, shapeRadius + coneRadiusInner, coneHeightInner); + + debugDisplay.PopMatrix(); } void DiskLightDelegate::SetEnableShutters(bool enabled) diff --git a/Gems/AtomLyIntegration/CommonFeatures/gem.json b/Gems/AtomLyIntegration/CommonFeatures/gem.json index 30738ad14c..6fa8938332 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/gem.json +++ b/Gems/AtomLyIntegration/CommonFeatures/gem.json @@ -2,6 +2,7 @@ "gem_name": "CommonFeaturesAtom", "display_name": "Common Features Atom", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json index f921990360..3514d4c1b9 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/gem.json +++ b/Gems/AtomLyIntegration/EMotionFXAtom/gem.json @@ -2,6 +2,7 @@ "gem_name": "EMotionFX_Atom", "display_name": "EMotionFX Atom", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/ImguiAtom/gem.json b/Gems/AtomLyIntegration/ImguiAtom/gem.json index 6b032275b9..fa611d8224 100644 --- a/Gems/AtomLyIntegration/ImguiAtom/gem.json +++ b/Gems/AtomLyIntegration/ImguiAtom/gem.json @@ -2,6 +2,7 @@ "gem_name": "ImguiAtom", "display_name": "Imgui Atom", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "", diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json index 1cee5c8298..4cc6fff169 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json @@ -3,6 +3,7 @@ "display_name": "Atom DccScriptingInterface (DCCsi)", "summary": "A python framework for working with various DCC tools and workflows.", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "canonical_tags": [ diff --git a/Gems/AtomLyIntegration/gem.json b/Gems/AtomLyIntegration/gem.json index 00b3a25f74..d9e526aee0 100644 --- a/Gems/AtomLyIntegration/gem.json +++ b/Gems/AtomLyIntegration/gem.json @@ -2,6 +2,7 @@ "gem_name": "AtomLyIntegration", "display_name": "Atom O3DE Integration", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Atom O3DE Integration Gem provides components, libraries, and functionality to support and integrate Atom Renderer in Open 3D Engine.", diff --git a/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass index 83f9f0d432..d8389a3da8 100644 --- a/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass +++ b/Gems/AtomTressFX/Assets/Passes/HairParentShortCutPass.pass @@ -264,7 +264,7 @@ "Attachment": "HairColorRenderTarget" } }, - { + { // The final render target - this is MSAA mode RT - would it be cheaper to // use non-MSAA and then copy? "LocalSlot": "RenderTargetInputOutput", @@ -280,6 +280,13 @@ "Attachment": "DepthLinearInput" } }, + { + "LocalSlot": "AccumulatedInverseAlpha", + "AttachmentRef": { + "Pass": "HairShortCutGeometryDepthAlphaPass", + "Attachment": "InverseAlphaRTOutput" + } + }, { "LocalSlot": "Depth", "AttachmentRef": { diff --git a/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass index 5940f8c549..53fa2b358b 100644 --- a/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass +++ b/Gems/AtomTressFX/Assets/Passes/HairShortCutGeometryShading.pass @@ -32,6 +32,12 @@ "SlotType": "Input", "ScopeAttachmentUsage": "Shader" }, + { // Used as the thickness accumulation to block TT (back) lobe lighting + "Name": "AccumulatedInverseAlpha", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ShaderInputName": "m_accumInvAlpha" + }, { // For comparing the depth to early disqualify but not to write "Name": "Depth", "SlotType": "Input", diff --git a/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl b/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl index 02777435f5..8dcd1ed372 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairRenderingFillPPLL.azsl @@ -51,9 +51,6 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback RWTexture2D m_fragmentListHead; RWStructuredBuffer m_linkedListNodes; RWBuffer m_linkedListCounter; - - // Linear depth is used for getting the screen to world transform - Texture2D m_linearDepth; } //------------------------------------------------------------------------------ diff --git a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl index c2a2958dfe..2a3e00b1e7 100644 --- a/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl +++ b/Gems/AtomTressFX/Assets/Shaders/HairShortCutGeometryShading.azsl @@ -52,6 +52,9 @@ ShaderResourceGroup PassSrg : SRG_PerPass_WithFallback //! Originally in TressFXRendering.hlsl this is space 0 HairObjectShadeParams m_hairParams[AMD_TRESSFX_MAX_HAIR_GROUP_RENDER]; + // Will be used as thickness indication to block TT (back) lobe + Texture2D m_accumInvAlpha; + // Linear depth is used for getting the screen to world transform Texture2D m_linearDepth; @@ -164,9 +167,11 @@ float4 HairShortCutGeometryColorPS(PS_INPUT_HAIR input) : SV_Target float2 pixelCoord = input.Position.xy; float depth = input.Position.z; - // [To Do] - the thickness will need to be corrected somehow since this technique doesn't - // keeps track of the accumulated alpha / thickness - float thickness = alpha; + + // The following is a quick correction to remove the TT lobe (back lobe) contribution in case + // the hair is thick. We do that by accumulating alpha from the hair for the blend operation + // and this can be used here as an indication of thickness. + float thickness = saturate(1.0 - PassSrg::m_accumInvAlpha[int2(pixelCoord)]); float3 shadedFragment = TressFXShading(pixelCoord, depth, input.Tangent.xyz, strandColor.rgb, thickness, RenderParamsIndex); // Color channel: Pre-multiply with alpha to create non-normalized weighted sum. diff --git a/Gems/AtomTressFX/gem.json b/Gems/AtomTressFX/gem.json index d3e1294568..b7588ea374 100644 --- a/Gems/AtomTressFX/gem.json +++ b/Gems/AtomTressFX/gem.json @@ -2,11 +2,18 @@ "gem_name": "AtomTressFX", "display_name": "Atom TressFX", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "Atom TressFX Gem provides a cutting edge hair and fur simulation and rendering in Atom enhancing the AMD TressFX 4.1. The open source TressFX can be found here: https://github.com/GPUOpen-Effects/TressFX", - "canonical_tags": ["Gem"], - "user_tags": ["Rendering", "Physics", "Animation"], + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "Rendering", + "Physics", + "Animation" + ], "requirements": "", "documentation_url": "https://o3de.org/docs/user-guide/gems/reference/rendering/amd/atom-tressfx/" } diff --git a/Gems/AudioEngineWwise/gem.json b/Gems/AudioEngineWwise/gem.json index 0588af1908..2b0af30d40 100644 --- a/Gems/AudioEngineWwise/gem.json +++ b/Gems/AudioEngineWwise/gem.json @@ -2,6 +2,7 @@ "gem_name": "AudioEngineWwise", "display_name": "Wwise Audio Engine", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Wwise Audio Engine Gem provides support for Audiokinetic Wave Works Interactive Sound Engine (Wwise).", diff --git a/Gems/AudioSystem/gem.json b/Gems/AudioSystem/gem.json index 64d4d9af5a..ed028068a1 100644 --- a/Gems/AudioSystem/gem.json +++ b/Gems/AudioSystem/gem.json @@ -2,6 +2,7 @@ "gem_name": "AudioSystem", "display_name": "Audio System", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Audio System Gem provides the Audio Translation Layer (ATL) and Audio Controls Editor, which add support for audio in Open 3D Engine.", diff --git a/Gems/BarrierInput/gem.json b/Gems/BarrierInput/gem.json index 7fdc58e8b3..72d6398550 100644 --- a/Gems/BarrierInput/gem.json +++ b/Gems/BarrierInput/gem.json @@ -2,6 +2,7 @@ "gem_name": "BarrierInput", "display_name": "Barrier Input", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Barrier Input Gem allows the Open 3D Engine to function as a Barrier client so that it can receive input from a remote Barrier server.", diff --git a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp index b7ee805b3c..00629eb65d 100644 --- a/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp @@ -143,6 +143,7 @@ namespace Blast void BlastSystemComponent::Deactivate() { AZ_PROFILE_FUNCTION(Physics); + AZ::Data::AssetBus::MultiHandler::BusDisconnect(); CrySystemEventBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); BlastSystemRequestBus::Handler::BusDisconnect(); diff --git a/Gems/Blast/gem.json b/Gems/Blast/gem.json index d6af47f482..761eb04761 100644 --- a/Gems/Blast/gem.json +++ b/Gems/Blast/gem.json @@ -2,6 +2,7 @@ "gem_name": "Blast", "display_name": "NVIDIA Blast", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The NVIDIA Blast Gem provides tools to author fractured mesh assets in Houdini, and functionality to create realistic destruction simulations in Open 3D Engine.", diff --git a/Gems/Camera/gem.json b/Gems/Camera/gem.json index 9f8ea22412..4cf0747c7b 100644 --- a/Gems/Camera/gem.json +++ b/Gems/Camera/gem.json @@ -2,6 +2,7 @@ "gem_name": "Camera", "display_name": "Camera", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Gem provides a basic camera component that defines a frustum for runtime rendering.", diff --git a/Gems/CameraFramework/gem.json b/Gems/CameraFramework/gem.json index c5520fd5b4..d24014be61 100644 --- a/Gems/CameraFramework/gem.json +++ b/Gems/CameraFramework/gem.json @@ -2,6 +2,7 @@ "gem_name": "CameraFramework", "display_name": "Camera Framework", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Camera Framework Gem provides a base for implementing more complex camera systems.", diff --git a/Gems/CertificateManager/gem.json b/Gems/CertificateManager/gem.json index 968da2788a..11ea14ea5e 100644 --- a/Gems/CertificateManager/gem.json +++ b/Gems/CertificateManager/gem.json @@ -2,6 +2,7 @@ "gem_name": "CertificateManager", "display_name": "Certificate Manager", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Certificate Manager Gem provides access to authentication files for secure game connections from Amazon S3, files on disk, and other 3rd party sources.", diff --git a/Gems/CrashReporting/gem.json b/Gems/CrashReporting/gem.json index 9b8d3a9e0a..b8c75548a4 100644 --- a/Gems/CrashReporting/gem.json +++ b/Gems/CrashReporting/gem.json @@ -2,6 +2,7 @@ "gem_name": "CrashReporting", "display_name": "Crash Reporting", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Crash Reporting Gem provides support for external crash reporting for Open 3D Engine projects.", diff --git a/Gems/CustomAssetExample/gem.json b/Gems/CustomAssetExample/gem.json index ab5ed7002d..89492ee6ea 100644 --- a/Gems/CustomAssetExample/gem.json +++ b/Gems/CustomAssetExample/gem.json @@ -2,6 +2,7 @@ "gem_name": "CustomAssetExample", "display_name": "Custom Asset Example", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Custom Asset Example Gem provides example code for creating a custom asset for Open 3D Engine's asset pipeline.", diff --git a/Gems/DebugDraw/gem.json b/Gems/DebugDraw/gem.json index 7e0da07103..58eff5f87a 100644 --- a/Gems/DebugDraw/gem.json +++ b/Gems/DebugDraw/gem.json @@ -2,6 +2,7 @@ "gem_name": "DebugDraw", "display_name": "Debug Draw", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Debug Draw Gem provides Editor and runtime debug visualization features for Open 3D Engine.", diff --git a/Gems/DevTextures/gem.json b/Gems/DevTextures/gem.json index 8b40badbf1..00cafbd6fb 100644 --- a/Gems/DevTextures/gem.json +++ b/Gems/DevTextures/gem.json @@ -2,6 +2,7 @@ "gem_name": "DevTextures", "display_name": "Dev Textures", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Dev Textures Gem provides a collection of general purpose texture assets useful for prototypes and preproduction.", diff --git a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp index 5da9716d15..11cf3feb3b 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Editor/Components/EditorActorComponent.cpp @@ -195,6 +195,8 @@ namespace EMotionFX ////////////////////////////////////////////////////////////////////////// void EditorActorComponent::Activate() { + AzToolsFramework::Components::EditorComponentBase::Activate(); + LoadActorAsset(); const AZ::EntityId entityId = GetEntityId(); @@ -225,6 +227,8 @@ namespace EMotionFX DestroyActorInstance(); m_actorAsset.Release(); + + AzToolsFramework::Components::EditorComponentBase::Deactivate(); } ////////////////////////////////////////////////////////////////////////// @@ -587,7 +591,15 @@ namespace EMotionFX if (asset) { m_actorAsset = asset; - OnAssetSelected(); + + // SetPrimaryAsset function can be called while this component is not activated + // due to incompatible services. For example by dragging and dropping a FBX to an + // entity that already has an actor or mesh component in it. Only proceed to load actor + // asset if the component is activated (by checking if it's connected to EditorActorComponentRequestBus). + if (EditorActorComponentRequestBus::Handler::BusIsConnected()) + { + OnAssetSelected(); + } } } diff --git a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h index 034fd2045b..62ebffa7e9 100644 --- a/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h +++ b/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h @@ -61,7 +61,9 @@ namespace EMotionFX constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path"; if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr) { - settingsRegistry->Set(projectPathKey, "AutomatedTesting"); + AZ::IO::FixedMaxPath enginePath; + settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder); + settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native()); AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry); } } diff --git a/Gems/EMotionFX/gem.json b/Gems/EMotionFX/gem.json index f1734d854d..ed80517af1 100644 --- a/Gems/EMotionFX/gem.json +++ b/Gems/EMotionFX/gem.json @@ -2,6 +2,7 @@ "gem_name": "EMotionFX", "display_name": "EMotion FX Animation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The EMotion FX Animation Gem provides Open 3D Engine's animation system for rigged actors and includes Animation Editor, a tool for creating animated behaviors, simulated objects, and colliders for rigged actors.", diff --git a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp index d630605cbc..9dbbb34e6c 100644 --- a/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp +++ b/Gems/EditorPythonBindings/Code/Tests/EditorPythonBindingsTest.cpp @@ -323,7 +323,9 @@ sys.version auto 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.RegisterComponentDescriptor(EditorPythonBindings::PythonSystemComponent::CreateDescriptor()); diff --git a/Gems/EditorPythonBindings/gem.json b/Gems/EditorPythonBindings/gem.json index 13c5800dd7..475483f13b 100644 --- a/Gems/EditorPythonBindings/gem.json +++ b/Gems/EditorPythonBindings/gem.json @@ -2,6 +2,7 @@ "gem_name": "EditorPythonBindings", "display_name": "Editor Python Bindings", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Editor Python Bindings Gem provides Python commands for Open 3D Engine Editor functions.", diff --git a/Gems/ExpressionEvaluation/gem.json b/Gems/ExpressionEvaluation/gem.json index 6bcc666a4d..cd712f4da9 100644 --- a/Gems/ExpressionEvaluation/gem.json +++ b/Gems/ExpressionEvaluation/gem.json @@ -2,6 +2,7 @@ "gem_name": "ExpressionEvaluation", "display_name": "Expression Evaluation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Expression Evaluation Gem provides a method for parsing and executing string expressions in Open 3D Engine.", diff --git a/Gems/FastNoise/gem.json b/Gems/FastNoise/gem.json index ac59fe804e..d8dfb878b4 100644 --- a/Gems/FastNoise/gem.json +++ b/Gems/FastNoise/gem.json @@ -2,6 +2,7 @@ "gem_name": "FastNoise", "display_name": "Fast Noise", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The FastNoise Gradient Gem uses the third-party, open source FastNoise library to provide a variety of high-performance noise generation algorithms.", diff --git a/Gems/GameState/gem.json b/Gems/GameState/gem.json index 7bb3cf4214..fe3a338997 100644 --- a/Gems/GameState/gem.json +++ b/Gems/GameState/gem.json @@ -2,6 +2,7 @@ "gem_name": "GameState", "display_name": "Game State", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Gem provides a generic framework to determine and manage game states and game state transitions in Open 3D Engine.", diff --git a/Gems/GameStateSamples/gem.json b/Gems/GameStateSamples/gem.json index be982b9c5b..80018ff1a8 100644 --- a/Gems/GameStateSamples/gem.json +++ b/Gems/GameStateSamples/gem.json @@ -2,6 +2,7 @@ "gem_name": "GameStateSamples", "display_name": "Game State Samples", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Game State Samples Gem provides a set of sample game states (built on top of the Game State Gem), including primary user selection, main menu, level loading, level running, and level paused.", diff --git a/Gems/Gestures/gem.json b/Gems/Gestures/gem.json index fcc56b4704..8efd389d53 100644 --- a/Gems/Gestures/gem.json +++ b/Gems/Gestures/gem.json @@ -2,6 +2,7 @@ "gem_name": "Gestures", "display_name": "Gestures", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gestures Gem provides detection for common gesture-based input actions on iOS and Android devices.", diff --git a/Gems/GradientSignal/gem.json b/Gems/GradientSignal/gem.json index e87ccfe13a..aac4c652c5 100644 --- a/Gems/GradientSignal/gem.json +++ b/Gems/GradientSignal/gem.json @@ -2,6 +2,7 @@ "gem_name": "GradientSignal", "display_name": "Gradient Signal", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Gradient Signal Gem provides a number of components for generating, modifying, and mixing gradient signals.", diff --git a/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp b/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp index bac58c3ba1..1b01f7885d 100644 --- a/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp +++ b/Gems/GraphCanvas/Code/Source/Translation/TranslationBuilder.cpp @@ -40,10 +40,14 @@ namespace GraphCanvas { AZ::Data::AssetManager::Instance().RegisterHandler(m_assetHandler.get(), assetType); } + + AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusConnect(GetUUID()); } void TranslationAssetWorker::Deactivate() { + AssetBuilderSDK::AssetBuilderCommandBus::Handler::BusDisconnect(); + if (AZ::Data::AssetManager::Instance().GetHandler(AZ::Data::AssetType{ azrtti_typeid() })) { AZ::Data::AssetManager::Instance().UnregisterHandler(m_assetHandler.get()); diff --git a/Gems/GraphCanvas/gem.json b/Gems/GraphCanvas/gem.json index 760bd157df..4762cfef35 100644 --- a/Gems/GraphCanvas/gem.json +++ b/Gems/GraphCanvas/gem.json @@ -2,6 +2,7 @@ "gem_name": "GraphCanvas", "display_name": "Graph Canvas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Graph Canvas Gem provides a C++ framework for creating custom graphical node based editors for Open 3D Engine.", diff --git a/Gems/GraphModel/gem.json b/Gems/GraphModel/gem.json index 256de75f6c..ad6b592430 100644 --- a/Gems/GraphModel/gem.json +++ b/Gems/GraphModel/gem.json @@ -2,6 +2,7 @@ "gem_name": "GraphModel", "display_name": "Graph Model", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Graph Model Gem provides a generic node graph data model framework for Open 3D Engine.", diff --git a/Gems/HttpRequestor/gem.json b/Gems/HttpRequestor/gem.json index eb1a112b0e..582bfa0071 100644 --- a/Gems/HttpRequestor/gem.json +++ b/Gems/HttpRequestor/gem.json @@ -2,6 +2,7 @@ "gem_name": "HttpRequestor", "display_name": "HTTP Requestor", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The HTTP Requestor Gem provides functionality to make asynchronous HTTP/HTTPS requests and return data through a user-provided call back function.", diff --git a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp index 81ec4b17c8..d2f86a65cd 100644 --- a/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp +++ b/Gems/ImGui/Code/Source/LYCommonMenu/ImGuiLYCommonMenu.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "ImGuiColorDefines.h" #include "LYImGuiUtils/ImGuiDrawHelpers.h" @@ -92,8 +93,36 @@ namespace ImGui void ImGuiLYCommonMenu::OnImGuiUpdate() { + float dpiScalingFactor = 1.0f; + ImGuiManagerBus::BroadcastResult(dpiScalingFactor, &ImGuiManagerBus::Events::GetDpiScalingFactor); + + // Utility function to calculate the size in device pixels based on the current DPI + const auto dpiAwareSizeFn = [dpiScalingFactor](float size) + { + return dpiScalingFactor * size; + }; + + AZStd::optional viewportBorderPaddingOpt; + AzFramework::ViewportBorderRequestBus::BroadcastResult( + viewportBorderPaddingOpt, &AzFramework::ViewportBorderRequestBus::Events::GetViewportBorderPadding); + + AzFramework::ViewportBorderPadding viewportBorderPadding = viewportBorderPaddingOpt.value_or(AzFramework::ViewportBorderPadding{}); + // Utility function to return the current offset (scaled by DPI) if a viewport border + // is active (otherwise 0.0) + auto dpiAwareBorderOffsetFn = [&viewportBorderPaddingOpt, &dpiAwareSizeFn](float size) + { + return viewportBorderPaddingOpt.has_value() ? dpiAwareSizeFn(size) : 0.0f; + }; + + // Shift the menu down if a viewport border is active + ImVec2 cachedSafeArea = ImGui::GetStyle().DisplaySafeAreaPadding; + ImGui::GetStyle().DisplaySafeAreaPadding = ImVec2(cachedSafeArea.x, cachedSafeArea.y + dpiAwareSizeFn(viewportBorderPadding.m_top)); + if (ImGui::BeginMainMenuBar()) { + // Constant to shift right aligned menu items by (distance to the left) when a viewport border is active + const float rightAlignedBorderOffset = dpiAwareBorderOffsetFn(36.0f); + // Get Discrete Input state now, we will use it both inside the ImGui SubMenu, and along the main task bar ( when it is on ) bool discreteInputEnabled = false; ImGuiManagerBus::BroadcastResult(discreteInputEnabled, &IImGuiManager::GetEnableDiscreteInputMode); @@ -101,7 +130,8 @@ namespace ImGui // Input Mode Display { const float prevCursorPos = ImGui::GetCursorPosX(); - ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 300.0f); + ImGui::SetCursorPosX( + ImGui::GetWindowWidth() - dpiAwareSizeFn(300.0f + viewportBorderPadding.m_right) - rightAlignedBorderOffset); AZStd::string inputTitle = "Input: "; if (!discreteInputEnabled) @@ -152,7 +182,7 @@ namespace ImGui } // Add some space before the first menu so it won't overlap with view control buttons - ImGui::SetCursorPosX(40.f); + ImGui::SetCursorPosX(dpiAwareSizeFn(40.0f + viewportBorderPadding.m_left)); // Main Open 3D Engine menu if (ImGui::BeginMenu("O3DE")) @@ -557,11 +587,12 @@ namespace ImGui // End LY Common Tools menu ImGui::EndMenu(); } - const int labelSize{ 100 }; - const int buttonSize{ 40 }; + + const float labelSize = dpiAwareSizeFn(100.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset; + const float buttonSize = dpiAwareSizeFn(40.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset; ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiMainMenuUpdate); ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - labelSize); - float backgroundHeight = ImGui::GetTextLineHeight() + 3; + float backgroundHeight = ImGui::GetTextLineHeight() + dpiAwareSizeFn(3.0f); ImVec2 cursorPos = ImGui::GetCursorScreenPos(); ImGui::GetWindowDrawList()->AddRectFilled( cursorPos, ImVec2(cursorPos.x + labelSize, cursorPos.y + backgroundHeight), IM_COL32(0, 115, 187, 255)); @@ -580,6 +611,9 @@ namespace ImGui ImGui::EndMainMenuBar(); } + // Restore original safe area. + ImGui::GetStyle().DisplaySafeAreaPadding = cachedSafeArea; + // Update Contextual Controller Window if (m_controllerLegendWindowVisible) { diff --git a/Gems/ImGui/gem.json b/Gems/ImGui/gem.json index c1d89d1728..dfc12f64b4 100644 --- a/Gems/ImGui/gem.json +++ b/Gems/ImGui/gem.json @@ -2,6 +2,7 @@ "gem_name": "ImGui", "display_name": "Immediate Mode GUI (IMGUI)", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Immediate Mode GUI Gem provides the 3rdParty library IMGUI which can be used to create run time immediate mode overlays for debugging and profiling information in Open 3D Engine.", diff --git a/Gems/InAppPurchases/gem.json b/Gems/InAppPurchases/gem.json index 1f1debd5fb..21febbfeca 100644 --- a/Gems/InAppPurchases/gem.json +++ b/Gems/InAppPurchases/gem.json @@ -2,6 +2,7 @@ "gem_name": "InAppPurchases", "display_name": "In-App Purchases", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The In-App Purchases Gem provides functionality for in app purchases for iOS and Android.", diff --git a/Gems/LandscapeCanvas/gem.json b/Gems/LandscapeCanvas/gem.json index ce0c64b75d..8653da40e1 100644 --- a/Gems/LandscapeCanvas/gem.json +++ b/Gems/LandscapeCanvas/gem.json @@ -2,6 +2,7 @@ "gem_name": "LandscapeCanvas", "display_name": "Landscape Canvas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Landscape Canvas Gem provides the Landscape Canvas editor, a node-based graph tool for authoring workflows to populate landscape with dynamic vegetation.", diff --git a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp index c9fc656488..168a3640a8 100644 --- a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp @@ -9,43 +9,40 @@ #include "BundlingSystemComponent.h" #include +#include +#include #include #include +#include #include -#include - -#include -#include - #include + namespace LmbrCentral { const char bundleRoot[] = "@products@"; + // Calls the LoadBundles method + static void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs); + // Calls the UnloadBundles method + static void ConsoleCommandUnloadBundles(const AZ::ConsoleCommandContainer& commandArgs); + + AZ_CONSOLEFREEFUNC("loadbundles", ConsoleCommandLoadBundles, AZ::ConsoleFunctorFlags::Null, "Load Asset Bundles"); + AZ_CONSOLEFREEFUNC("unloadbundles", ConsoleCommandUnloadBundles, AZ::ConsoleFunctorFlags::Null, "Unload Asset Bundles"); + void BundlingSystemComponent::Activate() { BundlingSystemRequestBus::Handler::BusConnect(); - CrySystemEventBus::Handler::BusConnect(); AZ::IO::ArchiveNotificationBus::Handler::BusConnect(); } void BundlingSystemComponent::Deactivate() { AZ::IO::ArchiveNotificationBus::Handler::BusDisconnect(); - CrySystemEventBus::Handler::BusDisconnect(); BundlingSystemRequestBus::Handler::BusDisconnect(); } - void BundlingSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) - { - AZ_UNUSED(systemInitParams); - - system.GetIConsole()->AddCommand("loadbundles", ConsoleCommandLoadBundles); - system.GetIConsole()->AddCommand("unloadbundles", ConsoleCommandUnloadBundles); - } - void BundlingSystemComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -58,7 +55,7 @@ namespace LmbrCentral AZStd::vector BundlingSystemComponent::GetBundleList(const char* bundlePath, const char* bundleExtension) const { - AZStd::string fileFilter{ AZStd::string::format("*%s",bundleExtension) }; + AZStd::string fileFilter{ AZStd::string::format("*%s", bundleExtension) }; AZStd::vector bundleList; AZ::IO::FileIOBase::GetInstance()->FindFiles(bundlePath, fileFilter.c_str(), [&bundleList](const char* foundPath) -> bool @@ -73,29 +70,28 @@ namespace LmbrCentral return bundleList; } - void BundlingSystemComponent::ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs) + void ConsoleCommandLoadBundles(const AZ::ConsoleCommandContainer& commandArgs) { const char defaultBundleFolder[] = "bundles"; const char defaultBundleExtension[] = ".pak"; - const char* bundleFolder = pCmdArgs->GetArgCount() > 1 ? pCmdArgs->GetArg(1) : defaultBundleFolder; - const char* bundleExtension = pCmdArgs->GetArgCount() > 2 ? pCmdArgs->GetArg(2) : defaultBundleExtension; + AZ::CVarFixedString bundleFolder = commandArgs.size() > 0 ? AZ::CVarFixedString(commandArgs[0]) : defaultBundleFolder; + AZ::CVarFixedString bundleExtension = commandArgs.size() > 1 ? AZ::CVarFixedString(commandArgs[1]) : defaultBundleExtension; - BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder, bundleExtension); + BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::LoadBundles, bundleFolder.c_str(), bundleExtension.c_str()); } - void BundlingSystemComponent::ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs) + void ConsoleCommandUnloadBundles([[maybe_unused]] const AZ::ConsoleCommandContainer& commandArgs) { - AZ_UNUSED(pCmdArgs); BundlingSystemRequestBus::Broadcast(&BundlingSystemRequestBus::Events::UnloadBundles); } void BundlingSystemComponent::UnloadBundles() { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) + auto archive = AZ::Interface::Get(); + if (!archive) { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to unload bundles!"); + AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!"); return; } if (!m_bundleModeBundles.size()) @@ -106,7 +102,7 @@ namespace LmbrCentral AZStd::lock_guard openBundleLock(m_bundleModeMutex); for (const auto& thisBundle : m_bundleModeBundles) { - if (crySystem->GetIPak()->ClosePack(thisBundle.c_str())) + if (archive->ClosePack(thisBundle.c_str())) { AZ_TracePrintf("BundlingSystem", "Unloaded %s\n",thisBundle.c_str()); } @@ -128,15 +124,8 @@ namespace LmbrCentral return; } - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load bundles!"); - return; - } - - auto cryPak = crySystem->GetIPak(); - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load bundles!"); return; @@ -152,8 +141,8 @@ namespace LmbrCentral } } AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath); - if (cryPak->OpenPack(bundleRoot, thisBundle.c_str())) + AZ::StringFunc::Path::Join(bundleRoot, thisBundle.c_str(), bundlePath); + if (archive->OpenPack(bundleRoot, thisBundle.c_str())) { AZ_TracePrintf("BundlingSystem", "Loaded bundle %s\n",bundlePath.c_str()); m_bundleModeBundles.emplace_back(AZStd::move(bundlePath)); @@ -230,28 +219,21 @@ namespace LmbrCentral void BundlingSystemComponent::OpenDependentBundles(const char* bundleName, AZStd::shared_ptr bundleManifest) { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't Get ISystem to load dependent bundles for %s", bundleName); - return; - } - - auto cryPak{ crySystem->GetIPak() }; - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't Get IArchive to load dependent bundles for %s", bundleName); return; } AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath); + AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath); for (const auto& thisBundle : bundleManifest->GetDependentBundleNames()) { AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); + AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); - if (!cryPak->OpenPack(bundleRoot, bundlePath.c_str())) + if (!archive->OpenPack(bundleRoot, bundlePath.c_str())) { // We're not bailing here intentionally - try to open the remaining bundles AZ_Warning("BundlingSystem", false, "Failed to open dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName); @@ -300,28 +282,21 @@ namespace LmbrCentral void BundlingSystemComponent::CloseDependentBundles(const char* bundleName, AZStd::shared_ptr bundleManifest) { - ISystem* crySystem{ GetISystem() }; - if (!crySystem) - { - AZ_Error("BundlingSystem", false, "Couldn't get ISystem to close dependent bundles for %s", bundleName); - return; - } - - auto cryPak{ crySystem->GetIPak() }; - if (!cryPak) + auto archive = AZ::Interface::Get(); + if (!archive) { AZ_Error("BundlingSystem", false, "Couldn't get IArchive to close dependent bundles for %s", bundleName); return; } AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(bundleName, folderPath); + AZ::StringFunc::Path::GetFolderPath(bundleName, folderPath); for (const auto& thisBundle : bundleManifest->GetDependentBundleNames()) { AZStd::string bundlePath; - AzFramework::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); + AZ::StringFunc::Path::Join(folderPath.c_str(), thisBundle.c_str(), bundlePath); - if (!cryPak->ClosePack(bundlePath.c_str())) + if (!archive->ClosePack(bundlePath.c_str())) { // We're not bailing here intentionally - try to close the remaining bundles AZ_Warning("BundlingSystem", false, "Failed to close dependent bundle %s of bundle %s", bundlePath.c_str(), bundleName); diff --git a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h index 15fdf1103c..e7c8775530 100644 --- a/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h +++ b/Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h @@ -19,11 +19,8 @@ #include -#include #include -struct IConsoleCmdArgs; - namespace AzFramework { class AssetBundleManifest; @@ -42,10 +39,9 @@ namespace LmbrCentral * System component for managing bundles */ class BundlingSystemComponent - : public AZ::Component, - public BundlingSystemRequestBus::Handler, - public CrySystemEventBus::Handler, - public AZ::IO::ArchiveNotificationBus::Handler + : public AZ::Component + , public BundlingSystemRequestBus::Handler + , public AZ::IO::ArchiveNotificationBus::Handler { public: AZ_COMPONENT(BundlingSystemComponent, "{0FB7153D-EE80-4B1C-9584-134270401AAF}"); @@ -70,13 +66,6 @@ namespace LmbrCentral void BundleOpened(const char* bundleName, AZStd::shared_ptr bundleManifest, const char* nextBundle, AZStd::shared_ptr bundleCatalog) override; void BundleClosed(const char* bundleName) override; - // CrySystemEventBus - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override; - - // Calls the LoadBundles method - static void ConsoleCommandLoadBundles(IConsoleCmdArgs* pCmdArgs); - // Calls the UnloadBundles method - static void ConsoleCommandUnloadBundles(IConsoleCmdArgs* pCmdArgs); AZStd::vector GetBundleList(const char* bundlePath, const char* bundleExtension) const; diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp index e509890efa..027d19fdef 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.cpp @@ -83,8 +83,6 @@ namespace LmbrCentral { - static const char* s_assetCatalogFilename = "assetcatalog.xml"; - using LmbrCentralAllocatorScope = AZ::AllocatorScope; // This component boots the required allocators for LmbrCentral everywhere but AssetBuilders @@ -353,8 +351,7 @@ namespace LmbrCentral AZ_Assert(AZ::Data::AssetManager::IsReady(), "Asset manager isn't ready!"); // Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService". - auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); - if (assetCatalog) + if (auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); assetCatalog) { assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo::Uuid()); assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo::Uuid()); @@ -373,7 +370,6 @@ namespace LmbrCentral assetCatalog->AddExtension("cax"); } - CrySystemEventBus::Handler::BusConnect(); AZ::Data::AssetManagerNotificationBus::Handler::BusConnect(); @@ -445,7 +441,6 @@ namespace LmbrCentral m_unhandledAssetInfo.clear(); AZ::Data::AssetManagerNotificationBus::Handler::BusDisconnect(); - CrySystemEventBus::Handler::BusDisconnect(); // AssetHandler's destructor calls Unregister() m_assetHandlers.clear(); @@ -456,42 +451,6 @@ namespace LmbrCentral } m_allocatorShutdowns.clear(); } - - void LmbrCentralSystemComponent::OnCrySystemPreInitialize([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams) - { - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StartMonitoringAssets); - } - - void LmbrCentralSystemComponent::OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) - { -#if !defined(AZ_MONOLITHIC_BUILD) - // When module is linked dynamically, we must set our gEnv pointer. - // When module is linked statically, we'll share the application's gEnv pointer. - gEnv = system.GetGlobalEnvironment(); -#endif - - // Enable catalog now that application's asset root is set. - if (system.GetGlobalEnvironment()->IsEditor()) - { - // In the editor, we build the catalog by scanning the disk. - if (systemInitParams.pUserCallback) - { - systemInitParams.pUserCallback->OnInitProgress("Refreshing asset catalog..."); - } - } - - // load the catalog from disk (supported over VFS). - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, LoadCatalog, AZStd::string::format("@products@/%s", s_assetCatalogFilename).c_str()); - } - - void LmbrCentralSystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system) - { - EBUS_EVENT(AZ::Data::AssetCatalogRequestBus, StopMonitoringAssets); - -#if !defined(AZ_MONOLITHIC_BUILD) - gEnv = nullptr; -#endif - } } // namespace LmbrCentral #if !defined(LMBR_CENTRAL_EDITOR) diff --git a/Gems/LmbrCentral/Code/Source/LmbrCentral.h b/Gems/LmbrCentral/Code/Source/LmbrCentral.h index 9a0c327ea7..650061dce6 100644 --- a/Gems/LmbrCentral/Code/Source/LmbrCentral.h +++ b/Gems/LmbrCentral/Code/Source/LmbrCentral.h @@ -15,8 +15,6 @@ #include #include -#include - /*! * \namespace LmbrCentral * LmbrCentral ties together systems from CryEngine and systems from the AZ framework. @@ -49,7 +47,6 @@ namespace LmbrCentral */ class LmbrCentralSystemComponent : public AZ::Component - , private CrySystemEventBus::Handler , private AZ::Data::AssetManagerNotificationBus::Handler { public: @@ -71,13 +68,6 @@ namespace LmbrCentral void Deactivate() override; //////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// - // CrySystemEvents - void OnCrySystemPreInitialize(ISystem& system, const SSystemInitParams& systemInitParams) override; - void OnCrySystemInitialized(ISystem& system, const SSystemInitParams& systemInitParams) override; - void OnCrySystemShutdown(ISystem& system) override; - //////////////////////////////////////////////////////////////////////////// - AZStd::vector > m_assetHandlers; AZStd::vector > m_unhandledAssetInfo; AZStd::vector> m_allocatorShutdowns; diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp index e50fbbe56c..697c5c3b06 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LevelBuilderTest.cpp @@ -99,7 +99,9 @@ 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(m_descriptor); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp index a9b36d4624..c62ed92c83 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/LuaBuilderTests.cpp @@ -31,7 +31,9 @@ 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(m_descriptor); diff --git a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp index f679a3502d..574fd4edfb 100644 --- a/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/Builders/SeedBuilderTests.cpp @@ -22,7 +22,9 @@ class SeedBuilderTests 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(AZ::ComponentApplication::Descriptor()); diff --git a/Gems/LmbrCentral/gem.json b/Gems/LmbrCentral/gem.json index 9fca421538..a0a6ea813f 100644 --- a/Gems/LmbrCentral/gem.json +++ b/Gems/LmbrCentral/gem.json @@ -2,6 +2,7 @@ "gem_name": "LmbrCentral", "display_name": "O3DE Core (LmbrCentral)", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The O3DE Core (LmbrCentral) Gem provides required code and assets for running Open 3D Engine Editor.", diff --git a/Gems/LocalUser/gem.json b/Gems/LocalUser/gem.json index f86e6e1bf6..5199b4f777 100644 --- a/Gems/LocalUser/gem.json +++ b/Gems/LocalUser/gem.json @@ -2,6 +2,7 @@ "gem_name": "LocalUser", "display_name": "Local User", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Local User Gem provides functionality for mapping local user ids to local player slots and managing local user profiles.", diff --git a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp index 60074ac0a0..59be8a85e1 100644 --- a/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp +++ b/Gems/LyShine/Code/Tests/LyShineEditorTest.cpp @@ -85,7 +85,9 @@ protected: 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(m_descriptor); diff --git a/Gems/LyShine/gem.json b/Gems/LyShine/gem.json index e2da8afa4d..f3c3fc2c67 100644 --- a/Gems/LyShine/gem.json +++ b/Gems/LyShine/gem.json @@ -2,6 +2,7 @@ "gem_name": "LyShine", "display_name": "LyShine", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The LyShine Gem provides the runtime UI system and creation tools for Open 3D Engine projects.", diff --git a/Gems/LyShineExamples/gem.json b/Gems/LyShineExamples/gem.json index 122273f6d9..74d2b6fe51 100644 --- a/Gems/LyShineExamples/gem.json +++ b/Gems/LyShineExamples/gem.json @@ -2,6 +2,7 @@ "gem_name": "LyShineExamples", "display_name": "LyShine Examples", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The LyShine Examples Gem provides example code and assets for LyShine, the runtime UI system and editor for Open 3D Engine projects.", diff --git a/Gems/Maestro/gem.json b/Gems/Maestro/gem.json index 5149df7c14..d8f884e271 100644 --- a/Gems/Maestro/gem.json +++ b/Gems/Maestro/gem.json @@ -2,6 +2,7 @@ "gem_name": "Maestro", "display_name": "Maestro Cinematics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Maestro Cinematics Gem provides Track View, Open 3D Engine's animated sequence and cinematics editor.", diff --git a/Gems/MessagePopup/gem.json b/Gems/MessagePopup/gem.json index 05d36bc2df..fb44dd93a5 100644 --- a/Gems/MessagePopup/gem.json +++ b/Gems/MessagePopup/gem.json @@ -2,6 +2,7 @@ "gem_name": "MessagePopup", "display_name": "Message Popup", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Message Popup Gem provides an example implementation of popup messages using LyShine in Open 3D Engine.", diff --git a/Gems/Metastream/gem.json b/Gems/Metastream/gem.json index 862b17dd1d..309f16b221 100644 --- a/Gems/Metastream/gem.json +++ b/Gems/Metastream/gem.json @@ -2,6 +2,7 @@ "gem_name": "Metastream", "display_name": "Metastream", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Metastream Gem provides functionality for an HTTP server that allows broadcasters to customize game streams with overlays of statistics and event data from a game session.", diff --git a/Gems/Microphone/gem.json b/Gems/Microphone/gem.json index 68492ea786..6e57bec854 100644 --- a/Gems/Microphone/gem.json +++ b/Gems/Microphone/gem.json @@ -2,6 +2,7 @@ "gem_name": "Microphone", "display_name": "Microphone", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Microphone Gem provides support for audio input through microphones.", diff --git a/Gems/Multiplayer/gem.json b/Gems/Multiplayer/gem.json index 47dbddfcbd..f895c78c5d 100644 --- a/Gems/Multiplayer/gem.json +++ b/Gems/Multiplayer/gem.json @@ -2,6 +2,7 @@ "gem_name": "Multiplayer", "display_name": "Multiplayer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Gem provides a public API for multiplayer functionality such as connecting and hosting.", diff --git a/Gems/MultiplayerCompression/gem.json b/Gems/MultiplayerCompression/gem.json index 7dd31476e3..98156cc404 100644 --- a/Gems/MultiplayerCompression/gem.json +++ b/Gems/MultiplayerCompression/gem.json @@ -2,6 +2,7 @@ "gem_name": "MultiplayerCompression", "display_name": "Multiplayer Compression", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Multiplayer Compression Gem provides an open source Compressor for use with AzNetworking's transport layer.", diff --git a/Gems/NvCloth/gem.json b/Gems/NvCloth/gem.json index 019ce23742..86a70b9373 100644 --- a/Gems/NvCloth/gem.json +++ b/Gems/NvCloth/gem.json @@ -3,6 +3,7 @@ "display_name": "NVIDIA Cloth (NvCloth)", "license": "Apache-2.0 Or MIT", "origin": "Open 3D Engine - o3de.org", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "type": "Code", "summary": "The NVIDIA Cloth Gem provides functionality to create fast, realistic cloth simulation with the NVIDIA Cloth library.", "canonical_tags": [ diff --git a/Gems/PhysX/gem.json b/Gems/PhysX/gem.json index bacbcf2dee..990d7502d8 100644 --- a/Gems/PhysX/gem.json +++ b/Gems/PhysX/gem.json @@ -2,6 +2,7 @@ "gem_name": "PhysX", "display_name": "PhysX", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The PhysX Gem provides physics simulation with NVIDIA PhysX including static and dynamic rigid body simulation, force regions, ragdolls, and dynamic PhysX joints.", diff --git a/Gems/PhysXDebug/gem.json b/Gems/PhysXDebug/gem.json index ece0774210..2d9f4dc24d 100644 --- a/Gems/PhysXDebug/gem.json +++ b/Gems/PhysXDebug/gem.json @@ -2,6 +2,7 @@ "gem_name": "PhysXDebug", "display_name": "PhysX Debug", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The PhysX Debug Gem provides debugging functionality and visualizations for NVIDIA PhysX in Open 3D Engine.", diff --git a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp index ab6770c3fe..d33b453c01 100644 --- a/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp +++ b/Gems/Prefab/PrefabBuilder/PrefabBuilderTests.cpp @@ -174,7 +174,9 @@ 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); AZ::ComponentApplication::Descriptor desc; diff --git a/Gems/Prefab/PrefabBuilder/gem.json b/Gems/Prefab/PrefabBuilder/gem.json index ba78f96358..2233a7235e 100644 --- a/Gems/Prefab/PrefabBuilder/gem.json +++ b/Gems/Prefab/PrefabBuilder/gem.json @@ -2,6 +2,7 @@ "gem_name": "PrefabBuilder", "display_name": "Prefab Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Prefab Builder Gem provides an Asset Processor module for prefabs, which are complex assets built by combining smaller entities.", diff --git a/Gems/Presence/gem.json b/Gems/Presence/gem.json index a70953ae4e..624af2e62d 100644 --- a/Gems/Presence/gem.json +++ b/Gems/Presence/gem.json @@ -2,6 +2,7 @@ "gem_name": "Presence", "display_name": "Presence", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Presence Gem provides a target platform agnostic interface for Presence services.", diff --git a/Gems/PrimitiveAssets/gem.json b/Gems/PrimitiveAssets/gem.json index 4ad3cb62ad..0e4c4689dc 100644 --- a/Gems/PrimitiveAssets/gem.json +++ b/Gems/PrimitiveAssets/gem.json @@ -2,6 +2,7 @@ "gem_name": "PrimitiveAssets", "display_name": "Primitive Assets", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Primitive Assets Gem provides primitive shape mesh assets with physics enabled.", diff --git a/Gems/Profiler/gem.json b/Gems/Profiler/gem.json index 2f121d7618..b160bc4b3c 100644 --- a/Gems/Profiler/gem.json +++ b/Gems/Profiler/gem.json @@ -2,6 +2,7 @@ "gem_name": "Profiler", "display_name": "Profiler", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "A collection of utilities for capturing performance data", diff --git a/Gems/PythonAssetBuilder/gem.json b/Gems/PythonAssetBuilder/gem.json index ce30ba9e82..8046104f03 100644 --- a/Gems/PythonAssetBuilder/gem.json +++ b/Gems/PythonAssetBuilder/gem.json @@ -2,6 +2,7 @@ "gem_name": "PythonAssetBuilder", "display_name": "Python Asset Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Python Asset Builder Gem provides functionality to implement custom asset builders in Python for Asset Processor.", diff --git a/Gems/QtForPython/gem.json b/Gems/QtForPython/gem.json index f83be43342..17d3a26f21 100644 --- a/Gems/QtForPython/gem.json +++ b/Gems/QtForPython/gem.json @@ -2,6 +2,7 @@ "gem_name": "QtForPython", "display_name": "Qt for Python", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Qt for Python Gem provides the PySide2 Python libraries to manage Qt widgets.", diff --git a/Gems/SaveData/gem.json b/Gems/SaveData/gem.json index 333b4682d2..1ee5a54cec 100644 --- a/Gems/SaveData/gem.json +++ b/Gems/SaveData/gem.json @@ -2,6 +2,7 @@ "gem_name": "SaveData", "display_name": "Save Data", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Save Data Gem provides a platform independent API to save and load persistent user data in Open 3D Engine projects.", diff --git a/Gems/SceneLoggingExample/gem.json b/Gems/SceneLoggingExample/gem.json index 16961b9c5b..ad950f3992 100644 --- a/Gems/SceneLoggingExample/gem.json +++ b/Gems/SceneLoggingExample/gem.json @@ -2,6 +2,7 @@ "gem_name": "SceneLoggingExample", "display_name": "Scene Logging Example", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The Scene Logging Example Gem demonstrates the basics of extending the Open 3D Engine Scene API by adding additional logging to the pipeline.", diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp index 2709cd40b7..15d50bcfa3 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderPhasesTests.cpp @@ -139,7 +139,9 @@ public: 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(AZ::ComponentApplication::Descriptor()); diff --git a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp index 3a7b20553e..a1ca5be766 100644 --- a/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp +++ b/Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp @@ -35,7 +35,9 @@ protected: 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(AZ::ComponentApplication::Descriptor()); diff --git a/Gems/SceneProcessing/gem.json b/Gems/SceneProcessing/gem.json index de1dfeb23f..576460d0a9 100644 --- a/Gems/SceneProcessing/gem.json +++ b/Gems/SceneProcessing/gem.json @@ -2,6 +2,7 @@ "gem_name": "SceneProcessing", "display_name": "Scene Processing", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scene Processing Gem provides Scene Settings, a tool you can use to specify the default settings for processing asset files for actors, meshes, motions, and PhysX.", diff --git a/Gems/ScriptCanvas/gem.json b/Gems/ScriptCanvas/gem.json index 621ea42961..fc8a504917 100644 --- a/Gems/ScriptCanvas/gem.json +++ b/Gems/ScriptCanvas/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvas", "display_name": "Script Canvas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Gem provides Open 3D Engine's visual scripting environment, Script Canvas.", diff --git a/Gems/ScriptCanvasDeveloper/gem.json b/Gems/ScriptCanvasDeveloper/gem.json index ee1bfcec84..51aed9d9fa 100644 --- a/Gems/ScriptCanvasDeveloper/gem.json +++ b/Gems/ScriptCanvasDeveloper/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvasDeveloperGem", "display_name": "Script Canvas Developer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Developer Gem provides a suite of utility features for the development and debugging of Script Canvas systems.", diff --git a/Gems/ScriptCanvasPhysics/gem.json b/Gems/ScriptCanvasPhysics/gem.json index 417fa6893a..2e35e85c46 100644 --- a/Gems/ScriptCanvasPhysics/gem.json +++ b/Gems/ScriptCanvasPhysics/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvasPhysics", "display_name": "Script Canvas Physics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Canvas Physics Gem provides Script Canvas nodes for physics scene queries such as raycasts.", diff --git a/Gems/ScriptCanvasTesting/gem.json b/Gems/ScriptCanvasTesting/gem.json index c45d2ac165..5cb718e674 100644 --- a/Gems/ScriptCanvasTesting/gem.json +++ b/Gems/ScriptCanvasTesting/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptCanvasTesting", "display_name": "Script Canvas Testing", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Script Canvas Testing Gem provides a framework for testing for and with Script Canvas.", diff --git a/Gems/ScriptEvents/gem.json b/Gems/ScriptEvents/gem.json index 386d9b5614..5640e08489 100644 --- a/Gems/ScriptEvents/gem.json +++ b/Gems/ScriptEvents/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptEvents", "display_name": "Script Events", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Script Events Gem provides a framework for creating event assets usable from any scripting solution in Open 3D Engine.", diff --git a/Gems/ScriptedEntityTweener/gem.json b/Gems/ScriptedEntityTweener/gem.json index c51f05df9a..477345093c 100644 --- a/Gems/ScriptedEntityTweener/gem.json +++ b/Gems/ScriptedEntityTweener/gem.json @@ -2,6 +2,7 @@ "gem_name": "ScriptedEntityTweener", "display_name": "Scripted Entity Tweener", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Scripted Entity Tweener Gem provides a script driven animation system for Open 3D Engine projects.", diff --git a/Gems/SliceFavorites/gem.json b/Gems/SliceFavorites/gem.json index 84f42fc1a2..c4536dc042 100644 --- a/Gems/SliceFavorites/gem.json +++ b/Gems/SliceFavorites/gem.json @@ -2,6 +2,7 @@ "gem_name": "SliceFavorites", "display_name": "SliceFavorites", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "Add the ability to favorite a slice to allow easy access and instantiation", diff --git a/Gems/StartingPointCamera/gem.json b/Gems/StartingPointCamera/gem.json index 613eb76267..1033770022 100644 --- a/Gems/StartingPointCamera/gem.json +++ b/Gems/StartingPointCamera/gem.json @@ -2,6 +2,7 @@ "gem_name": "StartingPointCamera", "display_name": "Starting Point Camera", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Camera Gem provides the behaviors used with the Camera Framework Gem to define a camera rig.", diff --git a/Gems/StartingPointInput/gem.json b/Gems/StartingPointInput/gem.json index d2641ea27b..ee1655b794 100644 --- a/Gems/StartingPointInput/gem.json +++ b/Gems/StartingPointInput/gem.json @@ -2,6 +2,7 @@ "gem_name": "StartingPointInput", "display_name": "Starting Point Input", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Input Gem provides functionality to map low-level input events to high-level actions.", diff --git a/Gems/StartingPointMovement/gem.json b/Gems/StartingPointMovement/gem.json index 7def6da768..188d8483bc 100644 --- a/Gems/StartingPointMovement/gem.json +++ b/Gems/StartingPointMovement/gem.json @@ -2,6 +2,7 @@ "gem_name": "StartingPointMovement", "display_name": "Starting Point Movement", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Starting Point Movement Gem provides a series of Lua scripts that listen and respond to input events and trigger transform operations such as translation and rotation.", diff --git a/Gems/SurfaceData/gem.json b/Gems/SurfaceData/gem.json index 51a134d5df..d16254040f 100644 --- a/Gems/SurfaceData/gem.json +++ b/Gems/SurfaceData/gem.json @@ -2,6 +2,7 @@ "gem_name": "SurfaceData", "display_name": "Surface Data", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Surface Data Gem provides functionality to emit signals or tags from surfaces such as meshes and terrain.", diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 231d5abc28..06b7320a06 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -151,24 +151,29 @@ namespace Terrain { float maxSample = 0.0f; terrainExists = false; - - GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); - - // Right now, when the list contains multiple entries, we will use the highest point from each gradient. - // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value - // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient API - // to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we could just - // make this list a prioritized list from top to bottom for any points that overlap. - for (auto& gradientId : m_configuration.m_gradientEntities) + AZ_WarningOnce("Terrain", !m_isRequestInProgress, "Detected cyclic dependences with terrain height entity references"); + if (!m_isRequestInProgress) { - // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain - // to *not* exist at a specific point. - terrainExists = true; - - float sample = 0.0f; - GradientSignal::GradientRequestBus::EventResult( - sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); - maxSample = AZ::GetMax(maxSample, sample); + m_isRequestInProgress = true; + GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f)); + + // Right now, when the list contains multiple entries, we will use the highest point from each gradient. + // This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value + // of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient + // API to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we + // could just make this list a prioritized list from top to bottom for any points that overlap. + for (auto& gradientId : m_configuration.m_gradientEntities) + { + // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain + // to *not* exist at a specific point. + terrainExists = true; + + float sample = 0.0f; + GradientSignal::GradientRequestBus::EventResult( + sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + maxSample = AZ::GetMax(maxSample, sample); + } + m_isRequestInProgress = false; } const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h index 6c3fd7b820..b5b1a44192 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.h @@ -91,6 +91,9 @@ namespace Terrain AZ::Vector2 m_cachedHeightQueryResolution{ 1.0f, 1.0f }; AZ::Aabb m_cachedShapeBounds; + // prevent recursion in case user attaches cyclic dependences + mutable bool m_isRequestInProgress{ false }; + LmbrCentral::DependencyMonitor m_dependencyMonitor; }; } diff --git a/Gems/Terrain/gem.json b/Gems/Terrain/gem.json index cd72a91708..0ca470c4d3 100644 --- a/Gems/Terrain/gem.json +++ b/Gems/Terrain/gem.json @@ -2,6 +2,7 @@ "gem_name": "Terrain", "display_name": "Terrain", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "summary": "The Terrain Gem is an experimental terrain system. The terrain system maps height, color, and surface data to regions of the world, provides gradient-based and shape-based authoring tools and workflows, includes specialized rendering for efficient display, and integrates with physics for physical simulation.", "canonical_tags": [ diff --git a/Gems/TestAssetBuilder/gem.json b/Gems/TestAssetBuilder/gem.json index ba7568005f..68ed706499 100644 --- a/Gems/TestAssetBuilder/gem.json +++ b/Gems/TestAssetBuilder/gem.json @@ -2,6 +2,7 @@ "gem_name": "TestAssetBuilder", "display_name": "Test Asset Builder", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Test Asset Builder Gem is used to feature test Asset Processor.", diff --git a/Gems/TextureAtlas/gem.json b/Gems/TextureAtlas/gem.json index 345e085359..142f0fb082 100644 --- a/Gems/TextureAtlas/gem.json +++ b/Gems/TextureAtlas/gem.json @@ -2,6 +2,7 @@ "gem_name": "TextureAtlas", "display_name": "Texture Atlas", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Texture Atlas Gem provides the formatting for texture atlases from 2D textures for LyShine.", diff --git a/Gems/TickBusOrderViewer/gem.json b/Gems/TickBusOrderViewer/gem.json index dc5cb6f66c..a6a6fd23ac 100644 --- a/Gems/TickBusOrderViewer/gem.json +++ b/Gems/TickBusOrderViewer/gem.json @@ -2,6 +2,7 @@ "gem_name": "TickBusOrderViewer", "display_name": "Tick Bus Order Viewer", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The Tick Bus Order Viewer Gem provides a console variable that displays the order of runtime tick events.", diff --git a/Gems/Twitch/gem.json b/Gems/Twitch/gem.json index f45433bc3f..42ec1b971e 100644 --- a/Gems/Twitch/gem.json +++ b/Gems/Twitch/gem.json @@ -2,6 +2,7 @@ "gem_name": "Twitch", "display_name": "Twitch", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Twitch Gem provides access to the Twitch API v5 SDK including social functions, channels, and other APIs.", diff --git a/Gems/UiBasics/gem.json b/Gems/UiBasics/gem.json index 9a1e16a462..bb2416c235 100644 --- a/Gems/UiBasics/gem.json +++ b/Gems/UiBasics/gem.json @@ -2,6 +2,7 @@ "gem_name": "UiBasics", "display_name": "UI Basics", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Asset", "summary": "The UI Basics Gem provides a collection of basic UI prefabs such as image, text, and button, that can be used with LyShine, the Open 3D Engine runtime User Interface system and editor.", diff --git a/Gems/Vegetation/gem.json b/Gems/Vegetation/gem.json index 9dbcc3450b..75416933f0 100644 --- a/Gems/Vegetation/gem.json +++ b/Gems/Vegetation/gem.json @@ -2,6 +2,7 @@ "gem_name": "Vegetation", "display_name": "Vegetation", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Vegetation Gem provides tools to place natural-looking vegetation in Open 3D Engine.", diff --git a/Gems/VideoPlaybackFramework/gem.json b/Gems/VideoPlaybackFramework/gem.json index 9f491f47cb..381738ab4b 100644 --- a/Gems/VideoPlaybackFramework/gem.json +++ b/Gems/VideoPlaybackFramework/gem.json @@ -2,6 +2,7 @@ "gem_name": "VideoPlaybackFramework", "display_name": "Video Playback Framework", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Video Playback Framework Gem provides the interface to play back video.", diff --git a/Gems/VirtualGamepad/gem.json b/Gems/VirtualGamepad/gem.json index c6305f1754..637776ac85 100644 --- a/Gems/VirtualGamepad/gem.json +++ b/Gems/VirtualGamepad/gem.json @@ -2,6 +2,7 @@ "gem_name": "VirtualGamepad", "display_name": "Virtual Gamepad", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Code", "summary": "The Virtual Gamepad Gem provides controls that emulate a gamepad on touch screen devices.", diff --git a/Gems/WhiteBox/gem.json b/Gems/WhiteBox/gem.json index 81e0ad5f88..41865efc56 100644 --- a/Gems/WhiteBox/gem.json +++ b/Gems/WhiteBox/gem.json @@ -2,6 +2,7 @@ "gem_name": "WhiteBox", "display_name": "White Box", "license": "Apache-2.0 Or MIT", + "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt", "origin": "Open 3D Engine - o3de.org", "type": "Tool", "summary": "The White Box Gem provides White Box rapid design components for Open 3D Engine.", diff --git a/Registry/application_lifecycle_events.setreg b/Registry/application_lifecycle_events.setreg new file mode 100644 index 0000000000..0d9cd0f170 --- /dev/null +++ b/Registry/application_lifecycle_events.setreg @@ -0,0 +1,30 @@ +// The Lifecycle events contains the name of the event as a string +// ComponentApplication derived classes +// will set these these keys to a JSON Object indicate an event has occured +// A callback can be registered with the SettingsRegistry +// to be notified when that key is set +// The JSON object that is set will contain any payload data +// related to the event +{ + "O3DE" : { + "Application": { + "LifecycleEvents": { + "SystemComponentsActivated": {}, + "SystemComponentsDeactivated": {}, + "ReflectionManagerAvailable": {}, + "ReflectionManagerUnavailable": {}, + "SystemAllocatorCreated": {}, + "SystemAllocatorPendingDestruction": {}, + "SettingsRegistryAvailable": {}, + "SettingsRegistryUnavailable": {}, + "ConsoleAvailable": {}, + "ConsoleUnavailable": {}, + "GemsLoaded": {}, + "GemsUnloaded": {}, + "FileIOAvailable": {}, + "FileIOUnavailable": {}, + "LegacySystemInterfaceCreated": {} + } + } + } +} diff --git a/Templates/AssetGem/Template/gem.json b/Templates/AssetGem/Template/gem.json index 2a688857f2..2a02362256 100644 --- a/Templates/AssetGem/Template/gem.json +++ b/Templates/AssetGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Asset", "summary": "A short description of ${Name}.", diff --git a/Templates/CppToolGem/Template/gem.json b/Templates/CppToolGem/Template/gem.json index 518d831e0f..079b7152ff 100644 --- a/Templates/CppToolGem/Template/gem.json +++ b/Templates/CppToolGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of ${Name}.", diff --git a/Templates/DefaultGem/Template/gem.json b/Templates/DefaultGem/Template/gem.json index 353ad6bf8d..d4ff637bee 100644 --- a/Templates/DefaultGem/Template/gem.json +++ b/Templates/DefaultGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of ${Name}.", diff --git a/Templates/PythonToolGem/Template/gem.json b/Templates/PythonToolGem/Template/gem.json index 353ad6bf8d..d4ff637bee 100644 --- a/Templates/PythonToolGem/Template/gem.json +++ b/Templates/PythonToolGem/Template/gem.json @@ -1,7 +1,8 @@ { "gem_name": "${Name}", "display_name": "${Name}", - "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "What license ${Name} uses goes here: i.e. Apache-2.0 Or MIT", + "license_url": "", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of ${Name}.", diff --git a/cmake/Projects.cmake b/cmake/Projects.cmake index c09fe0fc6f..2c42533e35 100644 --- a/cmake/Projects.cmake +++ b/cmake/Projects.cmake @@ -132,23 +132,18 @@ function(add_project_json_external_subdirectories project_path) endif() endfunction() -# Add the projects here so the above function is found -foreach(project ${LY_PROJECTS}) - file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) - string(SHA256 full_directory_hash ${full_directory_path}) - - # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit - # when the external subdirectory contains relative paths of significant length - string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash) - - get_filename_component(project_folder_name ${project} NAME) - list(APPEND LY_PROJECTS_FOLDER_NAME ${project_folder_name}) - add_subdirectory(${project} "${project_folder_name}-${full_directory_hash}") - ly_generate_project_build_path_setreg(${full_directory_path}) - add_project_json_external_subdirectories(${full_directory_path}) - - # Get project name - o3de_read_json_key(project_name ${full_directory_path}/project.json "project_name") +function(install_project_asset_artifacts project_real_path) + # The cmake tar command has a bit of a flaw + # Any paths within the archive files it creates are relative to the current working directory. + # That means with the setup of: + # cwd = "/Cache/pc" + # project product assets = "/Cache/pc/*" + # cmake dependency registry files = "/build/bin/Release/Registry/*" + # Running the tar command would result in the assets being placed in the to layout + # correctly, but the registry files + # engine.pak/ + # ../...build/bin/Release/Registry/cmake_dependencies.*.setreg -> Not correct + # project.json -> Correct # Generate pak for project in release installs cmake_path(RELATIVE_PATH CMAKE_RUNTIME_OUTPUT_DIRECTORY BASE_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_VARIABLE install_base_runtime_output_directory) @@ -156,16 +151,24 @@ foreach(project ${LY_PROJECTS}) if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") set(install_output_folder "${CMAKE_INSTALL_PREFIX}/@install_base_runtime_output_directory@/@PAL_PLATFORM_NAME@/${CMAKE_INSTALL_CONFIG_NAME}/@LY_BUILD_PERMUTATION@") set(install_pak_output_folder "${install_output_folder}/Cache/@LY_ASSET_DEPLOY_ASSET_TYPE@") + set(runtime_output_directory_RELEASE @CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE@) if(NOT DEFINED LY_ASSET_DEPLOY_ASSET_TYPE) set(LY_ASSET_DEPLOY_ASSET_TYPE @LY_ASSET_DEPLOY_ASSET_TYPE@) endif() - message(STATUS "Generating ${install_pak_output_folder}/engine.pak from @full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + message(STATUS "Generating ${install_pak_output_folder}/engine.pak from @project_real_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") file(MAKE_DIRECTORY "${install_pak_output_folder}") - cmake_path(SET cache_product_path "@full_directory_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + cmake_path(SET cache_product_path "@project_real_path@/Cache/${LY_ASSET_DEPLOY_ASSET_TYPE}") + # Copy the generated cmake_dependencies.*.setreg files for loading gems in non-monolithic to the cache + file(GLOB gem_source_paths_setreg "${runtime_output_directory_RELEASE}/Registry/*.setreg") + # The MergeSettingsToRegistry_TargetBuildDependencyRegistry function looks for lowercase "registry" directory + file(MAKE_DIRECTORY "${cache_product_path}/registry") + file(COPY ${gem_source_paths_setreg} DESTINATION "${cache_product_path}/registry") + file(GLOB product_assets "${cache_product_path}/*") - if(product_assets) + list(APPEND pak_artifacts ${product_assets}) + if(pak_artifacts) execute_process( - COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_pak_output_folder}/engine.pak" --format=zip -- ${product_assets} + COMMAND ${CMAKE_COMMAND} -E tar "cf" "${install_pak_output_folder}/engine.pak" --format=zip -- ${pak_artifacts} WORKING_DIRECTORY "${cache_product_path}" RESULT_VARIABLE archive_creation_result ) @@ -173,11 +176,40 @@ if("${CMAKE_INSTALL_CONFIG_NAME}" MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$") message(STATUS "${install_output_folder}/engine.pak generated") endif() endif() + + # Remove copied .setreg files from the Cache directory + unset(artifacts_to_remove) + foreach(gem_source_path_setreg IN LISTS gem_source_paths_setreg) + cmake_path(GET gem_source_path_setreg FILENAME setreg_filename) + list(APPEND artifacts_to_remove "${cache_product_path}/registry/${setreg_filename}") + endforeach() + file(REMOVE ${artifacts_to_remove}) endif() ]=]) string(CONFIGURE "${install_engine_pak_template}" install_engine_pak_code @ONLY) ly_install_run_code("${install_engine_pak_code}") +endfunction() + +# Add the projects here so the above function is found +foreach(project ${LY_PROJECTS}) + file(REAL_PATH ${project} full_directory_path BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) + string(SHA256 full_directory_hash ${full_directory_path}) + + # Truncate the full_directory_hash down to 8 characters to avoid hitting the Windows 260 character path limit + # when the external subdirectory contains relative paths of significant length + string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash) + + get_filename_component(project_folder_name ${project} NAME) + list(APPEND LY_PROJECTS_FOLDER_NAME ${project_folder_name}) + add_subdirectory(${project} "${project_folder_name}-${full_directory_hash}") + ly_generate_project_build_path_setreg(${full_directory_path}) + add_project_json_external_subdirectories(${full_directory_path}) + + # Get project name + o3de_read_json_key(project_name ${full_directory_path}/project.json "project_name") + + install_project_asset_artifacts(${full_directory_path}) endforeach() diff --git a/scripts/build/Platform/Linux/asset_linux.sh b/scripts/build/Platform/Linux/asset_linux.sh index df910db646..10f7ee6b6f 100755 --- a/scripts/build/Platform/Linux/asset_linux.sh +++ b/scripts/build/Platform/Linux/asset_linux.sh @@ -9,6 +9,8 @@ set -o errexit # exit on the first failure encountered +SOURCE_DIRECTORY=${PWD} + if [[ ! -d $OUTPUT_DIRECTORY ]]; then echo [ci_build] Error: $OUTPUT_DIRECTORY was not found exit 1 @@ -22,8 +24,8 @@ fi for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g") do - echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS - ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS + echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS + ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS done popd diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index b76a950beb..84c5976215 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -37,7 +37,7 @@ "PARAMETERS": { "CONFIGURATION": "debug", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -53,7 +53,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -66,7 +66,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -80,7 +80,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest --no-tests=error", @@ -93,7 +93,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-E Gem::EMotionFX.Editor.Tests -LE (SUITE_sandbox|SUITE_awsi) -L FRAMEWORK_googletest --no-tests=error", @@ -110,7 +110,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -124,7 +124,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "AssetProcessorBatch", "ASSET_PROCESSOR_BINARY": "bin/profile/AssetProcessorBatch", @@ -142,7 +142,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_periodic", "CTEST_OPTIONS": "-L (SUITE_periodic) --no-tests=error", @@ -162,7 +162,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", "CTEST_OPTIONS": "-L (SUITE_sandbox) --no-tests=error" @@ -178,7 +178,7 @@ "PARAMETERS": { "CONFIGURATION": "profile", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "TEST_SUITE_benchmark", "CTEST_OPTIONS": "-L (SUITE_benchmark) --no-tests=error", @@ -195,7 +195,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } @@ -210,7 +210,7 @@ "PARAMETERS": { "CONFIGURATION": "release", "OUTPUT_DIRECTORY": "build/mono_linux", - "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12 -DLY_MONOLITHIC_GAME=TRUE -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all" } diff --git a/scripts/build/Platform/Linux/pipeline.json b/scripts/build/Platform/Linux/pipeline.json index d964a693ce..7f16ec6ab5 100644 --- a/scripts/build/Platform/Linux/pipeline.json +++ b/scripts/build/Platform/Linux/pipeline.json @@ -1,6 +1,6 @@ { "ENV": { - "NODE_LABEL": "linux", + "NODE_LABEL": "linux-707531fc7", "LY_3RDPARTY_PATH": "/home/lybuilder/ly/workspace/3rdParty", "TIMEOUT": 30, "WORKSPACE": "/data/workspace", @@ -17,4 +17,4 @@ "CLEAN_WORKSPACE": true } } -} \ No newline at end of file +} diff --git a/scripts/build/Platform/Mac/asset_mac.sh b/scripts/build/Platform/Mac/asset_mac.sh index f70d898e0c..96eaeab5aa 100755 --- a/scripts/build/Platform/Mac/asset_mac.sh +++ b/scripts/build/Platform/Mac/asset_mac.sh @@ -9,6 +9,8 @@ set -o errexit # exit on the first failure encountered +SOURCE_DIRECTORY=${PWD} + if [[ ! -d $OUTPUT_DIRECTORY ]]; then echo [ci_build] Error: $OUTPUT_DIRECTORY was not found exit 1 @@ -22,8 +24,8 @@ fi for project in $(echo $CMAKE_LY_PROJECTS | sed "s/;/ /g") do - echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS - ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$project --platforms=$ASSET_PROCESSOR_PLATFORMS + echo [ci_build] ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS + ${ASSET_PROCESSOR_BINARY} $ASSET_PROCESSOR_OPTIONS --project-path=$SOURCE_DIRECTORY/$project --platforms=$ASSET_PROCESSOR_PLATFORMS done popd diff --git a/scripts/build/Platform/Windows/asset_windows.cmd b/scripts/build/Platform/Windows/asset_windows.cmd index 8db0e43e31..cc266ba42a 100644 --- a/scripts/build/Platform/Windows/asset_windows.cmd +++ b/scripts/build/Platform/Windows/asset_windows.cmd @@ -9,6 +9,8 @@ REM SETLOCAL EnableDelayedExpansion +SET SOURCE_DIRECTORY=%CD% + IF NOT EXIST %OUTPUT_DIRECTORY% ( ECHO [ci_build] Error: %OUTPUT_DIRECTORY% was not found GOTO :error @@ -21,8 +23,8 @@ IF NOT EXIST %ASSET_PROCESSOR_BINARY% ( ) FOR %%P in (%CMAKE_LY_PROJECTS%) do ( - ECHO [ci_build] %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% - %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% + ECHO [ci_build] %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%SOURCE_DIRECTORY%/%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% + %ASSET_PROCESSOR_BINARY% %ASSET_PROCESSOR_OPTIONS% --project-path=%SOURCE_DIRECTORY%/%%P --platforms=%ASSET_PROCESSOR_PLATFORMS% IF NOT !ERRORLEVEL!==0 GOTO :popd_error ) diff --git a/scripts/o3de/o3de/gem_properties.py b/scripts/o3de/o3de/gem_properties.py index 39011a213a..faaa4f86ec 100644 --- a/scripts/o3de/o3de/gem_properties.py +++ b/scripts/o3de/o3de/gem_properties.py @@ -54,6 +54,8 @@ def edit_gem_props(gem_path: pathlib.Path = None, new_icon: str = None, new_requirements: str = None, new_documentation_url: str = None, + new_license: str = None, + new_license_url: str = None, new_tags: list or str = None, remove_tags: list or str = None, replace_tags: list or str = None, @@ -94,6 +96,10 @@ def edit_gem_props(gem_path: pathlib.Path = None, update_key_dict['requirements'] = new_requirements if new_documentation_url: update_key_dict['documentation_url'] = new_documentation_url + if new_license: + update_key_dict['license'] = new_license + if new_license_url: + update_key_dict['license_url'] = new_license_url update_key_dict['user_tags'] = update_values_in_key_list(gem_json_data.get('user_tags', []), new_tags, remove_tags, replace_tags) @@ -114,6 +120,8 @@ def _edit_gem_props(args: argparse) -> int: args.gem_icon, args.gem_requirements, args.gem_documentation_url, + args.gem_license, + args.gem_license_url, args.add_tags, args.remove_tags, args.replace_tags) @@ -142,6 +150,10 @@ def add_parser_args(parser): help='Sets the description of the requirements needed to use the gem.') group.add_argument('-gdu', '--gem-documentation-url', type=str, required=False, help='Sets the url for documentation of the gem.') + group.add_argument('-gl', '--gem-license', type=str, required=False, + help='Sets the name for the license of the gem.') + group.add_argument('-glu', '--gem-license-url', type=str, required=False, + help='Sets the url for the license of the gem.') group = parser.add_mutually_exclusive_group(required=False) group.add_argument('-at', '--add-tags', type=str, nargs='*', required=False, help='Adds tag(s) to user_tags property. Can be specified multiple times.') diff --git a/scripts/o3de/tests/unit_test_gem_properties.py b/scripts/o3de/tests/unit_test_gem_properties.py index 5bfbe5573a..dee5811b65 100644 --- a/scripts/o3de/tests/unit_test_gem_properties.py +++ b/scripts/o3de/tests/unit_test_gem_properties.py @@ -18,7 +18,8 @@ TEST_GEM_JSON_PAYLOAD = ''' { "gem_name": "TestGem", "display_name": "TestGem", - "license": "What license TestGem uses goes here: i.e. https://opensource.org/licenses/MIT", + "license": "MIT", + "license_url": "https://opensource.org/licenses/MIT", "origin": "The primary repo for TestGem goes here: i.e. http://www.mydomain.com", "type": "Code", "summary": "A short description of TestGem.", @@ -46,26 +47,30 @@ def init_gem_json_data(request): class TestEditGemProperties: @pytest.mark.parametrize("gem_path, gem_name, gem_new_name, gem_display, gem_origin,\ gem_type, gem_summary, gem_icon, gem_requirements, gem_documentation_url,\ - add_tags, remove_tags, replace_tags, expected_tags, expected_result", [ + gem_license, gem_license_url, add_tags, remove_tags, replace_tags,\ + expected_tags, expected_result", [ pytest.param(pathlib.PurePath('D:/TestProject'), None, 'TestGem2', 'New Gem Name', 'O3DE', 'Code', 'Gem that exercises Default Gem Template', 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', + 'Apache 2.0', 'https://www.apache.org/licenses/LICENSE-2.0', ['Physics', 'Rendering', 'Scripting'], None, None, ['TestGem', 'Physics', 'Rendering', 'Scripting'], 0), pytest.param(None, 'TestGem2', None, 'New Gem Name', 'O3DE', 'Asset', 'Gem that exercises Default Gem Template', - 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', None, - ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0), + 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', + 'Apache 2.0', 'https://www.apache.org/licenses/LICENSE-2.0', + None, ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0), pytest.param(None, 'TestGem2', None, 'New Gem Name', 'O3DE', 'Tool', 'Gem that exercises Default Gem Template', - 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', None, - None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0) + 'new_preview.png', 'Do this extra thing', 'https://o3de.org/docs/user-guide/gems/', + 'Apache 2.0', 'https://www.apache.org/licenses/LICENSE-2.0', + None, None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0) ] ) def test_edit_gem_properties(self, gem_path, gem_name, gem_new_name, gem_display, gem_origin, gem_type, gem_summary, gem_icon, gem_requirements, - gem_documentation_url, add_tags, remove_tags, replace_tags, - expected_tags, expected_result): + gem_documentation_url, gem_license, gem_license_url, add_tags, remove_tags, + replace_tags, expected_tags, expected_result): def get_gem_json_data(gem_path: pathlib.Path) -> dict: return self.gem_json.data @@ -82,7 +87,8 @@ class TestEditGemProperties: patch('o3de.manifest.get_registered', side_effect=get_gem_path) as get_registered_patch: result = gem_properties.edit_gem_props(gem_path, gem_name, gem_new_name, gem_display, gem_origin, gem_type, gem_summary, gem_icon, gem_requirements, - gem_documentation_url, add_tags, remove_tags, replace_tags) + gem_documentation_url, gem_license, gem_license_url, + add_tags, remove_tags, replace_tags) assert result == expected_result if gem_new_name: assert self.gem_json.data.get('gem_name', '') == gem_new_name @@ -100,5 +106,9 @@ class TestEditGemProperties: assert self.gem_json.data.get('requirements', '') == gem_requirements if gem_documentation_url: assert self.gem_json.data.get('documentation_url', '') == gem_documentation_url + if gem_license: + assert self.gem_json.data.get('license', '') == gem_license + if gem_license_url: + assert self.gem_json.data.get('license_url', '') == gem_license_url assert set(self.gem_json.data.get('user_tags', [])) == set(expected_tags)