Merge branch 'feature/meta' into develop

This commit is contained in:
Petr Mrázek 2017-04-07 00:27:24 +02:00
commit 795889d934
115 changed files with 1854 additions and 6360 deletions

View File

@ -109,4 +109,3 @@ add_subdirectory(api/logic)
add_subdirectory(api/gui)
add_subdirectory(application)
add_subdirectory(wonkoclient)

View File

@ -23,7 +23,6 @@
#include "settings/Setting.h"
#include "settings/OverrideSetting.h"
#include "minecraft/MinecraftVersionList.h"
#include "FileSystem.h"
#include "Commandline.h"

View File

@ -30,7 +30,7 @@ BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
return BaseVersionPtr();
}
BaseVersionPtr BaseVersionList::getLatestStable() const
BaseVersionPtr BaseVersionList::getRecommended() const
{
if (count() <= 0)
return BaseVersionPtr();
@ -38,11 +38,6 @@ BaseVersionPtr BaseVersionList::getLatestStable() const
return at(0);
}
BaseVersionPtr BaseVersionList::getRecommended() const
{
return getLatestStable();
}
QVariant BaseVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
@ -93,7 +88,7 @@ QHash<int, QByteArray> BaseVersionList::roleNames() const
QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
roles.insert(VersionRole, "version");
roles.insert(VersionIdRole, "versionId");
roles.insert(ParentGameVersionRole, "parentGameVersion");
roles.insert(ParentVersionRole, "parentGameVersion");
roles.insert(RecommendedRole, "recommended");
roles.insert(LatestRole, "latest");
roles.insert(TypeRole, "type");

View File

@ -22,6 +22,7 @@
#include "BaseVersion.h"
#include "tasks/Task.h"
#include "multimc_logic_export.h"
#include "QObjectPtr.h"
/*!
* \brief Class that each instance type's version list derives from.
@ -44,7 +45,7 @@ public:
VersionPointerRole = Qt::UserRole,
VersionRole,
VersionIdRole,
ParentGameVersionRole,
ParentVersionRole,
RecommendedRole,
LatestRole,
TypeRole,
@ -63,7 +64,7 @@ public:
* The task returned by this function should reset the model when it's done.
* \return A pointer to a task that reloads the version list.
*/
virtual Task *getLoadTask() = 0;
virtual shared_qobject_ptr<Task> getLoadTask() = 0;
//! Checks whether or not the list is loaded. If this returns false, the list should be
//loaded.
@ -76,27 +77,22 @@ public:
virtual int count() const = 0;
//////// List Model Functions ////////
virtual QVariant data(const QModelIndex &index, int role) const;
virtual int rowCount(const QModelIndex &parent) const;
virtual int columnCount(const QModelIndex &parent) const;
virtual QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
virtual QHash<int, QByteArray> roleNames() const;
//! which roles are provided by this version list?
virtual RoleList providesRoles() const;
/*!
* \brief Finds a version by its descriptor.
* \param The descriptor of the version to find.
* \param descriptor The descriptor of the version to find.
* \return A const pointer to the version with the given descriptor. NULL if
* one doesn't exist.
*/
virtual BaseVersionPtr findVersion(const QString &descriptor);
/*!
* \brief Gets the latest stable version from this list
*/
virtual BaseVersionPtr getLatestStable() const;
/*!
* \brief Gets the recommended version from this list
* If the list doesn't support recommended versions, this works exactly as getLatestStable

View File

@ -252,10 +252,6 @@ set(MINECRAFT_SOURCES
minecraft/JarMod.h
minecraft/MinecraftInstance.cpp
minecraft/MinecraftInstance.h
minecraft/MinecraftVersion.cpp
minecraft/MinecraftVersion.h
minecraft/MinecraftVersionList.cpp
minecraft/MinecraftVersionList.h
minecraft/Rule.cpp
minecraft/Rule.h
minecraft/OpSys.cpp
@ -271,6 +267,7 @@ set(MINECRAFT_SOURCES
minecraft/VersionBuildError.h
minecraft/VersionFile.cpp
minecraft/VersionFile.h
minecraft/ProfilePatch.cpp
minecraft/ProfilePatch.h
minecraft/VersionFilterData.h
minecraft/VersionFilterData.cpp
@ -300,22 +297,10 @@ set(MINECRAFT_SOURCES
minecraft/AssetsUtils.cpp
# Forge and all things forge related
minecraft/forge/ForgeVersion.h
minecraft/forge/ForgeVersion.cpp
minecraft/forge/ForgeVersionList.h
minecraft/forge/ForgeVersionList.cpp
minecraft/forge/ForgeXzDownload.h
minecraft/forge/ForgeXzDownload.cpp
minecraft/forge/LegacyForge.h
minecraft/forge/LegacyForge.cpp
minecraft/forge/ForgeInstaller.h
minecraft/forge/ForgeInstaller.cpp
# Liteloader and related things
minecraft/liteloader/LiteLoaderInstaller.h
minecraft/liteloader/LiteLoaderInstaller.cpp
minecraft/liteloader/LiteLoaderVersionList.h
minecraft/liteloader/LiteLoaderVersionList.cpp
# Skin upload utilities
minecraft/SkinUpload.cpp
minecraft/SkinUpload.h
)
@ -430,32 +415,22 @@ set(TOOLS_SOURCES
tools/MCEditTool.h
)
set(WONKO_SOURCES
# Wonko
wonko/tasks/BaseWonkoEntityRemoteLoadTask.cpp
wonko/tasks/BaseWonkoEntityRemoteLoadTask.h
wonko/tasks/BaseWonkoEntityLocalLoadTask.cpp
wonko/tasks/BaseWonkoEntityLocalLoadTask.h
wonko/format/WonkoFormatV1.cpp
wonko/format/WonkoFormatV1.h
wonko/format/WonkoFormat.cpp
wonko/format/WonkoFormat.h
wonko/BaseWonkoEntity.cpp
wonko/BaseWonkoEntity.h
wonko/WonkoVersionList.cpp
wonko/WonkoVersionList.h
wonko/WonkoVersion.cpp
wonko/WonkoVersion.h
wonko/WonkoIndex.cpp
wonko/WonkoIndex.h
wonko/WonkoUtil.cpp
wonko/WonkoUtil.h
wonko/WonkoReference.cpp
wonko/WonkoReference.h
set(META_SOURCES
# Metadata sources
meta/JsonFormat.cpp
meta/JsonFormat.h
meta/BaseEntity.cpp
meta/BaseEntity.h
meta/VersionList.cpp
meta/VersionList.h
meta/Version.cpp
meta/Version.h
meta/Index.cpp
meta/Index.h
)
add_unit_test(WonkoIndex
SOURCES wonko/WonkoIndex_test.cpp
add_unit_test(Index
SOURCES meta/Index_test.cpp
LIBS MultiMC_logic
)
@ -480,7 +455,7 @@ set(LOGIC_SOURCES
${JAVA_SOURCES}
${TRANSLATIONS_SOURCES}
${TOOLS_SOURCES}
${WONKO_SOURCES}
${META_SOURCES}
${ICONS_SOURCES}
)

View File

@ -7,7 +7,7 @@
#include <QNetworkAccessManager>
#include <QDebug>
#include "tasks/Task.h"
#include "wonko/WonkoIndex.h"
#include "meta/Index.h"
#include <QDebug>
@ -18,8 +18,7 @@ public:
shared_qobject_ptr<HttpMetaCache> m_metacache;
std::shared_ptr<IIconList> m_iconlist;
QMap<QString, std::shared_ptr<BaseVersionList>> m_versionLists;
shared_qobject_ptr<WonkoIndex> m_wonkoIndex;
QString m_wonkoRootUrl;
shared_qobject_ptr<Meta::Index> m_metadataIndex;
};
static Env * instance;
@ -99,13 +98,13 @@ void Env::registerVersionList(QString name, std::shared_ptr< BaseVersionList > v
d->m_versionLists[name] = vlist;
}
shared_qobject_ptr<WonkoIndex> Env::wonkoIndex()
shared_qobject_ptr<Meta::Index> Env::metadataIndex()
{
if (!d->m_wonkoIndex)
if (!d->m_metadataIndex)
{
d->m_wonkoIndex.reset(new WonkoIndex());
d->m_metadataIndex.reset(new Meta::Index());
}
return d->m_wonkoIndex;
return d->m_metadataIndex;
}
@ -125,7 +124,7 @@ void Env::initHttpMetaCache()
m_metacache->addBase("root", QDir::currentPath());
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("wonko", QDir("cache/wonko").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->Load();
}
@ -191,14 +190,4 @@ void Env::updateProxySettings(QString proxyTypeStr, QString addr, int port, QStr
qDebug() << proxyDesc;
}
QString Env::wonkoRootUrl() const
{
return d->m_wonkoRootUrl;
}
void Env::setWonkoRootUrl(const QString& url)
{
d->m_wonkoRootUrl = url;
}
#include "Env.moc"
#include "Env.moc"

View File

@ -13,7 +13,11 @@ class QNetworkAccessManager;
class HttpMetaCache;
class BaseVersionList;
class BaseVersion;
class WonkoIndex;
namespace Meta
{
class Index;
}
#if defined(ENV)
#undef ENV
@ -53,10 +57,7 @@ public:
void registerIconList(std::shared_ptr<IIconList> iconlist);
shared_qobject_ptr<WonkoIndex> wonkoIndex();
QString wonkoRootUrl() const;
void setWonkoRootUrl(const QString &url);
shared_qobject_ptr<Meta::Index> metadataIndex();
protected:
Private * d;

View File

@ -4,7 +4,6 @@
#include "FileSystem.h"
//FIXME: remove this
#include "minecraft/MinecraftVersion.h"
#include "minecraft/onesix/OneSixInstance.h"
InstanceCreationTask::InstanceCreationTask(SettingsObjectPtr settings, BaseInstanceProvider* target, BaseVersionPtr version,
@ -21,12 +20,14 @@ InstanceCreationTask::InstanceCreationTask(SettingsObjectPtr settings, BaseInsta
void InstanceCreationTask::executeTask()
{
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
/*
auto minecraftVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_version);
if(!minecraftVersion)
{
emitFailed(tr("The supplied version is not a Minecraft version."));
return ;
}
*/
QString stagingPath = m_target->getStagedInstancePath();
QDir rootDir(stagingPath);
@ -34,7 +35,6 @@ void InstanceCreationTask::executeTask()
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(stagingPath, "instance.cfg"));
instanceSettings->registerSetting("InstanceType", "Legacy");
auto mcVer = std::dynamic_pointer_cast<MinecraftVersion>(m_version);
instanceSettings->set("InstanceType", "OneSix");
InstancePtr inst(new OneSixInstance(m_globalSettings, instanceSettings, stagingPath));
inst->setIntendedVersionId(m_version->descriptor());

View File

@ -0,0 +1,61 @@
#pragma once
enum class ProblemSeverity
{
None,
Warning,
Error
};
class PatchProblem
{
public:
PatchProblem(ProblemSeverity severity, const QString & description)
{
m_severity = severity;
m_description = description;
}
const QString & getDescription() const
{
return m_description;
}
const ProblemSeverity getSeverity() const
{
return m_severity;
}
private:
ProblemSeverity m_severity;
QString m_description;
};
class ProblemProvider
{
public:
virtual const QList<PatchProblem> getProblems() = 0;
virtual ProblemSeverity getProblemSeverity() = 0;
};
class ProblemContainer : public ProblemProvider
{
public:
const QList<PatchProblem> getProblems() override
{
return m_problems;
}
ProblemSeverity getProblemSeverity() override
{
return m_problemSeverity;
}
virtual void addProblem(ProblemSeverity severity, const QString &description)
{
if(severity > m_problemSeverity)
{
m_problemSeverity = severity;
}
m_problems.append(PatchProblem(severity, description));
}
private:
QList<PatchProblem> m_problems;
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
};

View File

@ -75,6 +75,7 @@ void Version::parse()
{
m_sections.clear();
// FIXME: this is bad. versions can contain a lot more separators...
QStringList parts = m_string.split('.');
for (const auto part : parts)
@ -82,59 +83,3 @@ void Version::parse()
m_sections.append(Section(part));
}
}
bool versionIsInInterval(const QString &version, const QString &interval)
{
return versionIsInInterval(Version(version), interval);
}
bool versionIsInInterval(const Version &version, const QString &interval)
{
if (interval.isEmpty() || version.toString() == interval)
{
return true;
}
// Interval notation is used
QRegularExpression exp(
"(?<start>[\\[\\]\\(\\)])(?<bottom>.*?)(,(?<top>.*?))?(?<end>[\\[\\]\\(\\)]),?");
QRegularExpressionMatch match = exp.match(interval);
if (match.hasMatch())
{
const QChar start = match.captured("start").at(0);
const QChar end = match.captured("end").at(0);
const QString bottom = match.captured("bottom");
const QString top = match.captured("top");
// check if in range (bottom)
if (!bottom.isEmpty())
{
const auto bottomVersion = Version(bottom);
if ((start == '[') && !(version >= bottomVersion))
{
return false;
}
else if ((start == '(') && !(version > bottomVersion))
{
return false;
}
}
// check if in range (top)
if (!top.isEmpty())
{
const auto topVersion = Version(top);
if ((end == ']') && !(version <= topVersion))
{
return false;
}
else if ((end == ')') && !(version < topVersion))
{
return false;
}
}
return true;
}
return false;
}

View File

@ -104,7 +104,3 @@ private:
void parse();
};
MULTIMC_LOGIC_EXPORT bool versionIsInInterval(const QString &version, const QString &interval);
MULTIMC_LOGIC_EXPORT bool versionIsInInterval(const Version &version, const QString &interval);

View File

@ -60,44 +60,6 @@ private slots:
}
void test_versionIsInInterval_data()
{
QTest::addColumn<QString>("version");
QTest::addColumn<QString>("interval");
QTest::addColumn<bool>("result");
QTest::newRow("empty, true") << "1.2.3" << "" << true;
QTest::newRow("one version, true") << "1.2.3" << "1.2.3" << true;
QTest::newRow("one version, false") << "1.2.3" << "1.2.2" << false;
QTest::newRow("one version inclusive <-> infinity, true") << "1.2.3" << "[1.2.3,)" << true;
QTest::newRow("one version exclusive <-> infinity, true") << "1.2.3" << "(1.2.2,)" << true;
QTest::newRow("one version inclusive <-> infitity, false") << "1.2.3" << "[1.2.4,)" << false;
QTest::newRow("one version exclusive <-> infinity, false") << "1.2.3" << "(1.2.3,)" << false;
QTest::newRow("infinity <-> one version inclusive, true") << "1.2.3" << "(,1.2.3]" << true;
QTest::newRow("infinity <-> one version exclusive, true") << "1.2.3" << "(,1.2.4)" << true;
QTest::newRow("infinity <-> one version inclusive, false") << "1.2.3" << "(,1.2.2]" << false;
QTest::newRow("infinity <-> one version exclusive, false") << "1.2.3" << "(,1.2.3)" << false;
QTest::newRow("inclusive <-> inclusive, true") << "1.2.3" << "[1.2.2,1.2.3]" << true;
QTest::newRow("inclusive <-> exclusive, true") << "1.2.3" << "[1.2.3,1.2.4)" << true;
QTest::newRow("exclusive <-> inclusive, true") << "1.2.3" << "(1.2.2,1.2.3]" << true;
QTest::newRow("exclusive <-> exclusive, true") << "1.2.3" << "(1.2.2,1.2.4)" << true;
QTest::newRow("inclusive <-> inclusive, false") << "1.2.3" << "[1.0.0,1.2.2]" << false;
QTest::newRow("inclusive <-> exclusive, false") << "1.2.3" << "[1.0.0,1.2.3)" << false;
QTest::newRow("exclusive <-> inclusive, false") << "1.2.3" << "(1.2.3,2.0.0]" << false;
QTest::newRow("exclusive <-> exclusive, false") << "1.2.3" << "(1.0.0,1.2.3)" << false;
}
void test_versionIsInInterval()
{
QFETCH(QString, version);
QFETCH(QString, interval);
QFETCH(bool, result);
QCOMPARE(versionIsInInterval(version, interval), result);
}
void test_versionCompare_data()
{
setupVersions();

View File

@ -29,7 +29,7 @@ JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
{
}
Task *JavaInstallList::getLoadTask()
shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
{
return new JavaListLoadTask(this);
}

View File

@ -34,17 +34,17 @@ class MULTIMC_LOGIC_EXPORT JavaInstallList : public BaseVersionList
public:
explicit JavaInstallList(QObject *parent = 0);
virtual Task *getLoadTask() override;
virtual bool isLoaded() override;
virtual const BaseVersionPtr at(int i) const override;
virtual int count() const override;
virtual void sortVersions() override;
shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
void sortVersions() override;
virtual QVariant data(const QModelIndex &index, int role) const override;
virtual RoleList providesRoles() const override;
QVariant data(const QModelIndex &index, int role) const override;
RoleList providesRoles() const override;
public slots:
virtual void updateListData(QList<BaseVersionPtr> versions) override;
void updateListData(QList<BaseVersionPtr> versions) override;
protected:
QList<BaseVersionPtr> m_vlist;
@ -60,7 +60,7 @@ public:
explicit JavaListLoadTask(JavaInstallList *vlist);
~JavaListLoadTask();
virtual void executeTask();
void executeTask() override;
public slots:
void javaCheckerFinished(QList<JavaCheckResult> results);

View File

@ -3,6 +3,14 @@
#include "multimc_logic_export.h"
#include <QString>
// NOTE: apparently the GNU C library pollutes the global namespace with these... undef them.
#ifdef major
#undef major
#endif
#ifdef minor
#undef minor
#endif
class MULTIMC_LOGIC_EXPORT JavaVersion
{
friend class JavaVersionTest;

View File

@ -0,0 +1,153 @@
/* Copyright 2015-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.
*/
#include "BaseEntity.h"
#include "Json.h"
#include "net/Download.h"
#include "net/HttpMetaCache.h"
#include "net/NetJob.h"
#include "Env.h"
#include "Json.h"
class ParsingValidator : public Net::Validator
{
public: /* con/des */
ParsingValidator(Meta::BaseEntity *entity) : m_entity(entity)
{
};
virtual ~ParsingValidator()
{
};
public: /* methods */
bool init(QNetworkRequest &) override
{
return true;
}
bool write(QByteArray & data) override
{
this->data.append(data);
return true;
}
bool abort() override
{
return true;
}
bool validate(QNetworkReply &) override
{
auto fname = m_entity->localFilename();
try
{
m_entity->parse(Json::requireObject(Json::requireDocument(data, fname), fname));
return true;
}
catch (Exception &e)
{
qWarning() << "Unable to parse response:" << e.cause();
return false;
}
}
private: /* data */
QByteArray data;
Meta::BaseEntity *m_entity;
};
Meta::BaseEntity::~BaseEntity()
{
}
QUrl Meta::BaseEntity::url() const
{
return QUrl("https://meta.multimc.org").resolved(localFilename());
}
bool Meta::BaseEntity::loadLocalFile()
{
const QString fname = QDir("meta").absoluteFilePath(localFilename());
if (!QFile::exists(fname))
{
return false;
}
// TODO: check if the file has the expected checksum
try
{
parse(Json::requireObject(Json::requireDocument(fname, fname), fname));
return true;
}
catch (Exception &e)
{
qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause());
// just make sure it's gone and we never consider it again.
QFile::remove(fname);
return false;
}
}
void Meta::BaseEntity::load()
{
// load local file if nothing is loaded yet
if(!isLoaded())
{
if(loadLocalFile())
{
m_loadStatus = LoadStatus::Local;
}
}
// if we need remote update, run the update task
if(!shouldStartRemoteUpdate())
{
return;
}
NetJob *job = new NetJob(QObject::tr("Download of meta file %1").arg(localFilename()));
auto url = this->url();
auto entry = ENV.metacache()->resolveEntry("meta", localFilename());
entry->setStale(true);
auto dl = Net::Download::makeCached(url, entry);
/*
* The validator parses the file and loads it into the object.
* If that fails, the file is not written to storage.
*/
dl->addValidator(new ParsingValidator(this));
job->addNetAction(dl);
m_updateStatus = UpdateStatus::InProgress;
m_updateTask.reset(job);
QObject::connect(job, &NetJob::succeeded, [&]()
{
m_loadStatus = LoadStatus::Remote;
m_updateStatus = UpdateStatus::Succeeded;
m_updateTask.reset();
});
QObject::connect(job, &NetJob::failed, [&]()
{
m_updateStatus = UpdateStatus::Failed;
m_updateTask.reset();
});
m_updateTask->start();
}
shared_qobject_ptr<Task> Meta::BaseEntity::getCurrentTask()
{
if(m_updateStatus == UpdateStatus::InProgress)
{
return m_updateTask;
}
return nullptr;
}
#include "BaseEntity.moc"

View File

@ -0,0 +1,74 @@
/* Copyright 2015-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 <QJsonObject>
#include <QObject>
#include "QObjectPtr.h"
#include "multimc_logic_export.h"
class Task;
namespace Meta
{
class MULTIMC_LOGIC_EXPORT BaseEntity
{
public: /* types */
using Ptr = std::shared_ptr<BaseEntity>;
enum class LoadStatus
{
NotLoaded,
Local,
Remote
};
enum class UpdateStatus
{
NotDone,
InProgress,
Failed,
Succeeded
};
public:
virtual ~BaseEntity();
virtual void merge(const std::shared_ptr<BaseEntity> &other) = 0;
virtual void parse(const QJsonObject &obj) = 0;
virtual QString localFilename() const = 0;
virtual QUrl url() const;
bool isLoaded() const
{
return m_loadStatus > LoadStatus::NotLoaded;
}
bool shouldStartRemoteUpdate() const
{
return m_updateStatus == UpdateStatus::NotDone;
}
void load();
shared_qobject_ptr<Task> getCurrentTask();
protected: /* methods */
bool loadLocalFile();
private:
LoadStatus m_loadStatus = LoadStatus::NotLoaded;
UpdateStatus m_updateStatus = UpdateStatus::NotDone;
shared_qobject_ptr<Task> m_updateTask;
};
}

View File

@ -13,18 +13,18 @@
* limitations under the License.
*/
#include "WonkoIndex.h"
#include "Index.h"
#include "WonkoVersionList.h"
#include "tasks/BaseWonkoEntityLocalLoadTask.h"
#include "tasks/BaseWonkoEntityRemoteLoadTask.h"
#include "format/WonkoFormat.h"
#include "VersionList.h"
#include "JsonFormat.h"
WonkoIndex::WonkoIndex(QObject *parent)
namespace Meta
{
Index::Index(QObject *parent)
: QAbstractListModel(parent)
{
}
WonkoIndex::WonkoIndex(const QVector<WonkoVersionListPtr> &lists, QObject *parent)
Index::Index(const QVector<VersionListPtr> &lists, QObject *parent)
: QAbstractListModel(parent), m_lists(lists)
{
for (int i = 0; i < m_lists.size(); ++i)
@ -34,14 +34,14 @@ WonkoIndex::WonkoIndex(const QVector<WonkoVersionListPtr> &lists, QObject *paren
}
}
QVariant WonkoIndex::data(const QModelIndex &index, int role) const
QVariant Index::data(const QModelIndex &index, int role) const
{
if (index.parent().isValid() || index.row() < 0 || index.row() >= m_lists.size())
{
return QVariant();
}
WonkoVersionListPtr list = m_lists.at(index.row());
VersionListPtr list = m_lists.at(index.row());
switch (role)
{
case Qt::DisplayRole:
@ -56,15 +56,15 @@ QVariant WonkoIndex::data(const QModelIndex &index, int role) const
}
return QVariant();
}
int WonkoIndex::rowCount(const QModelIndex &parent) const
int Index::rowCount(const QModelIndex &parent) const
{
return m_lists.size();
}
int WonkoIndex::columnCount(const QModelIndex &parent) const
int Index::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant WonkoIndex::headerData(int section, Qt::Orientation orientation, int role) const
QVariant Index::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0)
{
@ -76,36 +76,36 @@ QVariant WonkoIndex::headerData(int section, Qt::Orientation orientation, int ro
}
}
std::unique_ptr<Task> WonkoIndex::remoteUpdateTask()
{
return std::unique_ptr<WonkoIndexRemoteLoadTask>(new WonkoIndexRemoteLoadTask(this, this));
}
std::unique_ptr<Task> WonkoIndex::localUpdateTask()
{
return std::unique_ptr<WonkoIndexLocalLoadTask>(new WonkoIndexLocalLoadTask(this, this));
}
QJsonObject WonkoIndex::serialized() const
{
return WonkoFormat::serializeIndex(this);
}
bool WonkoIndex::hasUid(const QString &uid) const
bool Index::hasUid(const QString &uid) const
{
return m_uids.contains(uid);
}
WonkoVersionListPtr WonkoIndex::getList(const QString &uid) const
VersionListPtr Index::get(const QString &uid)
{
return m_uids.value(uid, nullptr);
}
WonkoVersionListPtr WonkoIndex::getListGuaranteed(const QString &uid) const
{
return m_uids.value(uid, std::make_shared<WonkoVersionList>(uid));
VersionListPtr out = m_uids.value(uid, nullptr);
if(!out)
{
out = std::make_shared<VersionList>(uid);
m_uids[uid] = out;
}
return out;
}
void WonkoIndex::merge(const Ptr &other)
VersionPtr Index::get(const QString &uid, const QString &version)
{
const QVector<WonkoVersionListPtr> lists = std::dynamic_pointer_cast<WonkoIndex>(other)->m_lists;
auto list = get(uid);
return list->getVersion(version);
}
void Index::parse(const QJsonObject& obj)
{
parseIndex(obj, this);
}
void Index::merge(const Ptr &other)
{
const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;
// initial load, no need to merge
if (m_lists.isEmpty())
{
@ -120,7 +120,7 @@ void WonkoIndex::merge(const Ptr &other)
}
else
{
for (const WonkoVersionListPtr &list : lists)
for (const VersionListPtr &list : lists)
{
if (m_uids.contains(list->uid()))
{
@ -138,10 +138,11 @@ void WonkoIndex::merge(const Ptr &other)
}
}
void WonkoIndex::connectVersionList(const int row, const WonkoVersionListPtr &list)
void Index::connectVersionList(const int row, const VersionListPtr &list)
{
connect(list.get(), &WonkoVersionList::nameChanged, this, [this, row]()
connect(list.get(), &VersionList::nameChanged, this, [this, row]()
{
emit dataChanged(index(row), index(row), QVector<int>() << Qt::DisplayRole);
});
}
}

View File

@ -18,19 +18,23 @@
#include <QAbstractListModel>
#include <memory>
#include "BaseWonkoEntity.h"
#include "BaseEntity.h"
#include "multimc_logic_export.h"
class Task;
using WonkoVersionListPtr = std::shared_ptr<class WonkoVersionList>;
class MULTIMC_LOGIC_EXPORT WonkoIndex : public QAbstractListModel, public BaseWonkoEntity
namespace Meta
{
using VersionListPtr = std::shared_ptr<class VersionList>;
using VersionPtr = std::shared_ptr<class Version>;
class MULTIMC_LOGIC_EXPORT Index : public QAbstractListModel, public BaseEntity
{
Q_OBJECT
public:
explicit WonkoIndex(QObject *parent = nullptr);
explicit WonkoIndex(const QVector<WonkoVersionListPtr> &lists, QObject *parent = nullptr);
explicit Index(QObject *parent = nullptr);
explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr);
enum
{
@ -44,25 +48,24 @@ public:
int columnCount(const QModelIndex &parent) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
std::unique_ptr<Task> remoteUpdateTask() override;
std::unique_ptr<Task> localUpdateTask() override;
QString localFilename() const override { return "index.json"; }
QJsonObject serialized() const override;
// queries
VersionListPtr get(const QString &uid);
VersionPtr get(const QString &uid, const QString &version);
bool hasUid(const QString &uid) const;
WonkoVersionListPtr getList(const QString &uid) const;
WonkoVersionListPtr getListGuaranteed(const QString &uid) const;
QVector<WonkoVersionListPtr> lists() const { return m_lists; }
QVector<VersionListPtr> lists() const { return m_lists; }
public: // for usage by parsers only
void merge(const BaseWonkoEntity::Ptr &other);
void merge(const BaseEntity::Ptr &other) override;
void parse(const QJsonObject &obj) override;
private:
QVector<WonkoVersionListPtr> m_lists;
QHash<QString, WonkoVersionListPtr> m_uids;
QVector<VersionListPtr> m_lists;
QHash<QString, VersionListPtr> m_uids;
void connectVersionList(const int row, const WonkoVersionListPtr &list);
void connectVersionList(const int row, const VersionListPtr &list);
};
}

View File

@ -0,0 +1,44 @@
#include <QTest>
#include "TestUtil.h"
#include "meta/Index.h"
#include "meta/VersionList.h"
#include "Env.h"
class IndexTest : public QObject
{
Q_OBJECT
private
slots:
void test_isProvidedByEnv()
{
QVERIFY(ENV.metadataIndex());
QCOMPARE(ENV.metadataIndex(), ENV.metadataIndex());
}
void test_hasUid_and_getList()
{
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
QVERIFY(windex.hasUid("list1"));
QVERIFY(!windex.hasUid("asdf"));
QVERIFY(windex.get("list2") != nullptr);
QCOMPARE(windex.get("list2")->uid(), QString("list2"));
QVERIFY(windex.get("adsf") != nullptr);
}
void test_merge()
{
Meta::Index windex({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")});
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list1"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list3")})));
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list4"), std::make_shared<Meta::VersionList>("list2"), std::make_shared<Meta::VersionList>("list5")})));
QCOMPARE(windex.lists().size(), 5);
windex.merge(std::shared_ptr<Meta::Index>(new Meta::Index({std::make_shared<Meta::VersionList>("list6")})));
QCOMPARE(windex.lists().size(), 6);
}
};
QTEST_GUILESS_MAIN(IndexTest)
#include "Index_test.moc"

