package main

import (
	"archive/zip"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

// URL манифеста версий Minecraft
const versionManifestURL = "https://launchermeta.mojang.com/mc/game/version_manifest.json"

// Переменная для хранения пользовательского пути
var minelauncherPath string

// Структура для хранения манифеста версий
type VersionManifest struct {
	Latest struct {
		Release  string `json:"release"`
		Snapshot string `json:"snapshot"`
	} `json:"latest"`
	Versions []struct {
		ID          string `json:"id"`
		Type        string `json:"type"`
		URL         string `json:"url"`
		Time        string `json:"time"`
		ReleaseTime string `json:"releaseTime"`
	} `json:"versions"`
}

// Получение манифеста всех версий
func getVersionManifest() (*VersionManifest, error) {
	fmt.Println("Получение манифеста версий...")
	resp, err := http.Get(versionManifestURL)
	if err != nil {
		return nil, fmt.Errorf("не удалось получить манифест версий: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("не удалось получить манифест версий, статус код: %d", resp.StatusCode)
	}

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("не удалось прочитать тело ответа манифеста версий: %v", err)
	}

	var manifest VersionManifest
	err = json.Unmarshal(bodyBytes, &manifest)
	if err != nil {
		return nil, fmt.Errorf("ошибка при разборе манифеста версий: %v", err)
	}
	return &manifest, nil
}

// Получение данных выбранной версии
func getVersionDetails(versionURL string) (map[string]interface{}, error) {
	fmt.Printf("Получение деталей версии из URL: %s\n", versionURL)
	resp, err := http.Get(versionURL)
	if err != nil {
		return nil, fmt.Errorf("не удалось получить данные версии: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("не удалось получить данные версии, статус код: %d", resp.StatusCode)
	}

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("не удалось прочитать тело ответа версии: %v", err)
	}

	var versionData map[string]interface{}
	err = json.Unmarshal(bodyBytes, &versionData)
	if err != nil {
		return nil, fmt.Errorf("ошибка при разборе данных версии: %v", err)
	}
	return versionData, nil
}

// Функция для скачивания файла по URL с повторными попытками
func downloadFile(url, filepath string) error {
	fmt.Printf("Скачивание файла: %s\n", url)
	client := http.Client{
		Timeout: 1 * time.Second,
	}

	resp, err := client.Get(url)
	if err != nil {
		return fmt.Errorf("ошибка при выполнении GET-запроса: %v", err)
	}
	defer resp.Body.Close()

	// Проверяем код ответа
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("не удалось скачать файл: %s, статус код: %d", url, resp.StatusCode)
	}

	out, err := os.Create(filepath)
	if err != nil {
		return fmt.Errorf("не удалось создать файл %s: %v", filepath, err)
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return fmt.Errorf("ошибка при записи файла %s: %v", filepath, err)
	}

	fmt.Printf("Файл скачан и сохранён: %s\n", filepath)
	return nil
}

// Скачивание файла с повторами
func downloadFileWithRetry(url, filepath string, retries int) error {
	for i := 0; i <= retries; i++ {
		err := downloadFile(url, filepath)
		if err == nil {
			return nil
		}
		fmt.Printf("Попытка %d скачать файл %s не удалась, ошибка: %v\n", i+1, url, err)
		time.Sleep(2 * time.Second) // Ждем перед повторной попыткой
	}
	return fmt.Errorf("не удалось скачать файл после %d попыток: %s", retries+1, url)
}

// Проверка SHA1 хэша файла
func verifyFileChecksum(filePath, expectedHash string) bool {
	file, err := os.Open(filePath)
	if err != nil {
		fmt.Printf("Не удалось открыть файл для проверки хэша: %s, ошибка: %v\n", filePath, err)
		return false
	}
	defer file.Close()

	hash := sha1.New()
	if _, err := io.Copy(hash, file); err != nil {
		fmt.Printf("Не удалось вычислить хэш для файла: %s, ошибка: %v\n", filePath, err)
		return false
	}
	calculatedHash := hex.EncodeToString(hash.Sum(nil))
	if expectedHash != calculatedHash {
		fmt.Printf("Хэш не совпадает для файла: %s (ожидалось: %s, получено: %s)\n", filePath, expectedHash, calculatedHash)
		return false
	}
	return true
}

// Функция для проверки, следует ли включать библиотеку согласно правилам
func shouldIncludeLibrary(libInfo map[string]interface{}) bool {
	if rules, ok := libInfo["rules"].([]interface{}); ok {
		allowed := false
		for _, ruleInterface := range rules {
			rule, ok := ruleInterface.(map[string]interface{})
			if !ok {
				continue
			}
			action, ok := rule["action"].(string)
			if !ok {
				continue
			}
			osMatch := true
			if osDict, ok := rule["os"].(map[string]interface{}); ok {
				osName := runtime.GOOS
				if osName == "darwin" {
					osName = "osx"
				}
				if name, ok := osDict["name"].(string); ok && name != osName {
					osMatch = false
				}
			}
			if osMatch {
				if action == "allow" {
					allowed = true
				} else if action == "disallow" {
					allowed = false
				}
			}
		}
		return allowed
	}
	return true
}

// Функция для получения имени нативного классификатора в зависимости от ОС и архитектуры
func getNatives(libInfo map[string]interface{}) string {
	archType := "64"
	if runtime.GOARCH == "386" || runtime.GOARCH == "arm" {
		archType = "32"
	}

	natives, ok := libInfo["natives"].(map[string]interface{})
	if !ok {
		return ""
	}

	var classifierName string
	switch runtime.GOOS {
	case "windows":
		if val, exists := natives["windows"].(string); exists {
			classifierName = strings.ReplaceAll(val, "${arch}", archType)
		}
	case "darwin":
		if val, exists := natives["osx"].(string); exists {
			classifierName = strings.ReplaceAll(val, "${arch}", archType)
		}
	case "linux":
		if val, exists := natives["linux"].(string); exists {
			classifierName = strings.ReplaceAll(val, "${arch}", archType)
		}
	default:
		return ""
	}

	return classifierName
}

