Forráskód Böngészése

Moved the Asset Catalog loading from LmbrCentral to the AzFramework::Application (#4568)

* Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application

Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event.
It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread.
This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard

The AssetCatalogRequestBus uses the custom lock guard to dispatch queued
events after it has unlocked it's context mutex for the current thread.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed GetContext call from the
AssetCatalogRequests::PostThreadDispatchInvoker

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath
function to return a path

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME

The _NAME parameter was not being used before, resulting in the Console
stringified name of the function being used.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed CrySystem dependencies from the BundlingSystemComponent

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Moved the loading of the AssetCatalog from LmbrCentralSystemComponent to AzFramework Application

Modified the AssetCatalog::InitializeCatalog function to no longer rely on the TickBus to send out the `AssetCatalogEventBus::OnCatalogLoaded` event.
It now queues a function on the AssetCatalogRequestBus to send the OnCatalogLoaded event as soon as the dispatching for the AssetCatalogRequestBus has completed on the current thread.
This is done by updating the AssetCatalogRequestBus to use EBus ThreadDispatchPolicy to add a callback to invoke any queued function has soon a thread has finished dispatching and has released its DispatchMutex

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the AssetCatalogRequestBus to add a custom DispatchLockGuard

The AssetCatalogRequestBus uses the custom lock guard to dispatch queued
events after it has unlocked it's context mutex for the current thread.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed GetContext call from the
AssetCatalogRequests::PostThreadDispatchInvoker

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the definition of FileTagQueryManager::GetDefaultFileTagFilePath
function to return a path

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the AZ_CONSOLEFREEFUNC macro to actually use the _NAME

The _NAME parameter was not being used before, resulting in the Console
stringified name of the function being used.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed CrySystem dependencies from the BundlingSystemComponent

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Addded missing template parameter to AssetCatalogRequests

The fixes the compile error.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Adding AssetBus::MultiHandler::BusDisconnect call

The BlastSystemComponent was connecting to the Bus, but not
disconnecting from it, causing an assert to fire to it being a
multi-thread bus

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Added support for DataDrive lifecycle events to the ComponentApplication

The events are using the SettingsRegistry NotifyEvent to track when
certain keys are modified to signal handlers.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Corrected invalid JSON creation in ModuleManager::DeactivateEntities

Resolved clang warning about used type alias

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fix for dangling reference in lambda registered to the SettingsRegistry
Notifier event

This was causing the EditorPythonBinding tests to crash due to the
following circumstances.

First Python has created an instance of a SettingsRegistryProxy
Second the SettingsRegistry sends an event during the time when the
SettingsRegistryProxy exists.

This issue was exposed due to the ComponentApplication Lifecycle events
using the SettingsRegistry to dispatch during various times of the
application workflow.

Signed-off-by: lumberyard-employee-dm <[email protected]>
lumberyard-employee-dm 3 éve
szülő
commit
b3b646dad9
25 módosított fájl, 390 hozzáadás és 173 törlés
  1. 40 2
      Code/Framework/AzCore/AzCore/Asset/AssetManagerBus.h
  2. 20 2
      Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp
  3. 3 1
      Code/Framework/AzCore/AzCore/Component/ComponentApplication.h
  4. 87 0
      Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp
  5. 54 0
      Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h
  6. 1 1
      Code/Framework/AzCore/AzCore/Console/IConsole.h
  7. 32 6
      Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp
  8. 9 9
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryScriptUtils.cpp
  9. 2 0
      Code/Framework/AzCore/AzCore/azcore_files.cmake
  10. 38 1
      Code/Framework/AzFramework/AzFramework/Application/Application.cpp
  11. 8 7
      Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
  12. 1 0
      Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp
  13. 13 8
      Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp
  14. 2 1
      Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h
  15. 3 1
      Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp
  16. 3 1
      Code/Framework/AzFramework/Tests/AssetCatalog.cpp
  17. 2 2
      Code/Legacy/CrySystem/IDebugCallStack.cpp
  18. 0 2
      Code/Tools/SerializeContextTools/SliceConverter.cpp
  19. 0 2
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp
  20. 1 0
      Gems/Blast/Code/Source/Components/BlastSystemComponent.cpp
  21. 36 61
      Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp
  22. 3 14
      Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h
  23. 1 42
      Gems/LmbrCentral/Code/Source/LmbrCentral.cpp
  24. 0 10
      Gems/LmbrCentral/Code/Source/LmbrCentral.h
  25. 31 0
      Registry/application_lifecycle_events.setreg

+ 40 - 2
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 <typename DispatchMutex>
+            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<LocklessDispatch, AZ::Internal::NullLockGuard<DispatchMutex>, AZStd::scoped_lock<DispatchMutex>>;
+                LockType m_lock;
+            };
+
+            template <typename DispatchMutex, bool>
+            using DispatchLockGuard = ThreadDispatchLockGuard<DispatchMutex>;
             //////////////////////////////////////////////////////////////////////////
 
             virtual ~AssetCatalogRequests() = default;