View File

@ -0,0 +1,150 @@
/* Copyright 2015-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.
*/
#include "JsonFormat.h"
// FIXME: remove this from here... somehow
#include "minecraft/onesix/OneSixVersionFormat.h"
#include "Json.h"
#include "Index.h"
#include "Version.h"
#include "VersionList.h"
using namespace Json;
namespace Meta
{
static const int currentFormatVersion = 0;
// Index
static BaseEntity::Ptr parseIndexInternal(const QJsonObject &obj)
{
const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages");
QVector<VersionListPtr> lists;
lists.reserve(objects.size());
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
{
VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid"));
list->setName(ensureString(obj, "name", QString()));
return list;
});
return std::make_shared<Index>(lists);
}
// Version
static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)
{
VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version"));
version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
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);
}
return version;
}
static BaseEntity::Ptr parseVersionInternal(const QJsonObject &obj)
{
VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
QString("%1/%2.json").arg(version->uid(), version->version()),
obj.contains("order")));
return version;
}
// Version list / package
static BaseEntity::Ptr parseVersionListInternal(const QJsonObject &obj)
{
const QString uid = requireString(obj, "uid");
const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
QVector<VersionPtr> versions;
versions.reserve(versionsRaw.size());
std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)
{
auto version = parseCommonVersion(uid, vObj);
version->setProvidesRecommendations();
return version;
});
VersionListPtr list = std::make_shared<VersionList>(uid);
list->setName(ensureString(obj, "name", QString()));
list->setParentUid(ensureString(obj, "parentUid", QString()));
list->setVersions(versions);
return list;
}
static int formatVersion(const QJsonObject &obj)
{
if (!obj.contains("formatVersion")) {
throw ParseException(QObject::tr("Missing required field: 'formatVersion'"));
}
if (!obj.value("formatVersion").isDouble()) {
throw ParseException(QObject::tr("Required field has invalid type: 'formatVersion'"));
}
return obj.value("formatVersion").toInt();
}
void parseIndex(const QJsonObject &obj, Index *ptr)
{
const int version = formatVersion(obj);
switch (version) {
case 0:
ptr->merge(parseIndexInternal(obj));
break;
default:
throw ParseException(QObject::tr("Unknown formatVersion: %1").arg(version));
}
}
void parseVersionList(const QJsonObject &obj, VersionList *ptr)
{
const int version = formatVersion(obj);
switch (version) {
case 0:
ptr->merge(parseVersionListInternal(obj));
break;
default:
throw ParseException(QObject::tr("Unknown formatVersion: %1").arg(version));
}
}
void parseVersion(const QJsonObject &obj, Version *ptr)
{
const int version = formatVersion(obj);
switch (version) {
case 0:
ptr->merge(parseVersionInternal(obj));
break;
default:
throw ParseException(QObject::tr("Unknown formatVersion: %1").arg(version));
}
}
}

View File

@ -15,17 +15,26 @@
#pragma once
#include "multimc_logic_export.h"
#include <QJsonObject>
#include <memory>
class QUrl;
class QString;
class QDir;
#include "Exception.h"
#include "meta/BaseEntity.h"
namespace Wonko
namespace Meta
{
MULTIMC_LOGIC_EXPORT QUrl rootUrl();
MULTIMC_LOGIC_EXPORT QUrl indexUrl();
MULTIMC_LOGIC_EXPORT QUrl versionListUrl(const QString &uid);
MULTIMC_LOGIC_EXPORT QUrl versionUrl(const QString &uid, const QString &version);
MULTIMC_LOGIC_EXPORT QDir localWonkoDir();
class Index;
class Version;
class VersionList;
class ParseException : public Exception
{
public:
using Exception::Exception;
};
void parseIndex(const QJsonObject &obj, Index *ptr);
void parseVersion(const QJsonObject &obj, Version *ptr);
void parseVersionList(const QJsonObject &obj, VersionList *ptr);
}

125
api/logic/meta/Version.cpp Normal file
View File

@ -0,0 +1,125 @@
/* Copyright 2015-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.
*/
#include "Version.h"
#include <QDateTime>
#include "JsonFormat.h"
#include "minecraft/MinecraftProfile.h"
Meta::Version::Version(const QString &uid, const QString &version)
: BaseVersion(), m_uid(uid), m_version(version)
{
}
QString Meta::Version::descriptor()
{
return m_version;
}
QString Meta::Version::name()
{
if(m_data)
return m_data->name;
return m_uid;
}
QString Meta::Version::typeString() const
{
return m_type;
}
QDateTime Meta::Version::time() const
{
return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
}
void Meta::Version::parse(const QJsonObject& obj)
{
parseVersion(obj, this);
}
void Meta::Version::merge(const std::shared_ptr<BaseEntity> &other)
{
VersionPtr version = std::dynamic_pointer_cast<Version>(other);
if(version->m_providesRecommendations)
{
if(m_recommended != version->m_recommended)
{
setRecommended(version->m_recommended);
}
}
if (m_type != version->m_type)
{
setType(version->m_type);
}
if (m_time != version->m_time)
{
setTime(version->m_time);
}
if (m_requires != version->m_requires)
{
setRequires(version->m_requires);
}
if (m_parentUid != version->m_parentUid)
{
setParentUid(version->m_parentUid);
}
setData(version->m_data);
}
QString Meta::Version::localFilename() const
{
return m_uid + '/' + m_version + ".json";
}
void Meta::Version::setParentUid(const QString& parentUid)
{
m_parentUid = parentUid;
emit requiresChanged();
}
void Meta::Version::setType(const QString &type)
{
m_type = type;
emit typeChanged();
}
void Meta::Version::setTime(const qint64 time)
{
m_time = time;
emit timeChanged();
}
void Meta::Version::setRequires(const QHash<QString, QString> &requires)
{
m_requires = requires;
emit requiresChanged();
}
void Meta::Version::setData(const VersionFilePtr &data)
{
m_data = data;
}
void Meta::Version::setProvidesRecommendations()
{
m_providesRecommendations = true;
}
void Meta::Version::setRecommended(bool recommended)
{
m_recommended = recommended;
}

View File

@ -16,54 +16,79 @@
#pragma once
#include "BaseVersion.h"
#include "BaseWonkoEntity.h"
#include <QVector>
#include <QStringList>
#include <QJsonObject>
#include <QStringList>
#include <QVector>
#include <memory>
#include "minecraft/VersionFile.h"
#include "WonkoReference.h"
#include "BaseEntity.h"
#include "multimc_logic_export.h"
using WonkoVersionPtr = std::shared_ptr<class WonkoVersion>;
namespace Meta
{
using VersionPtr = std::shared_ptr<class Version>;
class MULTIMC_LOGIC_EXPORT WonkoVersion : public QObject, public BaseVersion, public BaseWonkoEntity
class MULTIMC_LOGIC_EXPORT Version : public QObject, public BaseVersion, public BaseEntity
{
Q_OBJECT
Q_PROPERTY(QString uid READ uid CONSTANT)
Q_PROPERTY(QString version READ version CONSTANT)
Q_PROPERTY(QString type READ type NOTIFY typeChanged)
Q_PROPERTY(QDateTime time READ time NOTIFY timeChanged)
Q_PROPERTY(QVector<WonkoReference> requires READ requires NOTIFY requiresChanged)
public:
explicit WonkoVersion(const QString &uid, const QString &version);
public: /* con/des */
explicit Version(const QString &uid, const QString &version);
QString descriptor() override;
QString name() override;
QString typeString() const override;
QString uid() const { return m_uid; }
QString version() const { return m_version; }
QString type() const { return m_type; }
QString uid() const
{
return m_uid;
}
QString parentUid() const
{
return m_parentUid;
}
QString version() const
{
return m_version;
}
QString type() const
{
return m_type;
}
QDateTime time() const;
qint64 rawTime() const { return m_time; }
QVector<WonkoReference> requires() const { return m_requires; }
VersionFilePtr data() const { return m_data; }
qint64 rawTime() const
{
return m_time;
}
const QHash<QString, QString> &requires() const
{
return m_requires;
}
VersionFilePtr data() const
{
return m_data;
}
bool isRecommended() const
{
return m_recommended;
}
std::unique_ptr<Task> remoteUpdateTask() override;
std::unique_ptr<Task> localUpdateTask() override;
void merge(const std::shared_ptr<BaseWonkoEntity> &other) override;
void merge(const std::shared_ptr<BaseEntity> &other) override;
void parse(const QJsonObject &obj) override;
QString localFilename() const override;
QJsonObject serialized() const override;
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 QVector<WonkoReference> &requires);
void setRequires(const QHash<QString, QString> &requires);
void setRecommended(bool recommended);
void setProvidesRecommendations();
void setData(const VersionFilePtr &data);
signals:
@ -72,12 +97,17 @@ signals:
void requiresChanged();
private:
bool m_providesRecommendations = false;
bool m_recommended = false;
QString m_name;
QString m_uid;
QString m_parentUid;
QString m_version;
QString m_type;
qint64 m_time;
QVector<WonkoReference> m_requires;
QHash<QString, QString> m_requires;
VersionFilePtr m_data;
};
}
Q_DECLARE_METATYPE(WonkoVersionPtr)
Q_DECLARE_METATYPE(Meta::VersionPtr)

View File

@ -0,0 +1,235 @@
/* Copyright 2015-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.
*/
#include "VersionList.h"
#include <QDateTime>
#include "Version.h"
#include "JsonFormat.h"
namespace Meta
{
VersionList::VersionList(const QString &uid, QObject *parent)
: BaseVersionList(parent), m_uid(uid)
{
setObjectName("Version list: " + uid);
}
shared_qobject_ptr<Task> VersionList::getLoadTask()
{
load();
return getCurrentTask();
}
bool VersionList::isLoaded()
{
return BaseEntity::isLoaded();
}
const BaseVersionPtr VersionList::at(int i) const
{
return m_versions.at(i);
}
int VersionList::count() const
{
return m_versions.size();
}
void VersionList::sortVersions()
{
beginResetModel();
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
{
return *a.get() < *b.get();
});
endResetModel();
}
QVariant VersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid())
{
return QVariant();
}
VersionPtr version = m_versions.at(index.row());
switch (role)
{
case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast<BaseVersion>(version));
case VersionRole:
case VersionIdRole:
return version->version();
case ParentVersionRole:
{
auto parentUid = this->parentUid();
if(parentUid.isEmpty())
{
return QVariant();
}
auto & reqs = version->requires();
auto iter = reqs.find(parentUid);
if (iter != reqs.end())
{
return iter.value();
}
}
case TypeRole: return version->type();
case UidRole: return version->uid();
case TimeRole: return version->time();
case RequiresRole: return QVariant::fromValue(version->requires());
case SortRole: return version->rawTime();
case VersionPtrRole: return QVariant::fromValue(version);
case RecommendedRole: return version->isRecommended();
// FIXME: this should be determined in whatever view/proxy is used...
// case LatestRole: return version == getLatestStable();
default: return QVariant();
}
}
BaseVersionList::RoleList VersionList::providesRoles() const
{
return {VersionPointerRole, VersionRole, VersionIdRole, ParentVersionRole,
TypeRole, UidRole, TimeRole, RequiresRole, SortRole,
RecommendedRole, LatestRole, VersionPtrRole};
}
QHash<int, QByteArray> VersionList::roleNames() const
{
QHash<int, QByteArray> roles = BaseVersionList::roleNames();
roles.insert(UidRole, "uid");
roles.insert(TimeRole, "time");
roles.insert(SortRole, "sort");
roles.insert(RequiresRole, "requires");
return roles;
}
QString VersionList::localFilename() const
{
return m_uid + "/index.json";
}
QString VersionList::humanReadable() const
{
return m_name.isEmpty() ? m_uid : m_name;
}
VersionPtr VersionList::getVersion(const QString &version)
{
VersionPtr out = m_lookup.value(version, nullptr);
if(!out)
{
out = std::make_shared<Version>(m_uid, version);
m_lookup[version] = out;
}
return out;
}
void VersionList::setName(const QString &name)
{
m_name = name;
emit nameChanged(name);
}
void VersionList::setVersions(const QVector<VersionPtr> &versions)
{
beginResetModel();
m_versions = versions;
std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b)
{
return a->rawTime() > b->rawTime();
});
for (int i = 0; i < m_versions.size(); ++i)
{
m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i));
setupAddedVersion(i, m_versions.at(i));
}
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();
}
void VersionList::parse(const QJsonObject& obj)
{
parseVersionList(obj, this);
}
void VersionList::merge(const BaseEntity::Ptr &other)
{
const VersionListPtr list = std::dynamic_pointer_cast<VersionList>(other);
if (m_name != list->m_name)
{
setName(list->m_name);
}
if(m_parentUid != list->m_parentUid)
{
setParentUid(list->m_parentUid);
}
if (m_versions.isEmpty())
{
setVersions(list->m_versions);
}
else
{
for (const VersionPtr &version : list->m_versions)
{
if (m_lookup.contains(version->version()))
{
m_lookup.value(version->version())->merge(version);
}
else
{
beginInsertRows(QModelIndex(), m_versions.size(), m_versions.size());
setupAddedVersion(m_versions.size(), version);
m_versions.append(version);
m_lookup.insert(version->uid(), version);
endInsertRows();
if (!m_recommended || (version->type() == "release" && version->rawTime() > m_recommended->rawTime()))
{
m_recommended = version;
emit dataChanged(index(0), index(m_versions.size() - 1), QVector<int>() << RecommendedRole);
}
}
}
}
}
void VersionList::setupAddedVersion(const int row, const VersionPtr &version)
{
connect(version.get(), &Version::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
connect(version.get(), &Version::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
}
BaseVersionPtr VersionList::getRecommended() const
{
return m_recommended;
}
}
void Meta::VersionList::setParentUid(const QString& parentUid)
{
m_parentUid = parentUid;
}
#include "VersionList.moc"

View File

@ -15,78 +15,92 @@
#pragma once
#include "BaseEntity.h"
#include "BaseVersionList.h"
#include "BaseWonkoEntity.h"
#include <QJsonObject>
#include <memory>
using WonkoVersionPtr = std::shared_ptr<class WonkoVersion>;
using WonkoVersionListPtr = std::shared_ptr<class WonkoVersionList>;
namespace Meta
{
using VersionPtr = std::shared_ptr<class Version>;
using VersionListPtr = std::shared_ptr<class VersionList>;
class MULTIMC_LOGIC_EXPORT WonkoVersionList : public BaseVersionList, public BaseWonkoEntity
class MULTIMC_LOGIC_EXPORT VersionList : public BaseVersionList, public BaseEntity
{
Q_OBJECT
Q_PROPERTY(QString uid READ uid CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
public:
explicit WonkoVersionList(const QString &uid, QObject *parent = nullptr);
explicit VersionList(const QString &uid, QObject *parent = nullptr);
enum Roles
{
UidRole = Qt::UserRole + 100,
TimeRole,
RequiresRole,
WonkoVersionPtrRole
VersionPtrRole
};
Task *getLoadTask() override;
shared_qobject_ptr<Task> getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
void sortVersions() override;
BaseVersionPtr getLatestStable() const override;
BaseVersionPtr getRecommended() const override;
QVariant data(const QModelIndex &index, int role) const override;
RoleList providesRoles() const override;
QHash<int, QByteArray> roleNames() const override;
std::unique_ptr<Task> remoteUpdateTask() override;
std::unique_ptr<Task> localUpdateTask() override;
QString localFilename() const override;
QJsonObject serialized() const override;
QString uid() const { return m_uid; }
QString name() const { return m_name; }
QString parentUid() const
{
return m_parentUid;
}
QString uid() const
{
return m_uid;
}
QString name() const
{
return m_name;
}
QString humanReadable() const;
bool hasVersion(const QString &version) const;
WonkoVersionPtr getVersion(const QString &version) const;
VersionPtr getVersion(const QString &version);
QVector<WonkoVersionPtr> versions() const { return m_versions; }
QVector<VersionPtr> versions() const
{
return m_versions;
}
public: // for usage only by parsers
void setName(const QString &name);
void setVersions(const QVector<WonkoVersionPtr> &versions);
void merge(const BaseWonkoEntity::Ptr &other);
void setParentUid(const QString &parentUid);
void setVersions(const QVector<VersionPtr> &versions);
void merge(const BaseEntity::Ptr &other) override;
void parse(const QJsonObject &obj) override;
signals:
void nameChanged(const QString &name);
protected slots:
void updateListData(QList<BaseVersionPtr> versions) override {}
void updateListData(QList<BaseVersionPtr>) override
{
}
private:
QVector<WonkoVersionPtr> m_versions;
QHash<QString, WonkoVersionPtr> m_lookup;
QVector<VersionPtr> m_versions;
QHash<QString, VersionPtr> m_lookup;
QString m_uid;
QString m_parentUid;
QString m_name;
WonkoVersionPtr m_recommended;
WonkoVersionPtr m_latest;
VersionPtr m_recommended;
void setupAddedVersion(const int row, const WonkoVersionPtr &version);
void setupAddedVersion(const int row, const VersionPtr &version);
};
Q_DECLARE_METATYPE(WonkoVersionListPtr)
}
Q_DECLARE_METATYPE(Meta::VersionListPtr)

View File

@ -30,14 +30,15 @@ void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& na
};
if(m_mojangDownloads)
{
if(m_mojangDownloads->artifact)
{
auto artifact = m_mojangDownloads->artifact;
jar += actualPath(artifact->path);
}
if(!isNative())
return;
if(m_nativeClassifiers.contains(system))
{
if(m_mojangDownloads->artifact)
{
auto artifact = m_mojangDownloads->artifact;
jar += actualPath(artifact->path);
}
}
else if(m_nativeClassifiers.contains(system))
{
auto nativeClassifier = m_nativeClassifiers[system];
if(nativeClassifier.contains("${arch}"))
@ -55,7 +56,15 @@ void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& na
}
else
{
native += actualPath(m_mojangDownloads->getDownloadInfo(nativeClassifier)->path);
auto dlinfo = m_mojangDownloads->getDownloadInfo(nativeClassifier);
if(!dlinfo)
{
qWarning() << "Cannot get native for" << nativeClassifier << "while processing" << m_name;
}
else
{
native += actualPath(dlinfo->path);
}
}
}
}

View File

