Merge pull request #3804 from Janrupf/feature/default-server

Add ability to select a server to join in the instance settings
This commit is contained in:
Petr Mrázek 2021-05-24 02:41:54 +02:00 committed by GitHub
commit 60b686f014
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 520 additions and 37 deletions

View File

@ -34,6 +34,8 @@
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
#include "minecraft/launch/MinecraftServerTarget.h"
class QDir; class QDir;
class Task; class Task;
class LaunchTask; class LaunchTask;
@ -145,7 +147,8 @@ public:
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0; virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container) /// returns a valid launcher (task container)
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0; virtual shared_qobject_ptr<LaunchTask> createLaunchTask(
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
/// returns the current launch task (if any) /// returns the current launch task (if any)
shared_qobject_ptr<LaunchTask> getLaunchTask(); shared_qobject_ptr<LaunchTask> getLaunchTask();
@ -221,9 +224,9 @@ public:
bool reloadSettings(); 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; Status currentStatus() const;

View File

@ -124,6 +124,8 @@ set(NET_SOURCES
# Game launch logic # Game launch logic
set(LAUNCH_SOURCES set(LAUNCH_SOURCES
launch/steps/LookupServerAddress.cpp
launch/steps/LookupServerAddress.h
launch/steps/PostLaunchCommand.cpp launch/steps/PostLaunchCommand.cpp
launch/steps/PostLaunchCommand.h launch/steps/PostLaunchCommand.h
launch/steps/PreLaunchCommand.cpp launch/steps/PreLaunchCommand.cpp
@ -236,6 +238,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/ExtractNatives.h minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h minecraft/launch/LauncherPartLaunch.h
minecraft/launch/MinecraftServerTarget.cpp
minecraft/launch/MinecraftServerTarget.h
minecraft/launch/PrintInstanceInfo.cpp minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h minecraft/launch/PrintInstanceInfo.h
minecraft/launch/ReconstructAssets.cpp minecraft/launch/ReconstructAssets.cpp

View File

@ -27,7 +27,7 @@ public:
{ {
return instanceRoot(); return instanceRoot();
}; };
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override
{ {
return nullptr; return nullptr;
} }
@ -67,7 +67,7 @@ public:
{ {
return false; return false;
} }
QStringList verboseDescription(AuthSessionPtr session) override QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
{ {
QStringList out; QStringList out;
out << "Null instance - placeholder."; out << "Null instance - placeholder.";

View File

@ -0,0 +1,95 @@
/* 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 <launch/LaunchTask.h>
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::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, 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 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, 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);
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(port)),MessageLevel::MultiMC);
resolve(firstRecord.target(), port);
}
void LookupServerAddress::resolve(const QString &address, quint16 port)
{
m_output->address = address;
m_output->port = port;
emitSucceeded();
m_dnsLookup->deleteLater();
}

View File

@ -0,0 +1,49 @@
/* 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 <launch/LaunchStep.h>
#include <QObjectPtr.h>
#include <QDnsLookup>
#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 setOutputAddressPtr(MinecraftServerTargetPtr output);
private slots:
void on_dnsLookupFinished();
private:
void resolve(const QString &address, quint16 port);
QDnsLookup *m_dnsLookup;
QString m_lookupAddress;
MinecraftServerTargetPtr m_output;
};

View File

@ -12,6 +12,7 @@
#include <java/JavaVersion.h> #include <java/JavaVersion.h>
#include "launch/LaunchTask.h" #include "launch/LaunchTask.h"
#include "launch/steps/LookupServerAddress.h"
#include "launch/steps/PostLaunchCommand.h" #include "launch/steps/PostLaunchCommand.h"
#include "launch/steps/Update.h" #include "launch/steps/Update.h"
#include "launch/steps/PreLaunchCommand.h" #include "launch/steps/PreLaunchCommand.h"
@ -111,6 +112,10 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride); m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), 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", "");
// DEPRECATED: Read what versions the user configuration thinks should be used // DEPRECATED: Read what versions the user configuration thinks should be used
m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, ""); m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
m_settings->registerSetting("LWJGLVersion", ""); m_settings->registerSetting("LWJGLVersion", "");
@ -395,7 +400,8 @@ static QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result; return result;
} }
QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) const QStringList MinecraftInstance::processMinecraftArgs(
AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const
{ {
auto profile = m_components->getProfile(); auto profile = m_components->getProfile();
QString args_pattern = profile->getMinecraftArguments(); QString args_pattern = profile->getMinecraftArguments();
@ -404,6 +410,12 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) cons
args_pattern += " --tweakClass " + tweaker; args_pattern += " --tweakClass " + tweaker;
} }
if (serverToJoin && !serverToJoin->address.isEmpty())
{
args_pattern += " --server " + serverToJoin->address;
args_pattern += " --port " + QString::number(serverToJoin->port);
}
QMap<QString, QString> token_mapping; QMap<QString, QString> token_mapping;
// yggdrasil! // yggdrasil!
if(session) if(session)
@ -440,7 +452,7 @@ QStringList MinecraftInstance::processMinecraftArgs(AuthSessionPtr session) cons
return parts; return parts;
} }
QString MinecraftInstance::createLaunchScript(AuthSessionPtr session) QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
{ {
QString launchScript; QString launchScript;
@ -461,8 +473,17 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session)
launchScript += "appletClass " + appletClass + "\n"; launchScript += "appletClass " + appletClass + "\n";
} }
if (serverToJoin && !serverToJoin->address.isEmpty())
{
launchScript += "serverAddress " + serverToJoin->address + "\n";
launchScript += "serverPort " + QString::number(serverToJoin->port) + "\n";
}
// generic minecraft params // generic minecraft params
for (auto param : processMinecraftArgs(session)) for (auto param : processMinecraftArgs(
session,
nullptr /* When using a launch script, the server parameters are handled by it*/
))
{ {
launchScript += "param " + param + "\n"; launchScript += "param " + param + "\n";
} }
@ -512,7 +533,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session)
return launchScript; return launchScript;
} }
QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session) QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
{ {
QStringList out; QStringList out;
out << "Main Class:" << " " + getMainClass() << ""; out << "Main Class:" << " " + getMainClass() << "";
@ -627,7 +648,7 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session)
out << ""; out << "";
} }
auto params = processMinecraftArgs(nullptr); auto params = processMinecraftArgs(nullptr, serverToJoin);
out << "Params:"; out << "Params:";
out << " " + params.join(' '); out << " " + params.join(' ');
out << ""; out << "";
@ -801,7 +822,7 @@ shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
return nullptr; return nullptr;
} }
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session) shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
{ {
// FIXME: get rid of shared_from_this ... // FIXME: get rid of shared_from_this ...
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this())); auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
@ -833,6 +854,21 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
process->appendStep(new CreateGameFolders(pptr)); process->appendStep(new CreateGameFolders(pptr));
} }
if (!serverToJoin && m_settings->get("JoinServerOnLaunch").toBool())
{
QString fullAddress = m_settings->get("JoinServerOnLaunchAddress").toString();
serverToJoin.reset(new MinecraftServerTarget(MinecraftServerTarget::parse(fullAddress)));
}
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 // run pre-launch command if that's needed
if(getPreLaunchCommand().size()) if(getPreLaunchCommand().size())
{ {
@ -864,7 +900,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
// print some instance info here... // print some instance info here...
{ {
process->appendStep(new PrintInstanceInfo(pptr, session)); process->appendStep(new PrintInstanceInfo(pptr, session, serverToJoin));
} }
// extract native jars if needed // extract native jars if needed
@ -885,6 +921,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
auto step = new LauncherPartLaunch(pptr); auto step = new LauncherPartLaunch(pptr);
step->setWorkingDirectory(gameRoot()); step->setWorkingDirectory(gameRoot());
step->setAuthSession(session); step->setAuthSession(session);
step->setServerToJoin(serverToJoin);
process->appendStep(step); process->appendStep(step);
} }
else if (method == "DirectJava") else if (method == "DirectJava")
@ -892,6 +929,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
auto step = new DirectJavaLaunch(pptr); auto step = new DirectJavaLaunch(pptr);
step->setWorkingDirectory(gameRoot()); step->setWorkingDirectory(gameRoot());
step->setAuthSession(session); step->setAuthSession(session);
step->setServerToJoin(serverToJoin);
process->appendStep(step); process->appendStep(step);
} }
} }

