diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_AddComponentToEntity.py b/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_AddComponentToEntity.py index b93a845a25..f241a952ec 100644 --- a/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_AddComponentToEntity.py +++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_AddComponentToEntity.py @@ -20,7 +20,7 @@ class Tests(): def C28798177_WhiteBox_AddComponentToEntity(): import os import sys - from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init + import WhiteBoxInit as init import azlmbr.bus as bus import azlmbr.editor as editor diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetDefaultShape.py b/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetDefaultShape.py index c44929e5f2..1e4a62c346 100644 --- a/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetDefaultShape.py +++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetDefaultShape.py @@ -24,7 +24,7 @@ critical_shape_check = ("Default shape has more than 0 sides", "default shape ha def C29279329_WhiteBox_SetDefaultShape(): import os import sys - from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init + import WhiteBoxInit as init import azlmbr.whitebox.api as api import azlmbr.bus as bus diff --git a/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetInvisible.py b/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetInvisible.py index 3dd6ed2fc4..88deee999d 100644 --- a/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetInvisible.py +++ b/AutomatedTesting/Gem/PythonTests/WhiteBox/tests/WhiteBox_SetInvisible.py @@ -23,7 +23,7 @@ def C28798205_WhiteBox_SetInvisible(): # in future game_mode will be activated and a runtime White Box Component queried import os import sys - from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init + import WhiteBoxInit as init import editor_python_test_tools.hydra_editor_utils as hydra import azlmbr.whitebox.api as api diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 0a7cc3b27a..99eb6d53e2 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -43,13 +43,14 @@ AZ_POP_DISABLE_WARNING #include #include #include +#include #include #include // AzFramework #include -#include #include +#include #include #include @@ -768,28 +769,6 @@ QString CCryEditApp::ShowWelcomeDialog() return levelName; } -////////////////////////////////////////////////////////////////////////// -void CCryEditApp::InitDirectory() -{ - ////////////////////////////////////////////////////////////////////////// - // Initializes Root folder of the game. - ////////////////////////////////////////////////////////////////////////// - QString szExeFileName = qApp->applicationDirPath(); - const static char* s_engineMarkerFile = "engine.json"; - - while (!QFile::exists(QString("%1/%2").arg(szExeFileName, s_engineMarkerFile))) - { - QDir currentdir(szExeFileName); - if (!currentdir.cdUp()) - { - break; - } - szExeFileName = currentdir.absolutePath(); - } - QDir::setCurrent(szExeFileName); -} - - ////////////////////////////////////////////////////////////////////////// // Needed to work with custom memory manager. ////////////////////////////////////////////////////////////////////////// @@ -1502,7 +1481,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo) // We support specifying multiple files in the cmdline by separating them with ';' AZStd::vector fileList; - AzFramework::StringFunc::TokenizeVisitor( + AZ::StringFunc::TokenizeVisitor( fileStr.constData(), [&fileList](AZStd::string_view elem) { @@ -1514,7 +1493,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo) { QByteArray pythonArgsStr = cmdInfo.m_pythonArgs.toUtf8(); AZStd::vector pythonArgs; - AzFramework::StringFunc::TokenizeVisitor(pythonArgsStr.constData(), + AZ::StringFunc::TokenizeVisitor(pythonArgsStr.constData(), [&pythonArgs](AZStd::string_view elem) { pythonArgs.push_back(elem); @@ -1529,7 +1508,7 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo) testcaseList.resize(fileList.size()); { int i = 0; - AzFramework::StringFunc::TokenizeVisitor( + AZ::StringFunc::TokenizeVisitor( pythonTestCase.constData(), [&i, &testcaseList](AZStd::string_view elem) { @@ -1593,7 +1572,6 @@ bool CCryEditApp::InitInstance() { QElapsedTimer startupTimer; startupTimer.start(); - InitDirectory(); // create / attach to the environment: AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance()); @@ -1604,8 +1582,6 @@ bool CCryEditApp::InitInstance() InitFromCommandLine(cmdInfo); - InitDirectory(); - qobject_cast(qApp)->Initialize(); // Must be done after CEditorImpl() is created m_pEditor->Initialize(); @@ -3817,131 +3793,68 @@ CMainFrame * CCryEditApp::GetMainFrame() const return MainWindow::instance()->GetOldMainFrame(); } -void CCryEditApp::StartProcessDetached(const char* process, const char* args) -{ - // Build the arguments as a QStringList - AZStd::vector tokens; - - // separate the string based on spaces for paths like "-launch", "lua", "-files"; - // also separate the string and keep spaces inside the folder path; - // Ex: C:\dev\Foundation\dev\Cache\AutomatedTesting\pc\automatedtesting\scripts\components\a a\empty.lua; - // Ex: C:\dev\Foundation\dev\Cache\AutomatedTesting\pc\automatedtesting\scripts\components\a a\'empty'.lua; - AZStd::string currentStr(args); - AZStd::size_t firstQuotePos = AZStd::string::npos; - AZStd::size_t secondQuotePos = 0; - AZStd::size_t pos = 0; - while (!currentStr.empty()) - { - firstQuotePos = currentStr.find_first_of('\"'); - pos = currentStr.find_first_of(" "); - - if ((firstQuotePos != AZStd::string::npos) && (firstQuotePos < pos || pos == AZStd::string::npos)) - { - secondQuotePos = currentStr.find_first_of('\"', firstQuotePos + 1); - if (secondQuotePos == AZStd::string::npos) - { - AZ_Warning("StartProcessDetached", false, "String tokenize failed, no matching \" found."); - return; - } +void CCryEditApp::OpenLUAEditor(const char* files) +{ + AZ::IO::FixedMaxPathString enginePath = AZ::Utils::GetEnginePath(); - AZStd::string newElement(AZStd::string(currentStr.data() + (firstQuotePos + 1), (secondQuotePos - 1))); - tokens.push_back(newElement); + AZ::IO::FixedMaxPathString projectPath = AZ::Utils::GetProjectPath(); - currentStr = currentStr.substr(secondQuotePos + 1); + AZStd::string filename = "LuaIDE"; + AZ::IO::FixedMaxPath executablePath = AZ::Utils::GetExecutableDirectory(); + executablePath /= filename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION; - firstQuotePos = AZStd::string::npos; - secondQuotePos = 0; - continue; - } - else - { - if (pos != AZStd::string::npos) - { - AZStd::string newElement(AZStd::string(currentStr.data() + 0, pos)); - tokens.push_back(newElement); - currentStr = currentStr.substr(pos + 1); - } - else - { - tokens.push_back(AZStd::string(currentStr)); - break; - } - } - } - - QStringList argsList; - for (const auto& arg : tokens) + if (!AZ::IO::SystemFile::Exists(executablePath.c_str())) { - argsList.push_back(QString(arg.c_str())); + AZ_Error("LuaIDE", false, "%s not found", executablePath.c_str()); + return; } - // Launch the process - [[maybe_unused]] bool startDetachedReturn = QProcess::startDetached( - process, - argsList, - QCoreApplication::applicationDirPath() - ); - AZ_Warning("StartProcessDetached", startDetachedReturn, "Failed to start process:%s args:%s", process, args); -} + AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo; -void CCryEditApp::OpenLUAEditor(const char* files) -{ - AZStd::string args = "-launch lua"; - if (files && strlen(files) > 0) - { - AZStd::vector resolvedPaths; - - AZStd::vector tokens; + AZStd::vector launchCmd = { executablePath.String() }; + launchCmd.emplace_back("--engine-path"); + launchCmd.emplace_back(AZStd::string_view{ enginePath }); + launchCmd.emplace_back("--project-path"); + launchCmd.emplace_back(AZStd::string_view{ projectPath }); + launchCmd.emplace_back("--launch"); + launchCmd.emplace_back("lua"); - AzFramework::StringFunc::Tokenize(files, tokens, '|'); - - for (const auto& file : tokens) + auto ParseFilesList = [&launchCmd](AZStd::string_view filePath) + { + bool fullPathFound = false; + auto GetFullSourcePath = [&launchCmd, &filePath, &fullPathFound] + (AzToolsFramework::AssetSystem::AssetSystemRequest* assetSystemRequests) { - char resolved[AZ_MAX_PATH_LEN]; - - AZStd::string fullPath = Path::GamePathToFullPath(file.c_str()).toUtf8().data(); - azstrncpy(resolved, AZ_MAX_PATH_LEN, fullPath.c_str(), fullPath.size()); - - if (AZ::IO::FileIOBase::GetInstance()->Exists(resolved)) + AZ::IO::Path assetFullPath; + if(assetSystemRequests->GetFullSourcePathFromRelativeProductPath(filePath, assetFullPath.Native())) { - AZStd::string current = '\"' + AZStd::string(resolved) + '\"'; - AZStd::replace(current.begin(), current.end(), '\\', '/'); - resolvedPaths.push_back(current); + fullPathFound = true; + launchCmd.emplace_back("--files"); + launchCmd.emplace_back(AZStd::move(assetFullPath.Native())); } - } - - if (!resolvedPaths.empty()) - { - for (const auto& resolvedPath : resolvedPaths) + }; + AzToolsFramework::AssetSystemRequestBus::Broadcast(AZStd::move(GetFullSourcePath)); + // If the full source path could be found through the Asset System, then + // attempt to resolve the path using the FileIO instance + if (!fullPathFound) + { + AZ::IO::FixedMaxPath resolvedFilePath; + if (auto fileIo = AZ::IO::FileIOBase::GetInstance(); + fileIo != nullptr && fileIo->ResolvePath(resolvedFilePath, filePath) + && fileIo->Exists(resolvedFilePath.c_str())) { - args.append(AZStd::string::format(" -files %s", resolvedPath.c_str())); + launchCmd.emplace_back("--files"); + launchCmd.emplace_back(resolvedFilePath.String()); } } - } - - AZ::IO::FixedMaxPathString engineRoot = AZ::Utils::GetEnginePath(); - AZ_Assert(!engineRoot.empty(), "Unable to query Engine Path"); - - AZStd::string_view exePath; - AZ::ComponentApplicationBus::BroadcastResult(exePath, &AZ::ComponentApplicationRequests::GetExecutableFolder); + }; + AZ::StringFunc::TokenizeVisitor(files, ParseFilesList, "|"); -#if defined(AZ_PLATFORM_LINUX) - // On Linux platforms, launching a process is not done through a shell and its arguments are passed in - // separately. There is no need to wrap the process path in case of spaces in the path - constexpr const char* argumentQuoteString = ""; -#else - constexpr const char* argumentQuoteString = "\""; -#endif + processLaunchInfo.m_commandlineParameters = AZStd::move(launchCmd); - AZStd::string process = AZStd::string::format("%s%.*s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "LuaIDE" -#if defined(AZ_PLATFORM_WINDOWS) - ".exe" -#endif - "%s", argumentQuoteString, aznumeric_cast(exePath.size()), exePath.data(), argumentQuoteString); - - AZStd::string processArgs = AZStd::string::format("%s -engine-path \"%s\"", args.c_str(), engineRoot.c_str()); - StartProcessDetached(process.c_str(), processArgs.c_str()); + AZ_VerifyError("LuaIDE", AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo), + "Lua IDE has failed to launch at path %s", executablePath.c_str()); } void CCryEditApp::PrintAlways(const AZStd::string& output) @@ -4055,11 +3968,6 @@ extern "C" int AZ_DLL_EXPORT CryEditMain(int argc, char* argv[]) gSettings.Connect(); auto theApp = AZStd::make_unique(); - // this does some magic to set the current directory... - { - QCoreApplication app(argc, argv); - CCryEditApp::InitDirectory(); - } // Must be set before QApplication is initialized, so that we support HighDpi monitors, like the Retina displays // on Windows 10 diff --git a/Code/Editor/CryEdit.h b/Code/Editor/CryEdit.h index 2ed916a922..7518b18239 100644 --- a/Code/Editor/CryEdit.h +++ b/Code/Editor/CryEdit.h @@ -138,7 +138,6 @@ public: RecentFileList* GetRecentFileList(); virtual void AddToRecentFileList(const QString& lpszPathName); ECreateLevelResult CreateLevel(const QString& levelName, QString& fullyQualifiedLevelName); - static void InitDirectory(); bool FirstInstance(bool bForceNewInstance = false); void InitFromCommandLine(CEditCommandLineInfo& cmdInfo); bool CheckIfAlreadyRunning(); @@ -159,11 +158,6 @@ public: // Print to stdout even if there out has been redirected void PrintAlways(const AZStd::string& output); - //! Launches a detached process - //! \param process The path to the process to start - //! \param args Space separated list of arguments to pass to the process on start. - void StartProcessDetached(const char* process, const char* args); - //! Launches the Lua Editor/Debugger //! \param files A space separated list of aliased paths void OpenLUAEditor(const char* files); diff --git a/Code/Editor/CryEditPy.cpp b/Code/Editor/CryEditPy.cpp index 5dea57c1ed..e5b4b84b6e 100644 --- a/Code/Editor/CryEditPy.cpp +++ b/Code/Editor/CryEditPy.cpp @@ -260,11 +260,6 @@ namespace namespace { - void PyStartProcessDetached(const char* process, const char* args) - { - CCryEditApp::instance()->StartProcessDetached(process, args); - } - void PyLaunchLUAEditor(const char* files) { CCryEditApp::instance()->OpenLUAEditor(files); @@ -429,7 +424,6 @@ namespace AzToolsFramework addLegacyGeneral(behaviorContext->Method("idle_wait", PyIdleWait, nullptr, "Waits idling for a given seconds. Primarily used for auto-testing.")); addLegacyGeneral(behaviorContext->Method("idle_wait_frames", PyIdleWaitFrames, nullptr, "Waits idling for a frames. Primarily used for auto-testing.")); - addLegacyGeneral(behaviorContext->Method("start_process_detached", PyStartProcessDetached, nullptr, "Launches a detached process with an optional space separated list of arguments.")); addLegacyGeneral(behaviorContext->Method("launch_lua_editor", PyLaunchLUAEditor, nullptr, "Launches the Lua editor, may receive a list of space separate file paths, or an empty string to only open the editor.")); addLegacyGeneral(behaviorContext->Method("attach_debugger", PyAttachDebugger, nullptr, "Prompts for attaching the debugger")); diff --git a/Code/Editor/Lib/Tests/test_CryEditPythonBindings.cpp b/Code/Editor/Lib/Tests/test_CryEditPythonBindings.cpp index 1d9b26dbbc..ed14bd6400 100644 --- a/Code/Editor/Lib/Tests/test_CryEditPythonBindings.cpp +++ b/Code/Editor/Lib/Tests/test_CryEditPythonBindings.cpp @@ -36,7 +36,7 @@ namespace CryEditPythonBindingsUnitTests m_app.Start(appDesc); // 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); m_app.RegisterComponentDescriptor(AzToolsFramework::CryEditPythonHandler::CreateDescriptor()); @@ -74,7 +74,6 @@ namespace CryEditPythonBindingsUnitTests EXPECT_TRUE(behaviorContext->m_methods.find("idle_is_enabled") != behaviorContext->m_methods.end()); EXPECT_TRUE(behaviorContext->m_methods.find("idle_wait") != behaviorContext->m_methods.end()); EXPECT_TRUE(behaviorContext->m_methods.find("idle_wait_frames") != behaviorContext->m_methods.end()); - EXPECT_TRUE(behaviorContext->m_methods.find("start_process_detached") != behaviorContext->m_methods.end()); EXPECT_TRUE(behaviorContext->m_methods.find("launch_lua_editor") != behaviorContext->m_methods.end()); } diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp index eed0112ad1..97a38dcb60 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp @@ -85,6 +85,16 @@ namespace AZ::Internal [[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override { m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{valueName} }); + // Make sure any engine paths read from the manifest are absolute + AZ::IO::FixedMaxPath& recentEnginePath = m_enginePaths.back().m_path; + if (recentEnginePath.IsRelative()) + { + if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(recentEnginePath.Native()); + engineRootAbsPath.has_value()) + { + recentEnginePath = AZStd::move(*engineRootAbsPath); + } + } } AZStd::vector m_enginePaths{}; @@ -209,8 +219,15 @@ namespace AZ::Internal return {}; } - void InjectSettingToCommandLineBack(AZ::SettingsRegistryInterface& settingsRegistry, - AZStd::string_view path, AZStd::string_view value) + enum class InjectLocation : bool + { + Front, + Back + }; + + void InjectSettingToCommandLine(AZ::SettingsRegistryInterface& settingsRegistry, + AZStd::string_view path, AZStd::string_view value, + InjectLocation injectLocation = InjectLocation::Front) { AZ::CommandLine commandLine; AZ::SettingsRegistryMergeUtils::GetCommandLineFromRegistry(settingsRegistry, commandLine); @@ -219,7 +236,8 @@ namespace AZ::Internal auto projectPathOverride = AZStd::string::format(R"(--regset="%.*s=%.*s")", aznumeric_cast(path.size()), path.data(), aznumeric_cast(value.size()), value.data()); - paramContainer.emplace(paramContainer.end(), AZStd::move(projectPathOverride)); + auto emplaceIter = injectLocation == InjectLocation::Front ? paramContainer.begin() : paramContainer.end(); + paramContainer.emplace(emplaceIter, AZStd::move(projectPathOverride)); commandLine.Parse(paramContainer); AZ::SettingsRegistryMergeUtils::StoreCommandLineToRegistry(settingsRegistry, commandLine); } @@ -244,13 +262,26 @@ namespace AZ::SettingsRegistryMergeUtils { // We can scan up from exe directory to find engine.json, use that for engine root if it exists. engineRoot = Internal::ScanUpRootLocator("engine.json"); + // The Internal ScanUp Engine Root Key will be set as an absolute path + if (!engineRoot.empty()) + { + if (engineRoot.IsRelative()) + { + if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native()); + engineRootAbsPath.has_value()) + { + engineRoot = AZStd::move(*engineRootAbsPath); + } + } + } + // Set the {InternalScanUpEngineRootKey} to make sure this code path isn't called again for this settings registry settingsRegistry.Set(InternalScanUpEngineRootKey, engineRoot.Native()); if (!engineRoot.empty()) { settingsRegistry.Set(engineRootKey, engineRoot.Native()); - // Inject the engine root at the end of the command line settings - Internal::InjectSettingToCommandLineBack(settingsRegistry, engineRootKey, engineRoot.Native()); + // Inject the engine root to the front of the command line settings + Internal::InjectSettingToCommandLine(settingsRegistry, engineRootKey, engineRoot.Native()); return engineRoot; } } @@ -258,6 +289,14 @@ namespace AZ::SettingsRegistryMergeUtils // Step 2 check if the engine_path key has been supplied if (settingsRegistry.Get(engineRoot.Native(), engineRootKey); !engineRoot.empty()) { + if (engineRoot.IsRelative()) + { + if (auto engineRootAbsPath = AZ::Utils::ConvertToAbsolutePath(engineRoot.Native()); + engineRootAbsPath.has_value()) + { + engineRoot = AZStd::move(*engineRootAbsPath); + } + } return engineRoot; } @@ -298,13 +337,28 @@ namespace AZ::SettingsRegistryMergeUtils if (settingsRegistry.GetType(InternalScanUpProjectRootKey) == Type::NoType) { projectRoot = Internal::ScanUpRootLocator("project.json"); + // Convert the path to an absolute path before adding it as a setting to the + // InternalScanUpProjectRootKey + if (!projectRoot.empty()) + { + if (projectRoot.IsRelative()) + { + if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectRoot.Native()); + projectAbsPath.has_value()) + { + projectRoot = AZStd::move(*projectAbsPath); + } + } + } + // Set the {InternalScanUpProjectRootKey} to make sure this code path isn't called again for this settings registry settingsRegistry.Set(InternalScanUpProjectRootKey, projectRoot.Native()); if (!projectRoot.empty()) { settingsRegistry.Set(projectRootKey, projectRoot.c_str()); - // Inject the project root at the end of the command line settings - Internal::InjectSettingToCommandLineBack(settingsRegistry, projectRootKey, projectRoot.Native()); + // Inject the project root at to the front of the command line settings + Internal::InjectSettingToCommandLine(settingsRegistry, projectRootKey, projectRoot.Native()); + return projectRoot; } } @@ -312,6 +366,18 @@ namespace AZ::SettingsRegistryMergeUtils // Step 2 Check the project-path key // This is the project path root key, as passed from command-line or *.setreg files. settingsRegistry.Get(projectRoot.Native(), projectRootKey); + if (!projectRoot.empty()) + { + if (projectRoot.IsRelative()) + { + if (auto projectAbsPath = AZ::Utils::ConvertToAbsolutePath(projectRoot.Native()); + projectAbsPath.has_value()) + { + projectRoot = AZStd::move(*projectAbsPath); + } + } + } + return projectRoot; } @@ -325,13 +391,22 @@ namespace AZ::SettingsRegistryMergeUtils 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)) + AZ::IO::FixedMaxPath projectCachePath; + if (!settingsRegistry.Get(projectCachePath.Native(), projectCachePathKey)) { - return projectCachePath; + // Step 2 Append the "Cache" directory to the project-path + projectCachePath = projectPath / Internal::ProductCacheDirectoryName; } - // Step 2 Append the "Cache" directory to the project-path - return projectPath / Internal::ProductCacheDirectoryName; + if (projectCachePath.IsRelative()) + { + if (auto projectCacheAbsPath = AZ::Utils::ConvertToAbsolutePath(projectCachePath.Native()); + projectCacheAbsPath.has_value()) + { + projectCachePath = AZStd::move(*projectCacheAbsPath); + } + } + return projectCachePath; } //! Set the user directory with the provided path or using /user as default @@ -344,13 +419,22 @@ namespace AZ::SettingsRegistryMergeUtils 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)) + AZ::IO::FixedMaxPath projectUserPath; + if (!settingsRegistry.Get(projectUserPath.Native(), projectUserPathKey)) { - return projectUserPath; + // Step 2 Append the "User" directory to the project-path + projectUserPath = projectPath / "user"; } - // Step 2 Append the "User" directory to the project-path - return projectPath / "user"; + if (projectUserPath.IsRelative()) + { + if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native()); + projectUserAbsPath.has_value()) + { + projectUserPath = AZStd::move(*projectUserAbsPath); + } + } + return projectUserPath; } //! Set the log directory using the settings registry path or using /log as default @@ -363,20 +447,37 @@ namespace AZ::SettingsRegistryMergeUtils 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)) + AZ::IO::FixedMaxPath projectLogPath; + if (!settingsRegistry.Get(projectLogPath.Native(), projectLogPathKey)) + { + // Step 2 Append the "Log" directory to the project-user-path + projectLogPath = projectUserPath / "log"; + } + + if (projectLogPath.IsRelative()) { - return projectLogPath; + if (auto projectLogAbsPath = AZ::Utils::ConvertToAbsolutePath(projectLogPath.Native())) + { + projectLogPath = AZStd::move(*projectLogAbsPath); + } } - // Step 2 Append the "Log" directory to the project-user-path - return projectUserPath / "log"; + return projectLogPath; } // 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; + AZ::IO::FixedMaxPath devWriteStoragePath = devWriteStorage.has_value() ? *devWriteStorage : projectUserPath; + if (devWriteStoragePath.IsRelative()) + { + if (auto devWriteStorageAbsPath = AZ::Utils::ConvertToAbsolutePath(devWriteStoragePath.Native())) + { + devWriteStoragePath = AZStd::move(*devWriteStorageAbsPath); + } + } + return devWriteStoragePath; } // check for the project build path, which is a relative path from the project root @@ -669,15 +770,6 @@ namespace AZ::SettingsRegistryMergeUtils if ([[maybe_unused]] 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?)" @@ -699,15 +791,6 @@ namespace AZ::SettingsRegistryMergeUtils AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry); 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()); } @@ -716,15 +799,6 @@ namespace AZ::SettingsRegistryMergeUtils AZ::IO::FixedMaxPath projectCachePath = FindProjectCachePath(registry, projectPath).LexicallyNormal(); if (!projectCachePath.empty()) { - 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()); @@ -755,15 +829,6 @@ namespace AZ::SettingsRegistryMergeUtils AZ::IO::FixedMaxPath projectUserPath = FindProjectUserPath(registry, projectPath); if (!projectUserPath.empty()) { - if (projectUserPath.IsRelative()) - { - if (auto projectUserAbsPath = AZ::Utils::ConvertToAbsolutePath(projectUserPath.Native()); - projectUserAbsPath.has_value()) - { - projectUserPath = AZStd::move(*projectUserAbsPath); - } - } - projectUserPath = projectUserPath.LexicallyNormal(); registry.Set(FilePathKey_ProjectUserPath, projectUserPath.Native()); } @@ -771,14 +836,6 @@ namespace AZ::SettingsRegistryMergeUtils // Log folder if (AZ::IO::FixedMaxPath projectLogPath = FindProjectLogPath(registry, projectUserPath); !projectLogPath.empty()) { - if (projectLogPath.IsRelative()) - { - if (auto projectLogAbsPath = AZ::Utils::ConvertToAbsolutePath(projectLogPath.Native())) - { - projectLogPath = AZStd::move(*projectLogAbsPath); - } - } - projectLogPath = projectLogPath.LexicallyNormal(); registry.Set(FilePathKey_ProjectLogPath, projectLogPath.Native()); } @@ -786,14 +843,6 @@ namespace AZ::SettingsRegistryMergeUtils // Developer Write Storage folder if (AZ::IO::FixedMaxPath devWriteStoragePath = FindDevWriteStoragePath(projectUserPath); !devWriteStoragePath.empty()) { - if (devWriteStoragePath.IsRelative()) - { - if (auto devWriteStorageAbsPath = AZ::Utils::ConvertToAbsolutePath(devWriteStoragePath.Native())) - { - devWriteStoragePath = AZStd::move(*devWriteStorageAbsPath); - } - } - devWriteStoragePath = devWriteStoragePath.LexicallyNormal(); registry.Set(FilePathKey_DevWriteStorage, devWriteStoragePath.Native()); } diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h index 56eec91813..fcfc3b51fe 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.h @@ -89,15 +89,14 @@ namespace AZ::SettingsRegistryMergeUtils //! The algorithm that is used to find the project root is as follows //! 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 + //! In the same step it injects the path into the front 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 //! //! The order in which the project path settings are overridden proceeds in the following order - //! 1. project_path set in the /bootstrap.cfg file - //! 2. project_path set in a *.setreg/*.setregpatch file - //! 3. project_path found by scanning upwards from the executable directory to the project.json path - //! 4. project_path set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=" + //! 1. project_path set in a *.setreg/*.setregpatch file + //! 2. project_path found by scanning upwards from the executable directory to the project.json path + //! 3. project_path set on the Command line via either --regset="{BootstrapSettingsRootKey}/project_path=" //! or --project_path= AZ::IO::FixedMaxPath FindProjectRoot(SettingsRegistryInterface& settingsRegistry); @@ -213,9 +212,15 @@ namespace AZ::SettingsRegistryMergeUtils const SettingsRegistryInterface::Specializations& specializations, AZStd::vector* scratchBuffer = nullptr); //! Adds the settings set through the command line to the Settings Registry. This will also execute any Settings - //! Registry related arguments. Note that --regset and -regremove will run in the order in which they are parsed + //! Registry related arguments. Note that --regset, --regset-file and -regremove will run in the order in which they are parsed //! --regset Sets a value in the registry. See MergeCommandLineArgument for options for //! example: --regset "/My/String/Value=String value set" + //! --regset-file [::anchor] Merges the specified file into the Settings registry + //! If the extension is .setregpatch, then JSON Patch will be used to merge the file otherwise JSON Merge Patch will be used + //! `anchor` is a JSON path used to optionally select where to merge the settings underneath, otherwise settings are merged + //! under the root. + //! example: --regset-file="C:/Users/testuser/custom.setreg" + //! example: --regset-file="relative/path/other.setregpatch::/O3DE/settings" //! --regremove Removes a value in the registry //! example: --regremove "/My/String/Value" //! only when executeCommands is true are the following options supported: diff --git a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp index ed3ff44d1c..45e1013766 100644 --- a/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/ProjectManager/ProjectManager.cpp @@ -64,7 +64,7 @@ namespace AzFramework::ProjectManager // If we were able to locate a path to a project, we're done if (!projectRootPath.empty()) { - AZ::IO::FixedMaxPath projectJsonPath = engineRootPath / projectRootPath / "project.json"; + AZ::IO::FixedMaxPath projectJsonPath = projectRootPath / "project.json"; if (AZ::IO::SystemFile::Exists(projectJsonPath.c_str())) { return ProjectPathCheckResult::ProjectPathFound; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkAPI.h index 362812d7a2..7805d51a15 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkAPI.h @@ -208,10 +208,6 @@ namespace LegacyFramework */ virtual bool IsRunningInGUIMode() = 0; - /** Retrieve a Command Line Parser object that you can then use to check for values on the command line - */ - virtual const AzFramework::CommandLine* GetCommandLineParser() = 0; - /** (Windows) retrieves the main module of the executable. * This is always going to be the main executable except in the situation where the framework may be running as a DLL belonging to another process or program. */ diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp index 59f4b76065..cb6d8d1e58 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.cpp @@ -97,6 +97,10 @@ namespace LegacyFramework } Application::Application() + : Application(0, nullptr) + {} + Application::Application(int argc, char** argv) + : ComponentApplication(argc, argv) { m_isPrimary = true; m_desiredExitCode = 0; @@ -191,9 +195,6 @@ namespace LegacyFramework ::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, true); #endif - m_ptrCommandLineParser = aznew AzFramework::CommandLine(); - m_ptrCommandLineParser->Parse(m_desc.m_argc, m_desc.m_argv); - // If we don't have one create a serialize context if (GetSerializeContext() == nullptr) { @@ -206,7 +207,7 @@ namespace LegacyFramework m_ptrSystemEntity->Activate(); // If we aren't the primary, RunAsAnotherInstance unless we are being forcestarted - if (!m_isPrimary && !m_ptrCommandLineParser->HasSwitch("forcestart")) + if (!m_isPrimary && !m_commandLine.HasSwitch("forcestart")) { // Required for the application component to handle RunAsAnotherInstance CreateApplicationComponent(); @@ -246,9 +247,6 @@ namespace LegacyFramework ::SetConsoleCtrlHandler(CTRL_BREAK_HandlerRoutine, false); #endif - delete m_ptrCommandLineParser; - m_ptrCommandLineParser = nullptr; - CoreMessageBus::Handler::BusDisconnect(); FrameworkApplicationMessages::Handler::BusDisconnect(); @@ -271,11 +269,6 @@ namespace LegacyFramework } } - const AzFramework::CommandLine* Application::GetCommandLineParser() - { - return m_ptrCommandLineParser; - } - // returns TRUE if the component already existed, FALSE if it had to create one. bool Application::EnsureComponentCreated(AZ::Uuid componentCRC) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h index 99235c420c..46d91fdead 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/Core/EditorFrameworkApplication.h @@ -56,6 +56,7 @@ namespace LegacyFramework using CoreMessageBus::Handler::Run; virtual int Run(const ApplicationDesc& desc); Application(); + Application(int argc, char** argv); void CreateReflectionManager() override; @@ -70,7 +71,6 @@ namespace LegacyFramework const char* GetApplicationName() override; const char* GetApplicationModule() override; const char* GetApplicationDirectory() override; - const AzFramework::CommandLine* GetCommandLineParser() override; void TeardownApplicationComponent() override; void RunAssetProcessor() override; // ------------------------------------------------------------------ @@ -138,7 +138,6 @@ namespace LegacyFramework volatile bool m_abortRequested; // if you CTRL+C in a console app, this becomes true. its up to you to check... char m_applicationFilePath[AZ_MAX_PATH_LEN]; ApplicationDesc m_desc; - AzFramework::CommandLine* m_ptrCommandLineParser; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/UIFramework.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/UIFramework.cpp index 3eb794d892..197d0005df 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/UIFramework.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/LegacyFramework/UIFramework.cpp @@ -16,7 +16,7 @@ #include -#include +#include #include #include @@ -34,7 +34,6 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // '...' needs to have #include AZ_POP_DISABLE_WARNING -#include #ifndef AZ_PLATFORM_WINDOWS int __argc = 0; @@ -201,8 +200,11 @@ namespace AzToolsFramework // enable the built-in stylesheet by default: bool enableStyleSheet = true; - const AzFramework::CommandLine* comp = nullptr; - EBUS_EVENT_RESULT(comp, LegacyFramework::FrameworkApplicationMessages::Bus, GetCommandLineParser); + const AZ::CommandLine* comp = nullptr; + AZ::ComponentApplicationBus::Broadcast([&comp](AZ::ComponentApplicationRequests* requests) + { + comp = requests->GetAzCommandLine(); + }); if (comp != nullptr) { if (comp->HasSwitch("nostyle")) diff --git a/Code/Tools/LuaIDE/Source/LUA/LUAEditorContext.cpp b/Code/Tools/LuaIDE/Source/LUA/LUAEditorContext.cpp index 533a89a77c..cbc8e08d42 100644 --- a/Code/Tools/LuaIDE/Source/LUA/LUAEditorContext.cpp +++ b/Code/Tools/LuaIDE/Source/LUA/LUAEditorContext.cpp @@ -21,11 +21,11 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -244,7 +244,7 @@ namespace LUAEditor } AZStd::vector files; - AzFramework::StringFunc::Tokenize(parameters.c_str(), files, ";"); + AZ::StringFunc::Tokenize(parameters.c_str(), files, ";"); if (!files.empty()) { for (const auto& file : files) @@ -669,9 +669,12 @@ namespace LUAEditor return; } - const AzFramework::CommandLine* commandLine = nullptr; + const AZ::CommandLine* commandLine = nullptr; - EBUS_EVENT_RESULT(commandLine, LegacyFramework::FrameworkApplicationMessages::Bus, GetCommandLineParser); + AZ::ComponentApplicationBus::Broadcast([&commandLine](AZ::ComponentApplicationRequests* requests) + { + commandLine = requests->GetAzCommandLine(); + }); bool forceShow = false; bool forceHide = false; @@ -850,7 +853,7 @@ namespace LUAEditor DocumentInfo& info = infoEntry.first->second; info.m_assetId = normalizedAssetId; info.m_assetName = assetId; - AzFramework::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName); + AZ::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName); info.m_bSourceControl_Ready = true; info.m_bSourceControl_CanWrite = true; info.m_bUntitledDocument = false; @@ -945,7 +948,7 @@ namespace LUAEditor // do not allow SaveAs onto an existing asset, even if it could be checked out and modified "safely." // end user must check out and modify contents directly if they want this - if (AzFramework::StringFunc::Find(newAssetName.c_str(), ".lua") == AZStd::string::npos) + if (AZ::StringFunc::Find(newAssetName.c_str(), ".lua") == AZStd::string::npos) { newAssetName += ".lua"; } @@ -961,7 +964,7 @@ namespace LUAEditor trySaveAs = false; docInfoIter->second.m_bUntitledDocument = false; - AzFramework::StringFunc::Path::GetFullFileName(newAssetName.c_str(), docInfoIter->second.m_displayName); + AZ::StringFunc::Path::GetFullFileName(newAssetName.c_str(), docInfoIter->second.m_displayName); // when you 'save as' you can write to it, even if it started out not that way. docInfoIter->second.m_bSourceControl_Ready = true; @@ -1489,7 +1492,7 @@ namespace LUAEditor DocumentInfo info; info.m_assetName = assetIdLower; - AzFramework::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName); + AZ::StringFunc::Path::GetFullFileName(assetId.c_str(), info.m_displayName); info.m_assetId = assetIdLower; info.m_bSourceControl_BusyGettingStats = true; info.m_bSourceControl_BusyGettingStats = false; @@ -1555,7 +1558,10 @@ namespace LUAEditor const AZStd::string k_luaScriptFileString = "files"; const AzFramework::CommandLine* commandLine = nullptr; - EBUS_EVENT_RESULT(commandLine, LegacyFramework::FrameworkApplicationMessages::Bus, GetCommandLineParser); + AZ::ComponentApplicationBus::Broadcast([&commandLine](AZ::ComponentApplicationRequests* requests) + { + commandLine = requests->GetAzCommandLine(); + }); AZStd::string parameters = ""; size_t numSwitchValues = commandLine->GetNumSwitchValues(k_luaScriptFileString); @@ -2448,7 +2454,7 @@ namespace LUAEditor if (matchFound) { int lineNumber = 0; - if (AzFramework::StringFunc::LooksLikeInt(match[1].str().c_str(), &lineNumber)) + if (AZ::StringFunc::LooksLikeInt(match[1].str().c_str(), &lineNumber)) { errorData->m_lineNumber = lineNumber; finalMessage = match[2].str().c_str(); @@ -2467,6 +2473,6 @@ namespace LUAEditor bool Context::IsLuaAsset(const AZStd::string& assetPath) { - return AzFramework::StringFunc::Path::IsExtension(assetPath.c_str(), ".lua"); + return AZ::StringFunc::Path::IsExtension(assetPath.c_str(), ".lua"); } } diff --git a/Code/Tools/LuaIDE/Source/LuaIDEApplication.cpp b/Code/Tools/LuaIDE/Source/LuaIDEApplication.cpp index 4169326250..3e8217d723 100644 --- a/Code/Tools/LuaIDE/Source/LuaIDEApplication.cpp +++ b/Code/Tools/LuaIDE/Source/LuaIDEApplication.cpp @@ -27,7 +27,8 @@ namespace LUAEditor { - Application::Application(int &argc, char **argv) : BaseApplication(argc, argv) + Application::Application(int argc, char **argv) + : BaseApplication(argc, argv) { AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect(); } diff --git a/Code/Tools/LuaIDE/Source/LuaIDEApplication.h b/Code/Tools/LuaIDE/Source/LuaIDEApplication.h index 8a7bf0cc72..9f1c27d76c 100644 --- a/Code/Tools/LuaIDE/Source/LuaIDEApplication.h +++ b/Code/Tools/LuaIDE/Source/LuaIDEApplication.h @@ -17,7 +17,7 @@ namespace LUAEditor , protected AzToolsFramework::SourceControlNotificationBus::Handler { public: - Application(int &argc, char **argv); + Application(int argc, char **argv); ~Application() override; protected: diff --git a/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.cpp b/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.cpp index f9dac47dc5..b370538fc8 100644 --- a/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.cpp +++ b/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.cpp @@ -20,8 +20,8 @@ namespace StandaloneTools { - BaseApplication::BaseApplication(int&, char**) - : LegacyFramework::Application() + BaseApplication::BaseApplication(int argc, char** argv) + : LegacyFramework::Application(argc, argv) { AZ::UserSettingsFileLocatorBus::Handler::BusConnect(); } diff --git a/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.h b/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.h index 4ca45b8669..880a011285 100644 --- a/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.h +++ b/Code/Tools/LuaIDE/Source/StandaloneToolsApplication.h @@ -25,7 +25,7 @@ namespace StandaloneTools { public: - BaseApplication(int &argc, char **argv); + BaseApplication(int argc, char **argv); ~BaseApplication() override; protected: diff --git a/Gems/ScriptCanvas/Assets/TranslationAssets/GlobalMethods/start_process_detached.names b/Gems/ScriptCanvas/Assets/TranslationAssets/GlobalMethods/start_process_detached.names deleted file mode 100644 index 284efd4cb1..0000000000 --- a/Gems/ScriptCanvas/Assets/TranslationAssets/GlobalMethods/start_process_detached.names +++ /dev/null @@ -1,45 +0,0 @@ -{ - "entries": [ - { - "base": "start_process_detached", - "context": "Method", - "variant": "", - "details": { - "name": "", - "category": "Globals" - }, - "methods": [ - { - "base": "start_process_detached", - "context": "Global", - "entry": { - "name": "In", - "tooltip": "When signaled, this will invoke start_process_detached" - }, - "exit": { - "name": "Out", - "tooltip": "Signaled after start_process_detached is invoked" - }, - "details": { - "name": "start_process_detached", - "category": "Other" - }, - "params": [ - { - "typeid": "{3AB0037F-AF8D-48CE-BCA0-A170D18B2C03}", - "details": { - "name": "const char*" - } - }, - { - "typeid": "{3AB0037F-AF8D-48CE-BCA0-A170D18B2C03}", - "details": { - "name": "const char*" - } - } - ] - } - ] - } - ] -} \ No newline at end of file