All of the broken legacy things work.

This commit is contained in:
Petr Mrázek 2014-05-10 01:53:32 +02:00
parent 9860d5ee12
commit 92abe4c603
21 changed files with 701 additions and 356 deletions

View File

@ -479,6 +479,8 @@ SET(MULTIMC_SOURCES
logic/minecraft/OneSixRule.h logic/minecraft/OneSixRule.h
logic/minecraft/OpSys.cpp logic/minecraft/OpSys.cpp
logic/minecraft/OpSys.h logic/minecraft/OpSys.h
logic/minecraft/ParseUtils.cpp
logic/minecraft/ParseUtils.h
logic/minecraft/RawLibrary.cpp logic/minecraft/RawLibrary.cpp
logic/minecraft/RawLibrary.h logic/minecraft/RawLibrary.h
logic/minecraft/VersionBuilder.cpp logic/minecraft/VersionBuilder.cpp
@ -489,10 +491,6 @@ SET(MULTIMC_SOURCES
logic/minecraft/VersionFinal.h logic/minecraft/VersionFinal.h
logic/minecraft/VersionPatch.h logic/minecraft/VersionPatch.h
# Trivial operating system utilities
logic/minecraft/OpSys.h
logic/minecraft/OpSys.cpp
# Various base classes # Various base classes
logic/BaseInstaller.h logic/BaseInstaller.h
logic/BaseInstaller.cpp logic/BaseInstaller.cpp

View File

@ -24,6 +24,7 @@ import java.awt.BorderLayout;
import java.awt.Graphics; import java.awt.Graphics;
import java.applet.Applet; import java.applet.Applet;
import java.applet.AppletStub; import java.applet.AppletStub;
import java.net.MalformedURLException;
public class Launcher extends Applet implements AppletStub public class Launcher extends Applet implements AppletStub
{ {
@ -130,13 +131,23 @@ public class Launcher extends Applet implements AppletStub
@Override @Override
public URL getCodeBase() { public URL getCodeBase() {
return wrappedApplet.getCodeBase(); try {
return new URL("http://www.minecraft.net/game/");
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
} }
@Override @Override
public URL getDocumentBase() public URL getDocumentBase()
{ {
return documentBase; try {
return new URL("http://www.minecraft.net/game/");
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
} }
@Override @Override

View File

@ -53,7 +53,7 @@ public class OneSixLauncher implements Launcher
{ {
libraries = params.all("cp"); libraries = params.all("cp");
extlibs = params.all("ext"); extlibs = params.all("ext");
mcparams = params.all("param"); mcparams = params.allSafe("param", new ArrayList<String>() );
mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft");
appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet");
mods = params.allSafe("mods", new ArrayList<String>()); mods = params.allSafe("mods", new ArrayList<String>());

View File

@ -47,7 +47,7 @@ NewInstanceDialog::NewInstanceDialog(QWidget *parent)
taskDlg->exec(loadTask); taskDlg->exec(loadTask);
} }
*/ */
setSelectedVersion(MMC->minecraftlist()->getLatestStable()); setSelectedVersion(MMC->minecraftlist()->getLatestStable(), true);
InstIconKey = "infinity"; InstIconKey = "infinity";
ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey)); ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
} }
@ -63,13 +63,17 @@ void NewInstanceDialog::updateDialogState()
->setEnabled(!instName().isEmpty() && m_selectedVersion); ->setEnabled(!instName().isEmpty() && m_selectedVersion);
} }
void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version) void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version, bool initial)
{ {
m_selectedVersion = version; m_selectedVersion = version;
if (m_selectedVersion) if (m_selectedVersion)
{ {
ui->versionTextBox->setText(version->name()); ui->versionTextBox->setText(version->name());
if(ui->instNameTextBox->text().isEmpty() && !initial)
{
ui->instNameTextBox->setText(version->name());
}
} }
else else
{ {

View File

@ -33,7 +33,7 @@ public:
void updateDialogState(); void updateDialogState();
void setSelectedVersion(BaseVersionPtr version); void setSelectedVersion(BaseVersionPtr version, bool initial = false);
void loadVersionList(); void loadVersionList();

View File

@ -224,7 +224,10 @@ bool OneSixInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScr
} }
launchScript += "cp " + versionsPath().absoluteFilePath(minecraftjarpath) + "\n"; launchScript += "cp " + versionsPath().absoluteFilePath(minecraftjarpath) + "\n";
} }
launchScript += "mainClass " + version->mainClass + "\n"; if(!version->mainClass.isEmpty())
{
launchScript += "mainClass " + version->mainClass + "\n";
}
if(!version->appletClass.isEmpty()) if(!version->appletClass.isEmpty())
{ {
launchScript += "appletClass " + version->appletClass + "\n"; launchScript += "appletClass " + version->appletClass + "\n";

View File

@ -62,12 +62,14 @@ void OneSixUpdate::executeTask()
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;
} }
versionFileStart(); // builtins need no updates, so only update for Mojang
} if(targetVersion->m_versionSource == MinecraftVersion::Mojang)
else {
{ versionFileStart();
jarlibStart(); return;
}
} }
jarlibStart();
} }
void OneSixUpdate::versionFileStart() void OneSixUpdate::versionFileStart()

View File

@ -1,2 +1,95 @@
#include "MinecraftVersion.h" #include "MinecraftVersion.h"
#include "VersionFinal.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)
{
return QObject::tr("Snapshot");
}
else
{
return QObject::tr("Regular release");
}
}
bool MinecraftVersion::hasJarMods()
{
return false;
}
bool MinecraftVersion::isVanilla()
{
return true;
}
void MinecraftVersion::applyTo(VersionFinal *version)
{
// FIXME: make this work.
if(m_versionSource != Builtin)
{
return;
}
if (!m_descriptor.isNull())
{
version->id = m_descriptor;
}
if (!m_mainClass.isNull())
{
version->mainClass = m_mainClass;
}
if (!m_appletClass.isNull())
{
version->appletClass = m_appletClass;
}
if (!m_processArguments.isNull())
{
version->vanillaProcessArguments = m_processArguments;
version->processArguments = m_processArguments;
}
if (!m_type.isNull())
{
version->type = m_type;
}
if (!m_releaseTimeString.isNull())
{
version->m_releaseTimeString = m_releaseTimeString;
version->m_releaseTime = m_releaseTime;
}
if (!m_updateTimeString.isNull())
{
version->m_updateTimeString = m_updateTimeString;
version->m_updateTime = m_updateTime;
}
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>();
}

View File

