NOISSUE implement direct java launch

Just running the Java process and giving it params on the command line
This commit is contained in:
Petr Mrázek 2016-06-16 02:20:23 +02:00
parent 57c84ec2b1
commit 1f2bed2ef1
31 changed files with 990 additions and 627 deletions

View File

@ -210,6 +210,11 @@ public:
virtual bool reload(); virtual bool reload();
/**
* 'print' a verbose desription of the instance into a QStringList
*/
virtual QStringList verboseDescription(AuthSessionPtr session) = 0;
signals: signals:
/*! /*!
* \brief Signal emitted when properties relevant to the instance view change * \brief Signal emitted when properties relevant to the instance view change

View File

@ -202,8 +202,14 @@ set(MINECRAFT_SOURCES
minecraft/onesix/OneSixVersionFormat.h minecraft/onesix/OneSixVersionFormat.h
minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.cpp
minecraft/launch/ModMinecraftJar.h minecraft/launch/ModMinecraftJar.h
minecraft/launch/LaunchMinecraft.cpp minecraft/launch/DirectJavaLaunch.cpp
minecraft/launch/LaunchMinecraft.h minecraft/launch/DirectJavaLaunch.h
minecraft/launch/ExtractNatives.cpp
minecraft/launch/ExtractNatives.h
minecraft/launch/LauncherPartLaunch.cpp
minecraft/launch/LauncherPartLaunch.h
minecraft/launch/PrintInstanceInfo.cpp
minecraft/launch/PrintInstanceInfo.h
minecraft/legacy/LegacyModList.h minecraft/legacy/LegacyModList.h
minecraft/legacy/LegacyModList.cpp minecraft/legacy/LegacyModList.cpp
minecraft/legacy/LegacyUpdate.h minecraft/legacy/LegacyUpdate.h

View File

@ -87,4 +87,10 @@ public:
{ {
return false; return false;
} }
QStringList verboseDescription(AuthSessionPtr session) override
{
QStringList out;
out << "Null instance - placeholder.";
return out;
}
}; };

View File

@ -6,7 +6,7 @@
JavaVersion & JavaVersion::operator=(const QString & javaVersionString) JavaVersion & JavaVersion::operator=(const QString & javaVersionString)
{ {
string = javaVersionString; m_string = javaVersionString;
auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int auto getCapturedInteger = [](const QRegularExpressionMatch & match, const QString &what) -> int
{ {
@ -28,12 +28,12 @@ JavaVersion & JavaVersion::operator=(const QString & javaVersionString)
pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?"); pattern = QRegularExpression("(?<major>[0-9]+)([.](?<minor>[0-9]+))?([.](?<security>[0-9]+))?(-(?<prerelease>[a-zA-Z0-9]+))?");
} }
auto match = pattern.match(string); auto match = pattern.match(m_string);
parseable = match.hasMatch(); m_parseable = match.hasMatch();
major = getCapturedInteger(match, "major"); m_major = getCapturedInteger(match, "major");
minor = getCapturedInteger(match, "minor"); m_minor = getCapturedInteger(match, "minor");
security = getCapturedInteger(match, "security"); m_security = getCapturedInteger(match, "security");
prerelease = match.captured("prerelease"); m_prerelease = match.captured("prerelease");
return *this; return *this;
} }
@ -44,38 +44,38 @@ JavaVersion::JavaVersion(const QString &rhs)
QString JavaVersion::toString() QString JavaVersion::toString()
{ {
return string; return m_string;
} }
bool JavaVersion::requiresPermGen() bool JavaVersion::requiresPermGen()
{ {
if(parseable) if(m_parseable)
{ {
return major < 8; return m_major < 8;
} }
return true; return true;
} }
bool JavaVersion::operator<(const JavaVersion &rhs) bool JavaVersion::operator<(const JavaVersion &rhs)
{ {
if(parseable && rhs.parseable) if(m_parseable && rhs.m_parseable)
{ {
if(major < rhs.major) if(m_major < rhs.m_major)
return true; return true;
if(major > rhs.major) if(m_major > rhs.m_major)
return false; return false;
if(minor < rhs.minor) if(m_minor < rhs.m_minor)
return true; return true;
if(minor > rhs.minor) if(m_minor > rhs.m_minor)
return false; return false;
if(security < rhs.security) if(m_security < rhs.m_security)
return true; return true;
if(security > rhs.security) if(m_security > rhs.m_security)
return false; return false;
// everything else being equal, consider prerelease status // everything else being equal, consider prerelease status
bool thisPre = !prerelease.isEmpty(); bool thisPre = !m_prerelease.isEmpty();
bool rhsPre = !rhs.prerelease.isEmpty(); bool rhsPre = !rhs.m_prerelease.isEmpty();
if(thisPre && !rhsPre) if(thisPre && !rhsPre)
{ {
// this is a prerelease and the other one isn't -> lesser // this is a prerelease and the other one isn't -> lesser
@ -89,21 +89,21 @@ bool JavaVersion::operator<(const JavaVersion &rhs)
else if(thisPre && rhsPre) else if(thisPre && rhsPre)
{ {
// both are prereleases - use natural compare... // both are prereleases - use natural compare...
return Strings::naturalCompare(prerelease, rhs.prerelease, Qt::CaseSensitive) < 0; return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
} }
// neither is prerelease, so they are the same -> this cannot be less than rhs // neither is prerelease, so they are the same -> this cannot be less than rhs
return false; return false;
} }
else return Strings::naturalCompare(string, rhs.string, Qt::CaseSensitive) < 0; else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
} }
bool JavaVersion::operator==(const JavaVersion &rhs) bool JavaVersion::operator==(const JavaVersion &rhs)
{ {
if(parseable && rhs.parseable) if(m_parseable && rhs.m_parseable)
{ {
return major == rhs.major && minor == rhs.minor && security == rhs.security && prerelease == rhs.prerelease; return m_major == rhs.m_major && m_minor == rhs.m_minor && m_security == rhs.m_security && m_prerelease == rhs.m_prerelease;
} }
return string == rhs.string; return m_string == rhs.m_string;
} }
bool JavaVersion::operator>(const JavaVersion &rhs) bool JavaVersion::operator>(const JavaVersion &rhs)

View File

@ -20,11 +20,23 @@ public:
QString toString(); QString toString();
int major()
{
return m_major;
}
int minor()
{
return m_minor;
}
int security()
{
return m_security;
}
private: private:
QString string; QString m_string;
int major = 0; int m_major = 0;
int minor = 0; int m_minor = 0;
int security = 0; int m_security = 0;
bool parseable = false; bool m_parseable = false;
QString prerelease; QString m_prerelease;
}; };

View File

@ -34,12 +34,12 @@ slots:
QFETCH(QString, prerelease); QFETCH(QString, prerelease);
JavaVersion test(string); JavaVersion test(string);
QCOMPARE(test.string, string); QCOMPARE(test.m_string, string);
QCOMPARE(test.toString(), string); QCOMPARE(test.toString(), string);
QCOMPARE(test.major, major); QCOMPARE(test.m_major, major);
QCOMPARE(test.minor, minor); QCOMPARE(test.m_minor, minor);
QCOMPARE(test.security, security); QCOMPARE(test.m_security, security);
QCOMPARE(test.prerelease, prerelease); QCOMPARE(test.m_prerelease, prerelease);
} }
void test_Sort_data() void test_Sort_data()

View File

@ -53,20 +53,25 @@ void CheckJava::executeTask()
QFileInfo javaInfo(realJavaPath); QFileInfo javaInfo(realJavaPath);
qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch();
auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); auto storedUnixTime = settings->get("JavaTimestamp").toLongLong();
auto storedArchitecture = settings->get("JavaArchitecture").toString();
auto storedVersion = settings->get("JavaVersion").toString();
m_javaUnixTime = javaUnixTime; m_javaUnixTime = javaUnixTime;
// if they are not the same, check! // if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime) if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
{ {
m_JavaChecker = std::make_shared<JavaChecker>(); m_JavaChecker = std::make_shared<JavaChecker>();
QString errorLog;
QString version;
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC); emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
&CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath; m_JavaChecker->m_path = realJavaPath;
m_JavaChecker->performCheck(); m_JavaChecker->performCheck();
return; return;
} }
else
{
auto verString = instance->settings()->get("JavaVersion").toString();
auto archString = instance->settings()->get("JavaArchitecture").toString();
printJavaInfo(verString, archString);
}
emitSucceeded(); emitSucceeded();
} }
@ -83,10 +88,15 @@ void CheckJava::checkJavaFinished(JavaCheckResult result)
else else
{ {
auto instance = m_parent->instance(); auto instance = m_parent->instance();
emit logLine(tr("Java version is %1!\n").arg(result.javaVersion.toString()), printJavaInfo(result.javaVersion.toString(), result.mojangPlatform);
MessageLevel::MultiMC);
instance->settings()->set("JavaVersion", result.javaVersion.toString()); instance->settings()->set("JavaVersion", result.javaVersion.toString());
instance->settings()->set("JavaArchitecture", result.mojangPlatform);
instance->settings()->set("JavaTimestamp", m_javaUnixTime); instance->settings()->set("JavaTimestamp", m_javaUnixTime);
emitSucceeded(); emitSucceeded();
} }
} }
void CheckJava::printJavaInfo(const QString& version, const QString& architecture)
{
emit logLine(tr("Java is version %1, using %2-bit architecture.\n\n").arg(version, architecture), MessageLevel::MultiMC);
}

View File

@ -34,6 +34,9 @@ public:
private slots: private slots:
void checkJavaFinished(JavaCheckResult result); void checkJavaFinished(JavaCheckResult result);
private:
void printJavaInfo(const QString & version, const QString & architecture);
private: private:
QString m_javaPath; QString m_javaPath;
qlonglong m_javaUnixTime; qlonglong m_javaUnixTime;

View File

@ -1,4 +1,6 @@
#include "MinecraftInstance.h" #include "MinecraftInstance.h"
#include <minecraft/launch/ExtractNatives.h>
#include <minecraft/launch/PrintInstanceInfo.h>
#include <settings/Setting.h> #include <settings/Setting.h>
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "Env.h" #include "Env.h"
@ -9,6 +11,15 @@
#include <FileSystem.h> #include <FileSystem.h>
#include <java/JavaVersion.h> #include <java/JavaVersion.h>
#include "launch/LaunchTask.h"
#include "launch/steps/PostLaunchCommand.h"
#include "launch/steps/Update.h"
#include "launch/steps/PreLaunchCommand.h"
#include "launch/steps/TextPrint.h"
#include "minecraft/launch/LauncherPartLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h"
#include "java/launch/CheckJava.h"
#define IBUS "@im=ibus" #define IBUS "@im=ibus"
// all of this because keeping things compatible with deprecated old settings // all of this because keeping things compatible with deprecated old settings
@ -52,6 +63,7 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
// special! // special!
m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation); m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation);
m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation); m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation);
m_settings->registerPassthrough(globalSettings->getSetting("JavaArchitecture"), javaOrLocation);
// Window Size // Window Size
auto windowSetting = m_settings->registerSetting("OverrideWindow", false); auto windowSetting = m_settings->registerSetting("OverrideWindow", false);
@ -64,6 +76,10 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"), memorySetting); m_settings->registerOverride(globalSettings->getSetting("MinMemAlloc"), memorySetting);
m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"), memorySetting); m_settings->registerOverride(globalSettings->getSetting("MaxMemAlloc"), memorySetting);
m_settings->registerOverride(globalSettings->getSetting("PermGen"), memorySetting); m_settings->registerOverride(globalSettings->getSetting("PermGen"), memorySetting);
// Minecraft launch method
auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride);
} }
QString MinecraftInstance::minecraftRoot() const QString MinecraftInstance::minecraftRoot() const
@ -105,7 +121,7 @@ QStringList MinecraftInstance::javaArguments() const
args << QString("-Xmx%1m").arg(settings()->get("MaxMemAlloc").toInt()); args << QString("-Xmx%1m").arg(settings()->get("MaxMemAlloc").toInt());
// No PermGen in newer java. // No PermGen in newer java.
JavaVersion javaVersion(settings()->get("JavaVersion").toString()); JavaVersion javaVersion = getJavaVersion();
if(javaVersion.requiresPermGen()) if(javaVersion.requiresPermGen())
{ {
auto permgen = settings()->get("PermGen").toInt(); auto permgen = settings()->get("PermGen").toInt();
@ -116,7 +132,6 @@ QStringList MinecraftInstance::javaArguments() const
} }
args << "-Duser.language=en"; args << "-Duser.language=en";
args << "-jar" << FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "NewLaunch.jar");
return args; return args;
} }
@ -366,4 +381,95 @@ QString MinecraftInstance::getStatusbarDescription()
return description; return description;
} }
std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session)
{
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr()));
auto pptr = process.get();
// print a header
{
process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + minecraftRoot() + "\n\n", MessageLevel::MultiMC));
}
// check java
{
auto step = std::make_shared<CheckJava>(pptr);
process->appendStep(step);
}
// check launch method
QStringList validMethods = validLaunchMethods();
QString method = launchMethod();
if(!validMethods.contains(method))
{
process->appendStep(std::make_shared<TextPrint>(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
return process;
}
// run pre-launch command if that's needed
if(getPreLaunchCommand().size())
{
auto step = std::make_shared<PreLaunchCommand>(pptr);
step->setWorkingDirectory(minecraftRoot());
process->appendStep(step);
}
// if we aren't in offline mode,.
if(session->status != AuthSession::PlayableOffline)
{
process->appendStep(std::make_shared<Update>(pptr));
}
// if there are any jar mods
if(getJarMods().size())
{
auto step = std::make_shared<ModMinecraftJar>(pptr);
process->appendStep(step);
}
// print some instance info here...
{
auto step = std::make_shared<PrintInstanceInfo>(pptr, session);
process->appendStep(step);
}
// extract native jars if needed
auto jars = getNativeJars();
if(jars.size())
{
auto step = std::make_shared<ExtractNatives>(pptr);
process->appendStep(step);
}
{
// actually launch the game
auto step = createMainLaunchStep(pptr, session);
process->appendStep(step);
}
// run post-exit command if that's needed
if(getPostExitCommand().size())
{
auto step = std::make_shared<PostLaunchCommand>(pptr);
step->setWorkingDirectory(minecraftRoot());
process->appendStep(step);
}
if (session)
{
process->setCensorFilter(createCensorFilterFromSession(session));
}
return process;
}
QString MinecraftInstance::launchMethod()
{
return m_settings->get("MCLaunchMethod").toString();
}
JavaVersion MinecraftInstance::getJavaVersion() const
{
return JavaVersion(settings()->get("JavaVersion").toString());
}
#include "MinecraftInstance.moc" #include "MinecraftInstance.moc"

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "BaseInstance.h" #include "BaseInstance.h"
#include <java/JavaVersion.h>
#include "minecraft/Mod.h" #include "minecraft/Mod.h"
#include <QProcess> #include <QProcess>
@ -7,6 +8,7 @@
class ModList; class ModList;
class WorldList; class WorldList;
class LaunchStep;
class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance
{ {
@ -36,7 +38,7 @@ public:
return QList<Mod>(); return QList<Mod>();
} }
/// get the launch script to be used with this virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
virtual QString createLaunchScript(AuthSessionPtr session) = 0; virtual QString createLaunchScript(AuthSessionPtr session) = 0;
//FIXME: nuke? //FIXME: nuke?
@ -60,8 +62,22 @@ public:
virtual QString getStatusbarDescription() override; virtual QString getStatusbarDescription() override;
virtual QStringList getClassPath() const = 0;
virtual QStringList getNativeJars() const = 0;
virtual QString getMainClass() const = 0;
virtual QString getNativePath() const = 0;
virtual QStringList processMinecraftArgs(AuthSessionPtr account) const = 0;
virtual JavaVersion getJavaVersion() const;
protected: protected:
QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session); QMap<QString, QString> createCensorFilterFromSession(AuthSessionPtr session);
virtual QStringList validLaunchMethods() = 0;
virtual QString launchMethod();
virtual std::shared_ptr<LaunchStep> createMainLaunchStep(LaunchTask *parent, AuthSessionPtr session) = 0;
private: private:
QString prettifyTimeDuration(int64_t duration); QString prettifyTimeDuration(int64_t duration);
}; };

View File

@ -573,6 +573,26 @@ const QList<LibraryPtr> & MinecraftProfile::getLibraries() const
return m_libraries; return m_libraries;
} }
void MinecraftProfile::getLibraryFiles(const QString& architecture, QStringList& jars, QStringList& nativeJars) const
{
QStringList native32, native64;
jars.clear();
nativeJars.clear();
for (auto lib : getLibraries())
{
lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64);
}
if(architecture == "32")
{
nativeJars.append(native32);
}
else if(architecture == "64")
{
nativeJars.append(native64);
}
}
QString MinecraftProfile::getMainJarUrl() const QString MinecraftProfile::getMainJarUrl() const
{ {
auto iter = mojangDownloads.find("client"); auto iter = mojangDownloads.find("client");

View File

@ -110,6 +110,7 @@ public: /* getters for profile variables */
const QStringList & getTweakers() const; const QStringList & getTweakers() const;
const QList<JarmodPtr> & getJarMods() const; const QList<JarmodPtr> & getJarMods() const;
const QList<LibraryPtr> & getLibraries() const; const QList<LibraryPtr> & getLibraries() const;
void getLibraryFiles(const QString & architecture, QStringList & jars, QStringList & nativeJars) const;
QString getMainJarUrl() const; QString getMainJarUrl() const;
bool hasTrait(const QString & trait) const; bool hasTrait(const QString & trait) const;
ProblemSeverity getProblemSeverity() const; ProblemSeverity getProblemSeverity() const;