@ -234,7 +234,7 @@ slots:
auto test = readMojangJson("data/lib-native.json");
QStringList jar, native, native32, native64;
test->getApplicableFiles(Os_OSX, jar, native, native32, native64, QString());
QCOMPARE(jar, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar"));
QCOMPARE(jar, QStringList());
QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar"));
QCOMPARE(native32, {});
QCOMPARE(native64, {});

View File

@ -5,7 +5,6 @@
#include <settings/Setting.h>
#include "settings/SettingsObject.h"
#include "Env.h"
#include "minecraft/MinecraftVersionList.h"
#include <MMCStrings.h>
#include <pathmatcher/RegexpMatcher.h>
#include <pathmatcher/MultiMatcher.h>
@ -21,9 +20,13 @@
#include "minecraft/launch/ModMinecraftJar.h"
#include "minecraft/launch/ClaimAccount.h"
#include "java/launch/CheckJava.h"
#include <meta/Index.h>
#include <meta/VersionList.h>
#include <icons/IIconList.h>
#include <QCoreApplication>
#define IBUS "@im=ibus"
// all of this because keeping things compatible with deprecated old settings
@ -104,7 +107,7 @@ QString MinecraftInstance::binRoot() const
std::shared_ptr< BaseVersionList > MinecraftInstance::versionList() const
{
return ENV.getVersionList("net.minecraft");
return ENV.metadataIndex()->get("net.minecraft");
}
QStringList MinecraftInstance::javaArguments() const

View File

@ -80,7 +80,7 @@ void MinecraftProfile::clear()
m_traits.clear();
m_jarMods.clear();
mojangDownloads.clear();
m_problemSeverity = ProblemSeverity::PROBLEM_NONE;
m_problemSeverity = ProblemSeverity::None;
}
void MinecraftProfile::clearPatches()
@ -273,9 +273,9 @@ QVariant MinecraftProfile::data(const QModelIndex &index, int role) const
auto severity = patch->getProblemSeverity();
switch (severity)
{
case PROBLEM_WARNING:
case ProblemSeverity::Warning:
return "warning";
case PROBLEM_ERROR:
case ProblemSeverity::Error:
return "error";
default:
return QVariant();
@ -491,20 +491,29 @@ void MinecraftProfile::applyLibrary(LibraryPtr library)
{
return;
}
QList<LibraryPtr> * list = &m_libraries;
if(library->isNative())
{
list = &m_nativeLibraries;
}
auto libraryCopy = Library::limitedCopy(library);
// find the library by name.
const int index = findLibraryByName(m_libraries, library->rawName());
const int index = findLibraryByName(*list, library->rawName());
// library not found? just add it.
if (index < 0)
{
m_libraries.append(Library::limitedCopy(library));
list->append(libraryCopy);
return;
}
auto existingLibrary = m_libraries.at(index);
auto existingLibrary = list->at(index);
// if we are higher it means we should update
if (Version(library->version()) > Version(existingLibrary->version()))
{
auto libraryCopy = Library::limitedCopy(library);
m_libraries.replace(index, libraryCopy);
list->replace(index, libraryCopy);
}
}
@ -581,6 +590,11 @@ const QList<LibraryPtr> & MinecraftProfile::getLibraries() const
return m_libraries;
}
const QList<LibraryPtr> & MinecraftProfile::getNativeLibraries() const
{
return m_nativeLibraries;
}
void MinecraftProfile::getLibraryFiles(const QString& architecture, QStringList& jars, QStringList& nativeJars, const QString& overridePath) const
{
QStringList native32, native64;
@ -590,6 +604,10 @@ void MinecraftProfile::getLibraryFiles(const QString& architecture, QStringList&
{
lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
}
for (auto lib : getNativeLibraries())
{
lib->getApplicableFiles(currentSystem, jars, nativeJars, native32, native64, overridePath);
}
if(architecture == "32")
{
nativeJars.append(native32);
@ -621,6 +639,11 @@ void MinecraftProfile::installJarMods(QStringList selectedFiles)
m_strategy->installJarMods(selectedFiles);
}
void MinecraftProfile::installVersion(BaseVersionPtr version)
{
// TODO: implement
}
/*
* TODO: get rid of this. Get rid of all order numbers.
*/

View File

@ -22,8 +22,9 @@
#include <memory>
#include "Library.h"
#include "VersionFile.h"
#include "ProfilePatch.h"
#include "JarMod.h"
#include "BaseVersion.h"
#include "MojangDownloadInfo.h"
#include "multimc_logic_export.h"
@ -58,6 +59,9 @@ public:
/// install more jar mods
void installJarMods(QStringList selectedFiles);
/// install more jar mods
void installVersion(BaseVersionPtr version);
/// DEPRECATED, remove ASAP
int getFreeOrderNumber();
@ -111,6 +115,7 @@ public: /* getters for profile variables */
const QStringList & getTweakers() const;
const QList<JarmodPtr> & getJarMods() const;
const QList<LibraryPtr> & getLibraries() const;
const QList<LibraryPtr> & getNativeLibraries() const;
void getLibraryFiles(const QString & architecture, QStringList & jars, QStringList & nativeJars, const QString & overridePath) const;
QString getMainJarUrl() const;
bool hasTrait(const QString & trait) const;
@ -165,13 +170,16 @@ private: /* data */
/// the list of libraries
QList<LibraryPtr> m_libraries;
/// the list of libraries
QList<LibraryPtr> m_nativeLibraries;
/// traits, collected from all the version files (version files can only add)
QSet<QString> m_traits;
/// A list of jar mods. version files can add those.
QList<JarmodPtr> m_jarMods;
ProblemSeverity m_problemSeverity = PROBLEM_NONE;
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
/*
FIXME: add support for those rules here? Looks like a pile of quick hacks to me though.

View File

@ -1,230 +0,0 @@
#include "MinecraftVersion.h"
#include "MinecraftProfile.h"
#include "VersionBuildError.h"
#include "ProfileUtils.h"
#include "settings/SettingsObject.h"
bool MinecraftVersion::usesLegacyLauncher()
{
return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch");
}
QString MinecraftVersion::descriptor()
{
return m_descriptor;
}
QString MinecraftVersion::name()
{
return m_name;
}
QString MinecraftVersion::typeString() const
{
if(m_type == "snapshot")
{
return QObject::tr("Snapshot");
}
else if (m_type == "release")
{
return QObject::tr("Regular release");
}
else if (m_type == "old_alpha")
{
return QObject::tr("Alpha");
}
else if (m_type == "old_beta")
{
return QObject::tr("Beta");
}
else
{
return QString();
}
}
VersionSource MinecraftVersion::getVersionSource()
{
return m_versionSource;
}
bool MinecraftVersion::hasJarMods()
{
return false;
}
bool MinecraftVersion::isMinecraftVersion()
{
return true;
}
void MinecraftVersion::applyFileTo(MinecraftProfile *profile)
{
if(m_versionSource == VersionSource::Local && getVersionFile())
{
getVersionFile()->applyTo(profile);
}
else
{
throw VersionIncomplete(QObject::tr("Can't apply incomplete/builtin Minecraft version %1").arg(m_name));
}
}
QString MinecraftVersion::getUrl() const
{
// legacy fallback
if(m_versionFileURL.isEmpty())
{
return QString("http://") + URLConstants::AWS_DOWNLOAD_VERSIONS + m_descriptor + "/" + m_descriptor + ".json";
}
// current
return m_versionFileURL;
}
VersionFilePtr MinecraftVersion::getVersionFile()
{
QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_descriptor));
m_problems.clear();
m_problemSeverity = PROBLEM_NONE;
if(!versionFile.exists())
{
if(m_loadedVersionFile)
{
m_loadedVersionFile.reset();
}
addProblem(PROBLEM_WARNING, QObject::tr("The patch file doesn't exist locally. It's possible it just needs to be downloaded."));
}
else
{
try
{
if(versionFile.lastModified() != m_loadedVersionFileTimestamp)
{
auto loadedVersionFile = ProfileUtils::parseBinaryJsonFile(versionFile);
loadedVersionFile->name = "Minecraft";
loadedVersionFile->setCustomizable(true);
m_loadedVersionFileTimestamp = versionFile.lastModified();
m_loadedVersionFile = loadedVersionFile;
}
}
catch(Exception e)
{
m_loadedVersionFile.reset();
addProblem(PROBLEM_ERROR, QObject::tr("The patch file couldn't be read:\n%1").arg(e.cause()));
}
}
return m_loadedVersionFile;
}
bool MinecraftVersion::isCustomizable()
{
switch(m_versionSource)
{
case VersionSource::Local:
case VersionSource::Remote:
// locally cached file, or a remote file that we can acquire can be customized
return true;
case VersionSource::Builtin:
// builtins do not follow the normal OneSix format. They are not customizable.
default:
// Everything else is undefined and therefore not customizable.
return false;
}
return false;
}
const QList<PatchProblem> &MinecraftVersion::getProblems()
{
if(m_versionSource != VersionSource::Builtin && getVersionFile())
{
return getVersionFile()->getProblems();
}
return ProfilePatch::getProblems();
}
ProblemSeverity MinecraftVersion::getProblemSeverity()
{
if(m_versionSource != VersionSource::Builtin && getVersionFile())
{
return getVersionFile()->getProblemSeverity();
}
return ProfilePatch::getProblemSeverity();
}
void MinecraftVersion::applyTo(MinecraftProfile *profile)
{
// do we have this one cached?
if (m_versionSource == VersionSource::Local)
{
applyFileTo(profile);
return;
}
// if not builtin, do not proceed any further.
if (m_versionSource != VersionSource::Builtin)
{
throw VersionIncomplete(QObject::tr(
"Minecraft version %1 could not be applied: version files are missing.").arg(m_descriptor));
}
profile->applyMinecraftVersion(m_descriptor);
profile->applyMainClass(m_mainClass);
profile->applyAppletClass(m_appletClass);
profile->applyMinecraftArguments(" ${auth_player_name} ${auth_session}"); // all builtin versions are legacy
profile->applyMinecraftVersionType(m_type);
profile->applyTraits(m_traits);
profile->applyProblemSeverity(m_problemSeverity);
}
int MinecraftVersion::getOrder()
{
return order;
}
void MinecraftVersion::setOrder(int order)
{
this->order = order;
}
QList<JarmodPtr> MinecraftVersion::getJarMods()
{
return QList<JarmodPtr>();
}
QString MinecraftVersion::getName()
{
return "Minecraft";
}
QString MinecraftVersion::getVersion()
{
return m_descriptor;
}
QString MinecraftVersion::getID()
{
return "net.minecraft";
}
QString MinecraftVersion::getFilename()
{
return QString();
}
QDateTime MinecraftVersion::getReleaseDateTime()
{
return m_releaseTime;
}
bool MinecraftVersion::needsUpdate()
{
return m_versionSource == VersionSource::Remote || hasUpdate();
}
bool MinecraftVersion::hasUpdate()
{
return m_versionSource == VersionSource::Remote || (m_versionSource == VersionSource::Local && upstreamUpdate);
}
bool MinecraftVersion::isCustom()
{
// if we add any other source types, this will evaluate to false for them.
return m_versionSource != VersionSource::Builtin
&& m_versionSource != VersionSource::Local
&& m_versionSource != VersionSource::Remote;
}

View File

@ -1,132 +0,0 @@
/* 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 <QStringList>
#include <QSet>
#include <QDateTime>
#include "BaseVersion.h"
#include "ProfilePatch.h"
#include "VersionFile.h"
#include "multimc_logic_export.h"
class MinecraftProfile;
class MinecraftVersion;
typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr;
class MULTIMC_LOGIC_EXPORT MinecraftVersion : public BaseVersion, public ProfilePatch
{
friend class MinecraftVersionList;
public: /* methods */
bool usesLegacyLauncher();
virtual QString descriptor() override;
virtual QString name() override;
virtual QString typeString() const override;
virtual bool hasJarMods() override;
virtual bool isMinecraftVersion() override;
virtual void applyTo(MinecraftProfile *profile) override;
virtual int getOrder() override;
virtual void setOrder(int order) override;
virtual QList<JarmodPtr> getJarMods() override;
virtual QString getID() override;
virtual QString getVersion() override;
virtual QString getName() override;
virtual QString getFilename() override;
QDateTime getReleaseDateTime() override;
VersionSource getVersionSource() override;
bool needsUpdate();
bool hasUpdate();
virtual bool isCustom() override;
virtual bool isMoveable() override
{
return false;
}
virtual bool isCustomizable() override;
virtual bool isRemovable() override
{
return false;
}
virtual bool isRevertible() override
{
return false;
}
virtual bool isEditable() override
{
return false;
}
virtual bool isVersionChangeable() override
{
return true;
}
virtual VersionFilePtr getVersionFile() override;
// virtual QJsonDocument toJson(bool saveOrder) override;
QString getUrl() const;
virtual const QList<PatchProblem> &getProblems() override;
virtual ProblemSeverity getProblemSeverity() override;
private: /* methods */
void applyFileTo(MinecraftProfile *profile);
protected: /* data */
VersionSource m_versionSource = VersionSource::Builtin;
/// The URL that this version will be downloaded from.
QString m_versionFileURL;
/// the human readable version name
QString m_name;
/// the version ID.
QString m_descriptor;
/// version traits. added by MultiMC
QSet<QString> m_traits;
/// The main class this version uses (if any, can be empty).
QString m_mainClass;
/// The applet class this version uses (if any, can be empty).
QString m_appletClass;
/// The type of this release
QString m_type;
/// the time this version was actually released by Mojang
QDateTime m_releaseTime;
/// the time this version was last updated by Mojang
QDateTime m_updateTime;
/// MD5 hash of the minecraft jar
QString m_jarChecksum;
/// order of this file... default = -2
int order = -2;
/// an update available from Mojang
MinecraftVersionPtr upstreamUpdate;
QDateTime m_loadedVersionFileTimestamp;
mutable VersionFilePtr m_loadedVersionFile;
};

View File

@ -1,677 +0,0 @@
/* 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.
*/
#include <QtXml>
#include "Json.h"
#include <QtAlgorithms>
#include <QtNetwork>
#include "Env.h"
#include "Exception.h"
#include "MinecraftVersionList.h"
#include "net/URLConstants.h"
#include "ParseUtils.h"
#include "ProfileUtils.h"
#include "VersionFilterData.h"
#include "onesix/OneSixVersionFormat.h"
#include "MojangVersionFormat.h"
#include <FileSystem.h>
static const char * localVersionCache = "versions/versions.dat";
class MCVListLoadTask : public Task
{
Q_OBJECT
public:
explicit MCVListLoadTask(MinecraftVersionList *vlist);
virtual ~MCVListLoadTask() override{};
virtual void executeTask() override;
protected
slots:
void list_downloaded();
protected:
QNetworkReply *vlistReply;
MinecraftVersionList *m_list;
MinecraftVersion *m_currentStable;
};
class MCVListVersionUpdateTask : public Task
{
Q_OBJECT
public:
explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, std::shared_ptr<MinecraftVersion> updatedVersion);
virtual ~MCVListVersionUpdateTask() override{};
virtual void executeTask() override;
bool canAbort() const override;
public slots:
bool abort() override;
protected
slots:
void json_downloaded();
protected:
NetJobPtr specificVersionDownloadJob;
QByteArray versionIndexData;
std::shared_ptr<MinecraftVersion> updatedVersion;
MinecraftVersionList *m_list;
bool m_aborted = false;
};
class ListLoadError : public Exception
{
public:
ListLoadError(QString cause) : Exception(cause) {};
virtual ~ListLoadError() noexcept
{
}
};
MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
{
loadBuiltinList();
loadCachedList();
}
Task *MinecraftVersionList::getLoadTask()
{
return new MCVListLoadTask(this);
}
bool MinecraftVersionList::isLoaded()
{
return m_loaded;
}
const BaseVersionPtr MinecraftVersionList::at(int i) const
{
return m_vlist.at(i);
}
int MinecraftVersionList::count() const
{
return m_vlist.count();
}
static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
{
auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
return left->getReleaseDateTime() > right->getReleaseDateTime();
}
void MinecraftVersionList::sortInternal()
{
qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
}
void MinecraftVersionList::loadCachedList()
{
QFile localIndex(localVersionCache);
if (!localIndex.exists())
{
return;
}
if (!localIndex.open(QIODevice::ReadOnly))
{
// FIXME: this is actually a very bad thing! How do we deal with this?
qCritical() << "The minecraft version cache can't be read.";
return;
}
auto data = localIndex.readAll();
try
{
localIndex.close();
QJsonDocument jsonDoc = QJsonDocument::fromBinaryData(data);
if (jsonDoc.isNull())
{
throw ListLoadError(tr("Error reading the version list."));
}
loadMojangList(jsonDoc, VersionSource::Local);
}
catch (Exception &e)
{
// the cache has gone bad for some reason... flush it.
qCritical() << "The minecraft version cache is corrupted. Flushing cache.";
localIndex.remove();
return;
}
m_hasLocalIndex = true;
}
void MinecraftVersionList::loadBuiltinList()
{
qDebug() << "Loading builtin version list.";
// grab the version list data from internal resources.
const QJsonDocument doc =
Json::requireDocument(QString(":/versions/minecraft.json"), "builtin version list");
const QJsonObject root = doc.object();
// parse all the versions
for (const auto version : Json::requireArray(root.value("versions")))
{
QJsonObject versionObj = version.toObject();
QString versionID = versionObj.value("id").toString("");
QString versionTypeStr = versionObj.value("type").toString("");
if (versionID.isEmpty() || versionTypeStr.isEmpty())
{
qCritical() << "Parsed version is missing ID or type";
continue;
}
if (g_VersionFilterData.legacyBlacklist.contains(versionID))
{
qWarning() << "Blacklisted legacy version ignored: " << versionID;
continue;
}
// Now, we construct the version object and add it to the list.
std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
mcVersion->m_name = mcVersion->m_descriptor = versionID;
// Parse the timestamp.
mcVersion->m_releaseTime = timeFromS3Time(versionObj.value("releaseTime").toString(""));
mcVersion->m_versionFileURL = QString();
mcVersion->m_versionSource = VersionSource::Builtin;
mcVersion->m_type = versionTypeStr;
mcVersion->m_appletClass = versionObj.value("appletClass").toString("");
mcVersion->m_mainClass = versionObj.value("mainClass").toString("");
mcVersion->m_jarChecksum = versionObj.value("checksum").toString("");
if (versionObj.contains("+traits"))
{
for (auto traitVal : Json::requireArray(versionObj.value("+traits")))
{
mcVersion->m_traits.insert(Json::requireString(traitVal));
}
}
m_lookup[versionID] = mcVersion;
m_vlist.append(mcVersion);
}
}
void MinecraftVersionList::loadMojangList(QJsonDocument jsonDoc, VersionSource source)
{
qDebug() << "Loading" << ((source == VersionSource::Remote) ? "remote" : "local") << "version list.";
if (!jsonDoc.isObject())
{
throw ListLoadError(tr("Error parsing version list JSON: jsonDoc is not an object"));
}
QJsonObject root = jsonDoc.object();
try
{
QJsonObject latest = Json::requireObject(root.value("latest"));
m_latestReleaseID = Json::requireString(latest.value("release"));
m_latestSnapshotID = Json::requireString(latest.value("snapshot"));
}
catch (Exception &err)
{
qCritical()
<< tr("Error parsing version list JSON: couldn't determine latest versions");
}
// Now, get the array of versions.
if (!root.value("versions").isArray())
{
throw ListLoadError(tr("Error parsing version list JSON: version list object is "
"missing 'versions' array"));
}
QJsonArray versions = root.value("versions").toArray();
QList<BaseVersionPtr> tempList;
for (auto version : versions)
{
// Load the version info.
if (!version.isObject())
{
qCritical() << "Error while parsing version list : invalid JSON structure";
continue;
}
QJsonObject versionObj = version.toObject();
QString versionID = versionObj.value("id").toString("");
if (versionID.isEmpty())
{
qCritical() << "Error while parsing version : version ID is missing";
continue;
}
if (g_VersionFilterData.legacyBlacklist.contains(versionID))
{
qWarning() << "Blacklisted legacy version ignored: " << versionID;
continue;
}
// Now, we construct the version object and add it to the list.
std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
mcVersion->m_name = mcVersion->m_descriptor = versionID;
mcVersion->m_releaseTime = timeFromS3Time(versionObj.value("releaseTime").toString(""));
mcVersion->m_updateTime = timeFromS3Time(versionObj.value("time").toString(""));
if (mcVersion->m_releaseTime < g_VersionFilterData.legacyCutoffDate)
{
continue;
}
// depends on where we load the version from -- network request or local file?
mcVersion->m_versionSource = source;
mcVersion->m_versionFileURL = versionObj.value("url").toString("");
QString versionTypeStr = versionObj.value("type").toString("");
if (versionTypeStr.isEmpty())
{
qCritical() << "Ignoring" << versionID
<< "because it doesn't have the version type set.";
continue;
}
// OneSix or Legacy. use filter to determine type
if (versionTypeStr == "release")
{
}
else if (versionTypeStr == "snapshot") // It's a snapshot... yay
{
}
else if (versionTypeStr == "old_alpha")
{
}
else if (versionTypeStr == "old_beta")
{
}
else
{
qCritical() << "Ignoring" << versionID
<< "because it has an invalid version type.";
continue;
}
mcVersion->m_type = versionTypeStr;
qDebug() << "Loaded version" << versionID << "from"
<< ((source == VersionSource::Remote) ? "remote" : "local") << "version list.";
tempList.append(mcVersion);
}
updateListData(tempList);
if(source == VersionSource::Remote)
{
m_loaded = true;
}
}
void MinecraftVersionList::sortVersions()
{
beginResetModel();
sortInternal();
endResetModel();
}
QVariant MinecraftVersionList::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() > count())
return QVariant();
auto version = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[index.row()]);
switch (role)
{
case VersionPointerRole:
return qVariantFromValue(m_vlist[index.row()]);
case VersionRole:
return version->name();
case VersionIdRole:
return version->descriptor();
case RecommendedRole:
return version->descriptor() == m_latestReleaseID;
case LatestRole:
{
if(version->descriptor() != m_latestSnapshotID)
return false;
MinecraftVersionPtr latestRelease = std::dynamic_pointer_cast<MinecraftVersion>(getLatestStable());
/*
if(latestRelease && latestRelease->m_releaseTime > version->m_releaseTime)
{
return false;
}
*/
return true;
}
case TypeRole:
return version->typeString();
default:
return QVariant();
}
}
BaseVersionList::RoleList MinecraftVersionList::providesRoles() const
{
return {VersionPointerRole, VersionRole, VersionIdRole, RecommendedRole, LatestRole, TypeRole};
}
BaseVersionPtr MinecraftVersionList::getLatestStable() const
{
if(m_lookup.contains(m_latestReleaseID))
return m_lookup[m_latestReleaseID];
return BaseVersionPtr();
}
BaseVersionPtr MinecraftVersionList::getRecommended() const
{
return getLatestStable();
}
void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
{
beginResetModel();
for (auto version : versions)
{
auto descr = version->descriptor();
if (!m_lookup.contains(descr))
{
m_lookup[version->descriptor()] = version;
m_vlist.append(version);
continue;
}
auto orig = std::dynamic_pointer_cast<MinecraftVersion>(m_lookup[descr]);
auto added = std::dynamic_pointer_cast<MinecraftVersion>(version);
// updateListData is called after Mojang list loads. those can be local or remote
// remote comes always after local
// any other options are ignored
if (orig->m_versionSource != VersionSource::Local || added->m_versionSource != VersionSource::Remote)
{
continue;
}
// alright, it's an update. put it inside the original, for further processing.
orig->upstreamUpdate = added;
}
sortInternal();
endResetModel();
}
MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
{
m_list = vlist;
m_currentStable = NULL;
vlistReply = nullptr;
}
void MCVListLoadTask::executeTask()
{
setStatus(tr("Loading instance version list..."));
vlistReply = ENV.qnam().get(QNetworkRequest(QUrl("https://launchermeta.mojang.com/mc/game/version_manifest.json")));
connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
}
void MCVListLoadTask::list_downloaded()
{
if (vlistReply->error() != QNetworkReply::NoError)
{
vlistReply->deleteLater();
emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString());
return;
}
auto data = vlistReply->readAll();
vlistReply->deleteLater();
try
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
throw ListLoadError(
tr("Error parsing version list JSON: %1").arg(jsonError.errorString()));
}
m_list->loadMojangList(jsonDoc, VersionSource::Remote);
}
catch (Exception &e)
{
emitFailed(e.cause());
return;
}
emitSucceeded();
return;
}
MCVListVersionUpdateTask::MCVListVersionUpdateTask(MinecraftVersionList *vlist, std::shared_ptr<MinecraftVersion> updatedVersion)
: Task()
{
m_list = vlist;
this->updatedVersion = updatedVersion;
}
void MCVListVersionUpdateTask::executeTask()
{
if(m_aborted)
{
emitFailed(tr("Task aborted."));
return;
}
auto job = new NetJob("Version index");
job->addNetAction(Net::Download::makeByteArray(QUrl(updatedVersion->getUrl()), &versionIndexData));
specificVersionDownloadJob.reset(job);
connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(json_downloaded()));
connect(specificVersionDownloadJob.get(), SIGNAL(failed(QString)), SIGNAL(failed(QString)));
connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
specificVersionDownloadJob->start();
}
bool MCVListVersionUpdateTask::canAbort() const
{
return true;
}
bool MCVListVersionUpdateTask::abort()
{
m_aborted = true;
if(specificVersionDownloadJob)
{
return specificVersionDownloadJob->abort();
}
return true;
}
void MCVListVersionUpdateTask::json_downloaded()
{
specificVersionDownloadJob.reset();
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(versionIndexData, &jsonError);
versionIndexData.clear();
if (jsonError.error != QJsonParseError::NoError)
{
emitFailed(tr("The download version file is not valid."));
return;
}
VersionFilePtr file;
try
{
file = MojangVersionFormat::versionFileFromJson(jsonDoc, "net.minecraft.json");
}
catch (Exception &e)
{
emitFailed(tr("Couldn't process version file: %1").arg(e.cause()));
return;
}
// Strip LWJGL from the version file. We use our own.
ProfileUtils::removeLwjglFromPatch(file);
// TODO: recognize and add LWJGL versions here.
file->fileId = "net.minecraft";
// now dump the file to disk
auto doc = OneSixVersionFormat::versionFileToJson(file, false);
auto newdata = doc.toBinaryData();
auto id = updatedVersion->descriptor();
QString targetPath = "versions/" + id + "/" + id + ".dat";
FS::ensureFilePathExists(targetPath);
QSaveFile vfile1(targetPath);
if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
{
emitFailed(tr("Can't open %1 for writing.").arg(targetPath));
return;
}
qint64 actual = 0;
if ((actual = vfile1.write(newdata)) != newdata.size())
{
emitFailed(tr("Failed to write into %1. Written %2 out of %3.")
.arg(targetPath)
.arg(actual)
.arg(newdata.size()));
return;
}
if (!vfile1.commit())
{
emitFailed(tr("Can't commit changes to %1").arg(targetPath));
return;
}
m_list->finalizeUpdate(id);
emitSucceeded();
}
std::shared_ptr<Task> MinecraftVersionList::createUpdateTask(QString version)
{
auto iter = m_lookup.find(version);
if(iter == m_lookup.end())
return nullptr;
auto mcversion = std::dynamic_pointer_cast<MinecraftVersion>(*iter);
if(!mcversion)
{
return nullptr;
}
return std::shared_ptr<Task>(new MCVListVersionUpdateTask(this, mcversion));
}
void MinecraftVersionList::saveCachedList()
{
// FIXME: throw.
if (!FS::ensureFilePathExists(localVersionCache))
return;
QSaveFile tfile(localVersionCache);
if (!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate))
return;
QJsonObject toplevel;
QJsonArray entriesArr;
for (auto version : m_vlist)
{
auto mcversion = std::dynamic_pointer_cast<MinecraftVersion>(version);
// do not save the remote versions.
if (mcversion->m_versionSource != VersionSource::Local)
continue;
QJsonObject entryObj;
entryObj.insert("id", mcversion->descriptor());
entryObj.insert("version", mcversion->descriptor());
entryObj.insert("time", timeToS3Time(mcversion->m_updateTime));
entryObj.insert("releaseTime", timeToS3Time(mcversion->m_releaseTime));
entryObj.insert("url", mcversion->m_versionFileURL);
entryObj.insert("type", mcversion->m_type);
entriesArr.append(entryObj);
}
toplevel.insert("versions", entriesArr);
{
bool someLatest = false;
QJsonObject latestObj;
if(!m_latestReleaseID.isNull())
{
latestObj.insert("release", m_latestReleaseID);
someLatest = true;
}
if(!m_latestSnapshotID.isNull())
{
latestObj.insert("snapshot", m_latestSnapshotID);
someLatest = true;
}
if(someLatest)
{
toplevel.insert("latest", latestObj);
}
}
QJsonDocument doc(toplevel);
QByteArray jsonData = doc.toBinaryData();
qint64 result = tfile.write(jsonData);
if (result == -1)
return;
if (result != jsonData.size())
return;
tfile.commit();
}
void MinecraftVersionList::finalizeUpdate(QString version)
{
int idx = -1;
for (int i = 0; i < m_vlist.size(); i++)
{
if (version == m_vlist[i]->descriptor())
{
idx = i;
break;
}
}
if (idx == -1)
{
return;
}
auto updatedVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[idx]);
// reject any updates to builtin versions.
if (updatedVersion->m_versionSource == VersionSource::Builtin)
return;
// if we have an update for the version, replace it, make the update local
if (updatedVersion->upstreamUpdate)
{
auto updatedWith = updatedVersion->upstreamUpdate;
updatedWith->m_versionSource = VersionSource::Local;
m_vlist[idx] = updatedWith;
m_lookup[version] = updatedWith;
}
else
{
// otherwise, just set the version as local;
updatedVersion->m_versionSource = VersionSource::Local;
}
dataChanged(index(idx), index(idx));
saveCachedList();
}
#include "MinecraftVersionList.moc"

View File

