сдкеалй акт чтобы скачивались фалы nativees фот пример кода на патон коорый вроде работат сделай что он рабоатна golang то есть перепиши и добавь на golang

package main

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

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) {
	resp, err := http.Get(versionManifestURL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var manifest VersionManifest
	err = json.Unmarshal(bodyBytes, &manifest)
	if err != nil {
		return nil, err
	}
	return &manifest, nil
}

// Получение данных выбранной версии
func getVersionDetails(versionURL string) (map[string]interface{}, error) {
	resp, err := http.Get(versionURL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var versionData map[string]interface{}
	err = json.Unmarshal(bodyBytes, &versionData)
	if err != nil {
		return nil, err
	}
	return versionData, nil
}

// Функция для скачивания файла по URL с повторными попытками
func downloadFile(url, filepath string) error {
	client := http.Client{
		Timeout: 500 * time.Millisecond, // Устанавливаем тайм-аут
	}

	resp, err := client.Get(url)
	if err != nil {
		return 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 err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	return err
}

// Скачивание файла с повторами
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)
}

// Проверка хэша файла
func verifyFileChecksum(filePath, expectedHash string) bool {
	file, err := os.Open(filePath)
	if err != nil {
		return false
	}
	defer file.Close()

	hash := sha1.New()
	if _, err := io.Copy(hash, file); err != nil {
		return false
	}
	calculatedHash := hex.EncodeToString(hash.Sum(nil))
	return expectedHash == calculatedHash
}

// Скачивание клиентского JAR-файла
func downloadClientJar(versionData map[string]interface{}, versionID string) error {
	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)
		}
	}

	fmt.Println("Скачивание клиентского JAR-файла...")
	err := downloadFileWithRetry(url, filePath, 3)
	if err != nil {
		return err
	}

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

	return nil
}

// Функция для проверки правил библиотеки
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 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))
		dir := filepath.Dir(filePath)
		os.MkdirAll(dir, os.ModePerm)

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

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

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

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

			if !verifyFileChecksum(filePath, expectedHash) {
				fmt.Printf("\nХэш библиотеки не совпадает после загрузки: %s\n", filePath)
				os.Remove(filePath)
			}
		}(url, filePath, 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")
	}

	// Скачиваем индекс ассетов
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

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

	var assetIndexData map[string]interface{}
	err = json.Unmarshal(bodyBytes, &assetIndexData)
	if err != nil {
		return err
	}

	// Сохраняем индекс ассетов
	assetsDir := filepath.Join(minelauncherPath, "assets", "indexes")
	os.MkdirAll(assetsDir, os.ModePerm)
	indexFilePath := filepath.Join(assetsDir, assetIndex["id"].(string)+".json")
	err = ioutil.WriteFile(indexFilePath, bodyBytes, os.ModePerm)
	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)
			subHash := hashStr[:2]

			assetURL := fmt.Sprintf("https://resources.download.minecraft.net/%s/%s", subHash, hashStr)
			filePath := filepath.Join(minelauncherPath, "assets", "objects", subHash, hashStr)
			dir := filepath.Dir(filePath)
			os.MkdirAll(dir, os.ModePerm)

			// Проверяем, существует ли файл и его размер
			if fi, err := os.Stat(filePath); err == nil && fi.Size() == size {
				// Файл существует и его размер соответствует, пропускаем
			} else {
				err := downloadFileWithRetry(assetURL, filePath, 3)
				if err != nil {
					fmt.Println("\nОшибка при скачивании ассета:", err)
					return
				}
			}

			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
}

// Построение 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))
		classpath = append(classpath, libPath)
	}

	// Добавляем клиентский JAR
	clientJar := filepath.Join(minelauncherPath, "versions", versionID, versionID+".jar")
	classpath = append(classpath, clientJar)

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