// Функция для извлечения файлов из ZIP-архива
func unzip(src, dest string) error {
	fmt.Printf("Распаковка архива: %s в %s\n", src, dest)
	r, err := zip.OpenReader(src)
	if err != nil {
		return fmt.Errorf("не удалось открыть ZIP-архив %s: %v", src, err)
	}
	defer r.Close()

	for _, f := range r.File {
		fpath := filepath.Join(dest, f.Name)

		// Проверка на безопасность пути
		if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
			return fmt.Errorf("недопустимый путь в архиве: %s", fpath)
		}

		if f.FileInfo().IsDir() {
			err := os.MkdirAll(fpath, os.ModePerm)
			if err != nil {
				return fmt.Errorf("не удалось создать директорию %s: %v", fpath, err)
			}
			continue
		}

		if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
			return fmt.Errorf("не удалось создать директорию %s: %v", filepath.Dir(fpath), err)
		}

		outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return fmt.Errorf("не удалось открыть файл для записи %s: %v", fpath, err)
		}

		rc, err := f.Open()
		if err != nil {
			outFile.Close()
			return fmt.Errorf("не удалось открыть файл внутри архива %s: %v", f.Name, err)
		}

		_, err = io.Copy(outFile, rc)
		outFile.Close()
		rc.Close()

		if err != nil {
			return fmt.Errorf("ошибка при копировании файла %s: %v", fpath, err)
		}
	}
	return nil
}

// Функция для проверки наличия основного класса в client.jar
func verifyMainClass(clientJarPath string) bool {
	r, err := zip.OpenReader(clientJarPath)
	if err != nil {
		fmt.Printf("Не удалось открыть client.jar для проверки main class: %v\n", err)
		return false
	}
	defer r.Close()

	target := "net/minecraft/client/main/Main.class"
	for _, f := range r.File {
		if f.Name == target {
			return true
		}
	}
	fmt.Println("Main class не найден в client.jar")
	return false
}

// Скачивание клиентского JAR-файла
func downloadClientJar(versionData map[string]interface{}, versionID string) error {
	fmt.Println("Скачивание клиентского JAR-файла...")
	downloads, ok := versionData["downloads"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("не удалось найти downloads в данных версии")
	}
	client, ok := downloads["client"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("не удалось найти client в downloads")
	}
	url, ok := client["url"].(string)
	if !ok {
		return fmt.Errorf("не удалось получить URL клиента")
	}
	expectedHash, ok := client["sha1"].(string)
	if !ok {
		return fmt.Errorf("не удалось получить sha1 клиента")
	}

	// Путь для сохранения JAR-файла
	dirPath := filepath.Join(minelauncherPath, "versions", versionID)
	os.MkdirAll(dirPath, os.ModePerm)
	filePath := filepath.Join(dirPath, versionID+".jar")

	// Проверяем, существует ли файл и совпадает ли хэш
	if _, err := os.Stat(filePath); err == nil {
		if verifyFileChecksum(filePath, expectedHash) {
			fmt.Println("Клиентский JAR-файл уже существует и корректен, пропускаем скачивание.")
			return nil
		} else {
			fmt.Println("Хэш клиентского JAR-файла не совпадает, перезагружаем файл.")
			os.Remove(filePath)
		}
	}

	// Формируем абсолютный путь к файлу
	absFilePath, err := filepath.Abs(filePath)
	if err != nil {
		return fmt.Errorf("не удалось получить абсолютный путь к файлу %s: %v", filePath, err)
	}

	err = downloadFileWithRetry(url, absFilePath, 4)
	if err != nil {
		return fmt.Errorf("ошибка при скачивании клиентского JAR-файла: %v", err)
	}

	// Проверяем хэш после загрузки
	if !verifyFileChecksum(absFilePath, expectedHash) {
		return fmt.Errorf("хэш клиентского JAR-файла не совпадает после загрузки")
	}

	// Проверяем наличие основного класса
	if !verifyMainClass(absFilePath) {
		return fmt.Errorf("файл client.jar не содержит необходимого основного класса")
	}

	return nil
}

