summaryrefslogtreecommitdiffstats
path: root/logic/minecraft
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 /logic/minecraft
parent92abe4c603e1f4931cd02ae6b752cb7054d8e30d (diff)
downloadMultiMC-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar
MultiMC-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.gz
MultiMC-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.lz
MultiMC-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.xz
MultiMC-69c3e7111f93290d1278d6116e9fd50079b4fe79.zip
Make 1.6+ work with new instance format.
Diffstat (limited to 'logic/minecraft')
-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
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/minecraft/OneSixLibrary.cpp
index 45fa169e..7f69d9f8 100644
--- a/logic/minecraft/OneSixLibrary.cpp
+++ b/logic/minecraft/OneSixLibrary.cpp
@@ -23,6 +23,23 @@
#include <JlCompress.h>
#include "logger/QsLog.h"
+OneSixLibrary::OneSixLibrary(RawLibraryPtr base)
+{
+ m_name = base->m_name;
+ m_base_url = base->m_base_url;
+ m_hint = base->m_hint;
+ m_absolute_url = base->m_absolute_url;
+ extract_excludes = base->extract_excludes;
+ m_native_suffixes = base->m_native_suffixes;
+ m_rules = base->m_rules;
+ finalize();
+}
+
+OneSixLibraryPtr OneSixLibrary::fromRawLibrary(RawLibraryPtr lib)
+{
+ return OneSixLibraryPtr(new OneSixLibrary(lib));
+}
+
void OneSixLibrary::finalize()
{
QStringList parts = m_name.split(':');
@@ -30,7 +47,7 @@ void OneSixLibrary::finalize()
relative.replace('.', '/');
relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2];
- if (!m_is_native)
+ if (!isNative())
relative += ".jar";
else
{
@@ -65,7 +82,7 @@ void OneSixLibrary::finalize()
}
m_is_active = (result == Allow);
}
- if (m_is_native)
+ if (isNative())
{
m_is_active = m_is_active && m_native_suffixes.contains(currentSystem);
m_decenttype = "Native";
@@ -84,13 +101,8 @@ void OneSixLibrary::setBaseUrl(const QString &base_url)
{
m_base_url = base_url;
}
-void OneSixLibrary::setIsNative()
-{
- m_is_native = true;
-}
void OneSixLibrary::addNative(OpSys os, const QString &suffix)
{
- m_is_native = true;
m_native_suffixes[os] = suffix;
}
void OneSixLibrary::clearSuffixes()
@@ -105,10 +117,6 @@ bool OneSixLibrary::isActive() const
{
return m_is_active;
}
-bool OneSixLibrary::isNative() const
-{
- return m_is_native;
-}
QString OneSixLibrary::downloadUrl() const
{
if (m_absolute_url.size())
@@ -223,52 +231,3 @@ bool OneSixLibrary::extractTo(QString target_dir)
}
return true;
}
-
-QJsonObject OneSixLibrary::toJson()
-{
- QJsonObject libRoot;
- libRoot.insert("name", m_name);
- if (m_absolute_url.size())
- libRoot.insert("MMC-absoluteUrl", m_absolute_url);
- if (m_hint.size())
- libRoot.insert("MMC-hint", m_hint);
- if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
- m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
- m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
- {
- libRoot.insert("url", m_base_url);
- }
- if (isNative() && m_native_suffixes.size())
- {
- QJsonObject nativeList;
- auto iter = m_native_suffixes.begin();
- while (iter != m_native_suffixes.end())
- {
- nativeList.insert(OpSys_toString(iter.key()), iter.value());
- iter++;
- }
- libRoot.insert("natives", nativeList);
- }
- if (isNative() && extract_excludes.size())
- {
- QJsonArray excludes;
- QJsonObject extract;
- for (auto exclude : extract_excludes)
- {
- excludes.append(exclude);
- }
- extract.insert("exclude", excludes);
- libRoot.insert("extract", extract);
- }
- if (m_rules.size())
- {
- QJsonArray allRules;
- for (auto &rule : m_rules)
- {
- QJsonObject ruleObj = rule->toJson();
- allRules.append(ruleObj);
- }
- libRoot.insert("rules", allRules);
- }
- return libRoot;
-}
diff --git a/logic/minecraft/OneSixLibrary.h b/logic/minecraft/OneSixLibrary.h
index 13df0606..3d38985b 100644
--- a/logic/minecraft/OneSixLibrary.h
+++ b/logic/minecraft/OneSixLibrary.h
@@ -24,27 +24,16 @@
#include "logic/net/URLConstants.h"
#include "logic/minecraft/OpSys.h"
+#include "logic/minecraft/RawLibrary.h"
class Rule;
class OneSixLibrary;
typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr;
-class OneSixLibrary
+class OneSixLibrary : public RawLibrary
{
private:
- // basic values used internally (so far)
- QString m_name;
- QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
- QList<std::shared_ptr<Rule>> m_rules;
-
- // custom values
- /// absolute URL. takes precedence over m_download_path, if defined
- QString m_absolute_url;
- /// type hint - modifies how the library is treated
- QString m_hint;
-
- // derived values used for real things
/// a decent name fit for display
QString m_decentname;
/// a decent version fit for display
@@ -57,13 +46,9 @@ private:
QString m_download_url;
/// is this lib actually active on the current OS?
bool m_is_active = false;
- /// is the library a native?
- bool m_is_native = false;
- /// native suffixes per OS
- QMap<OpSys, QString> m_native_suffixes;
public:
- QStringList extract_excludes;
+
QString minVersion;
enum DependType
@@ -80,15 +65,16 @@ public:
m_name = name;
dependType = type;
}
-
+ /// Constructor
+ OneSixLibrary(RawLibraryPtr base);
+ static OneSixLibraryPtr fromRawLibrary(RawLibraryPtr lib);
+
/// Returns the raw name field
QString rawName() const
{
return m_name;
}
- QJsonObject toJson();
-
/**
* finalize the library, processing the input values into derived values and state
*
@@ -116,8 +102,6 @@ public:
/// Set the url base for downloads
void setBaseUrl(const QString &base_url);
- /// Call this to mark the library as 'native' (it's a zip archive with DLLs)
- void setIsNative();
/// Attach a name suffix to the specified OS native
void addNative(OpSys os, const QString &suffix);
/// Clears all suffixes
@@ -127,8 +111,6 @@ public:
/// Returns true if the library should be loaded (or extracted, in case of natives)
bool isActive() const;
- /// Returns true if the library is native
- bool isNative() const;
/// Get the URL to download the library from
QString downloadUrl() const;
/// Get the relative path where the library should be saved
diff --git a/logic/minecraft/OneSixRule.h b/logic/minecraft/OneSixRule.h
index 23d04151..a18093b0 100644
--- a/logic/minecraft/OneSixRule.h
+++ b/logic/minecraft/OneSixRule.h
@@ -16,8 +16,13 @@
#pragma once
#include <QString>
+#include <QList>
+#include <QJsonObject>
+#include <memory>
+#include "OpSys.h"
-#include "logic/minecraft/OneSixLibrary.h"
+class OneSixLibrary;
+class Rule;
enum RuleAction
{
diff --git a/logic/minecraft/ParseUtils.cpp b/logic/minecraft/ParseUtils.cpp
index 67460ca4..f94de6ff 100644
--- a/logic/minecraft/ParseUtils.cpp
+++ b/logic/minecraft/ParseUtils.cpp
@@ -8,16 +8,17 @@ QDateTime timeFromS3Time(QString str)
return QDateTime::fromString(str, Qt::ISODate);
}
-void parse_timestamp (const QString & raw, QString & save_here, QDateTime & parse_here)
+bool parse_timestamp (const QString & raw, QString & save_here, QDateTime & parse_here)
{
save_here = raw;
if (save_here.isEmpty())
{
- throw JSONValidationError("The timestamp is empty!");
+ return false;
}
parse_here = timeFromS3Time(save_here);
if (!parse_here.isValid())
{
- throw JSONValidationError("The timestamp not a valid timestamp!");
+ return false;
}
+ return true;
}
diff --git a/logic/minecraft/ParseUtils.h b/logic/minecraft/ParseUtils.h
index 73cf526a..bd2f6ffb 100644
--- a/logic/minecraft/ParseUtils.h
+++ b/logic/minecraft/ParseUtils.h
@@ -6,7 +6,7 @@
* parse the S3 timestamp in 'raw' and fill the forwarded variables.
* return true/false for success/failure
*/
-void parse_timestamp (const QString &raw, QString &save_here, QDateTime &parse_here);
+bool parse_timestamp (const QString &raw, QString &save_here, QDateTime &parse_here);
/**
* take the timestamp used by S3 and turn it into QDateTime
diff --git a/logic/minecraft/RawLibrary.cpp b/logic/minecraft/RawLibrary.cpp
index 12aac8c8..7e0ebff0 100644
--- a/logic/minecraft/RawLibrary.cpp
+++ b/logic/minecraft/RawLibrary.cpp
@@ -11,7 +11,7 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil
throw JSONValidationError(filename +
"contains a library that doesn't have a 'name' field");
}
- out->name = libObj.value("name").toString();
+ out->m_name = libObj.value("name").toString();
auto readString = [libObj, filename](const QString & key, QString & variable)
{
@@ -29,22 +29,21 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil
}
};
- readString("url", out->url);
- readString("MMC-hint", out->hint);
- readString("MMC-absulute_url", out->absoluteUrl);
- readString("MMC-absoluteUrl", out->absoluteUrl);
+ readString("url", out->m_base_url);
+ readString("MMC-hint", out->m_hint);
+ readString("MMC-absulute_url", out->m_absolute_url);
+ readString("MMC-absoluteUrl", out->m_absolute_url);
if (libObj.contains("extract"))
{
out->applyExcludes = true;
auto extractObj = ensureObject(libObj.value("extract"));
for (auto excludeVal : ensureArray(extractObj.value("exclude")))
{
- out->excludes.append(ensureString(excludeVal));
+ out->extract_excludes.append(ensureString(excludeVal));
}
}
if (libObj.contains("natives"))
{
- out->applyNatives = true;
QJsonObject nativesObj = ensureObject(libObj.value("natives"));
for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
{
@@ -55,14 +54,152 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil
OpSys opSys = OpSys_fromString(it.key());
if (opSys != Os_Other)
{
- out->natives.append(qMakePair(opSys, it.value().toString()));
+ out->m_native_suffixes[opSys] = it.value().toString();
}
}
}
if (libObj.contains("rules"))
{
out->applyRules = true;
- out->rules = rulesFromJsonV4(libObj);
+ out->m_rules = rulesFromJsonV4(libObj);
}
return out;
}
+
+RawLibraryPtr RawLibrary::fromJsonPlus(const QJsonObject &libObj, const QString &filename)
+{
+ auto lib = RawLibrary::fromJson(libObj, filename);
+ if (libObj.contains("insert"))
+ {
+ QJsonValue insertVal = ensureExists(libObj.value("insert"), "library insert rule");
+ QString insertString;
+ {
+ if (insertVal.isString())
+ {
+ insertString = insertVal.toString();
+ }
+ else if (insertVal.isObject())
+ {
+ QJsonObject insertObj = insertVal.toObject();
+ if (insertObj.isEmpty())
+ {
+ throw JSONValidationError("One library has an empty insert object in " +
+ filename);
+ }
+ insertString = insertObj.keys().first();
+ lib->insertData = insertObj.value(insertString).toString();
+ }
+ }
+ if (insertString == "apply")
+ {
+ lib->insertType = RawLibrary::Apply;
+ }
+ else if (insertString == "prepend")
+ {
+ lib->insertType = RawLibrary::Prepend;
+ }
+ else if (insertString == "append")
+ {
+ lib->insertType = RawLibrary::Append;
+ }
+ else if (insertString == "replace")
+ {
+ lib->insertType = RawLibrary::Replace;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid insert type");
+ }
+ }
+ if (libObj.contains("MMC-depend"))
+ {
+ const QString dependString = ensureString(libObj.value("MMC-depend"));
+ if (dependString == "hard")
+ {
+ lib->dependType = RawLibrary::Hard;
+ }
+ else if (dependString == "soft")
+ {
+ lib->dependType = RawLibrary::Soft;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid depend type");
+ }
+ }
+ return lib;
+}
+
+bool RawLibrary::isNative() const
+{
+ return m_native_suffixes.size() != 0;
+}
+
+QJsonObject RawLibrary::toJson()
+{
+ QJsonObject libRoot;
+ libRoot.insert("name", m_name);
+ if (m_absolute_url.size())
+ libRoot.insert("MMC-absoluteUrl", m_absolute_url);
+ if (m_hint.size())
+ libRoot.insert("MMC-hint", m_hint);
+ if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
+ m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
+ m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
+ {
+ libRoot.insert("url", m_base_url);
+ }
+ if (isNative())
+ {
+ QJsonObject nativeList;
+ auto iter = m_native_suffixes.begin();
+ while (iter != m_native_suffixes.end())
+ {
+ nativeList.insert(OpSys_toString(iter.key()), iter.value());
+ iter++;
+ }
+ libRoot.insert("natives", nativeList);
+ if (extract_excludes.size())
+ {
+ QJsonArray excludes;
+ QJsonObject extract;
+ for (auto exclude : extract_excludes)
+ {
+ excludes.append(exclude);
+ }
+ extract.insert("exclude", excludes);
+ libRoot.insert("extract", extract);
+ }
+ }
+ if (m_rules.size())
+ {
+ QJsonArray allRules;
+ for (auto &rule : m_rules)
+ {
+ QJsonObject ruleObj = rule->toJson();
+ allRules.append(ruleObj);
+ }
+ libRoot.insert("rules", allRules);
+ }
+ return libRoot;
+}
+
+QString RawLibrary::fullname()
+{
+ QStringList parts = m_name.split(':');
+ return parts[0] + ":" + parts[1];
+}
+
+QString RawLibrary::group()
+{
+ QStringList parts = m_name.split(':');
+ return parts[0];
+}
+
+QString RawLibrary::version()
+{
+ QStringList parts = m_name.split(':');
+ return parts[2];
+}
diff --git a/logic/minecraft/RawLibrary.h b/logic/minecraft/RawLibrary.h
index 46178d8e..f5a28c61 100644
--- a/logic/minecraft/RawLibrary.h
+++ b/logic/minecraft/RawLibrary.h
@@ -1,9 +1,14 @@
#pragma once
#include <QString>
#include <QPair>
+#include <QList>
+#include <QStringList>
+#include <QMap>
#include <memory>
-#include "OneSixRule.h"
+#include "logic/minecraft/OneSixRule.h"
+#include "logic/minecraft/OpSys.h"
+#include "logic/net/URLConstants.h"
class RawLibrary;
typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
@@ -11,24 +16,36 @@ typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
class RawLibrary
{
public: /* methods */
+ /// read and create a basic library
static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename);
+ /// read and create a MultiMC '+' library. Those have some extra fields.
+ static RawLibraryPtr fromJsonPlus(const QJsonObject &libObj, const QString &filename);
+ QJsonObject toJson();
+
+ QString fullname();
+ QString version();
+ QString group();
public: /* data */
- QString name;
- QString url;
- QString hint;
- QString absoluteUrl;
+ QString m_name;
+ QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
+ /// type hint - modifies how the library is treated
+ QString m_hint;
+ /// absolute URL. takes precedence over m_download_path, if defined
+ QString m_absolute_url;
bool applyExcludes = false;
- QStringList excludes;
+ QStringList extract_excludes;
- bool applyNatives = false;
- QList<QPair<OpSys, QString>> natives;
+ /// Returns true if the library is native
+ bool isNative() const;
+ /// native suffixes per OS
+ QMap<OpSys, QString> m_native_suffixes;
bool applyRules = false;
- QList<std::shared_ptr<Rule>> rules;
+ QList<std::shared_ptr<Rule>> m_rules;
- // user for '+' libraries
+ // used for '+' libraries
enum InsertType
{
Apply,
diff --git a/logic/minecraft/VersionBuilder.h b/logic/minecraft/VersionBuilder.h
index c56e6c18..fc06e614 100644
--- a/logic/minecraft/VersionBuilder.h
+++ b/logic/minecraft/VersionBuilder.h
@@ -30,7 +30,8 @@ class VersionBuilder
public:
static void build(VersionFinal *version, OneSixInstance *instance, const QStringList &external);
static void readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj);
-
+ static VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB = false);
+
static QMap<QString, int> readOverrideOrders(OneSixInstance *instance);
static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance);
@@ -49,7 +50,4 @@ private:
void readInstancePatches();
void readJsonAndApply(const QJsonObject &obj);
-
- VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
- bool isFTB = false);
};
diff --git a/logic/minecraft/VersionFile.cpp b/logic/minecraft/VersionFile.cpp
index bc9c42c1..74e7ba98 100644
--- a/logic/minecraft/VersionFile.cpp
+++ b/logic/minecraft/VersionFile.cpp
@@ -98,14 +98,10 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
readString("+minecraftArguments", out->addMinecraftArguments);
readString("-minecraftArguments", out->removeMinecraftArguments);
readString("type", out->type);
- if (out->isVanilla())
- {
- parse_timestamp(readStringRet("releaseTime"), out->m_releaseTimeString,
- out->m_releaseTime);
- parse_timestamp(readStringRet("time"), out->m_updateTimeString, out->m_updateTime);
- }
- readStringRet("time");
+ parse_timestamp(readStringRet("releaseTime"), out->m_releaseTimeString, out->m_releaseTime);
+ parse_timestamp(readStringRet("time"), out->m_updateTimeString, out->m_updateTime);
+
readString("assets", out->assets);
if (root.contains("minimumLauncherVersion"))
@@ -158,7 +154,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
// FIXME: This should be done when applying.
if (isFTB)
{
- lib->hint = "local";
+ lib->m_hint = "local";
lib->insertType = RawLibrary::Prepend;
out->addLibs.prepend(lib);
}
@@ -186,68 +182,8 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
for (auto libVal : ensureArray(root.value("+libraries")))
{
QJsonObject libObj = ensureObject(libVal);
- QJsonValue insertVal = ensureExists(libObj.value("insert"));
-
// parse the library
- auto lib = RawLibrary::fromJson(libObj, filename);
-
- // TODO: utility functions for handling this case. templates?
- QString insertString;
- {
- if (insertVal.isString())
- {
- insertString = insertVal.toString();
- }
- else if (insertVal.isObject())
- {
- QJsonObject insertObj = insertVal.toObject();
- if (insertObj.isEmpty())
- {
- throw JSONValidationError("One library has an empty insert object in " +
- filename);
- }
- insertString = insertObj.keys().first();
- lib->insertData = insertObj.value(insertString).toString();
- }
- }
- if (insertString == "apply")
- {
- lib->insertType = RawLibrary::Apply;
- }
- else if (insertString == "prepend")
- {
- lib->insertType = RawLibrary::Prepend;
- }
- else if (insertString == "append")
- {
- lib->insertType = RawLibrary::Prepend;
- }
- else if (insertString == "replace")
- {
- lib->insertType = RawLibrary::Replace;
- }
- else
- {
- throw JSONValidationError("A '+' library in " + filename +
- " contains an invalid insert type");
- }
- if (libObj.contains("MMC-depend"))
- {
- const QString dependString = ensureString(libObj.value("MMC-depend"));
- if (dependString == "hard")
- {
- lib->dependType = RawLibrary::Hard;
- }
- else if (dependString == "soft")
- {
- lib->dependType = RawLibrary::Soft;
- }
- else
- {
- throw JSONValidationError("A '+' library in " + filename +
- " contains an invalid depend type");
- }
- }
+ auto lib = RawLibrary::fromJsonPlus(libObj, filename);
out->addLibs.append(lib);
}
}
@@ -263,32 +199,66 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
return out;
}
-OneSixLibraryPtr VersionFile::createLibrary(RawLibraryPtr lib)
+QJsonDocument VersionFile::toJson(bool saveOrder)
{
- std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib->name));
- if (!lib->url.isEmpty())
+ QJsonObject root;
+ if (saveOrder)
+ {
+ root.insert("order", order);
+ }
+ writeString(root, "name", name);
+ writeString(root, "fileId", fileId);
+ writeString(root, "version", version);
+ writeString(root, "mcVersion", mcVersion);
+ writeString(root, "id", id);
+ writeString(root, "mainClass", mainClass);
+ writeString(root, "appletClass", appletClass);
+ writeString(root, "processArguments", processArguments);
+ writeString(root, "minecraftArguments", overwriteMinecraftArguments);
+ writeString(root, "+minecraftArguments", addMinecraftArguments);
+ writeString(root, "-minecraftArguments", removeMinecraftArguments);
+ writeString(root, "type", type);
+ writeString(root, "assets", assets);
+ if (isMinecraftVersion())
+ {
+ writeString(root, "releaseTime", m_releaseTimeString);
+ writeString(root, "time", m_updateTimeString);
+ }
+ if (minimumLauncherVersion != -1)
{
- out->setBaseUrl(lib->url);
+ root.insert("minimumLauncherVersion", minimumLauncherVersion);
}
- out->setHint(lib->hint);
- if (!lib->absoluteUrl.isEmpty())
+ writeStringList(root, "tweakers", overwriteTweakers);
+ writeStringList(root, "+tweakers", addTweakers);
+ writeStringList(root, "-tweakers", removeTweakers);
+ writeStringList(root, "+traits", traits.toList());
+ writeObjectList(root, "libraries", overwriteLibs);
+ writeObjectList(root, "+libraries", addLibs);
+ writeObjectList(root, "+jarMods", jarMods);
+ // FIXME: removed libs are special snowflakes.
+ if (removeLibs.size())
{
- out->setAbsoluteUrl(lib->absoluteUrl);
+ QJsonArray array;
+ for (auto lib : removeLibs)
+ {
+ QJsonObject rmlibobj;
+ rmlibobj.insert("name", lib);
+ array.append(rmlibobj);
+ }
+ root.insert("-libraries", array);
}
- out->setAbsoluteUrl(lib->absoluteUrl);
- out->extract_excludes = lib->excludes;
- for (auto native : lib->natives)
+ // write the contents to a json document.
{
- out->addNative(native.first, native.second);
+ QJsonDocument out;
+ out.setObject(root);
+ return out;
}
- out->setRules(lib->rules);
- out->finalize();
- return out;
}
-bool VersionFile::isVanilla()
+bool VersionFile::isMinecraftVersion()
{
- return fileId == "org.multimc.version.json";
+ return (fileId == "org.multimc.version.json") || (fileId == "net.minecraft") ||
+ (fileId == "org.multimc.custom.json");
}
bool VersionFile::hasJarMods()
@@ -330,13 +300,13 @@ void VersionFile::applyTo(VersionFinal *version)
}
if (!processArguments.isNull())
{
- if (isVanilla())
+ if (isMinecraftVersion())
{
version->vanillaProcessArguments = processArguments;
}
version->processArguments = processArguments;
}
- if(isVanilla())
+ if (isMinecraftVersion())
{
if (!type.isNull())
{
@@ -359,12 +329,12 @@ void VersionFile::applyTo(VersionFinal *version)
}
if (minimumLauncherVersion >= 0)
{
- if(version->minimumLauncherVersion < minimumLauncherVersion)
+ if (version->minimumLauncherVersion < minimumLauncherVersion)
version->minimumLauncherVersion = minimumLauncherVersion;
}
if (!overwriteMinecraftArguments.isNull())
{
- if (isVanilla())
+ if (isMinecraftVersion())
{
version->vanillaMinecraftArguments = overwriteMinecraftArguments;
}
@@ -397,10 +367,12 @@ void VersionFile::applyTo(VersionFinal *version)
QList<OneSixLibraryPtr> libs;
for (auto lib : overwriteLibs)
{
- libs.append(createLibrary(lib));
+ libs.append(OneSixLibrary::fromRawLibrary(lib));
}
- if (isVanilla())
+ if (isMinecraftVersion())
+ {
version->vanillaLibraries = libs;
+ }
version->libraries = libs;
}
for (auto lib : addLibs)
@@ -410,43 +382,46 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Apply:
{
// QLOG_INFO() << "Applying lib " << lib->name;
- int index = findLibrary(version->libraries, lib->name);
+ int index = findLibrary(version->libraries, lib->m_name);
if (index >= 0)
{
auto library = version->libraries[index];
- if (!lib->url.isNull())
+ if (!lib->m_base_url.isNull())
{
- library->setBaseUrl(lib->url);
+ library->setBaseUrl(lib->m_base_url);
}
- if (!lib->hint.isNull())
+ if (!lib->m_hint.isNull())
{
- library->setHint(lib->hint);
+ library->setHint(lib->m_hint);
}
- if (!lib->absoluteUrl.isNull())
+ if (!lib->m_absolute_url.isNull())
{
- library->setAbsoluteUrl(lib->absoluteUrl);
+ library->setAbsoluteUrl(lib->m_absolute_url);
}
if (lib->applyExcludes)
{
- library->extract_excludes = lib->excludes;
+ library->extract_excludes = lib->extract_excludes;
}
- if (lib->applyNatives)
+ if (lib->isNative())
{
- library->clearSuffixes();
+ // library->clearSuffixes();
+ library->m_native_suffixes = lib->m_native_suffixes;
+ /*
for (auto native : lib->natives)
{
library->addNative(native.first, native.second);
}
+ */
}
if (lib->applyRules)
{
- library->setRules(lib->rules);
+ library->setRules(lib->m_rules);
}
library->finalize();
}
else
{
- QLOG_WARN() << "Couldn't find" << lib->name << "(skipping)";
+ QLOG_WARN() << "Couldn't find" << lib->m_name << "(skipping)";
}
break;
}
@@ -454,24 +429,24 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Prepend:
{
// QLOG_INFO() << "Adding lib " << lib->name;
- const int startOfVersion = lib->name.lastIndexOf(':') + 1;
+ const int startOfVersion = lib->m_name.lastIndexOf(':') + 1;
const int index = findLibrary(
- version->libraries, QString(lib->name).replace(startOfVersion, INT_MAX, '*'));
+ version->libraries, QString(lib->m_name).replace(startOfVersion, INT_MAX, '*'));
if (index < 0)
{
if (lib->insertType == RawLibrary::Append)
{
- version->libraries.append(createLibrary(lib));
+ version->libraries.append(OneSixLibrary::fromRawLibrary(lib));
}
else
{
- version->libraries.prepend(createLibrary(lib));
+ version->libraries.prepend(OneSixLibrary::fromRawLibrary(lib));
}
}
else
{
auto otherLib = version->libraries.at(index);
- const Util::Version ourVersion = lib->name.mid(startOfVersion, INT_MAX);
+ const Util::Version ourVersion = lib->m_name.mid(startOfVersion, INT_MAX);
const Util::Version otherVersion = otherLib->version();
// if the existing version is a hard dependency we can either use it or
// fail, but we can't change it
@@ -485,7 +460,7 @@ void VersionFile::applyTo(VersionFinal *version)
throw VersionBuildError(
QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.")
- .arg(otherLib->rawName(), lib->name, filename));
+ .arg(otherLib->rawName(), lib->m_name, filename));
}
else
{
@@ -497,7 +472,7 @@ void VersionFile::applyTo(VersionFinal *version)
// if we are higher it means we should update
if (ourVersion > otherVersion)
{
- auto library = createLibrary(lib);
+ auto library = OneSixLibrary::fromRawLibrary(lib);
if (Util::Version(otherLib->minVersion) < ourVersion)
{
library->minVersion = ourVersion.toString();
@@ -512,7 +487,7 @@ void VersionFile::applyTo(VersionFinal *version)
{
throw VersionBuildError(QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.")
- .arg(otherLib->rawName(), lib->name,
+ .arg(otherLib->rawName(), lib->m_name,
filename));
}
}
@@ -525,8 +500,8 @@ void VersionFile::applyTo(VersionFinal *version)
QString toReplace;
if (lib->insertData.isEmpty())
{
- const int startOfVersion = lib->name.lastIndexOf(':') + 1;
- toReplace = QString(lib->name).replace(startOfVersion, INT_MAX, '*');
+ const int startOfVersion = lib->m_name.lastIndexOf(':') + 1;
+ toReplace = QString(lib->m_name).replace(startOfVersion, INT_MAX, '*');
}
else
toReplace = lib->insertData;
@@ -534,7 +509,7 @@ void VersionFile::applyTo(VersionFinal *version)
int index = findLibrary(version->libraries, toReplace);
if (index >= 0)
{
- version->libraries.replace(index, createLibrary(lib));
+ version->libraries.replace(index, OneSixLibrary::fromRawLibrary(lib));
}
else
{
diff --git a/logic/minecraft/VersionFile.h b/logic/minecraft/VersionFile.h
index 9c632d6f..2d3781cb 100644
--- a/logic/minecraft/VersionFile.h
+++ b/logic/minecraft/VersionFile.h
@@ -8,7 +8,7 @@
#include "logic/minecraft/OneSixRule.h"
#include "VersionPatch.h"
#include "MMCError.h"
-#include "RawLibrary.h"
+#include "OneSixLibrary.h"
#include "JarMod.h"
class VersionFinal;
@@ -20,10 +20,10 @@ class VersionFile : public VersionPatch
public: /* methods */
static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
const bool requireOrder, const bool isFTB = false);
+ QJsonDocument toJson(bool saveOrder);
- static OneSixLibraryPtr createLibrary(RawLibraryPtr lib);
virtual void applyTo(VersionFinal *version) override;
- virtual bool isVanilla() override;
+ virtual bool isMinecraftVersion() override;
virtual bool hasJarMods() override;
virtual int getOrder() override
{
diff --git a/logic/minecraft/VersionPatch.h b/logic/minecraft/VersionPatch.h
index cdb80199..ef082b37 100644
--- a/logic/minecraft/VersionPatch.h
+++ b/logic/minecraft/VersionPatch.h
@@ -11,7 +11,7 @@ public:
virtual ~VersionPatch(){};
virtual void applyTo(VersionFinal *version) = 0;
- virtual bool isVanilla() = 0;
+ virtual bool isMinecraftVersion() = 0;
virtual bool hasJarMods() = 0;
virtual QList<JarmodPtr> getJarMods() = 0;
diff --git a/logic/minecraft/VersionSource.h b/logic/minecraft/VersionSource.h
new file mode 100644
index 00000000..75b2c24b
--- /dev/null
+++ b/logic/minecraft/VersionSource.h
@@ -0,0 +1,9 @@
+#pragma once
+
+/// where is a version from?
+enum VersionSource
+{
+ Builtin, //!< version loaded from the internal resources.
+ Local, //!< version loaded from a file in the cache.
+ Remote, //!< incomplete version on a remote server.
+}; \ No newline at end of file