From 019030c09f67c4bb6c3d08e8d05ff5afc9a09708 Mon Sep 17 00:00:00 2001 From: Mehmet Samet Duman Date: Sat, 12 Sep 2026 15:46:27 +0300 Subject: [PATCH] The --world parameter has been added Signed-off-by: Mehmet Samet Duman --- launcher/Application.cpp | 38 +++++++++++++- launcher/Application.h | 3 ++ launcher/minecraft/MinecraftInstance.cpp | 20 ++++++-- .../launch/MinecraftServerTarget.cpp | 11 ++++- .../minecraft/launch/MinecraftServerTarget.h | 7 ++- launcher/ui/dialogs/CreateShortcutDialog.cpp | 6 +-- launcher/ui/pages/instance/ServersPage.cpp | 2 +- .../projecttick/modern/ModernLauncher.java | 49 ++++++++++++++++--- 8 files changed, 112 insertions(+), 24 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ed667731..916cba8c 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -348,6 +348,18 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) return; } + if (m_instanceIdToLaunch.isEmpty() && !m_worldToJoin.isEmpty()) { + qWarning() << "--world can only be used in combination with --launch!"; + m_status = Application::Failed; + return; + } + + if (!m_serverToJoin.isEmpty() && !m_worldToJoin.isEmpty()) { + qWarning() << "--server and --world cannot be used together!"; + m_status = Application::Failed; + return; + } + if (m_instanceIdToLaunch.isEmpty() && !m_profileToUse.isEmpty()) { qWarning() << "--profile can only be used in combination with --launch!"; @@ -504,6 +516,13 @@ QHash Application::parseCommandLine(int& argc, char** argv) parser.addDocumentation("server", "Join the specified server on launch (only valid " "in combination with --launch)"); + // --world + parser.addOption("world"); + parser.addShortOpt("world", 'w'); + parser.addDocumentation("world", + "Open the specified singleplayer world on launch, " + "by its save folder name (only valid in " + "combination with --launch)"); // --profile parser.addOption("profile"); parser.addShortOpt("profile", 'a'); @@ -571,6 +590,7 @@ QHash Application::parseCommandLine(int& argc, char** argv) m_instanceIdToLaunch = args["launch"].toString(); m_serverToJoin = args["server"].toString(); + m_worldToJoin = args["world"].toString(); m_profileToUse = args["profile"].toString(); m_liveCheck = args["alive"].toBool(); m_zipToImport = args["import"].toUrl(); @@ -732,6 +752,9 @@ bool Application::initPeerInstance() if (!m_serverToJoin.isEmpty()) { launch.args["server"] = m_serverToJoin; } + if (!m_worldToJoin.isEmpty()) { + launch.args["world"] = m_worldToJoin; + } if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; } @@ -822,6 +845,9 @@ void Application::setupPaths(const QString& binPath, const QString& origcwdPath, if (!m_serverToJoin.isEmpty()) { qInfo().noquote() << "Address of server to join :" << m_serverToJoin; } + if (!m_worldToJoin.isEmpty()) { + qInfo().noquote() << "Name of world to join :" << m_worldToJoin; + } qInfo().noquote() << "<> Paths set."; if (m_liveCheck) { @@ -1490,8 +1516,12 @@ void Application::performMainStartupAction() if (!m_serverToJoin.isEmpty()) { // FIXME: validate the server string serverToJoin.reset(new MinecraftServerTarget( - MinecraftServerTarget::parse(m_serverToJoin))); + MinecraftServerTarget::parse(m_serverToJoin, false))); qDebug() << " Launching with server" << m_serverToJoin; + } else if (!m_worldToJoin.isEmpty()) { + serverToJoin.reset(new MinecraftServerTarget( + MinecraftServerTarget::parse(m_worldToJoin, true))); + qDebug() << " Launching with world" << m_worldToJoin; } if (!m_profileToUse.isEmpty()) { @@ -1576,6 +1606,7 @@ void Application::messageReceived(const QByteArray& message) } else if (command == "launch") { QString id = received.args["id"]; QString server = received.args["server"]; + QString world = received.args["world"]; QString profile = received.args["profile"]; InstancePtr instance; @@ -1594,7 +1625,10 @@ void Application::messageReceived(const QByteArray& message) MinecraftServerTargetPtr serverObject = nullptr; if (!server.isEmpty()) { serverObject = std::make_shared( - MinecraftServerTarget::parse(server)); + MinecraftServerTarget::parse(server, false)); + } else if (!world.isEmpty()) { + serverObject = std::make_shared( + MinecraftServerTarget::parse(world, true)); } MinecraftAccountPtr accountObject; diff --git a/launcher/Application.h b/launcher/Application.h index 11ae0c9d..57d9b4e0 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -328,6 +328,9 @@ class Application : public QApplication public: QString m_instanceIdToLaunch; QString m_serverToJoin; + /* Save folder name of a world to open on launch, from --world. Mutually + * exclusive with m_serverToJoin; startup rejects both being set. */ + QString m_worldToJoin; QString m_profileToUse; bool m_liveCheck = false; QUrl m_zipToImport; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 6c1222be..f4317e22 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -569,8 +569,17 @@ QStringList MinecraftInstance::processMinecraftArgs( } if (serverToJoin && !serverToJoin->address.isEmpty()) { - args_pattern += " --server " + serverToJoin->address; - args_pattern += " --port " + QString::number(serverToJoin->port); + if (profile->hasTrait("feature:is_quick_play_multiplayer")) { + args_pattern += " --quickPlayMultiplayer " + + serverToJoin->address + ":" + + QString::number(serverToJoin->port); + } else { + args_pattern += " --server " + serverToJoin->address; + args_pattern += " --port " + QString::number(serverToJoin->port); + } + } else if (serverToJoin && !serverToJoin->world.isEmpty() && + profile->hasTrait("feature:is_quick_play_singleplayer")) { + args_pattern += " --quickPlaySingleplayer " + serverToJoin->world; } QMap token_mapping; @@ -636,6 +645,8 @@ MinecraftInstance::createLaunchScript(AuthSessionPtr session, launchScript += "serverAddress " + serverToJoin->address + "\n"; launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n"; + } else if (serverToJoin && !serverToJoin->world.isEmpty()) { + launchScript += "worldName " + serverToJoin->world + "\n"; } // generic minecraft params @@ -1016,10 +1027,11 @@ MinecraftInstance::createLaunchTask(AuthSessionPtr session, QString fullAddress = m_settings->get("JoinServerOnLaunchAddress").toString(); serverToJoin.reset(new MinecraftServerTarget( - MinecraftServerTarget::parse(fullAddress))); + MinecraftServerTarget::parse(fullAddress, false))); } - if (serverToJoin && serverToJoin->port == 25565) { + if (serverToJoin && !serverToJoin->address.isEmpty() && + serverToJoin->port == 25565) { // Resolve server address to join on launch auto* step = new LookupServerAddress(pptr); step->setLookupAddress(serverToJoin->address); diff --git a/launcher/minecraft/launch/MinecraftServerTarget.cpp b/launcher/minecraft/launch/MinecraftServerTarget.cpp index 80bcbbe5..b58f0fcd 100644 --- a/launcher/minecraft/launch/MinecraftServerTarget.cpp +++ b/launcher/minecraft/launch/MinecraftServerTarget.cpp @@ -24,8 +24,15 @@ // FIXME: the way this is written, it can't ever do any sort of validation and // can accept total junk -MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress) +MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress, + bool useWorld) { + if (useWorld) { + MinecraftServerTarget target; + target.world = fullAddress; + return target; + } + QStringList split = fullAddress.split(":"); // The logic below replicates the exact logic minecraft uses for parsing @@ -63,5 +70,5 @@ MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress) } } - return MinecraftServerTarget{realAddress, realPort}; + return MinecraftServerTarget{realAddress, realPort, QString()}; } diff --git a/launcher/minecraft/launch/MinecraftServerTarget.h b/launcher/minecraft/launch/MinecraftServerTarget.h index 5db9fd7d..1c27fdca 100644 --- a/launcher/minecraft/launch/MinecraftServerTarget.h +++ b/launcher/minecraft/launch/MinecraftServerTarget.h @@ -26,9 +26,12 @@ struct MinecraftServerTarget { QString address; - quint16 port; + quint16 port = 25565; - static MinecraftServerTarget parse(const QString& fullAddress); + QString world; + + static MinecraftServerTarget parse(const QString& fullAddress, + bool useWorld); }; typedef std::shared_ptr MinecraftServerTargetPtr; diff --git a/launcher/ui/dialogs/CreateShortcutDialog.cpp b/launcher/ui/dialogs/CreateShortcutDialog.cpp index 4451e480..aa82133b 100644 --- a/launcher/ui/dialogs/CreateShortcutDialog.cpp +++ b/launcher/ui/dialogs/CreateShortcutDialog.cpp @@ -78,7 +78,7 @@ CreateShortcutDialog::CreateShortcutDialog(MinecraftInstance* instance, tr("%1 [%2] - Last Played: %3") .arg(world.name(), world.gameType().toTranslatedString(), world.lastPlayed().toString(Qt::ISODate)), - world.name()); + world.folderName()); } } @@ -213,10 +213,6 @@ void CreateShortcutDialog::createShortcut() if (ui->targetCheckbox->isChecked()) { if (ui->worldTarget->isChecked()) { - /* Unreachable while m_canJoinWorld is always false. When - * quick play does arrive, --world has to be added to the - * command line in Application.cpp alongside --server, or - * the shortcut this writes will be rejected on startup. */ shortcut.targetString = tr("world"); shortcut.extraArgs << QStringLiteral("--world") diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index 820da668..c4ee3279 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -727,7 +727,7 @@ void ServersPage::on_actionJoin_triggered() const auto& address = m_model->at(currentServer)->m_address; APPLICATION->launch(m_inst, LaunchMode::Normal, std::make_shared( - MinecraftServerTarget::parse(address))); + MinecraftServerTarget::parse(address, false))); } #include "ServersPage.moc" diff --git a/libraries/launcher/org/projecttick/modern/ModernLauncher.java b/libraries/launcher/org/projecttick/modern/ModernLauncher.java index 8913a080..6e5b01ea 100644 --- a/libraries/launcher/org/projecttick/modern/ModernLauncher.java +++ b/libraries/launcher/org/projecttick/modern/ModernLauncher.java @@ -52,6 +52,8 @@ * natives - Path to the native libraries directory * serverAddress - Server address for direct-connect on launch (optional) * serverPort - Server port for direct-connect on launch (optional) + * worldName - Singleplayer world save folder to open on launch (optional) + * traits - Version traits, used to pick quick play vs. legacy args */ public class ModernLauncher implements MeshMC { @@ -124,21 +126,52 @@ private int doLaunch(ParamBucket params) throws Exception params.allSafe("param", Collections.emptyList()) ); - // Direct-connect: server address / port are passed as separate keys and - // must be appended to game args manually (processMinecraftArgs skips them - // when a launch script is used). + // Launch destination: a server or a world, never both. Passed as + // separate keys and appended here rather than by the C++ side, because + // processMinecraftArgs skips them when a launch script is used. + // + // Which argument the game understands depends on its version: + // Minecraft 1.20 (23w14a) removed --server/--port and replaced them + // with quick play. The version's traits say which it is, so ask them + // instead of guessing -- passing the wrong pair makes the game either + // ignore the destination or refuse to start. + List traits = params.allSafe("traits", Collections.emptyList()); + boolean quickPlayMultiplayer = traits.contains("feature:is_quick_play_multiplayer"); + boolean quickPlaySingleplayer = traits.contains("feature:is_quick_play_singleplayer"); + String serverAddress = params.firstSafe("serverAddress", ""); String serverPort = params.firstSafe("serverPort", ""); + String worldName = params.firstSafe("worldName", ""); + if (serverAddress != null && !serverAddress.isEmpty()) { - gameArgs.add("--server"); - gameArgs.add(serverAddress); - if (serverPort != null && !serverPort.isEmpty()) + if (quickPlayMultiplayer) + { + gameArgs.add("--quickPlayMultiplayer"); + // quickPlayMultiplayer takes one "host:port" argument, so an + // absent port has to become the default rather than nothing. + String port = (serverPort == null || serverPort.isEmpty()) ? "25565" : serverPort; + gameArgs.add(serverAddress + ":" + port); + } + else { - gameArgs.add("--port"); - gameArgs.add(serverPort); + gameArgs.add("--server"); + gameArgs.add(serverAddress); + if (serverPort != null && !serverPort.isEmpty()) + { + gameArgs.add("--port"); + gameArgs.add(serverPort); + } } } + else if (worldName != null && !worldName.isEmpty() && quickPlaySingleplayer) + { + // No pre-quick-play way to open a world exists, so unlike the + // server case there is nothing to fall back to: a version without + // the trait just launches to the main menu. + gameArgs.add("--quickPlaySingleplayer"); + gameArgs.add(worldName); + } // --- Build the full command --- List command = buildCommand(javaPath, jvmArgs, natives, classpath, mainClass, gameArgs);