// Скачивание библиотек
func downloadLibraries(versionData map[string]interface{}) error {
	fmt.Println("Скачивание библиотек...")
	libraries, ok := versionData["libraries"].([]interface{})
	if !ok {
		return fmt.Errorf("не удалось получить библиотеки из данных версии")
	}
	var wg sync.WaitGroup
	semaphore := make(chan struct{}, 10) // Ограничиваем количество одновременных загрузок

	for _, lib := range libraries {
		libInfo, ok := lib.(map[string]interface{})
		if !ok {
			continue
		}

		// Проверяем, следует ли включать эту библиотеку
		if !shouldIncludeLibrary(libInfo) {
			continue
		}

		downloads, ok := libInfo["downloads"].(map[string]interface{})
		if !ok {
			continue
		}

		artifact, ok := downloads["artifact"].(map[string]interface{})
		if !ok {
			continue
		}

		url, ok := artifact["url"].(string)
		if !ok {
			continue
		}
		path, ok := artifact["path"].(string)
		if !ok {
			continue
		}
		expectedHash, ok := artifact["sha1"].(string)
		if !ok {
			continue
		}

		// Путь для сохранения файла
		filePath := filepath.Join(minelauncherPath, "libraries", filepath.FromSlash(path))
		absFilePath, err := filepath.Abs(filePath)
		if err != nil {
			fmt.Printf("Не удалось получить абсолютный путь для файла %s: %v\n", filePath, err)
			continue
		}
		dir := filepath.Dir(absFilePath)
		os.MkdirAll(dir, os.ModePerm)

		// Проверяем, существует ли файл и совпадает ли хэш
		if _, err := os.Stat(absFilePath); err == nil {
			if verifyFileChecksum(absFilePath, expectedHash) {
				// Файл существует и корректен, пропускаем скачивание
				continue
			} else {
				fmt.Printf("Хэш библиотеки не совпадает, перезагружаем файл: %s\n", absFilePath)
				os.Remove(absFilePath)
			}
		}

		wg.Add(1)
		semaphore <- struct{}{} // блокирует, если семафор заполнен

		go func(url, absFilePath, expectedHash string) {
			defer wg.Done()
			defer func() { <-semaphore }()

			err := downloadFileWithRetry(url, absFilePath, 4)
			if err != nil {
				fmt.Printf("Ошибка при скачивании библиотеки %s: %v\n", url, err)
				return
			}

			if !verifyFileChecksum(absFilePath, expectedHash) {
				fmt.Printf("Хэш библиотеки не совпадает после загрузки: %s\n", absFilePath)
				os.Remove(absFilePath)
			}
		}(url, absFilePath, expectedHash)
	}
	wg.Wait()
	return nil
}

// Скачивание ассетов с параллельной загрузкой
func downloadAssets(versionData map[string]interface{}) error {
	fmt.Println("Скачивание ассетов...")
	assetIndex, ok := versionData["assetIndex"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("не удалось найти assetIndex в данных версии")
	}
	url, ok := assetIndex["url"].(string)
	if !ok {
		return fmt.Errorf("не удалось получить URL assetIndex")
	}
	expectedHash, ok := assetIndex["sha1"].(string)
	if !ok {
		return fmt.Errorf("не удалось получить sha1 assetIndex")
	}

	// Скачиваем индекс ассетов
	fmt.Println("Скачивание индекса ассетов...")
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("не удалось скачать индекс ассетов: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("не удалось скачать индекс ассетов, статус код: %d", resp.StatusCode)
	}

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("не удалось прочитать индекс ассетов: %v", err)
	}

	// Проверяем хэш индекса ассетов
	hash := sha1.New()
	hash.Write(bodyBytes)
	calculatedHash := hex.EncodeToString(hash.Sum(nil))
	if expectedHash != calculatedHash {
		return fmt.Errorf("хэш индекса ассетов не совпадает (ожидалось: %s, получено: %s)", expectedHash, calculatedHash)
	}

	var assetIndexData map[string]interface{}
	err = json.Unmarshal(bodyBytes, &assetIndexData)
	if err != nil {
		return fmt.Errorf("ошибка при разборе индекса ассетов: %v", err)
	}

	// Сохраняем индекс ассетов
	fmt.Println("Сохранение индекса ассетов...")
	assetsDir := filepath.Join(minelauncherPath, "assets", "indexes")
	os.MkdirAll(assetsDir, os.ModePerm)
	indexFilePath := filepath.Join(assetsDir, fmt.Sprintf("%s.json", assetIndex["id"].(string)))
	err = os.WriteFile(indexFilePath, bodyBytes, 0644)
	if err != nil {
		return fmt.Errorf("ошибка при сохранении индекса ассетов: %v", err)
	}

	objects, ok := assetIndexData["objects"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("не удалось получить объекты ассетов")
	}

	totalAssets := len(objects)
	fmt.Printf("Всего ассетов для загрузки: %d\n", totalAssets)

	var wg sync.WaitGroup
	semaphore := make(chan struct{}, 20) // Увеличиваем количество одновременных загрузок для ассетов

	currentAsset := 0
	mutex := &sync.Mutex{}

	for _, obj := range objects {
		wg.Add(1)
		semaphore <- struct{}{} // блокирует, если семафор заполнен

		go func(obj interface{}) {
			defer wg.Done()
			defer func() { <-semaphore }() // освобождаем место в семафоре

			objInfo, ok := obj.(map[string]interface{})
			if !ok {
				return
			}
			hashStr, ok := objInfo["hash"].(string)
			if !ok {
				return
			}
			sizeFloat, ok := objInfo["size"].(float64)
			if !ok {
				return
			}
			size := int64(sizeFloat)
			if len(hashStr) < 2 {
				fmt.Printf("Некорректный хэш ассета: %s\n", hashStr)
				return
			}
			subHash := hashStr[:2]

			assetURL := fmt.Sprintf("https://resources.download.minecraft.net/%s/%s", subHash, hashStr)
			filePath := filepath.Join(minelauncherPath, "assets", "objects", subHash, hashStr)
			absFilePath, err := filepath.Abs(filePath)
			if err != nil {
				fmt.Printf("Не удалось получить абсолютный путь для ассета %s: %v\n", filePath, err)
				return
			}
			dir := filepath.Dir(absFilePath)
			os.MkdirAll(dir, os.ModePerm)

			// Проверяем, существует ли файл и его размер
			if fi, err := os.Stat(absFilePath); err == nil && fi.Size() == size {
				// Файл существует и его размер соответствует, пропускаем
			} else {
				err := downloadFileWithRetry(assetURL, absFilePath, 4)
				if err != nil {
					fmt.Printf("\nОшибка при скачивании ассета %s: %v\n", assetURL, err)
					return
				}
				// Дополнительно можно проверить хэш ассета здесь
				// Можно реализовать verifyFileChecksum для ассетов, если необходимо
			}

			mutex.Lock()
			currentAsset++
			progress := float64(currentAsset) / float64(totalAssets) * 100
			fmt.Printf("\rСкачивание ассетов: %d/%d (%.2f%%)", currentAsset, totalAssets, progress)
			mutex.Unlock()
		}(obj)
	}

	wg.Wait()
	fmt.Println("\nЗагрузка ассетов завершена.")
	return nil
}