View File

@ -0,0 +1,149 @@
/* Copyright 2013-2015 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 "DirectJavaLaunch.h"
#include <launch/LaunchTask.h>
#include <minecraft/MinecraftInstance.h>
#include <FileSystem.h>
#include <QStandardPaths>
DirectJavaLaunch::DirectJavaLaunch(LaunchTask *parent) : LaunchStep(parent)
{
connect(&m_process, &LoggedProcess::log, this, &DirectJavaLaunch::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &DirectJavaLaunch::on_state);
}
void DirectJavaLaunch::executeTask()
{
auto instance = m_parent->instance();
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
QStringList args = minecraftInstance->javaArguments();
// HACK: this is a workaround for MCL-3732 - 'server-resource-packs' is created.
if(!FS::ensureFolderPathExists(FS::PathCombine(minecraftInstance->minecraftRoot(), "server-resource-packs")))
{
emit logLine(tr("Couldn't create the 'server-resource-packs' folder"), MessageLevel::Error);
}
args.append("-Djava.library.path=" + minecraftInstance->getNativePath());
auto classPathEntries = minecraftInstance->getClassPath();
args.append("-cp");
QString classpath;
#ifdef Q_OS_WIN32
classpath = classPathEntries.join(';');
#else
classpath = classPathEntries.join(':');
#endif
args.append(classpath);
args.append(minecraftInstance->getMainClass());
QString allArgs = args.join(", ");
emit logLine("Java Arguments:\n[" + m_parent->censorPrivateInfo(allArgs) + "]\n\n", MessageLevel::MultiMC);
auto javaPath = FS::ResolveExecutable(instance->settings()->get("JavaPath").toString());
m_process.setProcessEnvironment(instance->createEnvironment());
auto mcArgs = minecraftInstance->processMinecraftArgs(m_session);
args.append(mcArgs);
QString wrapperCommand = instance->getWrapperCommand();
if(!wrapperCommand.isEmpty())
{
auto realWrapperCommand = QStandardPaths::findExecutable(wrapperCommand);
if (realWrapperCommand.isEmpty())
{
QString reason = tr("The wrapper command \"%1\" couldn't be found.").arg(wrapperCommand);
emit logLine(reason, MessageLevel::Fatal);
emitFailed(reason);
return;
}
emit logLine("Wrapper command is:\n" + wrapperCommand + "\n\n", MessageLevel::MultiMC);
args.prepend(javaPath);
m_process.start(wrapperCommand, args);
}
else
{
m_process.start(javaPath, args);
}
}
void DirectJavaLaunch::on_state(LoggedProcess::State state)
{
switch(state)
{
case LoggedProcess::FailedToStart:
{
//: Error message displayed if instace can't start
QString reason = tr("Could not launch minecraft!");
emit logLine(reason, MessageLevel::Fatal);
emitFailed(reason);
return;
}
case LoggedProcess::Aborted:
case LoggedProcess::Crashed:
{
m_parent->setPid(-1);
emitFailed("Game crashed.");
return;
}
case LoggedProcess::Finished:
{
m_parent->setPid(-1);
// if the exit code wasn't 0, report this as a crash
auto exitCode = m_process.exitCode();
if(exitCode != 0)
{
emitFailed("Game crashed.");
return;
}
//FIXME: make this work again
// m_postlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(exitCode));
// run post-exit
emitSucceeded();
break;
}
case LoggedProcess::Running:
emit logLine(tr("Minecraft process ID: %1\n\n").arg(m_process.processId()), MessageLevel::MultiMC);
m_parent->setPid(m_process.processId());
m_parent->instance()->setLastLaunch();
break;
default:
break;
}
}
void DirectJavaLaunch::setWorkingDirectory(const QString &wd)
{
m_process.setWorkingDirectory(wd);
}
void DirectJavaLaunch::proceed()
{
// nil
}
bool DirectJavaLaunch::abort()
{
auto state = m_process.state();
if (state == LoggedProcess::Running || state == LoggedProcess::Starting)
{
m_process.kill();
}
return true;
}

View File

@ -0,0 +1,47 @@
/* Copyright 2013-2015 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 <launch/LoggedProcess.h>
#include <minecraft/auth/AuthSession.h>
class DirectJavaLaunch: public LaunchStep
{
Q_OBJECT
public:
explicit DirectJavaLaunch(LaunchTask *parent);
virtual void executeTask();
virtual bool abort();
virtual void proceed();
virtual bool canAbort() const
{
return true;
}
void setWorkingDirectory(const QString &wd);
void setAuthSession(AuthSessionPtr session)
{
m_session = session;
}
private slots:
void on_state(LoggedProcess::State state);
private:
LoggedProcess m_process;
QString m_command;
AuthSessionPtr m_session;
};

View File

@ -0,0 +1,86 @@
/* Copyright 2013-2016 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 "ExtractNatives.h"
#include <minecraft/MinecraftInstance.h>
#include <launch/LaunchTask.h>
#include <quazip.h>
#include <JlCompress.h>
#include <quazipdir.h>
#include "MMCZip.h"
#include "FileSystem.h"
#include <QDir>
static QString replaceSuffix (QString target, const QString &suffix, const QString &replacement)
{
if (!target.endsWith(suffix))
{
return target;
}
target.resize(target.length() - suffix.length());
return target + replacement;
}
static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack)
{
QuaZip zip(source);
if(!zip.open(QuaZip::mdUnzip))
{
return false;
}
QDir directory(targetFolder);
if (!zip.goToFirstFile())
{
return false;
}
do
{
QString name = zip.getCurrentFileName();
if(applyJnilibHack)
{
name = replaceSuffix(name, ".jnilib", ".dylib");
}
QString absFilePath = directory.absoluteFilePath(name);
if (!MMCZip::extractFile(&zip, "", absFilePath))
{
return false;
}
} while (zip.goToNextFile());
zip.close();
if(zip.getZipError()!=0)
{
return false;
}
return true;
}
void ExtractNatives::executeTask()
{
auto instance = m_parent->instance();
std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance);
auto outputPath = minecraftInstance->getNativePath();
auto toExtract = minecraftInstance->getNativeJars();
auto javaVersion = minecraftInstance->getJavaVersion();
bool jniHackEnabled = javaVersion.major() >= 8;
for(const auto &source: toExtract)
{
if(!unzipNatives(source, outputPath, jniHackEnabled))
{
emitFailed(tr("Couldn't extract native jar '%1' to destination '%2'").arg(source, outputPath));
}
}
emitSucceeded();
}

View File

@ -0,0 +1,37 @@
/* Copyright 2013-2016 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 <memory>
#include "minecraft/auth/AuthSession.h"
// FIXME: temporary wrapper for existing task.
class ExtractNatives: public LaunchStep
{
Q_OBJECT
public:
explicit ExtractNatives(LaunchTask *parent) : LaunchStep(parent){};
virtual ~ExtractNatives(){};
virtual void executeTask();
virtual bool canAbort() const
{
return false;
}
};

View File

@ -13,25 +13,25 @@
* limitations under the License. * limitations under the License.
*/ */
#include "LaunchMinecraft.h" #include "LauncherPartLaunch.h"
#include <QCoreApplication>
#include <launch/LaunchTask.h> #include <launch/LaunchTask.h>
#include <minecraft/MinecraftInstance.h> #include <minecraft/MinecraftInstance.h>
#include <FileSystem.h> #include <FileSystem.h>
#include <QStandardPaths> #include <QStandardPaths>
LaunchMinecraft::LaunchMinecraft(LaunchTask *parent) : LaunchStep(parent) LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
{ {
connect(&m_process, &LoggedProcess::log, this, &LaunchMinecraft::logLines); connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines);
connect(&m_process, &LoggedProcess::stateChanged, this, &LaunchMinecraft::on_state); connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state);
} }
void LaunchMinecraft::executeTask() 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);
QStringList args = minecraftInstance->javaArguments(); QStringList args = minecraftInstance->javaArguments();
// HACK: this is a workaround for MCL-3732 - 'server-resource-packs' is created. // HACK: this is a workaround for MCL-3732 - 'server-resource-packs' is created.
@ -47,6 +47,8 @@ void LaunchMinecraft::executeTask()
m_process.setProcessEnvironment(instance->createEnvironment()); m_process.setProcessEnvironment(instance->createEnvironment());
args << "-jar" << FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "NewLaunch.jar");
QString wrapperCommand = instance->getWrapperCommand(); QString wrapperCommand = instance->getWrapperCommand();
if(!wrapperCommand.isEmpty()) if(!wrapperCommand.isEmpty())
{ {
@ -68,7 +70,7 @@ void LaunchMinecraft::executeTask()
} }
} }
void LaunchMinecraft::on_state(LoggedProcess::State state) void LauncherPartLaunch::on_state(LoggedProcess::State state)
{ {
switch(state) switch(state)
{ {
@ -120,12 +122,12 @@ void LaunchMinecraft::on_state(LoggedProcess::State state)
} }
} }
void LaunchMinecraft::setWorkingDirectory(const QString &wd) void LauncherPartLaunch::setWorkingDirectory(const QString &wd)
{ {
m_process.setWorkingDirectory(wd); m_process.setWorkingDirectory(wd);
} }
void LaunchMinecraft::proceed() void LauncherPartLaunch::proceed()
{ {
if(mayProceed) if(mayProceed)
{ {
@ -135,7 +137,7 @@ void LaunchMinecraft::proceed()
} }
} }
bool LaunchMinecraft::abort() bool LauncherPartLaunch::abort()
{ {
if(mayProceed) if(mayProceed)
{ {

View File

@ -19,11 +19,11 @@
#include <launch/LoggedProcess.h> #include <launch/LoggedProcess.h>
#include <minecraft/auth/AuthSession.h> #include <minecraft/auth/AuthSession.h>
class LaunchMinecraft: public LaunchStep class LauncherPartLaunch: public LaunchStep
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit LaunchMinecraft(LaunchTask *parent); explicit LauncherPartLaunch(LaunchTask *parent);
virtual void executeTask(); virtual void executeTask();
virtual bool abort(); virtual bool abort();
virtual void proceed(); virtual void proceed();
@ -36,13 +36,14 @@ public:
{ {
m_session = session; m_session = session;
} }
private slots: private slots:
void on_state(LoggedProcess::State state); void on_state(LoggedProcess::State state);
private: private:
LoggedProcess m_process; LoggedProcess m_process;
QString m_command; QString m_command;
QString m_launchScript;
AuthSessionPtr m_session; AuthSessionPtr m_session;
QString m_launchScript;
bool mayProceed = false; bool mayProceed = false;
}; };

View File

@ -0,0 +1,25 @@
/* Copyright 2013-2016 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 "PrintInstanceInfo.h"
#include <launch/LaunchTask.h>
void PrintInstanceInfo::executeTask()
{
auto instance = m_parent->instance();
auto lines = instance->verboseDescription(m_session);
logLines(lines, MessageLevel::MultiMC);
emitSucceeded();
}

View File

@ -0,0 +1,38 @@
/* Copyright 2013-2016 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 <memory>
#include "minecraft/auth/AuthSession.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) {};
virtual ~PrintInstanceInfo(){};
virtual void executeTask();
virtual bool canAbort() const
{
return false;
}
private:
AuthSessionPtr m_session;
};

View File

@ -14,6 +14,7 @@
*/ */
#include <QFileInfo> #include <QFileInfo>
#include <minecraft/launch/LauncherPartLaunch.h>
#include <QDir> #include <QDir>
#include <settings/Setting.h> #include <settings/Setting.h>
@ -21,14 +22,6 @@
#include "minecraft/legacy/LegacyUpdate.h" #include "minecraft/legacy/LegacyUpdate.h"
#include "minecraft/legacy/LegacyModList.h" #include "minecraft/legacy/LegacyModList.h"
#include "launch/LaunchTask.h"
#include <launch/steps/PostLaunchCommand.h>
#include <launch/steps/Update.h>
#include <launch/steps/PreLaunchCommand.h>
#include <launch/steps/TextPrint.h>
#include "minecraft/launch/LaunchMinecraft.h"
#include "minecraft/launch/ModMinecraftJar.h"
#include "java/launch/CheckJava.h"
#include "minecraft/ModList.h" #include "minecraft/ModList.h"
#include "minecraft/WorldList.h" #include "minecraft/WorldList.h"
#include <MMCZip.h> #include <MMCZip.h>
@ -102,58 +95,6 @@ std::shared_ptr<Task> LegacyInstance::createUpdateTask()
return std::shared_ptr<Task>(new LegacyUpdate(this, this)); return std::shared_ptr<Task>(new LegacyUpdate(this, this));
} }
std::shared_ptr<LaunchTask> LegacyInstance::createLaunchTask(AuthSessionPtr session)
{
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr()));
auto pptr = process.get();
// print a header
{
process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + minecraftRoot() + "\n\n", MessageLevel::MultiMC));
}
{
auto step = std::make_shared<CheckJava>(pptr);
process->appendStep(step);
}
// run pre-launch command if that's needed
if(getPreLaunchCommand().size())
{
auto step = std::make_shared<PreLaunchCommand>(pptr);
step->setWorkingDirectory(minecraftRoot());
process->appendStep(step);
}
// if we aren't in offline mode,.
if(session->status != AuthSession::PlayableOffline)
{
process->appendStep(std::make_shared<Update>(pptr));
}
// if there are any jar mods
if(getJarMods().size())
{
auto step = std::make_shared<ModMinecraftJar>(pptr);
process->appendStep(step);
}
// actually launch the game
{
auto step = std::make_shared<LaunchMinecraft>(pptr);
step->setWorkingDirectory(minecraftRoot());
step->setAuthSession(session);
process->appendStep(step);
}
// run post-exit command if that's needed
if(getPostExitCommand().size())
{
auto step = std::make_shared<PostLaunchCommand>(pptr);
step->setWorkingDirectory(minecraftRoot());
process->appendStep(step);
}
if (session)
{
process->setCensorFilter(createCensorFilterFromSession(session));
}
return process;
}
std::shared_ptr<Task> LegacyInstance::createJarModdingTask() std::shared_ptr<Task> LegacyInstance::createJarModdingTask()
{ {
class JarModTask : public Task class JarModTask : public Task
@ -255,11 +196,35 @@ QString LegacyInstance::createLaunchScript(AuthSessionPtr session)
launchScript += "sessionId " + session->session + "\n"; launchScript += "sessionId " + session->session + "\n";
launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "windowTitle " + windowTitle() + "\n";
launchScript += "windowParams " + windowParams + "\n"; launchScript += "windowParams " + windowParams + "\n";
launchScript += "lwjgl " + lwjgl + "\n"; launchScript += "cp bin/minecraft.jar\n";
launchScript += "launcher legacy\n"; launchScript += "cp " + lwjgl + "/lwjgl.jar\n";
launchScript += "cp " + lwjgl + "/lwjgl_util.jar\n";
launchScript += "cp " + lwjgl + "/jinput.jar\n";
launchScript += "natives " + lwjgl + "/natives\n";
launchScript += "traits legacyLaunch\n";
launchScript += "launcher onesix\n";
return launchScript; return launchScript;
} }
std::shared_ptr<LaunchStep> LegacyInstance::createMainLaunchStep(LaunchTask * parent, AuthSessionPtr session)
{
auto step = std::make_shared<LauncherPartLaunch>(parent);
step->setWorkingDirectory(minecraftRoot());
step->setAuthSession(session);
return step;
}
QString LegacyInstance::launchMethod()
{
return "Legacy";
}
QStringList LegacyInstance::validLaunchMethods()
{
return {"Legacy"};
}
void LegacyInstance::cleanupAfterRun() void LegacyInstance::cleanupAfterRun()
{ {
// FIXME: delete the launcher and icons and whatnot. // FIXME: delete the launcher and icons and whatnot.
@ -452,3 +417,112 @@ QString LegacyInstance::typeName() const
{ {
return tr("Legacy"); return tr("Legacy");
} }
QStringList LegacyInstance::verboseDescription(AuthSessionPtr session)
{
QStringList out;
auto alltraits = traits();
if(alltraits.size())
{
out << "Traits:";
for (auto trait : alltraits)
{
out << " " + trait;
}
out << "";
}
if(loaderModList()->size())
{
out << "Mods:";
for(auto & mod: loaderModList()->allMods())
{
if(!mod.enabled())
continue;
if(mod.type() == Mod::MOD_FOLDER)
continue;
// TODO: proper implementation would need to descend into folders.
out << " " + mod.filename().completeBaseName();
}
out << "";
}
if(coreModList()->size())
{
out << "Core Mods:";
for(auto & coremod: coreModList()->allMods())
{
if(!coremod.enabled())
continue;
if(coremod.type() == Mod::MOD_FOLDER)
continue;
// TODO: proper implementation would need to descend into folders.
out << " " + coremod.filename().completeBaseName();
}
out << "";
}
if(jarModList()->size())
{
out << "Jar Mods:";
for(auto & jarmod: jarModList()->allMods())
{
out << " " + jarmod.name() + " (" + jarmod.filename().filePath() + ")";
}
out << "";
}
QString windowParams;
if (settings()->get("LaunchMaximized").toBool())
{
out << "Window size: max (if available)";
}
else
{
auto width = settings()->get("MinecraftWinWidth").toInt();
auto height = settings()->get("MinecraftWinHeight").toInt();
out << "Window size: " + QString::number(width) + " x " + QString::number(height);
}
out << "";
return out;
}
QStringList LegacyInstance::getClassPath() const
{
QString launchScript;
QString lwjgl = getNativePath();
QStringList out =
{
"bin/minecraft.jar",
lwjgl + "/lwjgl.jar",
lwjgl + "/lwjgl_util.jar",
lwjgl + "/jinput.jar"
};
return out;
}
QString LegacyInstance::getMainClass() const
{
return "net.minecraft.client.Minecraft";
}
QString LegacyInstance::getNativePath() const
{
return QDir(m_lwjglFolderSetting->get().toString() + "/" + lwjglVersion()).absolutePath();
}
QStringList LegacyInstance::getNativeJars() const
{
return {};
}
QStringList LegacyInstance::processMinecraftArgs(AuthSessionPtr account) const
{
QStringList out;
out.append(account->player_name);
out.append(account->session);
return out;
}

View File

@ -114,11 +114,7 @@ public:
virtual bool shouldUpdate() const override; virtual bool shouldUpdate() const override;
virtual void setShouldUpdate(bool val) override; virtual void setShouldUpdate(bool val) override;
virtual std::shared_ptr<Task> createUpdateTask() override; virtual std::shared_ptr<Task> createUpdateTask() override;
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
virtual std::shared_ptr<Task> createJarModdingTask() override; virtual std::shared_ptr<Task> createJarModdingTask() override;
virtual QString createLaunchScript(AuthSessionPtr session) override; virtual QString createLaunchScript(AuthSessionPtr session) override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;
@ -130,6 +126,20 @@ public:
return true; return true;
} }
QStringList getClassPath() const override;
QString getMainClass() const override;
QStringList getNativeJars() const override;
QString getNativePath() const override;
QStringList processMinecraftArgs(AuthSessionPtr account) const override;
QStringList verboseDescription(AuthSessionPtr session) override;
protected:
std::shared_ptr<LaunchStep> createMainLaunchStep(LaunchTask *parent, AuthSessionPtr session) override;
QStringList validLaunchMethods() override;
QString launchMethod() override;
protected: protected:
mutable std::shared_ptr<LegacyModList> jar_mod_list; mutable std::shared_ptr<LegacyModList> jar_mod_list;
mutable std::shared_ptr<ModList> core_mod_list; mutable std::shared_ptr<ModList> core_mod_list;