// Запуск игры
func launchMinecraft(versionData map[string]interface{}, authToken, username, uuid, versionID string) error {
	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")
	os.MkdirAll(nativesDir, os.ModePerm)

	// Распаковка нативных библиотек
	err := extractNatives(versionData, nativesDir)
	if err != nil {
		return err
	}

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

	// Аргументы игры
	gameArgs := buildGameArguments(versionData, authToken, username, uuid, 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()
	return err
}

// Построение аргументов игры
func buildGameArguments(versionData map[string]interface{}, authToken, username, uuid, versionID string) []string {
	var args []string

	// Проверяем, есть ли поле "arguments" (для новых версий)
	if arguments, ok := versionData["arguments"].(map[string]interface{}); ok {
		gameArgsList, ok := arguments["game"].([]interface{})
		if !ok {
			fmt.Println("Невозможно получить game arguments")
			return args
		}
		for _, arg := range gameArgsList {
			switch arg := arg.(type) {
			case string:
				arg = replaceVariables(arg, authToken, username, uuid, 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, authToken, username, uuid, 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, authToken, username, uuid, versionID, versionData)
									args = append(args, sv)
								}
							}
						}
					}
				}
			}
		}
	} else {
		// Для старых версий используем поле "minecraftArguments"
		if minecraftArguments, ok := versionData["minecraftArguments"].(string); ok {
			splitArgs := strings.Split(minecraftArguments, " ")
			for _, arg := range splitArgs {
				arg = replaceVariables(arg, authToken, username, uuid, versionID, versionData)
				args = append(args, arg)
			}
		}
	}

	return args
}

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

// Функция для замены переменных в аргументах
func replaceVariables(arg, authToken, username, uuid, versionID string, versionData map[string]interface{}) string {
	// Получаем индекс ассетов из versionData
	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, "${auth_uuid}", uuid)
	arg = strings.ReplaceAll(arg, "${auth_access_token}", authToken)
	arg = strings.ReplaceAll(arg, "${user_type}", "msa")
	arg = strings.ReplaceAll(arg, "${version_type}", "release")
	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 extractNatives(versionData map[string]interface{}, nativesDir string) error {
	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
		}

		// Определяем текущую ОС
		osName := runtime.GOOS
		var classifierName string
		switch osName {
		case "windows":
			classifierName = "natives-windows"
		case "darwin":
			classifierName = "natives-macos"
		case "linux":
			classifierName = "natives-linux"
		default:
			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
		}
		filePath := filepath.Join(minelauncherPath, "libraries", filepath.FromSlash(path))
		dir := filepath.Dir(filePath)
		os.MkdirAll(dir, os.ModePerm)

		fmt.Printf("Скачивание нативной библиотеки: %s\n", filePath)

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

		if _, err := os.Stat(filePath); os.IsNotExist(err) {
			err := downloadFileWithRetry(url, filePath, 3)
			if err != nil {
				fmt.Println("Ошибка при скачивании нативной библиотеки:", err)
				return err
			}
		}

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

		// Распаковываем нативы
		err := unzip(filePath, nativesDir)
		if err != nil {
			fmt.Println("Ошибка при распаковке нативной библиотеки:", err)
			return err
		}
	}
	return nil
}

// Функция для распаковки ZIP-архива
func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return 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() {
			os.MkdirAll(fpath, os.ModePerm)
			continue
		}

		if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
			return err
		}

		outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return err
		}

		rc, err := f.Open()
		if err != nil {
			outFile.Close()
			return err
		}

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

		outFile.Close()
		rc.Close()

		if err != nil {
			return err
		}
	}
	return nil
}

