Merge branch 'feature/meta' into develop
This commit is contained in:
commit
795889d934
@ -109,4 +109,3 @@ add_subdirectory(api/logic)
|
||||
add_subdirectory(api/gui)
|
||||
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(wonkoclient)
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include "settings/Setting.h"
|
||||
#include "settings/OverrideSetting.h"
|
||||
|
||||
#include "minecraft/MinecraftVersionList.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Commandline.h"
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
)
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
61
api/logic/ProblemProvider.h
Normal file
61
api/logic/ProblemProvider.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -29,7 +29,7 @@ JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
|
||||
{
|
||||
}
|
||||
|
||||
Task *JavaInstallList::getLoadTask()
|
||||
shared_qobject_ptr<Task> JavaInstallList::getLoadTask()
|
||||
{
|
||||
return new JavaListLoadTask(this);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
153
api/logic/meta/BaseEntity.cpp
Normal file
153
api/logic/meta/BaseEntity.cpp
Normal 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"
|
74
api/logic/meta/BaseEntity.h
Normal file
74
api/logic/meta/BaseEntity.h
Normal 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;
|
||||
};
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
44
api/logic/meta/Index_test.cpp
Normal file
44
api/logic/meta/Index_test.cpp
Normal 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"
|
150
api/logic/meta/JsonFormat.cpp
Normal file
150
api/logic/meta/JsonFormat.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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
125
api/logic/meta/Version.cpp
Normal 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;
|
||||
}
|
@ -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)
|
235
api/logic/meta/VersionList.cpp
Normal file
235
api/logic/meta/VersionList.cpp
Normal 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"
|
@ -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)
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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, {});
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
@ -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"
|
@ -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;
|
||||
};
|
@ -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)
|
||||
{
|
||||
|
175
api/logic/minecraft/ProfilePatch.cpp
Normal file
175
api/logic/minecraft/ProfilePatch.cpp
Normal 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."))};
|
||||
}
|
@ -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;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ProfileUtils.h"
|
||||
#include "ProfilePatch.h"
|
||||
|
||||
class MinecraftProfile;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
*/
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"
|
@ -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;
|
||||
};
|
@ -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;
|
||||
}
|
@ -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)
|
@ -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.";
|
||||
}
|
||||
}
|
@ -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);
|
||||
};
|
@ -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;
|
||||
}
|
@ -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);
|
||||
};
|
@ -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
|
||||
{
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
@ -27,7 +27,6 @@
|
||||
#include "LegacyModList.h"
|
||||
|
||||
#include "LwjglVersionList.h"
|
||||
#include "minecraft/MinecraftVersionList.h"
|
||||
#include "LegacyInstance.h"
|
||||
#include <FileSystem.h>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {};
|
||||
|
@ -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"
|
@ -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;
|
||||
};
|
@ -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();
|
||||
}
|
@ -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)
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
};
|
@ -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"
|
@ -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;
|
||||
}
|
@ -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)
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -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"
|
@ -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);
|
||||
}
|
@ -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;
|
||||
};
|
@ -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;
|
||||
}
|
@ -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;
|
||||
};
|
@ -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());
|
||||
}
|
@ -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;
|
||||
};
|
@ -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());
|
||||
}
|
@ -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;
|
||||
};
|
@ -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
|
||||
|
@ -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).
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 ¤t, 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 ¤t, 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
Loading…
Reference in New Issue
Block a user