Make 1.6+ work with new instance format.

This commit is contained in:
Petr Mrázek 2014-05-11 12:37:21 +02:00
parent 92abe4c603
commit 69c3e7111f
29 changed files with 951 additions and 562 deletions

View File

@ -485,11 +485,13 @@ SET(MULTIMC_SOURCES
logic/minecraft/RawLibrary.h logic/minecraft/RawLibrary.h
logic/minecraft/VersionBuilder.cpp logic/minecraft/VersionBuilder.cpp
logic/minecraft/VersionBuilder.h logic/minecraft/VersionBuilder.h
logic/minecraft/VersionBuildError.h
logic/minecraft/VersionFile.cpp logic/minecraft/VersionFile.cpp
logic/minecraft/VersionFile.h logic/minecraft/VersionFile.h
logic/minecraft/VersionFinal.cpp logic/minecraft/VersionFinal.cpp
logic/minecraft/VersionFinal.h logic/minecraft/VersionFinal.h
logic/minecraft/VersionPatch.h logic/minecraft/VersionPatch.h
logic/minecraft/VersionSource.h
# Various base classes # Various base classes
logic/BaseInstaller.h logic/BaseInstaller.h

View File

@ -11,7 +11,7 @@ bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
QJsonValue MMCJson::ensureExists(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"); throw JSONValidationError(what + " does not exist");
return val; return val;
} }
@ -59,3 +59,24 @@ QString MMCJson::ensureString(const QJsonValue val, const QString what)
return val.toString(); 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);
}
}

View File

@ -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. /// make sure the value is converted into a double precision floating number. throw otherwise.
double ensureDouble(const QJsonValue val, QString what = "value"); 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);
}
}
}

View File

@ -34,15 +34,12 @@
#include "logic/net/URLConstants.h" #include "logic/net/URLConstants.h"
#include "logic/assets/AssetsUtils.h" #include "logic/assets/AssetsUtils.h"
OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
: Task(parent), m_inst(inst)
{ {
} }
void OneSixUpdate::executeTask() void OneSixUpdate::executeTask()
{ {
QString intendedVersion = m_inst->intendedVersionId();
// Make directories // Make directories
QDir mcDir(m_inst->minecraftRoot()); QDir mcDir(m_inst->minecraftRoot());
if (!mcDir.exists() && !mcDir.mkpath(".")) if (!mcDir.exists() && !mcDir.mkpath("."))
@ -51,97 +48,37 @@ void OneSixUpdate::executeTask()
return; return;
} }
if (m_inst->shouldUpdate())
{
// Get a pointer to the version object that corresponds to the instance's version. // Get a pointer to the version object that corresponds to the instance's version.
targetVersion = std::dynamic_pointer_cast<MinecraftVersion>( targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
MMC->minecraftlist()->findVersion(intendedVersion)); MMC->minecraftlist()->findVersion(m_inst->intendedVersionId()));
if (targetVersion == nullptr) if (targetVersion == nullptr)
{ {
// don't do anything if it was invalid // don't do anything if it was invalid
emitFailed(tr("The specified Minecraft version is invalid. Choose a different one.")); emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
return; return;
} }
// builtins need no updates, so only update for Mojang if (m_inst->providesVersionFile() || !targetVersion->needsUpdate())
if(targetVersion->m_versionSource == MinecraftVersion::Mojang)
{
versionFileStart();
return;
}
}
jarlibStart();
}
void OneSixUpdate::versionFileStart()
{
if (m_inst->providesVersionFile())
{ {
jarlibStart(); jarlibStart();
return; return;
} }
QLOG_INFO() << m_inst->name() << ": getting version file."; versionUpdateTask = MMC->minecraftlist()->createUpdateTask(m_inst->intendedVersionId());
setStatus(tr("Getting the version files from Mojang...")); if (!versionUpdateTask)
{
QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + jarlibStart();
targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; return;
auto job = new NetJob("Version index"); }
job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); connect(versionUpdateTask.get(), SIGNAL(succeeded()), SLOT(jarlibStart()));
specificVersionDownloadJob.reset(job); connect(versionUpdateTask.get(), SIGNAL(failed(QString)), SLOT(versionUpdateFailed(QString)));
connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished())); connect(versionUpdateTask.get(), SIGNAL(progress(qint64, qint64)),
connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed()));
connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
SIGNAL(progress(qint64, qint64))); SIGNAL(progress(qint64, qint64)));
specificVersionDownloadJob->start(); setStatus(tr("Getting the version files from Mojang..."));
versionUpdateTask->start();
} }
void OneSixUpdate::versionFileFinished() void OneSixUpdate::versionUpdateFailed(QString reason)
{ {
NetActionPtr DlJob = specificVersionDownloadJob->first(); emitFailed(reason);
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())
{
finfo.remove();
}
// NOTE: Version is reloaded in jarlibStart
jarlibStart();
}
void OneSixUpdate::versionFileFailed()
{
emitFailed(tr("Failed to download the version description. Try again."));
} }
void OneSixUpdate::assetIndexStart() void OneSixUpdate::assetIndexStart()
@ -321,7 +258,10 @@ void OneSixUpdate::jarlibStart()
failed.append(brokenLib->files()); failed.append(brokenLib->files());
} }
QString failed_all = failed.join("\n"); 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; return;
} }
// TODO: think about how to propagate this from the original json file... or IF AT ALL // TODO: think about how to propagate this from the original json file... or IF AT ALL
@ -378,7 +318,8 @@ void OneSixUpdate::jarlibFailed()
{ {
QStringList failed = jarlibDownloadJob->getFailedFiles(); QStringList failed = jarlibDownloadJob->getFailedFiles();
QString failed_all = failed.join("\n"); 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) void OneSixUpdate::stripJar(QString origPath, QString newPath)
@ -462,7 +403,6 @@ bool OneSixUpdate::MergeZipFiles(QuaZip *into, QString from)
return true; return true;
} }
void OneSixUpdate::fmllibsStart() void OneSixUpdate::fmllibsStart()
{ {
// Get the mod list // Get the mod list

View File

@ -36,9 +36,7 @@ public:
private private
slots: slots:
void versionFileStart(); void versionUpdateFailed(QString reason);
void versionFileFinished();
void versionFileFailed();
void jarlibStart(); void jarlibStart();
void jarlibFinished(); void jarlibFinished();
@ -58,12 +56,14 @@ slots:
void stripJar(QString origPath, QString newPath); void stripJar(QString origPath, QString newPath);
bool MergeZipFiles(QuaZip *into, QString from); bool MergeZipFiles(QuaZip *into, QString from);
private: private:
NetJobPtr specificVersionDownloadJob;
NetJobPtr jarlibDownloadJob; NetJobPtr jarlibDownloadJob;
NetJobPtr legacyDownloadJob; NetJobPtr legacyDownloadJob;
// target version, determined during this task /// target version, determined during this task
std::shared_ptr<MinecraftVersion> targetVersion; std::shared_ptr<MinecraftVersion> targetVersion;
/// the task that is spawned for version updates
std::shared_ptr<Task> versionUpdateTask;
OneSixInstance *m_inst = nullptr; OneSixInstance *m_inst = nullptr;
QString jarHashOnEntry; QString jarHashOnEntry;
QList<FMLlib> fmlLibsToProcess; QList<FMLlib> fmlLibsToProcess;

View File

@ -1,4 +1,5 @@
#include "VersionFilterData.h" #include "VersionFilterData.h"
#include "minecraft/ParseUtils.h"
extern VersionFilterData g_VersionFilterData = VersionFilterData(); extern VersionFilterData g_VersionFilterData = VersionFilterData();
@ -57,8 +58,11 @@ VersionFilterData::VersionFilterData()
// don't use installers for those. // don't use installers for those.
forgeInstallerBlacklist = QSet<QString>({"1.5.2"}); forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
legacyLaunchWhitelist = // these won't show up in version lists because they are extremely bad and dangerous
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", legacyBlacklist = QSet<QString>({"rd-160052"});
"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"}); * 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");
} }

