NOISSUE move files into paths that make more sense

This commit is contained in:
Petr Mrázek
2016-02-27 19:58:40 +01:00
parent 71e4b147ec
commit 17ad1e64f8
75 changed files with 148 additions and 144 deletions

View File

@ -0,0 +1,455 @@
/* 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 <QFileInfo>
#include <QDir>
#include <QImage>
#include <settings/Setting.h>
#include "LegacyInstance.h"
#include "minecraft/legacy/LegacyUpdate.h"
#include "icons/IconList.h"
#include "launch/LaunchTask.h"
#include <launch/steps/LaunchMinecraft.h>
#include <launch/steps/PostLaunchCommand.h>
#include <launch/steps/ModMinecraftJar.h>
#include <launch/steps/Update.h>
#include <launch/steps/PreLaunchCommand.h>
#include <launch/steps/TextPrint.h>
#include <launch/steps/CheckJava.h>
#include "minecraft/ModList.h"
#include "minecraft/WorldList.h"
#include <MMCZip.h>
#include <FileSystem.h>
LegacyInstance::LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
: MinecraftInstance(globalSettings, settings, rootDir)
{
m_lwjglFolderSetting = globalSettings->getSetting("LWJGLDir");
settings->registerSetting("NeedsRebuild", true);
settings->registerSetting("ShouldUpdate", false);
settings->registerSetting("JarVersion", "Unknown");
settings->registerSetting("LwjglVersion", "2.9.0");
settings->registerSetting("IntendedJarVersion", "");
/*
* custom base jar has no default. it is determined in code... see the accessor methods for
*it
*
* for instances that DO NOT have the CustomBaseJar setting (legacy instances),
* [.]minecraft/bin/mcbackup.jar is the default base jar
*/
settings->registerSetting("UseCustomBaseJar", true);
settings->registerSetting("CustomBaseJar", "");
}
QString LegacyInstance::baseJar() const
{
bool customJar = m_settings->get("UseCustomBaseJar").toBool();
if (customJar)
{
return customBaseJar();
}
else
return defaultBaseJar();
}
QString LegacyInstance::customBaseJar() const
{
QString value = m_settings->get("CustomBaseJar").toString();
if (value.isNull() || value.isEmpty())
{
return defaultCustomBaseJar();
}
return value;
}
void LegacyInstance::setCustomBaseJar(QString val)
{
if (val.isNull() || val.isEmpty() || val == defaultCustomBaseJar())
m_settings->reset("CustomBaseJar");
else
m_settings->set("CustomBaseJar", val);
}
void LegacyInstance::setShouldUseCustomBaseJar(bool val)
{
m_settings->set("UseCustomBaseJar", val);
}
bool LegacyInstance::shouldUseCustomBaseJar() const
{
return m_settings->get("UseCustomBaseJar").toBool();
}
std::shared_ptr<Task> LegacyInstance::createUpdateTask()
{
// make sure the jar mods list is initialized by asking for it.
auto list = jarModList();
// create an update task
return std::shared_ptr<Task>(new LegacyUpdate(this, this));
}
std::shared_ptr<LaunchTask> LegacyInstance::createLaunchTask(AuthSessionPtr session)
{
QString launchScript;
QIcon icon = ENV.icons()->getIcon(iconKey());
auto pixmap = icon.pixmap(128, 128);
pixmap.save(FS::PathCombine(minecraftRoot(), "icon.png"), "PNG");
// create the launch script
{
// window size
QString windowParams;
if (settings()->get("LaunchMaximized").toBool())
windowParams = "max";
else
windowParams = QString("%1x%2")
.arg(settings()->get("MinecraftWinWidth").toInt())
.arg(settings()->get("MinecraftWinHeight").toInt());
QString lwjgl = QDir(m_lwjglFolderSetting->get().toString() + "/" + lwjglVersion())
.absolutePath();
launchScript += "userName " + session->player_name + "\n";
launchScript += "sessionId " + session->session + "\n";
launchScript += "windowTitle " + windowTitle() + "\n";
launchScript += "windowParams " + windowParams + "\n";
launchScript += "lwjgl " + lwjgl + "\n";
launchScript += "launcher legacy\n";
}
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->setLaunchScript(launchScript);
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
{
public:
explicit JarModTask(std::shared_ptr<LegacyInstance> inst) : Task(nullptr), m_inst(inst)
{
}
virtual void executeTask()
{
if (!m_inst->shouldRebuild())
{
emitSucceeded();
return;
}
// Get the mod list
auto modList = m_inst->getJarMods();
QFileInfo runnableJar(m_inst->runnableJar());
QFileInfo baseJar(m_inst->baseJar());
bool base_is_custom = m_inst->shouldUseCustomBaseJar();
// Nothing to do if there are no jar mods to install, no backup and just the mc jar
if (base_is_custom)
{
// yes, this can happen if the instance only has the runnable jar and not the base jar
// it *could* be assumed that such an instance is vanilla, but that wouldn't be safe
// because that's not something mmc4 guarantees
if (runnableJar.isFile() && !baseJar.exists() && modList.empty())
{
m_inst->setShouldRebuild(false);
emitSucceeded();
return;
}
setStatus(tr("Installing mods: Backing up minecraft.jar ..."));
if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath()))
{
emitFailed("It seems both the active and base jar are gone. A fresh base jar will "
"be used on next run.");
m_inst->setShouldRebuild(true);
m_inst->setShouldUpdate(true);
m_inst->setShouldUseCustomBaseJar(false);
return;
}
}
if (!baseJar.exists())
{
emitFailed("The base jar " + baseJar.filePath() + " does not exist");
return;
}
if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
{
emitFailed("Failed to delete old minecraft.jar");
return;
}
setStatus(tr("Installing mods: Opening minecraft.jar ..."));
QString outputJarPath = runnableJar.filePath();
QString inputJarPath = baseJar.filePath();
if(!MMCZip::createModdedJar(inputJarPath, outputJarPath, modList))
{
emitFailed(tr("Failed to create the custom Minecraft jar file."));
return;
}
m_inst->setShouldRebuild(false);
// inst->UpdateVersion(true);
emitSucceeded();
return;
}
std::shared_ptr<LegacyInstance> m_inst;
};
return std::make_shared<JarModTask>(std::dynamic_pointer_cast<LegacyInstance>(shared_from_this()));
}
void LegacyInstance::cleanupAfterRun()
{
// FIXME: delete the launcher and icons and whatnot.
}
std::shared_ptr<ModList> LegacyInstance::coreModList() const
{
if (!core_mod_list)
{
core_mod_list.reset(new ModList(coreModsDir()));
}
core_mod_list->update();
return core_mod_list;
}
std::shared_ptr<ModList> LegacyInstance::jarModList() const
{
if (!jar_mod_list)
{
auto list = new ModList(jarModsDir(), modListFile());
connect(list, SIGNAL(changed()), SLOT(jarModsChanged()));
jar_mod_list.reset(list);
}
jar_mod_list->update();
return jar_mod_list;
}
QList<Mod> LegacyInstance::getJarMods() const
{
return jarModList()->allMods();
}
void LegacyInstance::jarModsChanged()
{
qDebug() << "Jar mods of instance " << name() << " have changed. Jar will be rebuilt.";
setShouldRebuild(true);
}
std::shared_ptr<ModList> LegacyInstance::loaderModList() const
{
if (!loader_mod_list)
{
loader_mod_list.reset(new ModList(loaderModsDir()));
}
loader_mod_list->update();
return loader_mod_list;
}
std::shared_ptr<ModList> LegacyInstance::texturePackList() const
{
if (!texture_pack_list)
{
texture_pack_list.reset(new ModList(texturePacksDir()));
}
texture_pack_list->update();
return texture_pack_list;
}
std::shared_ptr<WorldList> LegacyInstance::worldList() const
{
if (!m_world_list)
{
m_world_list.reset(new WorldList(savesDir()));
}
return m_world_list;
}
QString LegacyInstance::jarModsDir() const
{
return FS::PathCombine(instanceRoot(), "instMods");
}
QString LegacyInstance::binDir() const
{
return FS::PathCombine(minecraftRoot(), "bin");
}
QString LegacyInstance::libDir() const
{
return FS::PathCombine(minecraftRoot(), "lib");
}
QString LegacyInstance::savesDir() const
{
return FS::PathCombine(minecraftRoot(), "saves");
}
QString LegacyInstance::loaderModsDir() const
{
return FS::PathCombine(minecraftRoot(), "mods");
}
QString LegacyInstance::coreModsDir() const
{
return FS::PathCombine(minecraftRoot(), "coremods");
}
QString LegacyInstance::resourceDir() const
{
return FS::PathCombine(minecraftRoot(), "resources");
}
QString LegacyInstance::texturePacksDir() const
{
return FS::PathCombine(minecraftRoot(), "texturepacks");
}
QString LegacyInstance::runnableJar() const
{
return FS::PathCombine(binDir(), "minecraft.jar");
}
QString LegacyInstance::modListFile() const
{
return FS::PathCombine(instanceRoot(), "modlist");
}
QString LegacyInstance::instanceConfigFolder() const
{
return FS::PathCombine(minecraftRoot(), "config");
}
bool LegacyInstance::shouldRebuild() const
{
return m_settings->get("NeedsRebuild").toBool();
}
void LegacyInstance::setShouldRebuild(bool val)
{
m_settings->set("NeedsRebuild", val);
}
QString LegacyInstance::currentVersionId() const
{
return m_settings->get("JarVersion").toString();
}
QString LegacyInstance::lwjglVersion() const
{
return m_settings->get("LwjglVersion").toString();
}
void LegacyInstance::setLWJGLVersion(QString val)
{
m_settings->set("LwjglVersion", val);
}
QString LegacyInstance::intendedVersionId() const
{
return m_settings->get("IntendedJarVersion").toString();
}
bool LegacyInstance::setIntendedVersionId(QString version)
{
settings()->set("IntendedJarVersion", version);
setShouldUpdate(true);
return true;
}
bool LegacyInstance::shouldUpdate() const
{
QVariant var = settings()->get("ShouldUpdate");
if (!var.isValid() || var.toBool() == false)
{
return intendedVersionId() != currentVersionId();
}
return true;
}
void LegacyInstance::setShouldUpdate(bool val)
{
settings()->set("ShouldUpdate", val);
}
QString LegacyInstance::defaultBaseJar() const
{
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
}
QString LegacyInstance::defaultCustomBaseJar() const
{
return FS::PathCombine(binDir(), "mcbackup.jar");
}
QString LegacyInstance::lwjglFolder() const
{
return m_lwjglFolderSetting->get().toString();
}
QString LegacyInstance::typeName() const
{
return tr("Legacy");
}

View File

@ -0,0 +1,135 @@
/* 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 "minecraft/MinecraftInstance.h"
#include "multimc_logic_export.h"
class ModList;
class Task;
class MULTIMC_LOGIC_EXPORT LegacyInstance : public MinecraftInstance
{
Q_OBJECT
public:
explicit LegacyInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
virtual void init() override {};
/// Path to the instance's minecraft.jar
QString runnableJar() const;
//! Path to the instance's modlist file.
QString modListFile() const;
/*
////// Edit Instance Dialog stuff //////
virtual QList<BasePage *> getPages();
virtual QString dialogTitle();
*/
////// Mod Lists //////
std::shared_ptr<ModList> jarModList() const ;
virtual QList< Mod > getJarMods() const override;
std::shared_ptr<ModList> coreModList() const;
std::shared_ptr<ModList> loaderModList() const;
std::shared_ptr<ModList> texturePackList() const override;
std::shared_ptr<WorldList> worldList() const override;
////// Directories //////
QString libDir() const;
QString savesDir() const;
QString texturePacksDir() const;
QString jarModsDir() const;
QString binDir() const;
QString loaderModsDir() const;
QString coreModsDir() const;
QString resourceDir() const;
virtual QString instanceConfigFolder() const override;
/// Get the curent base jar of this instance. By default, it's the
/// versions/$version/$version.jar
QString baseJar() const;
/// the default base jar of this instance
QString defaultBaseJar() const;
/// the default custom base jar of this instance
QString defaultCustomBaseJar() const;
/*!
* Whether or not custom base jar is used
*/
bool shouldUseCustomBaseJar() const;
void setShouldUseCustomBaseJar(bool val);
/*!
* The value of the custom base jar
*/
QString customBaseJar() const;
void setCustomBaseJar(QString val);
/*!
* Whether or not the instance's minecraft.jar needs to be rebuilt.
* If this is true, when the instance launches, its jar mods will be
* re-added to a fresh minecraft.jar file.
*/
bool shouldRebuild() const;
void setShouldRebuild(bool val);
virtual QString currentVersionId() const override;
//! The version of LWJGL that this instance uses.
QString lwjglVersion() const;
//! Where the lwjgl versions foor this instance can be found... HACK HACK HACK
QString lwjglFolder() const;
/// st the version of LWJGL libs this instance will use
void setLWJGLVersion(QString val);
virtual QString intendedVersionId() const override;
virtual bool setIntendedVersionId(QString version) override;
virtual QSet<QString> traits() override
{
return {"legacy-instance", "texturepacks"};
};
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 void cleanupAfterRun() override;
virtual QString typeName() const override;
protected:
mutable std::shared_ptr<ModList> jar_mod_list;
mutable std::shared_ptr<ModList> core_mod_list;
mutable std::shared_ptr<ModList> loader_mod_list;
mutable std::shared_ptr<ModList> texture_pack_list;
mutable std::shared_ptr<WorldList> m_world_list;
std::shared_ptr<Setting> m_lwjglFolderSetting;
protected
slots:
virtual void jarModsChanged();
};

