259 lines
9.9 KiB
C++
259 lines
9.9 KiB
C++
|
|
#include <iostream>
|
|||
|
|
#include <string>
|
|||
|
|
#include <vector>
|
|||
|
|
#include <cstdlib>
|
|||
|
|
#include <fstream>
|
|||
|
|
#include <sstream>
|
|||
|
|
|
|||
|
|
// Подключение зависимостей
|
|||
|
|
#include <curl/curl.h> // Для HTTP-запросов
|
|||
|
|
#include "json.hpp" // Для парсинга JSON
|
|||
|
|
|
|||
|
|
// Для удобства используем пространство имен для JSON
|
|||
|
|
using json = nlohmann::json;
|
|||
|
|
|
|||
|
|
// Прототипы функций
|
|||
|
|
void showMenu();
|
|||
|
|
std::string getMinecraftPath();
|
|||
|
|
void openModsFolder();
|
|||
|
|
void launchMinecraft();
|
|||
|
|
void downloadMods();
|
|||
|
|
|
|||
|
|
// Функция для сохранения данных, полученных от curl, в строку
|
|||
|
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
|||
|
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
|||
|
|
return size * nmemb;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int main() {
|
|||
|
|
int choice;
|
|||
|
|
do {
|
|||
|
|
showMenu();
|
|||
|
|
std::cout << "Введите ваш выбор: ";
|
|||
|
|
std::cin >> choice;
|
|||
|
|
|
|||
|
|
// Проверка на корректность ввода
|
|||
|
|
if (std::cin.fail()) {
|
|||
|
|
std::cin.clear(); // Сброс флагов ошибок
|
|||
|
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // Очистка буфера ввода
|
|||
|
|
choice = -1; // Присваиваем неверное значение для вывода ошибки
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch (choice) {
|
|||
|
|
case 1:
|
|||
|
|
launchMinecraft();
|
|||
|
|
break;
|
|||
|
|
case 2:
|
|||
|
|
openModsFolder();
|
|||
|
|
break;
|
|||
|
|
case 3:
|
|||
|
|
downloadMods();
|
|||
|
|
break;
|
|||
|
|
case 4:
|
|||
|
|
std::cout << "Выход из программы." << std::endl;
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
std::cout << "Неверный выбор. Пожалуйста, попробуйте снова." << std::endl;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
std::cout << std::endl;
|
|||
|
|
} while (choice != 4);
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Отображает главное меню лаунчера
|
|||
|
|
void showMenu() {
|
|||
|
|
std::cout << "========== Minecraft C++ Launcher ==========" << std::endl;
|
|||
|
|
std::cout << "1. Запустить Minecraft" << std::endl;
|
|||
|
|
std::cout << "2. Открыть папку с модами" << std::endl;
|
|||
|
|
std::cout << "3. Загрузить моды с GitHub" << std::endl;
|
|||
|
|
std::cout << "4. Выход" << std::endl;
|
|||
|
|
std::cout << "==========================================" << std::endl;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Определяет путь к директории .minecraft в зависимости от ОС
|
|||
|
|
std::string getMinecraftPath() {
|
|||
|
|
std::string path;
|
|||
|
|
#ifdef _WIN32
|
|||
|
|
// Для Windows путь находится в %APPDATA%\.minecraft
|
|||
|
|
char* appdata = nullptr;
|
|||
|
|
size_t len;
|
|||
|
|
_dupenv_s(&appdata, &len, "APPDATA");
|
|||
|
|
if (appdata) {
|
|||
|
|
path = std::string(appdata) + "\\.minecraft";
|
|||
|
|
free(appdata);
|
|||
|
|
}
|
|||
|
|
#elif __APPLE__
|
|||
|
|
// Для macOS
|
|||
|
|
path = std::string(getenv("HOME")) + "/Library/Application Support/minecraft";
|
|||
|
|
#else
|
|||
|
|
// Для Linux
|
|||
|
|
path = std::string(getenv("HOME")) + "/.minecraft";
|
|||
|
|
#endif
|
|||
|
|
return path;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Открывает папку mods
|
|||
|
|
void openModsFolder() {
|
|||
|
|
std::string minecraftPath = getMinecraftPath();
|
|||
|
|
if (minecraftPath.empty()) {
|
|||
|
|
std::cout << "Не удалось найти директорию .minecraft." << std::endl;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::string modsPath = minecraftPath + "/mods";
|
|||
|
|
std::cout << "Путь к папке с модами: " << modsPath << std::endl;
|
|||
|
|
std::cout << "Открытие папки..." << std::endl;
|
|||
|
|
|
|||
|
|
std::string command;
|
|||
|
|
#ifdef _WIN32
|
|||
|
|
command = "explorer " + modsPath;
|
|||
|
|
#elif __APPLE__
|
|||
|
|
command = "open " + modsPath;
|
|||
|
|
#else
|
|||
|
|
command = "xdg-open " + modsPath;
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
// system() выполняет команду в системной консоли
|
|||
|
|
int result = system(command.c_str());
|
|||
|
|
if (result != 0) {
|
|||
|
|
std::cerr << "Не удалось открыть папку. Убедитесь, что она существует." << std::endl;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Запускает официальный лаунчер Minecraft
|
|||
|
|
void launchMinecraft() {
|
|||
|
|
std::cout << "Запуск официального лаунчера Minecraft..." << std::endl;
|
|||
|
|
std::string command;
|
|||
|
|
#ifdef _WIN32
|
|||
|
|
// Эта команда предполагает, что лаунчер установлен в стандартное место
|
|||
|
|
command = "start \"\" \"%ProgramFiles(x86)%\\Minecraft Launcher\\MinecraftLauncher.exe\"";
|
|||
|
|
#elif __APPLE__
|
|||
|
|
command = "open /Applications/Minecraft.app";
|
|||
|
|
#else
|
|||
|
|
// Пользователям Linux, возможно, потребуется настроить эту команду
|
|||
|
|
command = "minecraft-launcher";
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
int result = system(command.c_str());
|
|||
|
|
if (result != 0) {
|
|||
|
|
std::cerr << "Не удалось запустить Minecraft. Убедитесь, что официальный лаунчер установлен." << std::endl;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Функция для загрузки модов из GitHub репозитория
|
|||
|
|
void downloadMods() {
|
|||
|
|
std::string repo_owner, repo_name;
|
|||
|
|
std::cout << "Введите владельца репозитория GitHub (например, 'octocat'): ";
|
|||
|
|
std::cin >> repo_owner;
|
|||
|
|
std::cout << "Введите имя репозитория (например, 'Hello-World'): ";
|
|||
|
|
std::cin >> repo_name;
|
|||
|
|
|
|||
|
|
std::string apiUrl = "https://api.github.com/repos/" + repo_owner + "/" + repo_name + "/contents/";
|
|||
|
|
std::string readBuffer;
|
|||
|
|
|
|||
|
|
CURL *curl = curl_easy_init();
|
|||
|
|
if (!curl) {
|
|||
|
|
std::cerr << "Ошибка инициализации cURL." << std::endl;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Настройка cURL для запроса к GitHub API
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_URL, apiUrl.c_str());
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Minecraft-C++-Launcher/1.0"); // GitHub требует User-Agent
|
|||
|
|
|
|||
|
|
CURLcode res = curl_easy_perform(curl);
|
|||
|
|
if (res != CURLE_OK) {
|
|||
|
|
std::cerr << "Ошибка запроса к GitHub API: " << curl_easy_strerror(res) << std::endl;
|
|||
|
|
curl_easy_cleanup(curl);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверка HTTP статуса
|
|||
|
|
long http_code = 0;
|
|||
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
|||
|
|
if (http_code != 200) {
|
|||
|
|
std::cerr << "Репозиторий не найден или API недоступен. HTTP статус: " << http_code << std::endl;
|
|||
|
|
curl_easy_cleanup(curl);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
curl_easy_cleanup(curl);
|
|||
|
|
|
|||
|
|
// Парсинг JSON ответа
|
|||
|
|
try {
|
|||
|
|
json repo_files = json::parse(readBuffer);
|
|||
|
|
std::vector<json> jar_files;
|
|||
|
|
|
|||
|
|
// Фильтруем только .jar файлы
|
|||
|
|
for (const auto& file : repo_files) {
|
|||
|
|
std::string filename = file["name"];
|
|||
|
|
if (filename.size() > 4 && filename.substr(filename.size() - 4) == ".jar") {
|
|||
|
|
jar_files.push_back(file);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (jar_files.empty()) {
|
|||
|
|
std::cout << "В репозитории не найдено .jar файлов." << std::endl;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Отображаем список модов для выбора
|
|||
|
|
std::cout << "Найденные моды (.jar):" << std::endl;
|
|||
|
|
for (int i = 0; i < jar_files.size(); ++i) {
|
|||
|
|
std::cout << i + 1 << ". " << jar_files[i]["name"] << std::endl;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int mod_choice;
|
|||
|
|
std::cout << "Выберите мод для загрузки (введите номер): ";
|
|||
|
|
std::cin >> mod_choice;
|
|||
|
|
|
|||
|
|
if (std::cin.fail() || mod_choice < 1 || mod_choice > jar_files.size()) {
|
|||
|
|
std::cin.clear();
|
|||
|
|
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
|||
|
|
std::cerr << "Неверный выбор." << std::endl;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получаем URL для загрузки файла
|
|||
|
|
std::string download_url = jar_files[mod_choice - 1]["download_url"];
|
|||
|
|
std::string mod_filename = jar_files[mod_choice - 1]["name"];
|
|||
|
|
std::string save_path = getMinecraftPath() + "/mods/" + mod_filename;
|
|||
|
|
|
|||
|
|
std::cout << "Загрузка '" << mod_filename << "' в " << save_path << "..." << std::endl;
|
|||
|
|
|
|||
|
|
// Используем cURL для скачивания файла
|
|||
|
|
curl = curl_easy_init();
|
|||
|
|
if (curl) {
|
|||
|
|
FILE *fp;
|
|||
|
|
errno_t err = fopen_s(&fp, save_path.c_str(), "wb"); // Используем fopen_s для безопасности
|
|||
|
|
if (err != 0 || fp == NULL) {
|
|||
|
|
std::cerr << "Не удалось создать файл для сохранения мода." << std::endl;
|
|||
|
|
curl_easy_cleanup(curl);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_URL, download_url.c_str());
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); // Используем стандартную функцию записи
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Minecraft-C++-Launcher/1.0");
|
|||
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Разрешаем редиректы
|
|||
|
|
|
|||
|
|
res = curl_easy_perform(curl);
|
|||
|
|
fclose(fp); // Закрываем файл
|
|||
|
|
|
|||
|
|
if (res != CURLE_OK) {
|
|||
|
|
std::cerr << "Ошибка при загрузке мода: " << curl_easy_strerror(res) << std::endl;
|
|||
|
|
} else {
|
|||
|
|
std::cout << "Мод успешно загружен!" << std::endl;
|
|||
|
|
}
|
|||
|
|
curl_easy_cleanup(curl);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (json::parse_error& e) {
|
|||
|
|
std::cerr << "Ошибка парсинга JSON: " << e.what() << std::endl;
|
|||
|
|
}
|
|||
|
|
}
|