PrismLauncher/api/logic/minecraft/OneSixVersionFormat.cpp
Jamie Mansfield 211cfb4af7
NOISSUE Disable Install Forge button on 1.17 (and above)
It appears that Minecraft Forge for Minecraft 1.17 will require setting
JVM arguments to operate, this is not currently possible with MultiMC's
meta.

Therefore, when Forge for 1.17 is released a solution will need to be
looked into.
2021-07-22 20:50:58 +01:00

392 lines
12 KiB
C++

#include "OneSixVersionFormat.h"
#include <Json.h>
#include "minecraft/ParseUtils.h"
#include <minecraft/MojangVersionFormat.h>
using namespace Json;
static void readString(const QJsonObject &root, const QString &key, QString &variable)
{
if (root.contains(key))
{
variable = requireString(root.value(key));
}
}
LibraryPtr OneSixVersionFormat::libraryFromJson(ProblemContainer & problems, const QJsonObject &libObj, const QString &filename)
{
LibraryPtr out = MojangVersionFormat::libraryFromJson(problems, libObj, filename);
readString(libObj, "MMC-hint", out->m_hint);
readString(libObj, "MMC-absulute_url", out->m_absoluteURL);
readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL);
readString(libObj, "MMC-filename", out->m_filename);
readString(libObj, "MMC-displayname", out->m_displayname);
return out;
}
QJsonObject OneSixVersionFormat::libraryToJson(Library *library)
{
QJsonObject libRoot = MojangVersionFormat::libraryToJson(library);
if (library->m_absoluteURL.size())
libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL);
if (library->m_hint.size())
libRoot.insert("MMC-hint", library->m_hint);
if (library->m_filename.size())
libRoot.insert("MMC-filename", library->m_filename);
if (library->m_displayname.size())
libRoot.insert("MMC-displayname", library->m_displayname);
return libRoot;
}
VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder)
{
VersionFilePtr out(new VersionFile());
if (doc.isEmpty() || doc.isNull())
{
throw JSONValidationError(filename + " is empty or null");
}
if (!doc.isObject())
{
throw JSONValidationError(filename + " is not an object");
}
QJsonObject root = doc.object();
Meta::MetadataVersion formatVersion = Meta::parseFormatVersion(root, false);
switch(formatVersion)
{
case Meta::MetadataVersion::InitialRelease:
break;
case Meta::MetadataVersion::Invalid:
throw JSONValidationError(filename + " does not contain a recognizable version of the metadata format.");
}
if (requireOrder)
{
if (root.contains("order"))
{
out->order = requireInteger(root.value("order"));
}
else
{
// FIXME: evaluate if we don't want to throw exceptions here instead
qCritical() << filename << "doesn't contain an order field";
}
}
out->name = root.value("name").toString();
if(root.contains("uid"))
{
out->uid = root.value("uid").toString();
}
else
{
out->uid = root.value("fileId").toString();
}
out->version = root.value("version").toString();
MojangVersionFormat::readVersionProperties(root, out.get());
// added for legacy Minecraft window embedding, TODO: remove
readString(root, "appletClass", out->appletClass);
if (root.contains("+tweakers"))
{
for (auto tweakerVal : requireArray(root.value("+tweakers")))
{
out->addTweakers.append(requireString(tweakerVal));
}
}
if (root.contains("+traits"))
{
for (auto tweakerVal : requireArray(root.value("+traits")))
{
out->traits.insert(requireString(tweakerVal));
}
}
if (root.contains("jarMods"))
{
for (auto libVal : requireArray(root.value("jarMods")))
{
QJsonObject libObj = requireObject(libVal);
// parse the jarmod
auto lib = OneSixVersionFormat::jarModFromJson(*out, libObj, filename);
// and add to jar mods
out->jarMods.append(lib);
}
}
else if (root.contains("+jarMods")) // DEPRECATED: old style '+jarMods' are only here for backwards compatibility
{
for (auto libVal : requireArray(root.value("+jarMods")))
{
QJsonObject libObj = requireObject(libVal);
// parse the jarmod
auto lib = OneSixVersionFormat::plusJarModFromJson(*out, libObj, filename, out->name);
// and add to jar mods
out->jarMods.append(lib);
}
}
if (root.contains("mods"))
{
for (auto libVal : requireArray(root.value("mods")))
{
QJsonObject libObj = requireObject(libVal);
// parse the jarmod
auto lib = OneSixVersionFormat::modFromJson(*out, libObj, filename);
// and add to jar mods
out->mods.append(lib);
}
}
auto readLibs = [&](const char * which, QList<LibraryPtr> & outList)
{
for (auto libVal : requireArray(root.value(which)))
{
QJsonObject libObj = requireObject(libVal);
// parse the library
auto lib = libraryFromJson(*out, libObj, filename);
outList.append(lib);
}
};
bool hasPlusLibs = root.contains("+libraries");
bool hasLibs = root.contains("libraries");
if (hasPlusLibs && hasLibs)
{
out->addProblem(ProblemSeverity::Warning,
QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported."));
readLibs("libraries", out->libraries);
readLibs("+libraries", out->libraries);
}
else if (hasLibs)
{
readLibs("libraries", out->libraries);
}
else if(hasPlusLibs)
{
readLibs("+libraries", out->libraries);
}
if(root.contains("mavenFiles")) {
readLibs("mavenFiles", out->mavenFiles);
}
// if we have mainJar, just use it
if(root.contains("mainJar"))
{
QJsonObject libObj = requireObject(root, "mainJar");
out->mainJar = libraryFromJson(*out, libObj, filename);
}
// else reconstruct it from downloads and id ... if that's available
else if(!out->minecraftVersion.isEmpty())
{
auto lib = std::make_shared<Library>();
lib->setRawName(GradleSpecifier(QString("com.mojang:minecraft:%1:client").arg(out->minecraftVersion)));
// we have a reliable client download, use it.
if(out->mojangDownloads.contains("client"))
{
auto LibDLInfo = std::make_shared<MojangLibraryDownloadInfo>();
LibDLInfo->artifact = out->mojangDownloads["client"];
lib->setMojangDownloadInfo(LibDLInfo);
}
// we got nothing...
else
{
out->addProblem(
ProblemSeverity::Error,
QObject::tr("URL for the main jar could not be determined - Mojang removed the server that we used as fallback.")
);
}
out->mainJar = lib;
}
if (root.contains("requires"))
{
Meta::parseRequires(root, &out->requires);
}
QString dependsOnMinecraftVersion = root.value("mcVersion").toString();
if(!dependsOnMinecraftVersion.isEmpty())
{
Meta::Require mcReq;
mcReq.uid = "net.minecraft";
mcReq.equalsVersion = dependsOnMinecraftVersion;
if (out->requires.count(mcReq) == 0)
{
out->requires.insert(mcReq);
}
}
if (root.contains("conflicts"))
{
Meta::parseRequires(root, &out->conflicts);
}
if (root.contains("volatile"))
{
out->m_volatile = requireBoolean(root, "volatile");
}
/* removed features that shouldn't be used */
if (root.contains("tweakers"))
{
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element 'tweakers'"));
}
if (root.contains("-libraries"))
{
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-libraries'"));
}
if (root.contains("-tweakers"))
{
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-tweakers'"));
}
if (root.contains("-minecraftArguments"))
{
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '-minecraftArguments'"));
}
if (root.contains("+minecraftArguments"))
{
out->addProblem(ProblemSeverity::Error, QObject::tr("Version file contains unsupported element '+minecraftArguments'"));
}
return out;
}
QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch)
{
QJsonObject root;
writeString(root, "name", patch->name);
writeString(root, "uid", patch->uid);
writeString(root, "version", patch->version);
Meta::serializeFormatVersion(root, Meta::MetadataVersion::InitialRelease);
MojangVersionFormat::writeVersionProperties(patch.get(), root);
if(patch->mainJar)
{
root.insert("mainJar", libraryToJson(patch->mainJar.get()));
}
writeString(root, "appletClass", patch->appletClass);
writeStringList(root, "+tweakers", patch->addTweakers);
writeStringList(root, "+traits", patch->traits.toList());
if (!patch->libraries.isEmpty())
{
QJsonArray array;
for (auto value: patch->libraries)
{
array.append(OneSixVersionFormat::libraryToJson(value.get()));
}
root.insert("libraries", array);
}
if (!patch->mavenFiles.isEmpty())
{
QJsonArray array;
for (auto value: patch->mavenFiles)
{
array.append(OneSixVersionFormat::libraryToJson(value.get()));
}
root.insert("mavenFiles", array);
}
if (!patch->jarMods.isEmpty())
{
QJsonArray array;
for (auto value: patch->jarMods)
{
array.append(OneSixVersionFormat::jarModtoJson(value.get()));
}
root.insert("jarMods", array);
}
if (!patch->mods.isEmpty())
{
QJsonArray array;
for (auto value: patch->jarMods)
{
array.append(OneSixVersionFormat::modtoJson(value.get()));
}
root.insert("mods", array);
}
if(!patch->requires.empty())
{
Meta::serializeRequires(root, &patch->requires, "requires");
}
if(!patch->conflicts.empty())
{
Meta::serializeRequires(root, &patch->conflicts, "conflicts");
}
if(patch->m_volatile)
{
root.insert("volatile", true);
}
// write the contents to a json document.
{
QJsonDocument out;
out.setObject(root);
return out;
}
}
LibraryPtr OneSixVersionFormat::plusJarModFromJson(
ProblemContainer & problems,
const QJsonObject &libObj,
const QString &filename,
const QString &originalName
) {
LibraryPtr out(new Library());
if (!libObj.contains("name"))
{
throw JSONValidationError(filename +
"contains a jarmod that doesn't have a 'name' field");
}
// just make up something unique on the spot for the library name.
auto uuid = QUuid::createUuid();
QString id = uuid.toString().remove('{').remove('}');
out->setRawName(GradleSpecifier("org.multimc.jarmods:" + id + ":1"));
// filename override is the old name
out->setFilename(libObj.value("name").toString());
// it needs to be local, it is stored in the instance jarmods folder
out->setHint("local");
// read the original name if present - some versions did not set it
// it is the original jar mod filename before it got renamed at the point of addition
auto displayName = libObj.value("originalName").toString();
if(displayName.isEmpty())
{
auto fixed = originalName;
fixed.remove(" (jar mod)");
out->setDisplayName(fixed);
}
else
{
out->setDisplayName(displayName);
}
return out;
}
LibraryPtr OneSixVersionFormat::jarModFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename)
{
return libraryFromJson(problems, libObj, filename);
}
QJsonObject OneSixVersionFormat::jarModtoJson(Library *jarmod)
{
return libraryToJson(jarmod);
}
LibraryPtr OneSixVersionFormat::modFromJson(ProblemContainer & problems, const QJsonObject& libObj, const QString& filename)
{
return libraryFromJson(problems, libObj, filename);
}
QJsonObject OneSixVersionFormat::modtoJson(Library *jarmod)
{
return libraryToJson(jarmod);
}