@ -1,73 +0,0 @@
/* 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 <QSet>
#include "BaseVersionList.h"
#include "tasks/Task.h"
#include "minecraft/MinecraftVersion.h"
#include <net/NetJob.h>
#include "multimc_logic_export.h"
class MCVListLoadTask;
class MCVListVersionUpdateTask;
class MULTIMC_LOGIC_EXPORT MinecraftVersionList : public BaseVersionList
{
Q_OBJECT
private:
void sortInternal();
void loadBuiltinList();
void loadMojangList(QJsonDocument jsonDoc, VersionSource source);
void loadCachedList();
void saveCachedList();
void finalizeUpdate(QString version);
public:
friend class MCVListLoadTask;
friend class MCVListVersionUpdateTask;
explicit MinecraftVersionList(QObject *parent = 0);
std::shared_ptr<Task> createUpdateTask(QString version);
virtual Task *getLoadTask() override;
virtual bool isLoaded() override;
virtual const BaseVersionPtr at(int i) const override;
virtual int count() const override;
virtual void sortVersions() override;
virtual QVariant data(const QModelIndex & index, int role) const override;
virtual RoleList providesRoles() const override;
virtual BaseVersionPtr getLatestStable() const override;
virtual BaseVersionPtr getRecommended() const override;
protected:
QList<BaseVersionPtr> m_vlist;
QMap<QString, BaseVersionPtr> m_lookup;
bool m_loaded = false;
bool m_hasLocalIndex = false;
QString m_latestReleaseID = "INVALID";
QString m_latestSnapshotID = "INVALID";
protected
slots:
virtual void updateListData(QList<BaseVersionPtr> versions) override;
};

View File

@ -1,6 +1,5 @@
#include "MojangVersionFormat.h"
#include "onesix/OneSixVersionFormat.h"
#include "MinecraftVersion.h"
#include "VersionBuildError.h"
#include "MojangDownloadInfo.h"
@ -152,7 +151,7 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
}
else if (!toCompare.isEmpty())
{
out->addProblem(PROBLEM_ERROR, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
}
}
Bits::readString(in, "type", out->type);
@ -167,8 +166,8 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets);
}
out->m_releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
out->m_updateTime = timeFromS3Time(in.value("time").toString(""));
out->releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
out->updateTime = timeFromS3Time(in.value("time").toString(""));
if (in.contains("minimumLauncherVersion"))
{
@ -176,7 +175,7 @@ void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFi
if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
{
out->addProblem(
PROBLEM_WARNING,
ProblemSeverity::Warning,
QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!")
.arg(out->minimumLauncherVersion)
.arg(CURRENT_MINIMUM_LAUNCHER_VERSION));
@ -211,9 +210,9 @@ VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc
readVersionProperties(root, out.get());
out->name = "Minecraft";
out->fileId = "net.minecraft";
out->uid = "net.minecraft";
out->version = out->minecraftVersion;
out->filename = filename;
// out->filename = filename;
if (root.contains("libraries"))
@ -235,13 +234,13 @@ void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObj
writeString(out, "mainClass", in->mainClass);
writeString(out, "minecraftArguments", in->minecraftArguments);
writeString(out, "type", in->type);
if(!in->m_releaseTime.isNull())
if(!in->releaseTime.isNull())
{
writeString(out, "releaseTime", timeToS3Time(in->m_releaseTime));
writeString(out, "releaseTime", timeToS3Time(in->releaseTime));
}
if(!in->m_updateTime.isNull())
if(!in->updateTime.isNull())
{
writeString(out, "time", timeToS3Time(in->m_updateTime));
writeString(out, "time", timeToS3Time(in->updateTime));
}
if(in->minimumLauncherVersion != -1)
{

View File

@ -0,0 +1,175 @@
#include <meta/VersionList.h>
#include <meta/Index.h>
#include <Env.h>
#include "ProfilePatch.h"
#include "meta/Version.h"
#include "VersionFile.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)
{
}
void ProfilePatch::applyTo(MinecraftProfile* profile)
{
auto vfile = getVersionFile();
if(vfile)
{
vfile->applyTo(profile);
}
}
std::shared_ptr<class VersionFile> ProfilePatch::getVersionFile()
{
if(m_metaVersion)
{
if(!m_metaVersion->isLoaded())
{
m_metaVersion->load();
}
return m_metaVersion->data();
}
return m_file;
}
std::shared_ptr<class Meta::VersionList> ProfilePatch::getVersionList()
{
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()
{
auto file = getVersionFile();
if(file)
{
return file->getProblemSeverity();
}
return ProblemSeverity::Error;
}
const QList<PatchProblem> ProfilePatch::getProblems()
{
auto file = getVersionFile();
if(file)
{
return file->getProblems();
}
return {PatchProblem(ProblemSeverity::Error, QObject::tr("Patch is not loaded yet."))};
}

View File

@ -5,101 +5,67 @@
#include <QJsonDocument>
#include <QDateTime>
#include "JarMod.h"
#include "ProblemProvider.h"
class MinecraftProfile;
enum ProblemSeverity
namespace Meta
{
PROBLEM_NONE,
PROBLEM_WARNING,
PROBLEM_ERROR
};
class Version;
class VersionList;
}
class VersionFile;
/// where is a version from?
enum class VersionSource
{
Builtin, //!< version loaded from the internal resources.
Local, //!< version loaded from a file in the cache.
Remote, //!< incomplete version on a remote server.
};
class PatchProblem
class ProfilePatch : public ProblemProvider
{
public:
PatchProblem(ProblemSeverity severity, const QString & description)
{
m_severity = severity;
m_description = description;
}
const QString & getDescription() const
{
return m_description;
}
const ProblemSeverity getSeverity() const
{
return m_severity;
}
private:
ProblemSeverity m_severity;
QString m_description;
};
ProfilePatch(std::shared_ptr<Meta::Version> version);
ProfilePatch(std::shared_ptr<VersionFile> file, const QString &filename = QString());
class ProfilePatch : public std::enable_shared_from_this<ProfilePatch>
{
public:
virtual ~ProfilePatch(){};
virtual void applyTo(MinecraftProfile *profile) = 0;
virtual void applyTo(MinecraftProfile *profile);
virtual bool isMinecraftVersion() = 0;
virtual bool hasJarMods() = 0;
virtual QList<JarmodPtr> getJarMods() = 0;
virtual bool isMoveable();
virtual bool isCustomizable();
virtual bool isRevertible();
virtual bool isRemovable();
virtual bool isCustom();
virtual bool isVersionChangeable();
virtual bool isMoveable() = 0;
virtual bool isCustomizable() = 0;
virtual bool isRevertible() = 0;
virtual bool isRemovable() = 0;
virtual bool isCustom() = 0;
virtual bool isEditable() = 0;
virtual bool isVersionChangeable() = 0;
virtual void setOrder(int order);
virtual int getOrder();
virtual void setOrder(int order) = 0;
virtual int getOrder() = 0;
virtual QString getID();
virtual QString getName();
virtual QString getVersion();
virtual QDateTime getReleaseDateTime();
virtual QString getID() = 0;
virtual QString getName() = 0;
virtual QString getVersion() = 0;
virtual QDateTime getReleaseDateTime() = 0;
virtual QString getFilename();
virtual QString getFilename() = 0;
virtual std::shared_ptr<class VersionFile> getVersionFile();
virtual std::shared_ptr<class Meta::VersionList> getVersionList();
virtual VersionSource getVersionSource() = 0;
void setVanilla (bool state);
void setRemovable (bool state);
void setRevertible (bool state);
void setMovable (bool state);
virtual std::shared_ptr<class VersionFile> getVersionFile() = 0;
virtual const QList<PatchProblem>& getProblems()
{
return m_problems;
}
virtual void addProblem(ProblemSeverity severity, const QString &description)
{
if(severity > m_problemSeverity)
{
m_problemSeverity = severity;
}
m_problems.append(PatchProblem(severity, description));
}
virtual ProblemSeverity getProblemSeverity()
{
return m_problemSeverity;
}
virtual bool hasFailed()
{
return getProblemSeverity() == PROBLEM_ERROR;
}
const QList<PatchProblem> getProblems() override;
ProblemSeverity getProblemSeverity() override;
protected:
QList<PatchProblem> m_problems;
ProblemSeverity m_problemSeverity = PROBLEM_NONE;
// 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;

View File

@ -1,6 +1,7 @@
#pragma once
#include "ProfileUtils.h"
#include "ProfilePatch.h"
class MinecraftProfile;

View File

@ -102,9 +102,9 @@ bool readOverrideOrders(QString path, PatchOrder &order)
static VersionFilePtr createErrorVersionFile(QString fileId, QString filepath, QString error)
{
auto outError = std::make_shared<VersionFile>();
outError->fileId = outError->name = fileId;
outError->filename = filepath;
outError->addProblem(PROBLEM_ERROR, error);
outError->uid = outError->name = fileId;
// outError->filename = filepath;
outError->addProblem(ProblemSeverity::Error, error);
return outError;
}

View File

@ -26,23 +26,6 @@ public:
}
};
/**
* some patch was intended for a different version of minecraft
*/
class MinecraftVersionMismatch : public VersionBuildError
{
public:
MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
: VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
"(%2) than that of the instance (%3).")
.arg(fileId)
.arg(mcVersion)
.arg(parentMcVersion)) {};
virtual ~MinecraftVersionMismatch() noexcept
{
}
};
/**
* files required for the version are not (yet?) present
*/

View File

@ -12,37 +12,28 @@
#include "VersionBuildError.h"
#include <Version.h>
bool VersionFile::isMinecraftVersion()
static bool isMinecraftVersion(const QString &uid)
{
return fileId == "net.minecraft";
}
bool VersionFile::hasJarMods()
{
return !jarMods.isEmpty();
return uid == "net.minecraft";
}
void VersionFile::applyTo(MinecraftProfile *profile)
{
auto theirVersion = profile->getMinecraftVersion();
if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull())
// Only real Minecraft can set those. Don't let anything override them.
if (isMinecraftVersion(uid))
{
if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1)
{
throw MinecraftVersionMismatch(fileId, dependsOnMinecraftVersion, theirVersion);
}
profile->applyMinecraftVersion(minecraftVersion);
profile->applyMinecraftVersionType(type);
// HACK: ignore assets from other version files than Minecraft
// workaround for stupid assets issue caused by amazon:
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
profile->applyMinecraftAssets(mojangAssetIndex);
}
profile->applyMinecraftVersion(minecraftVersion);
profile->applyMainClass(mainClass);
profile->applyAppletClass(appletClass);
profile->applyMinecraftArguments(minecraftArguments);
if (isMinecraftVersion())
{
profile->applyMinecraftVersionType(type);
}
profile->applyMinecraftAssets(mojangAssetIndex);
profile->applyTweakers(addTweakers);
profile->applyJarMods(jarMods);
profile->applyTraits(traits);
@ -58,3 +49,14 @@ void VersionFile::applyTo(MinecraftProfile *profile)
iter++;
}
}
/*
auto theirVersion = profile->getMinecraftVersion();
if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull())
{
if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1)
{
throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion);
}
}
*/

View File

@ -8,7 +8,7 @@
#include <memory>
#include "minecraft/OpSys.h"
#include "minecraft/Rule.h"
#include "ProfilePatch.h"
#include "ProblemProvider.h"
#include "Library.h"
#include "JarMod.h"
@ -18,126 +18,25 @@ struct MojangDownloadInfo;
struct MojangAssetIndexInfo;
typedef std::shared_ptr<VersionFile> VersionFilePtr;
class VersionFile : public ProfilePatch
class VersionFile : public ProblemContainer
{
friend class MojangVersionFormat;
friend class OneSixVersionFormat;
public: /* methods */
virtual void applyTo(MinecraftProfile *profile) override;
virtual bool isMinecraftVersion() override;
virtual bool hasJarMods() override;
virtual int getOrder() override
{
return order;
}
virtual void setOrder(int order) override
{
this->order = order;
}
virtual QList<JarmodPtr> getJarMods() override
{
return jarMods;
}
virtual QString getID() override
{
return fileId;
}
virtual QString getName() override
{
return name;
}
virtual QString getVersion() override
{
return version;
}
virtual QString getFilename() override
{
return filename;
}
virtual QDateTime getReleaseDateTime() override
{
return m_releaseTime;
}
VersionSource getVersionSource() override
{
return VersionSource::Local;
}
std::shared_ptr<class VersionFile> getVersionFile() override
{
return std::dynamic_pointer_cast<VersionFile>(shared_from_this());
}
virtual bool isCustom() override
{
return !m_isVanilla;
};
virtual bool isCustomizable() override
{
return m_isCustomizable;
}
virtual bool isRemovable() override
{
return m_isRemovable;
}
virtual bool isRevertible() override
{
return m_isRevertible;
}
virtual bool isMoveable() override
{
return m_isMovable;
}
virtual bool isEditable() override
{
return isCustom();
}
virtual bool isVersionChangeable() override
{
return false;
}
void setVanilla (bool state)
{
m_isVanilla = state;
}
void setRemovable (bool state)
{
m_isRemovable = state;
}
void setRevertible (bool state)
{
m_isRevertible = state;
}
void setCustomizable (bool state)
{
m_isCustomizable = state;
}
void setMovable (bool state)
{
m_isMovable = state;
}
void applyTo(MinecraftProfile *profile);
public: /* data */
/// MultiMC: order hint for this version file if no explicit order is set
int order = 0;
// Flags for UI and version file manipulation in general
bool m_isVanilla = false;
bool m_isRemovable = false;
bool m_isRevertible = false;
bool m_isCustomizable = false;
bool m_isMovable = false;
/// MultiMC: filename of the file this was loaded from
QString filename;
// QString filename;
/// MultiMC: human readable name of this package
QString name;
/// MultiMC: package ID of this package
QString fileId;
QString uid;
/// MultiMC: version of this package
QString version;
@ -148,13 +47,13 @@ public: /* data */
/// Mojang: used to version the Mojang version format
int minimumLauncherVersion = -1;
/// Mojang: version of Minecraft this is
/// Mojang: DEPRECATED version of Minecraft this is
QString minecraftVersion;
/// Mojang: class to launch Minecraft with
QString mainClass;
/// MultiMC: DEPRECATED class to launch legacy Minecraft with (ambed in a custom window)
/// MultiMC: class to launch legacy Minecraft with (embed in a custom window)
QString appletClass;
/// Mojang: Minecraft launch arguments (may contain placeholders for variable substitution)
@ -164,10 +63,10 @@ public: /* data */
QString type;
/// Mojang: the time this version was actually released by Mojang
QDateTime m_releaseTime;
QDateTime releaseTime;
/// Mojang: the time this version was last updated by Mojang
QDateTime m_updateTime;
QDateTime updateTime;
/// Mojang: DEPRECATED asset group to be used with Minecraft
QString assets;
@ -191,5 +90,3 @@ public:
// Mojang: extended asset index download information
std::shared_ptr<MojangAssetIndexInfo> mojangAssetIndex;
};

View File

@ -58,18 +58,11 @@ VersionFilterData::VersionFilterData()
// don't use installers for those.
forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
// these won't show up in version lists because they are extremely bad and dangerous
legacyBlacklist = QSet<QString>({"rd-160052"});
/*
* nothing older than this will be accepted from Mojang servers
* (these versions need to be tested by us first)
*/
// FIXME: remove, used for deciding when core mods should display
legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00");
lwjglWhitelist =
QSet<QString>{"net.java.jinput:jinput", "net.java.jinput:jinput-platform",
"net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl",
"org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"};
// Version list magic
recommendedMinecraftVersion = "1.7.10";
}

View File

@ -20,13 +20,9 @@ struct VersionFilterData
QMap<QString, QList<FMLlib>> fmlLibsMapping;
// set of minecraft versions for which using forge installers is blacklisted
QSet<QString> forgeInstallerBlacklist;
// set of 'legacy' versions that will not show up in the version lists.
QSet<QString> legacyBlacklist;
// no new versions below this date will be accepted from Mojang servers
QDateTime legacyCutoffDate;
// Libraries that belong to LWJGL
QSet<QString> lwjglWhitelist;
// Currently recommended minecraft version
QString recommendedMinecraftVersion;
};
extern VersionFilterData MULTIMC_LOGIC_EXPORT g_VersionFilterData;

View File

@ -1,458 +0,0 @@
/* 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.
*/
#include "ForgeInstaller.h"
#include "ForgeVersionList.h"
#include "minecraft/MinecraftProfile.h"
#include "minecraft/GradleSpecifier.h"
#include "net/HttpMetaCache.h"
#include "tasks/Task.h"
#include "minecraft/onesix/OneSixInstance.h"
#include <minecraft/onesix/OneSixVersionFormat.h>
#include "minecraft/VersionFilterData.h"
#include "minecraft/MinecraftVersion.h"
#include "Env.h"
#include "Exception.h"
#include <FileSystem.h>
#include <quazip.h>
#include <quazipfile.h>
#include <QStringList>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QJsonDocument>
#include <QJsonArray>
#include <QSaveFile>
#include <QCryptographicHash>
ForgeInstaller::ForgeInstaller() : BaseInstaller()
{
}
void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl)
{
VersionFilePtr newVersion;
m_universal_url = universalUrl;
QuaZip zip(filename);
if (!zip.open(QuaZip::mdUnzip))
return;
QuaZipFile file(&zip);
// read the install profile
if (!zip.setCurrentFile("install_profile.json"))
return;
QJsonParseError jsonError;
if (!file.open(QIODevice::ReadOnly))
return;
QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll(), &jsonError);
file.close();
if (jsonError.error != QJsonParseError::NoError)
return;
if (!jsonDoc.isObject())
return;
QJsonObject root = jsonDoc.object();
auto installVal = root.value("install");
auto versionInfoVal = root.value("versionInfo");
if (!installVal.isObject() || !versionInfoVal.isObject())
return;
try
{
newVersion = OneSixVersionFormat::versionFileFromJson(QJsonDocument(versionInfoVal.toObject()), QString(), false);
}
catch(Exception &err)
{
qWarning() << "Forge: Fatal error while parsing version file:" << err.what();
return;
}
for(auto problem: newVersion->getProblems())
{
qWarning() << "Forge: Problem found: " << problem.getDescription();
}
if(newVersion->getProblemSeverity() == ProblemSeverity::PROBLEM_ERROR)
{
qWarning() << "Forge: Errors found while parsing version file";
return;
}
QJsonObject installObj = installVal.toObject();
QString libraryName = installObj.value("path").toString();
internalPath = installObj.value("filePath").toString();
m_forgeVersionString = installObj.value("version").toString().remove("Forge", Qt::CaseInsensitive).trimmed();
// where do we put the library? decode the mojang path
GradleSpecifier lib(libraryName);
auto cacheentry = ENV.metacache()->resolveEntry("libraries", lib.toPath());
finalPath = "libraries/" + lib.toPath();
if (!FS::ensureFilePathExists(finalPath))
return;
if (!zip.setCurrentFile(internalPath))
return;
if (!file.open(QIODevice::ReadOnly))
return;
{
QByteArray data = file.readAll();
// extract file
QSaveFile extraction(finalPath);
if (!extraction.open(QIODevice::WriteOnly))
return;
if (extraction.write(data) != data.size())
return;
if (!extraction.commit())
return;
QCryptographicHash md5sum(QCryptographicHash::Md5);
md5sum.addData(data);
cacheentry->setStale(false);
cacheentry->setMD5Sum(md5sum.result().toHex().constData());
ENV.metacache()->updateEntry(cacheentry);
}
file.close();
m_forge_json = newVersion;
}
bool ForgeInstaller::add(OneSixInstance *to)
{
if (!BaseInstaller::add(to))
{
return false;
}
if (!m_forge_json)
{
return false;
}
// A blacklist
QSet<QString> blacklist{"authlib", "realms"};
QList<QString> xzlist{"org.scala-lang", "com.typesafe"};
// get the minecraft version from the instance
VersionFilePtr minecraft;
auto minecraftPatch = to->getMinecraftProfile()->versionPatch("net.minecraft");
if(minecraftPatch)
{
minecraft = std::dynamic_pointer_cast<VersionFile>(minecraftPatch);
if(!minecraft)
{
auto mcWrap = std::dynamic_pointer_cast<MinecraftVersion>(minecraftPatch);
if(mcWrap)
{
minecraft = mcWrap->getVersionFile();
}
}
}
// for each library in the version we are adding (except for the blacklisted)
QMutableListIterator<LibraryPtr> iter(m_forge_json->libraries);
while (iter.hasNext())
{
auto library = iter.next();
QString libName = library->artifactId();
QString libVersion = library->version();
QString rawName = library->rawName();
// ignore lwjgl libraries.
if (g_VersionFilterData.lwjglWhitelist.contains(library->artifactPrefix()))
{
iter.remove();
continue;
}
// ignore other blacklisted (realms, authlib)
if (blacklist.contains(libName))
{
iter.remove();
continue;
}
// if minecraft version was found, ignore everything that is already in the minecraft version
if(minecraft)
{
bool found = false;
for (auto & lib: minecraft->libraries)
{
if(library->artifactPrefix() == lib->artifactPrefix() && library->version() == lib->version())
{
found = true;
break;
}
}
if (found)
continue;
}
// if this is the actual forge lib, set an absolute url for the download
if (m_forge_version->type == ForgeVersion::Gradle)
{
if (libName == "forge")
{
library->setClassifier("universal");
}
else if (libName == "minecraftforge")
{
QString forgeCoord("net.minecraftforge:forge:%1:universal");
// using insane form of the MC version...
QString longVersion = m_forge_version->mcver + "-" + m_forge_version->jobbuildver;
GradleSpecifier spec(forgeCoord.arg(longVersion));
library->setRawName(spec);
}
}
else
{
if (libName.contains("minecraftforge"))
{
library->setAbsoluteUrl(m_universal_url);
}
}
// mark bad libraries based on the xzlist above
for (auto entry : xzlist)
{
qDebug() << "Testing " << rawName << " : " << entry;
if (rawName.startsWith(entry))
{
library->setHint("forge-pack-xz");
break;
}
}
}
QString &args = m_forge_json->minecraftArguments;
QStringList tweakers;
{
QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)");
QRegularExpressionMatch match = expression.match(args);
while (match.hasMatch())
{
tweakers.append(match.captured(1));
args.remove(match.capturedStart(), match.capturedLength());
match = expression.match(args);
}
if(tweakers.size())
{
args.operator=(args.trimmed());
m_forge_json->addTweakers = tweakers;
}
}
if(minecraft && args == minecraft->minecraftArguments)
{
args.clear();
}
m_forge_json->name = "Forge";
m_forge_json->fileId = id();
m_forge_json->version = m_forgeVersionString;
m_forge_json->dependsOnMinecraftVersion = to->intendedVersionId();
m_forge_json->order = 5;
// reset some things we do not want to be passed along.
m_forge_json->m_releaseTime = QDateTime();
m_forge_json->m_updateTime = QDateTime();
m_forge_json->minimumLauncherVersion = -1;
m_forge_json->type.clear();
m_forge_json->minecraftArguments.clear();
m_forge_json->minecraftVersion.clear();
QSaveFile file(filename(to->instanceRoot()));
if (!file.open(QFile::WriteOnly))
{
qCritical() << "Error opening" << file.fileName()
<< "for reading:" << file.errorString();
return false;
}
file.write(OneSixVersionFormat::versionFileToJson(m_forge_json, true).toJson());
file.commit();
return true;
}
bool ForgeInstaller::addLegacy(OneSixInstance *to)
{
if (!BaseInstaller::add(to))
{
return false;
}
auto entry = ENV.metacache()->resolveEntry("minecraftforge", m_forge_version->filename());
finalPath = FS::PathCombine(to->jarModsDir(), m_forge_version->filename());
if (!FS::ensureFilePathExists(finalPath))
{
return false;
}
if (!QFile::copy(entry->getFullPath(), finalPath))
{
return false;
}
QJsonObject obj;
obj.insert("order", 5);
{
QJsonArray jarmodsPlus;
{
QJsonObject libObj;
libObj.insert("name", m_forge_version->universal_filename);
jarmodsPlus.append(libObj);
}
obj.insert("+jarMods", jarmodsPlus);
}
obj.insert("name", QString("Forge"));
obj.insert("fileId", id());
obj.insert("version", m_forge_version->jobbuildver);
obj.insert("mcVersion", to->intendedVersionId());
if (g_VersionFilterData.fmlLibsMapping.contains(m_forge_version->mcver))
{
QJsonArray traitsPlus;
traitsPlus.append(QString("legacyFML"));
obj.insert("+traits", traitsPlus);
}
auto fullversion = to->getMinecraftProfile();
fullversion->remove("net.minecraftforge");
QFile file(filename(to->instanceRoot()));
if (!file.open(QFile::WriteOnly))
{
qCritical() << "Error opening" << file.fileName()
<< "for reading:" << file.errorString();
return false;
}
file.write(QJsonDocument(obj).toJson());
file.close();
return true;
}
class ForgeInstallTask : public Task
{
Q_OBJECT
public:
ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance,
BaseVersionPtr version, QObject *parent = 0)
: Task(parent), m_installer(installer), m_instance(instance), m_version(version)
{
}
protected:
void executeTask() override
{
setStatus(tr("Installing Forge..."));
ForgeVersionPtr forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(m_version);
if (!forgeVersion)
{
emitFailed(tr("Unknown error occured"));
return;
}
prepare(forgeVersion);
}
void prepare(ForgeVersionPtr forgeVersion)
{
auto entry = ENV.metacache()->resolveEntry("minecraftforge", forgeVersion->filename());
auto installFunction = [this, entry, forgeVersion]()
{
if (!install(entry, forgeVersion))
{
qCritical() << "Failure installing Forge";
emitFailed(tr("Failure to install Forge"));
}
else
{
reload();
}
};
/*
* HACK IF the local non-stale file is too small, mark is as stale
*
* This fixes some problems with bad files acquired because of unhandled HTTP redirects
* in old versions of MultiMC.
*/
if (!entry->isStale())
{
QFileInfo localFile(entry->getFullPath());
if (localFile.size() <= 0x4000)
{
entry->setStale(true);
}
}
if (entry->isStale())
{
NetJob *fjob = new NetJob("Forge download");
fjob->addNetAction(Net::Download::makeCached(forgeVersion->url(), entry));
connect(fjob, &NetJob::progress, this, &Task::setProgress);
connect(fjob, &NetJob::status, this, &Task::setStatus);
connect(fjob, &NetJob::failed, [this](QString reason)
{ emitFailed(tr("Failure to download Forge:\n%1").arg(reason)); });
connect(fjob, &NetJob::succeeded, installFunction);
fjob->start();
}
else
{
installFunction();
}
}
bool install(const std::shared_ptr<MetaEntry> &entry, const ForgeVersionPtr &forgeVersion)
{
if (forgeVersion->usesInstaller())
{
QString forgePath = entry->getFullPath();
m_installer->prepare(forgePath, forgeVersion->universal_url);
return m_installer->add(m_instance);
}
else
return m_installer->addLegacy(m_instance);
}
void reload()
{
try
{
m_instance->reloadProfile();
emitSucceeded();
}
catch (Exception &e)
{
emitFailed(e.cause());
}
catch (...)
{
emitFailed(tr("Failed to load the version description file for reasons unknown."));
}
}
private:
ForgeInstaller *m_installer;
OneSixInstance *m_instance;
BaseVersionPtr m_version;
};
Task *ForgeInstaller::createInstallTask(OneSixInstance *instance,
BaseVersionPtr version, QObject *parent)
{
if (!version)
{
return nullptr;
}
m_forge_version = std::dynamic_pointer_cast<ForgeVersion>(version);
return new ForgeInstallTask(this, instance, version, parent);
}
#include "ForgeInstaller.moc"

View File