// Функция для извлечения нативных библиотек
func extractNatives(versionData map[string]interface{}, nativesDir string) error {
	fmt.Println("Извлечение нативных библиотек...")
	libraries, ok := versionData["libraries"].([]interface{})
	if !ok {
		return fmt.Errorf("не удалось получить библиотеки для распаковки нативов")
	}

	for _, lib := range libraries {
		libInfo, ok := lib.(map[string]interface{})
		if !ok {
			continue
		}

		// Проверяем, следует ли включать эту библиотеку
		if !shouldIncludeLibrary(libInfo) {
			continue
		}

		downloads, ok := libInfo["downloads"].(map[string]interface{})
		if !ok {
			continue
		}

		classifiers, ok := downloads["classifiers"].(map[string]interface{})
		if !ok {
			continue
		}

		classifierName := getNatives(libInfo)
		if classifierName == "" {
			continue
		}

		native, ok := classifiers[classifierName].(map[string]interface{})
		if !ok {
			continue
		}

		url, ok := native["url"].(string)
		if !ok {
			continue
		}
		path, ok := native["path"].(string)
		if !ok {
			continue
		}
		expectedHash, ok := native["sha1"].(string)
		if !ok {
			continue
		}

		// Путь для сохранения нативного JAR-файла
		filePath := filepath.Join(minelauncherPath, "libraries", filepath.FromSlash(path))
		absFilePath, err := filepath.Abs(filePath)
		if err != nil {
			fmt.Printf("Не удалось получить абсолютный путь для файла %s: %v\n", filePath, err)
			continue
		}
		dir := filepath.Dir(absFilePath)
		os.MkdirAll(dir, os.ModePerm)

		// Проверяем, существует ли файл и совпадает ли хэш
		if _, err := os.Stat(absFilePath); err == nil {
			if verifyFileChecksum(absFilePath, expectedHash) {
				// Файл существует и корректен, пропускаем скачивание
				fmt.Printf("Нативная библиотека уже существует и корректна: %s\n", absFilePath)
			} else {
				fmt.Printf("Хэш нативной библиотеки не совпадает, перезагружаем файл: %s\n", absFilePath)
				os.Remove(absFilePath)
			}
		}

		if _, err := os.Stat(absFilePath); os.IsNotExist(err) {
			fmt.Printf("Скачивание нативной библиотеки: %s\n", absFilePath)
			err := downloadFileWithRetry(url, absFilePath, 4)
			if err != nil {
				fmt.Printf("Ошибка при скачивании нативной библиотеки %s: %v\n", url, err)
				return err
			}
		}

		// Проверяем хэш после загрузки
		if !verifyFileChecksum(absFilePath, expectedHash) {
			return fmt.Errorf("хэш нативной библиотеки не совпадает после загрузки: %s", absFilePath)
		}

		// Распаковываем нативы
		fmt.Printf("Распаковка нативных библиотек из %s в %s\n", absFilePath, nativesDir)
		err = unzip(absFilePath, nativesDir)
		if err != nil {
			return fmt.Errorf("ошибка при распаковке нативной библиотеки %s: %v", absFilePath, err)
		}
	}

	return nil
}

// Построение classpath для запуска игры
func buildClasspath(versionData map[string]interface{}, versionID string) string {
	var classpath []string

	// Добавляем библиотеки
	libraries, ok := versionData["libraries"].([]interface{})
	if !ok {
		fmt.Println("Не удалось получить библиотеки для classpath")
		return ""
	}
	for _, lib := range libraries {
		libInfo, ok := lib.(map[string]interface{})
		if !ok {
			continue
		}
		if !shouldIncludeLibrary(libInfo) {
			continue
		}
		downloads, ok := libInfo["downloads"].(map[string]interface{})
		if !ok {
			continue
		}
		artifact, ok := downloads["artifact"].(map[string]interface{})
		if !ok {
			continue
		}
		path, ok := artifact["path"].(string)
		if !ok {
			continue
		}
		libPath := filepath.Join(minelauncherPath, "libraries", filepath.FromSlash(path))
		absLibPath, err := filepath.Abs(libPath)
		if err != nil {
			fmt.Printf("Не удалось получить абсолютный путь для библиотеки %s: %v\n", libPath, err)
			continue
		}
		classpath = append(classpath, absLibPath)
	}

	// Добавляем клиентский JAR
	clientJar := filepath.Join(minelauncherPath, "versions", versionID, versionID+".jar")
	absClientJar, err := filepath.Abs(clientJar)
	if err != nil {
		fmt.Printf("Не удалось получить абсолютный путь для client.jar %s: %v\n", clientJar, err)
		return ""
	}
	classpath = append(classpath, absClientJar)

	return strings.Join(classpath, string(os.PathListSeparator))
}

// Функция для замены переменных в аргументах
func replaceVariables(arg, username, versionID string, versionData map[string]interface{}) string {
	assetIndex := versionDataGetAssetIndex(versionData)

	arg = strings.ReplaceAll(arg, "${auth_player_name}", username)
	arg = strings.ReplaceAll(arg, "${version_name}", versionID)
	arg = strings.ReplaceAll(arg, "${game_directory}", minelauncherPath)
	arg = strings.ReplaceAll(arg, "${assets_root}", filepath.Join(minelauncherPath, "assets"))
	arg = strings.ReplaceAll(arg, "${assets_index_name}", getAssetIndexName(assetIndex))
	// Эмулируем древний / пиратский режим
	arg = strings.ReplaceAll(arg, "${user_type}", "legacy")
	// Убираем демо режим
	arg = strings.ReplaceAll(arg, "--demo", "")
	return arg
}

