From 835cc2963aa86a62a9da72fdfca0d1da36eb57d1 Mon Sep 17 00:00:00 2001 From: Michael Tyson Date: Tue, 23 Apr 2024 14:23:53 +1000 Subject: [PATCH] Support for launching from command line in offline mode This allows launching an offline instance with --offline --name=OfflineName. This is useful for playing split screen by creating two side-by-side instances, which is impossible online, unless one is using two separate paid accounts. With this PR, it makes it possible to launch from a script - otherwise, one has to launch manually, which is a pain, or create offline profiles for each instance, which interferes with some functionality like skins (my autistic son takes great issue with his skin not being visible, when using offline profiles!). Implementation is based on MultiMC, which supports this feature. See also https://github.com/PrismLauncher/PrismLauncher/issues/1059 for discussion. Signed-off-by: Michael Tyson --- launcher/Application.cpp | 24 +++++++++++++++++++----- launcher/Application.h | 5 ++++- launcher/LaunchController.cpp | 24 +++++++++++++++--------- launcher/LaunchController.h | 3 +++ 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index bb8751cccc..85373d9aeb 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -236,6 +236,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { { "l", "launch" }, "Launch the specified instance (by instance ID)", "instance" }, { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, + { { "o", "offline" }, "Launch offline (only valid in combination with --launch)", "offline" }, + { { "n", "name" }, "When launching offline, use specified name (only makes sense in combination with --launch and --offline)", "name" }, { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); @@ -250,6 +252,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_instanceIdToLaunch = parser.value("launch"); m_serverToJoin = parser.value("server"); m_profileToUse = parser.value("profile"); + if (parser.isSet("offline")) { + m_offline = true; + m_offlineName = parser.value("name"); + } m_liveCheck = parser.isSet("alive"); m_instanceIdToShowWindowOf = parser.value("show"); @@ -264,8 +270,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // error if --launch is missing with --server or --profile - if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { - std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl; + if ((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline || !m_offlineName.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { + std::cerr << "--server, --profile, --offline and --name can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; } @@ -383,6 +389,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; } + if (m_offline) { + launch.args["offline_enabled"] = "true"; + launch.args["offline_name"] = m_offlineName; + } m_peerInstance->sendMessage(launch.serialize(), timeout); } m_status = Application::Succeeded; @@ -1178,7 +1188,7 @@ void Application::performMainStartupAction() qDebug() << " Launching with account" << m_profileToUse; } - launch(inst, true, false, serverToJoin, accountToUse); + launch(inst, !m_offline, false, serverToJoin, accountToUse, m_offlineName); return; } } @@ -1263,6 +1273,8 @@ void Application::messageReceived(const QByteArray& message) QString id = received.args["id"]; QString server = received.args["server"]; QString profile = received.args["profile"]; + bool offline = received.args["offline_enabled"] == "true"; + QString offlineName = received.args["offline_name"]; InstancePtr instance; if (!id.isEmpty()) { @@ -1291,7 +1303,7 @@ void Application::messageReceived(const QByteArray& message) } } - launch(instance, true, false, serverObject, accountObject); + launch(instance, !offline, false, serverObject, accountObject, offlineName); } else { qWarning() << "Received invalid message" << message; } @@ -1333,7 +1345,8 @@ bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftServerTargetPtr serverToJoin, - MinecraftAccountPtr accountToUse) + MinecraftAccountPtr accountToUse, + const QString& offlineName) { if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; @@ -1353,6 +1366,7 @@ bool Application::launch(InstancePtr instance, controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get()); controller->setServerToJoin(serverToJoin); controller->setAccountToUse(accountToUse); + controller->setOfflineName(offlineName); if (window) { controller->setParentWidget(window); } else if (m_mainWindow) { diff --git a/launcher/Application.h b/launcher/Application.h index 7669e08ec3..499030f10c 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -202,7 +202,8 @@ class Application : public QApplication { bool online = true, bool demo = false, MinecraftServerTargetPtr serverToJoin = nullptr, - MinecraftAccountPtr accountToUse = nullptr); + MinecraftAccountPtr accountToUse = nullptr, + const QString &offlineName = QString()); bool kill(InstancePtr instance); void closeCurrentWindow(); @@ -290,6 +291,8 @@ class Application : public QApplication { QString m_instanceIdToLaunch; QString m_serverToJoin; QString m_profileToUse; + bool m_offline = false; + QString m_offlineName; bool m_liveCheck = false; QList m_urlsToImport; QString m_instanceIdToShowWindowOf; diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index a30f99439b..0d9659b0a9 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -187,16 +187,22 @@ void LaunchController::login() message = tr("Choose your demo mode player name."); } - QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); - QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; - QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); - if (!ok) { - tryagain = false; - break; + QString usedname; + if(m_offlineName.isEmpty()) { + QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString(); + QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName; + QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok); + if (!ok) { + tryagain = false; + break; + } + if (name.length()) { + usedname = name; + APPLICATION->settings()->set("LastOfflinePlayerName", usedname); + } } - if (name.length()) { - usedname = name; - APPLICATION->settings()->set("LastOfflinePlayerName", usedname); + else { + usedname = m_offlineName; } m_session->MakeOffline(usedname); // offline flavored game from here :3 diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index f1c88afb7a..0c4fef12e1 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -56,6 +56,8 @@ class LaunchController : public Task { void setOnline(bool online) { m_online = online; } + void setOfflineName(const QString &offlineName) { m_offlineName = offlineName; } + void setDemo(bool demo) { m_demo = demo; } void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; } @@ -85,6 +87,7 @@ class LaunchController : public Task { private: BaseProfilerFactory* m_profiler = nullptr; bool m_online = true; + QString m_offlineName; bool m_demo = false; InstancePtr m_instance; QWidget* m_parentWidget = nullptr;