// Проверка наличия всех необходимых файлов перед запуском
func verifyAllFiles(versionData map[string]interface{}, versionID string) error {
	// Проверка клиентского 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)
		}
	}

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

	return nil
}

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

	// Получаем манифест версий
	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)
	os.MkdirAll(versionDir, os.ModePerm)
	versionJsonPath := filepath.Join(versionDir, selectedVersionID+".json")

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

	err = ioutil.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
	}

	// Создаем необходимые директории и файлы
	os.MkdirAll(filepath.Join(minelauncherPath, "saves"), os.ModePerm)
	os.MkdirAll(filepath.Join(minelauncherPath, "resourcepacks"), os.ModePerm)
	os.MkdirAll(filepath.Join(minelauncherPath, "logs"), os.ModePerm)
	os.MkdirAll(filepath.Join(minelauncherPath, "runtime"), os.ModePerm)
	optionsFilePath := filepath.Join(minelauncherPath, "options.txt")
	if _, err := os.Stat(optionsFilePath); os.IsNotExist(err) {
		ioutil.WriteFile(optionsFilePath, []byte(""), 0644)
	}

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

	// Здесь должна быть реализация аутентификации пользователя
	// Для простоты используем заглушки
	authToken := ``
	username := "NAMAAA"
	uuid := ``

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

"natives contains a function for extracting natives libraries to a specific folder"
from ._internal_types.shared_types import ClientJson, ClientJsonLibrary
from ._helper import parse_rule_list, inherit_json, get_library_path
from typing import List, Dict, Literal, Union
from .exceptions import VersionNotFound
import platform
import zipfile
import json
import os

__all__ = ["extract_natives"]


def get_natives(data: ClientJsonLibrary) -> str:
    """
    Returns the native part from the json data
    """
    if platform.architecture()[0] == "32bit":
        arch_type = "32"
    else:
        arch_type = "64"

    if "natives" in data:
        if platform.system() == 'Windows':
            if "windows" in data["natives"]:
                return data["natives"]["windows"].replace("${arch}", arch_type)
            else:
                return ""
        elif platform.system() == 'Darwin':
            if "osx" in data["natives"]:
                return data["natives"]["osx"].replace("${arch}", arch_type)
            else:
                return ""
        else:
            if "linux" in data["natives"]:
                return data["natives"]["linux"].replace("${arch}", arch_type)
            else:
                return ""
    else:
        return ""


def extract_natives_file(filename: str, extract_path: str, extract_data: Dict[Literal["exclude"], List[str]]) -> None:
    """
    Unpack natives
    """
    try:
        os.mkdir(extract_path)
    except Exception:
        pass

    with zipfile.ZipFile(filename, "r") as zf:
        for i in zf.namelist():
            for e in extract_data["exclude"]:
                if i.startswith(e):
                    break
            else:
                zf.extract(i, extract_path)


def extract_natives(versionid: str, path: Union[str, os.PathLike], extract_path: str) -> None:
    """
    Extract all native libraries from a version into the given directory. The directory will be created, if it does not exist.

    :param version: The Minecraft version
    :param minecraft_directory: The path to your Minecraft directory
    :param callback: The same dict as for :func:`~minecraft_launcher_lib.install.install_minecraft_version`
    :raises VersionNotFound: The Minecraft version was not found
    :raises FileOutsideMinecraftDirectory: A File should be placed outside the given Minecraft directory

    The natives are all extracted while installing. So you don't need to use this function in most cases.
    """
    if not os.path.isfile(os.path.join(path, "versions", versionid, versionid + ".json")):
        raise VersionNotFound(versionid)

    with open(os.path.join(path, "versions", versionid, versionid + ".json"), "r", encoding="utf-8") as f:
        data: ClientJson = json.load(f)

    if "inheritsFrom" in data:
        data = inherit_json(data, path)

    for i in data["libraries"]:
        # Check, if the rules allow this lib for the current system
        if "rules" in i and not parse_rule_list(i["rules"], {}):
            continue

        current_path = get_library_path(i["name"], path)
        native = get_natives(i)

        if native == "":
            continue

        lib_path, extension = os.path.splitext(current_path)
        extract_natives_file(f"{lib_path}-{native}{extension}", extract_path, i.get("extract", {"exclude": []}))