// Получение имени индекса ассетов
func getAssetIndexName(assetIndex map[string]interface{}) string {
	if id, ok := assetIndex["id"].(string); ok {
		return id
	}
	return "legacy"
}

// Получение assetIndex из versionData
func versionDataGetAssetIndex(versionData map[string]interface{}) map[string]interface{} {
	if assetIndex, ok := versionData["assetIndex"].(map[string]interface{}); ok {
		return assetIndex
	}
	return map[string]interface{}{}
}

// Построение аргументов игры
// Функция для построения аргументов игры без аутентификации
func buildGameArguments(versionData map[string]interface{}, username, versionID string) []string {
	var args []string

	// Проверяем, есть ли в данных версии аргументы новой структуры
	if arguments, ok := versionData["arguments"].(map[string]interface{}); ok {
		if gameArgsList, ok := arguments["game"].([]interface{}); ok {
			for _, arg := range gameArgsList {
				switch arg := arg.(type) {
				case string:
					// Заменяем переменные в строке аргумента
					arg = replaceVariables(arg, username, versionID, versionData)
					args = append(args, arg)
				case map[string]interface{}:
					// Обработка условных аргументов, при необходимости
					if rules, ok := arg["rules"].([]interface{}); ok {
						if checkRule(rules) {
							if value, ok := arg["value"].(string); ok {
								value = replaceVariables(value, username, versionID, versionData)
								args = append(args, value)
							} else if values, ok := arg["value"].([]interface{}); ok {
								for _, v := range values {
									if sv, ok := v.(string); ok {
										sv = replaceVariables(sv, username, versionID, versionData)
										args = append(args, sv)
									}
								}
							}
						}
					}
				}
			}
		}
	} else if minecraftArguments, ok := versionData["minecraftArguments"].(string); ok {
		// Если используется старая структура аргументов
		splitArgs := strings.Split(minecraftArguments, " ")
		for _, arg := range splitArgs {
			arg = replaceVariables(arg, username, versionID, versionData)
			args = append(args, arg)
		}
	}

	return args
}

// Простой пример функции checkRule, которая всегда возвращает true
// Для полноценной проверки можно добавить реальные правила
func checkRule(rules []interface{}) bool {
	return true
}

// Функция для запуска Minecraft
func launchMinecraft(versionData map[string]interface{}, authToken, username, uuid, versionID string) error {
	fmt.Println("Запуск Minecraft...")
	mainClass, ok := versionData["mainClass"].(string)
	if !ok {
		return fmt.Errorf("не удалось найти mainClass в данных версии")
	}
	fmt.Println("Main class:", mainClass)
	classpath := buildClasspath(versionData, versionID)
	fmt.Println("Classpath:", classpath)

	if classpath == "" {
		return fmt.Errorf("classpath пустой")
	}

	// Путь к директории natives
	nativesDir := filepath.Join(minelauncherPath, "versions", versionID, "natives")
	absNativesDir, err := filepath.Abs(nativesDir)
	if err != nil {
		return fmt.Errorf("не удалось получить абсолютный путь для nativesDir %s: %v", nativesDir, err)
	}
	os.MkdirAll(absNativesDir, os.ModePerm)

	// Распаковка нативных библиотек
	err = extractNatives(versionData, absNativesDir)
	if err != nil {
		return fmt.Errorf("ошибка при извлечении нативных библиотек: %v", err)
	}

	// Аргументы JVM
	jvmArgs := []string{
		"-Djava.library.path=" + absNativesDir,
		"-cp", classpath,
	}

	// Аргументы игры
	gameArgs := buildGameArguments(versionData, username, versionID)

	if len(gameArgs) == 0 {
		return fmt.Errorf("аргументы игры пустые")
	}

	// Объединяем аргументы
	args := append(jvmArgs, mainClass)
	args = append(args, gameArgs...)

	// Команда запуска
	cmd := exec.Command("java", args...)
	cmd.Dir = minelauncherPath // Устанавливаем рабочую директорию
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Выводим команду запуска для отладки
	fmt.Println("Команда запуска:", strings.Join(cmd.Args, " "))

	// Запускаем игру
	err = cmd.Run()
	if err != nil {
		return fmt.Errorf("ошибка при запуске Minecraft: %v", err)
	}

	return nil
}

// Проверка наличия всех необходимых файлов перед запуском
func verifyAllFiles(versionData map[string]interface{}, versionID string) error {
	fmt.Println("Проверка наличия всех необходимых файлов...")
	// Проверка клиентского JAR
	clientJar := filepath.Join(minelauncherPath, "versions", versionID, versionID+".jar")
	if _, err := os.Stat(clientJar); os.IsNotExist(err) {
		return fmt.Errorf("клиентский JAR-файл отсутствует: %s", clientJar)
	}

	// Проверка библиотек
	libraries, ok := versionData["libraries"].([]interface{})
	if !ok {
		return fmt.Errorf("не удалось получить библиотеки для проверки")
	}
	for _, lib := range libraries {
		libInfo, ok := lib.(map[string]interface{})
		if !ok {
			continue
		}
		if !shouldIncludeLibrary(libInfo) {
			continue
		}
		downloads, ok := libInfo["downloads"].(map[string]interface{})
		if !ok {
			continue
		}
		artifact, ok := downloads["artifact"].(map[string]interface{})
		if !ok {
			continue
		}
		path, ok := artifact["path"].(string)
		if !ok {
			continue
		}
		libPath := filepath.Join(minelauncherPath, "libraries", filepath.FromSlash(path))
		if _, err := os.Stat(libPath); os.IsNotExist(err) {
			return fmt.Errorf("отсутствует библиотека: %s", libPath)
		}
	}

	// Проверка ассетов (опционально)
	// Здесь можно добавить дополнительные проверки, если необходимо

	fmt.Println("Все необходимые файлы присутствуют.")
	return nil
}