View File

@ -2,6 +2,7 @@
#include <QMap> #include <QMap>
#include <QString> #include <QString>
#include <QSet> #include <QSet>
#include <QDateTime>
struct FMLlib struct FMLlib
{ {
@ -17,7 +18,9 @@ struct VersionFilterData
QMap<QString, QList<FMLlib>> fmlLibsMapping; QMap<QString, QList<FMLlib>> fmlLibsMapping;
// set of minecraft versions for which using forge installers is blacklisted // set of minecraft versions for which using forge installers is blacklisted
QSet<QString> forgeInstallerBlacklist; QSet<QString> forgeInstallerBlacklist;
// set of 'legacy' versions (ones that use the legacy launch) // set of 'legacy' versions that will not show up in the version lists.
QSet<QString> legacyLaunchWhitelist; QSet<QString> legacyBlacklist;
// no new versions below this date will be accepted from Mojang servers
QDateTime legacyCutoffDate;
}; };
extern VersionFilterData g_VersionFilterData; extern VersionFilterData g_VersionFilterData;

View File

@ -1,5 +1,6 @@
#include "JarMod.h" #include "JarMod.h"
#include "logic/MMCJson.h" #include "logic/MMCJson.h"
using namespace MMCJson;
JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename) 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("url", out->baseurl);
readString("MMC-hint", out->hint);
readString("MMC-absoluteUrl", out->absoluteUrl); readString("MMC-absoluteUrl", out->absoluteUrl);
if(!out->baseurl.isEmpty() && out->absoluteUrl.isEmpty()) if(!out->baseurl.isEmpty() && out->absoluteUrl.isEmpty())
{ {
@ -36,6 +38,16 @@ JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename)
return out; 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() QString Jarmod::url()
{ {
if(!absoluteUrl.isEmpty()) if(!absoluteUrl.isEmpty())

View File

@ -8,6 +8,7 @@ class Jarmod
{ {
public: /* methods */ public: /* methods */
static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename); static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename);
QJsonObject toJson();
QString url(); QString url();
public: /* data */ public: /* data */
QString name; QString name;

View File

@ -1,29 +1,26 @@
#include "MinecraftVersion.h" #include "MinecraftVersion.h"
#include "VersionFinal.h" #include "VersionFinal.h"
#include "VersionBuildError.h"
#include "VersionBuilder.h"
bool MinecraftVersion::usesLegacyLauncher() bool MinecraftVersion::usesLegacyLauncher()
{ {
return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch"); return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch");
} }
QString MinecraftVersion::descriptor() QString MinecraftVersion::descriptor()
{ {
return m_descriptor; return m_descriptor;
} }
QString MinecraftVersion::name() QString MinecraftVersion::name()
{ {
return m_name; return m_name;
} }
QString MinecraftVersion::typeString() const QString MinecraftVersion::typeString() const
{ {
if (is_latest && is_snapshot) if (is_snapshot)
{
return QObject::tr("Latest snapshot");
}
else if (is_latest)
{
return QObject::tr("Latest release");
}
else if (is_snapshot)
{ {
return QObject::tr("Snapshot"); return QObject::tr("Snapshot");
} }
@ -32,21 +29,40 @@ QString MinecraftVersion::typeString() const
return QObject::tr("Regular release"); return QObject::tr("Regular release");
} }
} }
bool MinecraftVersion::hasJarMods() bool MinecraftVersion::hasJarMods()
{ {
return false; return false;
} }
bool MinecraftVersion::isVanilla()
bool MinecraftVersion::isMinecraftVersion()
{ {
return true; 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) void MinecraftVersion::applyTo(VersionFinal *version)
{ {
// FIXME: make this work. // 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) if (m_versionSource != Builtin)
{ {
return; throw VersionIncomplete(QObject::tr(
"Minecraft version %1 could not be applied: version files are missing.").arg(m_descriptor));
} }
if (!m_descriptor.isNull()) if (!m_descriptor.isNull())
{ {
@ -81,15 +97,35 @@ void MinecraftVersion::applyTo(VersionFinal *version)
} }
version->traits.unite(m_traits); version->traits.unite(m_traits);
} }
int MinecraftVersion::getOrder() int MinecraftVersion::getOrder()
{ {
return order; return order;
} }
void MinecraftVersion::setOrder(int order) void MinecraftVersion::setOrder(int order)
{ {
this->order = order; this->order = order;
} }
QList<JarmodPtr> MinecraftVersion::getJarMods() QList<JarmodPtr> MinecraftVersion::getJarMods()
{ {
return QList<JarmodPtr>(); 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();
}

View File

@ -22,26 +22,49 @@
#include "logic/BaseVersion.h" #include "logic/BaseVersion.h"
#include "VersionPatch.h" #include "VersionPatch.h"
#include "VersionFile.h" #include "VersionFile.h"
#include "VersionSource.h"
class VersionFinal; 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. /// The URL that this version will be downloaded from. maybe.
QString download_url; QString download_url;
/// is this the latest version?
bool is_latest = false;
/// is this a snapshot? /// is this a snapshot?
bool is_snapshot = false; bool is_snapshot = false;
/// where is this from? VersionSource m_versionSource = Builtin;
enum VersionSource
{
Builtin,
Mojang
} m_versionSource = Builtin;
/// the human readable version name /// the human readable version name
QString m_name; QString m_name;
@ -75,30 +98,6 @@ struct MinecraftVersion : public BaseVersion, public VersionPatch
/// order of this file... default = -2 /// order of this file... default = -2
int order = -2; int order = -2;
bool usesLegacyLauncher(); /// an update available from Mojang
virtual QString descriptor() override; MinecraftVersionPtr upstreamUpdate;
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();
};
}; };

