diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6c0319984..84b759e09 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -236,6 +236,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { { { "d", "dir" }, "Use a custom path as application root (use '.' for current directory)", "directory" }, { { "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" }, + { { "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" }, { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, @@ -249,7 +250,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) parser.process(arguments()); m_instanceIdToLaunch = parser.value("launch"); - m_serverToJoin = parser.value("server"); + m_server_to_join = parser.value("server"); + m_world_to_join = parser.value("world"); m_profileToUse = parser.value("profile"); m_liveCheck = parser.isSet("alive"); @@ -265,7 +267,7 @@ 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()) { + if (((!m_server_to_join.isEmpty() || !m_world_to_join.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; @@ -383,8 +385,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) launch.command = "launch"; launch.args["id"] = m_instanceIdToLaunch; - if (!m_serverToJoin.isEmpty()) { - launch.args["server"] = m_serverToJoin; + if (!m_server_to_join.isEmpty()) { + launch.args["server"] = m_server_to_join; + } else if (!m_world_to_join.isEmpty()) { + launch.args["world"] = m_world_to_join; } if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; @@ -521,8 +525,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) if (!m_instanceIdToLaunch.isEmpty()) { qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch; } - if (!m_serverToJoin.isEmpty()) { - qDebug() << "Address of server to join :" << m_serverToJoin; + if (!m_server_to_join.isEmpty()) { + qDebug() << "Address of server to join :" << m_server_to_join; + } else if (!m_world_to_join.isEmpty()) { + qDebug() << "Name of the world to join :" << m_world_to_join; } qDebug() << "<> Paths set."; } @@ -1157,14 +1163,17 @@ void Application::performMainStartupAction() if (!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); if (inst) { - MinecraftServerTargetPtr serverToJoin = nullptr; + MinecraftTarget::Ptr targetToJoin = nullptr; MinecraftAccountPtr accountToUse = nullptr; qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching"; - if (!m_serverToJoin.isEmpty()) { + if (!m_server_to_join.isEmpty()) { // FIXME: validate the server string - serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin))); - qDebug() << " Launching with server" << m_serverToJoin; + targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_server_to_join, false))); + qDebug() << " Launching with server" << m_server_to_join; + } else if (!m_world_to_join.isEmpty()) { + targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(m_world_to_join, true))); + qDebug() << " Launching with world" << m_world_to_join; } if (!m_profileToUse.isEmpty()) { @@ -1175,7 +1184,7 @@ void Application::performMainStartupAction() qDebug() << " Launching with account" << m_profileToUse; } - launch(inst, true, false, serverToJoin, accountToUse); + launch(inst, true, false, targetToJoin, accountToUse); return; } } @@ -1265,6 +1274,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; @@ -1279,11 +1289,12 @@ void Application::messageReceived(const QByteArray& message) return; } - MinecraftServerTargetPtr serverObject = nullptr; + MinecraftTarget::Ptr serverObject = nullptr; if (!server.isEmpty()) { - serverObject = std::make_shared(MinecraftServerTarget::parse(server)); + serverObject = std::make_shared(MinecraftTarget::parse(server, false)); + } else if (!world.isEmpty()) { + serverObject = std::make_shared(MinecraftTarget::parse(world, true)); } - MinecraftAccountPtr accountObject; if (!profile.isEmpty()) { accountObject = accounts()->getAccountByProfileName(profile); @@ -1332,11 +1343,7 @@ bool Application::openJsonEditor(const QString& filename) } } -bool Application::launch(InstancePtr instance, - bool online, - bool demo, - MinecraftServerTargetPtr serverToJoin, - MinecraftAccountPtr accountToUse) +bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse) { if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; @@ -1354,7 +1361,7 @@ bool Application::launch(InstancePtr instance, controller->setOnline(online); controller->setDemo(demo); controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get()); - controller->setServerToJoin(serverToJoin); + controller->setTargetToJoin(targetToJoin); controller->setAccountToUse(accountToUse); if (window) { controller->setParentWidget(window); diff --git a/launcher/Application.h b/launcher/Application.h index 8303c7475..fb54b1005 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -47,7 +47,7 @@ #include -#include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/launch/MinecraftTarget.h" class LaunchController; class LocalPeer; @@ -202,7 +202,7 @@ class Application : public QApplication { bool launch(InstancePtr instance, bool online = true, bool demo = false, - MinecraftServerTargetPtr serverToJoin = nullptr, + MinecraftTarget::Ptr targetToJoin = nullptr, MinecraftAccountPtr accountToUse = nullptr); bool kill(InstancePtr instance); void closeCurrentWindow(); @@ -289,7 +289,8 @@ class Application : public QApplication { QString m_detectedGLFWPath; QString m_detectedOpenALPath; QString m_instanceIdToLaunch; - QString m_serverToJoin; + QString m_server_to_join; + QString m_world_to_join; QString m_profileToUse; bool m_liveCheck = false; QList m_urlsToImport; diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 499ec7866..8c80331bc 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -56,7 +56,7 @@ #include "net/Mode.h" #include "RuntimeContext.h" -#include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/launch/MinecraftTarget.h" class QDir; class Task; @@ -184,7 +184,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0; + virtual shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) = 0; /// returns the current launch task (if any) shared_qobject_ptr getLaunchTask(); @@ -256,7 +256,7 @@ class BaseInstance : public QObject, public std::enable_shared_from_this getPages() override { QList values; @@ -39,7 +39,7 @@ class InstancePageProvider : protected QObject, public BasePageProvider { values.append(new TexturePackPage(onesix.get(), onesix->texturePackList())); values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList())); values.append(new NotesPage(onesix.get())); - values.append(new WorldListPage(onesix.get(), onesix->worldList())); + values.append(new WorldListPage(onesix, onesix->worldList())); values.append(new ServersPage(onesix)); // values.append(new GameOptionsPage(onesix.get())); values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 3866a7672..83bf3838e 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -324,7 +324,7 @@ void LaunchController::launchInstance() return; } - m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin); + m_launcher = m_instance->createLaunchTask(m_session, m_target_to_join); if (!m_launcher) { emitFailed(tr("Couldn't instantiate a launcher.")); return; diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index bc688f2ba..e620b8c62 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -39,7 +39,7 @@ #include #include "minecraft/auth/MinecraftAccount.h" -#include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/launch/MinecraftTarget.h" class InstanceWindow; class LaunchController : public Task { @@ -48,7 +48,7 @@ class LaunchController : public Task { void executeTask() override; LaunchController(QObject* parent = nullptr); - virtual ~LaunchController() {}; + virtual ~LaunchController() = default; void setInstance(InstancePtr instance) { m_instance = instance; } @@ -62,7 +62,7 @@ class LaunchController : public Task { void setParentWidget(QWidget* widget) { m_parentWidget = widget; } - void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); } + void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_target_to_join = std::move(targetToJoin); } void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); } @@ -94,5 +94,5 @@ class LaunchController : public Task { MinecraftAccountPtr m_accountToUse = nullptr; AuthSessionPtr m_session; shared_qobject_ptr m_launcher; - MinecraftServerTargetPtr m_serverToJoin; + MinecraftTarget::Ptr m_target_to_join; }; diff --git a/launcher/NullInstance.h b/launcher/NullInstance.h index b6a9478a8..3ee38e76c 100644 --- a/launcher/NullInstance.h +++ b/launcher/NullInstance.h @@ -46,13 +46,13 @@ class NullInstance : public BaseInstance { { setVersionBroken(true); } - virtual ~NullInstance() {}; + virtual ~NullInstance() = default; void saveNow() override {} void loadSpecificSettings() override { setSpecificSettingsLoaded(true); } QString getStatusbarDescription() override { return tr("Unknown instance type"); }; QSet traits() const override { return {}; }; QString instanceConfigFolder() const override { return instanceRoot(); }; - shared_qobject_ptr createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; } + shared_qobject_ptr createLaunchTask(AuthSessionPtr, MinecraftTarget::Ptr) override { return nullptr; } shared_qobject_ptr createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; } QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); } QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); } @@ -64,7 +64,7 @@ class NullInstance : public BaseInstance { bool canEdit() const override { return false; } bool canLaunch() const override { return false; } void populateLaunchMenu(QMenu* menu) override {} - QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override + QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override { QStringList out; out << "Null instance - placeholder."; diff --git a/launcher/launch/steps/LookupServerAddress.cpp b/launcher/launch/steps/LookupServerAddress.cpp index 9bdac203b..4b67b3092 100644 --- a/launcher/launch/steps/LookupServerAddress.cpp +++ b/launcher/launch/steps/LookupServerAddress.cpp @@ -30,7 +30,7 @@ void LookupServerAddress::setLookupAddress(const QString& lookupAddress) m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress)); } -void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output) +void LookupServerAddress::setOutputAddressPtr(MinecraftTarget::Ptr output) { m_output = std::move(output); } diff --git a/launcher/launch/steps/LookupServerAddress.h b/launcher/launch/steps/LookupServerAddress.h index acbd74309..506314ee8 100644 --- a/launcher/launch/steps/LookupServerAddress.h +++ b/launcher/launch/steps/LookupServerAddress.h @@ -19,20 +19,20 @@ #include #include -#include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/launch/MinecraftTarget.h" class LookupServerAddress : public LaunchStep { Q_OBJECT public: explicit LookupServerAddress(LaunchTask* parent); - virtual ~LookupServerAddress() {}; + virtual ~LookupServerAddress() = default; virtual void executeTask(); virtual bool abort(); virtual bool canAbort() const { return true; } void setLookupAddress(const QString& lookupAddress); - void setOutputAddressPtr(MinecraftServerTargetPtr output); + void setOutputAddressPtr(MinecraftTarget::Ptr output); private slots: void on_dnsLookupFinished(); @@ -42,5 +42,5 @@ class LookupServerAddress : public LaunchStep { QDnsLookup* m_dnsLookup; QString m_lookupAddress; - MinecraftServerTargetPtr m_output; + MinecraftTarget::Ptr m_output; }; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index d119104fe..c50d57513 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -196,8 +196,9 @@ void MinecraftInstance::loadSpecificSettings() } // Join server on launch, this does not have a global override - m_settings->registerSetting("JoinServerOnLaunch", false); + m_settings->registerSetting({ "JoinServerOnLaunch", "JoinOnLaunch" }, false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); + m_settings->registerSetting("JoinWorldOnLaunch", ""); // Use account for instance, this does not have a global override m_settings->registerSetting("UseAccountForInstance", false); @@ -523,8 +524,7 @@ QStringList MinecraftInstance::javaArguments() if (javaVersion.isModular() && shouldApplyOnlineFixes()) // allow reflective access to java.net - required by the skin fix - args << "--add-opens" - << "java.base/java.net=ALL-UNNAMED"; + args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; return args; } @@ -656,7 +656,7 @@ static QString replaceTokensIn(QString text, QMap with) return result; } -QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const +QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) const { auto profile = m_components->getProfile(); QString args_pattern = profile->getMinecraftArguments(); @@ -664,12 +664,16 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine args_pattern += " --tweakClass " + tweaker; } - if (serverToJoin && !serverToJoin->address.isEmpty()) { - 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); + if (targetToJoin) { + if (!targetToJoin->address.isEmpty()) { + if (profile->hasTrait("feature:is_quick_play_multiplayer")) { + args_pattern += " --quickPlayMultiplayer " + targetToJoin->address + ':' + QString::number(targetToJoin->port); + } else { + args_pattern += " --server " + targetToJoin->address; + args_pattern += " --port " + QString::number(targetToJoin->port); + } + } else if (!targetToJoin->world.isEmpty() && profile->hasTrait("feature:is_quick_play_singleplayer")) { + args_pattern += " --quickPlaySingleplayer " + targetToJoin->world; } } @@ -713,7 +717,7 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session, Mine return parts; } -QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) +QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { QString launchScript; @@ -732,9 +736,13 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS launchScript += "appletClass " + appletClass + "\n"; } - if (serverToJoin && !serverToJoin->address.isEmpty()) { - launchScript += "serverAddress " + serverToJoin->address + "\n"; - launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n"; + if (targetToJoin) { + if (!targetToJoin->address.isEmpty()) { + launchScript += "serverAddress " + targetToJoin->address + "\n"; + launchScript += "serverPort " + QString::number(targetToJoin->port) + "\n"; + } else if (!targetToJoin->world.isEmpty()) { + launchScript += "worldName " + targetToJoin->world + "\n"; + } } // generic minecraft params @@ -787,13 +795,11 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS return launchScript; } -QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) +QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { QStringList out; - out << "Main Class:" - << " " + getMainClass() << ""; - out << "Native path:" - << " " + getNativePath() << ""; + out << "Main Class:" << " " + getMainClass() << ""; + out << "Native path:" << " " + getNativePath() << ""; auto profile = m_components->getProfile(); @@ -884,7 +890,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr out << ""; } - auto params = processMinecraftArgs(nullptr, serverToJoin); + auto params = processMinecraftArgs(nullptr, targetToJoin); out << "Params:"; out << " " + params.join(' '); out << ""; @@ -1034,7 +1040,7 @@ Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode) return nullptr; } -shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) +shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { updateRuntimeContext(); // FIXME: get rid of shared_from_this ... @@ -1058,16 +1064,23 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(makeShared(pptr)); } - if (!serverToJoin && settings()->get("JoinServerOnLaunch").toBool()) { + if (!targetToJoin && settings()->get("JoinOnLaunch").toBool()) { QString fullAddress = settings()->get("JoinServerOnLaunchAddress").toString(); - serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress))); + if (!fullAddress.isEmpty()) { + targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(fullAddress, false))); + } else { + QString world = settings()->get("JoinWorldOnLaunch").toString(); + if (!world.isEmpty()) { + targetToJoin.reset(new MinecraftTarget(MinecraftTarget::parse(world, true))); + } + } } - if (serverToJoin && serverToJoin->port == 25565) { + if (targetToJoin && targetToJoin->port == 25565) { // Resolve server address to join on launch auto step = makeShared(pptr); - step->setLookupAddress(serverToJoin->address); - step->setOutputAddressPtr(serverToJoin); + step->setLookupAddress(targetToJoin->address); + step->setOutputAddressPtr(targetToJoin); process->appendStep(step); } @@ -1100,7 +1113,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt // print some instance info here... { - process->appendStep(makeShared(pptr, session, serverToJoin)); + process->appendStep(makeShared(pptr, session, targetToJoin)); } // extract native jars if needed @@ -1123,7 +1136,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt auto step = makeShared(pptr); step->setWorkingDirectory(gameRoot()); step->setAuthSession(session); - step->setServerToJoin(serverToJoin); + step->setTargetToJoin(targetToJoin); process->appendStep(step); } diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 7af0df389..ad2cda186 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -39,7 +39,7 @@ #include #include #include "BaseInstance.h" -#include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/launch/MinecraftTarget.h" #include "minecraft/mod/Mod.h" class ModFolderModel; @@ -56,7 +56,7 @@ class MinecraftInstance : public BaseInstance { Q_OBJECT public: MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir); - virtual ~MinecraftInstance() {}; + virtual ~MinecraftInstance() = default; virtual void saveNow() override; void loadSpecificSettings() override; @@ -121,11 +121,11 @@ class MinecraftInstance : public BaseInstance { ////// Launch stuff ////// Task::Ptr createUpdateTask(Net::Mode mode) override; - shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override; + shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) override; QStringList extraArguments() override; - QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override; + QStringList verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) override; QList getJarMods() const; - QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin); + QString createLaunchScript(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin); /// get arguments passed to java QStringList javaArguments(); QString getLauncher(); @@ -155,7 +155,7 @@ class MinecraftInstance : public BaseInstance { virtual QString getMainClass() const; // FIXME: remove - virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const; + virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftTarget::Ptr targetToJoin) const; virtual JavaVersion getJavaVersion(); diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp index b35d55924..be1bde464 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.cpp +++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp @@ -90,7 +90,7 @@ void LauncherPartLaunch::executeTask() } } - m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin); + m_launchScript = minecraftInstance->createLaunchScript(m_session, m_target_to_join); QStringList args = minecraftInstance->javaArguments(); QString allArgs = args.join(", "); emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::Launcher); diff --git a/launcher/minecraft/launch/LauncherPartLaunch.h b/launcher/minecraft/launch/LauncherPartLaunch.h index fcd4daec6..ca0906550 100644 --- a/launcher/minecraft/launch/LauncherPartLaunch.h +++ b/launcher/minecraft/launch/LauncherPartLaunch.h @@ -19,13 +19,13 @@ #include #include -#include "MinecraftServerTarget.h" +#include "MinecraftTarget.h" class LauncherPartLaunch : public LaunchStep { Q_OBJECT public: explicit LauncherPartLaunch(LaunchTask* parent); - virtual ~LauncherPartLaunch() {}; + virtual ~LauncherPartLaunch() = default; virtual void executeTask(); virtual bool abort(); @@ -34,7 +34,7 @@ class LauncherPartLaunch : public LaunchStep { void setWorkingDirectory(const QString& wd); void setAuthSession(AuthSessionPtr session) { m_session = session; } - void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); } + void setTargetToJoin(MinecraftTarget::Ptr targetToJoin) { m_target_to_join = std::move(targetToJoin); } private slots: void on_state(LoggedProcess::State state); @@ -44,7 +44,7 @@ class LauncherPartLaunch : public LaunchStep { QString m_command; AuthSessionPtr m_session; QString m_launchScript; - MinecraftServerTargetPtr m_serverToJoin; + MinecraftTarget::Ptr m_target_to_join; bool mayProceed = false; }; diff --git a/launcher/minecraft/launch/MinecraftServerTarget.cpp b/launcher/minecraft/launch/MinecraftTarget.cpp similarity index 86% rename from launcher/minecraft/launch/MinecraftServerTarget.cpp rename to launcher/minecraft/launch/MinecraftTarget.cpp index e201efab1..ba9f87511 100644 --- a/launcher/minecraft/launch/MinecraftServerTarget.cpp +++ b/launcher/minecraft/launch/MinecraftTarget.cpp @@ -13,13 +13,18 @@ * limitations under the License. */ -#include "MinecraftServerTarget.h" +#include "MinecraftTarget.h" #include // 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) +MinecraftTarget MinecraftTarget::parse(const QString& fullAddress, bool useWorld) { + if (useWorld) { + MinecraftTarget target; + target.world = fullAddress; + return target; + } QStringList split = fullAddress.split(":"); // The logic below replicates the exact logic minecraft uses for parsing server addresses. @@ -56,5 +61,5 @@ MinecraftServerTarget MinecraftServerTarget::parse(const QString& fullAddress) } } - return MinecraftServerTarget{ realAddress, realPort }; + return MinecraftTarget{ realAddress, realPort }; } diff --git a/launcher/minecraft/launch/MinecraftServerTarget.h b/launcher/minecraft/launch/MinecraftTarget.h similarity index 80% rename from launcher/minecraft/launch/MinecraftServerTarget.h rename to launcher/minecraft/launch/MinecraftTarget.h index 2edd8a30d..7f8b268d9 100644 --- a/launcher/minecraft/launch/MinecraftServerTarget.h +++ b/launcher/minecraft/launch/MinecraftTarget.h @@ -19,11 +19,11 @@ #include -struct MinecraftServerTarget { +struct MinecraftTarget { QString address; quint16 port; - static MinecraftServerTarget parse(const QString& fullAddress); + QString world; + static MinecraftTarget parse(const QString& fullAddress, bool useWorld); + using Ptr = std::shared_ptr; }; - -using MinecraftServerTargetPtr = std::shared_ptr; diff --git a/launcher/minecraft/launch/PrintInstanceInfo.cpp b/launcher/minecraft/launch/PrintInstanceInfo.cpp index e3a45b030..78cb26378 100644 --- a/launcher/minecraft/launch/PrintInstanceInfo.cpp +++ b/launcher/minecraft/launch/PrintInstanceInfo.cpp @@ -129,6 +129,6 @@ void PrintInstanceInfo::executeTask() #endif logLines(log, MessageLevel::Launcher); - logLines(instance->verboseDescription(m_session, m_serverToJoin), MessageLevel::Launcher); + logLines(instance->verboseDescription(m_session, m_target_to_join), MessageLevel::Launcher); emitSucceeded(); } diff --git a/launcher/minecraft/launch/PrintInstanceInfo.h b/launcher/minecraft/launch/PrintInstanceInfo.h index 93c5f0fd6..780ac5b2c 100644 --- a/launcher/minecraft/launch/PrintInstanceInfo.h +++ b/launcher/minecraft/launch/PrintInstanceInfo.h @@ -16,22 +16,21 @@ #pragma once #include -#include #include "minecraft/auth/AuthSession.h" -#include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/launch/MinecraftTarget.h" // FIXME: temporary wrapper for existing task. class PrintInstanceInfo : public LaunchStep { Q_OBJECT public: - explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) - : LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {}; - virtual ~PrintInstanceInfo() {}; + explicit PrintInstanceInfo(LaunchTask* parent, AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) + : LaunchStep(parent), m_session(session), m_target_to_join(targetToJoin) {}; + virtual ~PrintInstanceInfo() = default; virtual void executeTask(); virtual bool canAbort() const { return false; } private: AuthSessionPtr m_session; - MinecraftServerTargetPtr m_serverToJoin; + MinecraftTarget::Ptr m_target_to_join; }; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 76add9402..a017d5e13 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -36,6 +36,8 @@ */ #include "InstanceSettingsPage.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/WorldList.h" #include "ui_InstanceSettingsPage.h" #include @@ -71,6 +73,22 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance* inst, QWidget* parent) connect(ui->useNativeGLFWCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeGLFWChanged); connect(ui->useNativeOpenALCheck, &QAbstractButton::toggled, this, &InstanceSettingsPage::onUseNativeOpenALChanged); + auto mInst = dynamic_cast(inst); + m_world_quickplay_supported = mInst && mInst->traits().contains("feature:is_quick_play_singleplayer"); + if (m_world_quickplay_supported) { + auto worlds = mInst->worldList(); + worlds->update(); + for (const auto& world : worlds->allWorlds()) { + ui->worldsCb->addItem(world.folderName()); + } + } else { + ui->worldsCb->hide(); + ui->worldJoinButton->hide(); + ui->serverJoinAddressButton->setChecked(true); + ui->serverJoinAddress->setEnabled(true); + ui->serverJoinAddressButton->setStyleSheet("QRadioButton::indicator { width: 0px; height: 0px; }"); + } + loadSettings(); updateThresholds(); @@ -256,9 +274,16 @@ void InstanceSettingsPage::applySettings() bool joinServerOnLaunch = ui->serverJoinGroupBox->isChecked(); m_settings->set("JoinServerOnLaunch", joinServerOnLaunch); if (joinServerOnLaunch) { - m_settings->set("JoinServerOnLaunchAddress", ui->serverJoinAddress->text()); + if (ui->serverJoinAddressButton->isChecked() || !m_world_quickplay_supported) { + m_settings->set("JoinServerOnLaunchAddress", ui->serverJoinAddress->text()); + m_settings->reset("JoinWorldOnLaunch"); + } else { + m_settings->set("JoinWorldOnLaunch", ui->worldsCb->currentText()); + m_settings->reset("JoinServerOnLaunchAddress"); + } } else { m_settings->reset("JoinServerOnLaunchAddress"); + m_settings->reset("JoinWorldOnLaunch"); } // Use an account for this instance @@ -379,7 +404,25 @@ void InstanceSettingsPage::loadSettings() ui->recordGameTime->setChecked(m_settings->get("RecordGameTime").toBool()); ui->serverJoinGroupBox->setChecked(m_settings->get("JoinServerOnLaunch").toBool()); - ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString()); + + if (auto server = m_settings->get("JoinServerOnLaunchAddress").toString(); !server.isEmpty()) { + ui->serverJoinAddress->setText(server); + ui->serverJoinAddressButton->setChecked(true); + ui->worldJoinButton->setChecked(false); + ui->serverJoinAddress->setEnabled(true); + ui->worldsCb->setEnabled(false); + } else if (auto world = m_settings->get("JoinWorldOnLaunch").toString(); !world.isEmpty() && m_world_quickplay_supported) { + ui->worldsCb->setCurrentText(world); + ui->serverJoinAddressButton->setChecked(false); + ui->worldJoinButton->setChecked(true); + ui->serverJoinAddress->setEnabled(false); + ui->worldsCb->setEnabled(true); + } else { + ui->serverJoinAddressButton->setChecked(true); + ui->worldJoinButton->setChecked(false); + ui->serverJoinAddress->setEnabled(true); + ui->worldsCb->setEnabled(false); + } ui->instanceAccountGroupBox->setChecked(m_settings->get("UseAccountForInstance").toBool()); updateAccountsMenu(); @@ -534,3 +577,13 @@ void InstanceSettingsPage::updateThresholds() ui->labelMaxMemIcon->setPixmap(pix); } } + +void InstanceSettingsPage::on_serverJoinAddressButton_toggled(bool checked) +{ + ui->serverJoinAddress->setEnabled(checked); +} + +void InstanceSettingsPage::on_worldJoinButton_toggled(bool checked) +{ + ui->worldsCb->setEnabled(checked); +} diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index 8b78dcb7f..6364502c9 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -70,6 +70,8 @@ class InstanceSettingsPage : public QWidget, public BasePage { void on_javaTestBtn_clicked(); void on_javaBrowseBtn_clicked(); void on_maxMemSpinBox_valueChanged(int i); + void on_serverJoinAddressButton_toggled(bool checked); + void on_worldJoinButton_toggled(bool checked); void onUseNativeGLFWChanged(bool checked); void onUseNativeOpenALChanged(bool checked); @@ -90,4 +92,5 @@ class InstanceSettingsPage : public QWidget, public BasePage { BaseInstance* m_instance; SettingsObjectPtr m_settings; unique_qobject_ptr checker; + bool m_world_quickplay_supported; }; diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 9490860ae..2f496318d 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -660,7 +660,7 @@ - Set a server to join on launch + Set a target to join on launch true @@ -668,26 +668,26 @@ false - - - - - - - - 0 - 0 - - - - Server address: - - - - - - - + + + + + Server address: + + + + + + + + + + Singleplayer world + + + + + diff --git a/launcher/ui/pages/instance/ServersPage.cpp b/launcher/ui/pages/instance/ServersPage.cpp index f842b4b93..d8035e73e 100644 --- a/launcher/ui/pages/instance/ServersPage.cpp +++ b/launcher/ui/pages/instance/ServersPage.cpp @@ -168,7 +168,7 @@ class ServersModel : public QAbstractListModel { m_saveTimer.setInterval(5000); connect(&m_saveTimer, &QTimer::timeout, this, &ServersModel::save_internal); } - virtual ~ServersModel() {}; + virtual ~ServersModel() = default; void observe() { @@ -731,7 +731,7 @@ void ServersPage::on_actionMove_Down_triggered() void ServersPage::on_actionJoin_triggered() { const auto& address = m_model->at(currentServer)->m_address; - APPLICATION->launch(m_inst, true, false, std::make_shared(MinecraftServerTarget::parse(address))); + APPLICATION->launch(m_inst, true, false, std::make_shared(MinecraftTarget::parse(address, false))); } #include "ServersPage.moc" diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 4f30e4bb7..548d1025e 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -82,7 +82,7 @@ class WorldListProxyModel : public QSortFilterProxyModel { } }; -WorldListPage::WorldListPage(BaseInstance* inst, std::shared_ptr worlds, QWidget* parent) +WorldListPage::WorldListPage(InstancePtr inst, std::shared_ptr worlds, QWidget* parent) : QMainWindow(parent), m_inst(inst), ui(new Ui::WorldListPage), m_worlds(worlds) { ui->setupUi(this); @@ -120,6 +120,8 @@ void WorldListPage::openedImpl() m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); + auto mInst = std::dynamic_pointer_cast(m_inst); + ui->toolBar->setActionVisible(ui->actionJoin, mInst && mInst->traits().contains("feature:is_quick_play_singleplayer")); } void WorldListPage::closedImpl() @@ -339,6 +341,9 @@ void WorldListPage::worldChanged([[maybe_unused]] const QModelIndex& current, [[ ui->actionDatapacks->setEnabled(enable); bool hasIcon = !index.data(WorldList::IconFileRole).isNull(); ui->actionReset_Icon->setEnabled(enable && hasIcon); + auto mInst = std::dynamic_pointer_cast(m_inst); + ui->actionJoin->setEnabled(enable); + ui->toolBar->setActionVisible(ui->actionJoin, mInst && mInst->traits().contains("feature:is_quick_play_singleplayer")); } void WorldListPage::on_actionAdd_triggered() @@ -418,4 +423,15 @@ void WorldListPage::on_actionRefresh_triggered() m_worlds->update(); } +void WorldListPage::on_actionJoin_triggered() +{ + QModelIndex index = getSelectedWorld(); + if (!index.isValid()) { + return; + } + auto worldVariant = m_worlds->data(index, WorldList::ObjectRole); + auto world = (World*)worldVariant.value(); + APPLICATION->launch(m_inst, true, false, std::make_shared(MinecraftTarget::parse(world->folderName(), true))); +} + #include "WorldListPage.moc" diff --git a/launcher/ui/pages/instance/WorldListPage.h b/launcher/ui/pages/instance/WorldListPage.h index 4f83002f4..84d9cd075 100644 --- a/launcher/ui/pages/instance/WorldListPage.h +++ b/launcher/ui/pages/instance/WorldListPage.h @@ -53,7 +53,7 @@ class WorldListPage : public QMainWindow, public BasePage { Q_OBJECT public: - explicit WorldListPage(BaseInstance* inst, std::shared_ptr worlds, QWidget* parent = 0); + explicit WorldListPage(InstancePtr inst, std::shared_ptr worlds, QWidget* parent = 0); virtual ~WorldListPage(); virtual QString displayName() const override { return tr("Worlds"); } @@ -72,7 +72,7 @@ class WorldListPage : public QMainWindow, public BasePage { QMenu* createPopupMenu() override; protected: - BaseInstance* m_inst; + InstancePtr m_inst; private: QModelIndex getSelectedWorld(); @@ -101,6 +101,7 @@ class WorldListPage : public QMainWindow, public BasePage { void on_actionReset_Icon_triggered(); void worldChanged(const QModelIndex& current, const QModelIndex& previous); void mceditState(LoggedProcess::State state); + void on_actionJoin_triggered(); void ShowContextMenu(const QPoint& pos); }; diff --git a/launcher/ui/pages/instance/WorldListPage.ui b/launcher/ui/pages/instance/WorldListPage.ui index d74dd0796..04344b453 100644 --- a/launcher/ui/pages/instance/WorldListPage.ui +++ b/launcher/ui/pages/instance/WorldListPage.ui @@ -81,6 +81,7 @@ + @@ -97,6 +98,11 @@ Add + + + Join + + Rename diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp index 46caaaef2..455b14f69 100644 --- a/launcher/ui/widgets/WideBar.cpp +++ b/launcher/ui/widgets/WideBar.cpp @@ -309,4 +309,17 @@ bool WideBar::checkHash(QByteArray const& old_hash) const return old_hash == getHash(); } +void WideBar::setActionVisible(QAction* action, bool visible) +{ + auto iter = getMatching(action); + if (iter == m_entries.end()) { + return; + } + + iter->bar_action->setVisible(visible); + + // NOTE: This is needed so that disabled actions get reflected on the button when it is made visible. + static_cast(widgetForAction(iter->bar_action))->actionChanged(); +} + #include "WideBar.moc" diff --git a/launcher/ui/widgets/WideBar.h b/launcher/ui/widgets/WideBar.h index c47f3a596..09480594a 100644 --- a/launcher/ui/widgets/WideBar.h +++ b/launcher/ui/widgets/WideBar.h @@ -38,6 +38,8 @@ class WideBar : public QToolBar { [[nodiscard]] QByteArray getVisibilityState() const; void setVisibilityState(QByteArray&&); + void setActionVisible(QAction* action, bool visible); + private: struct BarEntry { enum class Type { None, Action, Separator, Spacer } type = Type::None; diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java index de28a0401..a5f027ba6 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/AbstractLauncher.java @@ -70,7 +70,7 @@ public abstract class AbstractLauncher implements Launcher { // secondary parameters protected final int width, height; protected final boolean maximize; - protected final String serverAddress, serverPort; + protected final String serverAddress, serverPort, worldName; protected final String mainClassName; @@ -80,6 +80,7 @@ public abstract class AbstractLauncher implements Launcher { serverAddress = params.getString("serverAddress", null); serverPort = params.getString("serverPort", null); + worldName = params.getString("worldName", null); String windowParams = params.getString("windowParams", null); diff --git a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java index 49e5d518f..dc518be64 100644 --- a/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java +++ b/libraries/launcher/org/prismlauncher/launcher/impl/StandardLauncher.java @@ -62,13 +62,15 @@ import java.util.Collections; import java.util.List; public final class StandardLauncher extends AbstractLauncher { - private final boolean quickPlaySupported; + private final boolean quickPlayMultiplayerSupported; + private final boolean quickPlaySingleplayerSupported; public StandardLauncher(Parameters params) { super(params); List traits = params.getList("traits", Collections.emptyList()); - quickPlaySupported = traits.contains("feature:is_quick_play_multiplayer"); + quickPlayMultiplayerSupported = traits.contains("feature:is_quick_play_multiplayer"); + quickPlaySingleplayerSupported = traits.contains("feature:is_quick_play_singleplayer"); } @Override @@ -83,7 +85,7 @@ public final class StandardLauncher extends AbstractLauncher { } if (serverAddress != null) { - if (quickPlaySupported) { + if (quickPlayMultiplayerSupported) { // as of 23w14a gameArgs.add("--quickPlayMultiplayer"); gameArgs.add(serverAddress + ':' + serverPort); @@ -93,6 +95,9 @@ public final class StandardLauncher extends AbstractLauncher { gameArgs.add("--port"); gameArgs.add(serverPort); } + } else if (worldName != null && quickPlaySingleplayerSupported) { + gameArgs.add("--quickPlaySingleplayer"); + gameArgs.add(worldName); } // find and invoke the main method