diff options
| author | Petr Mrázek <peterix@gmail.com> | 2014-05-11 12:37:21 +0200 |
|---|---|---|
| committer | Petr Mrázek <peterix@gmail.com> | 2014-06-09 01:38:30 +0200 |
| commit | 69c3e7111f93290d1278d6116e9fd50079b4fe79 (patch) | |
| tree | cbaed3022e8705f1da29777afea0fca16c1abe60 /logic/minecraft | |
| parent | 92abe4c603e1f4931cd02ae6b752cb7054d8e30d (diff) | |
| download | PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.gz PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.bz2 PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.zip | |
Make 1.6+ work with new instance format.
Diffstat (limited to 'logic/minecraft')
| -rw-r--r-- | logic/minecraft/JarMod.cpp | 12 | ||||
| -rw-r--r-- | logic/minecraft/JarMod.h | 1 | ||||
| -rw-r--r-- | logic/minecraft/MinecraftVersion.cpp | 60 | ||||
| -rw-r--r-- | logic/minecraft/MinecraftVersion.h | 73 | ||||
| -rw-r--r-- | logic/minecraft/MinecraftVersionList.cpp | 518 | ||||
| -rw-r--r-- | logic/minecraft/MinecraftVersionList.h | 36 | ||||
| -rw-r--r-- | logic/minecraft/OneSixLibrary.cpp | 79 | ||||
| -rw-r--r-- | logic/minecraft/OneSixLibrary.h | 32 | ||||
| -rw-r--r-- | logic/minecraft/OneSixRule.h | 7 | ||||
| -rw-r--r-- | logic/minecraft/ParseUtils.cpp | 7 | ||||
| -rw-r--r-- | logic/minecraft/ParseUtils.h | 2 | ||||
| -rw-r--r-- | logic/minecraft/RawLibrary.cpp | 155 | ||||
| -rw-r--r-- | logic/minecraft/RawLibrary.h | 37 | ||||
| -rw-r--r-- | logic/minecraft/VersionBuilder.h | 6 | ||||
| -rw-r--r-- | logic/minecraft/VersionFile.cpp | 203 | ||||
| -rw-r--r-- | logic/minecraft/VersionFile.h | 6 | ||||
| -rw-r--r-- | logic/minecraft/VersionPatch.h | 2 | ||||
| -rw-r--r-- | logic/minecraft/VersionSource.h | 9 |
18 files changed, 822 insertions, 423 deletions
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) + { + throw ListLoadError( + tr("Error parsing version list JSON: %1").arg(jsonError.errorString())); + } + + QLOG_INFO() << ((source == Remote) ? "Remote version list: " : "Local version list:") << data; + if (!jsonDoc.isObject()) + { + throw ListLoadError(tr("Error parsing version list JSON: jsonDoc is not an object")); + } + + QJsonObject root = jsonDoc.object(); + + try + { + QJsonObject latest = MMCJson::ensureObject(root.value("latest")); + m_latestReleaseID = MMCJson::ensureString(latest.value("release")); + m_latestSnapshotID = MMCJson::ensureString(latest.value("snapshot")); + } + catch (MMCError &err) + { + QLOG_ERROR() + << tr("Error parsing version list JSON: couldn't determine latest versions"); + } + + // Now, get the array of versions. + if (!root.value("versions").isArray()) + { + throw ListLoadError(tr("Error parsing version list JSON: version list object is " + "missing 'versions' array")); + } + QJsonArray versions = root.value("versions").toArray(); + + QList<BaseVersionPtr> tempList; + for (auto version : versions) + { + bool is_snapshot = false; + + // Load the version info. + if (!version.isObject()) + { + QLOG_ERROR() << "Error while parsing version list : invalid JSON structure"; + continue; + } + + QJsonObject versionObj = version.toObject(); + QString versionID = versionObj.value("id").toString(""); + if (versionID.isEmpty()) + { + QLOG_ERROR() << "Error while parsing version : version ID is missing"; + 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; + + if (!parse_timestamp(versionObj.value("releaseTime").toString(""), + mcVersion->m_releaseTimeString, mcVersion->m_releaseTime)) + { + QLOG_ERROR() << "Error while parsing version" << versionID + << ": invalid release timestamp"; + continue; + } + if (!parse_timestamp(versionObj.value("time").toString(""), + mcVersion->m_updateTimeString, mcVersion->m_updateTime)) + { + QLOG_ERROR() << "Error while parsing version" << versionID + << ": invalid update timestamp"; + continue; + } + + if (mcVersion->m_releaseTime < g_VersionFilterData.legacyCutoffDate) + { + QLOG_ERROR() << "Ignoring Mojang version: " << versionID; + continue; + } + + // depends on where we load the version from -- network request or local file? + mcVersion->m_versionSource = source; + + QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/"; + mcVersion->download_url = dlUrl; + QString versionTypeStr = versionObj.value("type").toString(""); + if (versionTypeStr.isEmpty()) + { + // FIXME: log this somewhere + continue; + } + // OneSix or Legacy. use filter to determine type + if (versionTypeStr == "release") + { + is_snapshot = false; + } + else if (versionTypeStr == "snapshot") // It's a snapshot... yay + { + is_snapshot = true; + } + else if (versionTypeStr == "old_alpha") + { + is_snapshot = false; + } + else if (versionTypeStr == "old_beta") + { + is_snapshot = false; + } + else + { + // FIXME: log this somewhere + continue; + } + mcVersion->m_type = versionTypeStr; + mcVersion->is_snapshot = is_snapshot; + tempList.append(mcVersion); + } + updateListData(tempList); +} + void MinecraftVersionList::sort() { beginResetModel(); @@ -137,14 +308,8 @@ void MinecraftVersionList::sort() BaseVersionPtr MinecraftVersionList::getLatestStable() const { - for (int i = 0; i < m_vlist.length(); i++) - { - auto ver = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist.at(i)); - if (ver->is_latest && !ver->is_snapshot) - { - return m_vlist.at(i); - } - } + if(m_lookup.contains(m_latestReleaseID)) + return m_lookup[m_latestReleaseID]; return BaseVersionPtr(); } @@ -154,17 +319,29 @@ void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions) for (auto version : versions) { auto descr = version->descriptor(); - for (auto builtin_v : m_vlist) + + if (!m_lookup.contains(descr)) { - if (descr == builtin_v->descriptor()) - { - goto SKIP_THIS_ONE; - } + m_vlist.append(version); + continue; } - m_vlist.append(version); - SKIP_THIS_ONE: - { - } + auto orig = std::dynamic_pointer_cast<MinecraftVersion>(m_lookup[descr]); + auto added = std::dynamic_pointer_cast<MinecraftVersion>(version); + // updateListData is called after Mojang list loads. those can be local or remote + // remote comes always after local + // any other options are ignored + if (orig->m_versionSource != Local || added->m_versionSource != Remote) + { + continue; + } + // is it actually an update? + if (orig->m_updateTime >= added->m_updateTime) + { + // nope. + continue; + } + // alright, it's an update. put it inside the original, for further processing. + orig->upstreamUpdate = added; } m_loaded = true; sortInternal(); @@ -187,10 +364,6 @@ MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist) vlistReply = nullptr; } -MCVListLoadTask::~MCVListLoadTask() -{ -} - void MCVListLoadTask::executeTask() { setStatus(tr("Loading instance version list...")); @@ -209,133 +382,196 @@ void MCVListLoadTask::list_downloaded() return; } - auto foo = vlistReply->readAll(); - QJsonParseError jsonError; - QLOG_INFO() << foo; - QJsonDocument jsonDoc = QJsonDocument::fromJson(foo, &jsonError); + auto data = vlistReply->readAll(); vlistReply->deleteLater(); - - if (jsonError.error != QJsonParseError::NoError) + try { - emitFailed("Error parsing version list JSON:" + jsonError.errorString()); - return; + m_list->loadMojangList(data, Remote); } - - if (!jsonDoc.isObject()) + catch (MMCError &e) { - emitFailed("Error parsing version list JSON: jsonDoc is not an object"); + emitFailed(e.cause()); return; } - QJsonObject root = jsonDoc.object(); + emitSucceeded(); + return; +} - QString latestReleaseID = "INVALID"; - QString latestSnapshotID = "INVALID"; - try +MCVListVersionUpdateTask::MCVListVersionUpdateTask(MinecraftVersionList *vlist, + QString updatedVersion) + : Task() +{ + m_list = vlist; + versionToUpdate = updatedVersion; +} + +void MCVListVersionUpdateTask::executeTask() +{ + QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionToUpdate + "/" + + versionToUpdate + ".json"; + auto job = new NetJob("Version index"); + job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); + specificVersionDownloadJob.reset(job); + connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(json_downloaded())); + connect(specificVersionDownloadJob.get(), SIGNAL(failed(QString)), SIGNAL(failed(QString))); + connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + specificVersionDownloadJob->start(); +} + +void MCVListVersionUpdateTask::json_downloaded() +{ + NetActionPtr DlJob = specificVersionDownloadJob->first(); + auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data; + specificVersionDownloadJob.reset(); + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + + if (jsonError.error != QJsonParseError::NoError) { - QJsonObject latest = MMCJson::ensureObject(root.value("latest")); - latestReleaseID = MMCJson::ensureString(latest.value("release")); - latestSnapshotID = MMCJson::ensureString(latest.value("snapshot")); + emitFailed(tr("The download version file is not valid.")); + return; } - catch (MMCError &err) + VersionFilePtr file; + try { - QLOG_ERROR() - << tr("Error parsing version list JSON: couldn't determine latest versions"); + file = VersionFile::fromJson(jsonDoc, "net.minecraft.json", false); } - - // Now, get the array of versions. - if (!root.value("versions").isArray()) + catch (MMCError &e) { - emitFailed( - "Error parsing version list JSON: version list object is missing 'versions' array"); + emitFailed(tr("Couldn't process version file: %1").arg(e.cause())); return; } - QJsonArray versions = root.value("versions").toArray(); - - QList<BaseVersionPtr> tempList; - for (auto version : versions) + QList<RawLibraryPtr> filteredLibs; + QList<RawLibraryPtr> lwjglLibs; + QSet<QString> lwjglFilter = { + "net.java.jinput:jinput", "net.java.jinput:jinput-platform", + "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl", + "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"}; + for (auto lib : file->overwriteLibs) { - bool is_snapshot = false; - bool is_latest = false; - - // Load the version info. - if (!version.isObject()) + if (lwjglFilter.contains(lib->fullname())) { - QLOG_ERROR() << "Error while parsing version list : invalid JSON structure"; - continue; + lwjglLibs.append(lib); } - QJsonObject versionObj = version.toObject(); - QString versionID = versionObj.value("id").toString(""); - if (versionID.isEmpty()) + else { - QLOG_ERROR() << "Error while parsing version : version ID is missing"; - continue; + filteredLibs.append(lib); } - // Get the download URL. - QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/"; + } + file->overwriteLibs = filteredLibs; - // 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; + // TODO: recognize and add LWJGL versions here. - try - { - // Parse the timestamps. - parse_timestamp(versionObj.value("releaseTime").toString(""), - mcVersion->m_releaseTimeString, mcVersion->m_releaseTime); + file->fileId = "net.minecraft"; - parse_timestamp(versionObj.value("time").toString(""), - mcVersion->m_updateTimeString, mcVersion->m_updateTime); - } - catch (MMCError &e) - { - QLOG_ERROR() << "Error while parsing version" << versionID << ":" << e.cause(); + // now dump the file to disk + auto doc = file->toJson(false); + auto newdata = doc.toJson(); + QLOG_INFO() << newdata; + QString targetPath = "versions/" + versionToUpdate + "/" + versionToUpdate + ".json"; + ensureFilePathExists(targetPath); + QSaveFile vfile1(targetPath); + if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) + { + emitFailed(tr("Can't open %1 for writing.").arg(targetPath)); + return; + } + qint64 actual = 0; + if ((actual = vfile1.write(newdata)) != newdata.size()) + { + emitFailed(tr("Failed to write into %1. Written %2 out of %3.") + .arg(targetPath) + .arg(actual) + .arg(newdata.size())); + return; + } + if (!vfile1.commit()) + { + emitFailed(tr("Can't commit changes to %1").arg(targetPath)); + return; + } + + m_list->finalizeUpdate(versionToUpdate); + emitSucceeded(); +} + +std::shared_ptr<Task> MinecraftVersionList::createUpdateTask(QString version) +{ + return std::shared_ptr<Task>(new MCVListVersionUpdateTask(this, version)); +} + +void MinecraftVersionList::saveCachedList() +{ + // FIXME: throw. + if (!ensureFilePathExists("versions/versions.json")) + return; + QSaveFile tfile("versions/versions.json"); + if (!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) + return; + QJsonObject toplevel; + QJsonArray entriesArr; + for (auto version : m_vlist) + { + auto mcversion = std::dynamic_pointer_cast<MinecraftVersion>(version); + // do not save the remote versions. + if (mcversion->m_versionSource != Local) continue; - } + QJsonObject entryObj; - mcVersion->m_versionSource = MinecraftVersion::Builtin; - mcVersion->download_url = dlUrl; + entryObj.insert("id", mcversion->descriptor()); + entryObj.insert("time", mcversion->m_updateTimeString); + entryObj.insert("releaseTime", mcversion->m_releaseTimeString); + entryObj.insert("type", mcversion->m_type); + entriesArr.append(entryObj); + } + toplevel.insert("versions", entriesArr); + QJsonDocument doc(toplevel); + QByteArray jsonData = doc.toJson(); + qint64 result = tfile.write(jsonData); + if (result == -1) + return; + if (result != jsonData.size()) + return; + tfile.commit(); +} + +void MinecraftVersionList::finalizeUpdate(QString version) +{ + int idx = -1; + for (int i = 0; i < m_vlist.size(); i++) + { + if (version == m_vlist[i]->descriptor()) { - QString versionTypeStr = versionObj.value("type").toString(""); - if (versionTypeStr.isEmpty()) - { - // FIXME: log this somewhere - continue; - } - // OneSix or Legacy. use filter to determine type - if (versionTypeStr == "release") - { - is_latest = (versionID == latestReleaseID); - is_snapshot = false; - } - else if (versionTypeStr == "snapshot") // It's a snapshot... yay - { - is_latest = (versionID == latestSnapshotID); - is_snapshot = true; - } - else if (versionTypeStr == "old_alpha") - { - is_latest = false; - is_snapshot = false; - } - else if (versionTypeStr == "old_beta") - { - is_latest = false; - is_snapshot = false; - } - else - { - // FIXME: log this somewhere - continue; - } - mcVersion->m_type = versionTypeStr; - mcVersion->is_latest = is_latest; - mcVersion->is_snapshot = is_snapshot; + idx = i; + break; } - tempList.append(mcVersion); } - m_list->updateListData(tempList); + if (idx == -1) + { + return; + } - emitSucceeded(); - return; + auto updatedVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[idx]); + + if (updatedVersion->m_versionSource == Builtin) + return; + + if (updatedVersion->upstreamUpdate) + { + auto updatedWith = updatedVersion->upstreamUpdate; + updatedWith->m_versionSource = Local; + m_vlist[idx] = updatedWith; + m_lookup[version] = updatedWith; + } + else + { + updatedVersion->m_versionSource = Local; + } + + dataChanged(index(idx), index(idx)); + + saveCachedList(); } diff --git a/logic/minecraft/MinecraftVersionList.h b/logic/minecraft/MinecraftVersionList.h index 18b9b21e..bbbd71e1 100644 --- a/logic/minecraft/MinecraftVersionList.h +++ b/logic/minecraft/MinecraftVersionList.h @@ -22,8 +22,10 @@ #include "logic/BaseVersionList.h" #include "logic/tasks/Task.h" #include "logic/minecraft/MinecraftVersion.h" +#include <logic/net/NetJob.h> class MCVListLoadTask; +class MCVListVersionUpdateTask; class QNetworkReply; class MinecraftVersionList : public BaseVersionList @@ -32,11 +34,18 @@ class MinecraftVersionList : public BaseVersionList private: void sortInternal(); void loadBuiltinList(); + void loadMojangList(QByteArray data, VersionSource source); + void loadCachedList(); + void saveCachedList(); + void finalizeUpdate(QString version); public: friend class MCVListLoadTask; + friend class MCVListVersionUpdateTask; explicit MinecraftVersionList(QObject *parent = 0); + std::shared_ptr<Task> createUpdateTask(QString version); + virtual Task *getLoadTask(); virtual bool isLoaded(); virtual const BaseVersionPtr at(int i) const; @@ -47,8 +56,12 @@ public: protected: QList<BaseVersionPtr> m_vlist; + QMap<QString, BaseVersionPtr> m_lookup; bool m_loaded = false; + bool m_hasLocalIndex = false; + QString m_latestReleaseID = "INVALID"; + QString m_latestSnapshotID = "INVALID"; protected slots: @@ -61,9 +74,9 @@ class MCVListLoadTask : public Task public: explicit MCVListLoadTask(MinecraftVersionList *vlist); - ~MCVListLoadTask(); + virtual ~MCVListLoadTask() override{}; - virtual void executeTask(); + virtual void executeTask() override; protected slots: @@ -74,3 +87,22 @@ protected: MinecraftVersionList *m_list; MinecraftVersion *m_currentStable; }; + +class MCVListVersionUpdateTask : public Task +{ + Q_OBJECT + +public: + explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, QString updatedVersion); + virtual ~MCVListVersionUpdateTask() override{}; + virtual void executeTask() override; + +protected +slots: + void json_downloaded(); + +protected: + NetJobPtr specificVersionDownloadJob; + QString versionToUpdate; + MinecraftVersionList *m_list; +}; diff --git a/logic/minecraft/OneSixLibrary.cpp b/logic |
