diff options
| author | Petr Mrázek <peterix@gmail.com> | 2014-04-23 02:27:40 +0200 |
|---|---|---|
| committer | Petr Mrázek <peterix@gmail.com> | 2014-06-09 01:38:29 +0200 |
| commit | aade36860c373268857ca821c14a13f38c880b1a (patch) | |
| tree | 464b804216c7eadc00941daa33a1713c8071a088 /logic/forge | |
| parent | 3a0cdf2d3dde6192694ca34429ab277608357c2a (diff) | |
| download | PrismLauncher-aade36860c373268857ca821c14a13f38c880b1a.tar.gz PrismLauncher-aade36860c373268857ca821c14a13f38c880b1a.tar.bz2 PrismLauncher-aade36860c373268857ca821c14a13f38c880b1a.zip | |
Begin the transformation!
Nuke all the things.
Diffstat (limited to 'logic/forge')
| -rw-r--r-- | logic/forge/ForgeData.cpp | 62 | ||||
| -rw-r--r-- | logic/forge/ForgeData.h | 21 | ||||
| -rw-r--r-- | logic/forge/ForgeInstaller.cpp | 380 | ||||
| -rw-r--r-- | logic/forge/ForgeInstaller.h | 51 | ||||
| -rw-r--r-- | logic/forge/ForgeMirror.h | 10 | ||||
| -rw-r--r-- | logic/forge/ForgeMirrors.cpp | 118 | ||||
| -rw-r--r-- | logic/forge/ForgeMirrors.h | 58 | ||||
| -rw-r--r-- | logic/forge/ForgeVersion.cpp | 55 | ||||
| -rw-r--r-- | logic/forge/ForgeVersion.h | 31 | ||||
| -rw-r--r-- | logic/forge/ForgeVersionList.cpp | 435 | ||||
| -rw-r--r-- | logic/forge/ForgeVersionList.h | 83 | ||||
| -rw-r--r-- | logic/forge/ForgeXzDownload.cpp | 389 | ||||
| -rw-r--r-- | logic/forge/ForgeXzDownload.h | 66 | ||||
| -rw-r--r-- | logic/forge/LegacyForge.cpp | 56 | ||||
| -rw-r--r-- | logic/forge/LegacyForge.h | 25 |
15 files changed, 1840 insertions, 0 deletions
diff --git a/logic/forge/ForgeData.cpp b/logic/forge/ForgeData.cpp new file mode 100644 index 00000000..700b847b --- /dev/null +++ b/logic/forge/ForgeData.cpp @@ -0,0 +1,62 @@ +#include "ForgeData.h" + +extern ForgeData g_forgeData = ForgeData(); + +ForgeData::ForgeData() +{ + // 1.3.* + auto libs13 = + QList<FMLlib>{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false}, + {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false}, + {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}}; + + fmlLibsMapping["1.3.2"] = libs13; + + // 1.4.* + auto libs14 = QList<FMLlib>{ + {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false}, + {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false}, + {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}, + {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}}; + + fmlLibsMapping["1.4"] = libs14; + fmlLibsMapping["1.4.1"] = libs14; + fmlLibsMapping["1.4.2"] = libs14; + fmlLibsMapping["1.4.3"] = libs14; + fmlLibsMapping["1.4.4"] = libs14; + fmlLibsMapping["1.4.5"] = libs14; + fmlLibsMapping["1.4.6"] = libs14; + fmlLibsMapping["1.4.7"] = libs14; + + // 1.5 + fmlLibsMapping["1.5"] = QList<FMLlib>{ + {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, + {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, + {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, + {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, + {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false}, + {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; + + // 1.5.1 + fmlLibsMapping["1.5.1"] = QList<FMLlib>{ + {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, + {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, + {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, + {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, + {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false}, + {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; + + // 1.5.2 + fmlLibsMapping["1.5.2"] = QList<FMLlib>{ + {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false}, + {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false}, + {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false}, + {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true}, + {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false}, + {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}}; + + // don't use installers for those. + forgeInstallerBlacklist = QSet<QString>({ + "1.5.2" + }); +} diff --git a/logic/forge/ForgeData.h b/logic/forge/ForgeData.h new file mode 100644 index 00000000..27749778 --- /dev/null +++ b/logic/forge/ForgeData.h @@ -0,0 +1,21 @@ +#pragma once +#include <QMap> +#include <QString> +#include <QSet> + +struct FMLlib +{ + QString filename; + QString checksum; + bool ours; +}; + +struct ForgeData +{ + ForgeData(); + // mapping between minecraft versions and FML libraries required + QMap<QString, QList<FMLlib>> fmlLibsMapping; + // set of minecraft versions for which using forge installers is blacklisted + QSet<QString> forgeInstallerBlacklist; +}; +extern ForgeData g_forgeData; diff --git a/logic/forge/ForgeInstaller.cpp b/logic/forge/ForgeInstaller.cpp new file mode 100644 index 00000000..1204b855 --- /dev/null +++ b/logic/forge/ForgeInstaller.cpp @@ -0,0 +1,380 @@ +/* Copyright 2013 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 "ForgeInstaller.h" +#include "logic/VersionFinal.h" +#include "logic/OneSixLibrary.h" +#include "logic/net/HttpMetaCache.h" +#include "logic/tasks/Task.h" +#include "logic/OneSixInstance.h" +#include "logic/forge/ForgeVersionList.h" +#include "ForgeData.h" +#include "gui/dialogs/ProgressDialog.h" + +#include <quazip.h> +#include <quazipfile.h> +#include <pathutils.h> +#include <QStringList> +#include <QRegularExpression> +#include <QRegularExpressionMatch> +#include "MultiMC.h" +#include <QJsonDocument> +#include <QJsonArray> +#include <QSaveFile> +#include <QCryptographicHash> + +ForgeInstaller::ForgeInstaller() : BaseInstaller() +{ +} +void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl) +{ + std::shared_ptr<VersionFinal> newVersion; + m_universal_url = universalUrl; + + QuaZip zip(filename); + if (!zip.open(QuaZip::mdUnzip)) + return; + + QuaZipFile file(&zip); + + // read the install profile + if (!zip.setCurrentFile("install_profile.json")) + return; + + QJsonParseError jsonError; + if (!file.open(QIODevice::ReadOnly)) + return; + QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll(), &jsonError); + file.close(); + if (jsonError.error != QJsonParseError::NoError) + return; + + if (!jsonDoc.isObject()) + return; + + QJsonObject root = jsonDoc.object(); + + auto installVal = root.value("install"); + auto versionInfoVal = root.value("versionInfo"); + if (!installVal.isObject() || !versionInfoVal.isObject()) + return; + + // read the forge version info + { + newVersion = VersionFinal::fromJson(versionInfoVal.toObject()); + if (!newVersion) + return; + } + + QJsonObject installObj = installVal.toObject(); + QString libraryName = installObj.value("path").toString(); + internalPath = installObj.value("filePath").toString(); + m_forgeVersionString = installObj.value("version").toString().remove("Forge").trimmed(); + + // where do we put the library? decode the mojang path + OneSixLibrary lib(libraryName); + lib.finalize(); + + auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath()); + finalPath = "libraries/" + lib.storagePath(); + if (!ensureFilePathExists(finalPath)) + return; + + if (!zip.setCurrentFile(internalPath)) + return; + if (!file.open(QIODevice::ReadOnly)) + return; + { + QByteArray data = file.readAll(); + // extract file + QSaveFile extraction(finalPath); + if (!extraction.open(QIODevice::WriteOnly)) + return; + if (extraction.write(data) != data.size()) + return; + if (!extraction.commit()) + return; + QCryptographicHash md5sum(QCryptographicHash::Md5); + md5sum.addData(data); + + cacheentry->stale = false; + cacheentry->md5sum = md5sum.result().toHex().constData(); + MMC->metacache()->updateEntry(cacheentry); + } + file.close(); + + m_forge_json = newVersion; + realVersionId = m_forge_json->id = installObj.value("minecraft").toString(); +} +bool ForgeInstaller::add(OneSixInstance *to) +{ + if (!BaseInstaller::add(to)) + { + return false; + } + + QJsonObject obj; + obj.insert("order", 5); + + if (!m_forge_json) + return false; + int sliding_insert_window = 0; + { + QJsonArray librariesPlus; + + // for each library in the version we are adding (except for the blacklisted) + QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"}; + for (auto lib : m_forge_json->libraries) + { + QString libName = lib->name(); + // WARNING: This could actually break. + // if this is the actual forge lib, set an absolute url for the download + if (libName.contains("minecraftforge")) + { + lib->setAbsoluteUrl(m_universal_url); + } + else if (libName.contains("scala")) + { + lib->setHint("forge-pack-xz"); + } + if (blacklist.contains(libName)) + continue; + + QJsonObject libObj = lib->toJson(); + + bool found = false; + bool equals = false; + // find an entry that matches this one + for (auto tolib : to->getFullVersion()->vanillaLibraries) + { + if (tolib->name() != libName) + continue; + found = true; + if (tolib->toJson() == libObj) + { + equals = true; + } + // replace lib + libObj.insert("insert", QString("replace")); + break; + } + if (equals) + { + continue; + } + if (!found) + { + // add lib + libObj.insert("insert", QString("prepend")); + if (lib->name() == "minecraftforge") + { + libObj.insert("MMC-depend", QString("hard")); + } + sliding_insert_window++; + } + librariesPlus.prepend(libObj); + } + obj.insert("+libraries", librariesPlus); + obj.insert("mainClass", m_forge_json->mainClass); + QString args = m_forge_json->minecraftArguments; + QStringList tweakers; + { + QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)"); + QRegularExpressionMatch match = expression.match(args); + while (match.hasMatch()) + { + tweakers.append(match.captured(1)); + args.remove(match.capturedStart(), match.capturedLength()); + match = expression.match(args); + } + } + if (!args.isEmpty() && args != to->getFullVersion()->vanillaMinecraftArguments) + { + obj.insert("minecraftArguments", args); + } + if (!tweakers.isEmpty()) + { + obj.insert("+tweakers", QJsonArray::fromStringList(tweakers)); + } + if (!m_forge_json->processArguments.isEmpty() && + m_forge_json->processArguments != to->getFullVersion()->vanillaProcessArguments) + { + obj.insert("processArguments", m_forge_json->processArguments); + } + } + + obj.insert("name", QString("Forge")); + obj.insert("fileId", id()); + obj.insert("version", m_forgeVersionString); + obj.insert("mcVersion", to->intendedVersionId()); + + QFile file(filename(to->instanceRoot())); + if (!file.open(QFile::WriteOnly)) + { + QLOG_ERROR() << "Error opening" << file.fileName() + << "for reading:" << file.errorString(); + return false; + } + file.write(QJsonDocument(obj).toJson()); + file.close(); + + return true; +} + +bool ForgeInstaller::addLegacy(OneSixInstance *to) +{ + if (!BaseInstaller::add(to)) + { + return false; + } + + QJsonObject obj; + obj.insert("order", 5); + { + QJsonArray jarmodsPlus; + { + QJsonObject libObj; + libObj.insert("name", m_forge_version->universal_filename); + jarmodsPlus.append(libObj); + } + obj.insert("+jarMods", jarmodsPlus); + } + + obj.insert("name", QString("Forge")); + obj.insert("fileId", id()); + obj.insert("version", m_forge_version->jobbuildver); + obj.insert("mcVersion", to->intendedVersionId()); + if (g_forgeData.fmlLibsMapping.contains(m_forge_version->mcver)) + { + QJsonArray traitsPlus; + traitsPlus.append(QString("legacyFML")); + obj.insert("+traits", traitsPlus); + } + + QFile file(filename(to->instanceRoot())); + if (!file.open(QFile::WriteOnly)) + { + QLOG_ERROR() << "Error opening" << file.fileName() + << "for reading:" << file.errorString(); + return false; + } + file.write(QJsonDocument(obj).toJson()); + file.close(); + return true; +} + +class ForgeInstallTask : public Task +{ + Q_OBJECT +public: + ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance, + BaseVersionPtr version, QObject *parent = 0) + : Task(parent), m_installer(installer), m_instance(instance), m_version(version) + { + } + +protected: + void executeTask() override + { + setStatus(tr("Installing forge...")); + ForgeVersionPtr forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(m_version); + if (!forgeVersion) + { + emitFailed(tr("Unknown error occured")); + return; + } + prepare(forgeVersion); + } + void prepare(ForgeVersionPtr forgeVersion) + { + auto entry = + MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename()); + auto installFunction = [this, entry, forgeVersion]() + { + if (!install(entry, forgeVersion)) + { + QLOG_ERROR() << "Failure installing forge"; + emitFailed(tr("Failure to install forge")); + } + else + { + reload(); + } + }; + + if (entry->stale) + { + NetJob *fjob = new NetJob("Forge download"); + fjob->addNetAction(CacheDownload::make(forgeVersion->url(), entry)); + connect(fjob, &NetJob::progress, [this](qint64 current, qint64 total) + { setProgress(100 * current / qMax((qint64)1, total)); }); + connect(fjob, &NetJob::status, [this](const QString & msg) + { setStatus(msg); }); + connect(fjob, &NetJob::failed, [this]() + { emitFailed(tr("Failure to download forge")); }); + connect(fjob, &NetJob::succeeded, installFunction); + fjob->start(); + } + else + { + installFunction(); + } + } + bool install(const std::shared_ptr<MetaEntry> &entry, const ForgeVersionPtr &forgeVersion) + { + if (forgeVersion->usesInstaller()) + { + QString forgePath = entry->getFullPath(); + m_installer->prepare(forgePath, forgeVersion->universal_url); + return m_installer->add(m_instance); + } + else + return m_installer->addLegacy(m_instance); + } + void reload() + { + try + { + m_instance->reloadVersion(); + emitSucceeded(); + } + catch (MMCError &e) + { + emitFailed(e.cause()); + } + catch (...) + { + emitFailed(tr("Failed to load the version description file for reasons unknown.")); + } + } + +private: + ForgeInstaller *m_installer; + OneSixInstance *m_instance; + BaseVersionPtr m_version; +}; + +ProgressProvider *ForgeInstaller::createInstallTask(OneSixInstance *instance, + BaseVersionPtr version, QObject *parent) +{ + if (!version) + { + return nullptr; + } + m_forge_version = std::dynamic_pointer_cast<ForgeVersion>(version); + return new ForgeInstallTask(this, instance, version, parent); +} + +#include "ForgeInstaller.moc" diff --git a/logic/forge/ForgeInstaller.h b/logic/forge/ForgeInstaller.h new file mode 100644 index 00000000..dafa6305 --- /dev/null +++ b/logic/forge/ForgeInstaller.h @@ -0,0 +1,51 @@ +/* Copyright 2013 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 "logic/BaseInstaller.h" + +#include <QString> +#include <memory> + +class VersionFinal; +class ForgeInstallTask; +class ForgeVersion; + +class ForgeInstaller : public BaseInstaller +{ + friend class ForgeInstallTask; +public: + ForgeInstaller(); + virtual ~ForgeInstaller(){}; + virtual ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override; + +protected: + virtual QString id() const override { return "net.minecraftforge"; } + void prepare(const QString &filename, const QString &universalUrl); + bool add(OneSixInstance *to) override; + bool addLegacy(OneSixInstance *to); + +private: + // the parsed version json, read from the installer + std::shared_ptr<VersionFinal> m_forge_json; + // the actual forge version + std::shared_ptr<ForgeVersion> m_forge_version; + QString internalPath; + QString finalPath; + QString realVersionId; + QString m_forgeVersionString; + QString m_universal_url; +}; diff --git a/logic/forge/ForgeMirror.h b/logic/forge/ForgeMirror.h new file mode 100644 index 00000000..2518dffe --- /dev/null +++ b/logic/forge/ForgeMirror.h @@ -0,0 +1,10 @@ +#pragma once +#include <QString> + +struct ForgeMirror +{ + QString name; + QString logo_url; + QString website_url; + QString mirror_url; +};
\ No newline at end of file diff --git a/logic/forge/ForgeMirrors.cpp b/logic/forge/ForgeMirrors.cpp new file mode 100644 index 00000000..b224306f --- /dev/null +++ b/logic/forge/ForgeMirrors.cpp @@ -0,0 +1,118 @@ +#include "MultiMC.h" +#include "ForgeMirrors.h" +#include "logger/QsLog.h" +#include <algorithm> +#include <random> + +ForgeMirrors::ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist) +{ + m_libs = libs; + m_parent_job = parent_job; + m_url = QUrl(mirrorlist); + m_status = Job_NotStarted; +} + +void ForgeMirrors::start() +{ + QLOG_INFO() << "Downloading " << m_url.toString(); + QNetworkRequest request(m_url); + request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); + auto worker = MMC->qnam(); + QNetworkReply *rep = worker->get(request); + + m_reply = std::shared_ptr<QNetworkReply>(rep); + connect(rep, SIGNAL(downloadProgress(qint64, qint64)), + SLOT(downloadProgress(qint64, qint64))); + connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); + connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), + SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); +} + +void ForgeMirrors::downloadError(QNetworkReply::NetworkError error) +{ + // error happened during download. + QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit() + << "Network error: " << error; + m_status = Job_Failed; +} + +void ForgeMirrors::downloadFinished() +{ + // if the download succeeded + if (m_status != Job_Failed) + { + // nothing went wrong... ? + parseMirrorList(); + return; + } + // else the download failed, we use a fixed list + else + { + m_status = Job_Finished; + m_reply.reset(); + deferToFixedList(); + return; + } +} + +void ForgeMirrors::deferToFixedList() +{ + m_mirrors.clear(); + m_mirrors.append( + {"Minecraft Forge", "http://files.minecraftforge.net/forge_logo.png", + "http://files.minecraftforge.net/", "http://files.minecraftforge.net/maven/"}); + m_mirrors.append({"Creeper Host", + "http://files.minecraftforge.net/forge_logo.png", + "https://www.creeperhost.net/link.php?id=1", + "http://new.creeperrepo.net/forge/maven/"}); + injectDownloads(); + emit succeeded(m_index_within_job); +} + +void ForgeMirrors::parseMirrorList() +{ + m_status = Job_Finished; + auto data = m_reply->readAll(); + m_reply.reset(); + auto dataLines = data.split('\n'); + for(auto line: dataLines) + { + auto elements = line.split('!'); + if (elements.size() == 4) + { + m_mirrors.append({elements[0],elements[1],elements[2],elements[3]}); + } + } + if(!m_mirrors.size()) + deferToFixedList(); + injectDownloads(); + emit succeeded(m_index_within_job); +} + +void ForgeMirrors::injectDownloads() +{ + // shuffle the mirrors randomly + std::random_device rd; + std::mt19937 rng(rd()); + std::shuffle(m_mirrors.begin(), m_mirrors.end(), rng); + + // tell parent to download the libs + for(auto lib: m_libs) + { + lib->setMirrors(m_mirrors); + m_parent_job->addNetAction(lib); + } +} + +void ForgeMirrors::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + m_total_progress = bytesTotal; + m_progress = bytesReceived; + emit progress(m_index_within_job, bytesReceived, bytesTotal); +} + +void ForgeMirrors::downloadReadyRead() +{ +} diff --git a/logic/forge/ForgeMirrors.h b/logic/forge/ForgeMirrors.h new file mode 100644 index 00000000..d25762db --- /dev/null +++ b/logic/forge/ForgeMirrors.h @@ -0,0 +1,58 @@ +/* Copyright 2013 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 "logic/net/NetAction.h" +#include "logic/net/HttpMetaCache.h" +#include "logic/net/NetJob.h" +#include "logic/forge/ForgeXzDownload.h" +#include <QFile> +#include <QTemporaryFile> +typedef std::shared_ptr<class ForgeMirrors> ForgeMirrorsPtr; + +class ForgeMirrors : public NetAction +{ + Q_OBJECT +public: + QList<ForgeXzDownloadPtr> m_libs; + NetJobPtr m_parent_job; + QList<ForgeMirror> m_mirrors; + +public: + explicit ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist); + static ForgeMirrorsPtr make(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist) + { + return ForgeMirrorsPtr(new ForgeMirrors(libs, parent_job, mirrorlist)); + } + virtual ~ForgeMirrors(){}; +protected +slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +private: + void parseMirrorList(); + void deferToFixedList(); + void injectDownloads(); + +public +slots: + virtual void start(); +}; diff --git a/logic/forge/ForgeVersion.cpp b/logic/forge/ForgeVersion.cpp new file mode 100644 index 00000000..fd4efc4b --- /dev/null +++ b/logic/forge/ForgeVersion.cpp @@ -0,0 +1,55 @@ +#include "ForgeVersion.h" +#include "ForgeData.h" +#include <QObject> + +QString ForgeVersion::name() +{ + return "Forge " + jobbuildver; +} + +QString ForgeVersion::descriptor() +{ + return universal_filename; +} + +QString ForgeVersion::typeString() const +{ + if (is_recommended) + return QObject::tr("Recommended"); + return QString(); +} + +bool ForgeVersion::operator<(BaseVersion &a) +{ + ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a); + if (!pa) + return true; + return m_buildnr < pa->m_buildnr; +} + +bool ForgeVersion::operator>(BaseVersion &a) +{ + ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a); + if (!pa) + return false; + return m_buildnr > pa->m_buildnr; +} + +bool ForgeVersion::usesInstaller() +{ + if(installer_url.isEmpty()) + return false; + if(g_forgeData.forgeInstallerBlacklist.contains(mcver)) + return false; + return true; +} + +QString ForgeVersion::filename() +{ + return usesInstaller() ? installer_filename : universal_filename; +} + +QString ForgeVersion::url() +{ + return usesInstaller() ? installer_url : universal_url; +} diff --git a/logic/forge/ForgeVersion.h b/logic/forge/ForgeVersion.h new file mode 100644 index 00000000..74e45c5a --- /dev/null +++ b/logic/forge/ForgeVersion.h @@ -0,0 +1,31 @@ +#pragma once +#include <QString> +#include <memory> +#include "logic/BaseVersion.h" + +struct ForgeVersion; +typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr; + +struct ForgeVersion : public BaseVersion +{ + virtual QString descriptor() override; + virtual QString name() override; + virtual QString typeString() const override; + virtual bool operator<(BaseVersion &a) override; + virtual bool operator>(BaseVersion &a) override; + + QString filename(); + QString url(); + + bool usesInstaller(); + + int m_buildnr = 0; + QString universal_url; + QString changelog_url; + QString installer_url; + QString jobbuildver; + QString mcver; + QString universal_filename; + QString installer_filename; + bool is_recommended = false; +}; diff --git a/logic/forge/ForgeVersionList.cpp b/logic/forge/ForgeVersionList.cpp new file mode 100644 index 00000000..c873bab7 --- /dev/null +++ b/logic/forge/ForgeVersionList.cpp @@ -0,0 +1,435 @@ +/* Copyright 2013 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 "logic/forge/ForgeVersionList.h" +#include "logic/forge/ForgeVersion.h" +#include "logic/net/NetJob.h" +#include "logic/net/URLConstants.h" +#include "MultiMC.h" + +#include <QtNetwork> +#include <QtXml> +#include <QRegExp> + +#include "logger/QsLog.h" + +ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent) +{ +} + +Task *ForgeVersionList::getLoadTask() +{ + return new ForgeListLoadTask(this); +} + +bool ForgeVersionList::isLoaded() +{ + return m_loaded; +} + +const BaseVersionPtr ForgeVersionList::at(int i) const +{ + return m_vlist.at(i); +} + +int ForgeVersionList::count() const +{ + return m_vlist.count(); +} + +int ForgeVersionList::columnCount(const QModelIndex &parent) const +{ + return 3; +} + +QVariant ForgeVersionList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + auto version = std::dynamic_pointer_cast<ForgeVersion>(m_vlist[index.row()]); + switch (role) + { + case Qt::DisplayRole: + switch (index.column()) + { + case 0: + return version->name(); + + case 1: + return version->mcver; + + case 2: + return version->typeString(); + default: + return QVariant(); + } + + case Qt::ToolTipRole: + return version->descriptor(); + + case VersionPointerRole: + return qVariantFromValue(m_vlist[index.row()]); + + default: + return QVariant(); + } +} + +QVariant ForgeVersionList::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + switch (section) + { + case 0: + return "Version"; + + case 1: + return "Minecraft"; + + case 2: + return "Type"; + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case 0: + return "The name of the version."; + + case 1: + return "Minecraft version"; + + case 2: + return "The version's type."; + + default: + return QVariant(); + } + + default: + return QVariant(); + } +} + +BaseVersionPtr ForgeVersionList::getLatestStable() const +{ + return BaseVersionPtr(); +} + +void ForgeVersionList::updateListData(QList<BaseVersionPtr> versions) +{ + beginResetModel(); + m_vlist = versions; + m_loaded = true; + endResetModel(); + // NOW SORT!! + // sort(); +} + +void ForgeVersionList::sort() +{ + // NO-OP for now +} + +ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task() +{ + m_list = vlist; +} + +void ForgeListLoadTask::executeTask() +{ + setStatus(tr("Fetching Forge version lists...")); + auto job = new NetJob("Version index"); + // we do not care if the version is stale or not. + auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); + auto gradleForgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "json"); + + // verify by poking the server. + forgeListEntry->stale = true; + gradleForgeListEntry->stale = true; + + job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::FORGE_LEGACY_URL), + forgeListEntry)); + job->addNetAction(gradleListDownload = CacheDownload::make( + QUrl(URLConstants::FORGE_GRADLE_URL), gradleForgeListEntry)); + + connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed())); + connect(gradleListDownload.get(), SIGNAL(failed(int)), SLOT(gradleListFailed())); + + listJob.reset(job); + connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded())); + connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); + listJob->start(); +} + +bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out) +{ + QByteArray data; + { + auto dlJob = listDownload; + auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath(); + QFile listFile(filename); + if (!listFile.open(QIODevice::ReadOnly)) + { + return false; + } + data = listFile.readAll(); |