@ -15,16 +15,18 @@
#pragma once #pragma once
#include "logic/BaseVersion.h"
#include "VersionPatch.h"
#include <QStringList> #include <QStringList>
#include <QSet> #include <QSet>
#include <QDateTime>
#include "logic/BaseVersion.h"
#include "VersionPatch.h"
#include "VersionFile.h"
class VersionFinal;
struct MinecraftVersion : public BaseVersion, public VersionPatch struct MinecraftVersion : public BaseVersion, public VersionPatch
{ {
/// The version's timestamp - this is primarily used for sorting versions in a list.
qint64 timestamp;
/// 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;
@ -34,8 +36,12 @@ struct MinecraftVersion : public BaseVersion, public VersionPatch
/// is this a snapshot? /// is this a snapshot?
bool is_snapshot = false; bool is_snapshot = false;
/// is this a built-in version that comes with MultiMC? /// where is this from?
bool is_builtin = false; enum VersionSource
{
Builtin,
Mojang
} m_versionSource = Builtin;
/// the human readable version name /// the human readable version name
QString m_name; QString m_name;
@ -43,7 +49,7 @@ struct MinecraftVersion : public BaseVersion, public VersionPatch
/// the version ID. /// the version ID.
QString m_descriptor; QString m_descriptor;
/// version traits. generally launcher business... /// version traits. added by MultiMC
QSet<QString> m_traits; QSet<QString> m_traits;
/// The main class this version uses (if any, can be empty). /// The main class this version uses (if any, can be empty).
@ -52,57 +58,47 @@ struct MinecraftVersion : public BaseVersion, public VersionPatch
/// The applet class this version uses (if any, can be empty). /// The applet class this version uses (if any, can be empty).
QString m_appletClass; QString m_appletClass;
bool usesLegacyLauncher() /// The process arguments used by this version
{ QString m_processArguments;
return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch");
}
virtual QString descriptor() override /// The type of this release
QString m_type;
/// the time this version was actually released by Mojang, as string and as QDateTime
QString m_releaseTimeString;
QDateTime m_releaseTime;
/// the time this version was last updated by Mojang, as string and as QDateTime
QString m_updateTimeString;
QDateTime m_updateTime;
/// 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; return m_descriptor;
} }
virtual QString getPatchName()
virtual QString name() override
{ {
return m_name; return "Minecraft";
} }
virtual QString getPatchFilename()
virtual QString typeString() const override
{ {
if (is_latest && is_snapshot) return QString();
{ };
return QObject::tr("Latest snapshot");
}
else if(is_latest)
{
return QObject::tr("Latest release");
}
else if(is_snapshot)
{
return QObject::tr("Snapshot");
}
else if(is_builtin)
{
return QObject::tr("Museum piece");
}
else
{
return QObject::tr("Regular release");
}
}
virtual bool hasJarMods() override
{
return false;
}
virtual bool isVanilla() override
{
return true;
}
virtual void applyTo(VersionFinal *version)
{
// umm... what now?
}
}; };

View File

@ -16,7 +16,8 @@
#include "MinecraftVersionList.h" #include "MinecraftVersionList.h"
#include "MultiMC.h" #include "MultiMC.h"
#include "logic/net/URLConstants.h" #include "logic/net/URLConstants.h"
#include <logic/MMCJson.h> #include "logic/MMCJson.h"
#include "ParseUtils.h"
#include <QtXml> #include <QtXml>
@ -30,11 +31,6 @@
#include <QtNetwork> #include <QtNetwork>
inline QDateTime timeFromS3Time(QString str)
{
return QDateTime::fromString(str, Qt::ISODate);
}
MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent) MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
{ {
loadBuiltinList(); loadBuiltinList();
@ -64,7 +60,7 @@ static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
{ {
auto left = std::dynamic_pointer_cast<MinecraftVersion>(first); auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
auto right = std::dynamic_pointer_cast<MinecraftVersion>(second); auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
return left->timestamp > right->timestamp; return left->m_releaseTime > right->m_releaseTime;
} }
void MinecraftVersionList::sortInternal() void MinecraftVersionList::sortInternal()
@ -90,44 +86,44 @@ void MinecraftVersionList::loadBuiltinList()
{ {
QJsonObject versionObj = version.toObject(); QJsonObject versionObj = version.toObject();
QString versionID = versionObj.value("id").toString(""); QString versionID = versionObj.value("id").toString("");
QString versionTimeStr = versionObj.value("releaseTime").toString("");
QString versionTypeStr = versionObj.value("type").toString(""); QString versionTypeStr = versionObj.value("type").toString("");
QSet<QString> traits; if (versionID.isEmpty() || versionTypeStr.isEmpty())
if (versionObj.contains("+traits"))
{ {
for (auto traitVal : MMCJson::ensureArray(versionObj.value("+traits"))) QLOG_ERROR() << "Parsed version is missing ID or type";
{
traits.insert(MMCJson::ensureString(traitVal));
}
}
if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
{
// FIXME: log this somewhere
continue; continue;
} }
// Parse the timestamp.
QDateTime versionTime = timeFromS3Time(versionTimeStr);
if (!versionTime.isValid())
{
// FIXME: log this somewhere
continue;
}
// Get the download URL.
QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
// main class and applet class
QString mainClass = versionObj.value("type").toString("");
QString appletClass = versionObj.value("type").toString("");
// 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;
mcVersion->timestamp = versionTime.toMSecsSinceEpoch();
mcVersion->download_url = dlUrl; // Parse the timestamp.
mcVersion->is_builtin = true; try
mcVersion->m_appletClass = appletClass; {
mcVersion->m_mainClass = mainClass; parse_timestamp(versionObj.value("releaseTime").toString(""),
mcVersion->m_traits = traits; mcVersion->m_releaseTimeString, mcVersion->m_releaseTime);
}
catch (MMCError &e)
{
QLOG_ERROR() << "Error while parsing version" << versionID << ":" << e.cause();
continue;
}
// Get the download URL.
mcVersion->download_url =
"http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
mcVersion->m_versionSource = MinecraftVersion::Builtin;
mcVersion->m_appletClass = versionObj.value("appletClass").toString("");
mcVersion->m_mainClass = versionObj.value("mainClass").toString("");
mcVersion->m_processArguments = versionObj.value("processArguments").toString("legacy");
if (versionObj.contains("+traits"))
{
for (auto traitVal : MMCJson::ensureArray(versionObj.value("+traits")))
{
mcVersion->m_traits.insert(MMCJson::ensureString(traitVal));
}
}
m_vlist.append(mcVersion); m_vlist.append(mcVersion);
} }
} }
@ -265,50 +261,14 @@ void MCVListLoadTask::list_downloaded()
// Load the version info. // Load the version info.
if (!version.isObject()) if (!version.isObject())
{ {
// FIXME: log this somewhere QLOG_ERROR() << "Error while parsing version list : invalid JSON structure";
continue; continue;
} }
QJsonObject versionObj = version.toObject(); QJsonObject versionObj = version.toObject();
QString versionID = versionObj.value("id").toString(""); QString versionID = versionObj.value("id").toString("");
QString versionTimeStr = versionObj.value("releaseTime").toString(""); if (versionID.isEmpty())
QString versionTypeStr = versionObj.value("type").toString("");
if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
{ {
// FIXME: log this somewhere QLOG_ERROR() << "Error while parsing version : version ID is missing";
continue;
}
// Parse the timestamp.
QDateTime versionTime = timeFromS3Time(versionTimeStr);
if (!versionTime.isValid())
{
// 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; continue;
} }
// Get the download URL. // Get the download URL.
@ -317,10 +277,61 @@ void MCVListLoadTask::list_downloaded()
// 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;
mcVersion->timestamp = versionTime.toMSecsSinceEpoch();
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)
{
QLOG_ERROR() << "Error while parsing version" << versionID << ":" << e.cause();
continue;
}
mcVersion->m_versionSource = MinecraftVersion::Builtin;
mcVersion->download_url = dlUrl; mcVersion->download_url = dlUrl;
mcVersion->is_latest = is_latest; {
mcVersion->is_snapshot = is_snapshot; 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); tempList.append(mcVersion);
} }
m_list->updateListData(tempList); m_list->updateListData(tempList);

