diff --git a/mainwindow.cpp b/mainwindow.cpp index 81b31b6..bf0ae4d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -8,6 +8,10 @@ #include #include // Для диалога выбора #include // Для проверки файла +#include // Для работы с JSON +#include // Для работы с JSON +#include // Для работы с JSON +#include // Для работы с JSON MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) @@ -35,59 +39,208 @@ MainWindow::~MainWindow() delete ui; // Освобождаем память от объекта UI } +// Вспомогательная функция для проверки правил OS +bool checkRules(const QJsonObject &item) { + if (!item.contains("rules")) { + return true; // Нет правил - разрешено для всех. + } + + // Если правила есть, по умолчанию запрещаем, пока не найдем разрешающее правило. + bool isAllowed = false; + + QString currentOs; +#if defined(Q_OS_WIN) + currentOs = "windows"; +#elif defined(Q_OS_MAC) + currentOs = "osx"; +#elif defined(Q_OS_LINUX) + currentOs = "linux"; +#endif + + QJsonArray rules = item["rules"].toArray(); + + for (const QJsonValue &value : rules) { + QJsonObject rule = value.toObject(); + QString action = rule["action"].toString(); + + bool conditionMet = false; + if (rule.contains("os")) { + QJsonObject osRule = rule["os"].toObject(); + if (osRule.contains("name") && osRule["name"].toString() == currentOs) { + conditionMet = true; + } + } else { + // Правило без указания ОС применяется ко всем системам. + conditionMet = true; + } + + if (conditionMet) { + if (action == "allow") { + isAllowed = true; + } else if (action == "disallow") { + isAllowed = false; + } + } + } + + return isAllowed; +} + void MainWindow::on_launchButton_clicked() { + // --- 1. Выбор версии (остается без изменений) --- QString minecraftPath = getMinecraftPath(); if (minecraftPath.isEmpty()) { QMessageBox::warning(this, "Ошибка", "Не удалось найти директорию .minecraft."); return; } - - // 1. Находим папку с версиями - QString versionsPath = minecraftPath + "/versions"; + QString versionsPath = QDir::toNativeSeparators(minecraftPath + "/versions"); QDir versionsDir(versionsPath); if (!versionsDir.exists()) { QMessageBox::warning(this, "Ошибка", "Папка 'versions' не найдена!"); return; } - - // 2. Получаем список всех папок внутри 'versions' (это и есть наши версии) QStringList versionList = versionsDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); if (versionList.isEmpty()) { QMessageBox::warning(this, "Ошибка", "Не найдено ни одной установленной версии Minecraft."); return; } - - // 3. Предлагаем пользователю выбрать версию bool ok; QString selectedVersion = QInputDialog::getItem(this, "Выбор версии", "Выберите версию Minecraft для запуска:", versionList, 0, false, &ok); + if (!ok || selectedVersion.isEmpty()) { + return; // Пользователь отменил выбор + } - // 4. Если пользователь сделал выбор (нажал "OK" и версия не пустая) - if (ok && !selectedVersion.isEmpty()) { - // Формируем путь к .jar файлу. Например: /.minecraft/versions/1.19.2/1.19.2.jar - QString jarPath = versionsPath + "/" + selectedVersion + "/" + selectedVersion + ".jar"; + // --- 2. Чтение и парсинг JSON файла версии --- + QString jsonPath = QDir::toNativeSeparators(versionsPath + "/" + selectedVersion + "/" + selectedVersion + ".json"); + QFile jsonFile(jsonPath); + if (!jsonFile.open(QIODevice::ReadOnly)) { + QMessageBox::critical(this, "Ошибка", "Не удалось открыть .json файл для версии " + selectedVersion); + return; + } + QByteArray jsonData = jsonFile.readAll(); + jsonFile.close(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); + if (jsonDoc.isNull()) { + QMessageBox::critical(this, "Ошибка", "Некорректный .json файл для версии " + selectedVersion); + return; + } + QJsonObject rootObj = jsonDoc.object(); - // Проверяем, что .jar файл действительно существует - QFileInfo jarFile(jarPath); - if (!jarFile.exists() || !jarFile.isFile()) { - QMessageBox::critical(this, "Ошибка запуска", - "Не удалось найти запускаемый .jar файл для версии " + selectedVersion + ".\n" - "Проверьте целостность файлов игры.\n" - "Ожидаемый путь: " + jarPath); - return; + // --- 3. Формирование Classpath --- + QStringList classpathEntries; + QString librariesPath = QDir::toNativeSeparators(minecraftPath + "/libraries"); + QJsonArray libraries = rootObj["libraries"].toArray(); + + for (const QJsonValue &value : libraries) { + QJsonObject library = value.toObject(); + + if (!checkRules(library)) { + continue; // Пропускаем библиотеку, если она не для этой ОС } - // 5. Запускаем игру - QString javaPath = "java"; // Java должна быть в системной переменной PATH - QStringList arguments; - arguments << "-jar" << jarPath << "net.fabricmc.loader.impl.launch.knot.KnotClient"; + QJsonObject downloads = library["downloads"].toObject(); + if(downloads.isEmpty()){ // Для Fabric Loader и некоторых других + QString name = library["name"].toString(); + QStringList parts = name.split(':'); + QString path = parts[0].replace('.', '/') + "/" + parts[1] + "/" + parts[2] + "/" + parts[1] + "-" + parts[2] + ".jar"; + classpathEntries << QDir::toNativeSeparators(librariesPath + "/" + path); - ui->logOutput->appendPlainText("Запуск Minecraft версии: " + selectedVersion); - process->start(javaPath, arguments); - ui->progressBar->setVisible(true); + } else { + QJsonObject artifact = downloads["artifact"].toObject(); + QString path = artifact["path"].toString(); + classpathEntries << QDir::toNativeSeparators(librariesPath + "/" + path); + } } + // Добавляем сам .jar файл игры в classpath + classpathEntries << QDir::toNativeSeparators(versionsPath + "/" + selectedVersion + "/" + selectedVersion + ".jar"); + + // Определяем разделитель для classpath в зависимости от ОС + QString pathSeparator; +#if defined(Q_OS_WIN) + pathSeparator = ";"; +#else + pathSeparator = ":"; +#endif + QString classpath = classpathEntries.join(pathSeparator); + + // --- 4. Сборка аргументов --- + QStringList jvmArgs; + QStringList gameArgs; + + // Аргументы для JVM + QJsonObject argumentsObj = rootObj["arguments"].toObject(); + QJsonArray jvmArgsArray = argumentsObj["jvm"].toArray(); + for (const QJsonValue &value : jvmArgsArray) { + if (value.isString()) { + jvmArgs << value.toString(); + } else if (value.isObject()) { + if (checkRules(value.toObject())) { + QJsonArray values = value.toObject()["values"].toArray(); + for(const QJsonValue &v : values){ + jvmArgs << v.toString(); + } + } + } + } + + // Аргументы для игры + QJsonArray gameArgsArray = argumentsObj["game"].toArray(); + for (const QJsonValue &value : gameArgsArray) { + if (value.isString()) { + gameArgs << value.toString(); + } else if (value.isObject()) { + if (checkRules(value.toObject())) { + QJsonArray values = value.toObject()["values"].toArray(); + for(const QJsonValue &v : values){ + gameArgs << v.toString(); + } + } + } + } + + // --- 5. Подстановка значений в плейсхолдеры --- + QString nativesPath = QDir::toNativeSeparators(versionsPath + "/" + selectedVersion + "/natives"); + QDir(nativesPath).mkpath("."); // Создаем папку для нативных библиотек, если ее нет + + // Заменители для JVM аргументов + for (QString &arg : jvmArgs) { + arg.replace("${natives_directory}", nativesPath); + arg.replace("${launcher_name}", "CustomLauncher"); + arg.replace("${launcher_version}", "1.0"); + arg.replace("${classpath}", classpath); + } + // Заменители для игровых аргументов (используем базовые значения) + for (QString &arg : gameArgs) { + arg.replace("${auth_player_name}", "Player"); + arg.replace("${version_name}", selectedVersion); + arg.replace("${game_directory}", minecraftPath); + arg.replace("${assets_root}", QDir::toNativeSeparators(minecraftPath + "/assets")); + arg.replace("${assets_index_name}", rootObj["assetIndex"].toObject()["id"].toString()); + arg.replace("${auth_uuid}", "00000000-0000-0000-0000-000000000000"); + arg.replace("${auth_access_token}", "0"); + arg.replace("${clientid}", "N/A"); + arg.replace("${auth_xuid}", "N/A"); + arg.replace("${user_type}", "msa"); + arg.replace("${version_type}", rootObj["type"].toString()); + } + + // --- 6. Финальная сборка и запуск --- + QString mainClass = rootObj["mainClass"].toString(); + QString javaPath = "java"; + + QStringList finalArguments; + finalArguments << jvmArgs << mainClass << gameArgs; + + ui->logOutput->appendPlainText("Запуск Minecraft версии: " + selectedVersion); + ui->logOutput->appendPlainText("Главный класс: " + mainClass); + // Для отладки можно вывести всю команду + ui->logOutput->appendPlainText("Команда: " + javaPath + " " + finalArguments.join(" ")); + + process->start(javaPath, finalArguments); + ui->progressBar->setVisible(true); } void MainWindow::on_modsFolderButton_clicked()