View File

@ -0,0 +1,395 @@
/* 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 <QStringList>
#include <quazip.h>
#include <quazipfile.h>
#include <QDebug>
#include "Env.h"
#include "BaseInstance.h"
#include "net/URLConstants.h"
#include "MMCZip.h"
#include "LegacyUpdate.h"
#include "LwjglVersionList.h"
#include "minecraft/MinecraftVersionList.h"
#include "minecraft/ModList.h"
#include "LegacyInstance.h"
#include <FileSystem.h>
LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
}
void LegacyUpdate::executeTask()
{
fmllibsStart();
}
void LegacyUpdate::fmllibsStart()
{
// Get the mod list
LegacyInstance *inst = (LegacyInstance *)m_inst;
auto modList = inst->jarModList();
bool forge_present = false;
QString version = inst->intendedVersionId();
auto & fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
if (!fmlLibsMapping.contains(version))
{
lwjglStart();
return;
}
auto &libList = fmlLibsMapping[version];
// determine if we need some libs for FML or forge
setStatus(tr("Checking for FML libraries..."));
for (unsigned i = 0; i < modList->size(); i++)
{
auto &mod = modList->operator[](i);
// do not use disabled mods.
if (!mod.enabled())
continue;
if (mod.type() != Mod::MOD_ZIPFILE)
continue;
if (mod.mmc_id().contains("forge", Qt::CaseInsensitive))
{
forge_present = true;
break;
}
if (mod.mmc_id().contains("fml", Qt::CaseInsensitive))
{
forge_present = true;
break;
}
}
// we don't...
if (!forge_present)
{
lwjglStart();
return;
}
// now check the lib folder inside the instance for files.
for (auto &lib : libList)
{
QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename));
if (libInfo.exists())
continue;
fmlLibsToProcess.append(lib);
}
// if everything is in place, there's nothing to do here...
if (fmlLibsToProcess.isEmpty())
{
lwjglStart();
return;
}
// download missing libs to our place
setStatus(tr("Dowloading FML libraries..."));
auto dljob = new NetJob("FML libraries");
auto metacache = ENV.metacache();
for (auto &lib : fmlLibsToProcess)
{
auto entry = metacache->resolveEntry("fmllibs", lib.filename);
QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename
: URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename;
dljob->addNetAction(CacheDownload::make(QUrl(urlString), entry));
}
connect(dljob, &NetJob::succeeded, this, &LegacyUpdate::fmllibsFinished);
connect(dljob, &NetJob::failed, this, &LegacyUpdate::fmllibsFailed);
connect(dljob, &NetJob::progress, this, &LegacyUpdate::progress);
legacyDownloadJob.reset(dljob);
legacyDownloadJob->start();
}
void LegacyUpdate::fmllibsFinished()
{
legacyDownloadJob.reset();
if(!fmlLibsToProcess.isEmpty())
{
setStatus(tr("Copying FML libraries into the instance..."));
LegacyInstance *inst = (LegacyInstance *)m_inst;
auto metacache = ENV.metacache();
int index = 0;
for (auto &lib : fmlLibsToProcess)
{
progress(index, fmlLibsToProcess.size());
auto entry = metacache->resolveEntry("fmllibs", lib.filename);
auto path = FS::PathCombine(inst->libDir(), lib.filename);
if(!FS::ensureFilePathExists(path))
{
emitFailed(tr("Failed creating FML library folder inside the instance."));
return;
}
if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename)))
{
emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
return;
}
index++;
}
progress(index, fmlLibsToProcess.size());
}
lwjglStart();
}
void LegacyUpdate::fmllibsFailed(QString reason)
{
emitFailed(tr("Game update failed: it was impossible to fetch the required FML libraries. Reason: %1").arg(reason));
return;
}
void LegacyUpdate::lwjglStart()
{
LegacyInstance *inst = (LegacyInstance *)m_inst;
lwjglVersion = inst->lwjglVersion();
lwjglTargetPath = FS::PathCombine(inst->lwjglFolder(), lwjglVersion);
lwjglNativesPath = FS::PathCombine(lwjglTargetPath, "natives");
// if the 'done' file exists, we don't have to download this again
QFileInfo doneFile(FS::PathCombine(lwjglTargetPath, "done"));
if (doneFile.exists())
{
jarStart();
return;
}
auto list = std::dynamic_pointer_cast<LWJGLVersionList>(ENV.getVersionList("org.lwjgl.legacy"));
if (!list->isLoaded())
{
emitFailed("Too soon! Let the LWJGL list load :)");
return;
}
setStatus(tr("Downloading new LWJGL..."));
auto version = std::dynamic_pointer_cast<LWJGLVersion>(list->findVersion(lwjglVersion));
if (!version)
{
emitFailed("Game update failed: the selected LWJGL version is invalid.");
return;
}
QString url = version->url();
QUrl realUrl(url);
QString hostname = realUrl.host();
auto worker = ENV.qnam();
QNetworkRequest req(realUrl);
req.setRawHeader("Host", hostname.toLatin1());
req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
QNetworkReply *rep = worker->get(req);
m_reply = std::shared_ptr<QNetworkReply>(rep);
connect(rep, &QNetworkReply::downloadProgress, this, &LegacyUpdate::progress);
connect(worker.get(), &QNetworkAccessManager::finished, this, &LegacyUpdate::lwjglFinished);
}
void LegacyUpdate::lwjglFinished(QNetworkReply *reply)
{
if (m_reply.get() != reply)
{
return;
}
if (reply->error() != QNetworkReply::NoError)
{
emitFailed("Failed to download: " + reply->errorString() +
"\nSometimes you have to wait a bit if you download many LWJGL versions in "
"a row. YMMV");
return;
}
auto worker = ENV.qnam();
// Here i check if there is a cookie for me in the reply and extract it
QList<QNetworkCookie> cookies =
qvariant_cast<QList<QNetworkCookie>>(reply->header(QNetworkRequest::SetCookieHeader));
if (cookies.count() != 0)
{
// you must tell which cookie goes with which url
worker->cookieJar()->setCookiesFromUrl(cookies, QUrl("sourceforge.net"));
}
// here you can check for the 302 or whatever other header i need
QVariant newLoc = reply->header(QNetworkRequest::LocationHeader);
if (newLoc.isValid())
{
QString redirectedTo = reply->header(QNetworkRequest::LocationHeader).toString();
QUrl realUrl(redirectedTo);
QString hostname = realUrl.host();
QNetworkRequest req(redirectedTo);
req.setRawHeader("Host", hostname.toLatin1());
req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)");
QNetworkReply *rep = worker->get(req);
connect(rep, &QNetworkReply::downloadProgress, this, &LegacyUpdate::progress);
m_reply = std::shared_ptr<QNetworkReply>(rep);
return;
}
QFile saveMe("lwjgl.zip");
saveMe.open(QIODevice::WriteOnly);
saveMe.write(m_reply->readAll());
saveMe.close();
setStatus(tr("Installing new LWJGL..."));
extractLwjgl();
jarStart();
}
void LegacyUpdate::extractLwjgl()
{
// make sure the directories are there
bool success = FS::ensureFolderPathExists(lwjglNativesPath);
if (!success)
{
emitFailed("Failed to extract the lwjgl libs - error when creating required folders.");
return;
}
QuaZip zip("lwjgl.zip");
if (!zip.open(QuaZip::mdUnzip))
{
emitFailed("Failed to extract the lwjgl libs - not a valid archive.");
return;
}
// and now we are going to access files inside it
QuaZipFile file(&zip);
const QString jarNames[] = {"jinput.jar", "lwjgl_util.jar", "lwjgl.jar"};
for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile())
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
emitFailed("Failed to extract the lwjgl libs - error while reading archive.");
return;
}
QuaZipFileInfo info;
QString name = file.getActualFileName();
if (name.endsWith('/'))
{
file.close();
continue;
}
QString destFileName;
// Look for the jars
for (int i = 0; i < 3; i++)
{
if (name.endsWith(jarNames[i]))
{
destFileName = FS::PathCombine(lwjglTargetPath, jarNames[i]);
}
}
// Not found? look for the natives
if (destFileName.isEmpty())
{
#ifdef Q_OS_WIN32
QString nativesDir = "windows";
#else
#ifdef Q_OS_MAC
QString nativesDir = "macosx";
#else
QString nativesDir = "linux";
#endif
#endif
if (name.contains(nativesDir))
{
int lastSlash = name.lastIndexOf('/');
int lastBackSlash = name.lastIndexOf('\\');
if (lastSlash != -1)
name = name.mid(lastSlash + 1);
else if (lastBackSlash != -1)
name = name.mid(lastBackSlash + 1);
destFileName = FS::PathCombine(lwjglNativesPath, name);
}
}
// Now if destFileName is still empty, go to the next file.
if (!destFileName.isEmpty())
{
setStatus(tr("Installing new LWJGL - extracting ") + name + "...");
QFile output(destFileName);
output.open(QIODevice::WriteOnly);
output.write(file.readAll());
output.close();
}
file.close(); // do not forget to close!
}
zip.close();
m_reply.reset();
QFile doneFile(FS::PathCombine(lwjglTargetPath, "done"));
doneFile.open(QIODevice::WriteOnly);
doneFile.write("done.");
doneFile.close();
}
void LegacyUpdate::lwjglFailed(QString reason)
{
emitFailed(tr("Bad stuff happened while trying to get the lwjgl libs: %1").arg(reason));
}
void LegacyUpdate::jarStart()
{
LegacyInstance *inst = (LegacyInstance *)m_inst;
if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar())
{
emitSucceeded();
return;
}
setStatus(tr("Checking for jar updates..."));
// Make directories
QDir binDir(inst->binDir());
if (!binDir.exists() && !binDir.mkpath("."))
{
emitFailed("Failed to create bin folder.");
return;
}
// Build a list of URLs that will need to be downloaded.
setStatus(tr("Downloading new minecraft.jar ..."));
QString version_id = inst->intendedVersionId();
QString localPath = version_id + "/" + version_id + ".jar";
QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath;
auto dljob = new NetJob("Minecraft.jar for version " + version_id);
auto metacache = ENV.metacache();
auto entry = metacache->resolveEntry("versions", localPath);
dljob->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished()));
connect(dljob, SIGNAL(failed(QString)), SLOT(jarFailed(QString)));
connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
legacyDownloadJob.reset(dljob);
legacyDownloadJob->start();
}
void LegacyUpdate::jarFinished()
{
// process the jar
emitSucceeded();
}
void LegacyUpdate::jarFailed(QString reason)
{
// bad, bad
emitFailed(tr("Failed to download the minecraft jar: %1.").arg(reason));
}

View File

@ -0,0 +1,70 @@
/* 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 <QObject>
#include <QList>
#include <QUrl>
#include "net/NetJob.h"
#include "tasks/Task.h"
#include "minecraft/VersionFilterData.h"
class MinecraftVersion;
class BaseInstance;
class QuaZip;
class Mod;
class LegacyUpdate : public Task
{
Q_OBJECT
public:
explicit LegacyUpdate(BaseInstance *inst, QObject *parent = 0);
virtual void executeTask();
private
slots:
void lwjglStart();
void lwjglFinished(QNetworkReply *);
void lwjglFailed(QString reason);
void jarStart();
void jarFinished();
void jarFailed(QString reason);
void fmllibsStart();
void fmllibsFinished();
void fmllibsFailed(QString reason);
void extractLwjgl();
private:
std::shared_ptr<QNetworkReply> m_reply;
// target version, determined during this task
// MinecraftVersion *targetVersion;
QString lwjglURL;
QString lwjglVersion;
QString lwjglTargetPath;
QString lwjglNativesPath;
private:
NetJobPtr legacyDownloadJob;
BaseInstance *m_inst = nullptr;
QList<FMLlib> fmlLibsToProcess;
};

View File

@ -0,0 +1,189 @@
/* 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 "LwjglVersionList.h"
#include "Env.h"
#include <QtNetwork>
#include <QtXml>
#include <QRegExp>
#include <QDebug>
#define RSS_URL "http://sourceforge.net/projects/java-game-lib/rss"
LWJGLVersionList::LWJGLVersionList(QObject *parent) : BaseVersionList(parent)
{
setLoading(false);
}
QVariant LWJGLVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() > count())
return QVariant();
const PtrLWJGLVersion version = m_vlist.at(index.row());
switch (role)
{
case Qt::DisplayRole:
return version->name();
case Qt::ToolTipRole:
return version->url();
default:
return QVariant();
}
}
QVariant LWJGLVersionList::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (role)
{
case Qt::DisplayRole:
return tr("Version");
case Qt::ToolTipRole:
return tr("LWJGL version name.");
default:
return QVariant();
}
}
int LWJGLVersionList::columnCount(const QModelIndex &parent) const
{
return 1;
}
bool LWJGLVersionList::isLoading() const
{
return m_loading;
}
void LWJGLVersionList::loadList()
{
Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)");
setLoading(true);
auto worker = ENV.qnam();
QNetworkRequest req(QUrl(RSS_URL));
req.setRawHeader("Accept", "application/rss+xml, text/xml, */*");
req.setRawHeader("User-Agent", "MultiMC/5.0 (Uncached)");
reply = worker->get(req);
connect(reply, SIGNAL(finished()), SLOT(netRequestComplete()));
}
inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
{
QDomNodeList elementList = parent.elementsByTagName(tagname);
if (elementList.count())
return elementList.at(0).toElement();
else
return QDomElement();
}
void LWJGLVersionList::netRequestComplete()
{
if (reply->error() == QNetworkReply::NoError)
{
QRegExp lwjglRegex("lwjgl-(([0-9]\\.?)+)\\.zip");
Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", "LWJGL regex is invalid");
QDomDocument doc;
QString xmlErrorMsg;
int errorLine;
auto rawData = reply->readAll();
if (!doc.setContent(rawData, false, &xmlErrorMsg, &errorLine))
{
failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " +
QString::number(errorLine));
setLoading(false);
return;
}
QDomNodeList items = doc.elementsByTagName("item");
QList<PtrLWJGLVersion> tempList;
for (int i = 0; i < items.length(); i++)
{
Q_ASSERT_X(items.at(i).isElement(), "load LWJGL list",
"XML element isn't an element... wat?");
QDomElement linkElement = getDomElementByTagName(items.at(i).toElement(), "link");
if (linkElement.isNull())
{
qDebug() << "Link element" << i << "in RSS feed doesn't exist! Skipping.";
continue;
}
QString link = linkElement.text();
// Make sure it's a download link.
if (link.endsWith("/download") && link.contains(lwjglRegex))
{
QString name = link.mid(lwjglRegex.indexIn(link) + 6);
// Subtract 4 here to remove the .zip file extension.
name = name.left(lwjglRegex.matchedLength() - 10);
QUrl url(link);
if (!url.isValid())
{
qWarning() << "LWJGL version URL isn't valid:" << link << "Skipping.";
continue;
}
qDebug() << "Discovered LWGL version" << name << "at" << link;
tempList.append(std::make_shared<LWJGLVersion>(name, link));
}
}
beginResetModel();
m_vlist.swap(tempList);
endResetModel();
qDebug() << "Loaded LWJGL list.";
finished();
}
else
{
failed("Failed to load LWJGL list. Network error: " + reply->errorString());
}
setLoading(false);
reply->deleteLater();
}
void LWJGLVersionList::failed(QString msg)
{
qCritical() << msg;
emit loadListFailed(msg);
}
void LWJGLVersionList::finished()
{
emit loadListFinished();
}
void LWJGLVersionList::setLoading(bool loading)
{
m_loading = loading;
emit loadingStateUpdated(m_loading);
}