View File

@ -0,0 +1,23 @@
#include <QDateTime>
#include <QString>
#include "ParseUtils.h"
#include <logic/MMCJson.h>
QDateTime timeFromS3Time(QString str)
{
return QDateTime::fromString(str, Qt::ISODate);
}
void parse_timestamp (const QString & raw, QString & save_here, QDateTime & parse_here)
{
save_here = raw;
if (save_here.isEmpty())
{
throw JSONValidationError("The timestamp is empty!");
}
parse_here = timeFromS3Time(save_here);
if (!parse_here.isValid())
{
throw JSONValidationError("The timestamp not a valid timestamp!");
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <QString>
#include <QDateTime>
/**
* 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);
/**
* take the timestamp used by S3 and turn it into QDateTime
*/
QDateTime timeFromS3Time(QString str);

View File

@ -0,0 +1,58 @@
#include "MMCError.h"
class VersionBuildError : public MMCError
{
public:
VersionBuildError(QString cause) : MMCError(cause) {};
virtual ~VersionBuildError() noexcept
{
}
};
/**
* the base version file was meant for a newer version of the vanilla launcher than we support
*/
class LauncherVersionError : public VersionBuildError
{
public:
LauncherVersionError(int actual, int supported)
: VersionBuildError(QObject::tr(
"The base version file of this instance was meant for a newer (%1) "
"version of the vanilla launcher than this version of MultiMC supports (%2).")
.arg(actual)
.arg(supported)) {};
virtual ~LauncherVersionError() noexcept
{
}
};
/**
* some patch was intended for a different version of minecraft
*/
class MinecraftVersionMismatch : public VersionBuildError
{
public:
MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
: VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
"(%2) than that of the instance (%3).")
.arg(fileId)
.arg(mcVersion)
.arg(parentMcVersion)) {};
virtual ~MinecraftVersionMismatch() noexcept
{
}
};
/**
* files required for the version are not (yet?) present
*/
class VersionIncomplete : public VersionBuildError
{
public:
VersionIncomplete(QString missingPatch)
: VersionBuildError(QObject::tr("Version is incomplete: missing %1.")
.arg(missingPatch)) {};
virtual ~VersionIncomplete() noexcept
{
}
};

View File