@@ -200,6 +227,17 @@ namespace AZ
 
         using AssetCatalogRequestBus = AZ::EBus<AssetCatalogRequests>;
 
+        inline AssetCatalogRequests::PostThreadDispatchInvoker::~PostThreadDispatchInvoker()
+        {
+            if (!AssetCatalogRequestBus::IsInDispatchThisThread())
+            {
+                if (AssetCatalogRequestBus::QueuedEventCount())
+                {
+                    AssetCatalogRequestBus::ExecuteQueuedEvents();
+                }
+            }
+        }
+
         /*
          * Events that AssetManager listens for
          */

+ 20 - 2
Code/Framework/AzCore/AzCore/Component/ComponentApplication.cpp

@@ -14,6 +14,7 @@
 #include <AzCore/Casting/lossy_cast.h>
 
 #include <AzCore/Component/ComponentApplication.h>
+#include <AzCore/Component/ComponentApplicationLifecycle.h>
 #include <AzCore/Component/TickBus.h>
 
 #include <AzCore/Debug/LocalFileEventLogger.h>
@@ -44,8 +45,6 @@
 #include <AzCore/Module/Module.h>
 #include <AzCore/Module/ModuleManager.h>
 
-#include <AzCore/IO/FileIO.h>
-#include <AzCore/IO/Path/Path_fwd.h>
 #include <AzCore/IO/SystemFile.h>
 
 #include <AzCore/Driller/Driller.h>
@@ -506,6 +505,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 <engine-root>/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<ModuleManager>();
 
@@ -520,6 +529,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 +561,7 @@ namespace AZ
         {
             AZ::Interface<AZ::IConsole>::Unregister(m_console);
             delete m_console;
+            ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ConsoleUnavailable", R"({})");
         }
 
         m_moduleManager.reset();
@@ -558,6 +569,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 +685,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 +706,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 +772,14 @@ namespace AZ
         m_entities.rehash(0); // force free all memory
 
         DestroyReflectionManager();
+        ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "ReflectionManagerUnavailable", R"({})");
 
         static_cast<SettingsRegistryImpl*>(m_settingsRegistry.get())->ClearNotifiers();
         static_cast<SettingsRegistryImpl*>(m_settingsRegistry.get())->ClearMergeEvents();
 
         // Uninit and unload any dynamic modules.
         m_moduleManager->UnloadModules();
+        ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "GemsUnloaded", R"({})");
 
         NameDictionary::Destroy();
 

+ 3 - 1
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<typename Iterator>

+ 87 - 0
Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.cpp