@ -1,52 +0,0 @@
/* 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 "BaseInstaller.h"
#include <QString>
#include <memory>
#include "multimc_logic_export.h"
class VersionFile;
class ForgeInstallTask;
struct ForgeVersion;
class MULTIMC_LOGIC_EXPORT ForgeInstaller : public BaseInstaller
{
friend class ForgeInstallTask;
public:
ForgeInstaller();
virtual ~ForgeInstaller(){}
virtual Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
virtual QString id() const override { return "net.minecraftforge"; }
protected:
void prepare(const QString &filename, const QString &universalUrl);
bool add(OneSixInstance *to) override;
bool addLegacy(OneSixInstance *to);
private:
// the parsed version json, read from the installer
std::shared_ptr<VersionFile> m_forge_json;
// the actual forge version
std::shared_ptr<ForgeVersion> m_forge_version;
QString internalPath;
QString finalPath;
QString m_forgeVersionString;
QString m_universal_url;
};

View File

@ -1,55 +0,0 @@
#include "ForgeVersion.h"
#include "minecraft/VersionFilterData.h"
#include <QObject>
QString ForgeVersion::name()
{
return "Forge " + jobbuildver;
}
QString ForgeVersion::descriptor()
{
return universal_filename;
}
QString ForgeVersion::typeString() const
{
if (is_recommended)
return QObject::tr("Recommended");
return QString();
}
bool ForgeVersion::operator<(BaseVersion &a)
{
ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
if (!pa)
return true;
return m_buildnr < pa->m_buildnr;
}
bool ForgeVersion::operator>(BaseVersion &a)
{
ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
if (!pa)
return false;
return m_buildnr > pa->m_buildnr;
}
bool ForgeVersion::usesInstaller()
{
if(installer_url.isEmpty())
return false;
if(g_VersionFilterData.forgeInstallerBlacklist.contains(mcver))
return false;
return true;
}
QString ForgeVersion::filename()
{
return usesInstaller() ? installer_filename : universal_filename;
}
QString ForgeVersion::url()
{
return usesInstaller() ? installer_url : universal_url;
}

View File

@ -1,42 +0,0 @@
#pragma once
#include <QString>
#include <memory>
#include "BaseVersion.h"
struct ForgeVersion;
typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
struct ForgeVersion : public BaseVersion
{
virtual QString descriptor() override;
virtual QString name() override;
virtual QString typeString() const override;
virtual bool operator<(BaseVersion &a) override;
virtual bool operator>(BaseVersion &a) override;
QString filename();
QString url();
enum
{
Invalid,
Legacy,
Gradle
} type = Invalid;
bool usesInstaller();
int m_buildnr = 0;
QString branch;
QString universal_url;
QString changelog_url;
QString installer_url;
QString jobbuildver;
QString mcver;
QString mcver_sane;
QString universal_filename;
QString installer_filename;
bool is_recommended = false;
};
Q_DECLARE_METATYPE(ForgeVersionPtr)

View File

@ -1,333 +0,0 @@
/* 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.
*/
#include "ForgeVersionList.h"
#include "ForgeVersion.h"
#include "net/NetJob.h"
#include "net/URLConstants.h"
#include "Env.h"
#include <QtNetwork>
#include <QtXml>
#include <QRegExp>
#include <QDebug>
ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent)
{
}
Task *ForgeVersionList::getLoadTask()
{
return new ForgeListLoadTask(this);
}
bool ForgeVersionList::isLoaded()
{
return m_loaded;
}
const BaseVersionPtr ForgeVersionList::at(int i) const
{
return m_vlist.at(i);
}
int ForgeVersionList::count() const
{
return m_vlist.count();
}
int ForgeVersionList::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant ForgeVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() > count())
return QVariant();
auto version = std::dynamic_pointer_cast<ForgeVersion>(m_vlist[index.row()]);
switch (role)
{
case VersionPointerRole:
return qVariantFromValue(m_vlist[index.row()]);
case VersionRole:
return version->name();
case VersionIdRole:
return version->descriptor();
case ParentGameVersionRole:
return version->mcver_sane;
case RecommendedRole:
return version->is_recommended;
case BranchRole:
return version->branch;
default:
return QVariant();
}
}
BaseVersionList::RoleList ForgeVersionList::providesRoles() const
{
return {VersionPointerRole, VersionRole, VersionIdRole, ParentGameVersionRole, RecommendedRole, BranchRole};
}
BaseVersionPtr ForgeVersionList::getLatestStable() const
{
return BaseVersionPtr();
}
void ForgeVersionList::updateListData(QList<BaseVersionPtr> versions)
{
beginResetModel();
m_vlist = versions;
m_loaded = true;
endResetModel();
// NOW SORT!!
// sort();
}
void ForgeVersionList::sortVersions()
{
// NO-OP for now
}
ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task()
{
m_list = vlist;
}
void ForgeListLoadTask::executeTask()
{
setStatus(tr("Fetching Forge version lists..."));
auto job = new NetJob("Version index");
// we do not care if the version is stale or not.
auto forgeListEntry = ENV.metacache()->resolveEntry("minecraftforge", "list.json");
auto gradleForgeListEntry = ENV.metacache()->resolveEntry("minecraftforge", "json");
// verify by poking the server.
forgeListEntry->setStale(true);
gradleForgeListEntry->setStale(true);
job->addNetAction(listDownload = Net::Download::makeCached(QUrl(URLConstants::FORGE_LEGACY_URL),forgeListEntry));
job->addNetAction(gradleListDownload = Net::Download::makeCached(QUrl(URLConstants::FORGE_GRADLE_URL), gradleForgeListEntry));
connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed()));
connect(gradleListDownload.get(), SIGNAL(failed(int)), SLOT(gradleListFailed()));
listJob.reset(job);
connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded()));
connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
listJob->start();
}
bool ForgeListLoadTask::abort()
{
return listJob->abort();
}
bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
{
QMap<int, std::shared_ptr<ForgeVersion>> lookup;
QByteArray data;
{
auto filename = gradleListDownload->getTargetFilepath();
QFile listFile(filename);
if (!listFile.open(QIODevice::ReadOnly))
{
return false;
}
data = listFile.readAll();
gradleListDownload.reset();
}
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
emitFailed("Error parsing gradle version list JSON:" + jsonError.errorString());
return false;
}
if (!jsonDoc.isObject())
{
emitFailed("Error parsing gradle version list JSON: JSON root is not an object");
return false;
}
QJsonObject root = jsonDoc.object();
// we probably could hard code these, but it might still be worth doing it this way
const QString webpath = root.value("webpath").toString();
const QString artifact = root.value("artifact").toString();
QJsonObject numbers = root.value("number").toObject();
for (auto it = numbers.begin(); it != numbers.end(); ++it)
{
QJsonObject number = it.value().toObject();
std::shared_ptr<ForgeVersion> fVersion(new ForgeVersion());
fVersion->m_buildnr = number.value("build").toDouble();
if(fVersion->m_buildnr >= 953 && fVersion->m_buildnr <= 965)
{
qDebug() << fVersion->m_buildnr;
}
fVersion->jobbuildver = number.value("version").toString();
fVersion->branch = number.value("branch").toString("");
fVersion->mcver = number.value("mcversion").toString();
fVersion->universal_filename = "";
fVersion->installer_filename = "";
// HACK: here, we fix the minecraft version used by forge.
// HACK: this will inevitably break (later)
// FIXME: replace with a dictionary
fVersion->mcver_sane = fVersion->mcver;
fVersion->mcver_sane.replace("_pre", "-pre");
QString universal_filename, installer_filename;
QJsonArray files = number.value("files").toArray();
for (auto fIt = files.begin(); fIt != files.end(); ++fIt)
{
// TODO with gradle we also get checksums, use them
QJsonArray file = (*fIt).toArray();
if (file.size() < 3)
{
continue;
}
QString extension = file.at(0).toString();
QString part = file.at(1).toString();
QString checksum = file.at(2).toString();
// insane form of mcver is used here
QString longVersion = fVersion->mcver + "-" + fVersion->jobbuildver;
if (!fVersion->branch.isEmpty())
{
longVersion = longVersion + "-" + fVersion->branch;
}
QString filename = artifact + "-" + longVersion + "-" + part + "." + extension;
QString url = QString("%1/%2/%3")
.arg(webpath)
.arg(longVersion)
.arg(filename);
if (part == "installer")
{
fVersion->installer_url = url;
installer_filename = filename;
}
else if (part == "universal" || part == "client")
{
fVersion->universal_url = url;
universal_filename = filename;
}
else if (part == "changelog")
{
fVersion->changelog_url = url;
}
}
if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty())
{
continue;
}
fVersion->universal_filename = universal_filename;
fVersion->installer_filename = installer_filename;
fVersion->type = ForgeVersion::Gradle;
out.append(fVersion);
lookup[fVersion->m_buildnr] = fVersion;
}
QJsonObject promos = root.value("promos").toObject();
for (auto it = promos.begin(); it != promos.end(); ++it)
{
QString key = it.key();
int build = it.value().toInt();
QRegularExpression regexp("^(?<mcversion>[0-9]+(.[0-9]+)*)-(?<label>[a-z]+)$");
auto match = regexp.match(key);
if(!match.hasMatch())
{
qDebug() << key << "doesn't match." << "build" << build;
continue;
}
QString label = match.captured("label");
if(label != "recommended")
{
continue;
}
QString mcversion = match.captured("mcversion");
qDebug() << "Forge build" << build << "is the" << label << "for Minecraft" << mcversion << QString("<%1>").arg(key);
lookup[build]->is_recommended = true;
}
return true;
}
void ForgeListLoadTask::listDownloaded()
{
QList<BaseVersionPtr> list;
bool ret = true;
if (!parseForgeGradleList(list))
{
ret = false;
}
if (!ret)
{
return;
}
std::sort(list.begin(), list.end(), [](const BaseVersionPtr & l, const BaseVersionPtr & r)
{ return (*l > *r); });
m_list->updateListData(list);
emitSucceeded();
return;
}
void ForgeListLoadTask::listFailed()
{
auto &reply = listDownload->m_reply;
if (reply)
{
qCritical() << "Getting forge version list failed: " << reply->errorString();
}
else
{
qCritical() << "Getting forge version list failed for reasons unknown.";
}
}
void ForgeListLoadTask::gradleListFailed()
{
auto &reply = gradleListDownload->m_reply;
if (reply)
{
qCritical() << "Getting forge version list failed: " << reply->errorString();
}
else
{
qCritical() << "Getting forge version list failed for reasons unknown.";
}
}

View File

@ -1,90 +0,0 @@
/* 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 "ForgeVersion.h"
#include <QObject>
#include <QAbstractListModel>
#include <QUrl>
#include <QNetworkReply>
#include "BaseVersionList.h"
#include "tasks/Task.h"
#include "net/NetJob.h"
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT ForgeVersionList : public BaseVersionList
{
Q_OBJECT
public:
friend class ForgeListLoadTask;
explicit ForgeVersionList(QObject *parent = 0);
virtual Task *getLoadTask() override;
virtual bool isLoaded() override;
virtual const BaseVersionPtr at(int i) const override;
virtual int count() const override;
virtual void sortVersions() override;
virtual BaseVersionPtr getLatestStable() const override;
ForgeVersionPtr findVersionByVersionNr(QString version);
virtual QVariant data(const QModelIndex &index, int role) const override;
virtual RoleList providesRoles() const override;
virtual int columnCount(const QModelIndex &parent) const override;
protected:
QList<BaseVersionPtr> m_vlist;
bool m_loaded = false;
protected
slots:
virtual void updateListData(QList<BaseVersionPtr> versions) override;
};
class ForgeListLoadTask : public Task
{
Q_OBJECT
public:
explicit ForgeListLoadTask(ForgeVersionList *vlist);
virtual void executeTask();
virtual bool abort();
protected
slots:
void listDownloaded();
void listFailed();
void gradleListFailed();
protected:
NetJobPtr listJob;
ForgeVersionList *m_list;
Net::Download::Ptr listDownload;
Net::Download::Ptr gradleListDownload;
private:
bool parseForgeList(QList<BaseVersionPtr> &out);
bool parseForgeGradleList(QList<BaseVersionPtr> &out);
};

View File

@ -1,56 +0,0 @@
/* 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.
*/
#include "LegacyForge.h"
MinecraftForge::MinecraftForge(const QString &file) : Mod(file)
{
}
bool MinecraftForge::FixVersionIfNeeded(QString newVersion)
{/*
wxString reportedVersion = GetModVersion();
if(reportedVersion == "..." || reportedVersion.empty())
{
std::auto_ptr<wxFFileInputStream> in(new wxFFileInputStream("forge.zip"));
wxTempFileOutputStream out("forge.zip");
wxTextOutputStream textout(out);
wxZipInputStream inzip(*in);
wxZipOutputStream outzip(out);
std::auto_ptr<wxZipEntry> entry;
// preserve metadata
outzip.CopyArchiveMetaData(inzip);
// copy all entries
while (entry.reset(inzip.GetNextEntry()), entry.get() != NULL)
if (!outzip.CopyEntry(entry.release(), inzip))
return false;
// release last entry
in.reset();
outzip.PutNextEntry("forgeversion.properties");
wxStringTokenizer tokenizer(newVersion,".");
wxString verFile;
verFile << wxString("forge.major.number=") << tokenizer.GetNextToken() << "\n";
verFile << wxString("forge.minor.number=") << tokenizer.GetNextToken() << "\n";
verFile << wxString("forge.revision.number=") << tokenizer.GetNextToken() << "\n";
verFile << wxString("forge.build.number=") << tokenizer.GetNextToken() << "\n";
auto buf = verFile.ToUTF8();
outzip.Write(buf.data(), buf.length());
// check if we succeeded
return inzip.Eof() && outzip.Close() && out.Commit();
}
*/
return true;
}

View File

@ -1,25 +0,0 @@
/* 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 "minecraft/Mod.h"
class MinecraftForge : public Mod
{
public:
MinecraftForge(const QString &file);
bool FixVersionIfNeeded(QString newVersion);
};

View File

@ -9,7 +9,6 @@
#include <FileSystem.h>
#include "Env.h"
#include "minecraft/MinecraftVersion.h"
#include "LegacyFTBInstance.h"
#include "OneSixFTBInstance.h"
@ -246,17 +245,8 @@ InstancePtr FTBInstanceProvider::createInstance(const FTBRecord & record) const
m_settings->registerSetting("InstanceType", "Legacy");
// all legacy versions are built in. therefore we can do this even if we don't have ALL the versions Mojang has on their servers.
auto mcVersion = std::dynamic_pointer_cast<MinecraftVersion>(ENV.getVersion("net.minecraft", record.mcVersion));
if (mcVersion && mcVersion->usesLegacyLauncher())
{
m_settings->set("InstanceType", "LegacyFTB");
inst.reset(new LegacyFTBInstance(m_globalSettings, m_settings, record.instanceDir));
}
else
{
m_settings->set("InstanceType", "OneSixFTB");
inst.reset(new OneSixFTBInstance(m_globalSettings, m_settings, record.instanceDir));
}
m_settings->set("InstanceType", "OneSixFTB");
inst.reset(new OneSixFTBInstance(m_globalSettings, m_settings, record.instanceDir));
// initialize
{

View File

@ -1,11 +1,9 @@
#include "FTBPlugin.h"
#include <Env.h>
#include "FTBVersion.h"
#include "LegacyFTBInstance.h"
#include "OneSixFTBInstance.h"
#include <BaseInstance.h>
#include <InstanceList.h>
#include <minecraft/MinecraftVersionList.h>
#include <settings/INISettingsObject.h>
#include <FileSystem.h>

View File

@ -2,7 +2,6 @@
#include "OneSixFTBInstance.h"
#include "minecraft/VersionBuildError.h"
#include "minecraft/MinecraftVersionList.h"
#include <FileSystem.h>
#include <QDir>
@ -28,9 +27,8 @@ void FTBProfileStrategy::loadDefaultBuiltinPatches()
if(QFile::exists(mcJson))
{
auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false);
file->fileId = "net.minecraft";
file->uid = "net.minecraft";
file->name = QObject::tr("Minecraft (tracked)");
file->setVanilla(true);
if(file->version.isEmpty())
{
file->version = mcVersion;
@ -40,7 +38,8 @@ void FTBProfileStrategy::loadDefaultBuiltinPatches()
addLib->setHint("local");
addLib->setStoragePrefix(nativeInstance->librariesPath().absolutePath());
}
minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(file);
minecraftPatch = std::make_shared<ProfilePatch>(file);
minecraftPatch->setVanilla(true);
}
else
{
@ -65,8 +64,7 @@ void FTBProfileStrategy::loadDefaultBuiltinPatches()
addLib->setHint("local");
addLib->setStoragePrefix(nativeInstance->librariesPath().absolutePath());
}
file->fileId = "org.multimc.ftb.pack";
file->setVanilla(true);
file->uid = "org.multimc.ftb.pack";
file->name = QObject::tr("%1 (FTB pack)").arg(m_instance->name());
if(file->version.isEmpty())
{
@ -82,7 +80,8 @@ void FTBProfileStrategy::loadDefaultBuiltinPatches()
}
}
}
packPatch = std::dynamic_pointer_cast<ProfilePatch>(file);
packPatch = std::make_shared<ProfilePatch>(file);
packPatch->setVanilla(true);
}
else
{

View File

@ -1,32 +0,0 @@
#pragma once
#include <minecraft/MinecraftVersion.h>
class FTBVersion : public BaseVersion
{
public:
FTBVersion(MinecraftVersionPtr parent) : m_version(parent){};
public:
virtual QString descriptor() override
{
return m_version->descriptor();
}
virtual QString name() override
{
return m_version->name();
}
virtual QString typeString() const override
{
return m_version->typeString();
}
MinecraftVersionPtr getMinecraftVersion()
{
return m_version;
}
private:
MinecraftVersionPtr m_version;
};

View File

@ -27,7 +27,6 @@
#include "LegacyModList.h"
#include "LwjglVersionList.h"
#include "minecraft/MinecraftVersionList.h"
#include "LegacyInstance.h"
#include <FileSystem.h>

View File

@ -99,6 +99,7 @@ inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
void LWJGLVersionList::rssFailed(const QString& reason)
{
m_rssDLJob.reset();
m_loading = false;
qWarning() << "Failed to load LWJGL list. Network error: " + reason;
}
@ -116,8 +117,9 @@ void LWJGLVersionList::rssSucceeded()
if (!doc.setContent(m_rssData, false, &xmlErrorMsg, &errorLine))
{
qWarning() << "Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " + QString::number(errorLine);
m_loading = false;
m_rssDLJob.reset();
m_rssData.clear();
m_loading = false;
return;
}
m_rssData.clear();
@ -162,5 +164,6 @@ void LWJGLVersionList::rssSucceeded()
endResetModel();
qDebug() << "Loaded LWJGL list.";
m_rssDLJob.reset();
m_loading = false;
}

View File

@ -78,9 +78,9 @@ public:
return m_vlist[i];
}
virtual Task* getLoadTask() override
virtual shared_qobject_ptr<Task> getLoadTask() override
{
return nullptr;
return m_rssDLJob;
}
virtual void sortVersions() override {};

View File

@ -1,106 +0,0 @@
/* 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.
*/
#include "LiteLoaderInstaller.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QDebug>
#include "minecraft/MinecraftProfile.h"
#include "minecraft/Library.h"
#include "minecraft/onesix/OneSixInstance.h"
#include <minecraft/onesix/OneSixVersionFormat.h>
#include "minecraft/liteloader/LiteLoaderVersionList.h"
#include "Exception.h"
LiteLoaderInstaller::LiteLoaderInstaller() : BaseInstaller()
{
}
void LiteLoaderInstaller::prepare(LiteLoaderVersionPtr version)
{
m_version = version;
}
bool LiteLoaderInstaller::add(OneSixInstance *to)
{
if (!BaseInstaller::add(to))
{
return false;
}
QFile file(filename(to->instanceRoot()));
if (!file.open(QFile::WriteOnly))
{
qCritical() << "Error opening" << file.fileName()
<< "for reading:" << file.errorString();
return false;
}
file.write(OneSixVersionFormat::versionFileToJson(m_version->getVersionFile(), true).toJson());
file.close();
return true;
}
class LiteLoaderInstallTask : public Task
{
Q_OBJECT
public:
LiteLoaderInstallTask(LiteLoaderInstaller *installer, OneSixInstance *instance, BaseVersionPtr version, QObject *parent)
: Task(parent), m_installer(installer), m_instance(instance), m_version(version)
{
}
protected:
void executeTask() override
{
LiteLoaderVersionPtr liteloaderVersion = std::dynamic_pointer_cast<LiteLoaderVersion>(m_version);
if (!liteloaderVersion)
{
return;
}
m_installer->prepare(liteloaderVersion);
if (!m_installer->add(m_instance))
{
emitFailed(tr("For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details."));
return;
}
try
{
m_instance->reloadProfile();
emitSucceeded();
}
catch (Exception &e)
{
emitFailed(e.cause());
}
catch (...)
{
emitFailed(tr("Failed to load the version description file for reasons unknown."));
}
}
private:
LiteLoaderInstaller *m_installer;
OneSixInstance *m_instance;
BaseVersionPtr m_version;
};
Task *LiteLoaderInstaller::createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent)
{
return new LiteLoaderInstallTask(this, instance, version, parent);
}
#include "LiteLoaderInstaller.moc"

View File

@ -1,39 +0,0 @@
/* 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 <QString>
#include <QMap>
#include "BaseInstaller.h"
#include "LiteLoaderVersionList.h"
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT LiteLoaderInstaller : public BaseInstaller
{
public:
LiteLoaderInstaller();
void prepare(LiteLoaderVersionPtr version);
bool add(OneSixInstance *to) override;
virtual QString id() const override { return "com.mumfrey.liteloader"; }
Task *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
private:
LiteLoaderVersionPtr m_version;
};

View File

@ -1,332 +0,0 @@
/* 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.
*/
#include "LiteLoaderVersionList.h"
#include <minecraft/onesix/OneSixVersionFormat.h>
#include "Env.h"
#include "net/URLConstants.h"
#include "Exception.h"
#include <QtXml>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QJsonParseError>
#include <QtAlgorithms>
#include <QtNetwork>
LiteLoaderVersionList::LiteLoaderVersionList(QObject *parent) : BaseVersionList(parent)
{
}
Task *LiteLoaderVersionList::getLoadTask()
{
return new LLListLoadTask(this);
}
bool LiteLoaderVersionList::isLoaded()
{
return m_loaded;
}
const BaseVersionPtr LiteLoaderVersionList::at(int i) const
{
return m_vlist.at(i);
}
int LiteLoaderVersionList::count() const
{
return m_vlist.count();
}
static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
{
auto left = std::dynamic_pointer_cast<LiteLoaderVersion>(first);
auto right = std::dynamic_pointer_cast<LiteLoaderVersion>(second);
return left->timestamp > right->timestamp;
}
VersionFilePtr LiteLoaderVersion::getVersionFile()
{
auto f = std::make_shared<VersionFile>();
f->mainClass = "net.minecraft.launchwrapper.Launch";
f->addTweakers += tweakClass;
f->order = 10;
f->libraries = libraries;
f->name = "LiteLoader";
f->fileId = "com.mumfrey.liteloader";
f->version = version;
f->minecraftVersion = mcVersion;
return f;
}
void LiteLoaderVersionList::sortVersions()
{
beginResetModel();
std::sort(m_vlist.begin(), m_vlist.end(), cmpVersions);
endResetModel();
}
QVariant LiteLoaderVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() > count())
return QVariant();
auto version = std::dynamic_pointer_cast<LiteLoaderVersion>(m_vlist[index.row()]);
switch (role)
{
case VersionPointerRole:
return qVariantFromValue(m_vlist[index.row()]);
case VersionRole:
return version->name();
case VersionIdRole:
return version->descriptor();
case ParentGameVersionRole:
return version->mcVersion;
case LatestRole:
return version->isLatest;
case RecommendedRole:
return version->isRecommended;
case TypeRole:
return version->isSnapshot ? tr("Snapshot") : tr("Release");
default:
return QVariant();
}
}
BaseVersionList::RoleList LiteLoaderVersionList::providesRoles() const
{
return {VersionPointerRole, VersionRole, VersionIdRole, ParentGameVersionRole, RecommendedRole, LatestRole, TypeRole};
}
BaseVersionPtr LiteLoaderVersionList::getLatestStable() const
{
for (int i = 0; i < m_vlist.length(); i++)
{
auto ver = std::dynamic_pointer_cast<LiteLoaderVersion>(m_vlist.at(i));
if (ver->isRecommended)
{
return m_vlist.at(i);
}
}
return BaseVersionPtr();
}
void LiteLoaderVersionList::updateListData(QList<BaseVersionPtr> versions)
{
beginResetModel();
m_vlist = versions;
m_loaded = true;
std::sort(m_vlist.begin(), m_vlist.end(), cmpVersions);
endResetModel();
}
LLListLoadTask::LLListLoadTask(LiteLoaderVersionList *vlist)
{
m_list = vlist;
}
LLListLoadTask::~LLListLoadTask()
{
}
void LLListLoadTask::executeTask()
{
setStatus(tr("Loading LiteLoader version list..."));
auto job = new NetJob("Version index");
// we do not care if the version is stale or not.
auto liteloaderEntry = ENV.metacache()->resolveEntry("liteloader", "versions.json");
// verify by poking the server.
liteloaderEntry->setStale(true);
job->addNetAction(listDownload = Net::Download::makeCached(QUrl(URLConstants::LITELOADER_URL), liteloaderEntry));
connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed()));
listJob.reset(job);
connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded()));
connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
listJob->start();
}
void LLListLoadTask::listFailed()
{
emitFailed("Failed to load LiteLoader version list.");
return;
}
void LLListLoadTask::listDownloaded()
{
QByteArray data;
{
auto filename = listDownload->getTargetFilepath();
QFile listFile(filename);
if (!listFile.open(QIODevice::ReadOnly))
{
emitFailed("Failed to open the LiteLoader version list.");
return;
}
data = listFile.readAll();
listFile.close();
listDownload.reset();
}
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
emitFailed("Error parsing version list JSON:" + jsonError.errorString());
return;
}
if (!jsonDoc.isObject())
{
emitFailed("Error parsing version list JSON: jsonDoc is not an object");
return;
}
const QJsonObject root = jsonDoc.object();
// Now, get the array of versions.
if (!root.value("versions").isObject())
{
emitFailed("Error parsing version list JSON: missing 'versions' object");
return;
}
auto meta = root.value("meta").toObject();
QString description = meta.value("description").toString(tr("This is a lightweight loader for mods that don't change game mechanics."));
QString defaultUrl = meta.value("url").toString("http://dl.liteloader.com");
QString authors = meta.value("authors").toString("Mumfrey");
auto versions = root.value("versions").toObject();
QList<BaseVersionPtr> tempList;
for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt)
{
const QString mcVersion = vIt.key();
const QJsonObject versionObject = vIt.value().toObject();
auto processArtefacts = [&](QJsonObject artefacts, bool notSnapshots, std::shared_ptr<LiteLoaderVersion> &latest)
{
QString latestVersion;
QList<BaseVersionPtr> perMcVersionList;
for (auto aIt = artefacts.begin(); aIt != artefacts.end(); ++aIt)
{
const QString identifier = aIt.key();
const QJsonObject artefact = aIt.value().toObject();
if (identifier == "latest")
{
latestVersion = artefact.value("version").toString();
continue;
}
LiteLoaderVersionPtr version(new LiteLoaderVersion());
version->version = artefact.value("version").toString();
version->mcVersion = mcVersion;
version->md5 = artefact.value("md5").toString();
version->timestamp = artefact.value("timestamp").toString().toLong();
version->tweakClass = artefact.value("tweakClass").toString();
version->authors = authors;
version->description = description;
version->defaultUrl = defaultUrl;
version->isSnapshot = !notSnapshots;
const QJsonArray libs = artefact.value("libraries").toArray();
for (auto lIt = libs.begin(); lIt != libs.end(); ++lIt)
{
auto libobject = (*lIt).toObject();
try
{
auto lib = OneSixVersionFormat::libraryFromJson(libobject, "versions.json");
// hack to make liteloader 1.7.10_00 work
if(lib->rawName() == GradleSpecifier("org.ow2.asm:asm-all:5.0.3"))
{
lib->setRepositoryURL("http://repo.maven.apache.org/maven2/");
}
version->libraries.append(lib);
}
catch (Exception &e)
{
qCritical() << "Couldn't read JSON object:";
continue; // FIXME: ignores bad libraries and continues loading
}
}
auto liteloaderLib = std::make_shared<Library>("com.mumfrey:liteloader:" + version->version);
liteloaderLib->setRepositoryURL("http://dl.liteloader.com/versions/");
if(!notSnapshots)
{
liteloaderLib->setHint("always-stale");
}
version->libraries.append(liteloaderLib);
perMcVersionList.append(version);
}
if(notSnapshots)
{
for (auto version : perMcVersionList)
{
auto v = std::dynamic_pointer_cast<LiteLoaderVersion>(version);
if(v->version == latestVersion)
{
latest = v;
}
}
}
tempList.append(perMcVersionList);
};
std::shared_ptr<LiteLoaderVersion> latestSnapshot;
std::shared_ptr<LiteLoaderVersion> latestRelease;
// are there actually released versions for this mc version?
if(versionObject.contains("artefacts"))
{
const QJsonObject artefacts = versionObject.value("artefacts").toObject().value("com.mumfrey:liteloader").toObject();
processArtefacts(artefacts, true, latestRelease);
}
if(versionObject.contains("snapshots"))
{
QJsonObject artefacts = versionObject.value("snapshots").toObject().value("com.mumfrey:liteloader").toObject();
processArtefacts(artefacts, false, latestSnapshot);
}
if(latestSnapshot)
{
latestSnapshot->isLatest = true;
}
else if(latestRelease)
{
latestRelease->isLatest = true;
}
if(latestRelease)
{
latestRelease->isRecommended = true;
}
}
m_list->updateListData(tempList);
emitSucceeded();
}