@ -23,12 +23,17 @@
#include <QObject> #include <QObject>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <qresource.h>
#include <modutils.h> #include <modutils.h>
#include "MultiMC.h"
#include "logic/minecraft/VersionBuilder.h" #include "logic/minecraft/VersionBuilder.h"
#include "logic/minecraft/VersionFinal.h" #include "logic/minecraft/VersionFinal.h"
#include "logic/minecraft/OneSixRule.h" #include "logic/minecraft/OneSixRule.h"
#include "logic/minecraft/VersionPatch.h"
#include "logic/minecraft/VersionFile.h" #include "logic/minecraft/VersionFile.h"
#include "VersionBuildError.h"
#include "MinecraftVersionList.h"
#include "logic/OneSixInstance.h" #include "logic/OneSixInstance.h"
#include "logic/MMCJson.h" #include "logic/MMCJson.h"
@ -39,16 +44,17 @@ VersionBuilder::VersionBuilder()
{ {
} }
void VersionBuilder::build(VersionFinal *version, OneSixInstance *instance, const QStringList &external) void VersionBuilder::build(VersionFinal *version, OneSixInstance *instance,
const QStringList &external)
{ {
VersionBuilder builder; VersionBuilder builder;
builder.m_version = version; builder.m_version = version;
builder.m_instance = instance; builder.m_instance = instance;
builder.buildInternal(external); builder.external_patches = external;
builder.buildInternal();
} }
void VersionBuilder::readJsonAndApplyToVersion(VersionFinal *version, void VersionBuilder::readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj)
const QJsonObject &obj)
{ {
VersionBuilder builder; VersionBuilder builder;
builder.m_version = version; builder.m_version = version;
@ -56,88 +62,143 @@ void VersionBuilder::readJsonAndApplyToVersion(VersionFinal *version,
builder.readJsonAndApply(obj); builder.readJsonAndApply(obj);
} }
void VersionBuilder::buildInternal(const QStringList &external) void VersionBuilder::buildFromCustomJson()
{ {
m_version->versionFiles.clear(); QLOG_INFO() << "Building version from custom.json within the instance.";
QLOG_INFO() << "Reading custom.json";
auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("custom.json")), false);
file->name = "custom.json";
file->filename = "custom.json";
file->fileId = "org.multimc.custom.json";
file->order = -1;
file->version = QString();
m_version->VersionPatches.append(file);
// QObject::tr("The version descriptors of this instance are not compatible with the
// current version of MultiMC"));
// QObject::tr("Error while applying %1. Please check MultiMC-0.log for more info.")
// some final touches
m_version->finalize();
return;
}
QDir root(m_instance->instanceRoot()); void VersionBuilder::buildFromVersionJson()
QDir patches(root.absoluteFilePath("patches/")); {
QLOG_INFO() << "Building version from version.json and patches within the instance.";
QLOG_INFO() << "Reading version.json";
auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("version.json")), false);
file->name = "Minecraft";
file->fileId = "org.multimc.version.json";
file->order = -1;
file->version = m_instance->intendedVersionId();
file->mcVersion = m_instance->intendedVersionId();
m_version->VersionPatches.append(file);
// if we do external files, do just those. // load all patches, put into map for ordering, apply in the right order
if (!external.isEmpty()) readInstancePatches();
{
int externalOrder = -1;
for (auto fileName : external)
{
QLOG_INFO() << "Reading" << fileName;
auto file =
parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
file->name = QFileInfo(fileName).fileName();
file->fileId = "org.multimc.external." + file->name;
file->order = (externalOrder += 1);
file->version = QString();
file->mcVersion = QString();
m_version->versionFiles.append(file);
}
}
// else, if there's custom json, we just do that.
else if (QFile::exists(root.absoluteFilePath("custom.json")))
{
QLOG_INFO() << "Reading custom.json";
auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("custom.json")), false);
file->name = "custom.json";
file->filename = "custom.json";
file->fileId = "org.multimc.custom.json";
file->order = -1;
file->version = QString();
m_version->versionFiles.append(file);
// QObject::tr("The version descriptors of this instance are not compatible with the
// current version of MultiMC"));
// QObject::tr("Error while applying %1. Please check MultiMC-0.log for more info.")
}
// version.json -> patches/*.json -> user.json
else
do
{
// version.json
QLOG_INFO() << "Reading version.json";
auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false);
file->name = "Minecraft";
file->fileId = "org.multimc.version.json";
file->order = -1;
file->version = m_instance->intendedVersionId();
file->mcVersion = m_instance->intendedVersionId();
m_version->versionFiles.append(file);
// QObject::tr("Error while applying %1. Please check MultiMC-0.log for more
// info.").arg(root.absoluteFilePath("version.json")));
// patches/
// load all, put into map for ordering, apply in the right order
QMap<int, QPair<QString, VersionFilePtr>> files;
for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
QLOG_INFO() << "Reading" << info.fileName();
auto file = parseJsonFile(info, true);
if (files.contains(file->order))
{
throw VersionBuildError(QObject::tr("%1 has the same order as %2").arg(
file->fileId, files[file->order].second->fileId));
}
files.insert(file->order, qMakePair(info.fileName(), file));
}
for (auto order : files.keys())
{
auto &filePair = files[order];
m_version->versionFiles.append(filePair.second);
}
} while (0);
// some final touches // some final touches
m_version->finalize(); m_version->finalize();
} }
void VersionBuilder::readInstancePatches()
{
QDir patches(instance_root.absoluteFilePath("patches/"));
QMap<int, QPair<QString, VersionFilePtr>> files;
for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
QLOG_INFO() << "Reading" << info.fileName();
auto file = parseJsonFile(info, true);
if(file->fileId == "net.minecraft")
continue;
if(file->fileId == "org.lwjgl")
continue;
if (files.contains(file->order))
{
throw VersionBuildError(QObject::tr("%1 has the same order as %2")
.arg(file->fileId, files[file->order].second->fileId));
}
files.insert(file->order, qMakePair(info.fileName(), file));
}
for (auto order : files.keys())
{
auto &filePair = files[order];
m_version->VersionPatches.append(filePair.second);
}
}
void VersionBuilder::buildFromExternalPatches()
{
QLOG_INFO() << "Building version from external files.";
int externalOrder = -1;
for (auto fileName : external_patches)
{
QLOG_INFO() << "Reading" << fileName;
auto file = parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
file->name = QFileInfo(fileName).fileName();
file->fileId = "org.multimc.external." + file->name;
file->order = (externalOrder += 1);
file->version = QString();
file->mcVersion = QString();
m_version->VersionPatches.append(file);
}
// some final touches
m_version->finalize();
}
void VersionBuilder::buildFromMultilayer()
{
QLOG_INFO() << "Building version from multilayered sources.";
// just the builtin stuff for now
auto minecraftList = MMC->minecraftlist();
auto mcversion = minecraftList->findVersion(m_instance->intendedVersionId());
auto minecraftPatch = std::dynamic_pointer_cast<VersionPatch>(mcversion);
if(!minecraftPatch)
{
throw VersionIncomplete("net.minecraft");
}
minecraftPatch->setOrder(-2);
m_version->VersionPatches.append(minecraftPatch);
QResource LWJGL(":/versions/LWJGL/2.9.1.json");
auto lwjgl = parseJsonFile(LWJGL.absoluteFilePath(), false, false);
auto lwjglPatch = std::dynamic_pointer_cast<VersionPatch>(lwjgl);
if(!lwjglPatch)
{
throw VersionIncomplete("org.lwjgl");
}
lwjglPatch->setOrder(-1);
m_version->VersionPatches.append(lwjglPatch);
// load all patches, put into map for ordering, apply in the right order
readInstancePatches();
m_version->finalize();
}
void VersionBuilder::buildInternal()
{
m_version->VersionPatches.clear();
instance_root = QDir(m_instance->instanceRoot());
// if we do external files, do just those.
if (!external_patches.isEmpty())
{
buildFromExternalPatches();
}
// else, if there's custom json, we just do that.
else if (QFile::exists(instance_root.absoluteFilePath("custom.json")))
{
buildFromCustomJson();
}
// version.json -> patches/*.json
else if (QFile::exists(instance_root.absoluteFilePath("version.json")))
{
buildFromVersionJson();
}
else
{
buildFromMultilayer();
}
}
void VersionBuilder::readJsonAndApply(const QJsonObject &obj) void VersionBuilder::readJsonAndApply(const QJsonObject &obj)
{ {
@ -147,14 +208,14 @@ void VersionBuilder::readJsonAndApply(const QJsonObject &obj)
// QObject::tr("Error while reading. Please check MultiMC-0.log for more info.")); // QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
file->applyTo(m_version); file->applyTo(m_version);
m_version->versionFiles.append(file); m_version->VersionPatches.append(file);
// QObject::tr("Error while applying. Please check MultiMC-0.log for more info.")); // QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
// QObject::tr("The version descriptors of this instance are not compatible with the current // QObject::tr("The version descriptors of this instance are not compatible with the current
// version of MultiMC")); // version of MultiMC"));
} }
VersionFilePtr VersionBuilder::parseJsonFile(const QFileInfo &fileInfo, VersionFilePtr VersionBuilder::parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
const bool requireOrder, bool isFTB) bool isFTB)
{ {
QFile file(fileInfo.absoluteFilePath()); QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly)) if (!file.open(QFile::ReadOnly))
@ -166,9 +227,10 @@ VersionFilePtr VersionBuilder::parseJsonFile(const QFileInfo &fileInfo,
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError) if (error.error != QJsonParseError::NoError)
{ {
throw JSONValidationError(QObject::tr("Unable to process the version file %1: %2 at %3.") throw JSONValidationError(
.arg(fileInfo.fileName(), error.errorString()) QObject::tr("Unable to process the version file %1: %2 at %3.")
.arg(error.offset)); .arg(fileInfo.fileName(), error.errorString())
.arg(error.offset));
} }
return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB); return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
// QObject::tr("Error while reading %1. Please check MultiMC-0.log for more // QObject::tr("Error while reading %1. Please check MultiMC-0.log for more
@ -226,7 +288,7 @@ QMap<QString, int> VersionBuilder::readOverrideOrders(OneSixInstance *instance)
} }
bool VersionBuilder::writeOverrideOrders(const QMap<QString, int> &order, bool VersionBuilder::writeOverrideOrders(const QMap<QString, int> &order,
OneSixInstance *instance) OneSixInstance *instance)
{ {
QJsonObject obj; QJsonObject obj;
for (auto it = order.cbegin(); it != order.cend(); ++it) for (auto it = order.cbegin(); it != order.cend(); ++it)

View File

@ -37,8 +37,17 @@ public:
private: private:
VersionFinal *m_version; VersionFinal *m_version;
OneSixInstance *m_instance; OneSixInstance *m_instance;
QStringList external_patches;
QDir instance_root;
void buildInternal();
void buildFromExternalPatches();
void buildFromCustomJson();
void buildFromVersionJson();
void buildFromMultilayer();
void readInstancePatches();
void buildInternal(const QStringList& external);
void readJsonAndApply(const QJsonObject &obj); void readJsonAndApply(const QJsonObject &obj);
VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,

View File

@ -8,10 +8,13 @@
#include "logic/minecraft/OneSixLibrary.h" #include "logic/minecraft/OneSixLibrary.h"
#include "logic/minecraft/VersionFinal.h" #include "logic/minecraft/VersionFinal.h"
#include "logic/minecraft/JarMod.h" #include "logic/minecraft/JarMod.h"
#include "ParseUtils.h"
#include "logic/MMCJson.h" #include "logic/MMCJson.h"
using namespace MMCJson; using namespace MMCJson;
#include "VersionBuildError.h"
#define CURRENT_MINIMUM_LAUNCHER_VERSION 14 #define CURRENT_MINIMUM_LAUNCHER_VERSION 14
int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle) int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
@ -23,7 +26,7 @@ int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(chunk) != -1) if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(chunk) != -1)
{ {
// only one is allowed. // only one is allowed.
if(retval != -1) if (retval != -1)
return -1; return -1;
retval = i; retval = i;
} }
@ -32,7 +35,7 @@ int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
} }
VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &filename, VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &filename,
const bool requireOrder, const bool isFTB) const bool requireOrder, const bool isFTB)
{ {
VersionFilePtr out(new VersionFile()); VersionFilePtr out(new VersionFile());
if (doc.isEmpty() || doc.isNull()) if (doc.isEmpty() || doc.isNull())
@ -41,7 +44,6 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
} }
if (!doc.isObject()) if (!doc.isObject())
{ {
throw JSONValidationError("The root of " + filename + " is not an object");
} }
QJsonObject root = doc.object(); QJsonObject root = doc.object();
@ -65,7 +67,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
out->mcVersion = root.value("mcVersion").toString(); out->mcVersion = root.value("mcVersion").toString();
out->filename = filename; out->filename = filename;
auto readString = [root, filename](const QString & key, QString & variable) auto readString = [root](const QString & key, QString & variable)
{ {
if (root.contains(key)) if (root.contains(key))
{ {
@ -73,6 +75,16 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
} }
}; };
auto readStringRet = [root](const QString & key)->QString
{
if (root.contains(key))
{
return ensureString(root.value(key));
}
return QString();
}
;
// FIXME: This should be ignored when applying. // FIXME: This should be ignored when applying.
if (!isFTB) if (!isFTB)
{ {
@ -86,8 +98,14 @@ 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);
readString("releaseTime", out->versionReleaseTime); if (out->isVanilla())
readString("time", out->versionFileUpdateTime); {
parse_timestamp(readStringRet("releaseTime"), out->m_releaseTimeString,
out->m_releaseTime);
parse_timestamp(readStringRet("time"), out->m_updateTimeString, out->m_updateTime);
}
readStringRet("time");
readString("assets", out->assets); readString("assets", out->assets);
if (root.contains("minimumLauncherVersion")) if (root.contains("minimumLauncherVersion"))
@ -284,7 +302,8 @@ void VersionFile::applyTo(VersionFinal *version)
{ {
if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
{ {
throw LauncherVersionError(minimumLauncherVersion, CURRENT_MINIMUM_LAUNCHER_VERSION); throw LauncherVersionError(minimumLauncherVersion,
CURRENT_MINIMUM_LAUNCHER_VERSION);
} }
} }
@ -311,23 +330,28 @@ void VersionFile::applyTo(VersionFinal *version)
} }
if (!processArguments.isNull()) if (!processArguments.isNull())
{ {
if(isVanilla()) if (isVanilla())
{ {
version->vanillaProcessArguments = processArguments; version->vanillaProcessArguments = processArguments;
} }
version->processArguments = processArguments; version->processArguments = processArguments;
} }
if (!type.isNull()) if(isVanilla())
{ {
version->type = type; if (!type.isNull())
} {
if (!versionReleaseTime.isNull()) version->type = type;
{ }
version->versionReleaseTime = versionReleaseTime; if (!m_releaseTimeString.isNull())
} {
if (!versionFileUpdateTime.isNull()) version->m_releaseTimeString = m_releaseTimeString;
{ version->m_releaseTime = m_releaseTime;
version->time = versionFileUpdateTime; }
if (!m_updateTimeString.isNull())
{
version->m_updateTimeString = m_updateTimeString;
version->m_updateTime = m_updateTime;
}
} }
if (!assets.isNull()) if (!assets.isNull())
{ {
@ -335,11 +359,12 @@ void VersionFile::applyTo(VersionFinal *version)
} }
if (minimumLauncherVersion >= 0) if (minimumLauncherVersion >= 0)
{ {
version->minimumLauncherVersion = minimumLauncherVersion; if(version->minimumLauncherVersion < minimumLauncherVersion)
version->minimumLauncherVersion = minimumLauncherVersion;
} }
if (!overwriteMinecraftArguments.isNull()) if (!overwriteMinecraftArguments.isNull())
{ {
if(isVanilla()) if (isVanilla())
{ {
version->vanillaMinecraftArguments = overwriteMinecraftArguments; version->vanillaMinecraftArguments = overwriteMinecraftArguments;
} }
@ -374,7 +399,7 @@ void VersionFile::applyTo(VersionFinal *version)
{ {
libs.append(createLibrary(lib)); libs.append(createLibrary(lib));
} }
if(isVanilla()) if (isVanilla())
version->vanillaLibraries = libs; version->vanillaLibraries = libs;
version->libraries = libs; version->libraries = libs;
} }
@ -498,7 +523,7 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Replace: case RawLibrary::Replace:
{ {
QString toReplace; QString toReplace;
if(lib->insertData.isEmpty()) if (lib->insertData.isEmpty())
{ {
const int startOfVersion = lib->name.lastIndexOf(':') + 1; const int startOfVersion = lib->name.lastIndexOf(':') + 1;
toReplace = QString(lib->name).replace(startOfVersion, INT_MAX, '*'); toReplace = QString(lib->name).replace(startOfVersion, INT_MAX, '*');

View File

@ -2,6 +2,7 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QDateTime>
#include <memory> #include <memory>
#include "logic/minecraft/OpSys.h" #include "logic/minecraft/OpSys.h"
#include "logic/minecraft/OneSixRule.h" #include "logic/minecraft/OneSixRule.h"
@ -11,46 +12,8 @@
#include "JarMod.h" #include "JarMod.h"
class VersionFinal; class VersionFinal;
class VersionBuildError : public MMCError
{
public:
VersionBuildError(QString cause) : MMCError(cause) {};
virtual ~VersionBuildError() noexcept {}
};
/**
* the base version file was meant for a newer version of the vanilla launcher than we support
*/
class LauncherVersionError : public VersionBuildError
{
public:
LauncherVersionError(int actual, int supported)
: VersionBuildError(QObject::tr(
"The base version file of this instance was meant for a newer (%1) "
"version of the vanilla launcher than this version of MultiMC supports (%2).")
.arg(actual)
.arg(supported)) {};
virtual ~LauncherVersionError() noexcept {}
};
/**
* some patch was intended for a different version of minecraft
*/
class MinecraftVersionMismatch : public VersionBuildError
{
public:
MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
: VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
"(%2) than that of the instance (%3).")
.arg(fileId)
.arg(mcVersion)
.arg(parentMcVersion)) {};
virtual ~MinecraftVersionMismatch() noexcept {}
};
struct VersionFile; struct VersionFile;
typedef std::shared_ptr<VersionFile> VersionFilePtr; typedef std::shared_ptr<VersionFile> VersionFilePtr;
class VersionFile : public VersionPatch class VersionFile : public VersionPatch
{ {
@ -62,6 +25,34 @@ public: /* methods */
virtual void applyTo(VersionFinal *version) override; virtual void applyTo(VersionFinal *version) override;
virtual bool isVanilla() override; virtual bool isVanilla() override;
virtual bool hasJarMods() override; virtual bool hasJarMods() override;
virtual int getOrder() override
{
return order;
}
virtual void setOrder(int order) override
{
this->order = order;
}
virtual QList<JarmodPtr> getJarMods() override
{
return jarMods;
}
virtual QString getPatchID() override
{
return fileId;
}
virtual QString getPatchName() override
{
return name;
}
virtual QString getPatchVersion() override
{
return version;
}
virtual QString getPatchFilename() override
{
return filename;
}
public: /* data */ public: /* data */
int order = 0; int order = 0;
@ -81,8 +72,16 @@ public: /* data */
QString removeMinecraftArguments; QString removeMinecraftArguments;
QString processArguments; QString processArguments;
QString type; QString type;
QString versionReleaseTime;
QString versionFileUpdateTime; /// the time this version was actually released by Mojang, as string and as QDateTime
QString m_releaseTimeString;
QDateTime m_releaseTime;
/// the time this version was last updated by Mojang, as string and as QDateTime
QString m_updateTimeString;
QDateTime m_updateTime;
/// asset group used by this ... thing.
QString assets; QString assets;
int minimumLauncherVersion = -1; int minimumLauncherVersion = -1;

View File

@ -39,8 +39,10 @@ void VersionFinal::reload(const QStringList &external)
void VersionFinal::clear() void VersionFinal::clear()
{ {
id.clear(); id.clear();
time.clear(); m_updateTimeString.clear();
versionReleaseTime.clear(); m_updateTime = QDateTime();
m_releaseTimeString.clear();
m_releaseTime = QDateTime();
type.clear(); type.clear();
assets.clear(); assets.clear();
processArguments.clear(); processArguments.clear();
@ -56,17 +58,13 @@ void VersionFinal::clear()
bool VersionFinal::canRemove(const int index) const bool VersionFinal::canRemove(const int index) const
{ {
if (index < versionFiles.size()) return VersionPatches.at(index)->isMoveable();
{
return versionFiles.at(index)->fileId != "org.multimc.version.json";
}
return false;
} }
bool VersionFinal::preremove(VersionFilePtr versionfile) bool VersionFinal::preremove(VersionPatchPtr patch)
{ {
bool ok = true; bool ok = true;
for(auto & jarmod: versionfile->jarMods) for(auto & jarmod: patch->getJarMods())
{ {
QString fullpath =PathCombine(m_instance->jarModsDir(), jarmod->name); QString fullpath =PathCombine(m_instance->jarModsDir(), jarmod->name);
QFileInfo finfo (fullpath); QFileInfo finfo (fullpath);
@ -80,14 +78,14 @@ bool VersionFinal::remove(const int index)
{ {
if (!canRemove(index)) if (!canRemove(index))
return false; return false;
if(!preremove(versionFiles[index])) if(!preremove(VersionPatches[index]))
{ {
return false; return false;
} }
if(!QFile::remove(versionFiles.at(index)->filename)) if(!QFile::remove(VersionPatches.at(index)->getPatchFilename()))
return false; return false;
beginResetModel(); beginResetModel();
versionFiles.removeAt(index); VersionPatches.removeAt(index);
reapply(true); reapply(true);
endResetModel(); endResetModel();
return true; return true;
@ -96,9 +94,9 @@ bool VersionFinal::remove(const int index)
bool VersionFinal::remove(const QString id) bool VersionFinal::remove(const QString id)
{ {
int i = 0; int i = 0;
for (auto file : versionFiles) for (auto patch : VersionPatches)
{ {
if (file->fileId == id) if (patch->getPatchID() == id)
{ {
return remove(i); return remove(i);
} }
@ -109,18 +107,18 @@ bool VersionFinal::remove(const QString id)
QString VersionFinal::versionFileId(const int index) const QString VersionFinal::versionFileId(const int index) const
{ {
if (index < 0 || index >= versionFiles.size()) if (index < 0 || index >= VersionPatches.size())
{ {
return QString(); return QString();
} }
return versionFiles.at(index)->fileId; return VersionPatches.at(index)->getPatchID();
} }
VersionFilePtr VersionFinal::versionPatch(const QString &id) VersionPatchPtr VersionFinal::versionPatch(const QString &id)
{ {
for (auto file : versionFiles) for (auto file : VersionPatches)
{ {
if (file->fileId == id) if (file->getPatchID() == id)
{ {
return file; return file;
} }
@ -128,6 +126,14 @@ VersionFilePtr VersionFinal::versionPatch(const QString &id)
return 0; return 0;
} }
VersionPatchPtr VersionFinal::versionPatch(int index)
{
if(index < 0 || index >= VersionPatches.size())
return 0;
return VersionPatches[index];
}
bool VersionFinal::hasJarMods() bool VersionFinal::hasJarMods()
{ {
return !jarMods.isEmpty(); return !jarMods.isEmpty();
@ -146,7 +152,7 @@ bool VersionFinal::removeFtbPack()
bool VersionFinal::isVanilla() bool VersionFinal::isVanilla()
{ {
QDir patches(PathCombine(m_instance->instanceRoot(), "patches/")); QDir patches(PathCombine(m_instance->instanceRoot(), "patches/"));
if(versionFiles.size() > 1) if(VersionPatches.size() > 1)
return false; return false;
if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json"))) if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json")))
return false; return false;
@ -156,22 +162,22 @@ bool VersionFinal::isVanilla()
bool VersionFinal::revertToVanilla() bool VersionFinal::revertToVanilla()
{ {
beginResetModel(); beginResetModel();
auto it = versionFiles.begin(); auto it = VersionPatches.begin();
while (it != versionFiles.end()) while (it != VersionPatches.end())
{ {
if ((*it)->fileId != "org.multimc.version.json") if ((*it)->isMoveable())
{ {
if(!preremove(*it)) if(!preremove(*it))
{ {
endResetModel(); endResetModel();
return false; return false;
} }
if(!QFile::remove((*it)->filename)) if(!QFile::remove((*it)->getPatchFilename()))
{ {
endResetModel(); endResetModel();
return false; return false;
} }
it = versionFiles.erase(it); it = VersionPatches.erase(it);
} }
else else
it++; it++;
@ -233,7 +239,7 @@ QVariant VersionFinal::data(const QModelIndex &index, int role) const
int row = index.row(); int row = index.row();
int column = index.column(); int column = index.column();
if (row < 0 || row >= versionFiles.size()) if (row < 0 || row >= VersionPatches.size())
return QVariant(); return QVariant();
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole)
@ -241,9 +247,9 @@ QVariant VersionFinal::data(const QModelIndex &index, int role) const
switch (column) switch (column)
{ {
case 0: case 0:
return versionFiles.at(row)->name; return VersionPatches.at(row)->getPatchName();
case 1: case 1:
return versionFiles.at(row)->version; return VersionPatches.at(row)->getPatchVersion();
default: default:
return QVariant(); return QVariant();
} }
@ -278,7 +284,7 @@ Qt::ItemFlags VersionFinal::flags(const QModelIndex &index) const
int VersionFinal::rowCount(const QModelIndex &parent) const int VersionFinal::rowCount(const QModelIndex &parent) const
{ {
return versionFiles.size(); return VersionPatches.size();
} }
int VersionFinal::columnCount(const QModelIndex &parent) const int VersionFinal::columnCount(const QModelIndex &parent) const
@ -288,13 +294,12 @@ int VersionFinal::columnCount(const QModelIndex &parent) const
QMap<QString, int> VersionFinal::getExistingOrder() const QMap<QString, int> VersionFinal::getExistingOrder() const
{ {
QMap<QString, int> order; QMap<QString, int> order;
// default // default
{ {
for (auto file : versionFiles) for (auto file : VersionPatches)
{ {
order.insert(file->fileId, file->order); order.insert(file->getPatchID(), file->getOrder());
} }
} }
// overriden // overriden
@ -314,39 +319,37 @@ QMap<QString, int> VersionFinal::getExistingOrder() const
void VersionFinal::move(const int index, const MoveDirection direction) void VersionFinal::move(const int index, const MoveDirection direction)
{ {
int theirIndex; int theirIndex;
int theirIndex_qt;
if (direction == MoveUp) if (direction == MoveUp)
{ {
theirIndex = index - 1; theirIndex_qt = theirIndex = index - 1;
} }
else else
{ {
theirIndex = index + 1; theirIndex = index + 1;
theirIndex_qt = index + 2;
} }
if (theirIndex < 0 || theirIndex >= versionFiles.size()) auto from = versionPatch(index);
{ auto to = versionPatch(theirIndex);
return;
} if (!from || !to || !from->isMoveable() || !from->isMoveable())
const QString ourId = versionFileId(index);
const QString theirId = versionFileId(theirIndex);
if (ourId.isNull() || ourId.startsWith("org.multimc.") ||
theirId.isNull() || theirId.startsWith("org.multimc."))
{ {
return; return;
} }
if(direction == MoveDown) if(direction == MoveDown)
{ {
beginMoveRows(QModelIndex(), index, index, QModelIndex(), theirIndex+1); beginMoveRows(QModelIndex(), index, index, QModelIndex(), theirIndex_qt);
} }
else else
{ {
beginMoveRows(QModelIndex(), index, index, QModelIndex(), theirIndex); beginMoveRows(QModelIndex(), index, index, QModelIndex(), theirIndex_qt);
} }
versionFiles.swap(index, theirIndex); VersionPatches.swap(index, theirIndex);
endMoveRows(); endMoveRows();
auto order = getExistingOrder(); auto order = getExistingOrder();
order[ourId] = theirIndex; order[from->getPatchID()] = theirIndex;
order[theirId] = index; order[to->getPatchID()] = index;
if (!VersionBuilder::writeOverrideOrders(order, m_instance)) if (!VersionBuilder::writeOverrideOrders(order, m_instance))
{ {
@ -375,14 +378,14 @@ void VersionFinal::reapply(const bool alreadyReseting)
auto existingOrders = getExistingOrder(); auto existingOrders = getExistingOrder();
QList<int> orders = existingOrders.values(); QList<int> orders = existingOrders.values();
std::sort(orders.begin(), orders.end()); std::sort(orders.begin(), orders.end());
QList<VersionFilePtr> newVersionFiles; QList<VersionPatchPtr> newVersionFiles;
for (auto order : orders) for (auto order : orders)
{ {
auto file = versionPatch(existingOrders.key(order)); auto file = versionPatch(existingOrders.key(order));
newVersionFiles.append(file); newVersionFiles.append(file);
file->applyTo(this); file->applyTo(this);
} }
versionFiles.swap(newVersionFiles); VersionPatches.swap(newVersionFiles);
finalize(); finalize();
if (!alreadyReseting) if (!alreadyReseting)
{ {

View File

@ -83,16 +83,21 @@ public:
static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj); static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj);
private: private:
bool preremove(VersionFilePtr); bool preremove(VersionPatchPtr patch);
// data members // data members
public: public:
/// the ID - determines which jar to use! ACTUALLY IMPORTANT! /// the ID - determines which jar to use! ACTUALLY IMPORTANT!
QString id; QString id;
/// Last updated time - as a string
QString time; /// the time this version was actually released by Mojang, as string and as QDateTime
/// Release time - as a string QString m_releaseTimeString;
QString versionReleaseTime; QDateTime m_releaseTime;
/// the time this version was last updated by Mojang, as string and as QDateTime
QString m_updateTimeString;
QDateTime m_updateTime;
/// Release type - "release" or "snapshot" /// Release type - "release" or "snapshot"
QString type; QString type;
/// Assets type - "legacy" or a version ID /// Assets type - "legacy" or a version ID
@ -164,8 +169,9 @@ public:
*/ */
// QList<Rule> rules; // QList<Rule> rules;
QList<VersionFilePtr> versionFiles; QList<VersionPatchPtr> VersionPatches;
VersionFilePtr versionPatch(const QString &id); VersionPatchPtr versionPatch(const QString &id);
VersionPatchPtr versionPatch(int index);
private: private:
OneSixInstance *m_instance; OneSixInstance *m_instance;

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <QList>
#include "JarMod.h"
class VersionFinal; class VersionFinal;
class VersionPatch class VersionPatch
@ -8,8 +10,22 @@ class VersionPatch
public: public:
virtual ~VersionPatch(){}; virtual ~VersionPatch(){};
virtual void applyTo(VersionFinal *version) = 0; virtual void applyTo(VersionFinal *version) = 0;
virtual bool isVanilla() = 0; virtual bool isVanilla() = 0;
virtual bool hasJarMods() = 0; virtual bool hasJarMods() = 0;
virtual QList<JarmodPtr> getJarMods() = 0;
virtual bool isMoveable()
{
return getOrder() >= 0;
}
virtual void setOrder(int order) = 0;
virtual int getOrder() = 0;
virtual QString getPatchID() = 0;
virtual QString getPatchName() = 0;
virtual QString getPatchVersion() = 0;
virtual QString getPatchFilename() = 0;
}; };
typedef std::shared_ptr<VersionPatch> VersionPatchPtr; typedef std::shared_ptr<VersionPatch> VersionPatchPtr;

View File

@ -495,6 +495,7 @@
"releaseTime": "2010-07-13T00:00:00+02:00", "releaseTime": "2010-07-13T00:00:00+02:00",
"type": "old_alpha", "type": "old_alpha",
"processArguments": "legacy", "processArguments": "legacy",
"mainClass": "y",
"+traits": ["legacyLaunch"] "+traits": ["legacyLaunch"]
}, },
{ {
@ -502,6 +503,7 @@
"releaseTime": "2010-07-09T00:00:00+02:00", "releaseTime": "2010-07-09T00:00:00+02:00",
"type": "old_alpha", "type": "old_alpha",
"processArguments": "legacy", "processArguments": "legacy",
"mainClass": "ax",
"+traits": ["legacyLaunch"] "+traits": ["legacyLaunch"]
}, },
{ {
@ -509,6 +511,8 @@
"releaseTime": "2010-06-16T00:00:00+02:00", "releaseTime": "2010-06-16T00:00:00+02:00",
"type": "old_alpha", "type": "old_alpha",
"processArguments": "legacy", "processArguments": "legacy",
"mainClass": "net.minecraft.client.d",
"appletClass": "net.minecraft.client.MinecraftApplet",
"+traits": ["legacyLaunch"] "+traits": ["legacyLaunch"]
}, },
{ {
@ -516,6 +520,8 @@
"releaseTime": "2009-12-22T00:00:00+02:00", "releaseTime": "2009-12-22T00:00:00+02:00",
"type": "old_alpha", "type": "old_alpha",
"processArguments": "legacy", "processArguments": "legacy",
"mainClass": "com.mojang.minecraft.l",
"appletClass": "com.mojang.minecraft.MinecraftApplet",
"+traits": ["legacyLaunch"] "+traits": ["legacyLaunch"]
}, },
{ {
@ -523,6 +529,8 @@
"releaseTime": "2009-05-22T00:00:00+02:00", "releaseTime": "2009-05-22T00:00:00+02:00",
"type": "old_alpha", "type": "old_alpha",
"processArguments": "legacy", "processArguments": "legacy",
"mainClass": "com.mojang.minecraft.c",
"appletClass": "com.mojang.minecraft.MinecraftApplet",
"+traits": ["legacyLaunch"] "+traits": ["legacyLaunch"]
}, },
{ {
@ -530,6 +538,8 @@
"releaseTime": "2009-05-31T00:00:00+02:00", "releaseTime": "2009-05-31T00:00:00+02:00",
"type": "old_alpha", "type": "old_alpha",
"processArguments": "legacy", "processArguments": "legacy",
"mainClass": "com.mojang.minecraft.Minecraft",
"appletClass": "com.mojang.minecraft.MinecraftApplet",
"+traits": ["legacyLaunch"] "+traits": ["legacyLaunch"]
}, },
{ {
@ -537,6 +547,8 @@
"releaseTime": "2009-05-17T00:00:00+02:00", "releaseTime": "2009-05-17T00:00:00+02:00",
"type": "old_alpha", "type": "old_alpha",
"processArguments": "legacy", "processArguments": "legacy",
"mainClass": "com.mojang.minecraft.Minecraft",
"appletClass": "com.mojang.minecraft.MinecraftApplet",
"+traits": ["legacyLaunch"] "+traits": ["legacyLaunch"]
}, },
{ {