func main() {
	// Запрашиваем путь для хранения файлов Minecraft
	fmt.Print("Введите путь для хранения файлов Minecraft (по умолчанию './minecraft'): ")
	fmt.Scanln(&minelauncherPath)
	if minelauncherPath == "" {
		minelauncherPath = "./minecraft"
	}

	// Получаем абсолютный путь
	absPath, err := filepath.Abs(minelauncherPath)
	if err != nil {
		fmt.Printf("Не удалось получить абсолютный путь для %s: %v\n", minelauncherPath, err)
		return
	}
	minelauncherPath = absPath

	// Создаем директорию, если ее нет
	err = os.MkdirAll(minelauncherPath, os.ModePerm)
	if err != nil {
		fmt.Printf("Не удалось создать директорию %s: %v\n", minelauncherPath, err)
		return
	}

	// Получаем манифест версий
	manifest, err := getVersionManifest()
	if err != nil {
		fmt.Println("Ошибка при получении манифеста:", err)
		return
	}

	// Выводим список доступных версий
	fmt.Println("Доступные версии Minecraft:")
	for _, version := range manifest.Versions {
		fmt.Printf("- %s (%s)\n", version.ID, version.Type)
	}

	// Запрашиваем у пользователя выбор версии
	var selectedVersionID string
	fmt.Print("Введите ID версии для скачивания и запуска: ")
	fmt.Scanln(&selectedVersionID)

	// Находим URL выбранной версии
	var selectedVersionURL string
	for _, version := range manifest.Versions {
		if version.ID == selectedVersionID {
			selectedVersionURL = version.URL
			break
		}
	}

	if selectedVersionURL == "" {
		fmt.Println("Версия не найдена.")
		return
	}

	// Получаем данные выбранной версии
	versionData, err := getVersionDetails(selectedVersionURL)
	if err != nil {
		fmt.Println("Ошибка при получении данных версии:", err)
		return
	}

	// Сохраняем данные версии в файл version.json
	versionDir := filepath.Join(minelauncherPath, "versions", selectedVersionID)
	err = os.MkdirAll(versionDir, os.ModePerm)
	if err != nil {
		fmt.Printf("Не удалось создать директорию %s: %v\n", versionDir, err)
		return
	}
	versionJsonPath := filepath.Join(versionDir, selectedVersionID+".json")

	versionDataBytes, err := json.MarshalIndent(versionData, "", "  ")
	if err != nil {
		fmt.Println("Ошибка при сериализации данных версии:", err)
		return
	}

	err = os.WriteFile(versionJsonPath, versionDataBytes, 0644)
	if err != nil {
		fmt.Println("Ошибка при сохранении файла version.json:", err)
		return
	}

	// Скачиваем необходимые файлы
	err = downloadClientJar(versionData, selectedVersionID)
	if err != nil {
		fmt.Println("Ошибка при скачивании клиентского JAR-файла:", err)
		return
	}

	err = downloadLibraries(versionData)
	if err != nil {
		fmt.Println("Ошибка при скачивании библиотек:", err)
		return
	}

	err = downloadAssets(versionData)
	if err != nil {
		fmt.Println("Ошибка при скачивании ассетов:", err)
		return
	}

	// Проверяем наличие всех необходимых файлов
	err = verifyAllFiles(versionData, selectedVersionID)
	if err != nil {
		fmt.Println("Ошибка валидации файлов:", err)
		return
	}

	// Создаем необходимые директории и файлы
	dirs := []string{
		filepath.Join(minelauncherPath, "saves"),
		filepath.Join(minelauncherPath, "resourcepacks"),
		filepath.Join(minelauncherPath, "logs"),
		filepath.Join(minelauncherPath, "runtime"),
	}
	for _, dir := range dirs {
		err := os.MkdirAll(dir, os.ModePerm)
		if err != nil {
			fmt.Printf("Не удалось создать директорию %s: %v\n", dir, err)
			return
		}
	}

	optionsFilePath := filepath.Join(minelauncherPath, "options.txt")
	if _, err := os.Stat(optionsFilePath); os.IsNotExist(err) {
		err := os.WriteFile(optionsFilePath, []byte(""), 0644)
		if err != nil {
			fmt.Printf("Не удалось создать файл %s: %v\n", optionsFilePath, err)
			return
		}
	}

	// Проверяем наличие Java и её версии
	fmt.Println("Проверка наличия Java...")
	javaVersionCmd := exec.Command("java", "-version")
	javaVersionOutput, err := javaVersionCmd.CombinedOutput()
	if err != nil {
		fmt.Println("Ошибка при проверке версии Java:", err)
		return
	}
	fmt.Println("Java версия:", string(javaVersionOutput))

	// Реализация аутентификации пользователя
	// Для простоты используем заглушки
	// *** ВАЖНО: Замените эти значения на реальные данные ***
	authToken := ``      // Замените на реальный токен аутентификации
	username := "MAKAKA" // Замените на реальное имя пользователя
	uuid := ``           // Замените на реальный UUID

	// Запускаем игру
	err = launchMinecraft(versionData, authToken, username, uuid, selectedVersionID)
	if err != nil {
		fmt.Println("Ошибка при запуске Minecraft:", err)
		return
	}

	fmt.Println("Minecraft запущен успешно!")
}