View File

@ -1,123 +0,0 @@
/* 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 <QString>
#include <QStringList>
#include "BaseVersion.h"
#include "BaseVersionList.h"
#include "tasks/Task.h"
#include "net/NetJob.h"
#include <minecraft/Library.h>
#include <minecraft/VersionFile.h>
#include "multimc_logic_export.h"
class LLListLoadTask;
class QNetworkReply;
class LiteLoaderVersion : public BaseVersion
{
public:
QString descriptor() override
{
if (isLatest)
{
return QObject::tr("Latest");
}
return QString();
}
QString typeString() const override
{
return mcVersion;
}
QString name() override
{
return version;
}
VersionFilePtr getVersionFile();
// important info
QString version;
QString mcVersion;
QString md5;
long timestamp = 0;
bool isLatest = false;
bool isRecommended = false;
bool isSnapshot = false;
QString tweakClass;
QList<LibraryPtr> libraries;
// meta
QString defaultUrl;
QString description;
QString authors;
};
typedef std::shared_ptr<LiteLoaderVersion> LiteLoaderVersionPtr;
class MULTIMC_LOGIC_EXPORT LiteLoaderVersionList : public BaseVersionList
{
Q_OBJECT
public:
friend class LLListLoadTask;
explicit LiteLoaderVersionList(QObject *parent = 0);
Task *getLoadTask() override;
bool isLoaded() override;
const BaseVersionPtr at(int i) const override;
int count() const override;
void sortVersions() override;
QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const override;
RoleList providesRoles() const override;
virtual BaseVersionPtr getLatestStable() const override;
protected:
QList<BaseVersionPtr> m_vlist;
bool m_loaded = false;
protected
slots:
void updateListData(QList<BaseVersionPtr> versions) override;
};
class LLListLoadTask : public Task
{
Q_OBJECT
public:
explicit LLListLoadTask(LiteLoaderVersionList *vlist);
~LLListLoadTask();
virtual void executeTask();
protected
slots:
void listDownloaded();
void listFailed();
protected:
NetJobPtr listJob;
Net::Download::Ptr listDownload;
LiteLoaderVersionList *m_list;
};
Q_DECLARE_METATYPE(LiteLoaderVersionPtr)

View File

@ -34,7 +34,15 @@
OneSixInstance::OneSixInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
: MinecraftInstance(globalSettings, settings, rootDir)
{
// set explicitly during instance creation
m_settings->registerSetting({"IntendedVersion", "MinecraftVersion"}, "");
// defaults to the version we've been using for years (2.9.1)
m_settings->registerSetting("LWJGLVersion", "2.9.1");
// optionals
m_settings->registerSetting("ForgeVersion", "");
m_settings->registerSetting("LiteloaderVersion", "");
}
void OneSixInstance::init()
@ -275,6 +283,8 @@ QStringList OneSixInstance::verboseDescription(AuthSessionPtr session)
printLibFile(file);
}
printLibFile(mainJarPath());
out << "";
out << "Native libraries:";
for(auto file: nativeJars)
{
printLibFile(file);
@ -480,7 +490,32 @@ std::shared_ptr<WorldList> OneSixInstance::worldList() const
bool OneSixInstance::setIntendedVersionId(QString version)
{
settings()->set("IntendedVersion", version);
return setComponentVersion("net.minecraft", version);
}
QString OneSixInstance::intendedVersionId() const
{
return getComponentVersion("net.minecraft");
}
bool OneSixInstance::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(getMinecraftProfile())
{
clearProfile();
@ -489,6 +524,27 @@ bool OneSixInstance::setIntendedVersionId(QString version)
return true;
}
QString OneSixInstance::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();
}
QList< Mod > OneSixInstance::getJarMods() const
{
QList<Mod> mods;
@ -500,12 +556,6 @@ QList< Mod > OneSixInstance::getJarMods() const
return mods;
}
QString OneSixInstance::intendedVersionId() const
{
return settings()->get("IntendedVersion").toString();
}
void OneSixInstance::setShouldUpdate(bool)
{
}
@ -523,7 +573,7 @@ QString OneSixInstance::currentVersionId() const
void OneSixInstance::reloadProfile()
{
m_profile->reload();
setVersionBroken(m_profile->getProblemSeverity() == ProblemSeverity::PROBLEM_ERROR);
setVersionBroken(m_profile->getProblemSeverity() == ProblemSeverity::Error);
emit versionReloaded();
}

View File

@ -59,9 +59,11 @@ public:
virtual QString intendedVersionId() const override;
virtual bool setIntendedVersionId(QString version) override;
virtual QString currentVersionId() const override;
QString getComponentVersion(const QString &uid) const;
bool setComponentVersion(const QString &uid, const QString &version);
virtual bool shouldUpdate() const override;
virtual void setShouldUpdate(bool val) override;

View File

@ -3,7 +3,6 @@
#include "OneSixVersionFormat.h"
#include "minecraft/VersionBuildError.h"
#include "minecraft/MinecraftVersionList.h"
#include "Env.h"
#include <FileSystem.h>
@ -11,6 +10,12 @@
#include <QUuid>
#include <QJsonDocument>
#include <QJsonArray>
#include <QSaveFile>
#include <QResource>
#include <meta/Index.h>
#include <meta/Version.h>
#include <tuple>
OneSixProfileStrategy::OneSixProfileStrategy(OneSixInstance* instance)
{
@ -53,7 +58,7 @@ void OneSixProfileStrategy::upgradeDeprecatedFiles()
}
auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false);
ProfileUtils::removeLwjglFromPatch(file);
file->fileId = "net.minecraft";
file->uid = "net.minecraft";
file->version = file->minecraftVersion;
file->name = "Minecraft";
auto data = OneSixVersionFormat::versionFileToJson(file, false).toJson();
@ -80,156 +85,128 @@ void OneSixProfileStrategy::upgradeDeprecatedFiles()
void OneSixProfileStrategy::loadDefaultBuiltinPatches()
{
auto addBuiltinPatch = [&](const QString &uid, const QString intendedVersion, int order)
{
auto mcJson = FS::PathCombine(m_instance->instanceRoot(), "patches" , "net.minecraft.json");
auto jsonFilePath = FS::PathCombine(m_instance->instanceRoot(), "patches" , uid + ".json");
// load up the base minecraft patch
ProfilePatchPtr minecraftPatch;
if(QFile::exists(mcJson))
ProfilePatchPtr profilePatch;
if(QFile::exists(jsonFilePath))
{
auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false);
auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false);
if(file->version.isEmpty())
{
file->version = m_instance->intendedVersionId();
file->version = intendedVersion;
}
file->setVanilla(false);
file->setRevertible(true);
minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(file);
profilePatch = std::make_shared<ProfilePatch>(file, jsonFilePath);
profilePatch->setVanilla(false);
profilePatch->setRevertible(true);
}
else
{
auto mcversion = ENV.getVersion("net.minecraft", m_instance->intendedVersionId());
minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(mcversion);
auto metaVersion = ENV.metadataIndex()->get(uid, intendedVersion);
profilePatch = std::make_shared<ProfilePatch>(metaVersion);
profilePatch->setVanilla(true);
}
if (!minecraftPatch)
if (!profilePatch)
{
throw VersionIncomplete("net.minecraft");
throw VersionIncomplete(uid);
}
minecraftPatch->setOrder(-2);
profile->appendPatch(minecraftPatch);
}
{
auto lwjglJson = FS::PathCombine(m_instance->instanceRoot(), "patches" , "org.lwjgl.json");
ProfilePatchPtr lwjglPatch;
if(QFile::exists(lwjglJson))
{
auto file = ProfileUtils::parseJsonFile(QFileInfo(lwjglJson), false);
file->setVanilla(false);
file->setRevertible(true);
lwjglPatch = std::dynamic_pointer_cast<ProfilePatch>(file);
}
else
{
// NOTE: this is obviously fake, is fixed in unstable.
QResource LWJGL(":/versions/LWJGL/2.9.1.json");
auto lwjgl = ProfileUtils::parseJsonFile(LWJGL.absoluteFilePath(), false);
lwjgl->setVanilla(true);
lwjgl->setCustomizable(true);
lwjglPatch = std::dynamic_pointer_cast<ProfilePatch>(lwjgl);
}
if (!lwjglPatch)
{
throw VersionIncomplete("org.lwjgl");
}
lwjglPatch->setOrder(-1);
profile->appendPatch(lwjglPatch);
}
profilePatch->setOrder(order);
profile->appendPatch(profilePatch);
};
addBuiltinPatch("net.minecraft", m_instance->getComponentVersion("net.minecraft"), -2);
addBuiltinPatch("org.lwjgl", m_instance->getComponentVersion("org.lwjgl"), -1);
}
void OneSixProfileStrategy::loadUserPatches()
{
// load all patches, put into map for ordering, apply in the right order
ProfileUtils::PatchOrder userOrder;
ProfileUtils::readOverrideOrders(FS::PathCombine(m_instance->instanceRoot(), "order.json"), userOrder);
QDir patches(FS::PathCombine(m_instance->instanceRoot(),"patches"));
QSet<QString> seen_extra;
// first, load things by sort order.
for (auto id : userOrder)
{
// ignore builtins
if (id == "net.minecraft")
continue;
if (id == "org.lwjgl")
continue;
// parse the file
QString filename = patches.absoluteFilePath(id + ".json");
QFileInfo finfo(filename);
if(!finfo.exists())
{
qDebug() << "Patch file " << filename << " was deleted by external means...";
continue;
}
qDebug() << "Reading" << filename << "by user order";
VersionFilePtr file = ProfileUtils::parseJsonFile(finfo, false);
// sanity check. prevent tampering with files.
if (file->fileId != id)
{
file->addProblem(PROBLEM_WARNING, QObject::tr("load id %1 does not match internal id %2").arg(id, file->fileId));
seen_extra.insert(file->fileId);
}
file->setRemovable(true);
file->setMovable(true);
// HACK: ignore assets from other version files than Minecraft
// workaround for stupid assets issue caused by amazon:
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
file->assets = QString();
file->mojangAssetIndex.reset();
// HACK
profile->appendPatch(file);
}
// now load the rest by internal preference.
QMultiMap<int, VersionFilePtr> files;
for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
// first, collect all patches (that are not builtins of OneSix) and load them
QMap<QString, ProfilePatchPtr> loadedPatches;
QDir patchesDir(FS::PathCombine(m_instance->instanceRoot(),"patches"));
for (auto info : patchesDir.entryInfoList(QStringList() << "*.json", QDir::Files))
{
// parse the file
qDebug() << "Reading" << info.fileName();
auto file = ProfileUtils::parseJsonFile(info, true);
// ignore builtins
if (file->fileId == "net.minecraft")
if (file->uid == "net.minecraft")
continue;
if (file->fileId == "org.lwjgl")
if (file->uid == "org.lwjgl")
continue;
// do not load versions with broken IDs twice
if(seen_extra.contains(file->fileId))
continue;
// do not load what we already loaded in the first pass
if (userOrder.contains(file->fileId))
continue;
file->setRemovable(true);
file->setMovable(true);
// HACK: ignore assets from other version files than Minecraft
// workaround for stupid assets issue caused by amazon:
// https://www.theregister.co.uk/2017/02/28/aws_is_awol_as_s3_goes_haywire/
file->assets = QString();
file->mojangAssetIndex.reset();
// HACK
files.insert(file->order, file);
auto patch = std::make_shared<ProfilePatch>(file, info.filePath());
patch->setRemovable(true);
patch->setMovable(true);
if(ENV.metadataIndex()->hasUid(file->uid))
{
// FIXME: requesting a uid/list creates it in the index... this allows reverting to possibly invalid versions...
patch->setRevertible(true);
}
loadedPatches[file->uid] = patch;
}
QSet<int> seen;
// these are 'special'... if not already loaded from instance files, grab them from the metadata repo.
auto loadSpecial = [&](const QString & uid, int order)
{
auto patchVersion = m_instance->getComponentVersion(uid);
if(!patchVersion.isEmpty() && !loadedPatches.contains(uid))
{
auto patch = std::make_shared<ProfilePatch>(ENV.metadataIndex()->get(uid, patchVersion));
patch->setOrder(order);
patch->setVanilla(true);
patch->setRemovable(true);
patch->setMovable(true);
loadedPatches[uid] = patch;
}
};
loadSpecial("net.minecraftforge", 5);
loadSpecial("com.mumfrey.liteloader", 10);
// now add all the patches by user sort order
ProfileUtils::PatchOrder userOrder;
ProfileUtils::readOverrideOrders(FS::PathCombine(m_instance->instanceRoot(), "order.json"), userOrder);
bool orderIsDirty = false;
for (auto uid : userOrder)
{
// ignore builtins
if (uid == "net.minecraft")
continue;
if (uid == "org.lwjgl")
continue;
// ordering has a patch that is gone?
if(!loadedPatches.contains(uid))
{
orderIsDirty = true;
continue;
}
profile->appendPatch(loadedPatches.take(uid));
}
// is there anything left to sort?
if(loadedPatches.isEmpty())
{
// TODO: save the order here?
return;
}
// inserting into multimap by order number as key sorts the patches and detects duplicates
QMultiMap<int, ProfilePatchPtr> files;
auto iter = loadedPatches.begin();
while(iter != loadedPatches.end())
{
files.insert((*iter)->getOrder(), *iter);
iter++;
}
// then just extract the patches and put them in the list
for (auto order : files.keys())
{
if(seen.contains(order))
continue;
seen.insert(order);
const auto &values = files.values(order);
if(values.size() == 1)
for(auto &value: values)
{
profile->appendPatch(values[0]);
continue;
}
for(auto &file: values)
{
QStringList list;
for(auto &file2: values)
{
if(file != file2)
list.append(file2->name);
}
file->addProblem(PROBLEM_WARNING, QObject::tr("%1 has the same order as the following components:\n%2").arg(file->name, list.join(", ")));
profile->appendPatch(file);
// TODO: put back the insertion of problem messages here, so the user knows about the id duplication
profile->appendPatch(value);
}
}
// TODO: save the order here?
}
@ -266,7 +243,10 @@ bool OneSixProfileStrategy::removePatch(ProfilePatchPtr patch)
return false;
}
}
if(!m_instance->getComponentVersion(patch->getID()).isEmpty())
{
m_instance->setComponentVersion(patch->getID(), QString());
}
auto preRemoveJarMod = [&](JarmodPtr jarMod) -> bool
{
@ -285,7 +265,8 @@ bool OneSixProfileStrategy::removePatch(ProfilePatchPtr patch)
return true;
};
for(auto &jarmod: patch->getJarMods())
auto &jarMods = patch->getVersionFile()->jarMods;
for(auto &jarmod: jarMods)
{
ok &= preRemoveJarMod(jarmod);
}
@ -405,12 +386,9 @@ bool OneSixProfileStrategy::installJarMods(QStringList filepaths)
jarMod->originalName = sourceInfo.completeBaseName();
f->jarMods.append(jarMod);
f->name = target_name;
f->fileId = target_id;
f->uid = target_id;
f->order = profile->getFreeOrderNumber();
QString patchFileName = FS::PathCombine(patchDir, target_id + ".json");
f->filename = patchFileName;
f->setMovable(true);
f->setRemovable(true);
QFile file(patchFileName);
if (!file.open(QFile::WriteOnly))
@ -421,7 +399,11 @@ bool OneSixProfileStrategy::installJarMods(QStringList filepaths)
}
file.write(OneSixVersionFormat::versionFileToJson(f, true).toJson());
file.close();
profile->appendPatch(f);
auto patch = std::make_shared<ProfilePatch>(f, patchFileName);
patch->setMovable(true);
patch->setRemovable(true);
profile->appendPatch(patch);
}
profile->saveCurrentOrder();
profile->reapplyPatches();

View File

@ -24,7 +24,6 @@
#include <QDataStream>
#include "BaseInstance.h"
#include "minecraft/MinecraftVersionList.h"
#include "minecraft/MinecraftProfile.h"
#include "minecraft/Library.h"
#include "net/URLConstants.h"
@ -35,6 +34,9 @@
#include "update/FMLLibrariesTask.h"
#include "update/AssetUpdateTask.h"
#include <meta/Index.h>
#include <meta/Version.h>
OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
// create folders
@ -44,29 +46,23 @@ OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent)
// add a version update task, if necessary
{
auto list = std::dynamic_pointer_cast<MinecraftVersionList>(ENV.getVersionList("net.minecraft"));
auto version = std::dynamic_pointer_cast<MinecraftVersion>(list->findVersion(m_inst->intendedVersionId()));
if (version == nullptr)
/*
* FIXME: there are some corner cases here that remain unhandled:
* what if local load succeeds but remote fails? The version is still usable...
*/
// FIXME: derive this from the actual list of version patches...
auto loadVersion = [&](const QString & uid, const QString & version)
{
// don't do anything if it was invalid
m_preFailure = tr("The specified Minecraft version is invalid. Choose a different one.");
}
else if (m_inst->providesVersionFile() || !version->needsUpdate())
{
qDebug() << "Instance either provides a version file or doesn't need an update.";
}
else
{
auto versionUpdateTask = list->createUpdateTask(m_inst->intendedVersionId());
if (!versionUpdateTask)
auto obj = ENV.metadataIndex()->get(uid, version);
obj->load();
auto task = obj->getCurrentTask();
if(task)
{
qDebug() << "Didn't spawn an update task.";
m_tasks.append(task.unwrap());
}
else
{
m_tasks.append(versionUpdateTask);
}
}
};
loadVersion("org.lwjgl", m_inst->getComponentVersion("org.lwjgl"));
loadVersion("net.minecraft", m_inst->getComponentVersion("net.minecraft"));
}
// libraries download
@ -117,11 +113,20 @@ void OneSixUpdate::next()
return;
}
auto task = m_tasks[m_currentTask];
// if the task is already finished by the time we look at it, skip it
if(task->isFinished())
{
next();
}
connect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded);
connect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed);
connect(task.get(), &Task::progress, this, &OneSixUpdate::progress);
connect(task.get(), &Task::status, this, &OneSixUpdate::setStatus);
task->start();
// if the task is already running, do not start it again
if(!task->isRunning())
{
task->start();
}
}
void OneSixUpdate::subtaskSucceeded()

View File

@ -1,7 +1,6 @@
#include "OneSixVersionFormat.h"
#include <Json.h>
#include "minecraft/ParseUtils.h"
#include <minecraft/MinecraftVersion.h>
#include <minecraft/VersionBuildError.h>
#include <minecraft/MojangVersionFormat.h>
@ -62,10 +61,19 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
}
out->name = root.value("name").toString();
out->fileId = root.value("fileId").toString();
if(root.contains("uid"))
{
out->uid = root.value("uid").toString();
}
else
{
out->uid = root.value("fileId").toString();
}
out->version = root.value("version").toString();
out->dependsOnMinecraftVersion = root.value("mcVersion").toString();
out->filename = filename;
// out->filename = filename;
MojangVersionFormat::readVersionProperties(root, out.get());
@ -120,7 +128,8 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
bool hasLibs = root.contains("libraries");
if (hasPlusLibs && hasLibs)
{
out->addProblem(PROBLEM_WARNING, QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported."));
out->addProblem(ProblemSeverity::Warning,
QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported."));
readLibs("libraries");
readLibs("+libraries");
}
@ -136,23 +145,23 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
/* removed features that shouldn't be used */
if (root.contains("tweakers"))
{
out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element 'tweakers'"));
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));
}
if (root.contains("-libraries"))
{
out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element '-libraries'"));
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'"));
}
if (root.contains("-tweakers"))
{
out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element '-tweakers'"));
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'"));
}
if (root.contains("-minecraftArguments"))
{
out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element '-minecraftArguments'"));
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'"));
}
if (root.contains("+minecraftArguments"))
{
out->addProblem(PROBLEM_ERROR, QObject::tr("Version file contains unsupported element '+minecraftArguments'"));
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'"));
}
return out;
}
@ -165,7 +174,10 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch
root.insert("order", patch->order);
}
writeString(root, "name", patch->name);
writeString(root, "fileId", patch->fileId);
writeString(root, "uid", patch->uid);
writeString(root, "fileId", patch->uid);
writeString(root, "version", patch->version);
writeString(root, "mcVersion", patch->dependsOnMinecraftVersion);

View File