View File

@ -14,6 +14,8 @@
*/ */
#include <QDebug> #include <QDebug>
#include <minecraft/launch/DirectJavaLaunch.h>
#include <minecraft/launch/LauncherPartLaunch.h>
#include <Env.h> #include <Env.h>
#include "OneSixInstance.h" #include "OneSixInstance.h"
@ -22,14 +24,7 @@
#include "minecraft/MinecraftProfile.h" #include "minecraft/MinecraftProfile.h"
#include "minecraft/VersionBuildError.h" #include "minecraft/VersionBuildError.h"
#include "launch/LaunchTask.h"
#include "launch/steps/PreLaunchCommand.h"
#include "launch/steps/Update.h"
#include "launch/steps/PostLaunchCommand.h"
#include "launch/steps/TextPrint.h"
#include "minecraft/launch/LaunchMinecraft.h"
#include "minecraft/launch/ModMinecraftJar.h" #include "minecraft/launch/ModMinecraftJar.h"
#include "java/launch/CheckJava.h"
#include "MMCZip.h" #include "MMCZip.h"
#include "minecraft/AssetsUtils.h" #include "minecraft/AssetsUtils.h"
@ -94,7 +89,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result; return result;
} }
QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) const
{ {
QString args_pattern = m_profile->getMinecraftArguments(); QString args_pattern = m_profile->getMinecraftArguments();
for (auto tweaker : m_profile->getTweakers()) for (auto tweaker : m_profile->getTweakers())
@ -104,11 +99,16 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
QMap<QString, QString> token_mapping; QMap<QString, QString> token_mapping;
// yggdrasil! // yggdrasil!
token_mapping["auth_username"] = session->username; if(session)
token_mapping["auth_session"] = session->session; {
token_mapping["auth_access_token"] = session->access_token; token_mapping["auth_username"] = session->username;
token_mapping["auth_player_name"] = session->player_name; token_mapping["auth_session"] = session->session;
token_mapping["auth_uuid"] = session->uuid; token_mapping["auth_access_token"] = session->access_token;
token_mapping["auth_player_name"] = session->player_name;
token_mapping["auth_uuid"] = session->uuid;
token_mapping["user_properties"] = session->serializeUserProperties();
token_mapping["user_type"] = session->user_type;
}
// blatant self-promotion. // blatant self-promotion.
token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5"; token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
@ -127,9 +127,6 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
auto assets = m_profile->getMinecraftAssets(); auto assets = m_profile->getMinecraftAssets();
token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath(); token_mapping["game_assets"] = AssetsUtils::reconstructAssets(assets->id).absolutePath();
token_mapping["user_properties"] = session->serializeUserProperties();
token_mapping["user_type"] = session->user_type;
// 1.7.3+ assets tokens // 1.7.3+ assets tokens
token_mapping["assets_root"] = absAssetsDir; token_mapping["assets_root"] = absAssetsDir;
token_mapping["assets_index_name"] = assets->id; token_mapping["assets_index_name"] = assets->id;
@ -142,6 +139,26 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
return parts; return parts;
} }
QString OneSixInstance::getNativePath() const
{
QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/"));
return natives_dir.absolutePath();
}
QString OneSixInstance::mainJarPath() const
{
auto jarMods = getJarMods();
if (!jarMods.isEmpty())
{
return QDir(instanceRoot()).absoluteFilePath("minecraft.jar");
}
else
{
QString relpath = m_profile->getMinecraftVersion() + "/" + m_profile->getMinecraftVersion() + ".jar";
return versionsPath().absoluteFilePath(relpath);
}
}
QString OneSixInstance::createLaunchScript(AuthSessionPtr session) QString OneSixInstance::createLaunchScript(AuthSessionPtr session)
{ {
QString launchScript; QString launchScript;
@ -149,34 +166,7 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session)
if (!m_profile) if (!m_profile)
return nullptr; return nullptr;
for(auto & mod: loaderModList()->allMods()) auto mainClass = getMainClass();
{
if(!mod.enabled())
continue;
if(mod.type() == Mod::MOD_FOLDER)
continue;
// TODO: proper implementation would need to descend into folders.
launchScript += "mod " + mod.filename().completeBaseName() + "\n";;
}
for(auto & coremod: coreModList()->allMods())
{
if(!coremod.enabled())
continue;
if(coremod.type() == Mod::MOD_FOLDER)
continue;
// TODO: proper implementation would need to descend into folders.
launchScript += "coremod " + coremod.filename().completeBaseName() + "\n";;
}
for(auto & jarmod: m_profile->getJarMods())
{
launchScript += "jarmod " + jarmod->originalName + " (" + jarmod->name + ")\n";
}
auto mainClass = m_profile->getMainClass();
if (!mainClass.isEmpty()) if (!mainClass.isEmpty())
{ {
launchScript += "mainClass " + mainClass + "\n"; launchScript += "mainClass " + mainClass + "\n";
@ -207,6 +197,7 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session)
} }
// legacy auth // legacy auth
if(session)
{ {
launchScript += "userName " + session->player_name + "\n"; launchScript += "userName " + session->player_name + "\n";
launchScript += "sessionId " + session->session + "\n"; launchScript += "sessionId " + session->session + "\n";
@ -214,44 +205,21 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session)
// libraries and class path. // libraries and class path.
{ {
auto libs = m_profile->getLibraries(); QStringList jars, nativeJars;
auto javaArchitecture = settings()->get("JavaArchitecture").toString();
QStringList jar, native, native32, native64; m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars);
for (auto lib : libs) for(auto file: jars)
{
lib->getApplicableFiles(currentSystem, jar, native, native32, native64);
}
for(auto file: jar)
{ {
launchScript += "cp " + file + "\n"; launchScript += "cp " + file + "\n";
} }
for(auto file: native) launchScript += "cp " + mainJarPath() + "\n";
for(auto file: nativeJars)
{ {
launchScript += "ext " + file + "\n"; launchScript += "ext " + file + "\n";
} }
for(auto file: native32) launchScript += "natives " + getNativePath() + "\n";
{
launchScript += "ext32 " + file + "\n";
}
for(auto file: native64)
{
launchScript += "ext64 " + file + "\n";
}
QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/"));
launchScript += "natives " + natives_dir.absolutePath() + "\n";
auto jarMods = getJarMods();
if (!jarMods.isEmpty())
{
launchScript += "cp " + QDir(instanceRoot()).absoluteFilePath("minecraft.jar") + "\n";
}
else
{
QString relpath = m_profile->getMinecraftVersion() + "/" + m_profile->getMinecraftVersion() + ".jar";
launchScript += "cp " + versionsPath().absoluteFilePath(relpath) + "\n";
}
} }
// traits. including legacyLaunch and others ;)
for (auto trait : m_profile->getTraits()) for (auto trait : m_profile->getTraits())
{ {
launchScript += "traits " + trait + "\n"; launchScript += "traits " + trait + "\n";
@ -260,58 +228,139 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session)
return launchScript; return launchScript;
} }
std::shared_ptr<LaunchTask> OneSixInstance::createLaunchTask(AuthSessionPtr session) QStringList OneSixInstance::verboseDescription(AuthSessionPtr session)
{ {
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr())); QStringList out;
auto pptr = process.get(); out << "Main Class:" << " " + getMainClass() << "";
out << "Native path:" << " " + getNativePath() << "";
// print a header
auto alltraits = traits();
if(alltraits.size())
{ {
process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + minecraftRoot() + "\n\n", MessageLevel::MultiMC)); out << "Traits:";
for (auto trait : alltraits)
{
out << "traits " + trait;
}
out << "";
} }
// libraries and class path.
{ {
auto step = std::make_shared<CheckJava>(pptr); out << "Libraries:";
process->appendStep(step); QStringList jars, nativeJars;
auto javaArchitecture = settings()->get("JavaArchitecture").toString();
m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars);
auto printLibFile = [&](const QString & path)
{
QFileInfo info(path);
if(info.exists())
{
out << " " + path;
}
else
{
out << " " + path + " (missing)";
}
};
for(auto file: jars)
{
printLibFile(file);
}
printLibFile(mainJarPath());
for(auto file: nativeJars)
{
printLibFile(file);
}
out << "";
} }
// run pre-launch command if that's needed
if(getPreLaunchCommand().size()) if(loaderModList()->size())
{ {
auto step = std::make_shared<PreLaunchCommand>(pptr); out << "Mods:";
for(auto & mod: loaderModList()->allMods())
{
if(!mod.enabled())
continue;
if(mod.type() == Mod::MOD_FOLDER)
continue;
// TODO: proper implementation would need to descend into folders.
out << " " + mod.filename().completeBaseName();
}
out << "";
}
if(coreModList()->size())
{
out << "Core Mods:";
for(auto & coremod: coreModList()->allMods())
{
if(!coremod.enabled())
continue;
if(coremod.type() == Mod::MOD_FOLDER)
continue;
// TODO: proper implementation would need to descend into folders.
out << " " + coremod.filename().completeBaseName();
}
out << "";
}
auto & jarMods = m_profile->getJarMods();
if(jarMods.size())
{
out << "Jar Mods:";
for(auto & jarmod: jarMods)
{
out << " " + jarmod->originalName + " (" + jarmod->name + ")";
}
out << "";
}
auto params = processMinecraftArgs(nullptr);
out << "Params:";
out << " " + params.join(' ');
out << "";
QString windowParams;
if (settings()->get("LaunchMaximized").toBool())
{
out << "Window size: max (if available)";
}
else
{
auto width = settings()->get("MinecraftWinWidth").toInt();
auto height = settings()->get("MinecraftWinHeight").toInt();
out << "Window size: " + QString::number(width) + " x " + QString::number(height);
}
out << "";
return out;
}
std::shared_ptr<LaunchStep> OneSixInstance::createMainLaunchStep(LaunchTask * parent, AuthSessionPtr session)
{
auto method = launchMethod();
if(method == "LauncherPart")
{
auto step = std::make_shared<LauncherPartLaunch>(parent);
step->setAuthSession(session);
step->setWorkingDirectory(minecraftRoot()); step->setWorkingDirectory(minecraftRoot());
process->appendStep(step); return step;
} }
// if we aren't in offline mode,. else if (method == "DirectJava")
if(session->status != AuthSession::PlayableOffline)
{ {
process->appendStep(std::make_shared<Update>(pptr)); auto step = std::make_shared<DirectJavaLaunch>(parent);
}
// if there are any jar mods
if(getJarMods().size())
{
auto step = std::make_shared<ModMinecraftJar>(pptr);
process->appendStep(step);
}
// actually launch the game
{
auto step = std::make_shared<LaunchMinecraft>(pptr);
step->setWorkingDirectory(minecraftRoot()); step->setWorkingDirectory(minecraftRoot());
step->setAuthSession(session); step->setAuthSession(session);
process->appendStep(step); return step;
} }
// run post-exit command if that's needed return nullptr;
if(getPostExitCommand().size())
{
auto step = std::make_shared<PostLaunchCommand>(pptr);
step->setWorkingDirectory(minecraftRoot());
process->appendStep(step);
}
if (session)
{
process->setCensorFilter(createCensorFilterFromSession(session));
}
return process;
} }
std::shared_ptr<Task> OneSixInstance::createJarModdingTask() std::shared_ptr<Task> OneSixInstance::createJarModdingTask()
{ {
class JarModTask : public Task class JarModTask : public Task
@ -595,3 +644,30 @@ QString OneSixInstance::typeName() const
{ {
return tr("OneSix"); return tr("OneSix");
} }
QStringList OneSixInstance::validLaunchMethods()
{
return {"LauncherPart", "DirectJava"};
}
QStringList OneSixInstance::getClassPath() const
{
QStringList jars, nativeJars;
auto javaArchitecture = settings()->get("JavaArchitecture").toString();
m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars);
jars.append(mainJarPath());
return jars;
}
QString OneSixInstance::getMainClass() const
{
return m_profile->getMainClass();
}
QStringList OneSixInstance::getNativeJars() const
{
QStringList jars, nativeJars;
auto javaArchitecture = settings()->get("JavaArchitecture").toString();
m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars);
return nativeJars;
}