View File

@ -13,27 +13,35 @@
* limitations under the License. * limitations under the License.
*/ */
#include "MinecraftVersionList.h"
#include "MultiMC.h"
#include "logic/net/URLConstants.h"
#include "logic/MMCJson.h"
#include "ParseUtils.h"
#include <QtXml> #include <QtXml>
#include "logic/MMCJson.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QJsonParseError>
#include <QtAlgorithms> #include <QtAlgorithms>
#include <QtNetwork> #include <QtNetwork>
#include "MultiMC.h"
#include "MMCError.h"
#include "MinecraftVersionList.h"
#include "logic/net/URLConstants.h"
#include "ParseUtils.h"
#include "VersionBuilder.h"
#include <logic/VersionFilterData.h>
#include <pathutils.h>
class ListLoadError : public MMCError
{
public:
ListLoadError(QString cause) : MMCError(cause) {};
virtual ~ListLoadError() noexcept
{
}
};
MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent) MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
{ {
loadBuiltinList(); loadBuiltinList();
loadCachedList();
} }
Task *MinecraftVersionList::getLoadTask() Task *MinecraftVersionList::getLoadTask()
@ -68,6 +76,35 @@ void MinecraftVersionList::sortInternal()
qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); 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() void MinecraftVersionList::loadBuiltinList()
{ {
// grab the version list data from internal resources. // grab the version list data from internal resources.
@ -93,19 +130,22 @@ void MinecraftVersionList::loadBuiltinList()
continue; 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. // Now, we construct the version object and add it to the list.
std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion()); std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
mcVersion->m_name = mcVersion->m_descriptor = versionID; mcVersion->m_name = mcVersion->m_descriptor = versionID;
// Parse the timestamp. // Parse the timestamp.
try if (!parse_timestamp(versionObj.value("releaseTime").toString(""),
mcVersion->m_releaseTimeString, mcVersion->m_releaseTime))
{ {
parse_timestamp(versionObj.value("releaseTime").toString(""), QLOG_ERROR() << "Error while parsing version" << versionID
mcVersion->m_releaseTimeString, mcVersion->m_releaseTime); << ": invalid version timestamp";
}
catch (MMCError &e)
{
QLOG_ERROR() << "Error while parsing version" << versionID << ":" << e.cause();
continue; continue;
} }
@ -113,7 +153,7 @@ void MinecraftVersionList::loadBuiltinList()
mcVersion->download_url = mcVersion->download_url =
"http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/"; "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
mcVersion->m_versionSource = MinecraftVersion::Builtin; mcVersion->m_versionSource = Builtin;
mcVersion->m_appletClass = versionObj.value("appletClass").toString(""); mcVersion->m_appletClass = versionObj.value("appletClass").toString("");
mcVersion->m_mainClass = versionObj.value("mainClass").toString(""); mcVersion->m_mainClass = versionObj.value("mainClass").toString("");
mcVersion->m_processArguments = versionObj.value("processArguments").toString("legacy"); mcVersion->m_processArguments = versionObj.value("processArguments").toString("legacy");
@ -124,10 +164,141 @@ void MinecraftVersionList::loadBuiltinList()
mcVersion->m_traits.insert(MMCJson::ensureString(traitVal)); mcVersion->m_traits.insert(MMCJson::ensureString(traitVal));
} }
} }
m_lookup[versionID] = mcVersion;
m_vlist.append(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() void MinecraftVersionList::sort()
{ {
beginResetModel(); beginResetModel();
@ -137,14 +308,8 @@ void MinecraftVersionList::sort()
BaseVersionPtr MinecraftVersionList::getLatestStable() const BaseVersionPtr MinecraftVersionList::getLatestStable() const
{ {
for (int i = 0; i < m_vlist.length(); i++) if(m_lookup.contains(m_latestReleaseID))
{ return m_lookup[m_latestReleaseID];
auto ver = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist.at(i));
if (ver->is_latest && !ver->is_snapshot)
{
return m_vlist.at(i);
}
}
return BaseVersionPtr(); return BaseVersionPtr();
} }
@ -154,17 +319,29 @@ void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
for (auto version : versions) for (auto version : versions)
{ {
auto descr = version->descriptor(); 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); m_vlist.append(version);
SKIP_THIS_ONE: continue;
{
} }
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; m_loaded = true;
sortInternal(); sortInternal();
@ -187,10 +364,6 @@ MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
vlistReply = nullptr; vlistReply = nullptr;
} }
MCVListLoadTask::~MCVListLoadTask()
{
}
void MCVListLoadTask::executeTask() void MCVListLoadTask::executeTask()
{ {
setStatus(tr("Loading instance version list...")); setStatus(tr("Loading instance version list..."));
@ -209,133 +382,196 @@ void MCVListLoadTask::list_downloaded()
return; return;
} }
auto foo = vlistReply->readAll(); auto data = vlistReply->readAll();
QJsonParseError jsonError;
QLOG_INFO() << foo;
QJsonDocument jsonDoc = QJsonDocument::fromJson(foo, &jsonError);
vlistReply->deleteLater(); vlistReply->deleteLater();
if (jsonError.error != QJsonParseError::NoError)
{
emitFailed("Error parsing version list JSON:" + jsonError.errorString());
return;
}
if (!jsonDoc.isObject())
{
emitFailed("Error parsing version list JSON: jsonDoc is not an object");
return;
}
QJsonObject root = jsonDoc.object();
QString latestReleaseID = "INVALID";
QString latestSnapshotID = "INVALID";
try try
{ {
QJsonObject latest = MMCJson::ensureObject(root.value("latest")); m_list->loadMojangList(data, Remote);
latestReleaseID = MMCJson::ensureString(latest.value("release"));
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())
{
emitFailed(
"Error parsing version list JSON: version list object is missing 'versions' array");
return;
}
QJsonArray versions = root.value("versions").toArray();
QList<BaseVersionPtr> tempList;
for (auto version : versions)
{
bool is_snapshot = false;
bool is_latest = 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;
}
// Get the download URL.
QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
// 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;
try
{
// Parse the timestamps.
parse_timestamp(versionObj.value("releaseTime").toString(""),
mcVersion->m_releaseTimeString, mcVersion->m_releaseTime);
parse_timestamp(versionObj.value("time").toString(""),
mcVersion->m_updateTimeString, mcVersion->m_updateTime);
} }
catch (MMCError &e) catch (MMCError &e)
{ {
QLOG_ERROR() << "Error while parsing version" << versionID << ":" << e.cause(); emitFailed(e.cause());
continue; return;
} }
mcVersion->m_versionSource = MinecraftVersion::Builtin;
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_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;
}
tempList.append(mcVersion);
}
m_list->updateListData(tempList);
emitSucceeded(); emitSucceeded();
return; return;
} }
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)
{
emitFailed(tr("The download version file is not valid."));
return;
}
VersionFilePtr file;
try
{
file = VersionFile::fromJson(jsonDoc, "net.minecraft.json", false);
}
catch (MMCError &e)
{
emitFailed(tr("Couldn't process version file: %1").arg(e.cause()));
return;
}
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)
{
if (lwjglFilter.contains(lib->fullname()))
{
lwjglLibs.append(lib);
}
else
{
filteredLibs.append(lib);
}
}
file->overwriteLibs = filteredLibs;
// TODO: recognize and add LWJGL versions here.
file->fileId = "net.minecraft";
// 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;
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())
{
idx = i;
break;
}
}
if (idx == -1)
{
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();
}