@ -35,19 +35,23 @@ void LibrariesTask::executeTask()
downloadJob.reset(job);
}
auto libs = profile->getLibraries();
auto metacache = ENV.metacache();
QList<LibraryPtr> brokenLocalLibs;
QStringList failedFiles;
for (auto lib : libs)
auto createJobs = [&](const QList<LibraryPtr> & libs)
{
auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles, inst->getLocalLibraryPath());
for(auto dl : dls)
for (auto lib : libs)
{
downloadJob->addNetAction(dl);
auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles, inst->getLocalLibraryPath());
for(auto dl : dls)
{
downloadJob->addNetAction(dl);
}
}
}
};
createJobs(profile->getLibraries());
createJobs(profile->getNativeLibraries());
// FIXME: this is never filled!!!!
if (!brokenLocalLibs.empty())
{

View File

@ -1,39 +0,0 @@
/* Copyright 2015-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.
*/
#include "BaseWonkoEntity.h"
#include "Json.h"
#include "WonkoUtil.h"
BaseWonkoEntity::~BaseWonkoEntity()
{
}
void BaseWonkoEntity::store() const
{
Json::write(serialized(), Wonko::localWonkoDir().absoluteFilePath(localFilename()));
}
void BaseWonkoEntity::notifyLocalLoadComplete()
{
m_localLoaded = true;
store();
}
void BaseWonkoEntity::notifyRemoteLoadComplete()
{
m_remoteLoaded = true;
store();
}

View File

@ -1,51 +0,0 @@
/* Copyright 2015-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 <memory>
#include "multimc_logic_export.h"
class Task;
class MULTIMC_LOGIC_EXPORT BaseWonkoEntity
{
public:
virtual ~BaseWonkoEntity();
using Ptr = std::shared_ptr<BaseWonkoEntity>;
virtual std::unique_ptr<Task> remoteUpdateTask() = 0;
virtual std::unique_ptr<Task> localUpdateTask() = 0;
virtual void merge(const std::shared_ptr<BaseWonkoEntity> &other) = 0;
void store() const;
virtual QString localFilename() const = 0;
virtual QJsonObject serialized() const = 0;
bool isComplete() const { return m_localLoaded || m_remoteLoaded; }
bool isLocalLoaded() const { return m_localLoaded; }
bool isRemoteLoaded() const { return m_remoteLoaded; }
void notifyLocalLoadComplete();
void notifyRemoteLoadComplete();
private:
bool m_localLoaded = false;
bool m_remoteLoaded = false;
};

View File

@ -1,50 +0,0 @@
#include <QTest>
#include "TestUtil.h"
#include "wonko/WonkoIndex.h"
#include "wonko/WonkoVersionList.h"
#include "Env.h"
class WonkoIndexTest : public QObject
{
Q_OBJECT
private
slots:
void test_isProvidedByEnv()
{
QVERIFY(ENV.wonkoIndex());
QCOMPARE(ENV.wonkoIndex(), ENV.wonkoIndex());
}
void test_providesTasks()
{
QVERIFY(ENV.wonkoIndex()->localUpdateTask() != nullptr);
QVERIFY(ENV.wonkoIndex()->remoteUpdateTask() != nullptr);
}
void test_hasUid_and_getList()
{
WonkoIndex windex({std::make_shared<WonkoVersionList>("list1"), std::make_shared<WonkoVersionList>("list2"), std::make_shared<WonkoVersionList>("list3")});
QVERIFY(windex.hasUid("list1"));
QVERIFY(!windex.hasUid("asdf"));
QVERIFY(windex.getList("list2") != nullptr);
QCOMPARE(windex.getList("list2")->uid(), QString("list2"));
QVERIFY(windex.getList("adsf") == nullptr);
}
void test_merge()
{
WonkoIndex windex({std::make_shared<WonkoVersionList>("list1"), std::make_shared<WonkoVersionList>("list2"), std::make_shared<WonkoVersionList>("list3")});
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<WonkoIndex>(new WonkoIndex({std::make_shared<WonkoVersionList>("list1"), std::make_shared<WonkoVersionList>("list2"), std::make_shared<WonkoVersionList>("list3")})));
QCOMPARE(windex.lists().size(), 3);
windex.merge(std::shared_ptr<WonkoIndex>(new WonkoIndex({std::make_shared<WonkoVersionList>("list4"), std::make_shared<WonkoVersionList>("list2"), std::make_shared<WonkoVersionList>("list5")})));
QCOMPARE(windex.lists().size(), 5);
windex.merge(std::shared_ptr<WonkoIndex>(new WonkoIndex({std::make_shared<WonkoVersionList>("list6")})));
QCOMPARE(windex.lists().size(), 6);
}
};
QTEST_GUILESS_MAIN(WonkoIndexTest)
#include "WonkoIndex_test.moc"

View File

@ -1,44 +0,0 @@
/* Copyright 2015-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.
*/
#include "WonkoReference.h"
WonkoReference::WonkoReference(const QString &uid)
: m_uid(uid)
{
}
QString WonkoReference::uid() const
{
return m_uid;
}
QString WonkoReference::version() const
{
return m_version;
}
void WonkoReference::setVersion(const QString &version)
{
m_version = version;
}
bool WonkoReference::operator==(const WonkoReference &other) const
{
return m_uid == other.m_uid && m_version == other.m_version;
}
bool WonkoReference::operator!=(const WonkoReference &other) const
{
return m_uid != other.m_uid || m_version != other.m_version;
}

View File

@ -1,41 +0,0 @@
/* Copyright 2015-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 <QString>
#include <QMetaType>
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT WonkoReference
{
public:
WonkoReference() {}
explicit WonkoReference(const QString &uid);
QString uid() const;
QString version() const;
void setVersion(const QString &version);
bool operator==(const WonkoReference &other) const;
bool operator!=(const WonkoReference &other) const;
private:
QString m_uid;
QString m_version;
};
Q_DECLARE_METATYPE(WonkoReference)

View File

@ -1,47 +0,0 @@
/* Copyright 2015-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.
*/
#include "WonkoUtil.h"
#include <QUrl>
#include <QDir>
#include "Env.h"
namespace Wonko
{
QUrl rootUrl()
{
return ENV.wonkoRootUrl();
}
QUrl indexUrl()
{
return rootUrl().resolved(QStringLiteral("index.json"));
}
QUrl versionListUrl(const QString &uid)
{
return rootUrl().resolved(uid + ".json");
}
QUrl versionUrl(const QString &uid, const QString &version)
{
return rootUrl().resolved(uid + "/" + version + ".json");
}
QDir localWonkoDir()
{
return QDir("wonko");
}
}

View File

@ -1,102 +0,0 @@
/* Copyright 2015-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.
*/
#include "WonkoVersion.h"
#include <QDateTime>
#include "tasks/BaseWonkoEntityLocalLoadTask.h"
#include "tasks/BaseWonkoEntityRemoteLoadTask.h"
#include "format/WonkoFormat.h"
WonkoVersion::WonkoVersion(const QString &uid, const QString &version)
: BaseVersion(), m_uid(uid), m_version(version)
{
}
QString WonkoVersion::descriptor()
{
return m_version;
}
QString WonkoVersion::name()
{
return m_version;
}
QString WonkoVersion::typeString() const
{
return m_type;
}
QDateTime WonkoVersion::time() const
{
return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
}
std::unique_ptr<Task> WonkoVersion::remoteUpdateTask()
{
return std::unique_ptr<WonkoVersionRemoteLoadTask>(new WonkoVersionRemoteLoadTask(this, this));
}
std::unique_ptr<Task> WonkoVersion::localUpdateTask()
{
return std::unique_ptr<WonkoVersionLocalLoadTask>(new WonkoVersionLocalLoadTask(this, this));
}
void WonkoVersion::merge(const std::shared_ptr<BaseWonkoEntity> &other)
{
WonkoVersionPtr version = std::dynamic_pointer_cast<WonkoVersion>(other);
if (m_type != version->m_type)
{
setType(version->m_type);
}
if (m_time != version->m_time)
{
setTime(version->m_time);
}
if (m_requires != version->m_requires)
{
setRequires(version->m_requires);
}
setData(version->m_data);
}
QString WonkoVersion::localFilename() const
{
return m_uid + '/' + m_version + ".json";
}
QJsonObject WonkoVersion::serialized() const
{
return WonkoFormat::serializeVersion(this);
}
void WonkoVersion::setType(const QString &type)
{
m_type = type;
emit typeChanged();
}
void WonkoVersion::setTime(const qint64 time)
{
m_time = time;
emit timeChanged();
}
void WonkoVersion::setRequires(const QVector<WonkoReference> &requires)
{
m_requires = requires;
emit requiresChanged();
}
void WonkoVersion::setData(const VersionFilePtr &data)
{
m_data = data;
}

View File

@ -1,283 +0,0 @@
/* Copyright 2015-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.
*/
#include "WonkoVersionList.h"
#include <QDateTime>
#include "WonkoVersion.h"
#include "tasks/BaseWonkoEntityRemoteLoadTask.h"
#include "tasks/BaseWonkoEntityLocalLoadTask.h"
#include "format/WonkoFormat.h"
#include "WonkoReference.h"
class WVLLoadTask : public Task
{
Q_OBJECT
public:
explicit WVLLoadTask(WonkoVersionList *list, QObject *parent = nullptr)
: Task(parent), m_list(list)
{
}
bool canAbort() const override
{
return !m_currentTask || m_currentTask->canAbort();
}
bool abort() override
{
return m_currentTask->abort();
}
private:
void executeTask() override
{
if (!m_list->isLocalLoaded())
{
m_currentTask = m_list->localUpdateTask();
connect(m_currentTask.get(), &Task::succeeded, this, &WVLLoadTask::next);
}
else
{
m_currentTask = m_list->remoteUpdateTask();
connect(m_currentTask.get(), &Task::succeeded, this, &WVLLoadTask::emitSucceeded);
}
connect(m_currentTask.get(), &Task::status, this, &WVLLoadTask::setStatus);
connect(m_currentTask.get(), &Task::progress, this, &WVLLoadTask::setProgress);
connect(m_currentTask.get(), &Task::failed, this, &WVLLoadTask::emitFailed);
m_currentTask->start();
}
void next()
{
m_currentTask = m_list->remoteUpdateTask();
connect(m_currentTask.get(), &Task::status, this, &WVLLoadTask::setStatus);
connect(m_currentTask.get(), &Task::progress, this, &WVLLoadTask::setProgress);
connect(m_currentTask.get(), &Task::succeeded, this, &WVLLoadTask::emitSucceeded);
m_currentTask->start();
}
WonkoVersionList *m_list;
std::unique_ptr<Task> m_currentTask;
};
WonkoVersionList::WonkoVersionList(const QString &uid, QObject *parent)
: BaseVersionList(parent), m_uid(uid)
{
setObjectName("Wonko version list: " + uid);
}
Task *WonkoVersionList::getLoadTask()
{
return new WVLLoadTask(this);
}
bool WonkoVersionList::isLoaded()
{
return isLocalLoaded() && isRemoteLoaded();
}
const BaseVersionPtr WonkoVersionList::at(int i) const
{
return m_versions.at(i);
}
int WonkoVersionList::count() const
{
return m_versions.size();
}
void WonkoVersionList::sortVersions()
{
beginResetModel();
std::sort(m_versions.begin(), m_versions.end(), [](const WonkoVersionPtr &a, const WonkoVersionPtr &b)
{
return *a.get() < *b.get();
});
endResetModel();
}
QVariant WonkoVersionList::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_versions.size() || index.parent().isValid())
{
return QVariant();
}
WonkoVersionPtr version = m_versions.at(index.row());
switch (role)
{
case VersionPointerRole: return QVariant::fromValue(std::dynamic_pointer_cast<BaseVersion>(version));
case VersionRole:
case VersionIdRole:
return version->version();
case ParentGameVersionRole:
{
const auto end = version->requires().end();
const auto it = std::find_if(version->requires().begin(), end,
[](const WonkoReference &ref) { return ref.uid() == "net.minecraft"; });
if (it != end)
{
return (*it).version();
}
return QVariant();
}
case TypeRole: return version->type();
case UidRole: return version->uid();
case TimeRole: return version->time();
case RequiresRole: return QVariant::fromValue(version->requires());
case SortRole: return version->rawTime();
case WonkoVersionPtrRole: return QVariant::fromValue(version);
case RecommendedRole: return version == getRecommended();
case LatestRole: return version == getLatestStable();
default: return QVariant();
}
}
BaseVersionList::RoleList WonkoVersionList::providesRoles() const
{
return {VersionPointerRole, VersionRole, VersionIdRole, ParentGameVersionRole,
TypeRole, UidRole, TimeRole, RequiresRole, SortRole,
RecommendedRole, LatestRole, WonkoVersionPtrRole};
}
QHash<int, QByteArray> WonkoVersionList::roleNames() const
{
QHash<int, QByteArray> roles = BaseVersionList::roleNames();
roles.insert(UidRole, "uid");
roles.insert(TimeRole, "time");
roles.insert(SortRole, "sort");
roles.insert(RequiresRole, "requires");
return roles;
}
std::unique_ptr<Task> WonkoVersionList::remoteUpdateTask()
{
return std::unique_ptr<WonkoVersionListRemoteLoadTask>(new WonkoVersionListRemoteLoadTask(this, this));
}
std::unique_ptr<Task> WonkoVersionList::localUpdateTask()
{
return std::unique_ptr<WonkoVersionListLocalLoadTask>(new WonkoVersionListLocalLoadTask(this, this));
}
QString WonkoVersionList::localFilename() const
{
return m_uid + ".json";
}
QJsonObject WonkoVersionList::serialized() const
{
return WonkoFormat::serializeVersionList(this);
}
QString WonkoVersionList::humanReadable() const
{
return m_name.isEmpty() ? m_uid : m_name;
}
bool WonkoVersionList::hasVersion(const QString &version) const
{
return m_lookup.contains(version);
}
WonkoVersionPtr WonkoVersionList::getVersion(const QString &version) const
{
return m_lookup.value(version);
}
void WonkoVersionList::setName(const QString &name)
{
m_name = name;
emit nameChanged(name);
}
void WonkoVersionList::setVersions(const QVector<WonkoVersionPtr> &versions)
{
beginResetModel();
m_versions = versions;
std::sort(m_versions.begin(), m_versions.end(), [](const WonkoVersionPtr &a, const WonkoVersionPtr &b)
{
return a->rawTime() > b->rawTime();
});
for (int i = 0; i < m_versions.size(); ++i)
{
m_lookup.insert(m_versions.at(i)->version(), m_versions.at(i));
setupAddedVersion(i, m_versions.at(i));
}
m_latest = m_versions.isEmpty() ? nullptr : m_versions.first();
auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const WonkoVersionPtr &ptr) { return ptr->type() == "release"; });
m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;
endResetModel();
}
void WonkoVersionList::merge(const BaseWonkoEntity::Ptr &other)
{
const WonkoVersionListPtr list = std::dynamic_pointer_cast<WonkoVersionList>(other);
if (m_name != list->m_name)
{
setName(list->m_name);
}
if (m_versions.isEmpty())
{
setVersions(list->m_versions);
}
else
{
for (const WonkoVersionPtr &version : list->m_versions)
{
if (m_lookup.contains(version->version()))
{
m_lookup.value(version->version())->merge(version);
}
else
{
beginInsertRows(QModelIndex(), m_versions.size(), m_versions.size());
setupAddedVersion(m_versions.size(), version);
m_versions.append(version);
m_lookup.insert(version->uid(), version);
endInsertRows();
if (!m_latest || version->rawTime() > m_latest->rawTime())
{
m_latest = version;
emit dataChanged(index(0), index(m_versions.size() - 1), QVector<int>() << LatestRole);
}
if (!m_recommended || (version->type() == "release" && version->rawTime() > m_recommended->rawTime()))
{
m_recommended = version;
emit dataChanged(index(0), index(m_versions.size() - 1), QVector<int>() << RecommendedRole);
}
}
}
}
}
void WonkoVersionList::setupAddedVersion(const int row, const WonkoVersionPtr &version)
{
connect(version.get(), &WonkoVersion::requiresChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << RequiresRole); });
connect(version.get(), &WonkoVersion::timeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TimeRole << SortRole); });
connect(version.get(), &WonkoVersion::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });
}
BaseVersionPtr WonkoVersionList::getLatestStable() const
{
return m_latest;
}
BaseVersionPtr WonkoVersionList::getRecommended() const
{
return m_recommended;
}
#include "WonkoVersionList.moc"

View File

@ -1,80 +0,0 @@
/* Copyright 2015-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.
*/
#include "WonkoFormat.h"
#include "WonkoFormatV1.h"
#include "wonko/WonkoIndex.h"
#include "wonko/WonkoVersion.h"
#include "wonko/WonkoVersionList.h"
static int formatVersion(const QJsonObject &obj)
{
if (!obj.contains("formatVersion")) {
throw WonkoParseException(QObject::tr("Missing required field: 'formatVersion'"));
}
if (!obj.value("formatVersion").isDouble()) {
throw WonkoParseException(QObject::tr("Required field has invalid type: 'formatVersion'"));
}
return obj.value("formatVersion").toInt();
}
void WonkoFormat::parseIndex(const QJsonObject &obj, WonkoIndex *ptr)
{
const int version = formatVersion(obj);
switch (version) {
case 1:
ptr->merge(WonkoFormatV1().parseIndexInternal(obj));
break;
default:
throw WonkoParseException(QObject::tr("Unknown formatVersion: %1").arg(version));
}
}
void WonkoFormat::parseVersion(const QJsonObject &obj, WonkoVersion *ptr)
{
const int version = formatVersion(obj);
switch (version) {
case 1:
ptr->merge(WonkoFormatV1().parseVersionInternal(obj));
break;
default:
throw WonkoParseException(QObject::tr("Unknown formatVersion: %1").arg(version));
}
}
void WonkoFormat::parseVersionList(const QJsonObject &obj, WonkoVersionList *ptr)
{
const int version = formatVersion(obj);
switch (version) {
case 10:
ptr->merge(WonkoFormatV1().parseVersionListInternal(obj));
break;
default:
throw WonkoParseException(QObject::tr("Unknown formatVersion: %1").arg(version));
}
}
QJsonObject WonkoFormat::serializeIndex(const WonkoIndex *ptr)
{
return WonkoFormatV1().serializeIndexInternal(ptr);
}
QJsonObject WonkoFormat::serializeVersion(const WonkoVersion *ptr)
{
return WonkoFormatV1().serializeVersionInternal(ptr);
}
QJsonObject WonkoFormat::serializeVersionList(const WonkoVersionList *ptr)
{
return WonkoFormatV1().serializeVersionListInternal(ptr);
}

View File

@ -1,54 +0,0 @@
/* Copyright 2015-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 <QJsonObject>
#include <memory>
#include "Exception.h"
#include "wonko/BaseWonkoEntity.h"
class WonkoIndex;
class WonkoVersion;
class WonkoVersionList;
class WonkoParseException : public Exception
{
public:
using Exception::Exception;
};
class WonkoFormat
{
public:
virtual ~WonkoFormat() {}
static void parseIndex(const QJsonObject &obj, WonkoIndex *ptr);
static void parseVersion(const QJsonObject &obj, WonkoVersion *ptr);
static void parseVersionList(const QJsonObject &obj, WonkoVersionList *ptr);
static QJsonObject serializeIndex(const WonkoIndex *ptr);
static QJsonObject serializeVersion(const WonkoVersion *ptr);
static QJsonObject serializeVersionList(const WonkoVersionList *ptr);
protected:
virtual BaseWonkoEntity::Ptr parseIndexInternal(const QJsonObject &obj) const = 0;
virtual BaseWonkoEntity::Ptr parseVersionInternal(const QJsonObject &obj) const = 0;
virtual BaseWonkoEntity::Ptr parseVersionListInternal(const QJsonObject &obj) const = 0;
virtual QJsonObject serializeIndexInternal(const WonkoIndex *ptr) const = 0;
virtual QJsonObject serializeVersionInternal(const WonkoVersion *ptr) const = 0;
virtual QJsonObject serializeVersionListInternal(const WonkoVersionList *ptr) const = 0;
};

View File

@ -1,158 +0,0 @@
/* Copyright 2015-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.
*/
#include "WonkoFormatV1.h"
#include <minecraft/onesix/OneSixVersionFormat.h>
#include "Json.h"
#include "wonko/WonkoIndex.h"
#include "wonko/WonkoVersion.h"
#include "wonko/WonkoVersionList.h"
#include "Env.h"
using namespace Json;
static WonkoVersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)
{
const QVector<QJsonObject> requiresRaw = obj.contains("requires") ? requireIsArrayOf<QJsonObject>(obj, "requires") : QVector<QJsonObject>();
QVector<WonkoReference> requires;
requires.reserve(requiresRaw.size());
std::transform(requiresRaw.begin(), requiresRaw.end(), std::back_inserter(requires), [](const QJsonObject &rObj)
{
WonkoReference ref(requireString(rObj, "uid"));
ref.setVersion(ensureString(rObj, "version", QString()));
return ref;
});
WonkoVersionPtr version = std::make_shared<WonkoVersion>(uid, requireString(obj, "version"));
if (obj.value("time").isString())
{
version->setTime(QDateTime::fromString(requireString(obj, "time"), Qt::ISODate).toMSecsSinceEpoch() / 1000);
}
else
{
version->setTime(requireInteger(obj, "time"));
}
version->setType(ensureString(obj, "type", QString()));
version->setRequires(requires);
return version;
}
static void serializeCommonVersion(const WonkoVersion *version, QJsonObject &obj)
{
QJsonArray requires;
for (const WonkoReference &ref : version->requires())
{
if (ref.version().isEmpty())
{
QJsonObject out;
out["uid"] = ref.uid();
requires.append(out);
}
else
{
QJsonObject out;
out["uid"] = ref.uid();
out["version"] = ref.version();
requires.append(out);
}
}
obj.insert("version", version->version());
obj.insert("type", version->type());
obj.insert("time", version->time().toString(Qt::ISODate));
obj.insert("requires", requires);
}
BaseWonkoEntity::Ptr WonkoFormatV1::parseIndexInternal(const QJsonObject &obj) const
{
const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "index");
QVector<WonkoVersionListPtr> lists;
lists.reserve(objects.size());
std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)
{
WonkoVersionListPtr list = std::make_shared<WonkoVersionList>(requireString(obj, "uid"));
list->setName(ensureString(obj, "name", QString()));
return list;
});
return std::make_shared<WonkoIndex>(lists);
}
BaseWonkoEntity::Ptr WonkoFormatV1::parseVersionInternal(const QJsonObject &obj) const
{
WonkoVersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj);
version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),
QString("%1/%2.json").arg(version->uid(), version->version()),
obj.contains("order")));
return version;
}
BaseWonkoEntity::Ptr WonkoFormatV1::parseVersionListInternal(const QJsonObject &obj) const
{
const QString uid = requireString(obj, "uid");
const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions");
QVector<WonkoVersionPtr> versions;
versions.reserve(versionsRaw.size());
std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [this, uid](const QJsonObject &vObj)
{ return parseCommonVersion(uid, vObj); });
WonkoVersionListPtr list = std::make_shared<WonkoVersionList>(uid);
list->setName(ensureString(obj, "name", QString()));
list->setVersions(versions);
return list;
}
QJsonObject WonkoFormatV1::serializeIndexInternal(const WonkoIndex *ptr) const
{
QJsonArray index;
for (const WonkoVersionListPtr &list : ptr->lists())
{
QJsonObject out;
out["uid"] = list->uid();
out["version"] = list->name();
index.append(out);
}
QJsonObject out;
out["formatVersion"] = 1;
out["index"] = index;
return out;
}
QJsonObject WonkoFormatV1::serializeVersionInternal(const WonkoVersion *ptr) const
{
QJsonObject obj = OneSixVersionFormat::versionFileToJson(ptr->data(), true).object();
serializeCommonVersion(ptr, obj);
obj.insert("formatVersion", 1);
obj.insert("uid", ptr->uid());
// TODO: the name should be looked up in the UI based on the uid
obj.insert("name", ENV.wonkoIndex()->getListGuaranteed(ptr->uid())->name());
return obj;
}
QJsonObject WonkoFormatV1::serializeVersionListInternal(const WonkoVersionList *ptr) const
{
QJsonArray versions;
for (const WonkoVersionPtr &version : ptr->versions())
{
QJsonObject obj;
serializeCommonVersion(version.get(), obj);
versions.append(obj);
}
QJsonObject out;
out["formatVersion"] = 10;
out["uid"] = ptr->uid();
out["name"] = ptr->name().isNull() ? QJsonValue() : ptr->name();
out["versions"] = versions;
return out;
}

View File

@ -1,30 +0,0 @@
/* Copyright 2015-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 "WonkoFormat.h"
class WonkoFormatV1 : public WonkoFormat
{
public:
BaseWonkoEntity::Ptr parseIndexInternal(const QJsonObject &obj) const override;
BaseWonkoEntity::Ptr parseVersionInternal(const QJsonObject &obj) const override;
BaseWonkoEntity::Ptr parseVersionListInternal(const QJsonObject &obj) const override;
QJsonObject serializeIndexInternal(const WonkoIndex *ptr) const override;
QJsonObject serializeVersionInternal(const WonkoVersion *ptr) const override;
QJsonObject serializeVersionListInternal(const WonkoVersionList *ptr) const override;
};

View File

@ -1,117 +0,0 @@
/* Copyright 2015-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.
*/
#include "BaseWonkoEntityLocalLoadTask.h"
#include <QFile>
#include "wonko/format/WonkoFormat.h"
#include "wonko/WonkoUtil.h"
#include "wonko/WonkoIndex.h"
#include "wonko/WonkoVersion.h"
#include "wonko/WonkoVersionList.h"
#include "Env.h"
#include "Json.h"
BaseWonkoEntityLocalLoadTask::BaseWonkoEntityLocalLoadTask(BaseWonkoEntity *entity, QObject *parent)
: Task(parent), m_entity(entity)
{
}
void BaseWonkoEntityLocalLoadTask::executeTask()
{
const QString fname = Wonko::localWonkoDir().absoluteFilePath(filename());
if (!QFile::exists(fname))
{
emitFailed(tr("File doesn't exist"));
return;
}
setStatus(tr("Reading %1...").arg(name()));
setProgress(0, 0);
try
{
parse(Json::requireObject(Json::requireDocument(fname, name()), name()));
m_entity->notifyLocalLoadComplete();
emitSucceeded();
}
catch (Exception &e)
{
emitFailed(tr("Unable to parse file %1: %2").arg(fname, e.cause()));
}
}
// WONKO INDEX //
WonkoIndexLocalLoadTask::WonkoIndexLocalLoadTask(WonkoIndex *index, QObject *parent)
: BaseWonkoEntityLocalLoadTask(index, parent)
{
}
QString WonkoIndexLocalLoadTask::filename() const
{
return "index.json";
}
QString WonkoIndexLocalLoadTask::name() const
{
return tr("Wonko Index");
}
void WonkoIndexLocalLoadTask::parse(const QJsonObject &obj) const
{
WonkoFormat::parseIndex(obj, dynamic_cast<WonkoIndex *>(entity()));
}
// WONKO VERSION LIST //
WonkoVersionListLocalLoadTask::WonkoVersionListLocalLoadTask(WonkoVersionList *list, QObject *parent)
: BaseWonkoEntityLocalLoadTask(list, parent)
{
}
QString WonkoVersionListLocalLoadTask::filename() const
{
return list()->uid() + ".json";
}
QString WonkoVersionListLocalLoadTask::name() const
{
return tr("Wonko Version List for %1").arg(list()->humanReadable());
}
void WonkoVersionListLocalLoadTask::parse(const QJsonObject &obj) const
{
WonkoFormat::parseVersionList(obj, list());
}
WonkoVersionList *WonkoVersionListLocalLoadTask::list() const
{
return dynamic_cast<WonkoVersionList *>(entity());
}
// WONKO VERSION //
WonkoVersionLocalLoadTask::WonkoVersionLocalLoadTask(WonkoVersion *version, QObject *parent)
: BaseWonkoEntityLocalLoadTask(version, parent)
{
}
QString WonkoVersionLocalLoadTask::filename() const
{
return version()->uid() + "/" + version()->version() + ".json";
}
QString WonkoVersionLocalLoadTask::name() const
{
return tr("Wonko Version for %1").arg(version()->name());
}
void WonkoVersionLocalLoadTask::parse(const QJsonObject &obj) const
{
WonkoFormat::parseVersion(obj, version());
}
WonkoVersion *WonkoVersionLocalLoadTask::version() const
{
return dynamic_cast<WonkoVersion *>(entity());
}

View File

@ -1,81 +0,0 @@
/* Copyright 2015-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 "tasks/Task.h"
#include <memory>
class BaseWonkoEntity;
class WonkoIndex;
class WonkoVersionList;
class WonkoVersion;
class BaseWonkoEntityLocalLoadTask : public Task
{
Q_OBJECT
public:
explicit BaseWonkoEntityLocalLoadTask(BaseWonkoEntity *entity, QObject *parent = nullptr);
protected:
virtual QString filename() const = 0;
virtual QString name() const = 0;
virtual void parse(const QJsonObject &obj) const = 0;
BaseWonkoEntity *entity() const { return m_entity; }
private:
void executeTask() override;
BaseWonkoEntity *m_entity;
};
class WonkoIndexLocalLoadTask : public BaseWonkoEntityLocalLoadTask
{
Q_OBJECT
public:
explicit WonkoIndexLocalLoadTask(WonkoIndex *index, QObject *parent = nullptr);
private:
QString filename() const override;
QString name() const override;
void parse(const QJsonObject &obj) const override;
};
class WonkoVersionListLocalLoadTask : public BaseWonkoEntityLocalLoadTask
{
Q_OBJECT
public:
explicit WonkoVersionListLocalLoadTask(WonkoVersionList *list, QObject *parent = nullptr);
private:
QString filename() const override;
QString name() const override;
void parse(const QJsonObject &obj) const override;
WonkoVersionList *list() const;
};
class WonkoVersionLocalLoadTask : public BaseWonkoEntityLocalLoadTask
{
Q_OBJECT
public:
explicit WonkoVersionLocalLoadTask(WonkoVersion *version, QObject *parent = nullptr);
private:
QString filename() const override;
QString name() const override;
void parse(const QJsonObject &obj) const override;
WonkoVersion *version() const;
};

View File

@ -1,126 +0,0 @@
/* Copyright 2015-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.
*/
#include "BaseWonkoEntityRemoteLoadTask.h"
#include "net/Download.h"
#include "net/HttpMetaCache.h"
#include "net/NetJob.h"
#include "wonko/format/WonkoFormat.h"
#include "wonko/WonkoUtil.h"
#include "wonko/WonkoIndex.h"
#include "wonko/WonkoVersion.h"
#include "wonko/WonkoVersionList.h"
#include "Env.h"
#include "Json.h"
BaseWonkoEntityRemoteLoadTask::BaseWonkoEntityRemoteLoadTask(BaseWonkoEntity *entity, QObject *parent)
: Task(parent), m_entity(entity)
{
}
void BaseWonkoEntityRemoteLoadTask::executeTask()
{
NetJob *job = new NetJob(name());
auto entry = ENV.metacache()->resolveEntry("wonko", url().toString());
entry->setStale(true);
m_dl = Net::Download::makeCached(url(), entry);
job->addNetAction(m_dl);
connect(job, &NetJob::failed, this, &BaseWonkoEntityRemoteLoadTask::emitFailed);
connect(job, &NetJob::succeeded, this, &BaseWonkoEntityRemoteLoadTask::networkFinished);
connect(job, &NetJob::status, this, &BaseWonkoEntityRemoteLoadTask::setStatus);
connect(job, &NetJob::progress, this, &BaseWonkoEntityRemoteLoadTask::setProgress);
job->start();
}
void BaseWonkoEntityRemoteLoadTask::networkFinished()
{
setStatus(tr("Parsing..."));
setProgress(0, 0);
try
{
parse(Json::requireObject(Json::requireDocument(m_dl->getTargetFilepath(), name()), name()));
m_entity->notifyRemoteLoadComplete();
emitSucceeded();
}
catch (Exception &e)
{
emitFailed(tr("Unable to parse response: %1").arg(e.cause()));
}
}
// WONKO INDEX //
WonkoIndexRemoteLoadTask::WonkoIndexRemoteLoadTask(WonkoIndex *index, QObject *parent)
: BaseWonkoEntityRemoteLoadTask(index, parent)
{
}
QUrl WonkoIndexRemoteLoadTask::url() const
{
return Wonko::indexUrl();
}
QString WonkoIndexRemoteLoadTask::name() const
{
return tr("Wonko Index");
}
void WonkoIndexRemoteLoadTask::parse(const QJsonObject &obj) const
{
WonkoFormat::parseIndex(obj, dynamic_cast<WonkoIndex *>(entity()));
}
// WONKO VERSION LIST //
WonkoVersionListRemoteLoadTask::WonkoVersionListRemoteLoadTask(WonkoVersionList *list, QObject *parent)
: BaseWonkoEntityRemoteLoadTask(list, parent)
{
}
QUrl WonkoVersionListRemoteLoadTask::url() const
{
return Wonko::versionListUrl(list()->uid());
}
QString WonkoVersionListRemoteLoadTask::name() const
{
return tr("Wonko Version List for %1").arg(list()->humanReadable());
}
void WonkoVersionListRemoteLoadTask::parse(const QJsonObject &obj) const
{
WonkoFormat::parseVersionList(obj, list());
}
WonkoVersionList *WonkoVersionListRemoteLoadTask::list() const
{
return dynamic_cast<WonkoVersionList *>(entity());
}
// WONKO VERSION //
WonkoVersionRemoteLoadTask::WonkoVersionRemoteLoadTask(WonkoVersion *version, QObject *parent)
: BaseWonkoEntityRemoteLoadTask(version, parent)
{
}
QUrl WonkoVersionRemoteLoadTask::url() const
{
return Wonko::versionUrl(version()->uid(), version()->version());
}
QString WonkoVersionRemoteLoadTask::name() const
{
return tr("Wonko Version for %1").arg(version()->name());
}
void WonkoVersionRemoteLoadTask::parse(const QJsonObject &obj) const
{
WonkoFormat::parseVersion(obj, version());
}
WonkoVersion *WonkoVersionRemoteLoadTask::version() const
{
return dynamic_cast<WonkoVersion *>(entity());
}

