|
|
|
@ -272,18 +272,6 @@ void PackProfile::save_internal()
|
|
|
|
|
bool PackProfile::load()
|
|
|
|
|
{
|
|
|
|
|
auto filename = componentsFilePath();
|
|
|
|
|
QFile componentsFile(filename);
|
|
|
|
|
|
|
|
|
|
// migrate old config to new one, if needed
|
|
|
|
|
if(!componentsFile.exists())
|
|
|
|
|
{
|
|
|
|
|
if(!migratePreComponentConfig())
|
|
|
|
|
{
|
|
|
|
|
// FIXME: the user should be notified...
|
|
|
|
|
qCritical() << "Failed to convert old pre-component config for instance" << d->m_instance->name();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// load the new component list and swap it with the current one...
|
|
|
|
|
ComponentContainer newComponents;
|
|
|
|
@ -369,239 +357,6 @@ void PackProfile::updateFailed(const QString& error)
|
|
|
|
|
invalidateLaunchProfile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE this is really old stuff, and only needs to be used when loading the old hardcoded component-unaware format (loadPreComponentConfig).
|
|
|
|
|
static void upgradeDeprecatedFiles(QString root, QString instanceName)
|
|
|
|
|
{
|
|
|
|
|
auto versionJsonPath = FS::PathCombine(root, "version.json");
|
|
|
|
|
auto customJsonPath = FS::PathCombine(root, "custom.json");
|
|
|
|
|
auto mcJson = FS::PathCombine(root, "patches" , "net.minecraft.json");
|
|
|
|
|
|
|
|
|
|
QString sourceFile;
|
|
|
|
|
QString renameFile;
|
|
|
|
|
|
|
|
|
|
// convert old crap.
|
|
|
|
|
if(QFile::exists(customJsonPath))
|
|
|
|
|
{
|
|
|
|
|
sourceFile = customJsonPath;
|
|
|
|
|
renameFile = versionJsonPath;
|
|
|
|
|
}
|
|
|
|
|
else if(QFile::exists(versionJsonPath))
|
|
|
|
|
{
|
|
|
|
|
sourceFile = versionJsonPath;
|
|
|
|
|
}
|
|
|
|
|
if(!sourceFile.isEmpty() && !QFile::exists(mcJson))
|
|
|
|
|
{
|
|
|
|
|
if(!FS::ensureFilePathExists(mcJson))
|
|
|
|
|
{
|
|
|
|
|
qWarning() << "Couldn't create patches folder for" << instanceName;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!renameFile.isEmpty() && QFile::exists(renameFile))
|
|
|
|
|
{
|
|
|
|
|
if(!QFile::rename(renameFile, renameFile + ".old"))
|
|
|
|
|
{
|
|
|
|
|
qWarning() << "Couldn't rename" << renameFile << "to" << renameFile + ".old" << "in" << instanceName;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false);
|
|
|
|
|
ProfileUtils::removeLwjglFromPatch(file);
|
|
|
|
|
file->uid = "net.minecraft";
|
|
|
|
|
file->version = file->minecraftVersion;
|
|
|
|
|
file->name = "Minecraft";
|
|
|
|
|
|
|
|
|
|
Meta::Require needsLwjgl;
|
|
|
|
|
needsLwjgl.uid = "org.lwjgl";
|
|
|
|
|
file->requires.insert(needsLwjgl);
|
|
|
|
|
|
|
|
|
|
if(!ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), mcJson))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!QFile::rename(sourceFile, sourceFile + ".old"))
|
|
|
|
|
{
|
|
|
|
|
qWarning() << "Couldn't rename" << sourceFile << "to" << sourceFile + ".old" << "in" << instanceName;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Migrate old layout to the component based one...
|
|
|
|
|
* - Part of the version information is taken from `instance.cfg` (fed to this class from outside).
|
|
|
|
|
* - Part is taken from the old order.json file.
|
|
|
|
|
* - Part is loaded from loose json files in the instance's `patches` directory.
|
|
|
|
|
*/
|
|
|
|
|
bool PackProfile::migratePreComponentConfig()
|
|
|
|
|
{
|
|
|
|
|
// upgrade the very old files from the beginnings of MultiMC 5
|
|
|
|
|
upgradeDeprecatedFiles(d->m_instance->instanceRoot(), d->m_instance->name());
|
|
|
|
|
|
|
|
|
|
QList<ComponentPtr> components;
|
|
|
|
|
QSet<QString> loaded;
|
|
|
|
|
|
|
|
|
|
auto addBuiltinPatch = [&](const QString &uid, bool asDependency, const QString & emptyVersion, const Meta::Require & req, const Meta::Require & conflict)
|
|
|
|
|
{
|
|
|
|
|
auto jsonFilePath = FS::PathCombine(d->m_instance->instanceRoot(), "patches" , uid + ".json");
|
|
|
|
|
auto intendedVersion = d->getOldConfigVersion(uid);
|
|
|
|
|
// load up the base minecraft patch
|
|
|
|
|
ComponentPtr component;
|
|
|
|
|
if(QFile::exists(jsonFilePath))
|
|
|
|
|
{
|
|
|
|
|
if(intendedVersion.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
intendedVersion = emptyVersion;
|
|
|
|
|
}
|
|
|
|
|
auto file = ProfileUtils::parseJsonFile(QFileInfo(jsonFilePath), false);
|
|
|
|
|
// fix uid
|
|
|
|
|
file->uid = uid;
|
|
|
|
|
// if version is missing, add it from the outside.
|
|
|
|
|
if(file->version.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
file->version = intendedVersion;
|
|
|
|
|
}
|
|
|
|
|
// if this is a dependency (LWJGL), mark it also as volatile
|
|
|
|
|
if(asDependency)
|
|
|
|
|
{
|
|
|
|
|
file->m_volatile = true;
|
|
|
|
|
}
|
|
|
|
|
// insert requirements if needed
|
|
|
|
|
if(!req.uid.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
file->requires.insert(req);
|
|
|
|
|
}
|
|
|
|
|
// insert conflicts if needed
|
|
|
|
|
if(!conflict.uid.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
file->conflicts.insert(conflict);
|
|
|
|
|
}
|
|
|
|
|
// FIXME: @QUALITY do not ignore return value
|
|
|
|
|
ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), jsonFilePath);
|
|
|
|
|
component = new Component(this, uid, file);
|
|
|
|
|
component->m_version = intendedVersion;
|
|
|
|
|
}
|
|
|
|
|
else if(!intendedVersion.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
auto metaVersion = APPLICATION->metadataIndex()->get(uid, intendedVersion);
|
|
|
|
|
component = new Component(this, metaVersion);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
component->m_dependencyOnly = asDependency;
|
|
|
|
|
component->m_important = !asDependency;
|
|
|
|
|
components.append(component);
|
|
|
|
|
};
|
|
|
|
|
// TODO: insert depends and conflicts here if these are customized files...
|
|
|
|
|
Meta::Require reqLwjgl;
|
|
|
|
|
reqLwjgl.uid = "org.lwjgl";
|
|
|
|
|
reqLwjgl.suggests = "2.9.1";
|
|
|
|
|
Meta::Require conflictLwjgl3;
|
|
|
|
|
conflictLwjgl3.uid = "org.lwjgl3";
|
|
|
|
|
Meta::Require nullReq;
|
|
|
|
|
addBuiltinPatch("org.lwjgl", true, "2.9.1", nullReq, conflictLwjgl3);
|
|
|
|
|
addBuiltinPatch("net.minecraft", false, QString(), reqLwjgl, nullReq);
|
|
|
|
|
|
|
|
|
|
// first, collect all other file-based patches and load them
|
|
|
|
|
QMap<QString, ComponentPtr> loadedComponents;
|
|
|
|
|
QDir patchesDir(FS::PathCombine(d->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);
|
|
|
|
|
|
|
|
|
|
// correct missing or wrong uid based on the file name
|
|
|
|
|
QString uid = info.completeBaseName();
|
|
|
|
|
|
|
|
|
|
// ignore builtins, they've been handled already
|
|
|
|
|
if (uid == "net.minecraft")
|
|
|
|
|
continue;
|
|
|
|
|
if (uid == "org.lwjgl")
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// handle horrible corner cases
|
|
|
|
|
if(uid.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
// if you have a file named '.json', make it just go away.
|
|
|
|
|
// FIXME: @QUALITY do not ignore return value
|
|
|
|
|
QFile::remove(info.absoluteFilePath());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
file->uid = uid;
|
|
|
|
|
// FIXME: @QUALITY do not ignore return value
|
|
|
|
|
ProfileUtils::saveJsonFile(OneSixVersionFormat::versionFileToJson(file), info.absoluteFilePath());
|
|
|
|
|
|
|
|
|
|
auto component = new Component(this, file->uid, file);
|
|
|
|
|
auto version = d->getOldConfigVersion(file->uid);
|
|
|
|
|
if(!version.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
component->m_version = version;
|
|
|
|
|
}
|
|
|
|
|
loadedComponents[file->uid] = component;
|
|
|
|
|
}
|
|
|
|
|
// try to load the other 'hardcoded' patches (forge, liteloader), if they weren't loaded from files
|
|
|
|
|
auto loadSpecial = [&](const QString & uid, int order)
|
|
|
|
|
{
|
|
|
|
|
auto patchVersion = d->getOldConfigVersion(uid);
|
|
|
|
|
if(!patchVersion.isEmpty() && !loadedComponents.contains(uid))
|
|
|
|
|
{
|
|
|
|
|
auto patch = new Component(this, APPLICATION->metadataIndex()->get(uid, patchVersion));
|
|
|
|
|
patch->setOrder(order);
|
|
|
|
|
loadedComponents[uid] = patch;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
loadSpecial("net.minecraftforge", 5);
|
|
|
|
|
loadSpecial("com.mumfrey.liteloader", 10);
|
|
|
|
|
|
|
|
|
|
// load the old order.json file, if present
|
|
|
|
|
ProfileUtils::PatchOrder userOrder;
|
|
|
|
|
ProfileUtils::readOverrideOrders(FS::PathCombine(d->m_instance->instanceRoot(), "order.json"), userOrder);
|
|
|
|
|
|
|
|
|
|
// now add all the patches by user sort order
|
|
|
|
|
for (auto uid : userOrder)
|
|
|
|
|
{
|
|
|
|
|
// ignore builtins
|
|
|
|
|
if (uid == "net.minecraft")
|
|
|
|
|
continue;
|
|
|
|
|
if (uid == "org.lwjgl")
|
|
|
|
|
continue;
|
|
|
|
|
// ordering has a patch that is gone?
|
|
|
|
|
if(!loadedComponents.contains(uid))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
components.append(loadedComponents.take(uid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// is there anything left to sort? - this is used when there are leftover components that aren't part of the order.json
|
|
|
|
|
if(!loadedComponents.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
// inserting into multimap by order number as key sorts the patches and detects duplicates
|
|
|
|
|
QMultiMap<int, ComponentPtr> files;
|
|
|
|
|
auto iter = loadedComponents.begin();
|
|
|
|
|
while(iter != loadedComponents.end())
|
|
|
|
|
{
|
|
|
|
|
files.insert((*iter)->getOrder(), *iter);
|
|
|
|
|
iter++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// then just extract the patches and put them in the list
|
|
|
|
|
for (auto order : files.keys())
|
|
|
|
|
{
|
|
|
|
|
const auto &values = files.values(order);
|
|
|
|
|
for(auto &value: values)
|
|
|
|
|
{
|
|
|
|
|
// TODO: put back the insertion of problem messages here, so the user knows about the id duplication
|
|
|
|
|
components.append(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// new we have a complete list of components...
|
|
|
|
|
return savePackProfile(componentsFilePath(), components);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// END: save/load
|
|
|
|
|
|
|
|
|
|
void PackProfile::appendComponent(ComponentPtr component)
|
|
|
|
@ -1169,15 +924,6 @@ std::shared_ptr<LaunchProfile> PackProfile::getProfile() const
|
|
|
|
|
return d->m_profile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PackProfile::setOldConfigVersion(const QString& uid, const QString& version)
|
|
|
|
|
{
|
|
|
|
|
if(version.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
d->m_oldConfigVersions[uid] = version;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PackProfile::setComponentVersion(const QString& uid, const QString& version, bool important)
|
|
|
|
|
{
|
|
|
|
|
auto iter = d->componentIndex.find(uid);
|
|
|
|
|