This commit is contained in:
2026-09-29 09:40:37 +03:00
parent 441021b9a6
commit 6567e731d1
34 changed files with 57 additions and 709 deletions
+3 -27
View File
@@ -16,7 +16,6 @@
namespace {
// Больше десяти уровней inheritsFrom не бывает даже у Forge поверх Fabric-подобных сборок.
constexpr int kMaxInheritanceDepth = 10;
QString versionJsonPath(const QString &gameDir, const QString &id)
@@ -34,14 +33,11 @@ QString versionsRoot(const QString &gameDir)
return gameDir + QStringLiteral("/versions");
}
// Идентификатор версии — это имя папки, поэтому «чинить» его как sanitizeId()
// нельзя: подстановка символов увела бы нас не в ту версию. Только проверяем.
bool isSafeVersionId(const QString &id)
{
return !id.isEmpty() && !id.startsWith(u'.') && !id.contains(u'/') && !id.contains(u'\\');
}
// Верхний объект <id>.json без разворачивания цепочки: нужен только inheritsFrom.
QJsonObject readVersionObject(const QString &gameDir, const QString &id)
{
QFile file(versionJsonPath(gameDir, id));
@@ -50,7 +46,6 @@ QJsonObject readVersionObject(const QString &gameDir, const QString &id)
return QJsonDocument::fromJson(file.readAll()).object();
}
// org.lwjgl:lwjgl:3.3.1:natives-windows -> org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-windows.jar
QString mavenToPath(const QString &name)
{
QString coords = name;
@@ -74,8 +69,6 @@ QString mavenToPath(const QString &name)
+ u'/' + file + u'.' + extension;
}
// Ключ для дедупликации: у наследника и родителя одна и та же библиотека часто
// разных версий, и побеждать должна версия наследника.
QString libraryKey(const QString &name)
{
const QStringList parts = name.split(u':');
@@ -184,14 +177,10 @@ void parseLibraries(const QJsonObject &root,
for (const QJsonValue &value : extract.value(QStringLiteral("exclude")).toArray())
exclude << value.toString();
// Основной артефакт. В новом формате (1.19+) натив-библиотеки — это
// отдельные записи с классификатором natives-*, у них тоже есть artifact.
if (downloads.contains(QStringLiteral("artifact")) || !downloads.contains(QStringLiteral("classifiers"))) {
MinecraftLibrary library = libraryFromDownload(
name, downloads.value(QStringLiteral("artifact")).toObject(), relativePath);
// У Fabric у части библиотек нет блока downloads вообще — только
// name и адрес Maven-репозитория, путь собираем из координат.
if (library.url.isEmpty()) {
const QString repository = object.value(QStringLiteral("url")).toString();
if (!repository.isEmpty())
@@ -206,7 +195,6 @@ void parseLibraries(const QJsonObject &root,
}
}
// Старый формат (до 1.18): отдельная карта natives + downloads.classifiers.
const QJsonObject natives = object.value(QStringLiteral("natives")).toObject();
if (natives.isEmpty())
continue;
@@ -232,8 +220,6 @@ void parseLibraries(const QJsonObject &root,
}
}
// Для 1.19+ в списке лежат natives-windows, natives-windows-x86 и natives-windows-arm64
// одновременно — правила фильтруют только по ОС. Оставляем по одной на артефакт.
void pickNativesForCurrentArch(QList<MinecraftLibrary> &libraries)
{
const QString exact = VersionLoader::nativeClassifier();
@@ -242,7 +228,7 @@ void pickNativesForCurrentArch(QList<MinecraftLibrary> &libraries)
: VersionLoader::osName();
const QString base = QStringLiteral("natives-") + osPart;
QHash<QString, QString> chosen; // group:artifact -> классификатор
QHash<QString, QString> chosen;
for (const MinecraftLibrary &library : libraries) {
if (!library.native)
continue;
@@ -268,7 +254,7 @@ void pickNativesForCurrentArch(QList<MinecraftLibrary> &libraries)
});
}
} // namespace
}
QString VersionLoader::osName()
{
@@ -286,7 +272,7 @@ QString VersionLoader::osArch()
const QString arch = QSysInfo::currentCpuArchitecture();
if (arch == QLatin1String("i386") || arch == QLatin1String("i686"))
return QStringLiteral("x86");
return arch; // x86_64 / arm64 — совпадают с тем, что пишет Mojang
return arch;
}
QString VersionLoader::nativeClassifier()
@@ -339,8 +325,6 @@ QStringList VersionLoader::dependentsOf(const QString &gameDir, const QString &v
if (versionId.isEmpty())
return {};
// Одного уровня хватает: профили модлоадеров наследуются от ванильных версий,
// а не друг от друга.
QStringList result;
const QStringList installed = installedVersions(gameDir);
for (const QString &id : installed) {
@@ -378,8 +362,6 @@ bool VersionLoader::remove(const QString &gameDir, const QString &versionId, QSt
return false;
}
// Удаляем только внутри <gameDir>/versions: проверка id уже не пропустит «..»,
// но цена ошибки — чужая папка, а проверка стоит одной строки.
const QString dir = QDir::cleanPath(root + u'/' + versionId);
if (!dir.startsWith(root + u'/')) {
if (error)
@@ -403,7 +385,6 @@ MinecraftVersion VersionLoader::load(const QString &gameDir,
{
MinecraftVersion version;
// Цепочка от самой производной версии к базовой: Fabric -> 1.20.1.
QList<QJsonObject> chain;
QStringList chainIds;
QString currentId = versionId;
@@ -445,7 +426,6 @@ MinecraftVersion VersionLoader::load(const QString &gameDir,
version.id = versionId;
// Скаляры: идём от базовой версии к производной, наследник перекрывает родителя.
for (int i = chain.size() - 1; i >= 0; --i) {
const QJsonObject &object = chain[i];
@@ -480,7 +460,6 @@ MinecraftVersion VersionLoader::load(const QString &gameDir,
version.loggingConfigPath = gameDir + QStringLiteral("/assets/log_configs/") + fileId;
}
// minecraftArguments (до 1.13) заменяет список целиком, arguments.game — дополняет.
if (object.contains(QStringLiteral("minecraftArguments"))) {
version.gameArgs = object.value(QStringLiteral("minecraftArguments"))
.toString()
@@ -506,7 +485,6 @@ MinecraftVersion VersionLoader::load(const QString &gameDir,
if (version.type.isEmpty())
version.type = QStringLiteral("release");
// Библиотеки: наследник первым, дубли по group:artifact:classifier отбрасываем.
QList<MinecraftLibrary> collected;
for (const QJsonObject &object : chain)
parseLibraries(object, collected, features);
@@ -521,7 +499,6 @@ MinecraftVersion VersionLoader::load(const QString &gameDir,
}
pickNativesForCurrentArch(version.libraries);
// До 1.13 jvm-аргументов в json нет — собираем их сами.
const bool hasClasspathArg = std::any_of(
version.jvmArgs.cbegin(), version.jvmArgs.cend(), [](const QString &argument) {
return argument == QLatin1String("-cp") || argument == QLatin1String("-classpath");
@@ -533,7 +510,6 @@ MinecraftVersion VersionLoader::load(const QString &gameDir,
<< QStringLiteral("-cp") << QStringLiteral("${classpath}");
}
// Клиентский jar: у Fabric-версии он свой, у Forge обычно только у родителя.
for (const QString &id : std::as_const(chainIds)) {
const QString jar = versionJarPath(gameDir, id);
if (QFile::exists(jar)) {