View File

@ -5,6 +5,7 @@
#include <QProcess> #include <QProcess>
#include <QDir> #include <QDir>
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
#include "minecraft/launch/MinecraftServerTarget.h"
class ModFolderModel; class ModFolderModel;
class WorldList; class WorldList;
@ -76,11 +77,11 @@ public:
////// Launch stuff ////// ////// Launch stuff //////
shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override; shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override; shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
QStringList extraArguments() const override; QStringList extraArguments() const override;
QStringList verboseDescription(AuthSessionPtr session) override; QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
QList<Mod> getJarMods() const; QList<Mod> getJarMods() const;
QString createLaunchScript(AuthSessionPtr session); QString createLaunchScript(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin);
/// get arguments passed to java /// get arguments passed to java
QStringList javaArguments() const; QStringList javaArguments() const;
@ -107,7 +108,7 @@ public:
virtual QString getMainClass() const; virtual QString getMainClass() const;
// FIXME: remove // FIXME: remove
virtual QStringList processMinecraftArgs(AuthSessionPtr account) const; virtual QStringList processMinecraftArgs(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) const;
virtual JavaVersion getJavaVersion() const; virtual JavaVersion getJavaVersion() const;

View File

@ -55,7 +55,7 @@ void DirectJavaLaunch::executeTask()
// make detachable - this will keep the process running even if the object is destroyed // make detachable - this will keep the process running even if the object is destroyed
m_process.setDetachable(true); m_process.setDetachable(true);
auto mcArgs = minecraftInstance->processMinecraftArgs(m_session); auto mcArgs = minecraftInstance->processMinecraftArgs(m_session, m_serverToJoin);
args.append(mcArgs); args.append(mcArgs);
QString wrapperCommandStr = instance->getWrapperCommand().trimmed(); QString wrapperCommandStr = instance->getWrapperCommand().trimmed();