View File

@ -1,90 +0,0 @@
/* Copyright 2015-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 "tasks/Task.h"
#include <memory>
namespace Net
{
class Download;
}
class BaseWonkoEntity;
class WonkoIndex;
class WonkoVersionList;
class WonkoVersion;
class BaseWonkoEntityRemoteLoadTask : public Task
{
Q_OBJECT
public:
explicit BaseWonkoEntityRemoteLoadTask(BaseWonkoEntity *entity, QObject *parent = nullptr);
protected:
virtual QUrl url() const = 0;
virtual QString name() const = 0;
virtual void parse(const QJsonObject &obj) const = 0;
BaseWonkoEntity *entity() const { return m_entity; }
private slots:
void networkFinished();
private:
void executeTask() override;
BaseWonkoEntity *m_entity;
std::shared_ptr<Net::Download> m_dl;
};
class WonkoIndexRemoteLoadTask : public BaseWonkoEntityRemoteLoadTask
{
Q_OBJECT
public:
explicit WonkoIndexRemoteLoadTask(WonkoIndex *index, QObject *parent = nullptr);
private:
QUrl url() const override;
QString name() const override;
void parse(const QJsonObject &obj) const override;
};
class WonkoVersionListRemoteLoadTask : public BaseWonkoEntityRemoteLoadTask
{
Q_OBJECT
public:
explicit WonkoVersionListRemoteLoadTask(WonkoVersionList *list, QObject *parent = nullptr);
private:
QUrl url() const override;
QString name() const override;
void parse(const QJsonObject &obj) const override;
WonkoVersionList *list() const;
};
class WonkoVersionRemoteLoadTask : public BaseWonkoEntityRemoteLoadTask
{
Q_OBJECT
public:
explicit WonkoVersionRemoteLoadTask(WonkoVersion *version, QObject *parent = nullptr);
private:
QUrl url() const override;
QString name() const override;
void parse(const QJsonObject &obj) const override;
WonkoVersion *version() const;
};

View File

@ -33,7 +33,6 @@ Config::Config()
VERSION_STR = "@MultiMC_VERSION_STRING@";
NEWS_RSS_URL = "@MultiMC_NEWS_RSS_URL@";
PASTE_EE_KEY = "@MultiMC_PASTE_EE_API_KEY@";
WONKO_ROOT_URL = "@MultiMC_WONKO_ROOT_URL@";
}
QString Config::printableVersionString() const

View File

@ -60,11 +60,6 @@ public:
*/
QString PASTE_EE_KEY;
/**
* Root URL for wonko things. Other wonko URLs will be resolved relative to this.
*/
QString WONKO_ROOT_URL;
/**
* \brief Converts the Version to a string.
* \return The version number in string format (major.minor.revision.build).

View File

@ -30,9 +30,6 @@ set(MultiMC_ANALYTICS_ID "" CACHE STRING "ID you can get from Google analytics")
include(GetGitRevisionDescription)
get_git_head_revision(MultiMC_GIT_REFSPEC MultiMC_GIT_COMMIT)
# Root URL for wonko files
set(MultiMC_WONKO_ROOT_URL "" CACHE STRING "Root URL for wonko stuff")
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
message(STATUS "Git refspec: ${MultiMC_GIT_REFSPEC}")
@ -99,8 +96,6 @@ SET(MULTIMC_SOURCES
VersionProxyModel.cpp
ColorCache.h
ColorCache.cpp
WonkoGui.h
WonkoGui.cpp
# GUI - windows
MainWindow.h
@ -189,8 +184,8 @@ SET(MULTIMC_SOURCES
pages/global/ProxyPage.h
pages/global/PasteEEPage.cpp
pages/global/PasteEEPage.h
pages/global/WonkoPage.cpp
pages/global/WonkoPage.h
pages/global/PackagesPage.cpp
pages/global/PackagesPage.h
# GUI - dialogs
dialogs/AboutDialog.cpp
@ -289,7 +284,7 @@ SET(MULTIMC_UIS
pages/global/MultiMCPage.ui
pages/global/ProxyPage.ui
pages/global/PasteEEPage.ui
pages/global/WonkoPage.ui
pages/global/PackagesPage.ui
# Dialogs
dialogs/CopyInstanceDialog.ui
@ -318,7 +313,6 @@ set(MULTIMC_QRCS
resources/pe_blue/pe_blue.qrc
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/versions/versions.qrc
resources/certs/certs.qrc
)

View File

@ -54,7 +54,6 @@
#include <java/JavaUtils.h>
#include <java/JavaInstallList.h>
#include <launch/LaunchTask.h>
#include <minecraft/MinecraftVersionList.h>
#include <minecraft/legacy/LwjglVersionList.h>
#include <minecraft/auth/MojangAccountList.h>
#include <SkinUtils.h>
@ -555,19 +554,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
job->start();
}
// run the things that load and download other things... FIXME: this is NOT the place
// FIXME: invisible actions in the background = NOPE.
// load the news
{
if (!MMC->minecraftlist()->isLoaded())
{
m_versionLoadTask = MMC->minecraftlist()->getLoadTask();
startTask(m_versionLoadTask);
}
if (!MMC->lwjgllist()->isLoaded())
{
MMC->lwjgllist()->loadList();
}
m_newsChecker->reloadNews();
updateNewsLabel();
}
@ -1014,18 +1002,6 @@ void MainWindow::setCatBackground(bool enabled)
}
}
// FIXME: eliminate, should not be needed
void MainWindow::waitForMinecraftVersions()
{
if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && m_versionLoadTask->isRunning())
{
QEventLoop waitLoop;
waitLoop.connect(m_versionLoadTask, &Task::failed, &waitLoop, &QEventLoop::quit);
waitLoop.connect(m_versionLoadTask, &Task::succeeded, &waitLoop, &QEventLoop::quit);
waitLoop.exec();
}
}
void MainWindow::runModalTask(Task *task)
{
connect(task, &Task::failed, [this](QString reason)
@ -1117,8 +1093,6 @@ void MainWindow::on_actionAddInstance_triggered()
groupName = map["group"].toString();
} while(0);
waitForMinecraftVersions();
if(groupName.isEmpty())
{
groupName = MMC->settings()->get("LastUsedGroupForNewInstance").toString();

View File

@ -167,7 +167,6 @@ private:
void updateInstanceToolIcon(QString new_icon);
void setSelectedInstanceById(const QString &id);
void waitForMinecraftVersions();
void runModalTask(Task *task);
void instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version);
void instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url);

View File

@ -10,6 +10,7 @@
#include "pages/global/ExternalToolsPage.h"
#include "pages/global/AccountListPage.h"
#include "pages/global/PasteEEPage.h"
#include "pages/global/PackagesPage.h"
#include "themes/ITheme.h"
#include "themes/SystemTheme.h"
@ -41,9 +42,6 @@
#include "icons/IconList.h"
//FIXME: get rid of this
#include "minecraft/legacy/LwjglVersionList.h"
#include "minecraft/MinecraftVersionList.h"
#include "minecraft/liteloader/LiteLoaderVersionList.h"
#include "minecraft/forge/ForgeVersionList.h"
#include "net/HttpMetaCache.h"
#include "net/URLConstants.h"
@ -337,7 +335,6 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
initIcons();
initThemes();
// make sure we have at least some minecraft versions before we init instances
minecraftlist();
initInstances();
initAccounts();
initNetwork();
@ -842,6 +839,7 @@ void MultiMC::initGlobalSettings()
m_globalSettingsProvider->addPage<MinecraftPage>();
m_globalSettingsProvider->addPage<JavaPage>();
m_globalSettingsProvider->addPage<ProxyPage>();
m_globalSettingsProvider->addPage<PackagesPage>();
m_globalSettingsProvider->addPage<ExternalToolsPage>();
m_globalSettingsProvider->addPage<AccountListPage>();
m_globalSettingsProvider->addPage<PasteEEPage>();
@ -868,36 +866,6 @@ std::shared_ptr<LWJGLVersionList> MultiMC::lwjgllist()
return m_lwjgllist;
}
std::shared_ptr<ForgeVersionList> MultiMC::forgelist()
{
if (!m_forgelist)
{
m_forgelist.reset(new ForgeVersionList());
ENV.registerVersionList("net.minecraftforge", m_forgelist);
}
return m_forgelist;
}
std::shared_ptr<LiteLoaderVersionList> MultiMC::liteloaderlist()
{
if (!m_liteloaderlist)
{
m_liteloaderlist.reset(new LiteLoaderVersionList());
ENV.registerVersionList("com.mumfrey.liteloader", m_liteloaderlist);
}
return m_liteloaderlist;
}
std::shared_ptr<MinecraftVersionList> MultiMC::minecraftlist()
{
if (!m_minecraftlist)
{
m_minecraftlist.reset(new MinecraftVersionList());
ENV.registerVersionList("net.minecraft", m_minecraftlist);
}
return m_minecraftlist;
}
std::shared_ptr<JavaInstallList> MultiMC::javalist()
{
if (!m_javalist)

View File

@ -18,7 +18,6 @@ class SetupWizard;
class FolderInstanceProvider;
class GenericPageProvider;
class QFile;
class MinecraftVersionList;
class LWJGLVersionList;
class HttpMetaCache;
class SettingsObject;
@ -26,8 +25,6 @@ class InstanceList;
class MojangAccountList;
class IconList;
class QNetworkAccessManager;
class ForgeVersionList;
class LiteLoaderVersionList;
class JavaInstallList;
class UpdateChecker;
class BaseProfilerFactory;
@ -96,10 +93,7 @@ public:
}
std::shared_ptr<TranslationsModel> translations();
std::shared_ptr<MinecraftVersionList> minecraftlist();
std::shared_ptr<LWJGLVersionList> lwjgllist();
std::shared_ptr<ForgeVersionList> forgelist();
std::shared_ptr<LiteLoaderVersionList> liteloaderlist();
std::shared_ptr<JavaInstallList> javalist();
std::shared_ptr<InstanceList> instances() const
@ -202,9 +196,6 @@ private:
std::shared_ptr<UpdateChecker> m_updateChecker;
std::shared_ptr<MojangAccountList> m_accounts;
std::shared_ptr<LWJGLVersionList> m_lwjgllist;
std::shared_ptr<ForgeVersionList> m_forgelist;
std::shared_ptr<LiteLoaderVersionList> m_liteloaderlist;
std::shared_ptr<MinecraftVersionList> m_minecraftlist;
std::shared_ptr<JavaInstallList> m_javalist;
std::shared_ptr<TranslationsModel> m_translations;
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;

View File

@ -26,19 +26,9 @@ public:
switch(role)
{
case BaseVersionList::ParentGameVersionRole:
case BaseVersionList::ParentVersionRole:
case BaseVersionList::VersionIdRole:
{
auto versionString = data.toString();
if(it.value().exact)
{
return versionString == it.value().string;
}
else
{
return versionIsInInterval(versionString, it.value().string);
}
}
// TODO: work with metadata here. Previous implementation based on the Version class is not sufficient
default:
{
auto match = data.toString();
@ -146,7 +136,7 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
case Name:
return sourceModel()->data(parentIndex, BaseVersionList::VersionRole);
case ParentVersion:
return sourceModel()->data(parentIndex, BaseVersionList::ParentGameVersionRole);
return sourceModel()->data(parentIndex, BaseVersionList::ParentVersionRole);
case Branch:
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
case Type:
@ -313,9 +303,9 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw)
auto replacing = dynamic_cast<BaseVersionList *>(replacingRaw);
beginResetModel();
m_columns.clear();
if(!replacing)
{
m_columns.clear();
roles.clear();
filterModel->setSourceModel(replacing);
return;
@ -327,7 +317,7 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw)
m_columns.push_back(Name);
}
/*
if(roles.contains(BaseVersionList::ParentGameVersionRole))
if(roles.contains(BaseVersionList::ParentVersionRole))
{
m_columns.push_back(ParentVersion);
}

View File

@ -1,74 +0,0 @@
#include "WonkoGui.h"
#include "dialogs/ProgressDialog.h"
#include "wonko/WonkoIndex.h"
#include "wonko/WonkoVersionList.h"
#include "wonko/WonkoVersion.h"
#include "Env.h"
WonkoIndexPtr Wonko::ensureIndexLoaded(QWidget *parent)
{
if (!ENV.wonkoIndex()->isLocalLoaded())
{
ProgressDialog(parent).execWithTask(ENV.wonkoIndex()->localUpdateTask());
if (!ENV.wonkoIndex()->isRemoteLoaded() && ENV.wonkoIndex()->lists().size() == 0)
{
ProgressDialog(parent).execWithTask(ENV.wonkoIndex()->remoteUpdateTask());
}
}
return ENV.wonkoIndex();
}
WonkoVersionListPtr Wonko::ensureVersionListExists(const QString &uid, QWidget *parent)
{
ensureIndexLoaded(parent);
if (!ENV.wonkoIndex()->isRemoteLoaded() && !ENV.wonkoIndex()->hasUid(uid))
{
ProgressDialog(parent).execWithTask(ENV.wonkoIndex()->remoteUpdateTask());
}
return ENV.wonkoIndex()->getList(uid);
}
WonkoVersionListPtr Wonko::ensureVersionListLoaded(const QString &uid, QWidget *parent)
{
WonkoVersionListPtr list = ensureVersionListExists(uid, parent);
if (!list)
{
return nullptr;
}
if (!list->isLocalLoaded())
{
ProgressDialog(parent).execWithTask(list->localUpdateTask());
if (!list->isLocalLoaded())
{
ProgressDialog(parent).execWithTask(list->remoteUpdateTask());
}
}
return list->isComplete() ? list : nullptr;
}
WonkoVersionPtr Wonko::ensureVersionExists(const QString &uid, const QString &version, QWidget *parent)
{
WonkoVersionListPtr list = ensureVersionListLoaded(uid, parent);
if (!list)
{
return nullptr;
}
return list->getVersion(version);
}
WonkoVersionPtr Wonko::ensureVersionLoaded(const QString &uid, const QString &version, QWidget *parent, const UpdateType update)
{
WonkoVersionPtr vptr = ensureVersionExists(uid, version, parent);
if (!vptr)
{
return nullptr;
}
if (!vptr->isLocalLoaded() || update == AlwaysUpdate)
{
ProgressDialog(parent).execWithTask(vptr->localUpdateTask());
if (!vptr->isLocalLoaded() || update == AlwaysUpdate)
{
ProgressDialog(parent).execWithTask(vptr->remoteUpdateTask());
}
}
return vptr->isComplete() ? vptr : nullptr;
}

View File

@ -1,29 +0,0 @@
#pragma once
#include <memory>
#include "QObjectPtr.h"
class QWidget;
class QString;
using WonkoIndexPtr = shared_qobject_ptr<class WonkoIndex>;
using WonkoVersionListPtr = std::shared_ptr<class WonkoVersionList>;
using WonkoVersionPtr = std::shared_ptr<class WonkoVersion>;
namespace Wonko
{
enum UpdateType
{
AlwaysUpdate,
UpdateIfNeeded
};
/// Ensures that the index has been loaded, either from the local cache or remotely
WonkoIndexPtr ensureIndexLoaded(QWidget *parent);
/// Ensures that the given uid exists. Returns a nullptr if it doesn't.
WonkoVersionListPtr ensureVersionListExists(const QString &uid, QWidget *parent);
/// Ensures that the given uid exists and is loaded, either from the local cache or remotely. Returns nullptr if it doesn't exist or couldn't be loaded.
WonkoVersionListPtr ensureVersionListLoaded(const QString &uid, QWidget *parent);
WonkoVersionPtr ensureVersionExists(const QString &uid, const QString &version, QWidget *parent);
WonkoVersionPtr ensureVersionLoaded(const QString &uid, const QString &version, QWidget *parent, const UpdateType update = UpdateIfNeeded);
}

View File

@ -19,7 +19,6 @@
#include <BaseVersion.h>
#include <icons/IconList.h>
#include <minecraft/MinecraftVersionList.h>
#include <tasks/Task.h>
#include <InstanceList.h>
@ -32,6 +31,9 @@
#include <QFileDialog>
#include <QValidator>
#include <meta/Index.h>
#include <meta/VersionList.h>
class UrlValidator : public QValidator
{
public:
@ -62,7 +64,25 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, QWidget *pare
resize(minimumSizeHint());
layout()->setSizeConstraint(QLayout::SetFixedSize);
setSelectedVersion(MMC->minecraftlist()->getRecommended());
auto vlist = ENV.metadataIndex()->get("net.minecraft");
if(vlist->isLoaded())
{
setSelectedVersion(vlist->getRecommended());
}
else
{
vlist->load();
auto task = vlist->getLoadTask();
if(vlist->isLoaded())
{
setSelectedVersion(vlist->getRecommended());
}
if(task)
{
connect(task.get(), &Task::succeeded, this, &NewInstanceDialog::versionListUpdated);
}
}
InstIconKey = "default";
ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
@ -95,6 +115,15 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, QWidget *pare
updateDialogState();
}
void NewInstanceDialog::versionListUpdated()
{
if(!m_versionSetByUser)
{
auto vlist = ENV.metadataIndex()->get("net.minecraft");
setSelectedVersion(vlist->getRecommended());
}
}
NewInstanceDialog::~NewInstanceDialog()
{
delete ui;
@ -134,7 +163,7 @@ void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version)
if (m_selectedVersion)
{
ui->versionTextBox->setText(version->name());
ui->versionTextBox->setText(version->descriptor());
}
else
{
@ -192,14 +221,16 @@ BaseVersionPtr NewInstanceDialog::selectedVersion() const
void NewInstanceDialog::on_btnChangeVersion_clicked()
{
VersionSelectDialog vselect(MMC->minecraftlist().get(), tr("Change Minecraft version"),
this);
VersionSelectDialog vselect(ENV.metadataIndex()->get("net.minecraft").get(), tr("Change Minecraft version"), this);
vselect.exec();
if (vselect.result() == QDialog::Accepted)
{
BaseVersionPtr version = vselect.selectedVersion();
if (version)
{
m_versionSetByUser = true;
setSelectedVersion(version);
}
}
}

View File

@ -36,8 +36,6 @@ public:
void setSelectedVersion(BaseVersionPtr version);
void loadVersionList();
QString instName() const;
QString instGroup() const;
QString iconKey() const;
@ -50,10 +48,12 @@ slots:
void on_iconButton_clicked();
void on_modpackBtn_clicked();
void on_instNameTextBox_textChanged(const QString &arg1);
void versionListUpdated();
private:
Ui::NewInstanceDialog *ui;
bool m_versionSetByUser = false;
BaseVersionPtr m_selectedVersion;
QString InstIconKey;
QString originalPlaceholderText;

View File

@ -38,7 +38,6 @@ int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(multimc);
Q_INIT_RESOURCE(backgrounds);
Q_INIT_RESOURCE(versions);
Q_INIT_RESOURCE(pe_dark);
Q_INIT_RESOURCE(pe_light);

View File

@ -36,19 +36,16 @@
#include <QUrl>
#include "minecraft/MinecraftProfile.h"
#include "minecraft/forge/ForgeVersionList.h"
#include "minecraft/forge/ForgeInstaller.h"
#include "minecraft/liteloader/LiteLoaderVersionList.h"
#include "minecraft/liteloader/LiteLoaderInstaller.h"
#include "minecraft/auth/MojangAccountList.h"
#include "minecraft/Mod.h"
#include "minecraft/MinecraftVersion.h"
#include "minecraft/MinecraftVersionList.h"
#include "icons/IconList.h"
#include "Exception.h"
#include "MultiMC.h"
#include <meta/Index.h>
#include <meta/VersionList.h>
class IconProxy : public QIdentityProxyModel
{
Q_OBJECT
@ -155,14 +152,14 @@ void VersionPage::packageCurrent(const QModelIndex &current, const QModelIndex &
auto severity = patch->getProblemSeverity();
switch(severity)
{
case PROBLEM_WARNING:
case ProblemSeverity::Warning:
ui->frame->setModText(tr("%1 possibly has issues.").arg(patch->getName()));
break;
case PROBLEM_ERROR:
case ProblemSeverity::Error:
ui->frame->setModText(tr("%1 has issues!").arg(patch->getName()));
break;
default:
case PROBLEM_NONE:
case ProblemSeverity::None:
ui->frame->clear();
return;
}
@ -171,11 +168,11 @@ void VersionPage::packageCurrent(const QModelIndex &current, const QModelIndex &
QString problemOut;
for (auto &problem: problems)
{
if(problem.getSeverity() == PROBLEM_ERROR)
if(problem.getSeverity() == ProblemSeverity::Error)
{
problemOut += tr("Error: ");
}
else if(problem.getSeverity() == PROBLEM_WARNING)
else if(problem.getSeverity() == ProblemSeverity::Warning)
{
problemOut += tr("Warning: ");
}
@ -326,8 +323,20 @@ void VersionPage::on_moveDownBtn_clicked()
void VersionPage::on_changeVersionBtn_clicked()
{
VersionSelectDialog vselect(m_inst->versionList().get(), tr("Change Minecraft version"),
this);
auto versionRow = currentRow();
if(versionRow == -1)
{
return;
}
auto patch = m_profile->versionPatch(versionRow);
auto name = patch->getName();
auto list = patch->getVersionList();
if(!list)
{
return;
}
auto uid = list->uid();
VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this);
if (!vselect.exec() || !vselect.selectedVersion())
return;
@ -341,21 +350,25 @@ void VersionPage::on_changeVersionBtn_clicked()
return;
}
if (!m_profile->isVanilla())
qDebug() << "Change" << uid << "to" << vselect.selectedVersion()->descriptor();
if(uid == "net.minecraft")
{
auto result = CustomMessageBox::selectable(
this, tr("Are you sure?"),
tr("This will remove any library/version customization you did previously. "
"This includes things like Forge install and similar."),
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
QMessageBox::Abort)->exec();
if (!m_profile->isVanilla())
{
auto result = CustomMessageBox::selectable(
this, tr("Are you sure?"),
tr("This will remove any library/version customization you did previously. "
"This includes things like Forge install and similar."),
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
QMessageBox::Abort)->exec();
if (result != QMessageBox::Ok)
return;
m_profile->revertToVanilla();
reloadMinecraftProfile();
if (result != QMessageBox::Ok)
return;
m_profile->revertToVanilla();
reloadMinecraftProfile();
}
}
m_inst->setIntendedVersionId(vselect.selectedVersion()->descriptor());
m_inst->setComponentVersion(uid, vselect.selectedVersion()->descriptor());
doUpdate();
m_container->refreshContainer();
}
@ -377,16 +390,21 @@ int VersionPage::doUpdate()
void VersionPage::on_forgeBtn_clicked()
{
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
vselect.setExactFilter(BaseVersionList::ParentGameVersionRole, m_inst->currentVersionId());
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
m_inst->currentVersionId());
auto vlist = ENV.metadataIndex()->get("net.minecraftforge");
if(!vlist)
{
return;
}
VersionSelectDialog vselect(vlist.get(), tr("Select Forge version"), this);
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_inst->currentVersionId());
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + m_inst->currentVersionId());
vselect.setEmptyErrorString(tr("Couldn't load or download the Forge version lists!"));
if (vselect.exec() && vselect.selectedVersion())
{
ProgressDialog dialog(this);
dialog.execWithTask(
ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
auto vsn = vselect.selectedVersion();
m_inst->setComponentVersion("net.minecraftforge", vsn->descriptor());
m_profile->reload();
// m_profile->installVersion();
preselect(m_profile->rowCount(QModelIndex())-1);
m_container->refreshContainer();
}
@ -394,17 +412,21 @@ void VersionPage::on_forgeBtn_clicked()
void VersionPage::on_liteloaderBtn_clicked()
{
VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
this);
vselect.setExactFilter(BaseVersionList::ParentGameVersionRole, m_inst->currentVersionId());
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") +
m_inst->currentVersionId());
auto vlist = ENV.metadataIndex()->get("com.mumfrey.liteloader");
if(!vlist)
{
return;
}
VersionSelectDialog vselect(vlist.get(), tr("Select LiteLoader version"), this);
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_inst->currentVersionId());
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") + m_inst->currentVersionId());
vselect.setEmptyErrorString(tr("Couldn't load or download the LiteLoader version lists!"));
if (vselect.exec() && vselect.selectedVersion())
{
ProgressDialog dialog(this);
dialog.execWithTask(
LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
auto vsn = vselect.selectedVersion();
m_inst->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor());
m_profile->reload();
// m_profile->installVersion(vselect.selectedVersion());
preselect(m_profile->rowCount(QModelIndex())-1);
m_container->refreshContainer();
}
@ -456,8 +478,9 @@ void VersionPage::updateButtons(int row)
ui->moveDownBtn->setEnabled(patch->isMoveable());
ui->moveUpBtn->setEnabled(patch->isMoveable());
ui->changeVersionBtn->setEnabled(patch->isVersionChangeable());
ui->editBtn->setEnabled(patch->isEditable());
ui->customizeBtn->setEnabled(patch->isCustomizable());
ui->editBtn->setEnabled(patch->isCustom());
// FIXME: temporarily disabled, bring it back when the new format is stable and ready to replace the 'OneSix' one...
ui->customizeBtn->setEnabled(false); // patch->isCustomizable()
ui->revertBtn->setEnabled(patch->isRevertible());
}
}
@ -489,20 +512,18 @@ int VersionPage::currentRow()
void VersionPage::on_customizeBtn_clicked()
{
// FIXME: temporarily disabled, bring it back when the new format is stable and ready to replace the 'OneSix' one...
return;
auto version = currentRow();
if(version == -1)
{
return;
}
//HACK HACK remove, this is dumb
auto patch = m_profile->versionPatch(version);
auto mc = std::dynamic_pointer_cast<MinecraftVersion>(patch);
if(mc && mc->needsUpdate())
if(!patch->getVersionFile())
{
if(!doUpdate())
{
return;
}
// TODO: wait for the update task to finish here...
return;
}
if(!m_profile->customize(version))
{
@ -535,15 +556,6 @@ void VersionPage::on_revertBtn_clicked()
{
return;
}
auto mcraw = MMC->minecraftlist()->findVersion(m_inst->intendedVersionId());
auto mc = std::dynamic_pointer_cast<MinecraftVersion>(mcraw);
if(mc && mc->needsUpdate())
{
if(!doUpdate())
{
return;
}
}
if(!m_profile->revertToBase(version))
{
// TODO: some error box here

Some files were not shown because too many files have changed in this diff Show More