иногда фалый скаиваются не с первой попытки хотя раньше скачивались с первой исправь и устрони ошибку

Проверка наличия Java...
Java версия: java version "17.0.6" 2023-01-17 LTS
Java(TM) SE Runtime Environment (build 17.0.6+9-LTS-190)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.6+9-LTS-190, mixed mode, sharing)

Запуск Minecraft...
Main class: net.minecraft.client.main.Main
Classpath: D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\github\oshi\oshi-core\6.4.5\oshi-core-6.4.5.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\google\code\gson\gson\2.10.1\gson-2.10.1.jar;D:\JBProj
ects\GOLAND\MineLauncherTest\.legit\libraries\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\google\guava\guava\32.1.2-jre\guava-32.1.2-jre.jar;D:\JBProjects\
GOLAND\MineLauncherTest\.legit\libraries\com\ibm\icu\icu4j\73.2\icu4j-73.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\authlib\5.0.47\authlib-5.0.47.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\librari
es\com\mojang\blocklist\1.0.10\blocklist-1.0.10.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\brigadier\1.1.8\brigadier-1.1.8.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\datafixerup
per\6.0.8\datafixerupper-6.0.8.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\logging\1.1.1\logging-1.1.1.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\patchy\2.2.10\patchy-2.2.10.jar;
D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\text2speech\1.17.9\text2speech-1.17.9.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\commons-codec\commons-codec\1.16.0\commons-codec-1.16.0.jar;D:\JBPro
jects\GOLAND\MineLauncherTest\.legit\libraries\commons-io\commons-io\2.13.0\commons-io-2.13.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\JBProjects\GOLA
ND\MineLauncherTest\.legit\libraries\io\netty\netty-buffer\4.1.97.Final\netty-buffer-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-codec\4.1.97.Final\netty-codec-4.1.97.Final.jar;D:\JBProjec
ts\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-common\4.1.97.Final\netty-common-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-handler\4.1.97.Final\netty-handler-4.1.97.Final.jar;
D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-resolver\4.1.97.Final\netty-resolver-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-transport-classes-epoll\4.1.97.Final\
netty-transport-classes-epoll-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-transport-native-unix-common\4.1.97.Final\netty-transport-native-unix-common-4.1.97.Final.jar;D:\JBProjects\GOLAND
\MineLauncherTest\.legit\libraries\io\netty\netty-transport\4.1.97.Final\netty-transport-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\it\unimi\dsi\fastutil\8.5.12\fastutil-8.5.12.jar;D:\JBProjects\GOLAND\
MineLauncherTest\.legit\libraries\net\java\dev\jna\jna-platform\5.13.0\jna-platform-5.13.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\net\java\dev\jna\jna\5.13.0\jna-5.13.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\
.legit\libraries\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\apache\commons\commons-compress\1.22\commons-compress-1.22.jar;D:\JBProjects\GOLAND\MineLauncher
Test\.legit\libraries\org\apache\commons\commons-lang3\3.13.0\commons-lang3-3.13.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;D:\JBProjects\GOLAND\Mi
neLauncherTest\.legit\libraries\org\apache\httpcomponents\httpcore\4.4.16\httpcore-4.4.16.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;D:\JBProjects\GOLAN
D\MineLauncherTest\.legit\libraries\org\apache\logging\log4j\log4j-core\2.19.0\log4j-core-2.19.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\apache\logging\log4j\log4j-slf4j2-impl\2.19.0\log4j-slf4j2-impl-2.19.0.
jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\joml\joml\1.10.5\joml-1.10.5.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2.jar;D:\JBProjects\GOLAND\MineLaunche
rTest\.legit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAN
D\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2.jar;D:\JBProjects\GO
LAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2-natives-windo
ws-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lwj
gl-openal-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lwjgl-openal-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lwjg
l-openal-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lwjgl-openal-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjg
l\lwjgl-opengl\3.3.2\lwjgl-opengl-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-opengl\3.3.2\lwjgl-opengl-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl
\lwjgl-opengl\3.3.2\lwjgl-opengl-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-opengl\3.3.2\lwjgl-opengl-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.
legit\libraries\org\lwjgl\lwjgl-stb\3.3.2\lwjgl-stb-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-stb\3.3.2\lwjgl-stb-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\librari
es\org\lwjgl\lwjgl-stb\3.3.2\lwjgl-stb-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-stb\3.3.2\lwjgl-stb-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.
legit\libraries\org\lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.l
egit\libraries\org\lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2-natives-windows-x86.jar;D:\JBProjects\G
OLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit
\libraries\org\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\slf4j\slf4j-api\2.0.7\slf4j-api-2.0.7.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\versions\1.20.2\1.20.2.jar
Извлечение нативных библиотек...
Команда запуска: java -Djava.library.path=D:\JBProjects\GOLAND\MineLauncherTest\.legit\versions\1.20.2\natives -cp D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\github\oshi\oshi-core\6.4.5\oshi-core-6.4.5.jar;D:\JBProj
ects\GOLAND\MineLauncherTest\.legit\libraries\com\google\code\gson\gson\2.10.1\gson-2.10.1.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;D:\JBProjects\GOLAND
\MineLauncherTest\.legit\libraries\com\google\guava\guava\32.1.2-jre\guava-32.1.2-jre.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\ibm\icu\icu4j\73.2\icu4j-73.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\lib
raries\com\mojang\authlib\5.0.47\authlib-5.0.47.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\blocklist\1.0.10\blocklist-1.0.10.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\brigadier
\1.1.8\brigadier-1.1.8.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\datafixerupper\6.0.8\datafixerupper-6.0.8.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\logging\1.1.1\logging-1.1.
1.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\patchy\2.2.10\patchy-2.2.10.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\com\mojang\text2speech\1.17.9\text2speech-1.17.9.jar;D:\JBProjects\GOLAN
D\MineLauncherTest\.legit\libraries\commons-codec\commons-codec\1.16.0\commons-codec-1.16.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\commons-io\commons-io\2.13.0\commons-io-2.13.0.jar;D:\JBProjects\GOLAND\MineLaun
cherTest\.legit\libraries\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-buffer\4.1.97.Final\netty-buffer-4.1.97.Final.jar;D:\JBProjects\GOLAND\Mine
LauncherTest\.legit\libraries\io\netty\netty-codec\4.1.97.Final\netty-codec-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-common\4.1.97.Final\netty-common-4.1.97.Final.jar;D:\JBProjects\GOLA
ND\MineLauncherTest\.legit\libraries\io\netty\netty-handler\4.1.97.Final\netty-handler-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-resolver\4.1.97.Final\netty-resolver-4.1.97.Final.jar;D:\
JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-transport-classes-epoll\4.1.97.Final\netty-transport-classes-epoll-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-transport-
native-unix-common\4.1.97.Final\netty-transport-native-unix-common-4.1.97.Final.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\io\netty\netty-transport\4.1.97.Final\netty-transport-4.1.97.Final.jar;D:\JBProjects\GOLAND\
MineLauncherTest\.legit\libraries\it\unimi\dsi\fastutil\8.5.12\fastutil-8.5.12.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\net\java\dev\jna\jna-platform\5.13.0\jna-platform-5.13.0.jar;D:\JBProjects\GOLAND\MineLaunche
rTest\.legit\libraries\net\java\dev\jna\jna\5.13.0\jna-5.13.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\librar
ies\org\apache\commons\commons-compress\1.22\commons-compress-1.22.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\apache\commons\commons-lang3\3.13.0\commons-lang3-3.13.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.l
egit\libraries\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\apache\httpcomponents\httpcore\4.4.16\httpcore-4.4.16.jar;D:\JBProjects\GOLAND\MineLauncher
Test\.legit\libraries\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\apache\logging\log4j\log4j-core\2.19.0\log4j-core-2.19.0.jar;D:\JBProjects\GOLAND\MineL
auncherTest\.legit\libraries\org\apache\logging\log4j\log4j-slf4j2-impl\2.19.0\log4j-slf4j2-impl-2.19.0.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\joml\joml\1.10.5\joml-1.10.5.jar;D:\JBProjects\GOLAND\MineLaunch
erTest\.legit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.l
egit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-glfw\3.3.2\lwjgl-glfw-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\Mi
neLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2-natives-windows.jar;D:\JBProjects\GO
LAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-jemalloc\3.3.2\lwjgl-jemalloc-3.3.2-natives
-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lwjgl-openal-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lwjgl-openal-3.3.2-nati
ves-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lwjgl-openal-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-openal\3.3.2\lw
jgl-openal-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-opengl\3.3.2\lwjgl-opengl-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-opengl\3.3.2
\lwjgl-opengl-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-opengl\3.3.2\lwjgl-opengl-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwj
gl\lwjgl-opengl\3.3.2\lwjgl-opengl-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-stb\3.3.2\lwjgl-stb-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\
lwjgl-stb\3.3.2\lwjgl-stb-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-stb\3.3.2\lwjgl-stb-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\o
rg\lwjgl\lwjgl-stb\3.3.2\lwjgl-stb-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\
lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\
.legit\libraries\org\lwjgl\lwjgl-tinyfd\3.3.2\lwjgl-tinyfd-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libr
aries\org\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2-natives-windows.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2-natives-windows-arm64.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\o
rg\lwjgl\lwjgl\3.3.2\lwjgl-3.3.2-natives-windows-x86.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\libraries\org\slf4j\slf4j-api\2.0.7\slf4j-api-2.0.7.jar;D:\JBProjects\GOLAND\MineLauncherTest\.legit\versions\1.20.2\1.20.2.jar n
et.minecraft.client.main.Main --username MAKAKA --version 1.20.2 --gameDir D:\JBProjects\GOLAND\MineLauncherTest\.legit --assetsDir D:\JBProjects\GOLAND\MineLauncherTest\.legit\assets --assetIndex 8 --uuid ${auth_uuid} --accessToke
n ${auth_access_token} --clientId ${clientid} --xuid ${auth_xuid} --userType legacy --versionType ${version_type}  --width ${resolution_width} --height ${resolution_height} --quickPlayPath ${quickPlayPath} --quickPlaySingleplayer ${quickPlaySingleplayer} --quickPlayMultiplayer ${quickPlayMultiplayer} --quickPlayRealms ${quickPlayRealms}
Exception in thread "main" java.lang.NoClassDefFoundError: it/unimi/dsi/fastutil/objects/Object2ObjectArrayMap
        at com.mojang.serialization.CompressorHolder.<init>(CompressorHolder.java:10)
        at com.mojang.serialization.MapEncoder$Implementation.<init>(MapEncoder.java:95)
        at com.mojang.serialization.Encoder$4.<init>(Encoder.java:64)
        at com.mojang.serialization.Encoder.empty(Encoder.java:64)
        at com.mojang.serialization.Codec.<clinit>(Codec.java:611)
        at geb.b(SourceFile:85)
        at geb.<clinit>(SourceFile:28)
        at net.minecraft.client.main.Main.main(SourceFile:60)
Caused by: java.lang.ClassNotFoundException: it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        ... 8 more
Ошибка при запуске Minecraft: ошибка при запуске Minecraft: exit status 1