View File

@ -19,6 +19,8 @@
#include <LoggedProcess.h> #include <LoggedProcess.h>
#include <minecraft/auth/AuthSession.h> #include <minecraft/auth/AuthSession.h>
#include "MinecraftServerTarget.h"
class DirectJavaLaunch: public LaunchStep class DirectJavaLaunch: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
@ -38,6 +40,12 @@ public:
{ {
m_session = session; m_session = session;
} }
void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
{
m_serverToJoin = std::move(serverToJoin);
}
private slots: private slots:
void on_state(LoggedProcess::State state); void on_state(LoggedProcess::State state);
@ -45,5 +53,6 @@ private:
LoggedProcess m_process; LoggedProcess m_process;
QString m_command; QString m_command;
AuthSessionPtr m_session; AuthSessionPtr m_session;
MinecraftServerTargetPtr m_serverToJoin;
}; };

View File

@ -59,7 +59,7 @@ void LauncherPartLaunch::executeTask()
auto instance = m_parent->instance(); auto instance = m_parent->instance();
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance); std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
m_launchScript = minecraftInstance->createLaunchScript(m_session); m_launchScript = minecraftInstance->createLaunchScript(m_session, m_serverToJoin);
QStringList args = minecraftInstance->javaArguments(); QStringList args = minecraftInstance->javaArguments();
QString allArgs = args.join(", "); QString allArgs = args.join(", ");
emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC); emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);

View File

