aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2014-05-11 12:37:21 +0200
committerPetr Mrázek <peterix@gmail.com>2014-06-09 01:38:30 +0200
commit69c3e7111f93290d1278d6116e9fd50079b4fe79 (patch)
treecbaed3022e8705f1da29777afea0fca16c1abe60
parent92abe4c603e1f4931cd02ae6b752cb7054d8e30d (diff)
downloadPrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.gz
PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.bz2
PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.zip
Make 1.6+ work with new instance format.
-rw-r--r--CMakeLists.txt2
-rw-r--r--logic/MMCJson.cpp23
-rw-r--r--logic/MMCJson.h19
-rw-r--r--logic/OneSixUpdate.cpp144
-rw-r--r--logic/OneSixUpdate.h10
-rw-r--r--logic/VersionFilterData.cpp12
-rw-r--r--logic/VersionFilterData.h7
-rw-r--r--logic/minecraft/JarMod.cpp12
-rw-r--r--logic/minecraft/JarMod.h1
-rw-r--r--logic/minecraft/MinecraftVersion.cpp60
-rw-r--r--logic/minecraft/MinecraftVersion.h73
-rw-r--r--logic/minecraft/MinecraftVersionList.cpp518
-rw-r--r--logic/minecraft/MinecraftVersionList.h36
-rw-r--r--logic/minecraft/OneSixLibrary.cpp79
-rw-r--r--logic/minecraft/OneSixLibrary.h32
-rw-r--r--logic/minecraft/OneSixRule.h7
-rw-r--r--logic/minecraft/ParseUtils.cpp7
-rw-r--r--logic/minecraft/ParseUtils.h2
-rw-r--r--logic/minecraft/RawLibrary.cpp155
-rw-r--r--logic/minecraft/RawLibrary.h37
-rw-r--r--logic/minecraft/VersionBuilder.h6
-rw-r--r--logic/minecraft/VersionFile.cpp203
-rw-r--r--logic/minecraft/VersionFile.h6
-rw-r--r--logic/minecraft/VersionPatch.h2
-rw-r--r--logic/minecraft/VersionSource.h9
-rw-r--r--logic/tasks/ProgressProvider.h1
-rw-r--r--resources/versions/LWJGL/2.9.0.json2
-rw-r--r--resources/versions/LWJGL/2.9.1-nightly-20130708-debug3.json2
-rw-r--r--resources/versions/LWJGL/2.9.1.json2
29 files changed, 929 insertions, 540 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0be887dc..57b6352f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -485,11 +485,13 @@ SET(MULTIMC_SOURCES
logic/minecraft/RawLibrary.h
logic/minecraft/VersionBuilder.cpp
logic/minecraft/VersionBuilder.h
+ logic/minecraft/VersionBuildError.h
logic/minecraft/VersionFile.cpp
logic/minecraft/VersionFile.h
logic/minecraft/VersionFinal.cpp
logic/minecraft/VersionFinal.h
logic/minecraft/VersionPatch.h
+ logic/minecraft/VersionSource.h
# Various base classes
logic/BaseInstaller.h
diff --git a/logic/MMCJson.cpp b/logic/MMCJson.cpp
index 65423436..f7b4b6b0 100644
--- a/logic/MMCJson.cpp
+++ b/logic/MMCJson.cpp
@@ -11,7 +11,7 @@ bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what)
{
- if(val.isNull())
+ if(val.isUndefined() || val.isUndefined())
throw JSONValidationError(what + " does not exist");
return val;
}
@@ -59,3 +59,24 @@ QString MMCJson::ensureString(const QJsonValue val, const QString what)
return val.toString();
}
+void MMCJson::writeString(QJsonObject &to, QString key, QString value)
+{
+ if(value.size())
+ {
+ to.insert(key, value);
+ }
+}
+
+void MMCJson::writeStringList(QJsonObject &to, QString key, QStringList values)
+{
+ if(values.size())
+ {
+ QJsonArray array;
+ for(auto value: values)
+ {
+ array.append(value);
+ }
+ to.insert(key, array);
+ }
+}
+
diff --git a/logic/MMCJson.h b/logic/MMCJson.h
index 71ded435..8408f29b 100644
--- a/logic/MMCJson.h
+++ b/logic/MMCJson.h
@@ -43,4 +43,23 @@ int ensureInteger(const QJsonValue val, QString what = "value");
/// make sure the value is converted into a double precision floating number. throw otherwise.
double ensureDouble(const QJsonValue val, QString what = "value");
+
+void writeString(QJsonObject & to, QString key, QString value);
+
+void writeStringList (QJsonObject & to, QString key, QStringList values);
+
+template <typename T>
+void writeObjectList (QJsonObject & to, QString key, QList<T> values)
+{
+ if(values.size())
+ {
+ QJsonArray array;
+ for(auto value: values)
+ {
+ array.append(value->toJson());
+ }
+ to.insert(key, array);
+ }
+}
}
+
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index f77913dc..2a7bb5b6 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -34,15 +34,12 @@
#include "logic/net/URLConstants.h"
#include "logic/assets/AssetsUtils.h"
-OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent)
- : Task(parent), m_inst(inst)
+OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
}
void OneSixUpdate::executeTask()
{
- QString intendedVersion = m_inst->intendedVersionId();
-
// Make directories
QDir mcDir(m_inst->minecraftRoot());
if (!mcDir.exists() && !mcDir.mkpath("."))
@@ -51,97 +48,37 @@ void OneSixUpdate::executeTask()
return;
}
- if (m_inst->shouldUpdate())
+ // Get a pointer to the version object that corresponds to the instance's version.
+ targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
+ MMC->minecraftlist()->findVersion(m_inst->intendedVersionId()));
+ if (targetVersion == nullptr)
{
- // Get a pointer to the version object that corresponds to the instance's version.
- targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
- MMC->minecraftlist()->findVersion(intendedVersion));
- if (targetVersion == nullptr)
- {
- // don't do anything if it was invalid
- emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
- return;
- }
- // builtins need no updates, so only update for Mojang
- if(targetVersion->m_versionSource == MinecraftVersion::Mojang)
- {
- versionFileStart();
- return;
- }
+ // don't do anything if it was invalid
+ emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
+ return;
}
- jarlibStart();
-}
-
-void OneSixUpdate::versionFileStart()
-{
- if (m_inst->providesVersionFile())
+ if (m_inst->providesVersionFile() || !targetVersion->needsUpdate())
{
jarlibStart();
return;
}
- QLOG_INFO() << m_inst->name() << ": getting version file.";
- setStatus(tr("Getting the version files from Mojang..."));
-
- QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS +
- targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json";
- auto job = new NetJob("Version index");
- job->addNetAction(ByteArrayDownload::make(QUrl(urlstr)));
- specificVersionDownloadJob.reset(job);
- connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished()));
- connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed()));
- connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
- SIGNAL(progress(qint64, qint64)));
- specificVersionDownloadJob->start();
-}
-
-void OneSixUpdate::versionFileFinished()
-{
- NetActionPtr DlJob = specificVersionDownloadJob->first();
-
- QString version_id = targetVersion->descriptor();
- QString inst_dir = m_inst->instanceRoot();
- // save the version file in $instanceId/version.json
- {
- QString version1 = PathCombine(inst_dir, "/version.json");
- ensureFilePathExists(version1);
- // FIXME: detect errors here, download to a temp file, swap
- QSaveFile vfile1(version1);
- if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
- {
- emitFailed(tr("Can't open %1 for writing.").arg(version1));
- return;
- }
- auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
- qint64 actual = 0;
- if ((actual = vfile1.write(data)) != data.size())
- {
- emitFailed(tr("Failed to write into %1. Written %2 out of %3.").arg(version1).arg(actual).arg(data.size()));
- return;
- }
- if (!vfile1.commit())
- {
- emitFailed(tr("Can't commit changes to %1").arg(version1));
- return;
- }
- }
-
- // the version is downloaded safely. update is 'done' at this point
- m_inst->setShouldUpdate(false);
-
- // delete any custom version inside the instance (it's no longer relevant, we did an update)
- QString custom = PathCombine(inst_dir, "/custom.json");
- QFile finfo(custom);
- if (finfo.exists())
+ versionUpdateTask = MMC->minecraftlist()->createUpdateTask(m_inst->intendedVersionId());
+ if (!versionUpdateTask)
{
- finfo.remove();
+ jarlibStart();
+ return;
}
- // NOTE: Version is reloaded in jarlibStart
- jarlibStart();
+ connect(versionUpdateTask.get(), SIGNAL(succeeded()), SLOT(jarlibStart()));
+ connect(versionUpdateTask.get(), SIGNAL(failed(QString)), SLOT(versionUpdateFailed(QString)));
+ connect(versionUpdateTask.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+ setStatus(tr("Getting the version files from Mojang..."));
+ versionUpdateTask->start();
}
-void OneSixUpdate::versionFileFailed()
+void OneSixUpdate::versionUpdateFailed(QString reason)
{
- emitFailed(tr("Failed to download the version description. Try again."));
+ emitFailed(reason);
}
void OneSixUpdate::assetIndexStart()
@@ -236,12 +173,12 @@ void OneSixUpdate::jarlibStart()
{
inst->reloadVersion();
}
- catch(MMCError & e)
+ catch (MMCError &e)
{
emitFailed(e.cause());
return;
}
- catch(...)
+ catch (...)
{
emitFailed(tr("Failed to load the version description file for reasons unknown."));
return;
@@ -275,7 +212,7 @@ void OneSixUpdate::jarlibStart()
{
if (lib->hint() == "local")
{
- if(!lib->filesExist(m_inst->librariesPath()))
+ if (!lib->filesExist(m_inst->librariesPath()))
brokenLocalLibs.append(lib);
continue;
}
@@ -312,16 +249,19 @@ void OneSixUpdate::jarlibStart()
f(raw_storage, raw_dl);
}
}
- if(!brokenLocalLibs.empty())
+ if (!brokenLocalLibs.empty())
{
jarlibDownloadJob.reset();
QStringList failed;
- for(auto brokenLib : brokenLocalLibs)
+ for (auto brokenLib : brokenLocalLibs)
{
failed.append(brokenLib->files());
}
QString failed_all = failed.join("\n");
- emitFailed(tr("Some libraries marked as 'local' are missing their jar files:\n%1\n\nYou'll have to correct this problem manually. If this is an externally tracked instance, make sure to run it at least once outside of MultiMC.").arg(failed_all));
+ emitFailed(tr("Some libraries marked as 'local' are missing their jar "
+ "files:\n%1\n\nYou'll have to correct this problem manually. If this is "
+ "an externally tracked instance, make sure to run it at least once "
+ "outside of MultiMC.").arg(failed_all));
return;
}
// TODO: think about how to propagate this from the original json file... or IF AT ALL
@@ -344,27 +284,27 @@ void OneSixUpdate::jarlibFinished()
{
OneSixInstance *inst = (OneSixInstance *)m_inst;
std::shared_ptr<VersionFinal> version = inst->getFullVersion();
-
+
// create stripped jar, if needed
- if(version->hasJarMods())
+ if (version->hasJarMods())
{
- //FIXME: good candidate for moving elsewhere (jar location resolving/version caching).
+ // FIXME: good candidate for moving elsewhere (jar location resolving/version caching).
QString version_id = version->id;
QString localPath = version_id + "/" + version_id + ".jar";
QString strippedPath = version_id + "/" + version_id + "-stripped.jar";
auto metacache = MMC->metacache();
auto entry = metacache->resolveEntry("versions", localPath);
auto entryStripped = metacache->resolveEntry("versions", strippedPath);
-
+
QString fullJarPath = entry->getFullPath();
QString fullStrippedJarPath = entryStripped->getFullPath();
-
- if(entry->md5sum != jarHashOnEntry || !QFileInfo::exists(fullStrippedJarPath))
+
+ if (entry->md5sum != jarHashOnEntry || !QFileInfo::exists(fullStrippedJarPath))
{
stripJar(fullJarPath, fullStrippedJarPath);
}
}
- if(version->traits.contains("legacyFML"))
+ if (version->traits.contains("legacyFML"))
{
fmllibsStart();
}
@@ -378,7 +318,8 @@ void OneSixUpdate::jarlibFailed()
{
QStringList failed = jarlibDownloadJob->getFailedFiles();
QString failed_all = failed.join("\n");
- emitFailed(tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
+ emitFailed(
+ tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
}
void OneSixUpdate::stripJar(QString origPath, QString newPath)
@@ -462,7 +403,6 @@ bool OneSixUpdate::MergeZipFiles(QuaZip *into, QString from)
return true;
}
-
void OneSixUpdate::fmllibsStart()
{
// Get the mod list
@@ -471,7 +411,7 @@ void OneSixUpdate::fmllibsStart()
bool forge_present = false;
QString version = inst->intendedVersionId();
- auto & fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
+ auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
if (!fmlLibsMapping.contains(version))
{
assetIndexStart();
@@ -528,7 +468,7 @@ void OneSixUpdate::fmllibsStart()
void OneSixUpdate::fmllibsFinished()
{
legacyDownloadJob.reset();
- if(!fmlLibsToProcess.isEmpty())
+ if (!fmlLibsToProcess.isEmpty())
{
setStatus(tr("Copying FML libraries into the instance..."));
OneSixInstance *inst = (OneSixInstance *)m_inst;
@@ -539,7 +479,7 @@ void OneSixUpdate::fmllibsFinished()
progress(index, fmlLibsToProcess.size());
auto entry = metacache->resolveEntry("fmllibs", lib.filename);
auto path = PathCombine(inst->libDir(), lib.filename);
- if(!ensureFilePathExists(path))
+ if (!ensureFilePathExists(path))
{
emitFailed(tr("Failed creating FML library folder inside the instance."));
return;
diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h
index 00f7c135..139143db 100644
--- a/logic/OneSixUpdate.h
+++ b/logic/OneSixUpdate.h
@@ -36,9 +36,7 @@ public:
private
slots:
- void versionFileStart();
- void versionFileFinished();
- void versionFileFailed();
+ void versionUpdateFailed(QString reason);
void jarlibStart();
void jarlibFinished();
@@ -58,12 +56,14 @@ slots:
void stripJar(QString origPath, QString newPath);
bool MergeZipFiles(QuaZip *into, QString from);
private:
- NetJobPtr specificVersionDownloadJob;
NetJobPtr jarlibDownloadJob;
NetJobPtr legacyDownloadJob;
- // target version, determined during this task
+ /// target version, determined during this task
std::shared_ptr<MinecraftVersion> targetVersion;
+ /// the task that is spawned for version updates
+ std::shared_ptr<Task> versionUpdateTask;
+
OneSixInstance *m_inst = nullptr;
QString jarHashOnEntry;
QList<FMLlib> fmlLibsToProcess;
diff --git a/logic/VersionFilterData.cpp b/logic/VersionFilterData.cpp
index 8322dad4..8b521266 100644
--- a/logic/VersionFilterData.cpp
+++ b/logic/VersionFilterData.cpp
@@ -1,4 +1,5 @@
#include "VersionFilterData.h"
+#include "minecraft/ParseUtils.h"
extern VersionFilterData g_VersionFilterData = VersionFilterData();
@@ -57,8 +58,11 @@ VersionFilterData::VersionFilterData()
// don't use installers for those.
forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
- legacyLaunchWhitelist =
- QSet<QString>({"1.5.2", "1.5.1", "1.5", "1.4.7", "1.4.6", "1.4.5", "1.4.4", "1.4.3",
- "1.4.2", "1.4.1", "1.4", "1.3.2", "1.3.1", "1.3", "1.2.5", "1.2.4",
- "1.2.3", "1.2.2", "1.2.1", "1.1", "1.0.1", "1.0"});
+ // these won't show up in version lists because they are extremely bad and dangerous
+ legacyBlacklist = QSet<QString>({"rd-160052"});
+ /*
+ * nothing older than this will be accepted from Mojang servers
+ * (these versions need to be tested by us first)
+ */
+ legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00");
}
diff --git a/logic/VersionFilterData.h b/logic/VersionFilterData.h
index 562654a9..e010adc7 100644
--- a/logic/VersionFilterData.h
+++ b/logic/VersionFilterData.h
@@ -2,6 +2,7 @@
#include <QMap>
#include <QString>
#include <QSet>
+#include <QDateTime>
struct FMLlib
{
@@ -17,7 +18,9 @@ struct VersionFilterData
QMap<QString, QList<FMLlib>> fmlLibsMapping;
// set of minecraft versions for which using forge installers is blacklisted
QSet<QString> forgeInstallerBlacklist;
- // set of 'legacy' versions (ones that use the legacy launch)
- QSet<QString> legacyLaunchWhitelist;
+ // set of 'legacy' versions that will not show up in the version lists.
+ QSet<QString> legacyBlacklist;
+ // no new versions below this date will be accepted from Mojang servers
+ QDateTime legacyCutoffDate;
};
extern VersionFilterData g_VersionFilterData;
diff --git a/logic/minecraft/JarMod.cpp b/logic/minecraft/JarMod.cpp
index 99a30aa5..18a9411c 100644
--- a/logic/minecraft/JarMod.cpp
+++ b/logic/minecraft/JarMod.cpp
@@ -1,5 +1,6 @@
#include "JarMod.h"
#include "logic/MMCJson.h"
+using namespace MMCJson;
JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename)
{
@@ -28,6 +29,7 @@ JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename)
};
readString("url", out->baseurl);
+ readString("MMC-hint", out->hint);
readString("MMC-absoluteUrl", out->absoluteUrl);
if(!out->baseurl.isEmpty() && out->absoluteUrl.isEmpty())
{
@@ -36,6 +38,16 @@ JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename)
return out;
}
+QJsonObject Jarmod::toJson()
+{
+ QJsonObject out;
+ writeString(out, "name", name);
+ writeString(out, "url", baseurl);
+ writeString(out, "MMC-absoluteUrl", absoluteUrl);
+ writeString(out, "MMC-hint", hint);
+ return out;
+}
+
QString Jarmod::url()
{
if(!absoluteUrl.isEmpty())
diff --git a/logic/minecraft/JarMod.h b/logic/minecraft/JarMod.h
index da5d8db0..c438dbcd 100644
--- a/logic/minecraft/JarMod.h
+++ b/logic/minecraft/JarMod.h
@@ -8,6 +8,7 @@ class Jarmod
{
public: /* methods */
static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename);
+ QJsonObject toJson();
QString url();
public: /* data */
QString name;
diff --git a/logic/minecraft/MinecraftVersion.cpp b/logic/minecraft/MinecraftVersion.cpp
index 2191e8af..e0cbce8d 100644
--- a/logic/minecraft/MinecraftVersion.cpp
+++ b/logic/minecraft/MinecraftVersion.cpp
@@ -1,29 +1,26 @@
#include "MinecraftVersion.h"
#include "VersionFinal.h"
+#include "VersionBuildError.h"
+#include "VersionBuilder.h"
bool MinecraftVersion::usesLegacyLauncher()
{
return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch");
}
+
QString MinecraftVersion::descriptor()
{
return m_descriptor;
}
+
QString MinecraftVersion::name()
{
return m_name;
}
+
QString MinecraftVersion::typeString() const
{
- if (is_latest && is_snapshot)
- {
- return QObject::tr("Latest snapshot");
- }
- else if (is_latest)
- {
- return QObject::tr("Latest release");
- }
- else if (is_snapshot)
+ if (is_snapshot)
{
return QObject::tr("Snapshot");
}
@@ -32,22 +29,41 @@ QString MinecraftVersion::typeString() const
return QObject::tr("Regular release");
}
}
+
bool MinecraftVersion::hasJarMods()
{
return false;
}
-bool MinecraftVersion::isVanilla()
+
+bool MinecraftVersion::isMinecraftVersion()
{
return true;
}
+// 1. assume the local file is good. load, check. If it's good, apply.
+// 2. if discrepancies are found, fall out and fail (impossible to apply incomplete version).
+void MinecraftVersion::applyFileTo(VersionFinal *version)
+{
+ QFileInfo versionFile(QString("versions/%1/%1.json").arg(m_descriptor));
+
+ auto versionObj = VersionBuilder::parseJsonFile(versionFile, false, false);
+ versionObj->applyTo(version);
+}
+
void MinecraftVersion::applyTo(VersionFinal *version)
{
- // FIXME: make this work.
- if(m_versionSource != Builtin)
+ // do we have this one cached?
+ if (m_versionSource == Local)
{
+ applyFileTo(version);
return;
}
+ // if not builtin, do not proceed any further.
+ if (m_versionSource != Builtin)
+ {
+ throw VersionIncomplete(QObject::tr(
+ "Minecraft version %1 could not be applied: version files are missing.").arg(m_descriptor));
+ }
if (!m_descriptor.isNull())
{
version->id = m_descriptor;
@@ -81,15 +97,35 @@ void MinecraftVersion::applyTo(VersionFinal *version)
}
version->traits.unite(m_traits);
}
+
int MinecraftVersion::getOrder()
{
return order;
}
+
void MinecraftVersion::setOrder(int order)
{
this->order = order;
}
+
QList<JarmodPtr> MinecraftVersion::getJarMods()
{
return QList<JarmodPtr>();
}
+
+QString MinecraftVersion::getPatchName()
+{
+ return "Minecraft";
+}
+QString MinecraftVersion::getPatchVersion()
+{
+ return m_descriptor;
+}
+QString MinecraftVersion::getPatchID()
+{
+ return "net.minecraft";
+}
+QString MinecraftVersion::getPatchFilename()
+{
+ return QString();
+}
diff --git a/logic/minecraft/MinecraftVersion.h b/logic/minecraft/MinecraftVersion.h
index 6a1c54cb..02afd709 100644
--- a/logic/minecraft/MinecraftVersion.h
+++ b/logic/minecraft/MinecraftVersion.h
@@ -22,26 +22,49 @@
#include "logic/BaseVersion.h"
#include "VersionPatch.h"
#include "VersionFile.h"
+#include "VersionSource.h"
class VersionFinal;
+class MinecraftVersion;
+typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr;
-struct MinecraftVersion : public BaseVersion, public VersionPatch
+class MinecraftVersion : public BaseVersion, public VersionPatch
{
+public: /* methods */
+ bool usesLegacyLauncher();
+ virtual QString descriptor() override;
+ virtual QString name() override;
+ virtual QString typeString() const override;
+ virtual bool hasJarMods() override;
+ virtual bool isMinecraftVersion() override;
+ virtual void applyTo(VersionFinal *version) override;
+ virtual int getOrder();
+ virtual void setOrder(int order);
+ virtual QList<JarmodPtr> getJarMods() override;
+ virtual QString getPatchID() override;
+ virtual QString getPatchVersion() override;
+ virtual QString getPatchName() override;
+ virtual QString getPatchFilename() override;
+ bool needsUpdate()
+ {
+ return m_versionSource == Remote;
+ }
+ bool hasUpdate()
+ {
+ return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate);
+ }
+
+private: /* methods */
+ void applyFileTo(VersionFinal *version);
+
+public: /* data */
/// The URL that this version will be downloaded from. maybe.
QString download_url;
- /// is this the latest version?
- bool is_latest = false;
-
/// is this a snapshot?
bool is_snapshot = false;
- /// where is this from?
- enum VersionSource
- {
- Builtin,
- Mojang
- } m_versionSource = Builtin;
+ VersionSource m_versionSource = Builtin;
/// the human readable version name
QString m_name;
@@ -74,31 +97,7 @@ struct MinecraftVersion : public BaseVersion, public VersionPatch
/// order of this file... default = -2
int order = -2;
-
- bool usesLegacyLauncher();
- virtual QString descriptor() override;
- virtual QString name() override;
- virtual QString typeString() const override;
- virtual bool hasJarMods() override;
- virtual bool isVanilla() override;
- virtual void applyTo(VersionFinal *version) override;
- virtual int getOrder();
- virtual void setOrder(int order);
- virtual QList<JarmodPtr> getJarMods() override;
- virtual QString getPatchID()
- {
- return "net.minecraft";
- }
- virtual QString getPatchVersion()
- {
- return m_descriptor;
- }
- virtual QString getPatchName()
- {
- return "Minecraft";
- }
- virtual QString getPatchFilename()
- {
- return QString();
- };
+
+ /// an update available from Mojang
+ MinecraftVersionPtr upstreamUpdate;
};
diff --git a/logic/minecraft/MinecraftVersionList.cpp b/logic/minecraft/MinecraftVersionList.cpp
index 1aa220e8..5a5ea348 100644
--- a/logic/minecraft/MinecraftVersionList.cpp
+++ b/logic/minecraft/MinecraftVersionList.cpp
@@ -13,27 +13,35 @@
* limitations under the License.
*/
-#include "MinecraftVersionList.h"
-#include "MultiMC.h"
-#include "logic/net/URLConstants.h"
+#include <QtXml>
#include "logic/MMCJson.h"
-#include "ParseUtils.h"
+#include <QtAlgorithms>
+#include <QtNetwork>
-#include <QtXml>
+#include "MultiMC.h"
+#include "MMCError.h"
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <QJsonParseError>
+#include "MinecraftVersionList.h"
+#include "logic/net/URLConstants.h"
-#include <QtAlgorithms>
+#include "ParseUtils.h"
+#include "VersionBuilder.h"
+#include <logic/VersionFilterData.h>
+#include <pathutils.h>
-#include <QtNetwork>
+class ListLoadError : public MMCError
+{
+public:
+ ListLoadError(QString cause) : MMCError(cause) {};
+ virtual ~ListLoadError() noexcept
+ {
+ }
+};
MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
{
loadBuiltinList();
+ loadCachedList();
}
Task *MinecraftVersionList::getLoadTask()
@@ -68,6 +76,35 @@ void MinecraftVersionList::sortInternal()
qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
}
+void MinecraftVersionList::loadCachedList()
+{
+ QFile localIndex("versions/versions.json");
+ if (!localIndex.exists())
+ {
+ return;
+ }
+ if (!localIndex.open(QIODevice::ReadOnly))
+ {
+ // FIXME: this is actually a very bad thing! How do we deal with this?
+ QLOG_ERROR() << "The minecraft version cache can't be read.";
+ return;
+ }
+ auto data = localIndex.readAll();
+ try
+ {
+ loadMojangList(data, Local);
+ }
+ catch (MMCError &e)
+ {
+ // the cache has gone bad for some reason... flush it.
+ QLOG_ERROR() << "The minecraft version cache is corrupted. Flushing cache.";
+ localIndex.close();
+ localIndex.remove();
+ return;
+ }
+ m_hasLocalIndex = true;
+}
+
void MinecraftVersionList::loadBuiltinList()
{
// grab the version list data from internal resources.
@@ -93,19 +130,22 @@ void MinecraftVersionList::loadBuiltinList()
continue;
}
+ if (g_VersionFilterData.legacyBlacklist.contains(versionID))
+ {
+ QLOG_ERROR() << "Blacklisted legacy version ignored: " << versionID;
+ continue;
+ }
+
// Now, we construct the version object and add it to the list.
std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
mcVersion->m_name = mcVersion->m_descriptor = versionID;
// Parse the timestamp.
- try
- {
- parse_timestamp(versionObj.value("releaseTime").toString(""),
- mcVersion->m_releaseTimeString, mcVersion->m_releaseTime);
- }
- catch (MMCError &e)
+ if (!parse_timestamp(versionObj.value("releaseTime").toString(""),
+ mcVersion->m_releaseTimeString, mcVersion->m_releaseTime))
{
- QLOG_ERROR() << "Error while parsing version" << versionID << ":" << e.cause();
+ QLOG_ERROR() << "Error while parsing version" << versionID
+ << ": invalid version timestamp";
continue;
}
@@ -113,7 +153,7 @@ void MinecraftVersionList::loadBuiltinList()
mcVersion->download_url =
"http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
- mcVersion->m_versionSource = MinecraftVersion::Builtin;
+ mcVersion->m_versionSource = Builtin;
mcVersion->m_appletClass = versionObj.value("appletClass").toString("");
mcVersion->m_mainClass = versionObj.value("mainClass").toString("");
mcVersion->m_processArguments = versionObj.value("processArguments").toString("legacy");
@@ -124,10 +164,141 @@ void MinecraftVersionList::loadBuiltinList()
mcVersion->m_traits.insert(MMCJson::ensureString(traitVal));
}
}
+ m_lookup[versionID] = mcVersion;
m_vlist.append(mcVersion);
}
}
+void MinecraftVersionList::loadMojangList(QByteArray data, VersionSource source)
+{
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {