diff options
author | Petr Mrázek <peterix@gmail.com> | 2016-06-16 02:20:23 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2016-08-01 21:15:08 +0200 |
commit | 1f2bed2ef119094bdc156aa3a206b93dea5081d1 (patch) | |
tree | f154e39d3de3a3d71ed868cf396c4361b04e75c5 | |
parent | 57c84ec2b15d8aa6985681f79641f5989c2f049f (diff) | |
download | MultiMC-1f2bed2ef119094bdc156aa3a206b93dea5081d1.tar MultiMC-1f2bed2ef119094bdc156aa3a206b93dea5081d1.tar.gz MultiMC-1f2bed2ef119094bdc156aa3a206b93dea5081d1.tar.lz MultiMC-1f2bed2ef119094bdc156aa3a206b93dea5081d1.tar.xz MultiMC-1f2bed2ef119094bdc156aa3a206b93dea5081d1.zip |
NOISSUE implement direct java launch
Just running the Java process and giving it params on the command line
31 files changed, 983 insertions, 620 deletions
diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index 5e587c48..f0fb6096 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -210,6 +210,11 @@ public: virtual bool reload(); + /** + * 'print' a verbose desription of the instance into a QStringList + */ + virtual QStringList verboseDescription(AuthSessionPtr session) = 0; + signals: /*! * \brief Signal emitted when properties relevant to the instance view change diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 1048e091..87908363 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -202,8 +202,14 @@ set(MINECRAFT_SOURCES minecraft/onesix/OneSixVersionFormat.h minecraft/launch/ModMinecraftJar.cpp minecraft/launch/ModMinecraftJar.h - minecraft/launch/LaunchMinecraft.cpp - minecraft/launch/LaunchMinecraft.h + minecraft/launch/DirectJavaLaunch.cpp + 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.cpp minecraft/legacy/LegacyUpdate.h diff --git a/api/logic/NullInstance.h b/api/logic/NullInstance.h index fbb2d985..d87fb6f7 100644 --- a/api/logic/NullInstance.h +++ b/api/logic/NullInstance.h @@ -87,4 +87,10 @@ public: { return false; } + QStringList verboseDescription(AuthSessionPtr session) override + { + QStringList out; + out << "Null instance - placeholder."; + return out; + } }; diff --git a/api/logic/java/JavaVersion.cpp b/api/logic/java/JavaVersion.cpp index 84fc48a4..8c1bb430 100644 --- a/api/logic/java/JavaVersion.cpp +++ b/api/logic/java/JavaVersion.cpp @@ -6,7 +6,7 @@ JavaVersion & JavaVersion::operator=(const QString & javaVersionString) { - string = javaVersionString; + m_string = javaVersionString; 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]+))?"); } - auto match = pattern.match(string); - parseable = match.hasMatch(); - major = getCapturedInteger(match, "major"); - minor = getCapturedInteger(match, "minor"); - security = getCapturedInteger(match, "security"); - prerelease = match.captured("prerelease"); + auto match = pattern.match(m_string); + m_parseable = match.hasMatch(); + m_major = getCapturedInteger(match, "major"); + m_minor = getCapturedInteger(match, "minor"); + m_security = getCapturedInteger(match, "security"); + m_prerelease = match.captured("prerelease"); return *this; } @@ -44,38 +44,38 @@ JavaVersion::JavaVersion(const QString &rhs) QString JavaVersion::toString() { - return string; + return m_string; } bool JavaVersion::requiresPermGen() { - if(parseable) + if(m_parseable) { - return major < 8; + return m_major < 8; } return true; } 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; - if(major > rhs.major) + if(m_major > rhs.m_major) return false; - if(minor < rhs.minor) + if(m_minor < rhs.m_minor) return true; - if(minor > rhs.minor) + if(m_minor > rhs.m_minor) return false; - if(security < rhs.security) + if(m_security < rhs.m_security) return true; - if(security > rhs.security) + if(m_security > rhs.m_security) return false; // everything else being equal, consider prerelease status - bool thisPre = !prerelease.isEmpty(); - bool rhsPre = !rhs.prerelease.isEmpty(); + bool thisPre = !m_prerelease.isEmpty(); + bool rhsPre = !rhs.m_prerelease.isEmpty(); if(thisPre && !rhsPre) { // 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) { // 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 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) { - 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) diff --git a/api/logic/java/JavaVersion.h b/api/logic/java/JavaVersion.h index f9a733d3..deba5654 100644 --- a/api/logic/java/JavaVersion.h +++ b/api/logic/java/JavaVersion.h @@ -20,11 +20,23 @@ public: QString toString(); + int major() + { + return m_major; + } + int minor() + { + return m_minor; + } + int security() + { + return m_security; + } private: - QString string; - int major = 0; - int minor = 0; - int security = 0; - bool parseable = false; - QString prerelease; + QString m_string; + int m_major = 0; + int m_minor = 0; + int m_security = 0; + bool m_parseable = false; + QString m_prerelease; }; diff --git a/api/logic/java/JavaVersion_test.cpp b/api/logic/java/JavaVersion_test.cpp index 9dae0ba6..e719ffc8 100644 --- a/api/logic/java/JavaVersion_test.cpp +++ b/api/logic/java/JavaVersion_test.cpp @@ -34,12 +34,12 @@ slots: QFETCH(QString, prerelease); JavaVersion test(string); - QCOMPARE(test.string, string); + QCOMPARE(test.m_string, string); QCOMPARE(test.toString(), string); - QCOMPARE(test.major, major); - QCOMPARE(test.minor, minor); - QCOMPARE(test.security, security); - QCOMPARE(test.prerelease, prerelease); + QCOMPARE(test.m_major, major); + QCOMPARE(test.m_minor, minor); + QCOMPARE(test.m_security, security); + QCOMPARE(test.m_prerelease, prerelease); } void test_Sort_data() diff --git a/api/logic/java/launch/CheckJava.cpp b/api/logic/java/launch/CheckJava.cpp index a4eaa307..41bb6398 100644 --- a/api/logic/java/launch/CheckJava.cpp +++ b/api/logic/java/launch/CheckJava.cpp @@ -53,20 +53,25 @@ void CheckJava::executeTask() QFileInfo javaInfo(realJavaPath); qlonglong javaUnixTime = javaInfo.lastModified().toMSecsSinceEpoch(); auto storedUnixTime = settings->get("JavaTimestamp").toLongLong(); + auto storedArchitecture = settings->get("JavaArchitecture").toString(); + auto storedVersion = settings->get("JavaVersion").toString(); m_javaUnixTime = javaUnixTime; - // if they are not the same, check! - if (javaUnixTime != storedUnixTime) + // if timestamps are not the same, or something is missing, check! + if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0) { m_JavaChecker = std::make_shared<JavaChecker>(); - QString errorLog; - QString version; emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC); - connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, - &CheckJava::checkJavaFinished); + connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished); m_JavaChecker->m_path = realJavaPath; m_JavaChecker->performCheck(); return; } + else + { + auto verString = instance->settings()->get("JavaVersion").toString(); + auto archString = instance->settings()->get("JavaArchitecture").toString(); + printJavaInfo(verString, archString); + } emitSucceeded(); } @@ -83,10 +88,15 @@ void CheckJava::checkJavaFinished(JavaCheckResult result) else { auto instance = m_parent->instance(); - emit logLine(tr("Java version is %1!\n").arg(result.javaVersion.toString()), - MessageLevel::MultiMC); + printJavaInfo(result.javaVersion.toString(), result.mojangPlatform); instance->settings()->set("JavaVersion", result.javaVersion.toString()); + instance->settings()->set("JavaArchitecture", result.mojangPlatform); instance->settings()->set("JavaTimestamp", m_javaUnixTime); 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); +} diff --git a/api/logic/java/launch/CheckJava.h b/api/logic/java/launch/CheckJava.h index b63dd4f4..118da225 100644 --- a/api/logic/java/launch/CheckJava.h +++ b/api/logic/java/launch/CheckJava.h @@ -35,6 +35,9 @@ private slots: void checkJavaFinished(JavaCheckResult result); private: + void printJavaInfo(const QString & version, const QString & architecture); + +private: QString m_javaPath; qlonglong m_javaUnixTime; JavaCheckerPtr m_JavaChecker; diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index 405ccd26..8cc4f805 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -1,4 +1,6 @@ #include "MinecraftInstance.h" +#include <minecraft/launch/ExtractNatives.h> +#include <minecraft/launch/PrintInstanceInfo.h> #include <settings/Setting.h> #include "settings/SettingsObject.h" #include "Env.h" @@ -9,6 +11,15 @@ #include <FileSystem.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" // all of this because keeping things compatible with deprecated old settings @@ -52,6 +63,7 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO // special! m_settings->registerPassthrough(globalSettings->getSetting("JavaTimestamp"), javaOrLocation); m_settings->registerPassthrough(globalSettings->getSetting("JavaVersion"), javaOrLocation); + m_settings->registerPassthrough(globalSettings->getSetting("JavaArchitecture"), javaOrLocation); // Window Size 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("MaxMemAlloc"), 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 @@ -105,7 +121,7 @@ QStringList MinecraftInstance::javaArguments() const args << QString("-Xmx%1m").arg(settings()->get("MaxMemAlloc").toInt()); // No PermGen in newer java. - JavaVersion javaVersion(settings()->get("JavaVersion").toString()); + JavaVersion javaVersion = getJavaVersion(); if(javaVersion.requiresPermGen()) { auto permgen = settings()->get("PermGen").toInt(); @@ -116,7 +132,6 @@ QStringList MinecraftInstance::javaArguments() const } args << "-Duser.language=en"; - args << "-jar" << FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "NewLaunch.jar"); return args; } @@ -366,4 +381,95 @@ QString MinecraftInstance::getStatusbarDescription() 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" diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h index cd3a8d90..3aef2969 100644 --- a/api/logic/minecraft/MinecraftInstance.h +++ b/api/logic/minecraft/MinecraftInstance.h @@ -1,5 +1,6 @@ #pragma once #include "BaseInstance.h" +#include <java/JavaVersion.h> #include "minecraft/Mod.h" #include <QProcess> @@ -7,6 +8,7 @@ class ModList; class WorldList; +class LaunchStep; class MULTIMC_LOGIC_EXPORT MinecraftInstance: public BaseInstance { @@ -36,7 +38,7 @@ public: 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; //FIXME: nuke? @@ -60,8 +62,22 @@ public: 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: 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: QString prettifyTimeDuration(int64_t duration); }; diff --git a/api/logic/minecraft/MinecraftProfile.cpp b/api/logic/minecraft/MinecraftProfile.cpp index 70d0cee4..19127a54 100644 --- a/api/logic/minecraft/MinecraftProfile.cpp +++ b/api/logic/minecraft/MinecraftProfile.cpp @@ -573,6 +573,26 @@ const QList<LibraryPtr> & MinecraftProfile::getLibraries() const 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 { auto iter = mojangDownloads.find("client"); diff --git a/api/logic/minecraft/MinecraftProfile.h b/api/logic/minecraft/MinecraftProfile.h index ca9288ad..cc9b89b1 100644 --- a/api/logic/minecraft/MinecraftProfile.h +++ b/api/logic/minecraft/MinecraftProfile.h @@ -110,6 +110,7 @@ public: /* getters for profile variables */ const QStringList & getTweakers() const; const QList<JarmodPtr> & getJarMods() const; const QList<LibraryPtr> & getLibraries() const; + void getLibraryFiles(const QString & architecture, QStringList & jars, QStringList & nativeJars) const; QString getMainJarUrl() const; bool hasTrait(const QString & trait) const; ProblemSeverity getProblemSeverity() const; diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.cpp b/api/logic/minecraft/launch/DirectJavaLaunch.cpp new file mode 100644 index 00000000..c46cdcd4 --- /dev/null +++ b/api/logic/minecraft/launch/DirectJavaLaunch.cpp @@ -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; +} + diff --git a/api/logic/minecraft/launch/DirectJavaLaunch.h b/api/logic/minecraft/launch/DirectJavaLaunch.h new file mode 100644 index 00000000..0e3d66cc --- /dev/null +++ b/api/logic/minecraft/launch/DirectJavaLaunch.h @@ -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; +}; + diff --git a/api/logic/minecraft/launch/ExtractNatives.cpp b/api/logic/minecraft/launch/ExtractNatives.cpp new file mode 100644 index 00000000..089e2559 --- /dev/null +++ b/api/logic/minecraft/launch/ExtractNatives.cpp @@ -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(); +} diff --git a/api/logic/minecraft/launch/ExtractNatives.h b/api/logic/minecraft/launch/ExtractNatives.h new file mode 100644 index 00000000..699657d7 --- /dev/null +++ b/api/logic/minecraft/launch/ExtractNatives.h @@ -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; + } +}; + + diff --git a/api/logic/minecraft/launch/LaunchMinecraft.cpp b/api/logic/minecraft/launch/LauncherPartLaunch.cpp index 9b8cc0fb..5f233700 100644 --- a/api/logic/minecraft/launch/LaunchMinecraft.cpp +++ b/api/logic/minecraft/launch/LauncherPartLaunch.cpp @@ -13,25 +13,25 @@ * limitations under the License. */ -#include "LaunchMinecraft.h" +#include "LauncherPartLaunch.h" +#include <QCoreApplication> #include <launch/LaunchTask.h> #include <minecraft/MinecraftInstance.h> #include <FileSystem.h> #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::stateChanged, this, &LaunchMinecraft::on_state); + connect(&m_process, &LoggedProcess::log, this, &LauncherPartLaunch::logLines); + connect(&m_process, &LoggedProcess::stateChanged, this, &LauncherPartLaunch::on_state); } -void LaunchMinecraft::executeTask() +void LauncherPartLaunch::executeTask() { auto instance = m_parent->instance(); std::shared_ptr<MinecraftInstance> minecraftInstance = std::dynamic_pointer_cast<MinecraftInstance>(instance); m_launchScript = minecraftInstance->createLaunchScript(m_session); - QStringList args = minecraftInstance->javaArguments(); // 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()); + args << "-jar" << FS::PathCombine(QCoreApplication::applicationDirPath(), "jars", "NewLaunch.jar"); + QString wrapperCommand = instance->getWrapperCommand(); 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) { @@ -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); } -void LaunchMinecraft::proceed() +void LauncherPartLaunch::proceed() { if(mayProceed) { @@ -135,7 +137,7 @@ void LaunchMinecraft::proceed() } } -bool LaunchMinecraft::abort() +bool LauncherPartLaunch::abort() { if(mayProceed) { diff --git a/api/logic/minecraft/launch/LaunchMinecraft.h b/api/logic/minecraft/launch/LauncherPartLaunch.h index 6b9f7919..209902e0 100644 --- a/api/logic/minecraft/launch/LaunchMinecraft.h +++ b/api/logic/minecraft/launch/LauncherPartLaunch.h @@ -19,11 +19,11 @@ #include <launch/LoggedProcess.h> #include <minecraft/auth/AuthSession.h> -class LaunchMinecraft: public LaunchStep +class LauncherPartLaunch: public LaunchStep { Q_OBJECT public: - explicit LaunchMinecraft(LaunchTask *parent); + explicit LauncherPartLaunch(LaunchTask *parent); virtual void executeTask(); virtual bool abort(); virtual void proceed(); @@ -36,13 +36,14 @@ public: { m_session = session; } + private slots: void on_state(LoggedProcess::State state); private: LoggedProcess m_process; QString m_command; - QString m_launchScript; AuthSessionPtr m_session; + QString m_launchScript; bool mayProceed = false; }; diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.cpp b/api/logic/minecraft/launch/PrintInstanceInfo.cpp new file mode 100644 index 00000000..b863eb98 --- /dev/null +++ b/api/logic/minecraft/launch/PrintInstanceInfo.cpp @@ -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(); +} diff --git a/api/logic/minecraft/launch/PrintInstanceInfo.h b/api/logic/minecraft/launch/PrintInstanceInfo.h new file mode 100644 index 00000000..5dd2bdb3 --- /dev/null +++ b/api/logic/minecraft/launch/PrintInstanceInfo.h @@ -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; +}; + diff --git a/api/logic/minecraft/legacy/LegacyInstance.cpp b/api/logic/minecraft/legacy/LegacyInstance.cpp index 28fd8872..7c552369 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.cpp +++ b/api/logic/minecraft/legacy/LegacyInstance.cpp @@ -14,6 +14,7 @@ */ #include <QFileInfo> +#include <minecraft/launch/LauncherPartLaunch.h> #include <QDir> #include <settings/Setting.h> @@ -21,14 +22,6 @@ #include "minecraft/legacy/LegacyUpdate.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/WorldList.h" #include <MMCZip.h> @@ -102,58 +95,6 @@ std::shared_ptr<Task> LegacyInstance::createUpdateTask() 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() { class JarModTask : public Task @@ -255,11 +196,35 @@ QString LegacyInstance::createLaunchScript(AuthSessionPtr session) launchScript += "sessionId " + session->session + "\n"; launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "windowParams " + windowParams + "\n"; - launchScript += "lwjgl " + lwjgl + "\n"; - launchScript += "launcher legacy\n"; + launchScript += "cp bin/minecraft.jar\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; } +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() { // FIXME: delete the launcher and icons and whatnot. @@ -452,3 +417,112 @@ QString LegacyInstance::typeName() const { 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; +} diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h index 88db5360..f1cefbcc 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.h +++ b/api/logic/minecraft/legacy/LegacyInstance.h @@ -114,11 +114,7 @@ public: virtual bool shouldUpdate() const override; virtual void setShouldUpdate(bool val) 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 QString createLaunchScript(AuthSessionPtr session) override; virtual void cleanupAfterRun() override; @@ -130,6 +126,20 @@ public: 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: mutable std::shared_ptr<LegacyModList> jar_mod_list; mutable std::shared_ptr<ModList> core_mod_list; diff --git a/api/logic/minecraft/onesix/OneSixInstance.cpp b/api/logic/minecraft/onesix/OneSixInstance.cpp index a96704c8..8b5491e2 100644 --- a/api/logic/minecraft/onesix/OneSixInstance.cpp +++ b/api/logic/minecraft/onesix/OneSixInstance.cpp @@ -14,6 +14,8 @@ */ #include <QDebug> +#include <minecraft/launch/DirectJavaLaunch.h> +#include <minecraft/launch/LauncherPartLaunch.h> #include <Env.h> #include "OneSixInstance.h" @@ -22,14 +24,7 @@ #include "minecraft/MinecraftProfile.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 "java/launch/CheckJava.h" #include "MMCZip.h" #include "minecraft/AssetsUtils.h" @@ -94,7 +89,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with) return result; } -QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) +QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) const { QString args_pattern = m_profile->getMinecraftArguments(); for (auto tweaker : m_profile->getTweakers()) @@ -104,11 +99,16 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) QMap<QString, QString> token_mapping; // yggdrasil! - token_mapping["auth_username"] = session->username; - token_mapping["auth_session"] = session->session; - token_mapping["auth_access_token"] = session->access_token; - token_mapping["auth_player_name"] = session->player_name; - token_mapping["auth_uuid"] = session->uuid; + if(session) + { + token_mapping["auth_username"] = session->username; + token_mapping["auth_session"] = session->session; + 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. token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5"; @@ -127,9 +127,6 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) auto assets = m_profile->getMinecraftAssets(); 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 token_mapping["assets_root"] = absAssetsDir; token_mapping["assets_index_name"] = assets->id; @@ -142,41 +139,34 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session) return parts; } -QString OneSixInstance::createLaunchScript(AuthSessionPtr session) +QString OneSixInstance::getNativePath() const { - QString launchScript; - - if (!m_profile) - return nullptr; + QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/")); + return natives_dir.absolutePath(); +} - for(auto & mod: loaderModList()->allMods()) +QString OneSixInstance::mainJarPath() const +{ + auto jarMods = getJarMods(); + if (!jarMods.isEmpty()) { - 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";; + return QDir(instanceRoot()).absoluteFilePath("minecraft.jar"); } - - for(auto & coremod: coreModList()->allMods()) + else { - 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";; + QString relpath = m_profile->getMinecraftVersion() + "/" + m_profile->getMinecraftVersion() + ".jar"; + return versionsPath().absoluteFilePath(relpath); } +} - for(auto & jarmod: m_profile->getJarMods()) - { - launchScript += "jarmod " + jarmod->originalName + " (" + jarmod->name + ")\n"; - } +QString OneSixInstance::createLaunchScript(AuthSessionPtr session) +{ + QString launchScript; - auto mainClass = m_profile->getMainClass(); + if (!m_profile) + return nullptr; + + auto mainClass = getMainClass(); if (!mainClass.isEmpty()) { launchScript += "mainClass " + mainClass + "\n"; @@ -207,6 +197,7 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session) } // legacy auth + if(session) { launchScript += "userName " + session->player_name + "\n"; launchScript += "sessionId " + session->session + "\n"; @@ -214,44 +205,21 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session) // libraries and class path. { - auto libs = m_profile->getLibraries(); - - QStringList jar, native, native32, native64; - for (auto lib : libs) - { - lib->getApplicableFiles(currentSystem, jar, native, native32, native64); - } - for(auto file: jar) + QStringList jars, nativeJars; + auto javaArchitecture = settings()->get("JavaArchitecture").toString(); + m_profile->getLibraryFiles(javaArchitecture, jars, nativeJars); + for(auto file: jars) { launchScript += "cp " + file + "\n"; } - for(auto file: native) + launchScript += "cp " + mainJarPath() + "\n"; + for(auto file: nativeJars) { launchScript += "ext " + file + "\n"; } - for(auto file: native32) - { - 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"; - } + launchScript += "natives " + getNativePath() + "\n"; } - // traits. including legacyLaunch and others ;) for (auto trait : m_profile->getTraits()) { launchScript += "traits " + trait + "\n"; @@ -260,58 +228,139 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session) 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())); - auto pptr = process.get(); + QStringList out; + 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. + { + out << "Libraries:"; + 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 << ""; } + + if(loaderModList()->size()) { - auto step = std::make_shared<CheckJava>(pptr); - process->appendStep(step); + 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 << ""; } - // run pre-launch command if that's needed - if(getPreLaunchCommand().size()) + + if(coreModList()->size()) { - auto step = std::make_shared<PreLaunchCommand>(pptr); - step->setWorkingDirectory(minecraftRoot()); - process->appendStep(step); + 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 we aren't in offline mode,. - if(session->status != AuthSession::PlayableOffline) + + auto & jarMods = m_profile->getJarMods(); + if(jarMods.size()) { - process->appendStep(std::make_shared<Update>(pptr)); + out << "Jar Mods:"; + for(auto & jarmod: jarMods) + { + out << " " + jarmod->originalName + " (" + jarmod->name + ")"; + } + out << ""; } - // if there are any jar mods - if(getJarMods().size()) + + auto params = processMinecraftArgs(nullptr); + out << "Params:"; + out << " " + params.join(' '); + out << ""; + + QString windowParams; + if (settings()->get("LaunchMaximized").toBool()) { - auto step = std::make_shared<ModMinecraftJar>(pptr); - process->appendStep(step); + out << "Window size: max (if available)"; } - // actually launch the game + else { - auto step = std::make_shared<LaunchMinecraft>(pptr); - step->setWorkingDirectory(minecraftRoot()); - step->setAuthSession(session); - process->appendStep(step); + auto width = settings()->get("MinecraftWinWidth").toInt(); + auto height = settings()->get("MinecraftWinHeight").toInt(); + out << "Window size: " + QString::number(width) + " x " + QString::number(height); } - // run post-exit command if that's needed - if(getPostExitCommand().size()) + out << ""; + return out; +} + + +std::shared_ptr<LaunchStep> OneSixInstance::createMainLaunchStep(LaunchTask * parent, AuthSessionPtr session) +{ + auto method = launchMethod(); + if(method == "LauncherPart") { - auto step = std::make_shared<PostLaunchCommand>(pptr); + auto step = std::make_shared<LauncherPartLaunch>(parent); + step->setAuthSession(session); step->setWorkingDirectory(minecraftRoot()); - process->appendStep(step); + return step; } - if (session) + else if (method == "DirectJava") { - process->setCensorFilter(createCensorFilterFromSession(session)); + auto step = std::make_shared<DirectJavaLaunch>(parent); + step->setWorkingDirectory(minecraftRoot()); + step->setAuthSession(session); + return step; } - return process; + return nullptr; } + std::shared_ptr<Task> OneSixInstance::createJarModdingTask() { class JarModTask : public Task @@ -595,3 +644,30 @@ QString OneSixInstance::typeName() const { 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; +} diff --git a/api/logic/minecraft/onesix/OneSixInstance.h b/api/logic/minecraft/onesix/OneSixInstance.h index 2dfab48c..1a917e79 100644 --- a/api/logic/minecraft/onesix/OneSixInstance.h +++ b/api/logic/minecraft/onesix/OneSixInstance.h @@ -53,10 +53,9 @@ public: virtual QString instanceConfigFolder() const 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 QString createLaunchScript(AuthSessionPtr session) override; + QStringList verboseDescription(AuthSessionPtr session) override; virtual void cleanupAfterRun() override; @@ -99,11 +98,23 @@ public: 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: void versionReloaded(); private: - QStringList processMinecraftArgs(AuthSessionPtr account); + QString mainJarPath() const; protected: std::shared_ptr<MinecraftProfile> m_profile; diff --git a/application/MultiMC.cpp b/application/MultiMC.cpp index 7b891215..4b1447d9 100644 --- a/application/MultiMC.cpp +++ b/application/MultiMC.cpp @@ -524,11 +524,15 @@ void MultiMC::initGlobalSettings(bool test_mode) // Java Settings m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaTimestamp", 0); + m_settings->registerSetting("JavaArchitecture", ""); m_settings->registerSetting("JavaVersion", ""); m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("JavaDetectionHack", ""); m_settings->registerSetting("JvmArgs", ""); + // Minecraft launch method + m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); + // Wrapper command for launch m_settings->registerSetting("WrapperCommand", ""); diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index b62805e0..9c0e128b 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -17,7 +17,6 @@ set(SRC # legacy applet wrapper thing. # The launcher has to be there for silly FML/Forge relauncher. net/minecraft/Launcher.java - org/multimc/legacy/LegacyLauncher.java org/multimc/LegacyFrame.java # onesix launcher diff --git a/libraries/launcher/org/multimc/EntryPoint.java b/libraries/launcher/org/multimc/EntryPoint.java index d1fc54a8..1d46c023 100644 --- a/libraries/launcher/org/multimc/EntryPoint.java +++ b/libraries/launcher/org/multimc/EntryPoint.java @@ -14,7 +14,6 @@ package org.multimc;/* * limitations under the License. */ -import org.multimc.legacy.LegacyLauncher; import org.multimc.onesix.OneSixLauncher; import org.simplericity.macify.eawt.Application; import org.simplericity.macify.eawt.DefaultApplication; @@ -83,13 +82,6 @@ public class EntryPoint 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")) { m_launcher = new OneSixLauncher(); diff --git a/libraries/launcher/org/multimc/Utils.java b/libraries/launcher/org/multimc/Utils.java index 32cf7919..86182831 100644 --- a/libraries/launcher/org/multimc/Utils.java +++ b/libraries/launcher/org/multimc/Utils.java @@ -186,95 +186,5 @@ public class Utils { 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(); - } - } } diff --git a/libraries/launcher/org/multimc/legacy/LegacyLauncher.java b/libraries/launcher/org/multimc/legacy/LegacyLauncher.java deleted file mode 100644 index 347bb1a2..00000000 --- a/libraries/launcher/org/multimc/legacy/LegacyLauncher.java +++ /dev/null @@ -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; - } -} diff --git a/libraries/launcher/org/multimc/onesix/OneSixLauncher.java b/libraries/launcher/org/multimc/onesix/OneSixLauncher.java index 179df0ee..e3e4c283 100644 --- a/libraries/launcher/org/multimc/onesix/OneSixLauncher.java +++ b/libraries/launcher/org/multimc/onesix/OneSixLauncher.java @@ -30,9 +30,6 @@ public class OneSixLauncher implements Launcher { // parameters, separated from ParamBucket private List<String> libraries; - private List<String> extlibs; - private List<String> extlibs32; - private List<String> extlibs64; private List<String> mcparams; private List<String> mods; private List<String> jarmods; @@ -56,28 +53,9 @@ public class OneSixLauncher implements Launcher private void processParams(ParamBucket params) throws NotFoundException { 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>() ); mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); 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>()); 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() { // Get the Minecraft Class and set the base folder @@ -310,27 +219,6 @@ public class OneSixLauncher implements Launcher 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 try { diff --git a/wonkoclient/WonkoClient.cpp b/wonkoclient/WonkoClient.cpp index dd0dfae5..2e07acb1 100644 --- a/wonkoclient/WonkoClient.cpp +++ b/wonkoclient/WonkoClient.cpp @@ -68,6 +68,7 @@ void WonkoClient::initGlobalSettings() // Java Settings m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaTimestamp", 0); + m_settings->registerSetting("JavaArchitecture", ""); m_settings->registerSetting("JavaVersion", ""); m_settings->registerSetting("LastHostname", ""); m_settings->registerSetting("JavaDetectionHack", ""); @@ -79,4 +80,7 @@ void WonkoClient::initGlobalSettings() // Custom Commands m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, ""); m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, ""); + + // Minecraft launch method + m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); } |