mirror of
https://github.com/MultiMC/MultiMC5.git
synced 2024-12-26 18:14:22 +00:00
442 lines
9.1 KiB
C++
442 lines
9.1 KiB
C++
#include <meta/VersionList.h>
|
|
#include <meta/Index.h>
|
|
#include "Component.h"
|
|
|
|
#include <QSaveFile>
|
|
|
|
#include "meta/Version.h"
|
|
#include "VersionFile.h"
|
|
#include "minecraft/PackProfile.h"
|
|
#include "FileSystem.h"
|
|
#include "OneSixVersionFormat.h"
|
|
#include "Application.h"
|
|
|
|
#include <assert.h>
|
|
|
|
Component::Component(PackProfile * parent, const QString& uid)
|
|
{
|
|
assert(parent);
|
|
m_parent = parent;
|
|
|
|
m_uid = uid;
|
|
}
|
|
|
|
Component::Component(PackProfile * parent, std::shared_ptr<Meta::Version> version)
|
|
{
|
|
assert(parent);
|
|
m_parent = parent;
|
|
|
|
m_metaVersion = version;
|
|
m_uid = version->uid();
|
|
m_version = m_cachedVersion = version->version();
|
|
m_cachedName = version->name();
|
|
m_loaded = version->isLoaded();
|
|
}
|
|
|
|
Component::Component(PackProfile * parent, const QString& uid, std::shared_ptr<VersionFile> file)
|
|
{
|
|
assert(parent);
|
|
m_parent = parent;
|
|
|
|
m_file = file;
|
|
m_uid = uid;
|
|
m_cachedVersion = m_file->version;
|
|
m_cachedName = m_file->name;
|
|
m_loaded = true;
|
|
}
|
|
|
|
std::shared_ptr<Meta::Version> Component::getMeta()
|
|
{
|
|
return m_metaVersion;
|
|
}
|
|
|
|
void Component::applyTo(LaunchProfile* profile)
|
|
{
|
|
// do not apply disabled components
|
|
if(!isEnabled())
|
|
{
|
|
return;
|
|
}
|
|
auto vfile = getVersionFile();
|
|
if(vfile)
|
|
{
|
|
vfile->applyTo(profile);
|
|
}
|
|
else
|
|
{
|
|
profile->applyProblemSeverity(getProblemSeverity());
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<class VersionFile> Component::getVersionFile() const
|
|
{
|
|
if(m_metaVersion)
|
|
{
|
|
if(!m_metaVersion->isLoaded())
|
|
{
|
|
m_metaVersion->load(Net::Mode::Online);
|
|
}
|
|
return m_metaVersion->data();
|
|
}
|
|
else
|
|
{
|
|
return m_file;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<class Meta::VersionList> Component::getVersionList() const
|
|
{
|
|
// FIXME: what if the metadata index isn't loaded yet?
|
|
if(APPLICATION->metadataIndex()->hasUid(m_uid))
|
|
{
|
|
return APPLICATION->metadataIndex()->get(m_uid);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int Component::getOrder()
|
|
{
|
|
if(m_orderOverride)
|
|
return m_order;
|
|
|
|
auto vfile = getVersionFile();
|
|
if(vfile)
|
|
{
|
|
return vfile->order;
|
|
}
|
|
return 0;
|
|
}
|
|
void Component::setOrder(int order)
|
|
{
|
|
m_orderOverride = true;
|
|
m_order = order;
|
|
}
|
|
QString Component::getID()
|
|
{
|
|
return m_uid;
|
|
}
|
|
QString Component::getName()
|
|
{
|
|
if (!m_cachedName.isEmpty())
|
|
return m_cachedName;
|
|
return m_uid;
|
|
}
|
|
QString Component::getVersion()
|
|
{
|
|
return m_cachedVersion;
|
|
}
|
|
QString Component::getFilename()
|
|
{
|
|
return m_parent->patchFilePathForUid(m_uid);
|
|
}
|
|
QDateTime Component::getReleaseDateTime()
|
|
{
|
|
if(m_metaVersion)
|
|
{
|
|
return m_metaVersion->time();
|
|
}
|
|
auto vfile = getVersionFile();
|
|
if(vfile)
|
|
{
|
|
return vfile->releaseTime;
|
|
}
|
|
// FIXME: fake
|
|
return QDateTime::currentDateTime();
|
|
}
|
|
|
|
bool Component::isEnabled()
|
|
{
|
|
return !canBeDisabled() || !m_disabled;
|
|
}
|
|
|
|
bool Component::canBeDisabled()
|
|
{
|
|
return isRemovable() && !m_dependencyOnly;
|
|
}
|
|
|
|
bool Component::setEnabled(bool state)
|
|
{
|
|
bool intendedDisabled = !state;
|
|
if (!canBeDisabled())
|
|
{
|
|
intendedDisabled = false;
|
|
}
|
|
if(intendedDisabled != m_disabled)
|
|
{
|
|
m_disabled = intendedDisabled;
|
|
emit dataChanged();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Component::isCustom()
|
|
{
|
|
return m_file != nullptr;
|
|
}
|
|
|
|
bool Component::isCustomizable()
|
|
{
|
|
if(m_metaVersion)
|
|
{
|
|
if(getVersionFile())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool Component::isRemovable()
|
|
{
|
|
return !m_important;
|
|
}
|
|
bool Component::isRevertible()
|
|
{
|
|
if (isCustom())
|
|
{
|
|
if(APPLICATION->metadataIndex()->hasUid(m_uid))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool Component::isMoveable()
|
|
{
|
|
// HACK, FIXME: this was too dumb and wouldn't follow dependency constraints anyway. For now hardcoded to 'true'.
|
|
return true;
|
|
}
|
|
bool Component::isVersionChangeable()
|
|
{
|
|
auto list = getVersionList();
|
|
if(list)
|
|
{
|
|
if(!list->isLoaded())
|
|
{
|
|
list->load(Net::Mode::Online);
|
|
}
|
|
return list->count() != 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Component::setImportant(bool state)
|
|
{
|
|
if(m_important != state)
|
|
{
|
|
m_important = state;
|
|
emit dataChanged();
|
|
}
|
|
}
|
|
|
|
ProblemSeverity Component::getProblemSeverity() const
|
|
{
|
|
auto file = getVersionFile();
|
|
if(file)
|
|
{
|
|
return file->getProblemSeverity();
|
|
}
|
|
return ProblemSeverity::Error;
|
|
}
|
|
|
|
const QList<PatchProblem> Component::getProblems() const
|
|
{
|
|
auto file = getVersionFile();
|
|
if(file)
|
|
{
|
|
return file->getProblems();
|
|
}
|
|
return {{ProblemSeverity::Error, QObject::tr("Patch is not loaded yet.")}};
|
|
}
|
|
|
|
void Component::setVersion(const QString& version)
|
|
{
|
|
if(version == m_version)
|
|
{
|
|
return;
|
|
}
|
|
m_version = version;
|
|
if(m_loaded)
|
|
{
|
|
// we are loaded and potentially have state to invalidate
|
|
if(m_file)
|
|
{
|
|
// we have a file... explicit version has been changed and there is nothing else to do.
|
|
}
|
|
else
|
|
{
|
|
// we don't have a file, therefore we are loaded with metadata
|
|
m_cachedVersion = version;
|
|
// see if the meta version is loaded
|
|
auto metaVersion = APPLICATION->metadataIndex()->get(m_uid, version);
|
|
if(metaVersion->isLoaded())
|
|
{
|
|
// if yes, we can continue with that.
|
|
m_metaVersion = metaVersion;
|
|
}
|
|
else
|
|
{
|
|
// if not, we need loading
|
|
m_metaVersion.reset();
|
|
m_loaded = false;
|
|
}
|
|
updateCachedData();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// not loaded... assume it will be sorted out later by the update task
|
|
}
|
|
emit dataChanged();
|
|
}
|
|
|
|
bool Component::customize()
|
|
{
|
|
if(isCustom())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto filename = getFilename();
|
|
if(!FS::ensureFilePathExists(filename))
|
|
{
|
|
return false;
|
|
}
|
|
// FIXME: get rid of this try-catch.
|
|
try
|
|
{
|
|
QSaveFile jsonFile(filename);
|
|
if(!jsonFile.open(QIODevice::WriteOnly))
|
|
{
|
|
return false;
|
|
}
|
|
auto vfile = getVersionFile();
|
|
if(!vfile)
|
|
{
|
|
return false;
|
|
}
|
|
auto document = OneSixVersionFormat::versionFileToJson(vfile);
|
|
jsonFile.write(document.toJson());
|
|
if(!jsonFile.commit())
|
|
{
|
|
return false;
|
|
}
|
|
m_file = vfile;
|
|
m_metaVersion.reset();
|
|
emit dataChanged();
|
|
}
|
|
catch (const Exception &error)
|
|
{
|
|
qWarning() << "Version could not be loaded:" << error.cause();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Component::revert()
|
|
{
|
|
if(!isCustom())
|
|
{
|
|
// already not custom
|
|
return true;
|
|
}
|
|
auto filename = getFilename();
|
|
bool result = true;
|
|
// just kill the file and reload
|
|
if(QFile::exists(filename))
|
|
{
|
|
result = QFile::remove(filename);
|
|
}
|
|
if(result)
|
|
{
|
|
// file gone...
|
|
m_file.reset();
|
|
|
|
// check local cache for metadata...
|
|
auto version = APPLICATION->metadataIndex()->get(m_uid, m_version);
|
|
if(version->isLoaded())
|
|
{
|
|
m_metaVersion = version;
|
|
}
|
|
else
|
|
{
|
|
m_metaVersion.reset();
|
|
m_loaded = false;
|
|
}
|
|
emit dataChanged();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* deep inspecting compare for requirement sets
|
|
* By default, only uids are compared for set operations.
|
|
* This compares all fields of the Require structs in the sets.
|
|
*/
|
|
static bool deepCompare(const std::set<Meta::Require> & a, const std::set<Meta::Require> & b)
|
|
{
|
|
// NOTE: this needs to be rewritten if the type of Meta::RequireSet changes
|
|
if(a.size() != b.size())
|
|
{
|
|
return false;
|
|
}
|
|
for(const auto & reqA :a)
|
|
{
|
|
const auto &iter2 = b.find(reqA);
|
|
if(iter2 == b.cend())
|
|
{
|
|
return false;
|
|
}
|
|
const auto & reqB = *iter2;
|
|
if(!reqA.deepEquals(reqB))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Component::updateCachedData()
|
|
{
|
|
auto file = getVersionFile();
|
|
if(file)
|
|
{
|
|
bool changed = false;
|
|
if(m_cachedName != file->name)
|
|
{
|
|
m_cachedName = file->name;
|
|
changed = true;
|
|
}
|
|
if(m_cachedVersion != file->version)
|
|
{
|
|
m_cachedVersion = file->version;
|
|
changed = true;
|
|
}
|
|
if(m_cachedVolatile != file->m_volatile)
|
|
{
|
|
m_cachedVolatile = file->m_volatile;
|
|
changed = true;
|
|
}
|
|
if(!deepCompare(m_cachedRequires, file->requires))
|
|
{
|
|
m_cachedRequires = file->requires;
|
|
changed = true;
|
|
}
|
|
if(!deepCompare(m_cachedConflicts, file->conflicts))
|
|
{
|
|
m_cachedConflicts = file->conflicts;
|
|
changed = true;
|
|
}
|
|
if(changed)
|
|
{
|
|
emit dataChanged();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// in case we removed all the metadata
|
|
m_cachedRequires.clear();
|
|
m_cachedConflicts.clear();
|
|
emit dataChanged();
|
|
}
|
|
}
|