View File

@ -22,8 +22,10 @@
#include "logic/BaseVersionList.h" #include "logic/BaseVersionList.h"
#include "logic/tasks/Task.h" #include "logic/tasks/Task.h"
#include "logic/minecraft/MinecraftVersion.h" #include "logic/minecraft/MinecraftVersion.h"
#include <logic/net/NetJob.h>
class MCVListLoadTask; class MCVListLoadTask;
class MCVListVersionUpdateTask;
class QNetworkReply; class QNetworkReply;
class MinecraftVersionList : public BaseVersionList class MinecraftVersionList : public BaseVersionList
@ -32,11 +34,18 @@ class MinecraftVersionList : public BaseVersionList
private: private:
void sortInternal(); void sortInternal();
void loadBuiltinList(); void loadBuiltinList();
void loadMojangList(QByteArray data, VersionSource source);
void loadCachedList();
void saveCachedList();
void finalizeUpdate(QString version);
public: public:
friend class MCVListLoadTask; friend class MCVListLoadTask;
friend class MCVListVersionUpdateTask;
explicit MinecraftVersionList(QObject *parent = 0); explicit MinecraftVersionList(QObject *parent = 0);
std::shared_ptr<Task> createUpdateTask(QString version);
virtual Task *getLoadTask(); virtual Task *getLoadTask();
virtual bool isLoaded(); virtual bool isLoaded();
virtual const BaseVersionPtr at(int i) const; virtual const BaseVersionPtr at(int i) const;
@ -47,8 +56,12 @@ public:
protected: protected:
QList<BaseVersionPtr> m_vlist; QList<BaseVersionPtr> m_vlist;
QMap<QString, BaseVersionPtr> m_lookup;
bool m_loaded = false; bool m_loaded = false;
bool m_hasLocalIndex = false;
QString m_latestReleaseID = "INVALID";
QString m_latestSnapshotID = "INVALID";
protected protected
slots: slots:
@ -61,9 +74,9 @@ class MCVListLoadTask : public Task
public: public:
explicit MCVListLoadTask(MinecraftVersionList *vlist); explicit MCVListLoadTask(MinecraftVersionList *vlist);
~MCVListLoadTask(); virtual ~MCVListLoadTask() override{};
virtual void executeTask(); virtual void executeTask() override;
protected protected
slots: slots:
@ -74,3 +87,22 @@ protected:
MinecraftVersionList *m_list; MinecraftVersionList *m_list;
MinecraftVersion *m_currentStable; 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;
};

