GH-2026 implement changes necessary to support 1.13 snapshots
This commit is contained in:
parent
17c8f31a09
commit
85ae710d40
@ -197,7 +197,7 @@ bool BaseInstance::canLaunch() const
|
||||
return (!hasVersionBroken() && !isRunning());
|
||||
}
|
||||
|
||||
bool BaseInstance::reload()
|
||||
bool BaseInstance::reloadSettings()
|
||||
{
|
||||
return m_settings->reload();
|
||||
}
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "MessageLevel.h"
|
||||
#include "pathmatcher/IPathMatcher.h"
|
||||
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
class QDir;
|
||||
@ -148,7 +150,7 @@ public:
|
||||
virtual SettingsObjectPtr settings() const;
|
||||
|
||||
/// returns a valid update task
|
||||
virtual shared_qobject_ptr<Task> createUpdateTask() = 0;
|
||||
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
|
||||
|
||||
/// returns a valid launcher (task container)
|
||||
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
|
||||
@ -224,7 +226,7 @@ public:
|
||||
virtual bool canEdit() const = 0;
|
||||
virtual bool canExport() const = 0;
|
||||
|
||||
virtual bool reload();
|
||||
bool reloadSettings();
|
||||
|
||||
/**
|
||||
* 'print' a verbose desription of the instance into a QStringList
|
||||
|
@ -239,8 +239,14 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/MinecraftInstance.h
|
||||
minecraft/LaunchProfile.cpp
|
||||
minecraft/LaunchProfile.h
|
||||
minecraft/Component.cpp
|
||||
minecraft/Component.h
|
||||
minecraft/ComponentList.cpp
|
||||
minecraft/ComponentList.h
|
||||
minecraft/ComponentUpdateTask.cpp
|
||||
minecraft/ComponentUpdateTask.h
|
||||
minecraft/MinecraftLoadAndCheck.h
|
||||
minecraft/MinecraftLoadAndCheck.cpp
|
||||
minecraft/MinecraftUpdate.h
|
||||
minecraft/MinecraftUpdate.cpp
|
||||
minecraft/MojangVersionFormat.cpp
|
||||
@ -260,8 +266,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/MojangDownloadInfo.h
|
||||
minecraft/VersionFile.cpp
|
||||
minecraft/VersionFile.h
|
||||
minecraft/ProfilePatch.cpp
|
||||
minecraft/ProfilePatch.h
|
||||
minecraft/VersionFilterData.h
|
||||
minecraft/VersionFilterData.cpp
|
||||
minecraft/Mod.h
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
//FIXME: remove this
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/ComponentList.h"
|
||||
|
||||
InstanceCreationTask::InstanceCreationTask(SettingsObjectPtr settings, const QString & stagingPath, BaseVersionPtr version,
|
||||
const QString& instName, const QString& instIcon, const QString& instGroup)
|
||||
@ -25,11 +26,13 @@ void InstanceCreationTask::executeTask()
|
||||
instanceSettings->suspendSave();
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
auto inst = new MinecraftInstance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
inst->setComponentVersion("net.minecraft", m_version->descriptor());
|
||||
inst->setName(m_instName);
|
||||
inst->setIconKey(m_instIcon);
|
||||
inst->init();
|
||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
auto components = inst.getComponentList();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
||||
inst.setName(m_instName);
|
||||
inst.setIconKey(m_instIcon);
|
||||
inst.init();
|
||||
instanceSettings->resumeSave();
|
||||
}
|
||||
emitSucceeded();
|
||||
|
@ -242,7 +242,9 @@ void InstanceImportTask::processFlame()
|
||||
mcVersion.remove(QRegExp("[.]+$"));
|
||||
qWarning() << "Mysterious trailing dots removed from Minecraft version while importing pack.";
|
||||
}
|
||||
instance.setComponentVersion("net.minecraft", mcVersion);
|
||||
auto components = instance.getComponentList();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
||||
if(!forgeVersion.isEmpty())
|
||||
{
|
||||
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
||||
@ -257,7 +259,7 @@ void InstanceImportTask::processFlame()
|
||||
qWarning() << "Could not map recommended forge version for" << mcVersion;
|
||||
}
|
||||
}
|
||||
instance.setComponentVersion("net.minecraftforge", forgeVersion);
|
||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
||||
}
|
||||
if (m_instIcon != "default")
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
virtual shared_qobject_ptr< Task > createUpdateTask() override
|
||||
virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
enum class ProblemSeverity
|
||||
{
|
||||
None,
|
||||
@ -13,7 +15,7 @@ struct PatchProblem
|
||||
QString m_description;
|
||||
};
|
||||
|
||||
class ProblemProvider
|
||||
class MULTIMC_LOGIC_EXPORT ProblemProvider
|
||||
{
|
||||
public:
|
||||
virtual ~ProblemProvider() {};
|
||||
@ -21,7 +23,7 @@ public:
|
||||
virtual ProblemSeverity getProblemSeverity() const = 0;
|
||||
};
|
||||
|
||||
class ProblemContainer : public ProblemProvider
|
||||
class MULTIMC_LOGIC_EXPORT ProblemContainer : public ProblemProvider
|
||||
{
|
||||
public:
|
||||
const QList<PatchProblem> getProblems() const override
|
||||
|
@ -23,7 +23,7 @@ void Update::executeTask()
|
||||
emitFailed(tr("Task aborted."));
|
||||
return;
|
||||
}
|
||||
m_updateTask.reset(m_parent->instance()->createUpdateTask());
|
||||
m_updateTask.reset(m_parent->instance()->createUpdateTask(m_mode));
|
||||
if(m_updateTask)
|
||||
{
|
||||
connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished()));
|
||||
|
@ -19,13 +19,14 @@
|
||||
#include <QObjectPtr.h>
|
||||
#include <LoggedProcess.h>
|
||||
#include <java/JavaChecker.h>
|
||||
#include <net/Mode.h>
|
||||
|
||||
// FIXME: stupid. should be defined by the instance type? or even completely abstracted away...
|
||||
class Update: public LaunchStep
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Update(LaunchTask *parent):LaunchStep(parent) {};
|
||||
explicit Update(LaunchTask *parent, Net::Mode mode):LaunchStep(parent), m_mode(mode) {};
|
||||
virtual ~Update() {};
|
||||
|
||||
void executeTask() override;
|
||||
@ -40,4 +41,5 @@ private slots:
|
||||
private:
|
||||
shared_qobject_ptr<Task> m_updateTask;
|
||||
bool m_aborted = false;
|
||||
Net::Mode m_mode = Net::Mode::Offline;
|
||||
};
|
||||
|
@ -99,7 +99,7 @@ bool Meta::BaseEntity::loadLocalFile()
|
||||
}
|
||||
}
|
||||
|
||||
void Meta::BaseEntity::load()
|
||||
void Meta::BaseEntity::load(Net::Mode loadType)
|
||||
{
|
||||
// load local file if nothing is loaded yet
|
||||
if(!isLoaded())
|
||||
@ -110,7 +110,7 @@ void Meta::BaseEntity::load()
|
||||
}
|
||||
}
|
||||
// if we need remote update, run the update task
|
||||
if(!shouldStartRemoteUpdate())
|
||||
if(loadType == Net::Mode::Offline || !shouldStartRemoteUpdate())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
class Task;
|
||||
namespace Meta
|
||||
@ -54,7 +55,7 @@ public:
|
||||
bool isLoaded() const;
|
||||
bool shouldStartRemoteUpdate() const;
|
||||
|
||||
void load();
|
||||
void load(Net::Mode loadType);
|
||||
shared_qobject_ptr<Task> getCurrentTask();
|
||||
|
||||
protected: /* methods */
|
||||
|
@ -51,18 +51,11 @@ static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)
|
||||
version->setType(ensureString(obj, "type", QString()));
|
||||
version->setParentUid(ensureString(obj, "parentUid", QString()));
|
||||
version->setRecommended(ensureBoolean(obj, QString("recommended"), false));
|
||||
if(obj.contains("requires"))
|
||||
{
|
||||
QHash<QString, QString> requires;
|
||||
auto reqobj = requireObject(obj, "requires");
|
||||
auto iter = reqobj.begin();
|
||||
while(iter != reqobj.end())
|
||||
{
|
||||
requires[iter.key()] = requireString(iter.value());
|
||||
iter++;
|
||||
}
|
||||
version->setRequires(requires);
|
||||
}
|
||||
version->setVolatile(ensureBoolean(obj, QString("volatile"), false));
|
||||
RequireSet requires, conflicts;
|
||||
parseRequires(obj, &requires, "requires");
|
||||
parseRequires(obj, &conflicts, "conflicts");
|
||||
version->setRequires(requires, conflicts);
|
||||
return version;
|
||||
}
|
||||
|
||||
@ -145,4 +138,53 @@ void parseVersion(const QJsonObject &obj, Version *ptr)
|
||||
throw ParseException(QObject::tr("Unknown formatVersion: %1").arg(version));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
[
|
||||
{"uid":"foo", "equals":"version"}
|
||||
]
|
||||
*/
|
||||
void parseRequires(const QJsonObject& obj, RequireSet* ptr, const char * keyName)
|
||||
{
|
||||
if(obj.contains(keyName))
|
||||
{
|
||||
QSet<QString> requires;
|
||||
auto reqArray = requireArray(obj, keyName);
|
||||
auto iter = reqArray.begin();
|
||||
while(iter != reqArray.end())
|
||||
{
|
||||
auto reqObject = requireObject(*iter);
|
||||
auto uid = requireString(reqObject, "uid");
|
||||
auto equals = ensureString(reqObject, "equals", QString());
|
||||
auto suggests = ensureString(reqObject, "suggests", QString());
|
||||
ptr->insert({uid, equals, suggests});
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void serializeRequires(QJsonObject& obj, RequireSet* ptr, const char * keyName)
|
||||
{
|
||||
if(!ptr || ptr->empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
QJsonArray arrOut;
|
||||
for(auto &iter: *ptr)
|
||||
{
|
||||
QJsonObject reqOut;
|
||||
reqOut.insert("uid", iter.uid);
|
||||
if(!iter.equalsVersion.isEmpty())
|
||||
{
|
||||
reqOut.insert("equals", iter.equalsVersion);
|
||||
}
|
||||
if(!iter.suggests.isEmpty())
|
||||
{
|
||||
reqOut.insert("suggests", iter.suggests);
|
||||
}
|
||||
arrOut.append(reqOut);
|
||||
}
|
||||
obj.insert(keyName, arrOut);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "Exception.h"
|
||||
#include "meta/BaseEntity.h"
|
||||
#include <set>
|
||||
|
||||
namespace Meta
|
||||
{
|
||||
@ -32,9 +33,41 @@ class ParseException : public Exception
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
struct Require
|
||||
{
|
||||
bool operator==(const Require & rhs) const
|
||||
{
|
||||
return uid == rhs.uid;
|
||||
}
|
||||
bool operator<(const Require & rhs) const
|
||||
{
|
||||
return uid < rhs.uid;
|
||||
}
|
||||
bool deepEquals(const Require & rhs) const
|
||||
{
|
||||
return uid == rhs.uid
|
||||
&& equalsVersion == rhs.equalsVersion
|
||||
&& suggests == rhs.suggests;
|
||||
}
|
||||
QString uid;
|
||||
QString equalsVersion;
|
||||
QString suggests;
|
||||
};
|
||||
|
||||
inline Q_DECL_PURE_FUNCTION uint qHash(const Require &key, uint seed = 0) Q_DECL_NOTHROW
|
||||
{
|
||||
return qHash(key.uid, seed);
|
||||
}
|
||||
|
||||
using RequireSet = std::set<Require>;
|
||||
|
||||
void parseIndex(const QJsonObject &obj, Index *ptr);
|
||||
void parseVersion(const QJsonObject &obj, Version *ptr);
|
||||
void parseVersionList(const QJsonObject &obj, VersionList *ptr);
|
||||
|
||||
// FIXME: this has a different shape than the others...FIX IT!?
|
||||
void parseRequires(const QJsonObject &obj, RequireSet * ptr, const char * keyName = "requires");
|
||||
void serializeRequires(QJsonObject & objOut, RequireSet* ptr, const char * keyName = "requires");
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(std::set<Meta::Require>);
|
@ -74,12 +74,20 @@ void Meta::Version::merge(const std::shared_ptr<BaseEntity> &other)
|
||||
}
|
||||
if (m_requires != version->m_requires)
|
||||
{
|
||||
setRequires(version->m_requires);
|
||||
m_requires = version->m_requires;
|
||||
}
|
||||
if (m_conflicts != version->m_conflicts)
|
||||
{
|
||||
m_conflicts = version->m_conflicts;
|
||||
}
|
||||
if (m_parentUid != version->m_parentUid)
|
||||
{
|
||||
setParentUid(version->m_parentUid);
|
||||
}
|
||||
if(m_volatile != version->m_volatile)
|
||||
{
|
||||
setVolatile(version->m_volatile);
|
||||
}
|
||||
if(version->m_data)
|
||||
{
|
||||
setData(version->m_data);
|
||||
@ -109,12 +117,19 @@ void Meta::Version::setTime(const qint64 time)
|
||||
emit timeChanged();
|
||||
}
|
||||
|
||||
void Meta::Version::setRequires(const QHash<QString, QString> &requires)
|
||||
void Meta::Version::setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts)
|
||||
{
|
||||
m_requires = requires;
|
||||
m_conflicts = conflicts;
|
||||
emit requiresChanged();
|
||||
}
|
||||
|
||||
void Meta::Version::setVolatile(bool volatile_)
|
||||
{
|
||||
m_volatile = volatile_;
|
||||
}
|
||||
|
||||
|
||||
void Meta::Version::setData(const VersionFilePtr &data)
|
||||
{
|
||||
m_data = data;
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
#include "JsonFormat.h"
|
||||
|
||||
namespace Meta
|
||||
{
|
||||
using VersionPtr = std::shared_ptr<class Version>;
|
||||
@ -65,7 +67,7 @@ public: /* con/des */
|
||||
{
|
||||
return m_time;
|
||||
}
|
||||
const QHash<QString, QString> &requires() const
|
||||
const Meta::RequireSet &requires() const
|
||||
{
|
||||
return m_requires;
|
||||
}
|
||||
@ -77,6 +79,10 @@ public: /* con/des */
|
||||
{
|
||||
return m_recommended;
|
||||
}
|
||||
bool isLoaded() const
|
||||
{
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
void merge(const std::shared_ptr<BaseEntity> &other) override;
|
||||
void parse(const QJsonObject &obj) override;
|
||||
@ -87,7 +93,8 @@ public: // for usage by format parsers only
|
||||
void setParentUid(const QString &parentUid);
|
||||
void setType(const QString &type);
|
||||
void setTime(const qint64 time);
|
||||
void setRequires(const QHash<QString, QString> &requires);
|
||||
void setRequires(const Meta::RequireSet &requires, const Meta::RequireSet &conflicts);
|
||||
void setVolatile(bool volatile_);
|
||||
void setRecommended(bool recommended);
|
||||
void setProvidesRecommendations();
|
||||
void setData(const VersionFilePtr &data);
|
||||
@ -106,7 +113,9 @@ private:
|
||||
QString m_version;
|
||||
QString m_type;
|
||||
qint64 m_time = 0;
|
||||
QHash<QString, QString> m_requires;
|
||||
Meta::RequireSet m_requires;
|
||||
Meta::RequireSet m_conflicts;
|
||||
bool m_volatile = false;
|
||||
VersionFilePtr m_data;
|
||||
};
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ VersionList::VersionList(const QString &uid, QObject *parent)
|
||||
|
||||
shared_qobject_ptr<Task> VersionList::getLoadTask()
|
||||
{
|
||||
load();
|
||||
load(Net::Mode::Online);
|
||||
return getCurrentTask();
|
||||
}
|
||||
|
||||
@ -81,10 +81,13 @@ QVariant VersionList::data(const QModelIndex &index, int role) const
|
||||
return QVariant();
|
||||
}
|
||||
auto & reqs = version->requires();
|
||||
auto iter = reqs.find(parentUid);
|
||||
auto iter = std::find_if(reqs.begin(), reqs.end(), [&parentUid](const Require & req)
|
||||
{
|
||||
return req.uid == parentUid;
|
||||
});
|
||||
if (iter != reqs.end())
|
||||
{
|
||||
return iter.value();
|
||||
return (*iter).equalsVersion;
|
||||
}
|
||||
}
|
||||
case TypeRole: return version->type();
|
||||
@ -159,6 +162,7 @@ void VersionList::setVersions(const QVector<VersionPtr> &versions)
|
||||
setupAddedVersion(i, m_versions.at(i));
|
||||
}
|
||||
|
||||
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
|
||||
auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; });
|
||||
m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
|
||||
endResetModel();
|
||||
@ -169,6 +173,22 @@ void VersionList::parse(const QJsonObject& obj)
|
||||
parseVersionList(obj, this);
|
||||
}
|
||||
|
||||
// FIXME: this is dumb, we have 'recommended' as part of the metadata already...
|
||||
static const Meta::VersionPtr &getBetterVersion(const Meta::VersionPtr &a, const Meta::VersionPtr &b)
|
||||
{
|
||||
if(!a)
|
||||
return b;
|
||||
if(!b)
|
||||
return a;
|
||||
if(a->type() == b->type())
|
||||
{
|
||||
// newer of same type wins
|
||||
return (a->rawTime() > b->rawTime() ? a : b);
|
||||
}
|
||||
// 'release' type wins
|
||||
return (a->type() == "release" ? a : b);
|
||||
}
|
||||
|
||||
void VersionList::merge(const BaseEntity::Ptr &other)
|
||||
{
|
||||
const VersionListPtr list = std::dynamic_pointer_cast<VersionList>(other);
|
||||
@ -199,10 +219,7 @@ void VersionList::merge(const BaseEntity::Ptr &other)
|
||||
// connect it.
|
||||
setupAddedVersion(m_versions.size(), version);
|
||||
m_versions.append(version);
|
||||
if (!m_recommended || (version->type() == "release" && version->rawTime() > m_recommended->rawTime()))
|
||||
{
|
||||
m_recommended = version;
|
||||
}
|
||||
m_recommended = getBetterVersion(m_recommended, version);
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
408
api/logic/minecraft/Component.cpp
Normal file
408
api/logic/minecraft/Component.cpp
Normal file
@ -0,0 +1,408 @@
|
||||
#include <meta/VersionList.h>
|
||||
#include <meta/Index.h>
|
||||
#include <Env.h>
|
||||
#include "Component.h"
|
||||
|
||||
#include "meta/Version.h"
|
||||
#include "VersionFile.h"
|
||||
#include "minecraft/ComponentList.h"
|
||||
#include <FileSystem.h>
|
||||
#include <QSaveFile>
|
||||
#include "OneSixVersionFormat.h"
|
||||
#include <assert.h>
|
||||
|
||||
Component::Component(ComponentList * parent, const QString& uid)
|
||||
{
|
||||
assert(parent);
|
||||
m_parent = parent;
|
||||
|
||||
m_uid = uid;
|
||||
}
|
||||
|
||||
Component::Component(ComponentList * parent, std::shared_ptr<Meta::Version> version)
|
||||
{
|
||||
assert(parent);
|
||||
m_parent = parent;
|
||||
|
||||
m_metaVersion = version;
|
||||
m_uid = version->uid();
|
||||
m_version = m_cachedVersion = version->version();
|
||||
m_cachedName = version->name();
|
||||
m_loaded = version->isLoaded();
|
||||
}
|
||||
|
||||
Component::Component(ComponentList * parent, const QString& uid, std::shared_ptr<VersionFile> file)
|
||||
{
|
||||
assert(parent);
|
||||
m_parent = parent;
|
||||
|
||||
m_file = file;
|
||||
m_uid = uid;
|
||||
m_cachedVersion = m_file->version;
|
||||
m_cachedName = m_file->name;
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Meta::Version> Component::getMeta()
|
||||
{
|
||||
return m_metaVersion;
|
||||
}
|
||||
|
||||
void Component::applyTo(LaunchProfile* profile)
|
||||
{
|
||||
auto vfile = getVersionFile();
|
||||
if(vfile)
|
||||
{
|
||||
vfile->applyTo(profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
profile->applyProblemSeverity(getProblemSeverity());
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<class VersionFile> Component::getVersionFile() const
|
||||
{
|
||||
if(m_metaVersion)
|
||||
{
|
||||
if(!m_metaVersion->isLoaded())
|
||||
{
|
||||
m_metaVersion->load(Net::Mode::Online);
|
||||
}
|
||||
return m_metaVersion->data();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<class Meta::VersionList> Component::getVersionList() const
|
||||
{
|
||||
// FIXME: what if the metadata index isn't loaded yet?
|
||||
if(ENV.metadataIndex()->hasUid(m_uid))
|
||||
{
|
||||
return ENV.metadataIndex()->get(m_uid);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Component::getOrder()
|
||||
{
|
||||
if(m_orderOverride)
|
||||
return m_order;
|
||||
|
||||
auto vfile = getVersionFile();
|
||||
if(vfile)
|
||||
{
|
||||
return vfile->order;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void Component::setOrder(int order)
|
||||
{
|
||||
m_orderOverride = true;
|
||||
m_order = order;
|
||||
}
|
||||
QString Component::getID()
|
||||
{
|
||||
return m_uid;
|
||||
}
|
||||
QString Component::getName()
|
||||
{
|
||||
if (!m_cachedName.isEmpty())
|
||||
return m_cachedName;
|
||||
return m_uid;
|
||||
}
|
||||
QString Component::getVersion()
|
||||
{
|
||||
return m_cachedVersion;
|
||||
}
|
||||
QString Component::getFilename()
|
||||
{
|
||||
return m_parent->patchFilePathForUid(m_uid);
|
||||
}
|
||||
QDateTime Component::getReleaseDateTime()
|
||||
{
|
||||
if(m_metaVersion)
|
||||
{
|
||||
return m_metaVersion->time();
|
||||
}
|
||||
auto vfile = getVersionFile();
|
||||
if(vfile)
|
||||
{
|
||||
return vfile->releaseTime;
|
||||
}
|
||||
// FIXME: fake
|
||||
return QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
bool Component::isCustom()
|
||||
{
|
||||
return m_file != nullptr;
|
||||
};
|
||||
|
||||
bool Component::isCustomizable()
|
||||
{
|
||||
if(m_metaVersion)
|
||||
{
|
||||
if(getVersionFile())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Component::isRemovable()
|
||||
{
|
||||
return !m_important;
|
||||
}
|
||||
bool Component::isRevertible()
|
||||
{
|
||||
if (isCustom())
|
||||
{
|
||||
if(ENV.metadataIndex()->hasUid(m_uid))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Component::isMoveable()
|
||||
{
|
||||
// HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
|
||||
return true;
|
||||
}
|
||||
bool Component::isVersionChangeable()
|
||||
{
|
||||
auto list = getVersionList();
|
||||
if(list)
|
||||
{
|
||||
if(!list->isLoaded())
|
||||
{
|
||||
list->load(Net::Mode::Online);
|
||||
}
|
||||
return list->count() != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Component::setImportant(bool state)
|
||||
{
|
||||
if(m_important != state)
|
||||
{
|
||||
m_important = state;
|
||||
emit dataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
ProblemSeverity Component::getProblemSeverity() const
|
||||
{
|
||||
auto file = getVersionFile();
|
||||
if(file)
|
||||
{
|
||||
return file->getProblemSeverity();
|
||||
}
|
||||
return ProblemSeverity::Error;
|
||||
}
|
||||
|
||||
const QList<PatchProblem> Component::getProblems() const
|
||||
{
|
||||
auto file = getVersionFile();
|
||||
if(file)
|
||||
{
|
||||
return file->getProblems();
|
||||
}
|
||||
return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
|
||||
}
|
||||
|
||||
void Component::setVersion(const QString& version)
|
||||
{
|
||||
if(version == m_version)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_version = version;
|
||||
if(m_loaded)
|
||||
{
|
||||
// we are loaded and potentially have state to invalidate
|
||||
if(m_file)
|
||||
{
|
||||
// we have a file... explicit version has been changed and there is nothing else to do.
|
||||
}
|
||||
else
|
||||
{
|
||||
// we don't have a file, therefore we are loaded with metadata
|
||||
m_cachedVersion = version;
|
||||
// see if the meta version is loaded
|
||||
auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
|
||||
if(metaVersion->isLoaded())
|
||||
{
|
||||
// if yes, we can continue with that.
|
||||
m_metaVersion = metaVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not, we need loading
|
||||
m_metaVersion.reset();
|
||||
m_loaded = false;
|
||||
}
|
||||
updateCachedData();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not loaded... assume it will be sorted out later by the update task
|
||||
}
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
bool Component::customize()
|
||||
{
|
||||
if(isCustom())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto filename = getFilename();
|
||||
if(!FS::ensureFilePathExists(filename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// FIXME: get rid of this try-catch.
|
||||
try
|
||||
{
|
||||
QSaveFile jsonFile(filename);
|
||||
if(!jsonFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto vfile = getVersionFile();
|
||||
if(!vfile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto document = OneSixVersionFormat::versionFileToJson(vfile);
|
||||
jsonFile.write(document.toJson());
|
||||
if(!jsonFile.commit())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_file = vfile;
|
||||
m_metaVersion.reset();
|
||||
emit dataChanged();
|
||||
}
|
||||
catch (Exception &error)
|
||||
{
|
||||
qWarning() << "Version could not be loaded:" << error.cause();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Component::revert()
|
||||
{
|
||||
if(!isCustom())
|
||||
{
|
||||
// already not custom
|
||||
return true;
|
||||
}
|
||||
auto filename = getFilename();
|
||||
bool result = true;
|
||||
// just kill the file and reload
|
||||
if(QFile::exists(filename))
|
||||
{
|
||||
result = QFile::remove(filename);
|
||||
}
|
||||
if(result)
|
||||
{
|
||||
// file gone...
|
||||
m_file.reset();
|
||||
|
||||
// check local cache for metadata...
|
||||
auto version = ENV.metadataIndex()->get(m_uid, m_version);
|
||||
if(version->isLoaded())
|
||||
{
|
||||
m_metaVersion = version;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_metaVersion.reset();
|
||||
m_loaded = false;
|
||||
}
|
||||
emit dataChanged();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* deep inspecting compare for requirement sets
|
||||
* By default, only uids are compared for set operations.
|
||||
* This compares all fields of the Require structs in the sets.
|
||||
*/
|
||||
static bool deepCompare(const std::set<Meta::Require> & a, const std::set<Meta::Require> & b)
|
||||
{
|
||||
// NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
|
||||
if(a.size() != b.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for(const auto & reqA :a)
|
||||
{
|
||||
const auto &iter2 = b.find(reqA);
|
||||
if(iter2 == b.cend())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto & reqB = *iter2;
|
||||
if(!reqA.deepEquals(reqB))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Component::updateCachedData()
|
||||
{
|
||||
auto file = getVersionFile();
|
||||
if(file)
|
||||
{
|
||||
bool changed = false;
|
||||
if(m_cachedName != file->name)
|
||||
{
|
||||
m_cachedName = file->name;
|
||||
changed = true;
|
||||
}
|
||||
if(m_cachedVersion != file->version)
|
||||
{
|
||||
m_cachedVersion = file->version;
|
||||
changed = true;
|
||||
}
|
||||
if(m_cachedVolatile != file->m_volatile)
|
||||
{
|
||||
m_cachedVolatile = file->m_volatile;
|
||||
changed = true;
|
||||
}
|
||||
if(!deepCompare(m_cachedRequires, file->requires))
|
||||
{
|
||||
m_cachedRequires = file->requires;
|
||||
changed = true;
|
||||
}
|
||||
if(!deepCompare(m_cachedConflicts, file->conflicts))
|
||||
{
|
||||
m_cachedConflicts = file->conflicts;
|
||||
changed = true;
|
||||
}
|
||||
if(changed)
|
||||
{
|
||||
emit dataChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// in case we removed all the metadata
|
||||
m_cachedRequires.clear();
|
||||
m_cachedConflicts.clear();
|
||||
emit dataChanged();
|
||||
}
|
||||
}
|
104
api/logic/minecraft/Component.h
Normal file
104
api/logic/minecraft/Component.h
Normal file
@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QList>
|
||||
#include <QJsonDocument>
|
||||
#include <QDateTime>
|
||||
#include "meta/JsonFormat.h"
|
||||
#include "ProblemProvider.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
class ComponentList;
|
||||
class LaunchProfile;
|
||||
namespace Meta
|
||||
{
|
||||
class Version;
|
||||
class VersionList;
|
||||
}
|
||||
class VersionFile;
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT Component : public QObject, public ProblemProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Component(ComponentList * parent, const QString &uid);
|
||||
|
||||
// DEPRECATED: remove these constructors?
|
||||
Component(ComponentList * parent, std::shared_ptr<Meta::Version> version);
|
||||
Component(ComponentList * parent, const QString & uid, std::shared_ptr<VersionFile> file);
|
||||
|
||||
virtual ~Component(){};
|
||||
void applyTo(LaunchProfile *profile);
|
||||
|
||||
bool isMoveable();
|
||||
bool isCustomizable();
|
||||
bool isRevertible();
|
||||
bool isRemovable();
|
||||
bool isCustom();
|
||||
bool isVersionChangeable();
|
||||
|
||||
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
|
||||
void setOrder(int order);
|
||||
int getOrder();
|
||||
|
||||
QString getID();
|
||||
QString getName();
|
||||
QString getVersion();
|
||||
std::shared_ptr<Meta::Version> getMeta();
|
||||
QDateTime getReleaseDateTime();
|
||||
|
||||
QString getFilename();
|
||||
|
||||
std::shared_ptr<class VersionFile> getVersionFile() const;
|
||||
std::shared_ptr<class Meta::VersionList> getVersionList() const;
|
||||
|
||||
void setImportant (bool state);
|
||||
|
||||
const QList<PatchProblem> getProblems() const override;
|
||||
ProblemSeverity getProblemSeverity() const override;
|
||||
|
||||
void setVersion(const QString & version);
|
||||
bool customize();
|
||||
bool revert();
|
||||
|
||||
void updateCachedData();
|
||||
|
||||
signals:
|
||||
void dataChanged();
|
||||
|
||||
public: /* data */
|
||||
ComponentList * m_parent;
|
||||
|
||||
// BEGIN: persistent component list properties
|
||||
/// ID of the component
|
||||
QString m_uid;
|
||||
/// version of the component - when there's a custom json override, this is also the version the component reverts to
|
||||
QString m_version;
|
||||
/// if true, this has been added automatically to satisfy dependencies and may be automatically removed
|
||||
bool m_dependencyOnly = false;
|
||||
/// if true, the component is either the main component of the instance, or otherwise important and cannot be removed.
|
||||
bool m_important = false;
|
||||
|
||||
/// cached name for display purposes, taken from the version file (meta or local override)
|
||||
QString m_cachedName;
|
||||
/// cached version for display AND other purposes, taken from the version file (meta or local override)
|
||||
QString m_cachedVersion;
|
||||
/// cached set of requirements, taken from the version file (meta or local override)
|
||||
Meta::RequireSet m_cachedRequires;
|
||||
Meta::RequireSet m_cachedConflicts;
|
||||
/// if true, the component is volatile and may be automatically removed when no longer needed
|
||||
bool m_cachedVolatile = false;
|
||||
// END: persistent component list properties
|
||||
|
||||
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
|
||||
bool m_orderOverride = false;
|
||||
int m_order = 0;
|
||||
|
||||
// load state
|
||||
std::shared_ptr<Meta::Version> m_metaVersion;
|
||||
std::shared_ptr<VersionFile> m_file;
|
||||
bool m_loaded = false;
|
||||
};
|
||||
|
||||
typedef shared_qobject_ptr<Component> ComponentPtr;
|
File diff suppressed because it is too large
Load Diff
@ -23,19 +23,21 @@
|
||||
|
||||
#include "Library.h"
|
||||
#include "LaunchProfile.h"
|
||||
#include "ProfilePatch.h"
|
||||
#include "Component.h"
|
||||
#include "ProfileUtils.h"
|
||||
#include "BaseVersion.h"
|
||||
#include "MojangDownloadInfo.h"
|
||||
#include "multimc_logic_export.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
|
||||
struct ComponentListData;
|
||||
class ComponentUpdateTask;
|
||||
|
||||
class MULTIMC_LOGIC_EXPORT ComponentList : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend ComponentUpdateTask;
|
||||
public:
|
||||
explicit ComponentList(MinecraftInstance * instance);
|
||||
virtual ~ComponentList();
|
||||
@ -46,6 +48,9 @@ public:
|
||||
virtual int columnCount(const QModelIndex &parent) const override;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
/// call this to explicitly mark the component list as loaded - this is used to build a new component list from scratch.
|
||||
void buildingFromScratch();
|
||||
|
||||
/// is this version unchanged by the user?
|
||||
bool isVanilla();
|
||||
|
||||
@ -58,68 +63,76 @@ public:
|
||||
/// install a jar/zip as a replacement for the main jar
|
||||
void installCustomJar(QString selectedFile);
|
||||
|
||||
/// DEPRECATED, remove ASAP
|
||||
int getFreeOrderNumber();
|
||||
|
||||
enum MoveDirection { MoveUp, MoveDown };
|
||||
/// move patch file # up or down the list
|
||||
/// move component file # up or down the list
|
||||
void move(const int index, const MoveDirection direction);
|
||||
|
||||
/// remove patch file # - including files/records
|
||||
/// remove component file # - including files/records
|
||||
bool remove(const int index);
|
||||
|
||||
/// remove patch file by id - including files/records
|
||||
/// remove component file by id - including files/records
|
||||
bool remove(const QString id);
|
||||
|
||||
bool customize(int index);
|
||||
|
||||
bool revertToBase(int index);
|
||||
|
||||
void resetOrder();
|
||||
/// reload the list, reload all components, resolve dependencies
|
||||
void reload(Net::Mode netmode);
|
||||
|
||||
/// reload all profile patches from storage, clear the profile and apply the patches
|
||||
void reload();
|
||||
// reload all components, resolve dependencies
|
||||
void resolve(Net::Mode netmode);
|
||||
|
||||
/// apply the patches. Catches all the errors and returns true/false for success/failure
|
||||
bool reapplyPatches();
|
||||
/// get current running task...
|
||||
shared_qobject_ptr<Task> getCurrentTask();
|
||||
|
||||
std::shared_ptr<LaunchProfile> getProfile() const;
|
||||
void clearProfile();
|
||||
|
||||
// NOTE: used ONLY by MinecraftInstance to provide legacy version mappings from instance config
|
||||
void setOldConfigVersion(const QString &uid, const QString &version);
|
||||
|
||||
QString getComponentVersion(const QString &uid) const;
|
||||
|
||||
bool setComponentVersion(const QString &uid, const QString &version, bool important = false);
|
||||
|
||||
QString patchFilePathForUid(const QString &uid) const;
|
||||
public:
|
||||
/// get the profile patch by id
|
||||
ProfilePatchPtr versionPatch(const QString &id);
|
||||
/// get the profile component by id
|
||||
ComponentPtr getComponent(const QString &id);
|
||||
|
||||
/// get the profile patch by index
|
||||
ProfilePatchPtr versionPatch(int index);
|
||||
|
||||
/// save the current patch order
|
||||
void saveCurrentOrder() const;
|
||||
|
||||
/// Remove all the patches
|
||||
void clearPatches();
|
||||
|
||||
/// Add the patch object to the internal list of patches
|
||||
void appendPatch(ProfilePatchPtr patch);
|
||||
/// get the profile component by index
|
||||
ComponentPtr getComponent(int index);
|
||||
|
||||
private:
|
||||
void load_internal();
|
||||
bool resetOrder_internal();
|
||||
bool saveOrder_internal(ProfileUtils::PatchOrder order) const;
|
||||
void scheduleSave();
|
||||
bool saveIsScheduled() const;
|
||||
|
||||
/// apply the component patches. Catches all the errors and returns true/false for success/failure
|
||||
void invalidateLaunchProfile();
|
||||
|
||||
/// Add the component to the internal list of patches
|
||||
void appendComponent(ComponentPtr component);
|
||||
/// insert component so that its index is ideally the specified one (returns real index)
|
||||
void insertComponent(size_t index, ComponentPtr component);
|
||||
|
||||
QString componentsFilePath() const;
|
||||
QString patchesPattern() const;
|
||||
|
||||
private slots:
|
||||
void save();
|
||||
void updateSucceeded();
|
||||
void updateFailed(const QString & error);
|
||||
void componentDataChanged();
|
||||
|
||||
private:
|
||||
bool load();
|
||||
bool installJarMods_internal(QStringList filepaths);
|
||||
bool installCustomJar_internal(QString filepath);
|
||||
bool removePatch_internal(ProfilePatchPtr patch);
|
||||
bool customizePatch_internal(ProfilePatchPtr patch);
|
||||
bool revertPatch_internal(ProfilePatchPtr patch);
|
||||
void loadDefaultBuiltinPatches_internal();
|
||||
void loadUserPatches_internal();
|
||||
void upgradeDeprecatedFiles_internal();
|
||||
bool removeComponent_internal(ComponentPtr patch);
|
||||
|
||||
bool migratePreComponentConfig();
|
||||
|
||||
private: /* data */
|
||||
/// list of attached profile patches
|
||||
QList<ProfilePatchPtr> m_patches;
|
||||
|
||||
// the instance this belongs to
|
||||
MinecraftInstance *m_instance;
|
||||
|
||||
std::shared_ptr<LaunchProfile> m_profile;
|
||||
std::unique_ptr<ComponentListData> d;
|
||||
};
|
||||
|
42
api/logic/minecraft/ComponentList_p.h
Normal file
42
api/logic/minecraft/ComponentList_p.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "Component.h"
|
||||
#include <map>
|
||||
#include <QTimer>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
class MinecraftInstance;
|
||||
using ComponentContainer = QList<ComponentPtr>;
|
||||
using ComponentIndex = QMap<QString, ComponentPtr>;
|
||||
using ConnectionList = QList<QMetaObject::Connection>;
|
||||
|
||||
struct ComponentListData
|
||||
{
|
||||
// the instance this belongs to
|
||||
MinecraftInstance *m_instance;
|
||||
|
||||
// the launch profile (volatile, temporary thing created on demand)
|
||||
std::shared_ptr<LaunchProfile> m_profile;
|
||||
|
||||
// version information migrated from instance.cfg file. Single use on migration!
|
||||
std::map<QString, QString> m_oldConfigVersions;
|
||||
QString getOldConfigVersion(const QString& uid) const
|
||||
{
|
||||
const auto iter = m_oldConfigVersions.find(uid);
|
||||
if(iter != m_oldConfigVersions.cend())
|
||||
{
|
||||
return (*iter).second;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
// persistent list of components and related machinery
|
||||
ComponentContainer components;
|
||||
ComponentIndex componentIndex;
|
||||
bool dirty = false;
|
||||
QTimer m_saveTimer;
|
||||
shared_qobject_ptr<Task> m_updateTask;
|
||||
bool loaded = false;
|
||||
};
|
||||
|
688
api/logic/minecraft/ComponentUpdateTask.cpp
Normal file
688
api/logic/minecraft/ComponentUpdateTask.cpp
Normal file
@ -0,0 +1,688 @@
|
||||
#include "ComponentUpdateTask.h"
|
||||
|
||||
#include "ComponentList_p.h"
|
||||
#include "ComponentList.h"
|
||||
#include "Component.h"
|
||||
#include <Env.h>
|
||||
#include <meta/Index.h>
|
||||
#include <meta/VersionList.h>
|
||||
#include <meta/Version.h>
|
||||
#include "ComponentUpdateTask_p.h"
|
||||
#include <cassert>
|
||||
#include <Version.h>
|
||||
#include "net/Mode.h"
|
||||
#include "OneSixVersionFormat.h"
|
||||
|
||||
/*
|
||||
* This is responsible for loading the components of a component list AND resolving dependency issues between them
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME: the 'one shot async task' nature of this does not fit the intended usage
|
||||
* Really, it should be a reactor/state machine that receives input from the application
|
||||
* and dynamically adapts to changing requirements...
|
||||
*
|
||||
* The reactor should be the only entry into manipulating the ComponentList.
|
||||
* See: https://en.wikipedia.org/wiki/Reactor_pattern
|
||||
*/
|
||||
|
||||
/*
|
||||
* Or make this operate on a snapshot of the ComponentList state, then merge results in as long as the snapshot and ComponentList didn't change?
|
||||
* If the component list changes, start over.
|
||||
*/
|
||||
|
||||
ComponentUpdateTask::ComponentUpdateTask(Mode mode, Net::Mode netmode, ComponentList* list, QObject* parent)
|
||||
: Task(parent)
|
||||
{
|
||||
d.reset(new ComponentUpdateTaskData);
|
||||
d->m_list = list;
|
||||
d->mode = mode;
|
||||
d->netmode = netmode;
|
||||
}
|
||||
|
||||
ComponentUpdateTask::~ComponentUpdateTask()
|
||||
{
|
||||
}
|
||||
|
||||
void ComponentUpdateTask::executeTask()
|
||||
{
|
||||
qDebug() << "Loading components";
|
||||
loadComponents();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
enum class LoadResult
|
||||
{
|
||||
LoadedLocal,
|
||||
RequiresRemote,
|
||||
Failed
|
||||
};
|
||||
|
||||
LoadResult composeLoadResult(LoadResult a, LoadResult b)
|
||||
{
|
||||
if (a < b)
|
||||
{
|
||||
return b;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
static LoadResult loadComponent(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
|
||||
{
|
||||
if(component->m_loaded)
|
||||
{
|
||||
qDebug() << component->getName() << "is already loaded";
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
|
||||
LoadResult result = LoadResult::Failed;
|
||||
auto customPatchFilename = component->getFilename();
|
||||
if(QFile::exists(customPatchFilename))
|
||||
{
|
||||
// if local file exists...
|
||||
|
||||
// check for uid problems inside...
|
||||
bool fileChanged = false;
|
||||
auto file = ProfileUtils::parseJsonFile(QFileInfo(customPatchFilename), false);
|
||||
if(file->uid != component->m_uid)
|
||||
{
|
||||
file->uid = component->m_uid;
|
||||
fileChanged = true;
|
||||
}
|
||||
if(fileChanged)
|
||||
{
|
||||
// FIXME: @QUALITY do not ignore return value
|
||||
ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), customPatchFilename);
|
||||
}
|
||||
|
||||
component->m_file = file;
|
||||
component->m_loaded = true;
|
||||
result = LoadResult::LoadedLocal;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto metaVersion = ENV.metadataIndex()->get(component->m_uid, component->m_version);
|
||||
component->m_metaVersion = metaVersion;
|
||||
if(metaVersion->isLoaded())
|
||||
{
|
||||
component->m_loaded = true;
|
||||
result = LoadResult::LoadedLocal;
|
||||
}
|
||||
else
|
||||
{
|
||||
metaVersion->load(netmode);
|
||||
loadTask = metaVersion->getCurrentTask();
|
||||
if(loadTask)
|
||||
result = LoadResult::RequiresRemote;
|
||||
else if (metaVersion->isLoaded())
|
||||
result = LoadResult::LoadedLocal;
|
||||
else
|
||||
result = LoadResult::Failed;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static LoadResult loadComponentList(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
|
||||
{
|
||||
if(component->m_loaded)
|
||||
{
|
||||
qDebug() << component->getName() << "is already loaded";
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
|
||||
LoadResult result = LoadResult::Failed;
|
||||
auto metaList = ENV.metadataIndex()->get(component->m_uid);
|
||||
if(metaList->isLoaded())
|
||||
{
|
||||
component->m_loaded = true;
|
||||
result = LoadResult::LoadedLocal;
|
||||
}
|
||||
else
|
||||
{
|
||||
metaList->load(netmode);
|
||||
loadTask = metaList->getCurrentTask();
|
||||
result = LoadResult::RequiresRemote;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static LoadResult loadIndex(shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
|
||||
{
|
||||
// FIXME: DECIDE. do we want to run the update task anyway?
|
||||
if(ENV.metadataIndex()->isLoaded())
|
||||
{
|
||||
qDebug() << "Index is already loaded";
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
ENV.metadataIndex()->load(netmode);
|
||||
loadTask = ENV.metadataIndex()->getCurrentTask();
|
||||
if(loadTask)
|
||||
{
|
||||
return LoadResult::RequiresRemote;
|
||||
}
|
||||
// FIXME: this is assuming the load succeeded... did it really?
|
||||
return LoadResult::LoadedLocal;
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentUpdateTask::loadComponents()
|
||||
{
|
||||
LoadResult result = LoadResult::LoadedLocal;
|
||||
size_t taskIndex = 0;
|
||||
size_t componentIndex = 0;
|
||||
d->remoteLoadSuccessful = true;
|
||||
// load the main index (it is needed to determine if components can revert)
|
||||
{
|
||||
// FIXME: tear out as a method? or lambda?
|
||||
shared_qobject_ptr<Task> indexLoadTask;
|
||||
auto singleResult = loadIndex(indexLoadTask, d->netmode);
|
||||
result = composeLoadResult(result, singleResult);
|
||||
if(indexLoadTask)
|
||||
{
|
||||
qDebug() << "Remote loading is being run for metadata index";
|
||||
RemoteLoadStatus status;
|
||||
status.type = RemoteLoadStatus::Type::Index;
|
||||
d->remoteLoadStatusList.append(status);
|
||||
connect(indexLoadTask.get(), &Task::succeeded, [=]()
|
||||
{
|
||||
remoteLoadSucceeded(taskIndex);
|
||||
});
|
||||
connect(indexLoadTask.get(), &Task::failed, [=](const QString & error)
|
||||
{
|
||||
remoteLoadFailed(taskIndex, error);
|
||||
});
|
||||
taskIndex++;
|
||||
}
|
||||
}
|
||||
// load all the components OR their lists...
|
||||
for (auto component: d->m_list->d->components)
|
||||
{
|
||||
shared_qobject_ptr<Task> loadTask;
|
||||
LoadResult singleResult;
|
||||
RemoteLoadStatus::Type loadType;
|
||||
// FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, ignore all that...
|
||||
#if 0
|
||||
switch(d->mode)
|
||||
{
|
||||
case Mode::Launch:
|
||||
{
|
||||
singleResult = loadComponent(component, loadTask, d->netmode);
|
||||
loadType = RemoteLoadStatus::Type::Version;
|
||||
break;
|
||||
}
|
||||
case Mode::Resolution:
|
||||
{
|
||||
singleResult = loadComponentList(component, loadTask, d->netmode);
|
||||
loadType = RemoteLoadStatus::Type::List;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
singleResult = loadComponent(component, loadTask, d->netmode);
|
||||
loadType = RemoteLoadStatus::Type::Version;
|
||||
#endif
|
||||
if(singleResult == LoadResult::LoadedLocal)
|
||||
{
|
||||
component->updateCachedData();
|
||||
}
|
||||
result = composeLoadResult(result, singleResult);
|
||||
if (loadTask)
|
||||
{
|
||||
qDebug() << "Remote loading is being run for" << component->getName();
|
||||
connect(loadTask.get(), &Task::succeeded, [=]()
|
||||
{
|
||||
remoteLoadSucceeded(taskIndex);
|
||||
});
|
||||
connect(loadTask.get(), &Task::failed, [=](const QString & error)
|
||||
{
|
||||
remoteLoadFailed(taskIndex, error);
|
||||
});
|
||||
RemoteLoadStatus status;
|
||||
status.type = loadType;
|
||||
status.componentListIndex = componentIndex;
|
||||
d->remoteLoadStatusList.append(status);
|
||||
taskIndex++;
|
||||
}
|
||||
componentIndex++;
|
||||
}
|
||||
d->remoteTasksInProgress = taskIndex;
|
||||
switch(result)
|
||||
{
|
||||
case LoadResult::LoadedLocal:
|
||||
{
|
||||
// Everything got loaded. Advance to dependency resolution.
|
||||
resolveDependencies(d->mode == Mode::Launch || d->netmode == Net::Mode::Offline);
|
||||
break;
|
||||
}
|
||||
case LoadResult::RequiresRemote:
|
||||
{
|
||||
// we wait for signals.
|
||||
break;
|
||||
}
|
||||
case LoadResult::Failed:
|
||||
{
|
||||
emitFailed(tr("Some component metadata load tasks failed."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct RequireEx : public Meta::Require
|
||||
{
|
||||
size_t indexOfFirstDependee = 0;
|
||||
};
|
||||
struct RequireCompositionResult
|
||||
{
|
||||
bool ok;
|
||||
RequireEx outcome;
|
||||
};
|
||||
using RequireExSet = std::set<RequireEx>;
|
||||
}
|
||||
|
||||
static RequireCompositionResult composeRequirement(const RequireEx & a, const RequireEx & b)
|
||||
{
|
||||
assert(a.uid == b.uid);
|
||||
RequireEx out;
|
||||
out.uid = a.uid;
|
||||
out.indexOfFirstDependee = std::min(a.indexOfFirstDependee, b.indexOfFirstDependee);
|
||||
if(a.equalsVersion.isEmpty())
|
||||
{
|
||||
out.equalsVersion = b.equalsVersion;
|
||||
}
|
||||
else if (b.equalsVersion.isEmpty())
|
||||
{
|
||||
out.equalsVersion = a.equalsVersion;
|
||||
}
|
||||
else if (a.equalsVersion == b.equalsVersion)
|
||||
{
|
||||
out.equalsVersion = a.equalsVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: mark error as explicit version conflict
|
||||
return {false, out};
|
||||
}
|
||||
|
||||
if(a.suggests.isEmpty())
|
||||
{
|
||||
out.suggests = b.suggests;
|
||||
}
|
||||
else if (b.suggests.isEmpty())
|
||||
{
|
||||
out.suggests = a.suggests;
|
||||
}
|
||||
else
|
||||
{
|
||||
Version aVer(a.suggests);
|
||||
Version bVer(b.suggests);
|
||||
out.suggests = (aVer < bVer ? b.suggests : a.suggests);
|
||||
}
|
||||
return {true, out};
|
||||
}
|
||||
|
||||
// gather the requirements from all components, finding any obvious conflicts
|
||||
static bool gatherRequirementsFromComponents(const ComponentContainer & input, RequireExSet & output)
|
||||
{
|
||||
bool succeeded = true;
|
||||
size_t componentNum = 0;
|
||||
for(auto component: input)
|
||||
{
|
||||
auto &componentRequires = component->m_cachedRequires;
|
||||
for(const auto & componentRequire: componentRequires)
|
||||
{
|
||||
auto found = std::find_if(output.cbegin(), output.cend(), [componentRequire](const Meta::Require & req){
|
||||
return req.uid == componentRequire.uid;
|
||||
});
|
||||
|
||||
RequireEx componenRequireEx;
|
||||
componenRequireEx.uid = componentRequire.uid;
|
||||
componenRequireEx.suggests = componentRequire.suggests;
|
||||
componenRequireEx.equalsVersion = componentRequire.equalsVersion;
|
||||
componenRequireEx.indexOfFirstDependee = componentNum;
|
||||
|
||||
if(found != output.cend())
|
||||
{
|
||||
// found... process it further
|
||||
auto result = composeRequirement(componenRequireEx, *found);
|
||||
if(result.ok)
|
||||
{
|
||||
output.erase(componenRequireEx);
|
||||
output.insert(result.outcome);
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical()
|
||||
<< "Conflicting requirements:"
|
||||
<< componentRequire.uid
|
||||
<< "versions:"
|
||||
<< componentRequire.equalsVersion
|
||||
<< ";"
|
||||
<< (*found).equalsVersion;
|
||||
}
|
||||
succeeded &= result.ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not found, accumulate
|
||||
output.insert(componenRequireEx);
|
||||
}
|
||||
}
|
||||
componentNum++;
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
/// Get list of uids that can be trivially removed because nothing is depending on them anymore (and they are installed as deps)
|
||||
static void getTrivialRemovals(const ComponentContainer & components, const RequireExSet & reqs, QStringList &toRemove)
|
||||
{
|
||||
for(const auto & component: components)
|
||||
{
|
||||
if(!component->m_dependencyOnly)
|
||||
continue;
|
||||
if(!component->m_cachedVolatile)
|
||||
continue;
|
||||
RequireEx reqNeedle;
|
||||
reqNeedle.uid = component->m_uid;
|
||||
const auto iter = reqs.find(reqNeedle);
|
||||
if(iter == reqs.cend())
|
||||
{
|
||||
toRemove.append(component->m_uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handles:
|
||||
* - trivial addition (there is an unmet requirement and it can be trivially met by adding something)
|
||||
* - trivial version conflict of dependencies == explicit version required and installed is different
|
||||
*
|
||||
* toAdd - set of requirements than mean adding a new component
|
||||
* toChange - set of requirements that mean changing version of an existing component
|
||||
*/
|
||||
static bool getTrivialComponentChanges(const ComponentIndex & index, const RequireExSet & input, RequireExSet & toAdd, RequireExSet & toChange)
|
||||
{
|
||||
enum class Decision
|
||||
{
|
||||
Undetermined,
|
||||
Met,
|
||||
Missing,
|
||||
VersionNotSame,
|
||||
LockedVersionNotSame
|
||||
} decision = Decision::Undetermined;
|
||||
|
||||
QString reqStr;
|
||||
bool succeeded = true;
|
||||
// list the composed requirements and say if they are met or unmet
|
||||
for(auto & req: input)
|
||||
{
|
||||
do
|
||||
{
|
||||
if(req.equalsVersion.isEmpty())
|
||||
{
|
||||
reqStr = QString("Req: %1").arg(req.uid);
|
||||
if(index.contains(req.uid))
|
||||
{
|
||||
decision = Decision::Met;
|
||||
}
|
||||
else
|
||||
{
|
||||
toAdd.insert(req);
|
||||
decision = Decision::Missing;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
reqStr = QString("Req: %1 == %2").arg(req.uid, req.equalsVersion);
|
||||
const auto & compIter = index.find(req.uid);
|
||||
if(compIter == index.cend())
|
||||
{
|
||||
toAdd.insert(req);
|
||||
decision = Decision::Missing;
|
||||
break;
|
||||
}
|
||||
auto & comp = (*compIter);
|
||||
if(comp->getVersion() != req.equalsVersion)
|
||||
{
|
||||
if(comp->m_dependencyOnly)
|
||||
{
|
||||
decision = Decision::VersionNotSame;
|
||||
}
|
||||
else
|
||||
{
|
||||
decision = Decision::LockedVersionNotSame;
|
||||
}
|
||||
break;
|
||||
}
|
||||
decision = Decision::Met;
|
||||
}
|
||||
} while(false);
|
||||
switch(decision)
|
||||
{
|
||||
case Decision::Undetermined:
|
||||
qCritical() << "No decision for" << reqStr;
|
||||
succeeded = false;
|
||||
break;
|
||||
case Decision::Met:
|
||||
qDebug() << reqStr << "Is met.";
|
||||
break;
|
||||
case Decision::Missing:
|
||||
qDebug() << reqStr << "Is missing and should be added at" << req.indexOfFirstDependee;
|
||||
toAdd.insert(req);
|
||||
break;
|
||||
case Decision::VersionNotSame:
|
||||
qDebug() << reqStr << "already has different version that can be changed.";
|
||||
toChange.insert(req);
|
||||
break;
|
||||
case Decision::LockedVersionNotSame:
|
||||
qDebug() << reqStr << "already has different version that cannot be changed.";
|
||||
succeeded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
// FIXME, TODO: decouple dependency resolution from loading
|
||||
// FIXME: This works directly with the ComponentList internals. It shouldn't! It needs richer data types than ComponentList uses.
|
||||
// FIXME: throw all this away and use a graph
|
||||
void ComponentUpdateTask::resolveDependencies(bool checkOnly)
|
||||
{
|
||||
qDebug() << "Resolving dependencies";
|
||||
/*
|
||||
* this is a naive dependency resolving algorithm. all it does is check for following conditions and react in simple ways:
|
||||
* 1. There are conflicting dependencies on the same uid with different exact version numbers
|
||||
* -> hard error
|
||||
* 2. A dependency has non-matching exact version number
|
||||
* -> hard error
|
||||
* 3. A dependency is entirely missing and needs to be injected before the dependee(s)
|
||||
* -> requirements are injected
|
||||
*
|
||||
* NOTE: this is a placeholder and should eventually be replaced with something 'serious'
|
||||
*/
|
||||
auto & components = d->m_list->d->components;
|
||||
auto & componentIndex = d->m_list->d->componentIndex;
|
||||
|
||||
RequireExSet allRequires;
|
||||
QStringList toRemove;
|
||||
do
|
||||
{
|
||||
allRequires.clear();
|
||||
toRemove.clear();
|
||||
if(!gatherRequirementsFromComponents(components, allRequires))
|
||||
{
|
||||
emitFailed(tr("Conflicting requirements detected during dependency checking!"));
|
||||
return;
|
||||
}
|
||||
getTrivialRemovals(components, allRequires, toRemove);
|
||||
if(!toRemove.isEmpty())
|
||||
{
|
||||
qDebug() << "Removing obsolete components...";
|
||||
for(auto & remove : toRemove)
|
||||
{
|
||||
qDebug() << "Removing" << remove;
|
||||
d->m_list->remove(remove);
|
||||
}
|
||||
}
|
||||
} while (!toRemove.isEmpty());
|
||||
RequireExSet toAdd;
|
||||
RequireExSet toChange;
|
||||
bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange);
|
||||
if(!succeeded)
|
||||
{
|
||||
emitFailed(tr("Instance has conflicting dependencies."));
|
||||
return;
|
||||
}
|
||||
if(checkOnly)
|
||||
{
|
||||
if(toAdd.size() || toChange.size())
|
||||
{
|
||||
emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch."));
|
||||
}
|
||||
else
|
||||
{
|
||||
emitSucceeded();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool recursionNeeded = false;
|
||||
if(toAdd.size())
|
||||
{
|
||||
// add stuff...
|
||||
for(auto &add: toAdd)
|
||||
{
|
||||
ComponentPtr component = new Component(d->m_list, add.uid);
|
||||
if(!add.equalsVersion.isEmpty())
|
||||
{
|
||||
// exact version
|
||||
qDebug() << "Adding" << add.uid << "version" << add.equalsVersion << "at position" << add.indexOfFirstDependee;
|
||||
component->m_version = add.equalsVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
// version needs to be decided
|
||||
qDebug() << "Adding" << add.uid << "at position" << add.indexOfFirstDependee;
|
||||
// ############################################################################################################
|
||||
// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
|
||||
if(!add.suggests.isEmpty())
|
||||
{
|
||||
component->m_version = add.suggests;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(add.uid == "org.lwjgl")
|
||||
{
|
||||
component->m_version = "2.9.1";
|
||||
}
|
||||
else if (add.uid == "org.lwjgl3")
|
||||
{
|
||||
component->m_version = "3.1.2";
|
||||
}
|
||||
}
|
||||
// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
|
||||
// ############################################################################################################
|
||||
}
|
||||
component->m_dependencyOnly = true;
|
||||
// FIXME: this should not work directly with the component list
|
||||
d->m_list->insertComponent(add.indexOfFirstDependee, component);
|
||||
componentIndex[add.uid] = component;
|
||||
}
|
||||
recursionNeeded = true;
|
||||
}
|
||||
if(toChange.size())
|
||||
{
|
||||
// change a version of something that exists
|
||||
for(auto &change: toChange)
|
||||
{
|
||||
// FIXME: this should not work directly with the component list
|
||||
qDebug() << "Setting version of " << change.uid << "to" << change.equalsVersion;
|
||||
auto component = componentIndex[change.uid];
|
||||
component->setVersion(change.equalsVersion);
|
||||
}
|
||||
recursionNeeded = true;
|
||||
}
|
||||
|
||||
if(recursionNeeded)
|
||||
{
|
||||
loadComponents();
|
||||
}
|
||||
else
|
||||
{
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentUpdateTask::remoteLoadSucceeded(size_t taskIndex)
|
||||
{
|
||||
auto &taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
if(taskSlot.finished)
|
||||
{
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
}
|
||||
qDebug() << "Remote task" << taskIndex << "succeeded";
|
||||
taskSlot.succeeded = false;
|
||||
taskSlot.finished = true;
|
||||
d->remoteTasksInProgress --;
|
||||
// update the cached data of the component from the downloaded version file.
|
||||
if (taskSlot.type == RemoteLoadStatus::Type::Version)
|
||||
{
|
||||
auto component = d->m_list->getComponent(taskSlot.componentListIndex);
|
||||
component->m_loaded = true;
|
||||
component->updateCachedData();
|
||||
}
|
||||
checkIfAllFinished();
|
||||
}
|
||||
|
||||
|
||||
void ComponentUpdateTask::remoteLoadFailed(size_t taskIndex, const QString& msg)
|
||||
{
|
||||
auto &taskSlot = d->remoteLoadStatusList[taskIndex];
|
||||
if(taskSlot.finished)
|
||||
{
|
||||
qWarning() << "Got multiple results from remote load task" << taskIndex;
|
||||
return;
|
||||
}
|
||||
qDebug() << "Remote task" << taskIndex << "failed: " << msg;
|
||||
d->remoteLoadSuccessful = false;
|
||||
taskSlot.succeeded = false;
|
||||
taskSlot.finished = true;
|
||||
taskSlot.error = msg;
|
||||
d->remoteTasksInProgress --;
|
||||
checkIfAllFinished();
|
||||
}
|
||||
|
||||
void ComponentUpdateTask::checkIfAllFinished()
|
||||
{
|
||||
if(d->remoteTasksInProgress)
|
||||
{
|
||||
// not yet...
|
||||
return;
|
||||
}
|
||||
if(d->remoteLoadSuccessful)
|
||||
{
|
||||
// nothing bad happened... clear the temp load status and proceed with looking at dependencies
|
||||
d->remoteLoadStatusList.clear();
|
||||
resolveDependencies(d->mode == Mode::Launch);
|
||||
}
|
||||
else
|
||||
{
|
||||
// remote load failed... report error and bail
|
||||
QStringList allErrorsList;
|
||||
for(auto & item: d->remoteLoadStatusList)
|
||||
{
|
||||
if(!item.succeeded)
|
||||
{
|
||||
allErrorsList.append(item.error);
|
||||
}
|
||||
}
|
||||
auto allErrors = allErrorsList.join("\n");
|
||||
emitFailed(tr("Component metadata update task failed while downloading from remote server:\n%1").arg(allErrors));
|
||||
d->remoteLoadStatusList.clear();
|
||||
}
|
||||
}
|
37
api/logic/minecraft/ComponentUpdateTask.h
Normal file
37
api/logic/minecraft/ComponentUpdateTask.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include <memory>
|
||||
class ComponentList;
|
||||
struct ComponentUpdateTaskData;
|
||||
|
||||
class ComponentUpdateTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
Launch,
|
||||
Resolution
|
||||
};
|
||||
|
||||
public:
|
||||
explicit ComponentUpdateTask(Mode mode, Net::Mode netmode, ComponentList * list, QObject *parent = 0);
|
||||
virtual ~ComponentUpdateTask();
|
||||
|
||||
protected:
|
||||
void executeTask();
|
||||
|
||||
private:
|
||||
void loadComponents();
|
||||
void resolveDependencies(bool checkOnly);
|
||||
|
||||
void remoteLoadSucceeded(size_t index);
|
||||
void remoteLoadFailed(size_t index, const QString &msg);
|
||||
void checkIfAllFinished();
|
||||
|
||||
private:
|
||||
std::unique_ptr<ComponentUpdateTaskData> d;
|
||||
};
|
32
api/logic/minecraft/ComponentUpdateTask_p.h
Normal file
32
api/logic/minecraft/ComponentUpdateTask_p.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include "net/Mode.h"
|
||||
|
||||
class ComponentList;
|
||||
|
||||
struct RemoteLoadStatus
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Index,
|
||||
List,
|
||||
Version
|
||||
} type = Type::Version;
|
||||
size_t componentListIndex = 0;
|
||||
bool finished = false;
|
||||
bool succeeded = false;
|
||||
QString error;
|
||||
};
|
||||
|
||||
struct ComponentUpdateTaskData
|
||||
{
|
||||
ComponentList * m_list = nullptr;
|
||||
QList<RemoteLoadStatus> remoteLoadStatusList;
|
||||
bool remoteLoadSuccessful = true;
|
||||
size_t remoteTasksInProgress = 0;
|
||||
ComponentUpdateTask::Mode mode;
|
||||
Net::Mode netmode;
|
||||
};
|
@ -34,6 +34,7 @@
|
||||
#include "ComponentList.h"
|
||||
#include "AssetsUtils.h"
|
||||
#include "MinecraftUpdate.h"
|
||||
#include "MinecraftLoadAndCheck.h"
|
||||
|
||||
#define IBUS "@im=ibus"
|
||||
|
||||
@ -63,12 +64,6 @@ private:
|
||||
MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
|
||||
: BaseInstance(globalSettings, settings, rootDir)
|
||||
{
|
||||
// FIXME: remove these
|
||||
m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
|
||||
m_settings->registerSetting("LWJGLVersion", "2.9.1");
|
||||
m_settings->registerSetting("ForgeVersion", "");
|
||||
m_settings->registerSetting("LiteloaderVersion", "");
|
||||
|
||||
// Java Settings
|
||||
auto javaOverride = m_settings->registerSetting("OverrideJava", false);
|
||||
auto locationOverride = m_settings->registerSetting("OverrideJavaLocation", false);
|
||||
@ -101,11 +96,23 @@ MinecraftInstance::MinecraftInstance(SettingsObjectPtr globalSettings, SettingsO
|
||||
// Minecraft launch method
|
||||
auto launchMethodOverride = m_settings->registerSetting("OverrideMCLaunchMethod", false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("MCLaunchMethod"), launchMethodOverride);
|
||||
|
||||
// DEPRECATED: Read what versions the user configuration thinks should be used
|
||||
m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
|
||||
m_settings->registerSetting("LWJGLVersion", "");
|
||||
m_settings->registerSetting("ForgeVersion", "");
|
||||
m_settings->registerSetting("LiteloaderVersion", "");
|
||||
|
||||
m_components.reset(new ComponentList(this));
|
||||
m_components->setOldConfigVersion("net.minecraft", m_settings->get("IntendedVersion").toString());
|
||||
auto setting = m_settings->getSetting("LWJGLVersion");
|
||||
m_components->setOldConfigVersion("org.lwjgl", m_settings->get("LWJGLVersion").toString());
|
||||
m_components->setOldConfigVersion("net.minecraftforge", m_settings->get("ForgeVersion").toString());
|
||||
m_components->setOldConfigVersion("com.mumfrey.liteloader", m_settings->get("LiteloaderVersion").toString());
|
||||
}
|
||||
|
||||
void MinecraftInstance::init()
|
||||
{
|
||||
createProfile();
|
||||
}
|
||||
|
||||
QString MinecraftInstance::typeName() const
|
||||
@ -113,41 +120,6 @@ QString MinecraftInstance::typeName() const
|
||||
return "Minecraft";
|
||||
}
|
||||
|
||||
|
||||
bool MinecraftInstance::reload()
|
||||
{
|
||||
if (BaseInstance::reload())
|
||||
{
|
||||
try
|
||||
{
|
||||
reloadProfile();
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MinecraftInstance::createProfile()
|
||||
{
|
||||
m_components.reset(new ComponentList(this));
|
||||
}
|
||||
|
||||
void MinecraftInstance::reloadProfile()
|
||||
{
|
||||
m_components->reload();
|
||||
emit versionReloaded();
|
||||
}
|
||||
|
||||
void MinecraftInstance::clearProfile()
|
||||
{
|
||||
m_components->clearProfile();
|
||||
emit versionReloaded();
|
||||
}
|
||||
|
||||
std::shared_ptr<ComponentList> MinecraftInstance::getComponentList() const
|
||||
{
|
||||
return m_components;
|
||||
@ -771,7 +743,7 @@ QString MinecraftInstance::getStatusbarDescription()
|
||||
}
|
||||
|
||||
QString description;
|
||||
description.append(tr("Minecraft %1 (%2)").arg(getComponentVersion("net.minecraft")).arg(typeName()));
|
||||
description.append(tr("Minecraft %1 (%2)").arg(m_components->getComponentVersion("net.minecraft")).arg(typeName()));
|
||||
if(totalTimePlayed() > 0)
|
||||
{
|
||||
description.append(tr(", played for %1").arg(prettifyTimeDuration(totalTimePlayed())));
|
||||
@ -783,9 +755,20 @@ QString MinecraftInstance::getStatusbarDescription()
|
||||
return description;
|
||||
}
|
||||
|
||||
shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask()
|
||||
shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
|
||||
{
|
||||
return shared_qobject_ptr<Task>(new OneSixUpdate(this));
|
||||
switch (mode)
|
||||
{
|
||||
case Net::Mode::Offline:
|
||||
{
|
||||
return shared_qobject_ptr<Task>(new MinecraftLoadAndCheck(this));
|
||||
}
|
||||
case Net::Mode::Online:
|
||||
{
|
||||
return shared_qobject_ptr<Task>(new OneSixUpdate(this));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session)
|
||||
@ -827,11 +810,14 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
|
||||
if(session->status != AuthSession::PlayableOffline)
|
||||
{
|
||||
process->appendStep(std::make_shared<ClaimAccount>(pptr, session));
|
||||
process->appendStep(std::make_shared<Update>(pptr));
|
||||
process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Online));
|
||||
}
|
||||
else
|
||||
{
|
||||
process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Offline));
|
||||
}
|
||||
|
||||
// if there are any jar mods
|
||||
if(getJarMods().size())
|
||||
{
|
||||
auto step = std::make_shared<ModMinecraftJar>(pptr);
|
||||
process->appendStep(step);
|
||||
@ -900,53 +886,6 @@ JavaVersion MinecraftInstance::getJavaVersion() const
|
||||
return JavaVersion(settings()->get("JavaVersion").toString());
|
||||
}
|
||||
|
||||
bool MinecraftInstance::setComponentVersion(const QString& uid, const QString& version)
|
||||
{
|
||||
if(uid == "net.minecraft")
|
||||
{
|
||||
settings()->set("IntendedVersion", version);
|
||||
}
|
||||
else if (uid == "org.lwjgl")
|
||||
{
|
||||
settings()->set("LWJGLVersion", version);
|
||||
}
|
||||
else if (uid == "net.minecraftforge")
|
||||
{
|
||||
settings()->set("ForgeVersion", version);
|
||||
}
|
||||
else if (uid == "com.mumfrey.liteloader")
|
||||
{
|
||||
settings()->set("LiteloaderVersion", version);
|
||||
}
|
||||
if(getComponentList())
|
||||
{
|
||||
clearProfile();
|
||||
}
|
||||
emit propertiesChanged(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
QString MinecraftInstance::getComponentVersion(const QString& uid) const
|
||||
{
|
||||
if(uid == "net.minecraft")
|
||||
{
|
||||
return settings()->get("IntendedVersion").toString();
|
||||
}
|
||||
else if(uid == "org.lwjgl")
|
||||
{
|
||||
return settings()->get("LWJGLVersion").toString();
|
||||
}
|
||||
else if(uid == "net.minecraftforge")
|
||||
{
|
||||
return settings()->get("ForgeVersion").toString();
|
||||
}
|
||||
else if(uid == "com.mumfrey.liteloader")
|
||||
{
|
||||
return settings()->get("LiteloaderVersion").toString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
std::shared_ptr<ModList> MinecraftInstance::loaderModList() const
|
||||
{
|
||||
if (!m_loader_mod_list)
|
||||
|
@ -53,12 +53,7 @@ public:
|
||||
|
||||
|
||||
////// Profile management //////
|
||||
void createProfile();
|
||||
std::shared_ptr<ComponentList> getComponentList() const;
|
||||
void reloadProfile();
|
||||
void clearProfile();
|
||||
bool reload() override;
|
||||
|
||||
|
||||
////// Mod Lists //////
|
||||
std::shared_ptr<ModList> loaderModList() const;
|
||||
@ -69,7 +64,7 @@ public:
|
||||
|
||||
|
||||
////// Launch stuff //////
|
||||
shared_qobject_ptr<Task> createUpdateTask() override;
|
||||
shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
|
||||
std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
|
||||
QStringList extraArguments() const override;
|
||||
QStringList verboseDescription(AuthSessionPtr session) override;
|
||||
@ -105,11 +100,6 @@ public:
|
||||
|
||||
virtual JavaVersion getJavaVersion() const;
|
||||
|
||||
// FIXME: remove
|
||||
QString getComponentVersion(const QString &uid) const;
|
||||
// FIXME: remove
|
||||
bool setComponentVersion(const QString &uid, const QString &version);
|
||||
|
||||
signals:
|
||||
void versionReloaded();
|
||||
|
||||
|
45
api/logic/minecraft/MinecraftLoadAndCheck.cpp
Normal file
45
api/logic/minecraft/MinecraftLoadAndCheck.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "MinecraftLoadAndCheck.h"
|
||||
#include "MinecraftInstance.h"
|
||||
#include "ComponentList.h"
|
||||
|
||||
MinecraftLoadAndCheck::MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
|
||||
{
|
||||
}
|
||||
|
||||
void MinecraftLoadAndCheck::executeTask()
|
||||
{
|
||||
// add offline metadata load task
|
||||
auto components = m_inst->getComponentList();
|
||||
components->reload(Net::Mode::Offline);
|
||||
m_task = components->getCurrentTask();
|
||||
|
||||
if(!m_task)
|
||||
{
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
connect(m_task.get(), &Task::succeeded, this, &MinecraftLoadAndCheck::subtaskSucceeded);
|
||||
connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed);
|
||||
connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress);
|
||||
connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus);
|
||||
}
|
||||
|
||||
void MinecraftLoadAndCheck::subtaskSucceeded()
|
||||
{
|
||||
if(isFinished())
|
||||
{
|
||||
qCritical() << "OneSixUpdate: Subtask" << sender() << "succeeded, but work was already done!";
|
||||
return;
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void MinecraftLoadAndCheck::subtaskFailed(QString error)
|
||||
{
|
||||
if(isFinished())
|
||||
{
|
||||
qCritical() << "OneSixUpdate: Subtask" << sender() << "failed, but work was already done!";
|
||||
return;
|
||||
}
|
||||
emitFailed(error);
|
||||
}
|
47
api/logic/minecraft/MinecraftLoadAndCheck.h
Normal file
47
api/logic/minecraft/MinecraftLoadAndCheck.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* Copyright 2013-2017 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include <quazip.h>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
class MinecraftVersion;
|
||||
class MinecraftInstance;
|
||||
|
||||
class MinecraftLoadAndCheck : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MinecraftLoadAndCheck(MinecraftInstance *inst, QObject *parent = 0);
|
||||
void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void subtaskSucceeded();
|
||||
void subtaskFailed(QString error);
|
||||
|
||||
private:
|
||||
MinecraftInstance *m_inst = nullptr;
|
||||
shared_qobject_ptr<Task> m_task;
|
||||
QString m_preFailure;
|
||||
QString m_fail_reason;
|
||||
};
|
||||
|
@ -39,40 +39,24 @@
|
||||
|
||||
OneSixUpdate::OneSixUpdate(MinecraftInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
|
||||
{
|
||||
}
|
||||
|
||||
void OneSixUpdate::executeTask()
|
||||
{
|
||||
m_tasks.clear();
|
||||
// create folders
|
||||
{
|
||||
m_tasks.append(std::make_shared<FoldersTask>(m_inst));
|
||||
}
|
||||
|
||||
// add metadata update tasks, if necessary
|
||||
// add metadata update task if necessary
|
||||
{
|
||||
/*
|
||||
* FIXME: there are some corner cases here that remain unhandled:
|
||||
* what if local load succeeds but remote fails? The version is still usable...
|
||||
* We should not rely on the remote to be there... and prefer local files if it does not respond.
|
||||
*/
|
||||
qDebug() << "Updating patches...";
|
||||
auto profile = m_inst->getComponentList();
|
||||
m_inst->reloadProfile();
|
||||
for(int i = 0; i < profile->rowCount(); i++)
|
||||
auto components = m_inst->getComponentList();
|
||||
components->reload(Net::Mode::Online);
|
||||
auto task = components->getCurrentTask();
|
||||
if(task)
|
||||
{
|
||||
auto patch = profile->versionPatch(i);
|
||||
auto id = patch->getID();
|
||||
auto metadata = patch->getMeta();
|
||||
if(metadata)
|
||||
{
|
||||
metadata->load();
|
||||
auto task = metadata->getCurrentTask();
|
||||
if(task)
|
||||
{
|
||||
qDebug() << "Loading remote meta patch" << id;
|
||||
m_tasks.append(task.unwrap());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Ignoring local patch" << id;
|
||||
}
|
||||
m_tasks.append(task.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,10 +74,7 @@ OneSixUpdate::OneSixUpdate(MinecraftInstance *inst, QObject *parent) : Task(pare
|
||||
{
|
||||
m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst));
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixUpdate::executeTask()
|
||||
{
|
||||
if(!m_preFailure.isEmpty())
|
||||
{
|
||||
emitFailed(m_preFailure);
|
||||
|
@ -192,6 +192,19 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
|
||||
out->mainJar = lib;
|
||||
}
|
||||
|
||||
if (root.contains("requires"))
|
||||
{
|
||||
Meta::parseRequires(root, &out->requires);
|
||||
}
|
||||
if (root.contains("conflicts"))
|
||||
{
|
||||
Meta::parseRequires(root, &out->conflicts);
|
||||
}
|
||||
if (root.contains("volatile"))
|
||||
{
|
||||
out->m_volatile = requireBoolean(root, "volatile");
|
||||
}
|
||||
|
||||
/* removed features that shouldn't be used */
|
||||
if (root.contains("tweakers"))
|
||||
{
|
||||
@ -216,13 +229,9 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
|
||||
return out;
|
||||
}
|
||||
|
||||
QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch, bool saveOrder)
|
||||
QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch)
|
||||
{
|
||||
QJsonObject root;
|
||||
if (saveOrder)
|
||||
{
|
||||
root.insert("order", patch->order);
|
||||
}
|
||||
writeString(root, "name", patch->name);
|
||||
|
||||
writeString(root, "uid", patch->uid);
|
||||
@ -266,6 +275,18 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch
|
||||
}
|
||||
root.insert("mods", array);
|
||||
}
|
||||
if(!patch->requires.empty())
|
||||
{
|
||||
Meta::serializeRequires(root, &patch->requires, "requires");
|
||||
}
|
||||
if(!patch->conflicts.empty())
|
||||
{
|
||||
Meta::serializeRequires(root, &patch->conflicts, "conflicts");
|
||||
}
|
||||
if(patch->m_volatile)
|
||||
{
|
||||
root.insert("volatile", true);
|
||||
}
|
||||
// write the contents to a json document.
|
||||
{
|
||||
QJsonDocument out;
|
||||
|
@ -10,7 +10,7 @@ class OneSixVersionFormat
|
||||
public:
|
||||
// version files / profile patches
|
||||
static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder);
|
||||
static QJsonDocument versionFileToJson(const VersionFilePtr &patch, bool saveOrder);
|
||||
static QJsonDocument versionFileToJson(const VersionFilePtr &patch);
|
||||
|
||||
// libraries
|
||||
static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename);
|
||||
|
@ -1,188 +0,0 @@
|
||||
#include <meta/VersionList.h>
|
||||
#include <meta/Index.h>
|
||||
#include <Env.h>
|
||||
#include "ProfilePatch.h"
|
||||
|
||||
#include "meta/Version.h"
|
||||
#include "VersionFile.h"
|
||||
#include "minecraft/ComponentList.h"
|
||||
|
||||
ProfilePatch::ProfilePatch(std::shared_ptr<Meta::Version> version)
|
||||
:m_metaVersion(version)
|
||||
{
|
||||
}
|
||||
|
||||
ProfilePatch::ProfilePatch(std::shared_ptr<VersionFile> file, const QString& filename)
|
||||
:m_file(file), m_filename(filename)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<Meta::Version> ProfilePatch::getMeta()
|
||||
{
|
||||
return m_metaVersion;
|
||||
}
|
||||
|
||||
void ProfilePatch::applyTo(LaunchProfile* profile)
|
||||
{
|
||||
auto vfile = getVersionFile();
|
||||
if(vfile)
|
||||
{
|
||||
vfile->applyTo(profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
profile->applyProblemSeverity(getProblemSeverity());
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<class VersionFile> ProfilePatch::getVersionFile() const
|
||||
{
|
||||
if(m_metaVersion)
|
||||
{
|
||||
if(!m_metaVersion->isLoaded())
|
||||
{
|
||||
m_metaVersion->load();
|
||||
}
|
||||
return m_metaVersion->data();
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<class Meta::VersionList> ProfilePatch::getVersionList() const
|
||||
{
|
||||
if(m_metaVersion)
|
||||
{
|
||||
return ENV.metadataIndex()->get(m_metaVersion->uid());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ProfilePatch::getOrder()
|
||||
{
|
||||
if(m_orderOverride)
|
||||
return m_order;
|
||||
|
||||
auto vfile = getVersionFile();
|
||||
if(vfile)
|
||||
{
|
||||
return vfile->order;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void ProfilePatch::setOrder(int order)
|
||||
{
|
||||
m_orderOverride = true;
|
||||
m_order = order;
|
||||
}
|
||||
QString ProfilePatch::getID()
|
||||
{
|
||||
if(m_metaVersion)
|
||||
return m_metaVersion->uid();
|
||||
return getVersionFile()->uid;
|
||||
}
|
||||
QString ProfilePatch::getName()
|
||||
{
|
||||
if(m_metaVersion)
|
||||
return m_metaVersion->name();
|
||||
return getVersionFile()->name;
|
||||
}
|
||||
QString ProfilePatch::getVersion()
|
||||
{
|
||||
if(m_metaVersion)
|
||||
return m_metaVersion->version();
|
||||
return getVersionFile()->version;
|
||||
}
|
||||
QString ProfilePatch::getFilename()
|
||||
{
|
||||
return m_filename;
|
||||
}
|
||||
QDateTime ProfilePatch::getReleaseDateTime()
|
||||
{
|
||||
if(m_metaVersion)
|
||||
{
|
||||
return m_metaVersion->time();
|
||||
}
|
||||
return getVersionFile()->releaseTime;
|
||||
}
|
||||
|
||||
bool ProfilePatch::isCustom()
|
||||
{
|
||||
return !m_isVanilla;
|
||||
};
|
||||
|
||||
bool ProfilePatch::isCustomizable()
|
||||
{
|
||||
if(m_metaVersion)
|
||||
{
|
||||
if(getVersionFile())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool ProfilePatch::isRemovable()
|
||||
{
|
||||
return m_isRemovable;
|
||||
}
|
||||
bool ProfilePatch::isRevertible()
|
||||
{
|
||||
return m_isRevertible;
|
||||
}
|
||||
bool ProfilePatch::isMoveable()
|
||||
{
|
||||
return m_isMovable;
|
||||
}
|
||||
bool ProfilePatch::isVersionChangeable()
|
||||
{
|
||||
auto list = getVersionList();
|
||||
if(list)
|
||||
{
|
||||
if(!list->isLoaded())
|
||||
{
|
||||
list->load();
|
||||
}
|
||||
return list->count() != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProfilePatch::setVanilla (bool state)
|
||||
{
|
||||
m_isVanilla = state;
|
||||
}
|
||||
void ProfilePatch::setRemovable (bool state)
|
||||
{
|
||||
m_isRemovable = state;
|
||||
}
|
||||
void ProfilePatch::setRevertible (bool state)
|
||||
{
|
||||
m_isRevertible = state;
|
||||
}
|
||||
void ProfilePatch::setMovable (bool state)
|
||||
{
|
||||
m_isMovable = state;
|
||||
}
|
||||
|
||||
ProblemSeverity ProfilePatch::getProblemSeverity() const
|
||||
{
|
||||
auto file = getVersionFile();
|
||||
if(file)
|
||||
{
|
||||
return file->getProblemSeverity();
|
||||
}
|
||||
return ProblemSeverity::Error;
|
||||
}
|
||||
|
||||
const QList<PatchProblem> ProfilePatch::getProblems() const
|
||||
{
|
||||
auto file = getVersionFile();
|
||||
if(file)
|
||||
{
|
||||
return file->getProblems();
|
||||
}
|
||||
return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QList>
|
||||
#include <QJsonDocument>
|
||||
#include <QDateTime>
|
||||
#include "ProblemProvider.h"
|
||||
|
||||
class ComponentList;
|
||||
class LaunchProfile;
|
||||
namespace Meta
|
||||
{
|
||||
class Version;
|
||||
class VersionList;
|
||||
}
|
||||
class VersionFile;
|
||||
|
||||
class ProfilePatch : public ProblemProvider
|
||||
{
|
||||
public:
|
||||
ProfilePatch(std::shared_ptr<Meta::Version> version);
|
||||
ProfilePatch(std::shared_ptr<VersionFile> file, const QString &filename = QString());
|
||||
|
||||
virtual ~ProfilePatch(){};
|
||||
virtual void applyTo(LaunchProfile *profile);
|
||||
|
||||
virtual bool isMoveable();
|
||||
virtual bool isCustomizable();
|
||||
virtual bool isRevertible();
|
||||
virtual bool isRemovable();
|
||||
virtual bool isCustom();
|
||||
virtual bool isVersionChangeable();
|
||||
|
||||
virtual void setOrder(int order);
|
||||
virtual int getOrder();
|
||||
|
||||
virtual QString getID();
|
||||
virtual QString getName();
|
||||
virtual QString getVersion();
|
||||
virtual std::shared_ptr<Meta::Version> getMeta();
|
||||
virtual QDateTime getReleaseDateTime();
|
||||
|
||||
virtual QString getFilename();
|
||||
|
||||
virtual std::shared_ptr<class VersionFile> getVersionFile() const;
|
||||
virtual std::shared_ptr<class Meta::VersionList> getVersionList() const;
|
||||
|
||||
void setVanilla (bool state);
|
||||
void setRemovable (bool state);
|
||||
void setRevertible (bool state);
|
||||
void setMovable (bool state);
|
||||
|
||||
const QList<PatchProblem> getProblems() const override;
|
||||
ProblemSeverity getProblemSeverity() const override;
|
||||
|
||||
protected:
|
||||
// Properties for UI and version manipulation from UI in general
|
||||
bool m_isMovable = false;
|
||||
bool m_isRevertible = false;
|
||||
bool m_isRemovable = false;
|
||||
bool m_isVanilla = false;
|
||||
|
||||
bool m_orderOverride = false;
|
||||
int m_order = 0;
|
||||
|
||||
std::shared_ptr<Meta::Version> m_metaVersion;
|
||||
std::shared_ptr<VersionFile> m_file;
|
||||
QString m_filename;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ProfilePatch> ProfilePatchPtr;
|
@ -14,38 +14,6 @@ namespace ProfileUtils
|
||||
|
||||
static const int currentOrderFileVersion = 1;
|
||||
|
||||
bool writeOverrideOrders(QString path, const PatchOrder &order)
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj.insert("version", currentOrderFileVersion);
|
||||
QJsonArray orderArray;
|
||||
for(auto str: order)
|
||||
{
|
||||
orderArray.append(str);
|
||||
}
|
||||
obj.insert("order", orderArray);
|
||||
QSaveFile orderFile(path);
|
||||
if (!orderFile.open(QFile::WriteOnly))
|
||||
{
|
||||
qCritical() << "Couldn't open" << orderFile.fileName()
|
||||
<< "for writing:" << orderFile.errorString();
|
||||
return false;
|
||||
}
|
||||
auto data = QJsonDocument(obj).toJson(QJsonDocument::Indented);
|
||||
if(orderFile.write(data) != data.size())
|
||||
{
|
||||
qCritical() << "Couldn't write all the data into" << orderFile.fileName()
|
||||
<< "because:" << orderFile.errorString();
|
||||
return false;
|
||||
}
|
||||
if(!orderFile.commit())
|
||||
{
|
||||
qCritical() << "Couldn't save" << orderFile.fileName()
|
||||
<< "because:" << orderFile.errorString();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readOverrideOrders(QString path, PatchOrder &order)
|
||||
{
|
||||
QFile orderFile(path);
|
||||
@ -154,6 +122,25 @@ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder)
|
||||
return guardedParseJson(doc, fileInfo.completeBaseName(), fileInfo.absoluteFilePath(), requireOrder);
|
||||
}
|
||||
|
||||
bool saveJsonFile(const QJsonDocument doc, const QString & filename)
|
||||
{
|
||||
auto data = doc.toJson();
|
||||
QSaveFile jsonFile(filename);
|
||||
if(!jsonFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
jsonFile.cancelWriting();
|
||||
qWarning() << "Couldn't open" << filename << "for writing";
|
||||
return false;
|
||||
}
|
||||
jsonFile.write(data);
|
||||
if(!jsonFile.commit())
|
||||
{
|
||||
qWarning() << "Couldn't save" << filename;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo)
|
||||
{
|
||||
QFile file(fileInfo.absoluteFilePath());
|
||||
|
@ -16,6 +16,9 @@ bool writeOverrideOrders(QString path, const PatchOrder &order);
|
||||
/// Parse a version file in JSON format
|
||||
VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder);
|
||||
|
||||
/// Save a JSON file (in any format)
|
||||
bool saveJsonFile(const QJsonDocument doc, const QString & filename);
|
||||
|
||||
/// Parse a version file in binary JSON format
|
||||
VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "minecraft/Rule.h"
|
||||
#include "ProblemProvider.h"
|
||||
#include "Library.h"
|
||||
#include <meta/JsonFormat.h>
|
||||
|
||||
class ComponentList;
|
||||
class VersionFile;
|
||||
@ -29,9 +30,6 @@ public: /* data */
|
||||
/// MultiMC: order hint for this version file if no explicit order is set
|
||||
int order = 0;
|
||||
|
||||
/// MultiMC: filename of the file this was loaded from
|
||||
// QString filename;
|
||||
|
||||
/// MultiMC: human readable name of this package
|
||||
QString name;
|
||||
|
||||
@ -77,7 +75,7 @@ public: /* data */
|
||||
/// Mojang: list of libraries to add to the version
|
||||
QList<LibraryPtr> libraries;
|
||||
|
||||
// The main jar (Minecraft version library, normally)
|
||||
/// The main jar (Minecraft version library, normally)
|
||||
LibraryPtr mainJar;
|
||||
|
||||
/// MultiMC: list of attached traits of this version file - used to enable features
|
||||
@ -89,6 +87,21 @@ public: /* data */
|
||||
/// MultiMC: list of mods added to this version
|
||||
QList<LibraryPtr> mods;
|
||||
|
||||
/**
|
||||
* MultiMC: set of packages this depends on
|
||||
* NOTE: this is shared with the meta format!!!
|
||||
*/
|
||||
Meta::RequireSet requires;
|
||||
|
||||
/**
|
||||
* MultiMC: set of packages this conflicts with
|
||||
* NOTE: this is shared with the meta format!!!
|
||||
*/
|
||||
Meta::RequireSet conflicts;
|
||||
|
||||
/// is volatile -- may be removed as soon as it is no longer needed by something else
|
||||
bool m_volatile = false;
|
||||
|
||||
public:
|
||||
// Mojang: DEPRECATED list of 'downloads' - client jar, server jar, windows server exe, maybe more.
|
||||
QMap <QString, std::shared_ptr<MojangDownloadInfo>> mojangDownloads;
|
||||
|
@ -25,6 +25,11 @@ void ModMinecraftJar::executeTask()
|
||||
{
|
||||
auto m_inst = std::dynamic_pointer_cast<MinecraftInstance>(m_parent->instance());
|
||||
|
||||
if(!m_inst->getJarMods().size())
|
||||
{
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
// nuke obsolete stripped jar(s) if needed
|
||||
if(!FS::ensureFolderPathExists(m_inst->binRoot()))
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ bool LegacyInstance::shouldUseCustomBaseJar() const
|
||||
}
|
||||
|
||||
|
||||
shared_qobject_ptr<Task> LegacyInstance::createUpdateTask()
|
||||
shared_qobject_ptr<Task> LegacyInstance::createUpdateTask(Net::Mode)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ public:
|
||||
};
|
||||
|
||||
virtual bool shouldUpdate() const;
|
||||
virtual shared_qobject_ptr<Task> createUpdateTask() override;
|
||||
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
|
||||
|
||||
virtual QString typeName() const override;
|
||||
|
||||
|
@ -67,69 +67,70 @@ void LegacyUpgradeTask::copyFinished()
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
instanceSettings->registerSetting("InstanceType", "Legacy");
|
||||
instanceSettings->set("InstanceType", "OneSix");
|
||||
std::shared_ptr<MinecraftInstance> inst(new MinecraftInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||
inst->setName(m_newName);
|
||||
inst->init();
|
||||
|
||||
QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId());
|
||||
if(preferredVersionNumber.isNull())
|
||||
// NOTE: this scope ensures the instance is fully saved before we emitSucceeded
|
||||
{
|
||||
// try to decide version based on the jar(s?)
|
||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar());
|
||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
inst.setName(m_newName);
|
||||
inst.init();
|
||||
|
||||
QString preferredVersionNumber = decideVersion(legacyInst->currentVersionId(), legacyInst->intendedVersionId());
|
||||
if(preferredVersionNumber.isNull())
|
||||
{
|
||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar());
|
||||
// try to decide version based on the jar(s?)
|
||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->baseJar());
|
||||
if(preferredVersionNumber.isNull())
|
||||
{
|
||||
emitFailed(tr("Could not decide Minecraft version."));
|
||||
return;
|
||||
preferredVersionNumber = classparser::GetMinecraftJarVersion(legacyInst->runnableJar());
|
||||
if(preferredVersionNumber.isNull())
|
||||
{
|
||||
emitFailed(tr("Could not decide Minecraft version."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inst->setComponentVersion("net.minecraft", preferredVersionNumber);
|
||||
auto components = inst.getComponentList();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", preferredVersionNumber, true);
|
||||
|
||||
// BUG: reloadProfile should not be necessary, but setComponentVersion voids the profile created by init()!
|
||||
inst->reloadProfile();
|
||||
auto profile = inst->getComponentList();
|
||||
|
||||
if(legacyInst->shouldUseCustomBaseJar())
|
||||
{
|
||||
QString jarPath = legacyInst->customBaseJar();
|
||||
qDebug() << "Base jar is custom! : " << jarPath;
|
||||
// FIXME: handle case when the jar is unreadable?
|
||||
// TODO: check the hash, if it's the same as the upstream jar, do not do this
|
||||
profile->installCustomJar(jarPath);
|
||||
}
|
||||
|
||||
auto jarMods = legacyInst->getJarMods();
|
||||
for(auto & jarMod: jarMods)
|
||||
{
|
||||
QString modPath = jarMod.filename().absoluteFilePath();
|
||||
qDebug() << "jarMod: " << modPath;
|
||||
profile->installJarMods({modPath});
|
||||
}
|
||||
|
||||
// remove all the extra garbage we no longer need
|
||||
auto removeAll = [&](const QString &root, const QStringList &things)
|
||||
{
|
||||
for(auto &thing : things)
|
||||
if(legacyInst->shouldUseCustomBaseJar())
|
||||
{
|
||||
auto removePath = FS::PathCombine(root, thing);
|
||||
QFileInfo stat(removePath);
|
||||
if(stat.isDir())
|
||||
{
|
||||
FS::deletePath(removePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
QFile::remove(removePath);
|
||||
}
|
||||
QString jarPath = legacyInst->customBaseJar();
|
||||
qDebug() << "Base jar is custom! : " << jarPath;
|
||||
// FIXME: handle case when the jar is unreadable?
|
||||
// TODO: check the hash, if it's the same as the upstream jar, do not do this
|
||||
components->installCustomJar(jarPath);
|
||||
}
|
||||
};
|
||||
QStringList rootRemovables = {"modlist", "version", "instMods"};
|
||||
QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"};
|
||||
removeAll(inst->instanceRoot(), rootRemovables);
|
||||
removeAll(inst->minecraftRoot(), mcRemovables);
|
||||
|
||||
auto jarMods = legacyInst->getJarMods();
|
||||
for(auto & jarMod: jarMods)
|
||||
{
|
||||
QString modPath = jarMod.filename().absoluteFilePath();
|
||||
qDebug() << "jarMod: " << modPath;
|
||||
components->installJarMods({modPath});
|
||||
}
|
||||
|
||||
// remove all the extra garbage we no longer need
|
||||
auto removeAll = [&](const QString &root, const QStringList &things)
|
||||
{
|
||||
for(auto &thing : things)
|
||||
{
|
||||
auto removePath = FS::PathCombine(root, thing);
|
||||
QFileInfo stat(removePath);
|
||||
if(stat.isDir())
|
||||
{
|
||||
FS::deletePath(removePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
QFile::remove(removePath);
|
||||
}
|
||||
}
|
||||
};
|
||||
QStringList rootRemovables = {"modlist", "version", "instMods"};
|
||||
QStringList mcRemovables = {"bin", "MultiMCLauncher.jar", "icon.png"};
|
||||
removeAll(inst.instanceRoot(), rootRemovables);
|
||||
removeAll(inst.minecraftRoot(), mcRemovables);
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ void FMLLibrariesTask::executeTask()
|
||||
MinecraftInstance *inst = (MinecraftInstance *)m_inst;
|
||||
auto components = inst->getComponentList();
|
||||
auto profile = components->getProfile();
|
||||
bool forge_present = false;
|
||||
|
||||
if (!profile->hasTrait("legacyFML"))
|
||||
{
|
||||
@ -23,7 +22,7 @@ void FMLLibrariesTask::executeTask()
|
||||
return;
|
||||
}
|
||||
|
||||
QString version = inst->getComponentVersion("net.minecraft");
|
||||
QString version = components->getComponentVersion("net.minecraft");
|
||||
auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
|
||||
if (!fmlLibsMapping.contains(version))
|
||||
{
|
||||
@ -35,9 +34,7 @@ void FMLLibrariesTask::executeTask()
|
||||
|
||||
// determine if we need some libs for FML or forge
|
||||
setStatus(tr("Checking for FML libraries..."));
|
||||
forge_present = (components->versionPatch("net.minecraftforge") != nullptr);
|
||||
// we don't...
|
||||
if (!forge_present)
|
||||
if(!components->getComponent("net.minecraftforge"))
|
||||
{
|
||||
emitSucceeded();
|
||||
return;
|
||||
|
@ -13,12 +13,6 @@ void LibrariesTask::executeTask()
|
||||
setStatus(tr("Getting the library files from Mojang..."));
|
||||
qDebug() << m_inst->name() << ": downloading libraries";
|
||||
MinecraftInstance *inst = (MinecraftInstance *)m_inst;
|
||||
inst->reloadProfile();
|
||||
if(inst->hasVersionBroken())
|
||||
{
|
||||
emitFailed(tr("Failed to load the version description files - check the instance for errors."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a list of URLs that will need to be downloaded.
|
||||
auto components = inst->getComponentList();
|
||||
|
10
api/logic/net/Mode.h
Normal file
10
api/logic/net/Mode.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Net
|
||||
{
|
||||
enum class Mode
|
||||
{
|
||||
Offline,
|
||||
Online
|
||||
};
|
||||
}
|
@ -192,7 +192,7 @@ void LaunchController::launchInstance()
|
||||
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
||||
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
|
||||
|
||||
if(!m_instance->reload())
|
||||
if(!m_instance->reloadSettings())
|
||||
{
|
||||
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't load the instance profile."));
|
||||
emitFailed(tr("Couldn't load the instance profile."));
|
||||
|
@ -1303,7 +1303,7 @@ void MainWindow::finalizeInstance(InstancePtr inst)
|
||||
if (MMC->accounts()->anyAccountIsValid())
|
||||
{
|
||||
ProgressDialog loadDialog(this);
|
||||
auto update = inst->createUpdateTask();
|
||||
auto update = inst->createUpdateTask(Net::Mode::Online);
|
||||
connect(update.get(), &Task::failed, [this](QString reason)
|
||||
{
|
||||
QString error = QString("Instance load failed: %1").arg(reason);
|
||||
|
@ -71,7 +71,7 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString
|
||||
}
|
||||
else
|
||||
{
|
||||
vlist->load();
|
||||
vlist->load(Net::Mode::Online);
|
||||
auto task = vlist->getLoadTask();
|
||||
if(vlist->isLoaded())
|
||||
{
|
||||
|
@ -106,15 +106,15 @@ bool CoreModFolderPage::shouldDisplay() const
|
||||
auto version = inst->getComponentList();
|
||||
if (!version)
|
||||
return true;
|
||||
if(!version->versionPatch("net.minecraftforge"))
|
||||
if(!version->getComponent("net.minecraftforge"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(!version->versionPatch("net.minecraft"))
|
||||
if(!version->getComponent("net.minecraft"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(version->versionPatch("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate)
|
||||
if(version->getComponent("net.minecraft")->getReleaseDateTime() < g_VersionFilterData.legacyCutoffDate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -103,10 +103,10 @@ VersionPage::VersionPage(MinecraftInstance *inst, QWidget *parent)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->tabWidget->tabBar()->hide();
|
||||
m_profile = m_inst->getComponentList();
|
||||
|
||||
reloadComponentList();
|
||||
|
||||
m_profile = m_inst->getComponentList();
|
||||
if (m_profile)
|
||||
{
|
||||
auto proxy = new IconProxy(ui->packageView);
|
||||
@ -142,7 +142,7 @@ void VersionPage::packageCurrent(const QModelIndex ¤t, const QModelIndex &
|
||||
return;
|
||||
}
|
||||
int row = current.row();
|
||||
auto patch = m_profile->versionPatch(row);
|
||||
auto patch = m_profile->getComponent(row);
|
||||
auto severity = patch->getProblemSeverity();
|
||||
switch(severity)
|
||||
{
|
||||
@ -196,7 +196,7 @@ bool VersionPage::reloadComponentList()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_inst->reloadProfile();
|
||||
m_profile->reload(Net::Mode::Online);
|
||||
return true;
|
||||
}
|
||||
catch (Exception &e)
|
||||
@ -262,19 +262,6 @@ void VersionPage::on_jarBtn_clicked()
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void VersionPage::on_resetOrderBtn_clicked()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_profile->resetOrder();
|
||||
}
|
||||
catch (Exception &e)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||
}
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
void VersionPage::on_moveUpBtn_clicked()
|
||||
{
|
||||
try
|
||||
@ -308,7 +295,7 @@ void VersionPage::on_changeVersionBtn_clicked()
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto patch = m_profile->versionPatch(versionRow);
|
||||
auto patch = m_profile->getComponent(versionRow);
|
||||
auto name = patch->getName();
|
||||
auto list = patch->getVersionList();
|
||||
if(!list)
|
||||
@ -331,8 +318,10 @@ void VersionPage::on_changeVersionBtn_clicked()
|
||||
}
|
||||
|
||||
qDebug() << "Change" << uid << "to" << vselect.selectedVersion()->descriptor();
|
||||
bool important = false;
|
||||
if(uid == "net.minecraft")
|
||||
{
|
||||
important = true;
|
||||
if (!m_profile->isVanilla())
|
||||
{
|
||||
auto result = CustomMessageBox::selectable(
|
||||
@ -348,14 +337,14 @@ void VersionPage::on_changeVersionBtn_clicked()
|
||||
reloadComponentList();
|
||||
}
|
||||
}
|
||||
m_inst->setComponentVersion(uid, vselect.selectedVersion()->descriptor());
|
||||
m_profile->setComponentVersion(uid, vselect.selectedVersion()->descriptor(), important);
|
||||
doUpdate();
|
||||
m_container->refreshContainer();
|
||||
}
|
||||
|
||||
int VersionPage::doUpdate()
|
||||
{
|
||||
auto updateTask = m_inst->createUpdateTask();
|
||||
auto updateTask = m_inst->createUpdateTask(Net::Mode::Online);
|
||||
if (!updateTask)
|
||||
{
|
||||
return 1;
|
||||
@ -376,20 +365,58 @@ void VersionPage::on_forgeBtn_clicked()
|
||||
return;
|
||||
}
|
||||
VersionSelectDialog vselect(vlist.get(), tr("Select Forge version"), this);
|
||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_inst->getComponentVersion("net.minecraft"));
|
||||
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + m_inst->getComponentVersion("net.minecraft"));
|
||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
||||
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + m_profile->getComponentVersion("net.minecraft"));
|
||||
vselect.setEmptyErrorString(tr("Couldn't load or download the Forge version lists!"));
|
||||
if (vselect.exec() && vselect.selectedVersion())
|
||||
{
|
||||
auto vsn = vselect.selectedVersion();
|
||||
m_inst->setComponentVersion("net.minecraftforge", vsn->descriptor());
|
||||
m_profile->reload();
|
||||
m_profile->setComponentVersion("net.minecraftforge", vsn->descriptor());
|
||||
m_profile->resolve(Net::Mode::Online);
|
||||
// m_profile->installVersion();
|
||||
preselect(m_profile->rowCount(QModelIndex())-1);
|
||||
m_container->refreshContainer();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use something like this... except the final decision of what to show has to be deferred until the lists are known
|
||||
/*
|
||||
void VersionPage::on_liteloaderBtn_clicked()
|
||||
{
|
||||
QString uid = "com.mumfrey.liteloader";
|
||||
auto vlist = ENV.metadataIndex()->get(uid);
|
||||
if(!vlist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
VersionSelectDialog vselect(vlist.get(), tr("Select %1 version").arg(vlist->name()), this);
|
||||
auto parentUid = vlist->parentUid();
|
||||
if(!parentUid.isEmpty())
|
||||
{
|
||||
auto parentvlist = ENV.metadataIndex()->get(parentUid);
|
||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion(parentUid));
|
||||
vselect.setEmptyString(
|
||||
tr("No %1 versions are currently available for %2 %3")
|
||||
.arg(vlist->name())
|
||||
.arg(parentvlist->name())
|
||||
.arg(m_profile->getComponentVersion(parentUid)));
|
||||
}
|
||||
else
|
||||
{
|
||||
vselect.setEmptyString(tr("No %1 versions are currently available"));
|
||||
}
|
||||
vselect.setEmptyErrorString(tr("Couldn't load or download the %1 version lists!").arg(vlist->name()));
|
||||
if (vselect.exec() && vselect.selectedVersion())
|
||||
{
|
||||
auto vsn = vselect.selectedVersion();
|
||||
m_profile->setComponentVersion(uid, vsn->descriptor());
|
||||
m_profile->resolve();
|
||||
preselect(m_profile->rowCount(QModelIndex())-1);
|
||||
m_container->refreshContainer();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void VersionPage::on_liteloaderBtn_clicked()
|
||||
{
|
||||
auto vlist = ENV.metadataIndex()->get("com.mumfrey.liteloader");
|
||||
@ -398,14 +425,14 @@ void VersionPage::on_liteloaderBtn_clicked()
|
||||
return;
|
||||
}
|
||||
VersionSelectDialog vselect(vlist.get(), tr("Select LiteLoader version"), this);
|
||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_inst->getComponentVersion("net.minecraft"));
|
||||
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") + m_inst->getComponentVersion("net.minecraft"));
|
||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
||||
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") + m_profile->getComponentVersion("net.minecraft"));
|
||||
vselect.setEmptyErrorString(tr("Couldn't load or download the LiteLoader version lists!"));
|
||||
if (vselect.exec() && vselect.selectedVersion())
|
||||
{
|
||||
auto vsn = vselect.selectedVersion();
|
||||
m_inst->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor());
|
||||
m_profile->reload();
|
||||
m_profile->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor());
|
||||
m_profile->resolve(Net::Mode::Online);
|
||||
// m_profile->installVersion(vselect.selectedVersion());
|
||||
preselect(m_profile->rowCount(QModelIndex())-1);
|
||||
m_container->refreshContainer();
|
||||
@ -441,7 +468,7 @@ void VersionPage::updateButtons(int row)
|
||||
{
|
||||
if(row == -1)
|
||||
row = currentRow();
|
||||
auto patch = m_profile->versionPatch(row);
|
||||
auto patch = m_profile->getComponent(row);
|
||||
if (!patch)
|
||||
{
|
||||
ui->removeBtn->setDisabled(true);
|
||||
@ -470,14 +497,14 @@ void VersionPage::onGameUpdateError(QString error)
|
||||
QMessageBox::Warning)->show();
|
||||
}
|
||||
|
||||
ProfilePatchPtr VersionPage::current()
|
||||
ComponentPtr VersionPage::current()
|
||||
{
|
||||
auto row = currentRow();
|
||||
if(row < 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return m_profile->versionPatch(row);
|
||||
return m_profile->getComponent(row);
|
||||
}
|
||||
|
||||
int VersionPage::currentRow()
|
||||
@ -496,7 +523,7 @@ void VersionPage::on_customizeBtn_clicked()
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto patch = m_profile->versionPatch(version);
|
||||
auto patch = m_profile->getComponent(version);
|
||||
if(!patch->getVersionFile())
|
||||
{
|
||||
// TODO: wait for the update task to finish here...
|
||||
|
@ -53,7 +53,6 @@ private slots:
|
||||
void on_liteloaderBtn_clicked();
|
||||
void on_reloadBtn_clicked();
|
||||
void on_removeBtn_clicked();
|
||||
void on_resetOrderBtn_clicked();
|
||||
void on_moveUpBtn_clicked();
|
||||
void on_moveDownBtn_clicked();
|
||||
void on_jarmodBtn_clicked();
|
||||
@ -68,7 +67,7 @@ private slots:
|
||||
void on_changeVersionBtn_clicked();
|
||||
|
||||
private:
|
||||
ProfilePatchPtr current();
|
||||
ComponentPtr current();
|
||||
int currentRow();
|
||||
void updateButtons(int row = -1);
|
||||
void preselect(int row = 0);
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>693</width>
|
||||
<height>788</height>
|
||||
<height>833</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@ -217,16 +217,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetOrderBtn">
|
||||
<property name="toolTip">
|
||||
<string>Reset apply order of packages.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reset order</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="reloadBtn">
|
||||
<property name="toolTip">
|
||||
@ -300,7 +290,6 @@
|
||||
<tabstop>liteloaderBtn</tabstop>
|
||||
<tabstop>modBtn</tabstop>
|
||||
<tabstop>jarmodBtn</tabstop>
|
||||
<tabstop>resetOrderBtn</tabstop>
|
||||
<tabstop>reloadBtn</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
@ -38,10 +38,17 @@ static QString formatRequires(const VersionPtr &version)
|
||||
auto iter = reqs.begin();
|
||||
while (iter != reqs.end())
|
||||
{
|
||||
auto &uid = iter.key();
|
||||
auto &version = iter.value();
|
||||
auto &uid = iter->uid;
|
||||
auto &version = iter->equalsVersion;
|
||||
const QString readable = ENV.metadataIndex()->hasUid(uid) ? ENV.metadataIndex()->get(uid)->humanReadable() : uid;
|
||||
lines.append(QString("%1 (%2)").arg(readable, version));
|
||||
if(!version.isEmpty())
|
||||
{
|
||||
lines.append(QString("%1 (%2)").arg(readable, version));
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.append(QString("%1").arg(readable));
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
return lines.join('\n');
|
||||
@ -95,7 +102,7 @@ QIcon PackagesPage::icon() const
|
||||
|
||||
void PackagesPage::on_refreshIndexBtn_clicked()
|
||||
{
|
||||
ENV.metadataIndex()->load();
|
||||
ENV.metadataIndex()->load(Net::Mode::Online);
|
||||
}
|
||||
void PackagesPage::on_refreshFileBtn_clicked()
|
||||
{
|
||||
@ -104,7 +111,7 @@ void PackagesPage::on_refreshFileBtn_clicked()
|
||||
{
|
||||
return;
|
||||
}
|
||||
list->load();
|
||||
list->load(Net::Mode::Online);
|
||||
}
|
||||
void PackagesPage::on_refreshVersionBtn_clicked()
|
||||
{
|
||||
@ -113,7 +120,7 @@ void PackagesPage::on_refreshVersionBtn_clicked()
|
||||
{
|
||||
return;
|
||||
}
|
||||
version->load();
|
||||
version->load(Net::Mode::Online);
|
||||
}
|
||||
|
||||
void PackagesPage::on_fileSearchEdit_textChanged(const QString &search)
|
||||
@ -156,7 +163,7 @@ void PackagesPage::updateCurrentVersionList(const QModelIndex &index)
|
||||
ui->fileName->setText(list->name());
|
||||
m_versionProxy->setSourceModel(list.get());
|
||||
ui->refreshFileBtn->setText(tr("Refresh %1").arg(list->humanReadable()));
|
||||
list->load();
|
||||
list->load(Net::Mode::Offline);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -213,5 +220,5 @@ void PackagesPage::updateVersion()
|
||||
|
||||
void PackagesPage::opened()
|
||||
{
|
||||
ENV.metadataIndex()->load();
|
||||
ENV.metadataIndex()->load(Net::Mode::Offline);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user