ATOM-15439 Implement basic local socket and server for IPC in material editor and other tools

This replaces grid hub usage in the material editor. It allows material editor and other tools to intercommunicate on the local host.  This will allow enforcing that there is only one instance of the material editor running.  Opening a second instance will forward command line options to the first instance running a local server.

https://jira.agscollab.com/browse/ATOM-15439
https://jira.agscollab.com/browse/ATOM-13742
This commit is contained in:
guthadam
2021-05-02 17:08:21 -05:00
parent 781415755d
commit fae33e9235
18 changed files with 406 additions and 135 deletions
@@ -90,6 +90,14 @@ namespace MaterialEditor
});
}
MaterialEditorApplication::~MaterialEditorApplication()
{
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
MaterialEditorWindowNotificationBus::Handler::BusDisconnect();
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
}
void MaterialEditorApplication::CreateReflectionManager()
{
Application::CreateReflectionManager();
@@ -299,12 +307,12 @@ namespace MaterialEditor
return false;
}
void MaterialEditorApplication::ProcessCommandLine()
void MaterialEditorApplication::ProcessCommandLine(const AZ::CommandLine& commandLine)
{
const AZStd::string timeoputSwitchName = "timeout";
if (m_commandLine.HasSwitch(timeoputSwitchName))
if (commandLine.HasSwitch(timeoputSwitchName))
{
const AZStd::string& timeoutValue = m_commandLine.GetSwitchValue(timeoputSwitchName, 0);
const AZStd::string& timeoutValue = commandLine.GetSwitchValue(timeoputSwitchName, 0);
const uint32_t timeoutInMs = atoi(timeoutValue.c_str());
AZ_Printf("MaterialEditor", "Timeout scheduled, shutting down in %u ms", timeoutInMs);
QTimer::singleShot(timeoutInMs, [this] {
@@ -315,10 +323,10 @@ namespace MaterialEditor
// Process command line options for running one or more python scripts on startup
const AZStd::string runPythonScriptSwitchName = "runpython";
size_t runPythonScriptCount = m_commandLine.GetNumSwitchValues(runPythonScriptSwitchName);
size_t runPythonScriptCount = commandLine.GetNumSwitchValues(runPythonScriptSwitchName);
for (size_t runPythonScriptIndex = 0; runPythonScriptIndex < runPythonScriptCount; ++runPythonScriptIndex)
{
const AZStd::string runPythonScriptPath = m_commandLine.GetSwitchValue(runPythonScriptSwitchName, runPythonScriptIndex);
const AZStd::string runPythonScriptPath = commandLine.GetSwitchValue(runPythonScriptSwitchName, runPythonScriptIndex);
AZStd::vector<AZStd::string_view> runPythonArgs;
AZ_Printf("MaterialEditor", "Launching script: %s", runPythonScriptPath.c_str());
@@ -329,17 +337,17 @@ namespace MaterialEditor
}
// Process command line options for opening one or more material documents on startup
size_t openDocumentCount = m_commandLine.GetNumMiscValues();
size_t openDocumentCount = commandLine.GetNumMiscValues();
for (size_t openDocumentIndex = 0; openDocumentIndex < openDocumentCount; ++openDocumentIndex)
{
const AZStd::string openDocumentPath = m_commandLine.GetMiscValue(openDocumentIndex);
const AZStd::string openDocumentPath = commandLine.GetMiscValue(openDocumentIndex);
AZ_Printf("MaterialEditor", "Opening document: %s", openDocumentPath.c_str());
MaterialDocumentSystemRequestBus::Broadcast(&MaterialDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath);
}
const AZStd::string exitAfterCommandsSwitchName = "exitaftercommands";
if (m_commandLine.HasSwitch(exitAfterCommandsSwitchName))
if (commandLine.HasSwitch(exitAfterCommandsSwitchName))
{
ExitMainLoop();
}
@@ -409,9 +417,50 @@ namespace MaterialEditor
bool MaterialEditorApplication::LaunchDiscoveryService()
{
const QStringList arguments = { "-fail_silently" };
// Determine if this is the first launch of the tool by attempting to connect to a running server
if (m_socket.Connect(QApplication::applicationName()))
{
// If the server was located, the application is already running.
// Forward commandline options to other application instance.
QByteArray buffer;
buffer.append("ProcessCommandLine:");
for (int argi = 1; argi < m_argC; ++argi)
{
buffer.append(QString(m_argV[argi]).append("\n").toUtf8());
}
m_socket.Send(buffer);
m_socket.Disconnect();
return false;
}
return AtomToolsFramework::LaunchTool("GridHub", AZ_TRAIT_MATERIALEDITOR_EXT, arguments);
// Setup server to handle basic commands
m_server.SetReadHandler([this](const QByteArray& buffer) {
// Handle commmand line params from connected socket
if (buffer.startsWith("ProcessCommandLine:"))
{
// Remove header and parse commands
AZStd::string params(buffer.data(), buffer.size());
params = params.substr(strlen("ProcessCommandLine:"));
AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(params, tokens, "\n");
if (!tokens.empty())
{
AZ::CommandLine commandLine;
commandLine.Parse(tokens);
ProcessCommandLine(commandLine);
}
}
});
// Launch local server
if (!m_server.Connect(QApplication::applicationName()))
{
return false;
}
return true;
}
void MaterialEditorApplication::StartInternal()
@@ -421,10 +470,14 @@ namespace MaterialEditor
return;
}
//[GFX TODO][ATOM-415] Try to factor out some of this stuff with AtomSampleViewerApplication
WriteStartupLog();
if (!LaunchDiscoveryService())
{
ExitMainLoop();
return;
}
AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusConnect();
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(&AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
@@ -434,8 +487,6 @@ namespace MaterialEditor
LoadSettings();
LaunchDiscoveryService();
MaterialEditorWindowNotificationBus::Handler::BusConnect();
MaterialEditor::MaterialEditorWindowFactoryRequestBus::Broadcast(
@@ -450,7 +501,7 @@ namespace MaterialEditor
}
// Delay execution of commands and scripts post initialization
QTimer::singleShot(0, [this]() { ProcessCommandLine(); });
QTimer::singleShot(0, [this]() { ProcessCommandLine(m_commandLine); });
}
bool MaterialEditorApplication::GetAssetDatabaseLocation(AZStd::string& result)