View File

@ -23,6 +23,23 @@
#include <JlCompress.h> #include <JlCompress.h>
#include "logger/QsLog.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() void OneSixLibrary::finalize()
{ {
QStringList parts = m_name.split(':'); QStringList parts = m_name.split(':');
@ -30,7 +47,7 @@ void OneSixLibrary::finalize()
relative.replace('.', '/'); relative.replace('.', '/');
relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2];
if (!m_is_native) if (!isNative())
relative += ".jar"; relative += ".jar";
else else
{ {
@ -65,7 +82,7 @@ void OneSixLibrary::finalize()
} }
m_is_active = (result == Allow); m_is_active = (result == Allow);
} }
if (m_is_native) if (isNative())
{ {
m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); m_is_active = m_is_active && m_native_suffixes.contains(currentSystem);
m_decenttype = "Native"; m_decenttype = "Native";
@ -84,13 +101,8 @@ void OneSixLibrary::setBaseUrl(const QString &base_url)
{ {
m_base_url = base_url; m_base_url = base_url;
} }
void OneSixLibrary::setIsNative()
{
m_is_native = true;
}
void OneSixLibrary::addNative(OpSys os, const QString &suffix) void OneSixLibrary::addNative(OpSys os, const QString &suffix)
{ {
m_is_native = true;
m_native_suffixes[os] = suffix; m_native_suffixes[os] = suffix;
} }
void OneSixLibrary::clearSuffixes() void OneSixLibrary::clearSuffixes()
@ -105,10 +117,6 @@ bool OneSixLibrary::isActive() const
{ {
return m_is_active; return m_is_active;
} }
bool OneSixLibrary::isNative() const
{
return m_is_native;
}
QString OneSixLibrary::downloadUrl() const QString OneSixLibrary::downloadUrl() const
{ {
if (m_absolute_url.size()) if (m_absolute_url.size())
@ -223,52 +231,3 @@ bool OneSixLibrary::extractTo(QString target_dir)
} }
return true; 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;
}

View File

