From 2e2a5d0943a43f16c2d52a98c69323d34815cfd0 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 13:15:59 +0200 Subject: [PATCH 01/10] NOISSUE Required UI elements for setting a server --- .../pages/instance/InstanceSettingsPage.ui | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/application/pages/instance/InstanceSettingsPage.ui b/application/pages/instance/InstanceSettingsPage.ui index 64f90fad..50c3ac6b 100644 --- a/application/pages/instance/InstanceSettingsPage.ui +++ b/application/pages/instance/InstanceSettingsPage.ui @@ -39,7 +39,7 @@ QTabWidget::Rounded - 0 + 4 @@ -453,6 +453,58 @@ + + + + Set a server to join on launch + + + true + + + false + + + + + + + + + 0 + 0 + + + + Server address: + + + + + + + + + + Server port: + + + + + + + 65535 + + + 25565 + + + + + + + + From f0eb5b4a0c16b0ddd17ee73e7ecebae2def16df7 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 13:28:23 +0200 Subject: [PATCH 02/10] NOISSUE Register settings for setting a server --- api/logic/minecraft/MinecraftInstance.cpp | 5 +++++ .../pages/instance/InstanceSettingsPage.cpp | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index 2d112757..a84071c3 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -111,6 +111,11 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride); m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride); + // Join server on launch, this does not have a global override + m_settings->registerSetting("JoinServerOnLaunch", false); + m_settings->registerSetting("JoinServerOnLaunchAddress", ""); + m_settings->registerSetting("JoinServerOnLaunchPort", 25565); + // DEPRECATED: Read what versions the user configuration thinks should be used m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, ""); m_settings->registerSetting("LWJGLVersion", ""); diff --git a/application/pages/instance/InstanceSettingsPage.cpp b/application/pages/instance/InstanceSettingsPage.cpp index 05041c82..ff8659c5 100644 --- a/application/pages/instance/InstanceSettingsPage.cpp +++ b/application/pages/instance/InstanceSettingsPage.cpp @@ -191,6 +191,20 @@ void InstanceSettingsPage::applySettings() m_settings->reset("ShowGameTime"); m_settings->reset("RecordGameTime"); } + + // Join server on launch + bool joinServerOnLaunch = ui->serverJoinGroupBox->isChecked(); + m_settings->set("JoinServerOnLaunch", joinServerOnLaunch); + if (joinServerOnLaunch) + { + m_settings->set("JoinServerOnLaunchAddress", ui->serverJoinAddress->text()); + m_settings->set("JoinServerOnLaunchPort", ui->serverJoinPort->value()); + } + else + { + m_settings->reset("JoinServerOnLaunchAddress"); + m_settings->reset("JoinServerOnLaunchPort"); + } } void InstanceSettingsPage::loadSettings() @@ -257,6 +271,10 @@ void InstanceSettingsPage::loadSettings() ui->gameTimeGroupBox->setChecked(m_settings->get("OverrideGameTime").toBool()); ui->showGameTime->setChecked(m_settings->get("ShowGameTime").toBool()); ui->recordGameTime->setChecked(m_settings->get("RecordGameTime").toBool()); + + ui->serverJoinGroupBox->setChecked(m_settings->get("JoinServerOnLaunch").toBool()); + ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString()); + ui->serverJoinPort->setValue(m_settings->get("JoinServerOnLaunchPort").toInt()); } void InstanceSettingsPage::on_javaDetectBtn_clicked() From cc6cd0648a0fcda419bb5469d6322ed86d49c03e Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 13:54:34 +0200 Subject: [PATCH 03/10] NOISSUE Add server launch arguments --- api/logic/minecraft/MinecraftInstance.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index a84071c3..4dd70ed1 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -409,6 +409,12 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) cons args_pattern += " --tweakClass " + tweaker; } + if (m_settings->get("JoinServerOnLaunch").toBool()) + { + args_pattern += " --server " + m_settings->get("JoinServerOnLaunchAddress").toString(); + args_pattern += " --port " + m_settings->get("JoinServerOnLaunchPort").toString(); + } + QMap token_mapping; // yggdrasil! if(session) From 23a706bbaebcc8ec9126d019a276c158581bdb53 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 16:14:25 +0200 Subject: [PATCH 04/10] NOISSUE Resolve minecraft server using DNS SRV --- api/logic/BaseInstance.h | 6 +- api/logic/CMakeLists.txt | 3 + api/logic/NullInstance.h | 2 +- .../launch/steps/LookupServerAddress.cpp | 95 +++++++++++++++++++ api/logic/launch/steps/LookupServerAddress.h | 51 ++++++++++ api/logic/minecraft/MinecraftInstance.cpp | 34 +++++-- api/logic/minecraft/MinecraftInstance.h | 7 +- .../minecraft/launch/DirectJavaLaunch.cpp | 2 +- api/logic/minecraft/launch/DirectJavaLaunch.h | 9 ++ .../minecraft/launch/LauncherPartLaunch.cpp | 2 +- .../minecraft/launch/LauncherPartLaunch.h | 9 ++ .../minecraft/launch/MinecraftServerTarget.h | 25 +++++ .../minecraft/launch/PrintInstanceInfo.cpp | 2 +- .../minecraft/launch/PrintInstanceInfo.h | 5 +- api/logic/minecraft/legacy/LegacyInstance.cpp | 2 +- api/logic/minecraft/legacy/LegacyInstance.h | 2 +- 16 files changed, 235 insertions(+), 21 deletions(-) create mode 100644 api/logic/launch/steps/LookupServerAddress.cpp create mode 100644 api/logic/launch/steps/LookupServerAddress.h create mode 100644 api/logic/minecraft/launch/MinecraftServerTarget.h diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index bbeabb41..7ea4e045 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -34,6 +34,8 @@ #include "multimc_logic_export.h" +#include "minecraft/launch/MinecraftServerTarget.h" + class QDir; class Task; class LaunchTask; @@ -221,9 +223,9 @@ public: bool reloadSettings(); /** - * 'print' a verbose desription of the instance into a QStringList + * 'print' a verbose description of the instance into a QStringList */ - virtual QStringList verboseDescription(AuthSessionPtr session) = 0; + virtual QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) = 0; Status currentStatus() const; diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 91155ea7..52821a61 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -124,6 +124,8 @@ set(NET_SOURCES # Game launch logic set(LAUNCH_SOURCES + launch/steps/LookupServerAddress.cpp + launch/steps/LookupServerAddress.h launch/steps/PostLaunchCommand.cpp launch/steps/PostLaunchCommand.h launch/steps/PreLaunchCommand.cpp @@ -236,6 +238,7 @@ set(MINECRAFT_SOURCES minecraft/launch/ExtractNatives.h minecraft/launch/LauncherPartLaunch.cpp minecraft/launch/LauncherPartLaunch.h + minecraft/launch/MinecraftServerTarget.h minecraft/launch/PrintInstanceInfo.cpp minecraft/launch/PrintInstanceInfo.h minecraft/launch/ReconstructAssets.cpp diff --git a/api/logic/NullInstance.h b/api/logic/NullInstance.h index e9ba1a13..ad39c179 100644 --- a/api/logic/NullInstance.h +++ b/api/logic/NullInstance.h @@ -67,7 +67,7 @@ public: { return false; } - QStringList verboseDescription(AuthSessionPtr session) override + QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override { QStringList out; out << "Null instance - placeholder."; diff --git a/api/logic/launch/steps/LookupServerAddress.cpp b/api/logic/launch/steps/LookupServerAddress.cpp new file mode 100644 index 00000000..c8e2a20c --- /dev/null +++ b/api/logic/launch/steps/LookupServerAddress.cpp @@ -0,0 +1,95 @@ +#include "LookupServerAddress.h" + +#include + +LookupServerAddress::LookupServerAddress(LaunchTask *parent) : + LaunchStep(parent), m_dnsLookup(new QDnsLookup(this)) +{ + connect(m_dnsLookup, &QDnsLookup::finished, this, &LookupServerAddress::on_dnsLookupFinished); + + m_dnsLookup->setType(QDnsLookup::SRV); +} + +void LookupServerAddress::setLookupAddress(const QString &lookupAddress) +{ + m_lookupAddress = lookupAddress; + m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress)); +} + +void LookupServerAddress::setPort(quint16 port) +{ + m_port = port; +} + +void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output) +{ + m_output = std::move(output); +} + +bool LookupServerAddress::abort() +{ + m_dnsLookup->abort(); + emitFailed("Aborted"); + return true; +} + +void LookupServerAddress::executeTask() +{ + m_dnsLookup->lookup(); +} + +void LookupServerAddress::on_dnsLookupFinished() +{ + if (isFinished()) + { + // Aborted + return; + } + + if (m_dnsLookup->error() != QDnsLookup::NoError) + { + emit logLine(QString("Failed to resolve server address (this is NOT an error!) %1: %2\n") + .arg(m_dnsLookup->name(), m_dnsLookup->errorString()), MessageLevel::MultiMC); + resolve(m_lookupAddress, m_port); // Technically the task failed, however, we don't abort the launch + // and leave it up to minecraft to fail (or maybe not) when connecting + return; + } + + const auto records = m_dnsLookup->serviceRecords(); + if (records.empty()) + { + emit logLine( + QString("Failed to resolve server address %1: the DNS lookup succeeded, but no records were returned.\n") + .arg(m_dnsLookup->name()), MessageLevel::Warning); + resolve(m_lookupAddress, m_port); // Technically the task failed, however, we don't abort the launch + // and leave it up to minecraft to fail (or maybe not) when connecting + return; + } + + const auto &firstRecord = records.at(0); + + if (firstRecord.port() != m_port && m_port != 0) + { + emit logLine( + QString("DNS record for %1 suggested %2 as server port, but user supplied %3. Using user override," + " but the port may be wrong!\n").arg(m_dnsLookup->name(), QString::number(firstRecord.port()), QString::number(m_port)), + MessageLevel::Warning); + } + else if (m_port == 0) + { + m_port = firstRecord.port(); + } + + emit logLine(QString("Resolved server address %1 to %2 with port %3\n").arg( + m_dnsLookup->name(), firstRecord.target(), QString::number(m_port)),MessageLevel::MultiMC); + resolve(firstRecord.target(), m_port); +} + +void LookupServerAddress::resolve(const QString &address, quint16 port) +{ + m_output->address = address; + m_output->port = port; + + emitSucceeded(); + m_dnsLookup->deleteLater(); +} diff --git a/api/logic/launch/steps/LookupServerAddress.h b/api/logic/launch/steps/LookupServerAddress.h new file mode 100644 index 00000000..1f70e97b --- /dev/null +++ b/api/logic/launch/steps/LookupServerAddress.h @@ -0,0 +1,51 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include "minecraft/launch/MinecraftServerTarget.h" + +class LookupServerAddress: public LaunchStep { +Q_OBJECT +public: + explicit LookupServerAddress(LaunchTask *parent); + virtual ~LookupServerAddress() {}; + + virtual void executeTask(); + virtual bool abort(); + virtual bool canAbort() const + { + return true; + } + + void setLookupAddress(const QString &lookupAddress); + void setPort(quint16 port); + void setOutputAddressPtr(MinecraftServerTargetPtr output); + +private slots: + void on_dnsLookupFinished(); + +private: + void resolve(const QString &address, quint16 port); + + QDnsLookup *m_dnsLookup; + QString m_lookupAddress; + quint16 m_port; + MinecraftServerTargetPtr m_output; +}; diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index 4dd70ed1..e426d5c4 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -12,6 +12,7 @@ #include #include "launch/LaunchTask.h" +#include "launch/steps/LookupServerAddress.h" #include "launch/steps/PostLaunchCommand.h" #include "launch/steps/Update.h" #include "launch/steps/PreLaunchCommand.h" @@ -400,7 +401,8 @@ static QString replaceTokensIn(QString text, QMap with) return result; } -QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) const +QStringList MinecraftInstance::processMinecraftArgs( + AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const { auto profile = m_components->getProfile(); QString args_pattern = profile->getMinecraftArguments(); @@ -409,10 +411,10 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) cons args_pattern += " --tweakClass " + tweaker; } - if (m_settings->get("JoinServerOnLaunch").toBool()) + if (serverToJoin && !serverToJoin->address.isEmpty()) { - args_pattern += " --server " + m_settings->get("JoinServerOnLaunchAddress").toString(); - args_pattern += " --port " + m_settings->get("JoinServerOnLaunchPort").toString(); + args_pattern += " --server " + serverToJoin->address; + args_pattern += " --port " + QString::number(serverToJoin->port); } QMap token_mapping; @@ -451,7 +453,7 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) cons return parts; } -QString MinecraftInstance::createLaunchScript(AuthSessionPtr session) +QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) { QString launchScript; @@ -473,7 +475,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session) } // generic minecraft params - for (auto param : processMinecraftArgs(session)) + for (auto param : processMinecraftArgs(session, serverToJoin)) { launchScript += "param " + param + "\n"; } @@ -523,7 +525,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session) return launchScript; } -QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session) +QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) { QStringList out; out << "Main Class:" << " " + getMainClass() << ""; @@ -638,7 +640,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session) out << ""; } - auto params = processMinecraftArgs(nullptr); + auto params = processMinecraftArgs(nullptr, serverToJoin); out << "Params:"; out << " " + params.join(' '); out << ""; @@ -844,6 +846,18 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(new CreateGameFolders(pptr)); } + MinecraftServerTargetPtr serverToJoin = std::make_shared(); + + if (m_settings->get("JoinServerOnLaunch").toBool()) + { + // Resolve server address to join on launch + auto *step = new LookupServerAddress(pptr); + step->setLookupAddress(m_settings->get("JoinServerOnLaunchAddress").toString()); + step->setPort(m_settings->get("JoinServerOnLaunchPort").toInt()); + step->setOutputAddressPtr(serverToJoin); + process->appendStep(step); + } + // run pre-launch command if that's needed if(getPreLaunchCommand().size()) { @@ -875,7 +889,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt // print some instance info here... { - process->appendStep(new PrintInstanceInfo(pptr, session)); + process->appendStep(new PrintInstanceInfo(pptr, session, serverToJoin)); } // extract native jars if needed @@ -896,6 +910,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt auto step = new LauncherPartLaunch(pptr); step->setWorkingDirectory(gameRoot()); step->setAuthSession(session); + step->setServerToJoin(serverToJoin); process->appendStep(step); } else if (method == "DirectJava") @@ -903,6 +918,7 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt auto step = new DirectJavaLaunch(pptr); step->setWorkingDirectory(gameRoot()); step->setAuthSession(session); + step->setServerToJoin(serverToJoin); process->appendStep(step); } } diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h index 985a8a76..eb1a9005 100644 --- a/api/logic/minecraft/MinecraftInstance.h +++ b/api/logic/minecraft/MinecraftInstance.h @@ -5,6 +5,7 @@ #include #include #include "multimc_logic_export.h" +#include "minecraft/launch/MinecraftServerTarget.h" class ModFolderModel; class WorldList; @@ -78,9 +79,9 @@ public: shared_qobject_ptr createUpdateTask(Net::Mode mode) override; shared_qobject_ptr createLaunchTask(AuthSessionPtr account) override; QStringList extraArguments() const override; - QStringList verboseDescription(AuthSessionPtr session) override; + QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override; QList getJarMods() const; - QString createLaunchScript(AuthSessionPtr session); + QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin); /// get arguments passed to java QStringList javaArguments() const; @@ -107,7 +108,7 @@ public: virtual QString getMainClass() const; // FIXME: remove - virtual QStringList processMinecraftArgs(AuthSessionPtr account) const; + virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const; virtual JavaVersion getJavaVersion() const; diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.cpp b/api/logic/minecraft/launch/DirectJavaLaunch.cpp index d9841a43..cf4564b6 100644 --- a/api/logic/minecraft/launch/DirectJavaLaunch.cpp +++ b/api/logic/minecraft/launch/DirectJavaLaunch.cpp @@ -55,7 +55,7 @@ void DirectJavaLaunch::executeTask() // make detachable - this will keep the process running even if the object is destroyed m_process.setDetachable(true); - auto mcArgs = minecraftInstance->processMinecraftArgs(m_session); + auto mcArgs = minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin); args.append(mcArgs); QString wrapperCommandStr = instance->getWrapperCommand().trimmed(); diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.h b/api/logic/minecraft/launch/DirectJavaLaunch.h index d9d9bdc2..58b119b8 100644 --- a/api/logic/minecraft/launch/DirectJavaLaunch.h +++ b/api/logic/minecraft/launch/DirectJavaLaunch.h @@ -19,6 +19,8 @@ #include #include +#include "MinecraftServerTarget.h" + class DirectJavaLaunch: public LaunchStep { Q_OBJECT @@ -38,6 +40,12 @@ public: { m_session = session; } + + void setServerToJoin(MinecraftServerTargetPtr serverToJoin) + { + m_serverToJoin = std::move(serverToJoin); + } + private slots: void on_state(LoggedProcess::State state); @@ -45,5 +53,6 @@ private: LoggedProcess m_process; QString m_command; AuthSessionPtr m_session; + MinecraftServerTargetPtr m_serverToJoin; }; diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.cpp b/api/logic/minecraft/launch/LauncherPartLaunch.cpp index 1408e6ad..ab3b6d10 100644 --- a/api/logic/minecraft/launch/LauncherPartLaunch.cpp +++ b/api/logic/minecraft/launch/LauncherPartLaunch.cpp @@ -59,7 +59,7 @@ void LauncherPartLaunch::executeTask() auto instance = m_parent->instance(); std::shared_ptr minecraftInstance = std::dynamic_pointer_cast(instance); - m_launchScript = minecraftInstance->createLaunchScript(m_session); + m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin); QStringList args = minecraftInstance->javaArguments(); QString allArgs = args.join(", "); emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC); diff --git a/api/logic/minecraft/launch/LauncherPartLaunch.h b/api/logic/minecraft/launch/LauncherPartLaunch.h index 9e951849..6a7ee0e5 100644 --- a/api/logic/minecraft/launch/LauncherPartLaunch.h +++ b/api/logic/minecraft/launch/LauncherPartLaunch.h @@ -19,6 +19,8 @@ #include #include +#include "MinecraftServerTarget.h" + class LauncherPartLaunch: public LaunchStep { Q_OBJECT @@ -39,6 +41,11 @@ public: m_session = session; } + void setServerToJoin(MinecraftServerTargetPtr serverToJoin) + { + m_serverToJoin = std::move(serverToJoin); + } + private slots: void on_state(LoggedProcess::State state); @@ -47,5 +54,7 @@ private: QString m_command; AuthSessionPtr m_session; QString m_launchScript; + MinecraftServerTargetPtr m_serverToJoin; + bool mayProceed = false; }; diff --git a/api/logic/minecraft/launch/MinecraftServerTarget.h b/api/logic/minecraft/launch/MinecraftServerTarget.h new file mode 100644 index 00000000..f9b407e0 --- /dev/null +++ b/api/logic/minecraft/launch/MinecraftServerTarget.h @@ -0,0 +1,25 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +struct MinecraftServerTarget { + QString address; + quint16 port; +}; + +typedef std::shared_ptr MinecraftServerTargetPtr; diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.cpp b/api/logic/minecraft/launch/PrintInstanceInfo.cpp index af0b5bbf..0b9611ad 100644 --- a/api/logic/minecraft/launch/PrintInstanceInfo.cpp +++ b/api/logic/minecraft/launch/PrintInstanceInfo.cpp @@ -101,6 +101,6 @@ void PrintInstanceInfo::executeTask() #endif logLines(log, MessageLevel::MultiMC); - logLines(instance->verboseDescription(m_session), MessageLevel::MultiMC); + logLines(instance->verboseDescription(m_session, m_serverToJoin), MessageLevel::MultiMC); emitSucceeded(); } diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.h b/api/logic/minecraft/launch/PrintInstanceInfo.h index 168eff9d..fdc30f31 100644 --- a/api/logic/minecraft/launch/PrintInstanceInfo.h +++ b/api/logic/minecraft/launch/PrintInstanceInfo.h @@ -18,13 +18,15 @@ #include #include #include "minecraft/auth/AuthSession.h" +#include "minecraft/launch/MinecraftServerTarget.h" // FIXME: temporary wrapper for existing task. class PrintInstanceInfo: public LaunchStep { Q_OBJECT public: - explicit PrintInstanceInfo(LaunchTask *parent, AuthSessionPtr session) : LaunchStep(parent), m_session(session) {}; + explicit PrintInstanceInfo(LaunchTask *parent, AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) : + LaunchStep(parent), m_session(session), m_serverToJoin(serverToJoin) {}; virtual ~PrintInstanceInfo(){}; virtual void executeTask(); @@ -34,5 +36,6 @@ public: } private: AuthSessionPtr m_session; + MinecraftServerTargetPtr m_serverToJoin; }; diff --git a/api/logic/minecraft/legacy/LegacyInstance.cpp b/api/logic/minecraft/legacy/LegacyInstance.cpp index f86e5d05..9f9bda5a 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.cpp +++ b/api/logic/minecraft/legacy/LegacyInstance.cpp @@ -225,7 +225,7 @@ QString LegacyInstance::getStatusbarDescription() return tr("Instance from previous versions."); } -QStringList LegacyInstance::verboseDescription(AuthSessionPtr session) +QStringList LegacyInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) { QStringList out; diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h index 9caddcba..7450fdda 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.h +++ b/api/logic/minecraft/legacy/LegacyInstance.h @@ -125,7 +125,7 @@ public: } QString getStatusbarDescription() override; - QStringList verboseDescription(AuthSessionPtr session) override; + QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override; QProcessEnvironment createEnvironment() override { From 0ccd7223fd554f86272dbffddd2e7818c26ffa53 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 16:32:15 +0200 Subject: [PATCH 05/10] NOISSUE Make LauncherPart aware of server to join --- api/logic/minecraft/MinecraftInstance.cpp | 11 ++++++++++- .../launcher/org/multimc/LegacyFrame.java | 18 +++++++++++++++++- .../org/multimc/onesix/OneSixLauncher.java | 16 +++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index e426d5c4..54c7f594 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -474,8 +474,17 @@ 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"; + } + // generic minecraft params - for (auto param : processMinecraftArgs(session, serverToJoin)) + for (auto param : processMinecraftArgs( + session, + nullptr /* When using a launch script, the server parameters are handled by it*/ + )) { launchScript += "param " + param + "\n"; } diff --git a/libraries/launcher/org/multimc/LegacyFrame.java b/libraries/launcher/org/multimc/LegacyFrame.java index c72c053e..985a10e6 100644 --- a/libraries/launcher/org/multimc/LegacyFrame.java +++ b/libraries/launcher/org/multimc/LegacyFrame.java @@ -46,7 +46,16 @@ public class LegacyFrame extends Frame implements WindowListener this.addWindowListener ( this ); } - public void start ( Applet mcApplet, String user, String session, int winSizeW, int winSizeH, boolean maximize ) + public void start ( + Applet mcApplet, + String user, + String session, + int winSizeW, + int winSizeH, + boolean maximize, + String serverAddress, + String serverPort + ) { try { appletWrap = new Launcher( mcApplet, new URL ( "http://www.minecraft.net/game" ) ); @@ -95,6 +104,13 @@ public class LegacyFrame extends Frame implements WindowListener e.printStackTrace(System.err); System.exit(-1); } + + if (serverAddress != null) + { + appletWrap.setParameter("server", serverAddress); + appletWrap.setParameter("port", serverPort); + } + appletWrap.setParameter ( "username", user ); appletWrap.setParameter ( "sessionid", session ); appletWrap.setParameter ( "stand-alone", "true" ); // Show the quit button. diff --git a/libraries/launcher/org/multimc/onesix/OneSixLauncher.java b/libraries/launcher/org/multimc/onesix/OneSixLauncher.java index b6b384ab..ea445995 100644 --- a/libraries/launcher/org/multimc/onesix/OneSixLauncher.java +++ b/libraries/launcher/org/multimc/onesix/OneSixLauncher.java @@ -47,6 +47,9 @@ public class OneSixLauncher implements Launcher private boolean maximize; private String cwd; + private String serverAddress; + private String serverPort; + // the much abused system classloader, for convenience (for further abuse) private ClassLoader cl; @@ -64,6 +67,9 @@ public class OneSixLauncher implements Launcher windowTitle = params.firstSafe("windowTitle", "Minecraft"); windowParams = params.firstSafe("windowParams", "854x480"); + serverAddress = params.firstSafe("serverAddress", null); + serverPort = params.firstSafe("serverPort", null); + cwd = System.getProperty("user.dir"); winSizeW = 854; @@ -122,7 +128,7 @@ public class OneSixLauncher implements Launcher Class MCAppletClass = cl.loadClass(appletClass); Applet mcappl = (Applet) MCAppletClass.newInstance(); LegacyFrame mcWindow = new LegacyFrame(windowTitle); - mcWindow.start(mcappl, userName, sessionId, winSizeW, winSizeH, maximize); + mcWindow.start(mcappl, userName, sessionId, winSizeW, winSizeH, maximize, serverAddress, serverPort); return 0; } catch (Exception e) { @@ -164,6 +170,14 @@ public class OneSixLauncher implements Launcher mcparams.add(Integer.toString(winSizeH)); } + if (serverAddress != null) + { + mcparams.add("--server"); + mcparams.add(serverAddress); + mcparams.add("--port"); + mcparams.add(serverPort); + } + // Get the Minecraft Class. Class mc; try From d97f13b4aacd00b7157735702fa4484317640a4f Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 17:00:14 +0200 Subject: [PATCH 06/10] NOISSUE Use Vanilla logic for resolving servers --- .../launch/steps/LookupServerAddress.cpp | 26 ++++--------------- api/logic/launch/steps/LookupServerAddress.h | 2 -- api/logic/minecraft/MinecraftInstance.cpp | 20 +++++++++----- .../pages/instance/InstanceSettingsPage.ui | 2 +- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/api/logic/launch/steps/LookupServerAddress.cpp b/api/logic/launch/steps/LookupServerAddress.cpp index c8e2a20c..c6ca5bd5 100644 --- a/api/logic/launch/steps/LookupServerAddress.cpp +++ b/api/logic/launch/steps/LookupServerAddress.cpp @@ -16,11 +16,6 @@ void LookupServerAddress::setLookupAddress(const QString &lookupAddress) m_dnsLookup->setName(QString("_minecraft._tcp.%1").arg(lookupAddress)); } -void LookupServerAddress::setPort(quint16 port) -{ - m_port = port; -} - void LookupServerAddress::setOutputAddressPtr(MinecraftServerTargetPtr output) { m_output = std::move(output); @@ -50,7 +45,7 @@ void LookupServerAddress::on_dnsLookupFinished() { emit logLine(QString("Failed to resolve server address (this is NOT an error!) %1: %2\n") .arg(m_dnsLookup->name(), m_dnsLookup->errorString()), MessageLevel::MultiMC); - resolve(m_lookupAddress, m_port); // Technically the task failed, however, we don't abort the launch + resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch // and leave it up to minecraft to fail (or maybe not) when connecting return; } @@ -61,28 +56,17 @@ void LookupServerAddress::on_dnsLookupFinished() emit logLine( QString("Failed to resolve server address %1: the DNS lookup succeeded, but no records were returned.\n") .arg(m_dnsLookup->name()), MessageLevel::Warning); - resolve(m_lookupAddress, m_port); // Technically the task failed, however, we don't abort the launch + resolve(m_lookupAddress, 25565); // Technically the task failed, however, we don't abort the launch // and leave it up to minecraft to fail (or maybe not) when connecting return; } const auto &firstRecord = records.at(0); - - if (firstRecord.port() != m_port && m_port != 0) - { - emit logLine( - QString("DNS record for %1 suggested %2 as server port, but user supplied %3. Using user override," - " but the port may be wrong!\n").arg(m_dnsLookup->name(), QString::number(firstRecord.port()), QString::number(m_port)), - MessageLevel::Warning); - } - else if (m_port == 0) - { - m_port = firstRecord.port(); - } + quint16 port = firstRecord.port(); emit logLine(QString("Resolved server address %1 to %2 with port %3\n").arg( - m_dnsLookup->name(), firstRecord.target(), QString::number(m_port)),MessageLevel::MultiMC); - resolve(firstRecord.target(), m_port); + m_dnsLookup->name(), firstRecord.target(), QString::number(port)),MessageLevel::MultiMC); + resolve(firstRecord.target(), port); } void LookupServerAddress::resolve(const QString &address, quint16 port) diff --git a/api/logic/launch/steps/LookupServerAddress.h b/api/logic/launch/steps/LookupServerAddress.h index 1f70e97b..5a5c3de1 100644 --- a/api/logic/launch/steps/LookupServerAddress.h +++ b/api/logic/launch/steps/LookupServerAddress.h @@ -35,7 +35,6 @@ public: } void setLookupAddress(const QString &lookupAddress); - void setPort(quint16 port); void setOutputAddressPtr(MinecraftServerTargetPtr output); private slots: @@ -46,6 +45,5 @@ private: QDnsLookup *m_dnsLookup; QString m_lookupAddress; - quint16 m_port; MinecraftServerTargetPtr m_output; }; diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index 54c7f594..abb360f2 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -859,12 +859,20 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt if (m_settings->get("JoinServerOnLaunch").toBool()) { - // Resolve server address to join on launch - auto *step = new LookupServerAddress(pptr); - step->setLookupAddress(m_settings->get("JoinServerOnLaunchAddress").toString()); - step->setPort(m_settings->get("JoinServerOnLaunchPort").toInt()); - step->setOutputAddressPtr(serverToJoin); - process->appendStep(step); + quint16 port = m_settings->get("JoinServerOnLaunchPort").toInt(); + QString address = m_settings->get("JoinServerOnLaunchAddress").toString(); + + serverToJoin->port = port; + serverToJoin->address = address; + + if(port == 25565) + { + // Resolve server address to join on launch + auto *step = new LookupServerAddress(pptr); + step->setLookupAddress(address); + step->setOutputAddressPtr(serverToJoin); + process->appendStep(step); + } } // run pre-launch command if that's needed diff --git a/application/pages/instance/InstanceSettingsPage.ui b/application/pages/instance/InstanceSettingsPage.ui index 50c3ac6b..eb8ed13a 100644 --- a/application/pages/instance/InstanceSettingsPage.ui +++ b/application/pages/instance/InstanceSettingsPage.ui @@ -39,7 +39,7 @@ QTabWidget::Rounded - 4 + 0 From f33fe05e5febf76d8e11ccd44073d99e00946d71 Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 17:24:37 +0200 Subject: [PATCH 07/10] NOISSUE Use minecraft logic for parsing adresses --- api/logic/minecraft/MinecraftInstance.cpp | 55 ++++++++++++++++--- .../pages/instance/InstanceSettingsPage.cpp | 3 - .../pages/instance/InstanceSettingsPage.ui | 19 +------ 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index abb360f2..00ac7964 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -115,7 +115,6 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO // Join server on launch, this does not have a global override m_settings->registerSetting("JoinServerOnLaunch", false); m_settings->registerSetting("JoinServerOnLaunchAddress", ""); - m_settings->registerSetting("JoinServerOnLaunchPort", 25565); // DEPRECATED: Read what versions the user configuration thinks should be used m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, ""); @@ -859,17 +858,59 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt if (m_settings->get("JoinServerOnLaunch").toBool()) { - quint16 port = m_settings->get("JoinServerOnLaunchPort").toInt(); - QString address = m_settings->get("JoinServerOnLaunchAddress").toString(); + QString fullAddress = m_settings->get("JoinServerOnLaunchAddress").toString(); + QStringList split = fullAddress.split(":"); - serverToJoin->port = port; - serverToJoin->address = address; + // The logic below replicates the exact logic minecraft uses for parsing server addresses. + // While the conversion is not lossless and eats errors, it ensures the same behavior + // within Minecraft and MultiMC when entering server addresses. + if (fullAddress.startsWith("[")) + { + int bracket = fullAddress.indexOf("]"); + if (bracket > 0) + { + QString ipv6 = fullAddress.mid(1, bracket - 1); + QString port = fullAddress.mid(bracket + 1).trimmed(); - if(port == 25565) + if (port.startsWith(":") && !ipv6.isEmpty()) + { + port = port.mid(1); + split = QStringList({ ipv6, port }); + } + else + { + split = QStringList({ipv6}); + } + } + } + + if (split.size() > 2) + { + split = QStringList({fullAddress}); + } + + QString realAddress = split[0]; + + quint16 realPort = 25565; + if (split.size() > 1) + { + bool ok; + realPort = split[1].toUInt(&ok); + + if (!ok) + { + realPort = 25565; + } + } + + serverToJoin->port = realPort; + serverToJoin->address = realAddress; + + if(realPort == 25565) { // Resolve server address to join on launch auto *step = new LookupServerAddress(pptr); - step->setLookupAddress(address); + step->setLookupAddress(realAddress); step->setOutputAddressPtr(serverToJoin); process->appendStep(step); } diff --git a/application/pages/instance/InstanceSettingsPage.cpp b/application/pages/instance/InstanceSettingsPage.cpp index ff8659c5..00fc19af 100644 --- a/application/pages/instance/InstanceSettingsPage.cpp +++ b/application/pages/instance/InstanceSettingsPage.cpp @@ -198,12 +198,10 @@ void InstanceSettingsPage::applySettings() if (joinServerOnLaunch) { m_settings->set("JoinServerOnLaunchAddress", ui->serverJoinAddress->text()); - m_settings->set("JoinServerOnLaunchPort", ui->serverJoinPort->value()); } else { m_settings->reset("JoinServerOnLaunchAddress"); - m_settings->reset("JoinServerOnLaunchPort"); } } @@ -274,7 +272,6 @@ void InstanceSettingsPage::loadSettings() ui->serverJoinGroupBox->setChecked(m_settings->get("JoinServerOnLaunch").toBool()); ui->serverJoinAddress->setText(m_settings->get("JoinServerOnLaunchAddress").toString()); - ui->serverJoinPort->setValue(m_settings->get("JoinServerOnLaunchPort").toInt()); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/application/pages/instance/InstanceSettingsPage.ui b/application/pages/instance/InstanceSettingsPage.ui index eb8ed13a..de9fa9e3 100644 --- a/application/pages/instance/InstanceSettingsPage.ui +++ b/application/pages/instance/InstanceSettingsPage.ui @@ -39,7 +39,7 @@ QTabWidget::Rounded - 0 + 4 @@ -483,23 +483,6 @@ - - - - Server port: - - - - - - - 65535 - - - 25565 - - - From ea6c42a93c95e3e62d7ac984ff2cc4f461dc43dd Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 18:07:08 +0200 Subject: [PATCH 08/10] NOISSUE Allow joining servers from the servers page --- api/logic/BaseInstance.h | 3 +- api/logic/CMakeLists.txt | 1 + api/logic/NullInstance.h | 2 +- api/logic/minecraft/MinecraftInstance.cpp | 69 +++---------------- api/logic/minecraft/MinecraftInstance.h | 2 +- .../launch/MinecraftServerTarget.cpp | 66 ++++++++++++++++++ .../minecraft/launch/MinecraftServerTarget.h | 5 ++ api/logic/minecraft/legacy/LegacyInstance.h | 3 +- application/InstancePageProvider.h | 2 +- application/LaunchController.cpp | 2 +- application/LaunchController.h | 7 ++ application/MultiMC.cpp | 9 ++- application/MultiMC.h | 9 ++- .../pages/instance/InstanceSettingsPage.ui | 2 +- application/pages/instance/ServersPage.cpp | 11 ++- application/pages/instance/ServersPage.h | 5 +- application/pages/instance/ServersPage.ui | 6 ++ 17 files changed, 132 insertions(+), 72 deletions(-) create mode 100644 api/logic/minecraft/launch/MinecraftServerTarget.cpp diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index 7ea4e045..64de4bb3 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -147,7 +147,8 @@ public: virtual shared_qobject_ptr createUpdateTask(Net::Mode mode) = 0; /// returns a valid launcher (task container) - virtual shared_qobject_ptr createLaunchTask(AuthSessionPtr account) = 0; + virtual shared_qobject_ptr createLaunchTask( + AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0; /// returns the current launch task (if any) shared_qobject_ptr getLaunchTask(); diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 52821a61..c3322955 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -238,6 +238,7 @@ set(MINECRAFT_SOURCES minecraft/launch/ExtractNatives.h minecraft/launch/LauncherPartLaunch.cpp minecraft/launch/LauncherPartLaunch.h + minecraft/launch/MinecraftServerTarget.cpp minecraft/launch/MinecraftServerTarget.h minecraft/launch/PrintInstanceInfo.cpp minecraft/launch/PrintInstanceInfo.h diff --git a/api/logic/NullInstance.h b/api/logic/NullInstance.h index ad39c179..94ed6c3a 100644 --- a/api/logic/NullInstance.h +++ b/api/logic/NullInstance.h @@ -27,7 +27,7 @@ public: { return instanceRoot(); }; - shared_qobject_ptr createLaunchTask(AuthSessionPtr) override + shared_qobject_ptr createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; } diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index 00ac7964..a1341e69 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -822,7 +822,7 @@ shared_qobject_ptr MinecraftInstance::createUpdateTask(Net::Mode mode) return nullptr; } -shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr session) +shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) { // FIXME: get rid of shared_from_this ... auto process = LaunchTask::create(std::dynamic_pointer_cast(shared_from_this())); @@ -854,66 +854,19 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(new CreateGameFolders(pptr)); } - MinecraftServerTargetPtr serverToJoin = std::make_shared(); - - if (m_settings->get("JoinServerOnLaunch").toBool()) + if (!serverToJoin && m_settings->get("JoinServerOnLaunch").toBool()) { QString fullAddress = m_settings->get("JoinServerOnLaunchAddress").toString(); - QStringList split = fullAddress.split(":"); + serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress))); + } - // The logic below replicates the exact logic minecraft uses for parsing server addresses. - // While the conversion is not lossless and eats errors, it ensures the same behavior - // within Minecraft and MultiMC when entering server addresses. - if (fullAddress.startsWith("[")) - { - int bracket = fullAddress.indexOf("]"); - if (bracket > 0) - { - QString ipv6 = fullAddress.mid(1, bracket - 1); - QString port = fullAddress.mid(bracket + 1).trimmed(); - - if (port.startsWith(":") && !ipv6.isEmpty()) - { - port = port.mid(1); - split = QStringList({ ipv6, port }); - } - else - { - split = QStringList({ipv6}); - } - } - } - - if (split.size() > 2) - { - split = QStringList({fullAddress}); - } - - QString realAddress = split[0]; - - quint16 realPort = 25565; - if (split.size() > 1) - { - bool ok; - realPort = split[1].toUInt(&ok); - - if (!ok) - { - realPort = 25565; - } - } - - serverToJoin->port = realPort; - serverToJoin->address = realAddress; - - if(realPort == 25565) - { - // Resolve server address to join on launch - auto *step = new LookupServerAddress(pptr); - step->setLookupAddress(realAddress); - step->setOutputAddressPtr(serverToJoin); - process->appendStep(step); - } + if(serverToJoin && serverToJoin->port == 25565) + { + // Resolve server address to join on launch + auto *step = new LookupServerAddress(pptr); + step->setLookupAddress(serverToJoin->address); + step->setOutputAddressPtr(serverToJoin); + process->appendStep(step); } // run pre-launch command if that's needed diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h index eb1a9005..05600797 100644 --- a/api/logic/minecraft/MinecraftInstance.h +++ b/api/logic/minecraft/MinecraftInstance.h @@ -77,7 +77,7 @@ public: ////// Launch stuff ////// shared_qobject_ptr createUpdateTask(Net::Mode mode) override; - shared_qobject_ptr createLaunchTask(AuthSessionPtr account) override; + shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override; QStringList extraArguments() const override; QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override; QList getJarMods() const; diff --git a/api/logic/minecraft/launch/MinecraftServerTarget.cpp b/api/logic/minecraft/launch/MinecraftServerTarget.cpp new file mode 100644 index 00000000..569273b6 --- /dev/null +++ b/api/logic/minecraft/launch/MinecraftServerTarget.cpp @@ -0,0 +1,66 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MinecraftServerTarget.h" + +#include + +MinecraftServerTarget MinecraftServerTarget::parse(const QString &fullAddress) { + QStringList split = fullAddress.split(":"); + + // The logic below replicates the exact logic minecraft uses for parsing server addresses. + // While the conversion is not lossless and eats errors, it ensures the same behavior + // within Minecraft and MultiMC when entering server addresses. + if (fullAddress.startsWith("[")) + { + int bracket = fullAddress.indexOf("]"); + if (bracket > 0) + { + QString ipv6 = fullAddress.mid(1, bracket - 1); + QString port = fullAddress.mid(bracket + 1).trimmed(); + + if (port.startsWith(":") && !ipv6.isEmpty()) + { + port = port.mid(1); + split = QStringList({ ipv6, port }); + } + else + { + split = QStringList({ipv6}); + } + } + } + + if (split.size() > 2) + { + split = QStringList({fullAddress}); + } + + QString realAddress = split[0]; + + quint16 realPort = 25565; + if (split.size() > 1) + { + bool ok; + realPort = split[1].toUInt(&ok); + + if (!ok) + { + realPort = 25565; + } + } + + return MinecraftServerTarget { realAddress, realPort }; +} diff --git a/api/logic/minecraft/launch/MinecraftServerTarget.h b/api/logic/minecraft/launch/MinecraftServerTarget.h index f9b407e0..3c5786f4 100644 --- a/api/logic/minecraft/launch/MinecraftServerTarget.h +++ b/api/logic/minecraft/launch/MinecraftServerTarget.h @@ -17,9 +17,14 @@ #include +#include +#include + struct MinecraftServerTarget { QString address; quint16 port; + + static MULTIMC_LOGIC_EXPORT MinecraftServerTarget parse(const QString &fullAddress); }; typedef std::shared_ptr MinecraftServerTargetPtr; diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h index 7450fdda..325bac7a 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.h +++ b/api/logic/minecraft/legacy/LegacyInstance.h @@ -111,7 +111,8 @@ public: { return false; } - shared_qobject_ptr createLaunchTask(AuthSessionPtr account) override + shared_qobject_ptr createLaunchTask( + AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override { return nullptr; } diff --git a/application/InstancePageProvider.h b/application/InstancePageProvider.h index dde36aef..3cb723c4 100644 --- a/application/InstancePageProvider.h +++ b/application/InstancePageProvider.h @@ -46,7 +46,7 @@ public: values.append(new TexturePackPage(onesix.get())); values.append(new NotesPage(onesix.get())); values.append(new WorldListPage(onesix.get(), onesix->worldList())); - values.append(new ServersPage(onesix.get())); + values.append(new ServersPage(onesix)); // values.append(new GameOptionsPage(onesix.get())); values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); values.append(new InstanceSettingsPage(onesix.get())); diff --git a/application/LaunchController.cpp b/application/LaunchController.cpp index bebc3db1..3c4491a3 100644 --- a/application/LaunchController.cpp +++ b/application/LaunchController.cpp @@ -197,7 +197,7 @@ void LaunchController::launchInstance() return; } - m_launcher = m_instance->createLaunchTask(m_session); + m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin); if (!m_launcher) { emitFailed(tr("Couldn't instantiate a launcher.")); diff --git a/application/LaunchController.h b/application/LaunchController.h index 1d879028..5f177e00 100644 --- a/application/LaunchController.h +++ b/application/LaunchController.h @@ -3,6 +3,8 @@ #include #include +#include "minecraft/launch/MinecraftServerTarget.h" + class InstanceWindow; class LaunchController: public Task { @@ -33,6 +35,10 @@ public: { m_parentWidget = widget; } + void setServerToJoin(MinecraftServerTargetPtr serverToJoin) + { + m_serverToJoin = std::move(serverToJoin); + } QString id() { return m_instance->id(); @@ -58,4 +64,5 @@ private: InstanceWindow *m_console = nullptr; AuthSessionPtr m_session; shared_qobject_ptr m_launcher; + MinecraftServerTargetPtr m_serverToJoin; }; diff --git a/application/MultiMC.cpp b/application/MultiMC.cpp index 3429a377..8a81238e 100644 --- a/application/MultiMC.cpp +++ b/application/MultiMC.cpp @@ -1014,8 +1014,12 @@ bool MultiMC::openJsonEditor(const QString &filename) } } -bool MultiMC::launch(InstancePtr instance, bool online, BaseProfilerFactory *profiler) -{ +bool MultiMC::launch( + InstancePtr instance, + bool online, + BaseProfilerFactory *profiler, + MinecraftServerTargetPtr serverToJoin +) { if(m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; @@ -1036,6 +1040,7 @@ bool MultiMC::launch(InstancePtr instance, bool online, BaseProfilerFactory *pro controller->setInstance(instance); controller->setOnline(online); controller->setProfiler(profiler); + controller->setServerToJoin(serverToJoin); if(window) { controller->setParentWidget(window); diff --git a/application/MultiMC.h b/application/MultiMC.h index e6588a14..57a43cb9 100644 --- a/application/MultiMC.h +++ b/application/MultiMC.h @@ -11,6 +11,8 @@ #include +#include "minecraft/launch/MinecraftServerTarget.h" + class LaunchController; class LocalPeer; class InstanceWindow; @@ -150,7 +152,12 @@ signals: void globalSettingsClosed(); public slots: - bool launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr); + bool launch( + InstancePtr instance, + bool online = true, + BaseProfilerFactory *profiler = nullptr, + MinecraftServerTargetPtr serverToJoin = nullptr + ); bool kill(InstancePtr instance); private slots: diff --git a/application/pages/instance/InstanceSettingsPage.ui b/application/pages/instance/InstanceSettingsPage.ui index de9fa9e3..29024b65 100644 --- a/application/pages/instance/InstanceSettingsPage.ui +++ b/application/pages/instance/InstanceSettingsPage.ui @@ -39,7 +39,7 @@ QTabWidget::Rounded - 4 + 0 diff --git a/application/pages/instance/ServersPage.cpp b/application/pages/instance/ServersPage.cpp index 8b0c655c..d63c6e70 100644 --- a/application/pages/instance/ServersPage.cpp +++ b/application/pages/instance/ServersPage.cpp @@ -556,7 +556,7 @@ private: QTimer m_saveTimer; }; -ServersPage::ServersPage(MinecraftInstance * inst, QWidget* parent) +ServersPage::ServersPage(InstancePtr inst, QWidget* parent) : QMainWindow(parent), ui(new Ui::ServersPage) { ui->setupUi(this); @@ -579,7 +579,7 @@ ServersPage::ServersPage(MinecraftInstance * inst, QWidget* parent) auto selectionModel = ui->serversView->selectionModel(); connect(selectionModel, &QItemSelectionModel::currentChanged, this, &ServersPage::currentChanged); - connect(m_inst, &MinecraftInstance::runningStatusChanged, this, &ServersPage::on_RunningState_changed); + connect(m_inst.get(), &MinecraftInstance::runningStatusChanged, this, &ServersPage::on_RunningState_changed); connect(ui->nameLine, &QLineEdit::textEdited, this, &ServersPage::nameEdited); connect(ui->addressLine, &QLineEdit::textEdited, this, &ServersPage::addressEdited); connect(ui->resourceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(resourceIndexChanged(int))); @@ -695,6 +695,7 @@ void ServersPage::updateState() ui->actionMove_Down->setEnabled(serverEditEnabled); ui->actionMove_Up->setEnabled(serverEditEnabled); ui->actionRemove->setEnabled(serverEditEnabled); + ui->actionJoin->setEnabled(serverEditEnabled); if(server) { @@ -758,4 +759,10 @@ void ServersPage::on_actionMove_Down_triggered() } } +void ServersPage::on_actionJoin_triggered() +{ + const auto &address = m_model->at(currentServer)->m_address; + MMC->launch(m_inst, true, nullptr, std::make_shared(MinecraftServerTarget::parse(address))); +} + #include "ServersPage.moc" diff --git a/application/pages/instance/ServersPage.h b/application/pages/instance/ServersPage.h index f164da1e..8c5b7eb8 100644 --- a/application/pages/instance/ServersPage.h +++ b/application/pages/instance/ServersPage.h @@ -35,7 +35,7 @@ class ServersPage : public QMainWindow, public BasePage Q_OBJECT public: - explicit ServersPage(MinecraftInstance *inst, QWidget *parent = 0); + explicit ServersPage(InstancePtr inst, QWidget *parent = 0); virtual ~ServersPage(); void openedImpl() override; @@ -74,6 +74,7 @@ private slots: void on_actionRemove_triggered(); void on_actionMove_Up_triggered(); void on_actionMove_Down_triggered(); + void on_actionJoin_triggered(); void on_RunningState_changed(bool running); @@ -88,6 +89,6 @@ private: // data bool m_locked = true; Ui::ServersPage *ui = nullptr; ServersModel * m_model = nullptr; - MinecraftInstance * m_inst = nullptr; + InstancePtr m_inst = nullptr; }; diff --git a/application/pages/instance/ServersPage.ui b/application/pages/instance/ServersPage.ui index e9518e35..d89b7cba 100644 --- a/application/pages/instance/ServersPage.ui +++ b/application/pages/instance/ServersPage.ui @@ -148,6 +148,7 @@ + @@ -169,6 +170,11 @@ Move Down + + + Join + + From 58ab005f7e22785a47402b17c54880c5b32292db Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sat, 22 May 2021 18:10:17 +0200 Subject: [PATCH 09/10] NOISSUE Add missing license header --- api/logic/launch/steps/LookupServerAddress.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/api/logic/launch/steps/LookupServerAddress.cpp b/api/logic/launch/steps/LookupServerAddress.cpp index c6ca5bd5..de56c28a 100644 --- a/api/logic/launch/steps/LookupServerAddress.cpp +++ b/api/logic/launch/steps/LookupServerAddress.cpp @@ -1,3 +1,19 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + #include "LookupServerAddress.h" #include From 52c1150522139f042903321387c6582a99e8ad9a Mon Sep 17 00:00:00 2001 From: Janrupf Date: Sun, 23 May 2021 14:42:20 +0200 Subject: [PATCH 10/10] NOISSUE Add --server argument for --launch --- application/MultiMC.cpp | 67 +++++++++++++++++++++++++++++++++++++++-- application/MultiMC.h | 1 + 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/application/MultiMC.cpp b/application/MultiMC.cpp index 8a81238e..2dd4f83f 100644 --- a/application/MultiMC.cpp +++ b/application/MultiMC.cpp @@ -191,6 +191,11 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) parser.addOption("launch"); parser.addShortOpt("launch", 'l'); parser.addDocumentation("launch", "Launch the specified instance (by instance ID)"); + // --server + parser.addOption("server"); + parser.addShortOpt("server", 's'); + parser.addDocumentation("server", "Join the specified server on launch " + "(only valid in combination with --launch)"); // --alive parser.addSwitch("alive"); parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after MultiMC starts"); @@ -232,6 +237,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) } } m_instanceIdToLaunch = args["launch"].toString(); + m_serverToJoin = args["server"].toString(); m_liveCheck = args["alive"].toBool(); m_zipToImport = args["import"].toUrl(); @@ -293,6 +299,13 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) return; } + if(m_instanceIdToLaunch.isEmpty() && !m_serverToJoin.isEmpty()) + { + std::cerr << "--server can only be used in combination with --launch!" << std::endl; + m_status = MultiMC::Failed; + return; + } + /* * Establish the mechanism for communication with an already running MultiMC that uses the same data path. * If there is one, tell it what the user actually wanted to do and exit. @@ -318,7 +331,15 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) } else { - m_peerInstance->sendMessage("launch " + m_instanceIdToLaunch, timeout); + if(!m_serverToJoin.isEmpty()) + { + m_peerInstance->sendMessage( + "launch-with-server " + m_instanceIdToLaunch + " " + m_serverToJoin, timeout); + } + else + { + m_peerInstance->sendMessage("launch " + m_instanceIdToLaunch, timeout); + } } m_status = MultiMC::Succeeded; return; @@ -399,6 +420,10 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) { qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch; } + if(!m_serverToJoin.isEmpty()) + { + qDebug() << "Address of server to join :" << m_serverToJoin; + } qDebug() << "<> Paths set."; } @@ -844,8 +869,19 @@ void MultiMC::performMainStartupAction() auto inst = instances()->getInstanceById(m_instanceIdToLaunch); if(inst) { - qDebug() << "<> Instance launching:" << m_instanceIdToLaunch; - launch(inst, true, nullptr); + MinecraftServerTargetPtr serverToJoin = nullptr; + + if(!m_serverToJoin.isEmpty()) + { + serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(m_serverToJoin))); + qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching with server" << m_serverToJoin; + } + else + { + qDebug() << "<> Instance" << m_instanceIdToLaunch << "launching"; + } + + launch(inst, true, nullptr, serverToJoin); return; } } @@ -927,6 +963,31 @@ void MultiMC::messageReceived(const QString& message) launch(inst, true, nullptr); } } + else if(command == "launch-with-server") + { + QString instanceID = message.section(' ', 1, 1); + QString serverToJoin = message.section(' ', 2, 2); + if(instanceID.isEmpty()) + { + qWarning() << "Received" << command << "message without an instance ID."; + return; + } + if(serverToJoin.isEmpty()) + { + qWarning() << "Received" << command << "message without a server to join."; + return; + } + auto inst = instances()->getInstanceById(instanceID); + if(inst) + { + launch( + inst, + true, + nullptr, + std::make_shared(MinecraftServerTarget::parse(serverToJoin)) + ); + } + } else { qWarning() << "Received invalid message" << message; diff --git a/application/MultiMC.h b/application/MultiMC.h index 57a43cb9..af2b41c1 100644 --- a/application/MultiMC.h +++ b/application/MultiMC.h @@ -228,6 +228,7 @@ private: SetupWizard * m_setupWizard = nullptr; public: QString m_instanceIdToLaunch; + QString m_serverToJoin; bool m_liveCheck = false; QUrl m_zipToImport; std::unique_ptr logFile;