View File

@ -0,0 +1,156 @@
/* 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 <QObject>
#include <QAbstractListModel>
#include <QUrl>
#include <QNetworkReply>
#include <memory>
#include "BaseVersion.h"
#include "BaseVersionList.h"
#include "multimc_logic_export.h"
class LWJGLVersion;
typedef std::shared_ptr<LWJGLVersion> PtrLWJGLVersion;
class MULTIMC_LOGIC_EXPORT LWJGLVersion : public BaseVersion
{
public:
LWJGLVersion(const QString &name, const QString &url)
: m_name(name), m_url(url)
{
}
virtual QString descriptor()
{
return m_name;
}
virtual QString name()
{
return m_name;
}
virtual QString typeString() const
{
return QObject::tr("Upstream");
}
QString url() const
{
return m_url;
}
protected:
QString m_name;
QString m_url;
};
class MULTIMC_LOGIC_EXPORT LWJGLVersionList : public BaseVersionList
{
Q_OBJECT
public:
explicit LWJGLVersionList(QObject *parent = 0);
bool isLoaded() override
{
return m_vlist.length() > 0;
}
virtual const BaseVersionPtr at(int i) const override
{
return m_vlist[i];
}
virtual Task* getLoadTask() override
{
return nullptr;
}
virtual void sortVersions() override {};
virtual void updateListData(QList< BaseVersionPtr > versions) override {};
int count() const override
{
return m_vlist.length();
}
virtual QVariant data(const QModelIndex &index, int role) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
virtual int rowCount(const QModelIndex &parent) const override
{
return count();
}
virtual int columnCount(const QModelIndex &parent) const override;
virtual bool isLoading() const;
virtual bool errored() const
{
return m_errored;
}
virtual QString lastErrorMsg() const
{
return m_lastErrorMsg;
}
public
slots:
/*!
* Loads the version list.
* This is done asynchronously. On success, the loadListFinished() signal will
* be emitted. The list model will be reset as well, resulting in the modelReset()
* signal being emitted. Note that the model will be reset before loadListFinished() is
* emitted.
* If loading the list failed, the loadListFailed(QString msg),
* signal will be emitted.
*/
virtual void loadList();
signals:
/*!
* Emitted when the list either starts or finishes loading.
* \param loading Whether or not the list is loading.
*/
void loadingStateUpdated(bool loading);
void loadListFinished();
void loadListFailed(QString msg);
private:
QList<PtrLWJGLVersion> m_vlist;
QNetworkReply *m_netReply;
QNetworkReply *reply;
bool m_loading;
bool m_errored;
QString m_lastErrorMsg;
void failed(QString msg);
void finished();
void setLoading(bool loading);
private
slots:
virtual void netRequestComplete();
};