@ -19,6 +19,8 @@
#include <LoggedProcess.h> #include <LoggedProcess.h>
#include <minecraft/auth/AuthSession.h> #include <minecraft/auth/AuthSession.h>
#include "MinecraftServerTarget.h"
class LauncherPartLaunch: public LaunchStep class LauncherPartLaunch: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
@ -39,6 +41,11 @@ public:
m_session = session; m_session = session;
} }
void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
{
m_serverToJoin = std::move(serverToJoin);
}
private slots: private slots:
void on_state(LoggedProcess::State state); void on_state(LoggedProcess::State state);
@ -47,5 +54,7 @@ private:
QString m_command; QString m_command;
AuthSessionPtr m_session; AuthSessionPtr m_session;
QString m_launchScript; QString m_launchScript;
MinecraftServerTargetPtr m_serverToJoin;
bool mayProceed = false; bool mayProceed = false;
}; };

View File

@ -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 <QStringList>
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 };
}

View File

@ -0,0 +1,30 @@
/* 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 <memory>
#include <QString>
#include <multimc_logic_export.h>
struct MinecraftServerTarget {
QString address;
quint16 port;
static MULTIMC_LOGIC_EXPORT MinecraftServerTarget parse(const QString &fullAddress);
};
typedef std::shared_ptr<MinecraftServerTarget> MinecraftServerTargetPtr;

View File

@ -101,6 +101,6 @@ void PrintInstanceInfo::executeTask()
#endif #endif
logLines(log, MessageLevel::MultiMC); logLines(log, MessageLevel::MultiMC);
logLines(instance->verboseDescription(m_session), MessageLevel::MultiMC); logLines(instance->verboseDescription(m_session, m_serverToJoin), MessageLevel::MultiMC);
emitSucceeded(); emitSucceeded();
} }

View File

@ -18,13 +18,15 @@
#include <launch/LaunchStep.h> #include <launch/LaunchStep.h>
#include <memory> #include <memory>
#include "minecraft/auth/AuthSession.h" #include "minecraft/auth/AuthSession.h"
#include "minecraft/launch/MinecraftServerTarget.h"
// FIXME: temporary wrapper for existing task. // FIXME: temporary wrapper for existing task.
class PrintInstanceInfo: public LaunchStep class PrintInstanceInfo: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
public: 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 ~PrintInstanceInfo(){};
virtual void executeTask(); virtual void executeTask();
@ -34,5 +36,6 @@ public:
} }
private: private:
AuthSessionPtr m_session; AuthSessionPtr m_session;
MinecraftServerTargetPtr m_serverToJoin;
}; };

View File

@ -225,7 +225,7 @@ QString LegacyInstance::getStatusbarDescription()
return tr("Instance from previous versions."); return tr("Instance from previous versions.");
} }
QStringList LegacyInstance::verboseDescription(AuthSessionPtr session) QStringList LegacyInstance::verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
{ {
QStringList out; QStringList out;

View File

@ -111,7 +111,8 @@ public:
{ {
return false; return false;
} }
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override shared_qobject_ptr<LaunchTask> createLaunchTask(
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override
{ {
return nullptr; return nullptr;
} }
@ -125,7 +126,7 @@ public:
} }
QString getStatusbarDescription() override; QString getStatusbarDescription() override;
QStringList verboseDescription(AuthSessionPtr session) override; QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
QProcessEnvironment createEnvironment() override QProcessEnvironment createEnvironment() override
{ {

View File

@ -46,7 +46,7 @@ public:
values.append(new TexturePackPage(onesix.get())); values.append(new TexturePackPage(onesix.get()));
values.append(new NotesPage(onesix.get())); values.append(new NotesPage(onesix.get()));
values.append(new WorldListPage(onesix.get(), onesix->worldList())); 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 GameOptionsPage(onesix.get()));
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots"))); values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
values.append(new InstanceSettingsPage(onesix.get())); values.append(new InstanceSettingsPage(onesix.get()));

View File

@ -197,7 +197,7 @@ void LaunchController::launchInstance()
return; return;
} }
m_launcher = m_instance->createLaunchTask(m_session); m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
if (!m_launcher) if (!m_launcher)
{ {
emitFailed(tr("Couldn't instantiate a launcher.")); emitFailed(tr("Couldn't instantiate a launcher."));

View File

@ -3,6 +3,8 @@
#include <BaseInstance.h> #include <BaseInstance.h>
#include <tools/BaseProfiler.h> #include <tools/BaseProfiler.h>
#include "minecraft/launch/MinecraftServerTarget.h"
class InstanceWindow; class InstanceWindow;
class LaunchController: public Task class LaunchController: public Task
{ {
@ -33,6 +35,10 @@ public:
{ {
m_parentWidget = widget; m_parentWidget = widget;
} }
void setServerToJoin(MinecraftServerTargetPtr serverToJoin)
{
m_serverToJoin = std::move(serverToJoin);
}
QString id() QString id()
{ {
return m_instance->id(); return m_instance->id();
@ -58,4 +64,5 @@ private:
InstanceWindow *m_console = nullptr; InstanceWindow *m_console = nullptr;
AuthSessionPtr m_session; AuthSessionPtr m_session;
shared_qobject_ptr<LaunchTask> m_launcher; shared_qobject_ptr<LaunchTask> m_launcher;
MinecraftServerTargetPtr m_serverToJoin;
}; };

View File

@ -191,6 +191,11 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
parser.addOption("launch"); parser.addOption("launch");
parser.addShortOpt("launch", 'l'); parser.addShortOpt("launch", 'l');
parser.addDocumentation("launch", "Launch the specified instance (by instance ID)"); 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 // --alive
parser.addSwitch("alive"); parser.addSwitch("alive");
parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after MultiMC starts"); 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_instanceIdToLaunch = args["launch"].toString();
m_serverToJoin = args["server"].toString();
m_liveCheck = args["alive"].toBool(); m_liveCheck = args["alive"].toBool();
m_zipToImport = args["import"].toUrl(); m_zipToImport = args["import"].toUrl();
@ -293,6 +299,13 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
return; 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. * 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. * 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 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; m_status = MultiMC::Succeeded;
return; return;
@ -399,6 +420,10 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
{ {
qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch; qDebug() << "ID of instance to launch : " << m_instanceIdToLaunch;
} }
if(!m_serverToJoin.isEmpty())
{
qDebug() << "Address of server to join :" << m_serverToJoin;
}
qDebug() << "<> Paths set."; qDebug() << "<> Paths set.";
} }
@ -844,8 +869,19 @@ void MultiMC::performMainStartupAction()
auto inst = instances()->getInstanceById(m_instanceIdToLaunch); auto inst = instances()->getInstanceById(m_instanceIdToLaunch);
if(inst) if(inst)
{ {
qDebug() << "<> Instance launching:" << m_instanceIdToLaunch; MinecraftServerTargetPtr serverToJoin = nullptr;
launch(inst, true, 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; return;
} }
} }
@ -927,6 +963,31 @@ void MultiMC::messageReceived(const QString& message)
launch(inst, true, nullptr); 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>(MinecraftServerTarget::parse(serverToJoin))
);
}
}
else else
{ {
qWarning() << "Received invalid message" << message; qWarning() << "Received invalid message" << message;
@ -1014,8 +1075,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) if(m_updateRunning)
{ {
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
@ -1036,6 +1101,7 @@ bool MultiMC::launch(InstancePtr instance, bool online, BaseProfilerFactory *pro
controller->setInstance(instance); controller->setInstance(instance);
controller->setOnline(online); controller->setOnline(online);
controller->setProfiler(profiler); controller->setProfiler(profiler);
controller->setServerToJoin(serverToJoin);
if(window) if(window)
{ {
controller->setParentWidget(window); controller->setParentWidget(window);

View File

@ -11,6 +11,8 @@
#include <BaseInstance.h> #include <BaseInstance.h>
#include "minecraft/launch/MinecraftServerTarget.h"
class LaunchController; class LaunchController;
class LocalPeer; class LocalPeer;
class InstanceWindow; class InstanceWindow;
@ -150,7 +152,12 @@ signals:
void globalSettingsClosed(); void globalSettingsClosed();
public slots: 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); bool kill(InstancePtr instance);
private slots: private slots:
@ -221,6 +228,7 @@ private:
SetupWizard * m_setupWizard = nullptr; SetupWizard * m_setupWizard = nullptr;
public: public:
QString m_instanceIdToLaunch; QString m_instanceIdToLaunch;
QString m_serverToJoin;
bool m_liveCheck = false; bool m_liveCheck = false;
QUrl m_zipToImport; QUrl m_zipToImport;
std::unique_ptr<QFile> logFile; std::unique_ptr<QFile> logFile;

View File

@ -191,6 +191,18 @@ void InstanceSettingsPage::applySettings()
m_settings->reset("ShowGameTime"); m_settings->reset("ShowGameTime");
m_settings->reset("RecordGameTime"); 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());
}
else
{
m_settings->reset("JoinServerOnLaunchAddress");
}
} }
void InstanceSettingsPage::loadSettings() void InstanceSettingsPage::loadSettings()
@ -257,6 +269,9 @@ void InstanceSettingsPage::loadSettings()
ui->gameTimeGroupBox->setChecked(m_settings->get("OverrideGameTime").toBool()); ui->gameTimeGroupBox->setChecked(m_settings->get("OverrideGameTime").toBool());
ui->showGameTime->setChecked(m_settings->get("ShowGameTime").toBool()); ui->showGameTime->setChecked(m_settings->get("ShowGameTime").toBool());
ui->recordGameTime->setChecked(m_settings->get("RecordGameTime").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());
} }
void InstanceSettingsPage::on_javaDetectBtn_clicked() void InstanceSettingsPage::on_javaDetectBtn_clicked()

View File

@ -453,6 +453,41 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="serverJoinGroupBox">
<property name="title">
<string>Set a server to join on launch</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<layout class="QGridLayout" name="serverJoinLayout">
<item row="0" column="0">
<widget class="QLabel" name="serverJoinAddressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Server address:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="serverJoinAddress"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacerMiscellanous"> <spacer name="verticalSpacerMiscellanous">
<property name="orientation"> <property name="orientation">

View File

@ -556,7 +556,7 @@ private:
QTimer m_saveTimer; QTimer m_saveTimer;
}; };
ServersPage::ServersPage(MinecraftInstance * inst, QWidget* parent) ServersPage::ServersPage(InstancePtr inst, QWidget* parent)
: QMainWindow(parent), ui(new Ui::ServersPage) : QMainWindow(parent), ui(new Ui::ServersPage)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -579,7 +579,7 @@ ServersPage::ServersPage(MinecraftInstance * inst, QWidget* parent)
auto selectionModel = ui->serversView->selectionModel(); auto selectionModel = ui->serversView->selectionModel();
connect(selectionModel, &QItemSelectionModel::currentChanged, this, &ServersPage::currentChanged); 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->nameLine, &QLineEdit::textEdited, this, &ServersPage::nameEdited);
connect(ui->addressLine, &QLineEdit::textEdited, this, &ServersPage::addressEdited); connect(ui->addressLine, &QLineEdit::textEdited, this, &ServersPage::addressEdited);
connect(ui->resourceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(resourceIndexChanged(int))); connect(ui->resourceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(resourceIndexChanged(int)));
@ -695,6 +695,7 @@ void ServersPage::updateState()
ui->actionMove_Down->setEnabled(serverEditEnabled); ui->actionMove_Down->setEnabled(serverEditEnabled);
ui->actionMove_Up->setEnabled(serverEditEnabled); ui->actionMove_Up->setEnabled(serverEditEnabled);
ui->actionRemove->setEnabled(serverEditEnabled); ui->actionRemove->setEnabled(serverEditEnabled);
ui->actionJoin->setEnabled(serverEditEnabled);
if(server) 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>(MinecraftServerTarget::parse(address)));
}
#include "ServersPage.moc" #include "ServersPage.moc"

View File

@ -35,7 +35,7 @@ class ServersPage : public QMainWindow, public BasePage
Q_OBJECT Q_OBJECT
public: public:
explicit ServersPage(MinecraftInstance *inst, QWidget *parent = 0); explicit ServersPage(InstancePtr inst, QWidget *parent = 0);
virtual ~ServersPage(); virtual ~ServersPage();
void openedImpl() override; void openedImpl() override;
@ -74,6 +74,7 @@ private slots:
void on_actionRemove_triggered(); void on_actionRemove_triggered();
void on_actionMove_Up_triggered(); void on_actionMove_Up_triggered();
void on_actionMove_Down_triggered(); void on_actionMove_Down_triggered();
void on_actionJoin_triggered();
void on_RunningState_changed(bool running); void on_RunningState_changed(bool running);
@ -88,6 +89,6 @@ private: // data
bool m_locked = true; bool m_locked = true;
Ui::ServersPage *ui = nullptr; Ui::ServersPage *ui = nullptr;
ServersModel * m_model = nullptr; ServersModel * m_model = nullptr;
MinecraftInstance * m_inst = nullptr; InstancePtr m_inst = nullptr;
}; };

View File

@ -148,6 +148,7 @@
<addaction name="actionRemove"/> <addaction name="actionRemove"/>
<addaction name="actionMove_Up"/> <addaction name="actionMove_Up"/>
<addaction name="actionMove_Down"/> <addaction name="actionMove_Down"/>
<addaction name="actionJoin"/>
</widget> </widget>
<action name="actionAdd"> <action name="actionAdd">
<property name="text"> <property name="text">
@ -169,6 +170,11 @@
<string>Move Down</string> <string>Move Down</string>
</property> </property>
</action> </action>
<action name="actionJoin">
<property name="text">
<string>Join</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -46,7 +46,16 @@ public class LegacyFrame extends Frame implements WindowListener
this.addWindowListener ( this ); 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 { try {
appletWrap = new Launcher( mcApplet, new URL ( "http://www.minecraft.net/game" ) ); 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); e.printStackTrace(System.err);
System.exit(-1); System.exit(-1);
} }
if (serverAddress != null)
{
appletWrap.setParameter("server", serverAddress);
appletWrap.setParameter("port", serverPort);
}
appletWrap.setParameter ( "username", user ); appletWrap.setParameter ( "username", user );
appletWrap.setParameter ( "sessionid", session ); appletWrap.setParameter ( "sessionid", session );
appletWrap.setParameter ( "stand-alone", "true" ); // Show the quit button. appletWrap.setParameter ( "stand-alone", "true" ); // Show the quit button.

View File

@ -47,6 +47,9 @@ public class OneSixLauncher implements Launcher
private boolean maximize; private boolean maximize;
private String cwd; private String cwd;
private String serverAddress;
private String serverPort;
// the much abused system classloader, for convenience (for further abuse) // the much abused system classloader, for convenience (for further abuse)
private ClassLoader cl; private ClassLoader cl;
@ -64,6 +67,9 @@ public class OneSixLauncher implements Launcher
windowTitle = params.firstSafe("windowTitle", "Minecraft"); windowTitle = params.firstSafe("windowTitle", "Minecraft");
windowParams = params.firstSafe("windowParams", "854x480"); windowParams = params.firstSafe("windowParams", "854x480");
serverAddress = params.firstSafe("serverAddress", null);
serverPort = params.firstSafe("serverPort", null);
cwd = System.getProperty("user.dir"); cwd = System.getProperty("user.dir");
winSizeW = 854; winSizeW = 854;
@ -122,7 +128,7 @@ public class OneSixLauncher implements Launcher
Class<?> MCAppletClass = cl.loadClass(appletClass); Class<?> MCAppletClass = cl.loadClass(appletClass);
Applet mcappl = (Applet) MCAppletClass.newInstance(); Applet mcappl = (Applet) MCAppletClass.newInstance();
LegacyFrame mcWindow = new LegacyFrame(windowTitle); 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; return 0;
} catch (Exception e) } catch (Exception e)
{ {
@ -164,6 +170,14 @@ public class OneSixLauncher implements Launcher
mcparams.add(Integer.toString(winSizeH)); mcparams.add(Integer.toString(winSizeH));
} }
if (serverAddress != null)
{
mcparams.add("--server");
mcparams.add(serverAddress);
mcparams.add("--port");
mcparams.add(serverPort);
}
// Get the Minecraft Class. // Get the Minecraft Class.
Class<?> mc; Class<?> mc;
try try