@@ -0,0 +1,87 @@
+/*
+ * 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 <AzCore/Component/ComponentApplicationLifecycle.h>
+#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
+
+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 '<engine-root>/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)
+    {
+        using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
+        using Type = AZ::SettingsRegistryInterface::Type;
+        using NotifyEventHandler = AZ::SettingsRegistryInterface::NotifyEventHandler;
+
+        if (!ValidateEvent(settingsRegistry, eventName))
+        {
+            AZ_Warning("ComponentApplicationLifecycle", false, R"(Cannot register event %.*s. Name does is not a field of object "%.*s".)"
+                R"( Please make sure the entry exists in the '<engine-root>/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;
+    }
+}

+ 54 - 0
Code/Framework/AzCore/AzCore/Component/ComponentApplicationLifecycle.h

@@ -0,0 +1,54 @@
+/*
+ * 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 <AzCore/Settings/SettingsRegistry.h>
+#include <AzCore/std/string/string_view.h>
+
+namespace AZ::ComponentApplicationLifecycle
+{
+    //! Root Key where lifecycle events should be registered under
+    inline constexpr AZStd::string_view ApplicationLifecycleEventRegistrationKey = "/O3DE/Runtime/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 <ApplicationLifecycleEventRootKey>/<eventName>
+    //! @return true if the eventValue was successfully merged at the <ApplicationLifecycleEventRootKey>/<eventName>
+    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
+    //! @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);
+}

+ 1 - 1
Code/Framework/AzCore/AzCore/Console/IConsole.h

@@ -262,6 +262,6 @@ static constexpr AZ::ThreadSafety ConsoleThreadSafety<_TYPE, std::enable_if_t<st
 //! @param _FLAGS a set of AzFramework::ConsoleFunctorFlags used to mutate behaviour
 //! @param _DESC a description of the cvar
 #define AZ_CONSOLEFREEFUNC_4(_NAME, _FUNCTION, _FLAGS, _DESC) \
-    inline AZ::ConsoleFunctor<void, false> Functor##_FUNCTION(#_FUNCTION, _DESC, _FLAGS | AZ::ConsoleFunctorFlags::DontDuplicate, AZ::TypeId::CreateNull(), &_FUNCTION)
+    inline AZ::ConsoleFunctor<void, false> 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__))

+ 32 - 6
Code/Framework/AzCore/AzCore/Module/ModuleManager.cpp

@@ -10,16 +10,13 @@
 #include <AzCore/Module/Internal/ModuleManagerSearchPathTool.h>
 
 #include <AzCore/Module/Module.h>
-#include <AzCore/RTTI/AttributeReader.h>
-#include <AzCore/RTTI/BehaviorContext.h>
 #include <AzCore/Serialization/SerializeContext.h>
 #include <AzCore/Serialization/EditContext.h>
 #include <AzCore/Component/Entity.h>
-#include <AzCore/Component/ComponentApplicationBus.h>
 #include <AzCore/Component/ComponentApplication.h>
+#include <AzCore/Component/ComponentApplicationBus.h>
+#include <AzCore/Component/ComponentApplicationLifecycle.h>
 #include <AzCore/NativeUI/NativeUIRequests.h>
-#include <AzCore/Script/ScriptSystemBus.h>
-#include <AzCore/Script/ScriptContext.h>
 
 #include <AzCore/std/algorithm.h>
 #include <AzCore/std/smart_ptr/make_shared.h>
@@ -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<AZStd::string_view> tagList;
-        AZStd::tokenize<AZStd::string_view>(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

+ 9 - 9
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<NotifyEventProxy>())
     {
-        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<NotifyEventProxy>())
     {
-        RegisterScriptProxyForNotify(*this);
+        RegisterScriptProxyForNotify(m_settingsRegistry.get(), m_notifyEventProxy.get());
     }
 
     // SettingsRegistryScriptProxy function that determines if the SettingsRegistry object is valid

+ 2 - 0
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

+ 38 - 1
Code/Framework/AzFramework/AzFramework/Application/Application.cpp

@@ -10,6 +10,7 @@
 #include <AzCore/IO/SystemFile.h>
 #include <AzCore/Math/Crc.h>
 #include <AzCore/Component/ComponentApplication.h>
+#include <AzCore/Component/ComponentApplicationLifecycle.h>
 #include <AzCore/Component/NonUniformScaleBus.h>
 #include <AzCore/Debug/Profiler.h>
 #include <AzCore/Memory/MemoryComponent.h>
@@ -120,6 +121,11 @@ namespace AzFramework
             m_archiveFileIO = AZStd::make_unique<AZ::IO::ArchiveFileIO>(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 <engine-root>
+            // (That happens in MergeSettingsToRegistry
+            AZ::ComponentApplicationLifecycle::RegisterEvent(*m_settingsRegistry, "FileIOAvailable");
+            AZ::ComponentApplicationLifecycle::SignalEvent(*m_settingsRegistry, "FileIOAvailable", R"({})");
         }
 
         if (auto nativeUI = AZ::Interface<AZ::NativeUI::NativeUIRequests>::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,25 @@ 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 +236,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();

+ 8 - 7
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<AzFramework::AssetRegistry> 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<AZStd::recursive_mutex> lock(m_registryMutex);
 
         m_registry->Clear();
+        m_initialized = false;
     }
 
 

+ 1 - 0
Code/Framework/AzFramework/AzFramework/Asset/AssetRegistry.cpp

@@ -61,6 +61,7 @@ namespace AzFramework
     //=========================================================================
     void AssetRegistry::Clear()
     {
+        m_assetDependencies = {};
         m_assetIdToInfo = AssetIdToInfoMap();
         m_assetPathToId = AssetPathToIdMap();
     }

+ 13 - 8
Code/Framework/AzFramework/AzFramework/FileTag/FileTag.cpp

@@ -10,11 +10,11 @@
 #include <AzCore/IO/FileIO.h>
 #include <AzCore/IO/Path/Path.h>
 #include <AzCore/IO/SystemFile.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
 #include <AzCore/std/algorithm.h>
 #include <AzCore/std/string/wildcard.h>
 #include <AzCore/std/string/regex.h>
 #include <AzCore/std/string/conversions.h>
-#include <AzCore/Utils/Utils.h>
 #include <AzCore/XML/rapidxml.h>
 #include <AzFramework/API/ApplicationAPI.h>
 #include <AzFramework/Asset/FileTagAsset.h>
@@ -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<AZStd::string, AZStd::string> FileTagManager::AddTagsInternal(AZStd::string filePath, FileTagType fileTagType, AZStd::vector<AZStd::string> 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);

+ 2 - 1
Code/Framework/AzFramework/AzFramework/FileTag/FileTag.h

@@ -11,6 +11,7 @@
 #include <AzCore/std/containers/map.h>
 #include <AzCore/std/containers/set.h>
 #include <AzCore/std/string/string.h>
+#include <AzCore/IO/Path/Path_fwd.h>
 #include <AzFramework/FileTag/FileTagBus.h>
 
 namespace AzFramework
@@ -88,7 +89,7 @@ namespace AzFramework
 
             /////////////////////////////////////////////////////////////////////////
 
-            static AZStd::string GetDefaultFileTagFilePath(FileTagType fileTagType);
+            static AZ::IO::Path GetDefaultFileTagFilePath(FileTagType fileTagType);
 
         protected:
 

+ 3 - 1
Code/Framework/AzFramework/AzFramework/FileTag/FileTagComponent.cpp

@@ -16,6 +16,7 @@
 
 #include <AzCore/std/string/wildcard.h>
 #include <AzCore/IO/FileIO.h>
+#include <AzCore/IO/Path/Path.h>
 #include <AzCore/XML/rapidxml.h>
 
 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();

+ 3 - 1
Code/Framework/AzFramework/Tests/AssetCatalog.cpp

@@ -308,7 +308,9 @@ namespace UnitTest
             registry->Set(projectPathKey, "AutomatedTesting");
             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 

+ 2 - 2
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);
 }

+ 0 - 2
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;

+ 0 - 2
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();

+ 1 - 0
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();

+ 36 - 61
Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.cpp

@@ -9,43 +9,40 @@
 #include "BundlingSystemComponent.h"
 
 #include <AzCore/Asset/AssetManagerBus.h>
+#include <AzCore/Console/IConsole.h>
+#include <AzCore/Interface/Interface.h>
 #include <AzCore/Serialization/Utils.h>
 #include <AzCore/std/string/string_view.h>
+#include <AzCore/StringFunc/StringFunc.h>
 
 #include <AzFramework/Asset/AssetBundleManifest.h>
-#include <AzFramework/StringFunc/StringFunc.h>
-
-#include <ISystem.h>
-#include <IConsole.h>
-
 #include <AzFramework/Archive/IArchive.h>
 
+
 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<AZ::SerializeContext*>(context))
@@ -58,7 +55,7 @@ namespace LmbrCentral
 
     AZStd::vector<AZStd::string> 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<AZStd::string> 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<AZ::IO::IArchive>::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<AZStd::mutex> 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<AZ::IO::IArchive>::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<AzFramework::AssetBundleManifest> 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<AZ::IO::IArchive>::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<AzFramework::AssetBundleManifest> 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<AZ::IO::IArchive>::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);

+ 3 - 14
Gems/LmbrCentral/Code/Source/Bundling/BundlingSystemComponent.h

@@ -19,11 +19,8 @@
 
 #include <LmbrCentral/Bundling/BundlingSystemComponentBus.h>
 
-#include <CrySystemBus.h>
 #include <AzFramework/Archive/ArchiveBus.h>
 
-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<AzFramework::AssetBundleManifest> bundleManifest, const char* nextBundle, AZStd::shared_ptr<AzFramework::AssetRegistry> 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<AZStd::string> GetBundleList(const char* bundlePath, const char* bundleExtension) const;
 

+ 1 - 42
Gems/LmbrCentral/Code/Source/LmbrCentral.cpp

@@ -84,8 +84,6 @@
 
 namespace LmbrCentral
 {
-    static const char* s_assetCatalogFilename = "assetcatalog.xml";
-
     using LmbrCentralAllocatorScope = AZ::AllocatorScope<AZ::LegacyAllocator>;
 
     // This component boots the required allocators for LmbrCentral everywhere but AssetBuilders
@@ -354,8 +352,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<AZ::ScriptAsset>::Uuid());
             assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<MaterialAsset>::Uuid());
@@ -376,7 +373,6 @@ namespace LmbrCentral
             assetCatalog->AddExtension("cax");
         }
 
-        CrySystemEventBus::Handler::BusConnect();
         AZ::Data::AssetManagerNotificationBus::Handler::BusConnect();
 
 
@@ -448,7 +444,6 @@ namespace LmbrCentral
         m_unhandledAssetInfo.clear();
 
         AZ::Data::AssetManagerNotificationBus::Handler::BusDisconnect();
-        CrySystemEventBus::Handler::BusDisconnect();
 
         // AssetHandler's destructor calls Unregister()
         m_assetHandlers.clear();
@@ -459,42 +454,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)

+ 0 - 10
Gems/LmbrCentral/Code/Source/LmbrCentral.h

@@ -15,8 +15,6 @@
 #include <AzCore/std/smart_ptr/unique_ptr.h>
 #include <AzCore/std/functional.h>
 
-#include <CrySystemBus.h>
-
 /*!
  * \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<AZStd::unique_ptr<AZ::Data::AssetHandler> > m_assetHandlers;
         AZStd::vector<AZStd::unique_ptr<AZ::AssetTypeInfoBus::Handler> > m_unhandledAssetInfo;
         AZStd::vector<AZStd::function<void()>> m_allocatorShutdowns;

+ 31 - 0
Registry/application_lifecycle_events.setreg

@@ -0,0 +1,31 @@
+// 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" : {
+        "Runtime": {
+            "Application": {
+                "LifecycleEvents": {
+                    "SystemComponentsActivated": {},
+                    "SystemComponentsDeactivated": {},
+                    "ReflectionManagerAvailable": {},
+                    "ReflectionManagerUnavailable": {},
+                    "SystemAllocatorCreated": {},
+                    "SystemAllocatorPendingDestruction": {},
+                    "SettingsRegistryAvailable": {},
+                    "SettingsRegistryUnavailable": {},
+                    "ConsoleAvailable": {},
+                    "ConsoleUnavailable": {},
+                    "GemsLoaded": {},
+                    "GemsUnloaded": {},
+                    "FileIOAvailable": {},
+                    "FileIOUnavailable": {}
+                }
+            }
+        }
+    }
+}