@ -24,27 +24,16 @@
#include "logic/net/URLConstants.h" #include "logic/net/URLConstants.h"
#include "logic/minecraft/OpSys.h" #include "logic/minecraft/OpSys.h"
#include "logic/minecraft/RawLibrary.h"
class Rule; class Rule;
class OneSixLibrary; class OneSixLibrary;
typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr; typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr;
class OneSixLibrary class OneSixLibrary : public RawLibrary
{ {
private: 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 /// a decent name fit for display
QString m_decentname; QString m_decentname;
/// a decent version fit for display /// a decent version fit for display
@ -57,13 +46,9 @@ private:
QString m_download_url; QString m_download_url;
/// is this lib actually active on the current OS? /// is this lib actually active on the current OS?
bool m_is_active = false; 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: public:
QStringList extract_excludes;
QString minVersion; QString minVersion;
enum DependType enum DependType
@ -80,6 +65,9 @@ public:
m_name = name; m_name = name;
dependType = type; dependType = type;
} }
/// Constructor
OneSixLibrary(RawLibraryPtr base);
static OneSixLibraryPtr fromRawLibrary(RawLibraryPtr lib);
/// Returns the raw name field /// Returns the raw name field
QString rawName() const QString rawName() const
@ -87,8 +75,6 @@ public:
return m_name; return m_name;
} }
QJsonObject toJson();
/** /**
* finalize the library, processing the input values into derived values and state * finalize the library, processing the input values into derived values and state
* *
@ -116,8 +102,6 @@ public:
/// Set the url base for downloads /// Set the url base for downloads
void setBaseUrl(const QString &base_url); 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 /// Attach a name suffix to the specified OS native
void addNative(OpSys os, const QString &suffix); void addNative(OpSys os, const QString &suffix);
/// Clears all suffixes /// Clears all suffixes
@ -127,8 +111,6 @@ public:
/// Returns true if the library should be loaded (or extracted, in case of natives) /// Returns true if the library should be loaded (or extracted, in case of natives)
bool isActive() const; bool isActive() const;
/// Returns true if the library is native
bool isNative() const;
/// Get the URL to download the library from /// Get the URL to download the library from
QString downloadUrl() const; QString downloadUrl() const;
/// Get the relative path where the library should be saved /// Get the relative path where the library should be saved

View File

@ -16,8 +16,13 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <QList>
#include <QJsonObject>
#include <memory>
#include "OpSys.h"
#include "logic/minecraft/OneSixLibrary.h" class OneSixLibrary;
class Rule;
enum RuleAction enum RuleAction
{ {

View File

@ -8,16 +8,17 @@ QDateTime timeFromS3Time(QString str)
return QDateTime::fromString(str, Qt::ISODate); 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; save_here = raw;
if (save_here.isEmpty()) if (save_here.isEmpty())
{ {
throw JSONValidationError("The timestamp is empty!"); return false;
} }
parse_here = timeFromS3Time(save_here); parse_here = timeFromS3Time(save_here);
if (!parse_here.isValid()) if (!parse_here.isValid())
{ {
throw JSONValidationError("The timestamp not a valid timestamp!"); return false;
} }
return true;
} }

View File

@ -6,7 +6,7 @@
* parse the S3 timestamp in 'raw' and fill the forwarded variables. * parse the S3 timestamp in 'raw' and fill the forwarded variables.
* return true/false for success/failure * 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 * take the timestamp used by S3 and turn it into QDateTime

View File

@ -11,7 +11,7 @@ RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &fil
throw JSONValidationError(filename + throw JSONValidationError(filename +
"contains a library that doesn't have a 'name' field"); "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) 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("url", out->m_base_url);
readString("MMC-hint", out->hint); readString("MMC-hint", out->m_hint);
readString("MMC-absulute_url", out->absoluteUrl); readString("MMC-absulute_url", out->m_absolute_url);
readString("MMC-absoluteUrl", out->absoluteUrl); readString("MMC-absoluteUrl", out->m_absolute_url);
if (libObj.contains("extract")) if (libObj.contains("extract"))
{ {
out->applyExcludes = true; out->applyExcludes = true;
auto extractObj = ensureObject(libObj.value("extract")); auto extractObj = ensureObject(libObj.value("extract"));
for (auto excludeVal : ensureArray(extractObj.value("exclude"))) for (auto excludeVal : ensureArray(extractObj.value("exclude")))
{ {
out->excludes.append(ensureString(excludeVal)); out->extract_excludes.append(ensureString(excludeVal));
} }
} }
if (libObj.contains("natives")) if (libObj.contains("natives"))
{ {
out->applyNatives = true;
QJsonObject nativesObj = ensureObject(libObj.value("natives")); QJsonObject nativesObj = ensureObject(libObj.value("natives"));
for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) 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()); OpSys opSys = OpSys_fromString(it.key());
if (opSys != Os_Other) if (opSys != Os_Other)
{ {
out->natives.append(qMakePair(opSys, it.value().toString())); out->m_native_suffixes[opSys] = it.value().toString();
} }
} }
} }
if (libObj.contains("rules")) if (libObj.contains("rules"))
{ {
out->applyRules = true; out->applyRules = true;
out->rules = rulesFromJsonV4(libObj); out->m_rules = rulesFromJsonV4(libObj);
} }
return out; 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];
}

View File

@ -1,9 +1,14 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <QPair> #include <QPair>
#include <QList>
#include <QStringList>
#include <QMap>
#include <memory> #include <memory>
#include "OneSixRule.h" #include "logic/minecraft/OneSixRule.h"
#include "logic/minecraft/OpSys.h"
#include "logic/net/URLConstants.h"
class RawLibrary; class RawLibrary;
typedef std::shared_ptr<RawLibrary> RawLibraryPtr; typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
@ -11,24 +16,36 @@ typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
class RawLibrary class RawLibrary
{ {
public: /* methods */ public: /* methods */
/// read and create a basic library
static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename); 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 */ public: /* data */
QString name; QString m_name;
QString url; QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
QString hint; /// type hint - modifies how the library is treated
QString absoluteUrl; QString m_hint;
/// absolute URL. takes precedence over m_download_path, if defined
QString m_absolute_url;
bool applyExcludes = false; bool applyExcludes = false;
QStringList excludes; QStringList extract_excludes;
bool applyNatives = false; /// Returns true if the library is native
QList<QPair<OpSys, QString>> natives; bool isNative() const;
/// native suffixes per OS
QMap<OpSys, QString> m_native_suffixes;
bool applyRules = false; bool applyRules = false;
QList<std::shared_ptr<Rule>> rules; QList<std::shared_ptr<Rule>> m_rules;
// user for '+' libraries // used for '+' libraries
enum InsertType enum InsertType
{ {
Apply, Apply,

View File

@ -30,6 +30,7 @@ class VersionBuilder
public: public:
static void build(VersionFinal *version, OneSixInstance *instance, const QStringList &external); static void build(VersionFinal *version, OneSixInstance *instance, const QStringList &external);
static void readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj); 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 QMap<QString, int> readOverrideOrders(OneSixInstance *instance);
static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance); static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance);
@ -49,7 +50,4 @@ private:
void readInstancePatches(); void readInstancePatches();
void readJsonAndApply(const QJsonObject &obj); void readJsonAndApply(const QJsonObject &obj);
VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
bool isFTB = false);
}; };

View File