View File

@ -53,10 +53,9 @@ public:
virtual QString instanceConfigFolder() const override; virtual QString instanceConfigFolder() const override;
virtual std::shared_ptr<Task> createUpdateTask() override; virtual std::shared_ptr<Task> createUpdateTask() override;
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
virtual std::shared_ptr<Task> createJarModdingTask() override; virtual std::shared_ptr<Task> createJarModdingTask() override;
virtual QString createLaunchScript(AuthSessionPtr session) override; virtual QString createLaunchScript(AuthSessionPtr session) override;
QStringList verboseDescription(AuthSessionPtr session) override;
virtual void cleanupAfterRun() override; virtual void cleanupAfterRun() override;
@ -99,11 +98,23 @@ public:
return true; return true;
} }
QStringList getClassPath() const override;
QString getMainClass() const override;
QStringList getNativeJars() const override;
QString getNativePath() const override;
QStringList processMinecraftArgs(AuthSessionPtr account) const override;
protected:
std::shared_ptr<LaunchStep> createMainLaunchStep(LaunchTask *parent, AuthSessionPtr session) override;
QStringList validLaunchMethods() override;
signals: signals:
void versionReloaded(); void versionReloaded();
private: private:
QStringList processMinecraftArgs(AuthSessionPtr account); QString mainJarPath() const;
protected: protected:
std::shared_ptr<MinecraftProfile> m_profile; std::shared_ptr<MinecraftProfile> m_profile;

