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 <michael@atastypixel.com>
This commit is contained in:
Michael Tyson 2024-04-23 14:23:53 +10:00
parent 65b0fdf08d
commit 43e4481b70
4 changed files with 39 additions and 10 deletions

View File

@ -242,6 +242,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
{ { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" },
{ { "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" } });
@ -257,6 +259,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_serverToJoin = parser.value("server");
m_worldToJoin = parser.value("world");
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");
@ -271,8 +277,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
}
// error if --launch is missing with --server or --profile
if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.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_worldToJoin.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;
}
@ -397,6 +403,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;
@ -1209,7 +1219,7 @@ void Application::performMainStartupAction()
qDebug() << " Launching with account" << m_profileToUse;
}
launch(inst, true, false, targetToJoin, accountToUse);
launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName);
return;
}
}
@ -1308,6 +1318,8 @@ void Application::messageReceived(const QByteArray& message)
QString server = received.args["server"];
QString world = received.args["world"];
QString profile = received.args["profile"];
bool offline = received.args["offline_enabled"] == "true";
QString offlineName = received.args["offline_name"];
InstancePtr instance;
if (!id.isEmpty()) {
@ -1337,7 +1349,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;
}
@ -1375,7 +1387,12 @@ bool Application::openJsonEditor(const QString& filename)
}
}
bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse)
bool Application::launch(InstancePtr instance,
bool online,
bool demo,
MinecraftTarget::Ptr targetToJoin,
MinecraftAccountPtr accountToUse,
const QString& offlineName)
{
if (m_updateRunning) {
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
@ -1395,6 +1412,7 @@ bool Application::launch(InstancePtr instance, bool online, bool demo, Minecraft
controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get());
controller->setTargetToJoin(targetToJoin);
controller->setAccountToUse(accountToUse);
controller->setOfflineName(offlineName);
if (window) {
controller->setParentWidget(window);
} else if (m_mainWindow) {

View File

@ -211,7 +211,8 @@ class Application : public QApplication {
bool online = true,
bool demo = false,
MinecraftTarget::Ptr targetToJoin = nullptr,
MinecraftAccountPtr accountToUse = nullptr);
MinecraftAccountPtr accountToUse = nullptr,
const QString &offlineName = QString());
bool kill(InstancePtr instance);
void closeCurrentWindow();
@ -300,6 +301,8 @@ class Application : public QApplication {
QString m_serverToJoin;
QString m_worldToJoin;
QString m_profileToUse;
bool m_offline = false;
QString m_offlineName;
bool m_liveCheck = false;
QList<QUrl> m_urlsToImport;
QString m_instanceIdToShowWindowOf;

View File

@ -235,11 +235,16 @@ void LaunchController::login()
if (!m_session->wants_online) {
// we ask the user for a player name
bool ok = false;
auto name = askOfflineName(m_session->player_name, m_session->demo, ok);
QString name;
if (m_offlineName.isEmpty()) {
name = askOfflineName(m_session->player_name, m_session->demo, ok);
if (!ok) {
tryagain = false;
break;
}
} else {
name = m_offlineName;
}
m_session->MakeOffline(name);
// offline flavored game from here :3
}

View File

@ -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; }
@ -88,6 +90,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;