@ -98,14 +98,10 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
readString("+minecraftArguments", out->addMinecraftArguments); readString("+minecraftArguments", out->addMinecraftArguments);
readString("-minecraftArguments", out->removeMinecraftArguments); readString("-minecraftArguments", out->removeMinecraftArguments);
readString("type", out->type); 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); readString("assets", out->assets);
if (root.contains("minimumLauncherVersion")) if (root.contains("minimumLauncherVersion"))
@ -158,7 +154,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
// FIXME: This should be done when applying. // FIXME: This should be done when applying.
if (isFTB) if (isFTB)
{ {
lib->hint = "local"; lib->m_hint = "local";
lib->insertType = RawLibrary::Prepend; lib->insertType = RawLibrary::Prepend;
out->addLibs.prepend(lib); out->addLibs.prepend(lib);
} }
@ -186,68 +182,8 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
for (auto libVal : ensureArray(root.value("+libraries"))) for (auto libVal : ensureArray(root.value("+libraries")))
{ {
QJsonObject libObj = ensureObject(libVal); QJsonObject libObj = ensureObject(libVal);
QJsonValue insertVal = ensureExists(libObj.value("insert"));
// parse the library // parse the library
auto lib = RawLibrary::fromJson(libObj, filename); auto lib = RawLibrary::fromJsonPlus(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");
}
}
out->addLibs.append(lib); out->addLibs.append(lib);
} }
} }
@ -263,32 +199,66 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
return out; return out;
} }
OneSixLibraryPtr VersionFile::createLibrary(RawLibraryPtr lib) QJsonDocument VersionFile::toJson(bool saveOrder)
{ {
std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib->name)); QJsonObject root;
if (!lib->url.isEmpty()) if (saveOrder)
{ {
out->setBaseUrl(lib->url); root.insert("order", order);
} }
out->setHint(lib->hint); writeString(root, "name", name);
if (!lib->absoluteUrl.isEmpty()) 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())
{ {
out->setAbsoluteUrl(lib->absoluteUrl); writeString(root, "releaseTime", m_releaseTimeString);
writeString(root, "time", m_updateTimeString);
} }
out->setAbsoluteUrl(lib->absoluteUrl); if (minimumLauncherVersion != -1)
out->extract_excludes = lib->excludes;
for (auto native : lib->natives)
{ {
out->addNative(native.first, native.second); root.insert("minimumLauncherVersion", minimumLauncherVersion);
} }
out->setRules(lib->rules); writeStringList(root, "tweakers", overwriteTweakers);
out->finalize(); 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())
{
QJsonArray array;
for (auto lib : removeLibs)
{
QJsonObject rmlibobj;
rmlibobj.insert("name", lib);
array.append(rmlibobj);
}
root.insert("-libraries", array);
}
// write the contents to a json document.
{
QJsonDocument out;
out.setObject(root);
return out; 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() bool VersionFile::hasJarMods()
@ -330,13 +300,13 @@ void VersionFile::applyTo(VersionFinal *version)
} }
if (!processArguments.isNull()) if (!processArguments.isNull())
{ {
if (isVanilla()) if (isMinecraftVersion())
{ {
version->vanillaProcessArguments = processArguments; version->vanillaProcessArguments = processArguments;
} }
version->processArguments = processArguments; version->processArguments = processArguments;
} }
if(isVanilla()) if (isMinecraftVersion())
{ {
if (!type.isNull()) if (!type.isNull())
{ {
@ -364,7 +334,7 @@ void VersionFile::applyTo(VersionFinal *version)
} }
if (!overwriteMinecraftArguments.isNull()) if (!overwriteMinecraftArguments.isNull())
{ {
if (isVanilla()) if (isMinecraftVersion())
{ {
version->vanillaMinecraftArguments = overwriteMinecraftArguments; version->vanillaMinecraftArguments = overwriteMinecraftArguments;
} }
@ -397,10 +367,12 @@ void VersionFile::applyTo(VersionFinal *version)
QList<OneSixLibraryPtr> libs; QList<OneSixLibraryPtr> libs;
for (auto lib : overwriteLibs) for (auto lib : overwriteLibs)
{ {
libs.append(createLibrary(lib)); libs.append(OneSixLibrary::fromRawLibrary(lib));
} }
if (isVanilla()) if (isMinecraftVersion())
{
version->vanillaLibraries = libs; version->vanillaLibraries = libs;
}
version->libraries = libs; version->libraries = libs;
} }
for (auto lib : addLibs) for (auto lib : addLibs)
@ -410,43 +382,46 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Apply: case RawLibrary::Apply:
{ {
// QLOG_INFO() << "Applying lib " << lib->name; // QLOG_INFO() << "Applying lib " << lib->name;
int index = findLibrary(version->libraries, lib->name); int index = findLibrary(version->libraries, lib->m_name);
if (index >= 0) if (index >= 0)
{ {
auto library = version->libraries[index]; 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) 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) for (auto native : lib->natives)
{ {
library->addNative(native.first, native.second); library->addNative(native.first, native.second);
} }
*/
} }
if (lib->applyRules) if (lib->applyRules)
{ {
library->setRules(lib->rules); library->setRules(lib->m_rules);
} }
library->finalize(); library->finalize();
} }
else else
{ {
QLOG_WARN() << "Couldn't find" << lib->name << "(skipping)"; QLOG_WARN() << "Couldn't find" << lib->m_name << "(skipping)";
} }
break; break;
} }
@ -454,24 +429,24 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Prepend: case RawLibrary::Prepend:
{ {
// QLOG_INFO() << "Adding lib " << lib->name; // 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( 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 (index < 0)
{ {
if (lib->insertType == RawLibrary::Append) if (lib->insertType == RawLibrary::Append)
{ {
version->libraries.append(createLibrary(lib)); version->libraries.append(OneSixLibrary::fromRawLibrary(lib));
} }
else else
{ {
version->libraries.prepend(createLibrary(lib)); version->libraries.prepend(OneSixLibrary::fromRawLibrary(lib));
} }
} }
else else
{ {
auto otherLib = version->libraries.at(index); 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(); const Util::Version otherVersion = otherLib->version();
// if the existing version is a hard dependency we can either use it or // if the existing version is a hard dependency we can either use it or
// fail, but we can't change it // fail, but we can't change it
@ -485,7 +460,7 @@ void VersionFile::applyTo(VersionFinal *version)
throw VersionBuildError( throw VersionBuildError(
QObject::tr( QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.") "Error resolving library dependencies between %1 and %2 in %3.")
.arg(otherLib->rawName(), lib->name, filename)); .arg(otherLib->rawName(), lib->m_name, filename));
} }
else else
{ {
@ -497,7 +472,7 @@ void VersionFile::applyTo(VersionFinal *version)
// if we are higher it means we should update // if we are higher it means we should update
if (ourVersion > otherVersion) if (ourVersion > otherVersion)
{ {
auto library = createLibrary(lib); auto library = OneSixLibrary::fromRawLibrary(lib);
if (Util::Version(otherLib->minVersion) < ourVersion) if (Util::Version(otherLib->minVersion) < ourVersion)
{ {
library->minVersion = ourVersion.toString(); library->minVersion = ourVersion.toString();
@ -512,7 +487,7 @@ void VersionFile::applyTo(VersionFinal *version)
{ {
throw VersionBuildError(QObject::tr( throw VersionBuildError(QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.") "Error resolving library dependencies between %1 and %2 in %3.")
.arg(otherLib->rawName(), lib->name, .arg(otherLib->rawName(), lib->m_name,
filename)); filename));
} }
} }
@ -525,8 +500,8 @@ void VersionFile::applyTo(VersionFinal *version)
QString toReplace; QString toReplace;
if (lib->insertData.isEmpty()) if (lib->insertData.isEmpty())
{ {
const int startOfVersion = lib->name.lastIndexOf(':') + 1; const int startOfVersion = lib->m_name.lastIndexOf(':') + 1;
toReplace = QString(lib->name).replace(startOfVersion, INT_MAX, '*'); toReplace = QString(lib->m_name).replace(startOfVersion, INT_MAX, '*');
} }
else else
toReplace = lib->insertData; toReplace = lib->insertData;
@ -534,7 +509,7 @@ void VersionFile::applyTo(VersionFinal *version)
int index = findLibrary(version->libraries, toReplace); int index = findLibrary(version->libraries, toReplace);
if (index >= 0) if (index >= 0)
{ {
version->libraries.replace(index, createLibrary(lib)); version->libraries.replace(index, OneSixLibrary::fromRawLibrary(lib));
} }
else else
{ {

View File

@ -8,7 +8,7 @@
#include "logic/minecraft/OneSixRule.h" #include "logic/minecraft/OneSixRule.h"
#include "VersionPatch.h" #include "VersionPatch.h"
#include "MMCError.h" #include "MMCError.h"
#include "RawLibrary.h" #include "OneSixLibrary.h"
#include "JarMod.h" #include "JarMod.h"
class VersionFinal; class VersionFinal;
@ -20,10 +20,10 @@ class VersionFile : public VersionPatch
public: /* methods */ public: /* methods */
static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename, static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
const bool requireOrder, const bool isFTB = false); const bool requireOrder, const bool isFTB = false);
QJsonDocument toJson(bool saveOrder);
static OneSixLibraryPtr createLibrary(RawLibraryPtr lib);
virtual void applyTo(VersionFinal *version) override; virtual void applyTo(VersionFinal *version) override;
virtual bool isVanilla() override; virtual bool isMinecraftVersion() override;
virtual bool hasJarMods() override; virtual bool hasJarMods() override;
virtual int getOrder() override virtual int getOrder() override
{ {

View File

@ -11,7 +11,7 @@ public:
virtual ~VersionPatch(){}; virtual ~VersionPatch(){};
virtual void applyTo(VersionFinal *version) = 0; virtual void applyTo(VersionFinal *version) = 0;
virtual bool isVanilla() = 0; virtual bool isMinecraftVersion() = 0;
virtual bool hasJarMods() = 0; virtual bool hasJarMods() = 0;
virtual QList<JarmodPtr> getJarMods() = 0; virtual QList<JarmodPtr> getJarMods() = 0;

View File

@ -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.
};

View File

@ -32,6 +32,7 @@ signals:
void status(QString status); void status(QString status);
public: public:
virtual ~ProgressProvider() {};
virtual QString getStatus() const = 0; virtual QString getStatus() const = 0;
virtual void getProgress(qint64 &current, qint64 &total) = 0; virtual void getProgress(qint64 &current, qint64 &total) = 0;
virtual bool isRunning() const = 0; virtual bool isRunning() const = 0;

View File

@ -2,7 +2,7 @@
"fileId": "org.lwjgl", "fileId": "org.lwjgl",
"name": "LWJGL", "name": "LWJGL",
"version": "2.9.0", "version": "2.9.0",
"libraries": [ "+libraries": [
{ {
"name": "net.java.jinput:jinput:2.0.5" "name": "net.java.jinput:jinput:2.0.5"
}, },

View File

@ -2,7 +2,7 @@
"fileId": "org.lwjgl", "fileId": "org.lwjgl",
"name": "LWJGL", "name": "LWJGL",
"version": "2.9.1-nightly-20130708-debug3", "version": "2.9.1-nightly-20130708-debug3",
"libraries": [ "+libraries": [
{ {
"name": "net.java.jinput:jinput:2.0.5" "name": "net.java.jinput:jinput:2.0.5"
}, },

View File

@ -2,7 +2,7 @@
"fileId": "org.lwjgl", "fileId": "org.lwjgl",
"name": "LWJGL", "name": "LWJGL",
"version": "2.9.1", "version": "2.9.1",
"libraries": [ "+libraries": [
{ {
"name": "net.java.jinput:jinput:2.0.5" "name": "net.java.jinput:jinput:2.0.5"
}, },