View File

@ -524,11 +524,15 @@ void MultiMC::initGlobalSettings(bool test_mode)
// Java Settings // Java Settings
m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaPath", "");
m_settings->registerSetting("JavaTimestamp", 0); m_settings->registerSetting("JavaTimestamp", 0);
m_settings->registerSetting("JavaArchitecture", "");
m_settings->registerSetting("JavaVersion", ""); m_settings->registerSetting("JavaVersion", "");
m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("LastHostname", "");
m_settings->registerSetting("JavaDetectionHack", ""); m_settings->registerSetting("JavaDetectionHack", "");
m_settings->registerSetting("JvmArgs", ""); m_settings->registerSetting("JvmArgs", "");
// Minecraft launch method
m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
// Wrapper command for launch // Wrapper command for launch
m_settings->registerSetting("WrapperCommand", ""); m_settings->registerSetting("WrapperCommand", "");

View File

@ -17,7 +17,6 @@ set(SRC
# legacy applet wrapper thing. # legacy applet wrapper thing.
# The launcher has to be there for silly FML/Forge relauncher. # The launcher has to be there for silly FML/Forge relauncher.
net/minecraft/Launcher.java net/minecraft/Launcher.java
org/multimc/legacy/LegacyLauncher.java
org/multimc/LegacyFrame.java org/multimc/LegacyFrame.java
# onesix launcher # onesix launcher

View File

@ -14,7 +14,6 @@ package org.multimc;/*
* limitations under the License. * limitations under the License.
*/ */
import org.multimc.legacy.LegacyLauncher;
import org.multimc.onesix.OneSixLauncher; import org.multimc.onesix.OneSixLauncher;
import org.simplericity.macify.eawt.Application; import org.simplericity.macify.eawt.Application;
import org.simplericity.macify.eawt.DefaultApplication; import org.simplericity.macify.eawt.DefaultApplication;
@ -83,13 +82,6 @@ public class EntryPoint
if(command.equals("launcher")) if(command.equals("launcher"))
{ {
if(param.equals("legacy"))
{
m_launcher = new LegacyLauncher();
Utils.log("Using legacy launcher.");
Utils.log();
return Action.Proceed;
}
if(param.equals("onesix")) if(param.equals("onesix"))
{ {
m_launcher = new OneSixLauncher(); m_launcher = new OneSixLauncher();

View File

@ -186,95 +186,5 @@ public class Utils
{ {
System.out.println(); System.out.println();
} }
/**
* Pushes bytes from in to out. Closes both streams no matter what.
* @param in the input stream
* @param out the output stream
* @throws IOException
*/
private static void copyStream(InputStream in, OutputStream out) throws IOException
{
try
{
byte[] buffer = new byte[4096];
int len;
while((len = in.read(buffer)) >= 0)
out.write(buffer, 0, len);
} finally
{
in.close();
out.close();
}
}
/**
* Replace a 'target' string 'suffix' with 'replacement'
*/
public static String replaceSuffix (String target, String suffix, String replacement)
{
if (!target.endsWith(suffix))
{
return target;
}
String prefix = target.substring(0, target.length() - suffix.length());
return prefix + replacement;
}
/**
* Unzip zip file with natives 'source' into the folder 'targetFolder'
*
* Contains a hack for OSX. Yay.
* @param source
* @param targetFolder
* @throws IOException
*/
public static void unzipNatives(File source, File targetFolder) throws IOException
{
ZipFile zip = new ZipFile(source);
boolean applyHacks = false;
String[] javaVersionElements = System.getProperty("java.version").split("[.\\-+]");
int major = Integer.parseInt(javaVersionElements[0]);
if(major == 1)
{
major = Integer.parseInt(javaVersionElements[1]);
}
if (major >= 8)
{
applyHacks = true;
}
try
{
Enumeration entries = zip.entries();
while (entries.hasMoreElements())
{
ZipEntry entry = (ZipEntry) entries.nextElement();
String entryName = entry.getName();
String fileName = entryName;
if(applyHacks)
{
fileName = replaceSuffix(entryName, ".jnilib", ".dylib");
}
File targetFile = new File(targetFolder, fileName);
if (targetFile.getParentFile() != null)
{
targetFile.getParentFile().mkdirs();
}
if (entry.isDirectory())
continue;
copyStream(zip.getInputStream(entry), new BufferedOutputStream(new FileOutputStream(targetFile)));
}
} finally
{
zip.close();
}
}
} }

View File

@ -1,175 +0,0 @@
package org.multimc.legacy;/*
* Copyright 2012-2014 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.
*/
import org.multimc.*;
import java.applet.Applet;
import java.awt.*;
import java.io.File;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class LegacyLauncher implements Launcher
{
@Override
public int launch(ParamBucket params)
{
String userName, sessionId, windowTitle, windowParams, lwjgl;
String mainClass = "net.minecraft.client.Minecraft";
try
{
userName = params.first("userName");
sessionId = params.first("sessionId");
windowTitle = params.first("windowTitle");
windowParams = params.first("windowParams");
lwjgl = params.first("lwjgl");
} catch (NotFoundException e)
{
System.err.println("Not enough arguments.");
return -1;
}
String cwd = System.getProperty("user.dir");
Dimension winSize = new Dimension(854, 480);
boolean maximize = false;
String[] dimStrings = windowParams.split("x");
if (windowParams.equalsIgnoreCase("max"))
{
maximize = true;
}
else if (dimStrings.length == 2)
{
try
{
winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1]));
} catch (NumberFormatException ignored) {}
}
File binDir = new File(cwd, "bin");
File lwjglDir;
if (lwjgl.equalsIgnoreCase("Mojang"))
{
lwjglDir = binDir;
}
else
{
lwjglDir = new File(lwjgl);
}
URL[] classpath;
{
try
{
classpath = new URL[]
{
new File(binDir, "minecraft.jar").toURI().toURL(),
new File(lwjglDir, "lwjgl.jar").toURI().toURL(),
new File(lwjglDir, "lwjgl_util.jar").toURI().toURL(),
new File(lwjglDir, "jinput.jar").toURI().toURL(),
};
} catch (MalformedURLException e)
{
System.err.println("Class path entry is badly formed:");
e.printStackTrace(System.err);
return -1;
}
}
String nativesDir = new File(lwjglDir, "natives").toString();
System.setProperty("org.lwjgl.librarypath", nativesDir);
System.setProperty("net.java.games.input.librarypath", nativesDir);
// print the pretty things
{
Utils.log("Main Class:");
Utils.log(" " + mainClass);
Utils.log();
Utils.log("Class Path:");
for (URL s : classpath)
{
Utils.log(" " + s);
}
Utils.log();
Utils.log("Native Path:");
Utils.log(" " + nativesDir);
Utils.log();
}
URLClassLoader cl = new URLClassLoader(classpath, LegacyLauncher.class.getClassLoader());
// Get the Minecraft Class and set the base folder
Class<?> mc;
try
{
mc = cl.loadClass(mainClass);
Field f = Utils.getMCPathField(mc);
if (f == null)
{
System.err.println("Could not find Minecraft path field. Launch failed.");
return -1;
}
f.setAccessible(true);
f.set(null, new File(cwd));
} catch (Exception e)
{
System.err.println("Could not set base folder. Failed to find/access Minecraft main class:");
e.printStackTrace(System.err);
return -1;
}
System.setProperty("minecraft.applet.TargetDirectory", cwd);
String[] mcArgs = new String[2];
mcArgs[0] = userName;
mcArgs[1] = sessionId;
Utils.log("Launching with applet wrapper...");
try
{
Class<?> MCAppletClass = cl.loadClass("net.minecraft.client.MinecraftApplet");
Applet mcappl = (Applet) MCAppletClass.newInstance();
LegacyFrame mcWindow = new LegacyFrame(windowTitle);
mcWindow.start(mcappl, userName, sessionId, winSize, maximize);
} catch (Exception e)
{
Utils.log("Applet wrapper failed:", "Error");
e.printStackTrace(System.err);
Utils.log();
Utils.log("Falling back to compatibility mode.");
try
{
mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
} catch (Exception e1)
{
Utils.log("Failed to invoke the Minecraft main class:", "Fatal");
e1.printStackTrace(System.err);
return -1;
}
}
return 0;
}
}

View File

@ -30,9 +30,6 @@ public class OneSixLauncher implements Launcher
{ {
// parameters, separated from ParamBucket // parameters, separated from ParamBucket
private List<String> libraries; private List<String> libraries;
private List<String> extlibs;
private List<String> extlibs32;
private List<String> extlibs64;
private List<String> mcparams; private List<String> mcparams;
private List<String> mods; private List<String> mods;
private List<String> jarmods; private List<String> jarmods;
@ -56,28 +53,9 @@ public class OneSixLauncher implements Launcher
private void processParams(ParamBucket params) throws NotFoundException private void processParams(ParamBucket params) throws NotFoundException
{ {
libraries = params.all("cp"); libraries = params.all("cp");
extlibs = params.allSafe("ext", new ArrayList<String>());
extlibs32 = params.allSafe("ext32", new ArrayList<String>());
extlibs64 = params.allSafe("ext64", new ArrayList<String>());
// Unify the extracted native libs according to actual system architecture
String property = System.getProperty("os.arch");
boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64");
if(is_64)
{
extlibs.addAll(extlibs64);
}
else
{
extlibs.addAll(extlibs32);
}
mcparams = params.allSafe("param", new ArrayList<String>() ); mcparams = params.allSafe("param", new ArrayList<String>() );
mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft");
appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet");
mods = params.allSafe("mod", new ArrayList<String>());
jarmods = params.allSafe("jarmod", new ArrayList<String>());
coremods = params.allSafe("coremod", new ArrayList<String>());
traits = params.allSafe("traits", new ArrayList<String>()); traits = params.allSafe("traits", new ArrayList<String>());
nativePath = params.first("natives"); nativePath = params.first("natives");
@ -105,75 +83,6 @@ public class OneSixLauncher implements Launcher
} }
} }
private void printStats()
{
Utils.log("Main Class:");
Utils.log(" " + mainClass);
Utils.log();
Utils.log("Native path:");
Utils.log(" " + nativePath);
Utils.log();
Utils.log("Traits:");
Utils.log(" " + traits);
Utils.log();
Utils.log("Libraries:");
for (String s : libraries)
{
File f = new File(s);
if (f.exists())
{
Utils.log(" " + s);
}
else
{
Utils.log(" " + s + " (missing)", "Warning");
}
}
Utils.log();
if(mods.size() > 0)
{
Utils.log("Mods:");
for (String s : mods)
{
Utils.log(" " + s);
}
Utils.log();
}
if(coremods.size() > 0)
{
Utils.log("Core Mods:");
for (String s : coremods)
{
Utils.log(" " + s);
}
Utils.log();
}
if(jarmods.size() > 0)
{
Utils.log("Jar Mods:");
for (String s : jarmods)
{
Utils.log(" " + s);
}
Utils.log();
}
Utils.log("Params:");
Utils.log(" " + mcparams.toString());
Utils.log();
if(maximize)
Utils.log("Window size: max (if available)");
else
Utils.log("Window size: " + Integer.toString(winSize.width) + " x " + Integer.toString(winSize.height));
Utils.log();
}
int legacyLaunch() int legacyLaunch()
{ {
// Get the Minecraft Class and set the base folder // Get the Minecraft Class and set the base folder
@ -310,27 +219,6 @@ public class OneSixLauncher implements Launcher
return -1; return -1;
} }
// print the pretty things
printStats();
// extract native libs (depending on platform here... java!)
Utils.log("Preparing native libraries...");
for(String extlib: extlibs)
{
try
{
File extlibf = new File(extlib);
Utils.log("Extracting " + extlibf.getName());
Utils.unzipNatives(extlibf, new File(nativePath));
} catch (IOException e)
{
System.err.println("Failed to extract native library:");
e.printStackTrace(System.err);
return -1;
}
}
Utils.log();
// set the native libs path... the brute force way // set the native libs path... the brute force way
try try
{ {

View File

@ -68,6 +68,7 @@ void WonkoClient::initGlobalSettings()
// Java Settings // Java Settings
m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaPath", "");
m_settings->registerSetting("JavaTimestamp", 0); m_settings->registerSetting("JavaTimestamp", 0);
m_settings->registerSetting("JavaArchitecture", "");
m_settings->registerSetting("JavaVersion", ""); m_settings->registerSetting("JavaVersion", "");
m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("LastHostname", "");
m_settings->registerSetting("JavaDetectionHack", ""); m_settings->registerSetting("JavaDetectionHack", "");
@ -79,4 +80,7 @@ void WonkoClient::initGlobalSettings()
// Custom Commands // Custom Commands
m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, ""); m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, "");
m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, ""); m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, "");
// Minecraft launch method
m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
} }