Advertisement
PaffcioStudio

Untitled

Apr 29th, 2025
171
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 72.31 KB | None | 0 0
  1. import subprocess
  2. import sys
  3. import importlib.metadata
  4. import tkinter as tk
  5. from tkinter import ttk, messagebox, scrolledtext
  6. import requests
  7. import os
  8. import json
  9. import threading
  10. import re
  11. import hashlib
  12. import pyperclip
  13. import zipfile
  14. from datetime import datetime
  15. import platform
  16. import webbrowser
  17. from packaging import version as pkg_version
  18. import humanize
  19. from colorama import init, Fore, Style
  20. from PIL import Image, ImageTk
  21.  
  22. # Inicjalizacja colorama
  23. init(autoreset=True)
  24.  
  25. # Instalacja zależności
  26. def install_requirements():
  27.     required_libraries = ['requests', 'pyperclip', 'packaging', 'humanize', 'colorama', 'pillow']
  28.     for library in required_libraries:
  29.         try:
  30.             importlib.metadata.version(library)
  31.             print(f"{Fore.LIGHTCYAN_EX}{Fore.GREEN}[OK] {library} już zainstalowane.")
  32.         except importlib.metadata.PackageNotFoundError:
  33.             try:
  34.                 subprocess.check_call([sys.executable, "-m", "pip", "install", library])
  35.                 print(f"{Fore.LIGHTCYAN_EX}{Fore.CYAN}[INFO] {library} zainstalowane.")
  36.             except subprocess.CalledProcessError:
  37.                 print(f"{Fore.LIGHTCYAN_EX}{Fore.RED}[ERROR] Nie udało się zainstalować {library}.")
  38.  
  39. install_requirements()
  40.  
  41. # Ścieżki
  42. BASE_DIR = os.path.join(os.getcwd(), "minecraft")
  43. CONFIG_FILE = os.path.join(BASE_DIR, "config.json")
  44. ASSETS_DIR = os.path.join(BASE_DIR, "assets")
  45. LIBRARIES_DIR = os.path.join(BASE_DIR, "libraries")
  46. NATIVES_DIR = os.path.join(BASE_DIR, "natives")
  47. LOGS_DIR = os.path.join(BASE_DIR, "logs")
  48. JAVA_DIR = os.path.join(BASE_DIR, "java")
  49. ICONS_DIR = os.path.join(BASE_DIR, "icons")
  50.  
  51. # Kolory i styl
  52. BG_COLOR = "#1a1a1a"
  53. FG_COLOR = "#ffffff"
  54. ACCENT_COLOR = "#2a9fd6"
  55. CONSOLE_BG = "#0d0d0d"
  56. CONSOLE_FG = "#00ff00"
  57. BUTTON_BG = "#333333"
  58. BUTTON_HOVER = "#555555"
  59. SIDEBAR_BG = "#222222"
  60. ACTIVE_TAB_COLOR = "#1e6f9f"
  61. HOVER_TAB_COLOR = "#3a3a3a"
  62.  
  63. # Globalne zmienne
  64. pending_instance_settings = {}
  65. pending_version = ""
  66. download_thread = None
  67. download_active = False
  68. global_progress_bar = None
  69. global_status_label = None
  70. instances = {}  # Globalna zmienna dla instancji
  71. java_versions_cache = {}  # Globalna zmienna dla wersji Javy
  72. console = None  # Globalna zmienna dla konsoli
  73.  
  74. # Funkcje narzędziowe
  75. def log_to_console(console, message, level="INFO"):
  76.     timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  77.     try:
  78.         console.config(state="normal")
  79.         if level == "ERROR":
  80.             console.tag_config("error", foreground="red")
  81.             console.insert(tk.END, f"[{timestamp}] {level}: {message}\n", "error")
  82.         elif level == "WARNING":
  83.             console.tag_config("warning", foreground="yellow")
  84.             console.insert(tk.END, f"[{timestamp}] {level}: {message}\n", "warning")
  85.         elif level == "SUCCESS":
  86.             console.tag_config("success", foreground="green")
  87.             console.insert(tk.END, f"[{timestamp}] {level}: {message}\n", "success")
  88.         else:
  89.             console.insert(tk.END, f"[{timestamp}] {level}: {message}\n")
  90.         console.see(tk.END)
  91.         console.config(state="disabled")
  92.     except:
  93.         os.makedirs(LOGS_DIR, exist_ok=True)
  94.         with open(os.path.join(LOGS_DIR, "error.log"), "a", encoding="utf-8") as f:
  95.             f.write(f"[{timestamp}] {level}: {message}\n")
  96.  
  97. def verify_sha1(file_path, expected_sha1):
  98.     sha1 = hashlib.sha1()
  99.     with open(file_path, "rb") as f:
  100.         for chunk in iter(lambda: f.read(4096), b""):
  101.             sha1.update(chunk)
  102.     return sha1.hexdigest() == expected_sha1
  103.  
  104. def save_config():
  105.     config = {
  106.         "default_settings": {
  107.             "username": username_var.get(),
  108.             "memory": memory_var.get(),
  109.             "shared_assets": shared_assets_var.get(),
  110.             "shared_libraries": shared_libraries_var.get(),
  111.             "shared_natives": shared_natives_var.get()
  112.         },
  113.         "version_filters": {
  114.             "snapshots": snapshots_var.get(),
  115.             "releases": releases_var.get(),
  116.             "alpha": alpha_var.get(),
  117.             "beta": beta_var.get()
  118.         },
  119.         "instances": instances,
  120.         "java_versions": java_versions_cache
  121.     }
  122.     os.makedirs(BASE_DIR, exist_ok=True)
  123.     with open(CONFIG_FILE, "w", encoding="utf-8") as f:
  124.         json.dump(config, f, indent=4)
  125.  
  126. def load_config():
  127.     global instances, java_versions_cache, username_var, memory_var, shared_assets_var, shared_libraries_var, shared_natives_var, snapshots_var, releases_var, alpha_var, beta_var
  128.    
  129.     if os.path.exists(CONFIG_FILE):
  130.         try:
  131.             with open(CONFIG_FILE, "r", encoding="utf-8") as f:
  132.                 config = json.load(f)
  133.                
  134.                 # Aktualizuj globalne zmienne zamiast tworzyć nowe
  135.                 if "default_settings" in config:
  136.                     default_settings = config["default_settings"]
  137.                     username_var.set(default_settings.get("username", "Player"))
  138.                     memory_var.set(default_settings.get("memory", "2"))
  139.                     shared_assets_var.set(default_settings.get("shared_assets", True))
  140.                     shared_libraries_var.set(default_settings.get("shared_libraries", True))
  141.                     shared_natives_var.set(default_settings.get("shared_natives", True))
  142.                
  143.                 if "version_filters" in config:
  144.                     version_filters = config["version_filters"]
  145.                     snapshots_var.set(version_filters.get("snapshots", True))
  146.                     releases_var.set(version_filters.get("releases", True))
  147.                     alpha_var.set(version_filters.get("alpha", False))
  148.                     beta_var.set(version_filters.get("beta", False))
  149.                
  150.                 if "instances" in config:
  151.                     instances = config["instances"]
  152.                
  153.                 if "java_versions" in config:
  154.                     java_versions_cache = config["java_versions"]
  155.                
  156.                 return instances, java_versions_cache
  157.         except Exception as e:
  158.             log_to_console(console, f"Błąd wczytywania konfiguracji: {e}", "ERROR")
  159.             # Przywróć domyślne wartości w przypadku błędu
  160.             instances = {}
  161.             java_versions_cache = {}
  162.             return instances, java_versions_cache
  163.    
  164.     # Domyślne wartości jeśli plik nie istnieje
  165.     instances = {}
  166.     java_versions_cache = {}
  167.     return instances, java_versions_cache
  168.  
  169. def get_versions():
  170.     try:
  171.         url = "https://launchermeta.mojang.com/mc/game/version_manifest.json"
  172.         resp = requests.get(url, timeout=10)
  173.         resp.raise_for_status()
  174.         manifest = resp.json()
  175.         versions = []
  176.         allowed_types = []
  177.         if snapshots_var.get():
  178.             allowed_types.append("snapshot")
  179.         if releases_var.get():
  180.             allowed_types.append("release")
  181.         if alpha_var.get():
  182.             allowed_types.append("old_alpha")
  183.         if beta_var.get():
  184.             allowed_types.append("old_beta")
  185.         for v in manifest["versions"]:
  186.             if v["type"] in allowed_types:
  187.                 versions.append(v["id"])
  188.         log_to_console(None, f"Pobrano {len(versions)} wersji (filtry: {allowed_types})", "INFO")
  189.         return sorted(versions, key=lambda x: x, reverse=True)
  190.     except Exception as e:
  191.         log_to_console(None, f"Nie udało się pobrać wersji: {e}", "ERROR")
  192.         return []
  193.  
  194. def get_version_info(version):
  195.     try:
  196.         url = "https://launchermeta.mojang.com/mc/game/version_manifest.json"
  197.         resp = requests.get(url, timeout=10)
  198.         resp.raise_for_status()
  199.         manifest = resp.json()
  200.         for v in manifest["versions"]:
  201.             if v["id"] == version:
  202.                 version_url = v["url"]
  203.                 return requests.get(version_url, timeout=10).json()
  204.         return None
  205.     except Exception as e:
  206.         log_to_console(None, f"Nie udało się pobrać info o wersji: {e}", "ERROR")
  207.         return None
  208.  
  209. def download_java(java_version, console):
  210.     try:
  211.         system = platform.system().lower()
  212.         arch = "x64"
  213.         base_url = "https://api.adoptium.net/v3/binary/latest/"
  214.         java_map = {
  215.             "1.8": "8",
  216.             "16": "16",
  217.             "17": "17",
  218.             "21": "21"
  219.         }
  220.         feature_version = java_map.get(java_version, java_version)
  221.         url = f"{base_url}{feature_version}/ga/{system}/{arch}/jdk/hotspot/normal/eclipse"
  222.         log_to_console(console, f"Pobieranie Javy {java_version} z {url}", "INFO")
  223.  
  224.         resp = requests.get(url, stream=True, timeout=10)
  225.         resp.raise_for_status()
  226.         total_size = int(resp.headers.get('content-length', 0))
  227.         java_dir = os.path.join(JAVA_DIR, f"jdk-{feature_version}")
  228.         os.makedirs(java_dir, exist_ok=True)
  229.         zip_path = os.path.join(java_dir, f"jdk-{feature_version}.zip")
  230.  
  231.         downloaded_size = 0
  232.         with open(zip_path, "wb") as f:
  233.             for chunk in resp.iter_content(chunk_size=8192):
  234.                 if chunk:
  235.                     f.write(chunk)
  236.                     downloaded_size += len(chunk)
  237.                     if total_size > 0 and global_progress_bar:
  238.                         update_progress(global_progress_bar, global_status_label,
  239.                                       (downloaded_size/total_size)*100, total_size-downloaded_size, "java", 1, 1)
  240.         log_to_console(console, f"Rozpakowywanie Javy do {java_dir}", "INFO")
  241.         with zipfile.ZipFile(zip_path, 'r') as zip_ref:
  242.             zip_ref.extractall(java_dir)
  243.         os.remove(zip_path)
  244.  
  245.         for root, _, files in os.walk(java_dir):
  246.             if "java.exe" in files:
  247.                 java_path = os.path.join(root, "java.exe")
  248.                 version = get_java_version(java_path)
  249.                 if version:
  250.                     log_to_console(console, f"Pobrano Javę: {java_path} (wersja: {version})", "SUCCESS")
  251.                     return java_path, version
  252.         log_to_console(console, "Nie znaleziono java.exe w pobranym archiwum!", "ERROR")
  253.         return None, None
  254.     except Exception as e:
  255.         log_to_console(console, f"Błąd pobierania Javy {java_version}: {e}", "ERROR")
  256.         return None, None
  257.  
  258. def find_java(required_version=None):
  259.     possible_paths = []
  260.     log_to_console(None, f"Szukam Javy {required_version or 'dowolnej'}...", "INFO")
  261.  
  262.     if os.path.exists(JAVA_DIR):
  263.         for java_folder in os.listdir(JAVA_DIR):
  264.             java_path = os.path.join(JAVA_DIR, java_folder, "bin", "java.exe")
  265.             if os.path.exists(java_path):
  266.                 version = get_java_version(java_path)
  267.                 if version and (not required_version or check_java_version(version, required_version)):
  268.                     possible_paths.append((java_path, version))
  269.                     log_to_console(None, f"Znaleziono Javę w {java_path} (wersja: {version})", "INFO")
  270.  
  271.     java_home = os.environ.get("JAVA_HOME")
  272.     if java_home:
  273.         java_path = os.path.join(java_home, "bin", "java.exe" if platform.system() == "Windows" else "java")
  274.         if os.path.exists(java_path):
  275.             version = get_java_version(java_path)
  276.             if version and (not required_version or check_java_version(version, required_version)):
  277.                 possible_paths.append((java_path, version))
  278.                 log_to_console(None, f"Znaleziono Javę w JAVA_HOME: {java_path} (wersja: {version})", "INFO")
  279.  
  280.     for base in [os.environ.get('ProgramFiles'), os.environ.get('ProgramFiles(x86)'), "C:\\Program Files\\Java", "C:\\Program Files (x86)\\Java"]:
  281.         if base:
  282.             java_dir = os.path.join(base, "Java")
  283.             if os.path.isdir(java_dir):
  284.                 for item in os.listdir(java_dir):
  285.                     java_path = os.path.join(java_dir, item, "bin", "java.exe")
  286.                     if os.path.exists(java_path):
  287.                         version = get_java_version(java_path)
  288.                         if version and (not required_version or check_java_version(version, required_version)):
  289.                             possible_paths.append((java_path, version))
  290.                             log_to_console(None, f"Znaleziono Javę w {java_path} (wersja: {version})", "INFO")
  291.  
  292.     try:
  293.         out = subprocess.check_output(["where", "java"], stderr=subprocess.DEVNULL).decode()
  294.         for line in out.splitlines():
  295.             line = line.strip()
  296.             if os.path.exists(line) and line not in [p[0] for p in possible_paths]:
  297.                 version = get_java_version(line)
  298.                 if version and (not required_version or check_java_version(version, required_version)):
  299.                     possible_paths.append((line, version))
  300.                     log_to_console(None, f"Znaleziono Javę w {line} (wersja: {version})", "INFO")
  301.     except:
  302.         pass
  303.  
  304.     if not possible_paths:
  305.         log_to_console(None, f"Nie znaleziono Javy {required_version}! Spróbuj pobrać automatycznie.", "ERROR")
  306.     return possible_paths
  307.  
  308. def get_java_version(java_path):
  309.     try:
  310.         result = subprocess.run([java_path, "-version"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
  311.         version_line = result.stderr.split('\n')[0]
  312.         version_match = re.search(r'version "([^"]+)"', version_line)
  313.         if version_match:
  314.             version = version_match.group(1)
  315.             is_64bit = "64-Bit" in result.stderr
  316.             log_to_console(None, f"Java {java_path}: {version}, 64-bit: {is_64bit}", "INFO")
  317.             return version if is_64bit else None
  318.     except Exception as e:
  319.         log_to_console(None, f"Błąd sprawdzania wersji Javy {java_path}: {e}", "ERROR")
  320.     return None
  321.  
  322. def check_java_version(installed_version, required_version):
  323.     try:
  324.         installed = installed_version.split("_")[0]
  325.         if required_version == "1.8":
  326.             return installed.startswith("1.8.")
  327.         if required_version == "16":
  328.             return installed.startswith("16.")
  329.         if required_version == "17":
  330.             return installed.startswith("17.")
  331.         if required_version == "21":
  332.             return installed.startswith("21.")
  333.         return pkg_version.parse(installed) >= pkg_version.parse(required_version)
  334.     except:
  335.         return False
  336.  
  337. def is_new_launcher(version):
  338.     try:
  339.         ver_match = re.match(r'1\.(\d+)', version)
  340.         if ver_match:
  341.             ver_float = float(ver_match.group(1))
  342.             return ver_float >= 6
  343.         return True
  344.     except:
  345.         return True
  346.  
  347. def get_required_java(version, version_info):
  348.     if version_info and "javaVersion" in version_info:
  349.         major_version = version_info["javaVersion"].get("majorVersion", 8)
  350.         return str(major_version)
  351.     try:
  352.         if re.match(r'(\d+w\d+[a-z])', version):  # Snapshoty
  353.             return "1.8"  # Większość starych snapshotów używa Javy 8
  354.         ver = pkg_version.parse(version)
  355.         if ver >= pkg_version.parse("1.21"):
  356.             return "21"
  357.         if ver >= pkg_version.parse("1.17"):
  358.             return "17"  # 1.18 też używa Javy 17
  359.         return "1.8"
  360.     except pkg_version.InvalidVersion:
  361.         return "1.8"  # Domyślnie dla niestandardowych wersji
  362.  
  363. def download_file(url, path, progress_callback, expected_sha1=None, console=None):
  364.     try:
  365.         resp = requests.get(url, stream=True, timeout=10)
  366.         resp.raise_for_status()
  367.         total_size = int(resp.headers.get('content-length', 0))
  368.         downloaded_size = 0
  369.         os.makedirs(os.path.dirname(path), exist_ok=True)
  370.         with open(path, "wb") as f:
  371.             for chunk in resp.iter_content(chunk_size=8192):
  372.                 if chunk:
  373.                     f.write(chunk)
  374.                     downloaded_size += len(chunk)
  375.                     if total_size > 0 and progress_callback:
  376.                         progress_callback(downloaded_size, total_size)
  377.         if expected_sha1 and not verify_sha1(path, expected_sha1):
  378.             log_to_console(console, f"Błąd SHA1 dla {path}", "ERROR")
  379.             return False
  380.         return True
  381.     except Exception as e:
  382.         log_to_console(console, f"Błąd pobierania {path}: {e}", "ERROR")
  383.         return False
  384.  
  385. def download_assets(version, version_info, version_dir, shared_assets, console):
  386.     try:
  387.         asset_index = version_info.get("assetIndex", {})
  388.         asset_url = asset_index.get("url")
  389.         asset_id = asset_index.get("id")
  390.         if not asset_url:
  391.             log_to_console(console, "Brak assetIndex dla tej wersji", "WARNING")
  392.             return True
  393.  
  394.         resp = requests.get(asset_url, timeout=10)
  395.         resp.raise_for_status()
  396.         assets_data = resp.json()
  397.  
  398.         assets_dir = ASSETS_DIR if shared_assets else os.path.join(version_dir, "assets")
  399.         objects_dir = os.path.join(assets_dir, "objects")
  400.         indexes_dir = os.path.join(assets_dir, "indexes")
  401.         os.makedirs(objects_dir, exist_ok=True)
  402.         os.makedirs(indexes_dir, exist_ok=True)
  403.  
  404.         index_file = os.path.join(indexes_dir, f"{asset_id}.json")
  405.         log_to_console(console, f"Zapisywanie indeksu assetów: {index_file}", "INFO")
  406.         with open(index_file, "w", encoding="utf-8") as f:
  407.             json.dump(assets_data, f)
  408.  
  409.         total_assets = len(assets_data["objects"])
  410.         total_size = sum(asset["size"] for asset in assets_data["objects"].values())
  411.         downloaded_size = 0
  412.  
  413.         for i, (asset_name, asset_info) in enumerate(assets_data["objects"].items()):
  414.             asset_hash = asset_info["hash"]
  415.             asset_size = asset_info["size"]
  416.             asset_subpath = f"{asset_hash[:2]}/{asset_hash}"
  417.             asset_path = os.path.join(objects_dir, asset_subpath)
  418.             asset_url = f"https://resources.download.minecraft.net/{asset_subpath}"
  419.             if not os.path.exists(asset_path):
  420.                 log_to_console(console, f"Pobieranie assetu: {asset_name}", "INFO")
  421.                 if not download_file(
  422.                     asset_url, asset_path,
  423.                     lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "assets", i+1, total_assets),
  424.                     expected_sha1=asset_hash, console=console
  425.                 ):
  426.                     return False
  427.             downloaded_size += asset_size
  428.             update_progress(global_progress_bar, global_status_label, (downloaded_size/total_size)*100,
  429.                            total_size-downloaded_size, "assets", i+1, total_assets)
  430.         return True
  431.     except Exception as e:
  432.         log_to_console(console, f"Błąd pobierania assetów: {e}", "ERROR")
  433.         return False
  434.  
  435. def download_natives(version, version_info, version_dir, shared_natives, console):
  436.     try:
  437.         libraries = version_info.get("libraries", [])
  438.         natives_dir = NATIVES_DIR if shared_natives else os.path.join(version_dir, "natives")
  439.         os.makedirs(natives_dir, exist_ok=True)
  440.  
  441.         system = platform.system().lower()
  442.         natives_classifier = "natives-windows" if system == "windows" else "natives-osx" if system == "darwin" else "natives-linux"
  443.  
  444.         total_size = 0
  445.         natives_to_download = []
  446.         for lib in libraries:
  447.             if "downloads" in lib and "classifiers" in lib["downloads"]:
  448.                 classifiers = lib["downloads"]["classifiers"]
  449.                 if natives_classifier in classifiers:
  450.                     artifact = classifiers[natives_classifier]
  451.                     natives_to_download.append(artifact)
  452.                     total_size += artifact.get("size", 0)
  453.  
  454.         log_to_console(console, f"Pobieranie {len(natives_to_download)} natywnych bibliotek", "INFO")
  455.         downloaded_size = 0
  456.         for i, artifact in enumerate(natives_to_download):
  457.             native_url = artifact["url"]
  458.             native_sha1 = artifact["sha1"]
  459.             native_size = artifact.get("size", 0)
  460.             native_path = os.path.join(version_dir, f"temp_native_{i}.jar")
  461.             log_to_console(console, f"Pobieranie natywnej biblioteki: {native_url}", "INFO")
  462.             if not download_file(
  463.                 native_url, native_path,
  464.                 lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "natives", i+1, len(natives_to_download)),
  465.                 expected_sha1=native_sha1, console=console
  466.             ):
  467.                 return False
  468.             with zipfile.ZipFile(native_path, 'r') as zip_ref:
  469.                 zip_ref.extractall(natives_dir)
  470.             os.remove(native_path)
  471.             downloaded_size += native_size
  472.             update_progress(global_progress_bar, global_status_label, (downloaded_size/total_size)*100,
  473.                            total_size-downloaded_size, "natives", i+1, len(natives_to_download))
  474.         return True
  475.     except Exception as e:
  476.         log_to_console(console, f"Błąd pobierania natywnych bibliotek: {e}", "ERROR")
  477.         return False
  478.  
  479. def verify_instance(version, console):
  480.     instance = instances.get(version)
  481.     if not instance:
  482.         log_to_console(console, f"Instancja {version} nie istnieje!", "ERROR")
  483.         messagebox.showerror("Błąd", f"Instancja {version} nie istnieje!")
  484.         return False
  485.  
  486.     version_dir = instance["path"]
  487.     settings = instance["settings"]
  488.     info = get_version_info(version)
  489.     if not info:
  490.         log_to_console(console, f"Nie udało się pobrać info o wersji {version}", "ERROR")
  491.         return False
  492.  
  493.     log_to_console(console, f"Weryfikacja instancji {version}...", "INFO")
  494.     all_valid = True
  495.  
  496.     # Sprawdź client.jar
  497.     client_path = os.path.join(version_dir, f"{version}.jar")
  498.     client_sha1 = info["downloads"]["client"]["sha1"]
  499.     if not os.path.exists(client_path):
  500.         log_to_console(console, f"Brak client.jar dla {version}", "ERROR")
  501.         all_valid = False
  502.     elif not verify_sha1(client_path, client_sha1):
  503.         log_to_console(console, f"Błąd SHA1 dla client.jar ({version})", "ERROR")
  504.         all_valid = False
  505.     else:
  506.         log_to_console(console, f"client.jar OK", "INFO")
  507.  
  508.     # Sprawdź biblioteki
  509.     libraries_dir = LIBRARIES_DIR if settings.get("shared_libraries", True) else os.path.join(version_dir, "libraries")
  510.     for lib in info.get("libraries", []):
  511.         if "downloads" in lib and "artifact" in lib["downloads"]:
  512.             artifact = lib["downloads"]["artifact"]
  513.             lib_path = artifact["path"].replace("/", os.sep)
  514.             full_lib_path = os.path.join(libraries_dir, lib_path)
  515.             lib_sha1 = artifact["sha1"]
  516.             if not os.path.exists(full_lib_path):
  517.                 log_to_console(console, f"Brak biblioteki {lib_path}", "ERROR")
  518.                 all_valid = False
  519.             elif not verify_sha1(full_lib_path, lib_sha1):
  520.                 log_to_console(console, f"Błąd SHA1 dla biblioteki {lib_path}", "ERROR")
  521.                 all_valid = False
  522.             else:
  523.                 log_to_console(console, f"Biblioteka {lib_path} OK", "INFO")
  524.  
  525.     # Sprawdź natywne biblioteki
  526.     natives_dir = NATIVES_DIR if settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
  527.     system = platform.system().lower()
  528.     natives_classifier = "natives-windows" if system == "windows" else "natives-osx" if system == "darwin" else "natives-linux"
  529.     for lib in info.get("libraries", []):
  530.         if "downloads" in lib and "classifiers" in lib["downloads"]:
  531.             classifiers = lib["downloads"]["classifiers"]
  532.             if natives_classifier in classifiers:
  533.                 artifact = classifiers[natives_classifier]
  534.                 native_path = os.path.join(version_dir, f"temp_native_check.jar")
  535.                 if not download_file(artifact["url"], native_path, None, artifact["sha1"], console):
  536.                     log_to_console(console, f"Błąd weryfikacji natywnej biblioteki {artifact['url']}", "ERROR")
  537.                     all_valid = False
  538.                 else:
  539.                     log_to_console(console, f"Natywna biblioteka {artifact['url']} OK", "INFO")
  540.                 if os.path.exists(native_path):
  541.                     os.remove(native_path)
  542.  
  543.     # Sprawdź assets
  544.     assets_dir = ASSETS_DIR if settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
  545.     asset_index = info.get("assetIndex", {})
  546.     if asset_index.get("url"):
  547.         resp = requests.get(asset_index["url"], timeout=10)
  548.         if resp.status_code == 200:
  549.             assets_data = resp.json()
  550.             for asset_name, asset_info in assets_data["objects"].items():
  551.                 asset_hash = asset_info["hash"]
  552.                 asset_subpath = f"{asset_hash[:2]}/{asset_hash}"
  553.                 asset_path = os.path.join(assets_dir, "objects", asset_subpath)
  554.                 if not os.path.exists(asset_path):
  555.                     log_to_console(console, f"Brak assetu {asset_name}", "ERROR")
  556.                     all_valid = False
  557.                 elif not verify_sha1(asset_path, asset_hash):
  558.                     log_to_console(console, f"Błąd SHA1 dla assetu {asset_name}", "ERROR")
  559.                     all_valid = False
  560.                 else:
  561.                     log_to_console(console, f"Asset {asset_name} OK", "INFO")
  562.         else:
  563.             log_to_console(console, f"Nie udało się pobrać indeksu assetów", "ERROR")
  564.             all_valid = False
  565.  
  566.     # Sprawdź i zregeneruj start.bat
  567.     bat_path = os.path.join(version_dir, "start.bat")
  568.     if not os.path.exists(bat_path):
  569.         log_to_console(console, f"Brak start.bat dla {version}, regeneruję...", "WARNING")
  570.         all_valid = False
  571.     else:
  572.         log_to_console(console, f"start.bat OK, regeneruję dla pewności...", "INFO")
  573.  
  574.     # Regeneracja start.bat
  575.     classpath = f"{version}.jar"
  576.     for lib in info.get("libraries", []):
  577.         if "downloads" in lib and "artifact" in lib["downloads"]:
  578.             lib_path = lib["downloads"]["artifact"]["path"].replace("/", os.sep)
  579.             classpath += f";{os.path.join(libraries_dir, lib_path)}"
  580.     assets_path = ASSETS_DIR if settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
  581.     natives_path = NATIVES_DIR if settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
  582.     java_path = instance.get("java_path", "")
  583.     asset_index = info.get("assetIndex", {}).get("id", version)
  584.     with open(bat_path, "w", encoding="utf-8") as f:
  585.         if is_new_launcher(version):
  586.             f.write(f'@echo off\n')
  587.             f.write(f'title Minecraft {version}\n')
  588.             f.write(f'"{java_path}" -Xmx{settings["memory"]}G -Djava.library.path="{natives_path}" -Dorg.lwjgl.util.Debug=true -cp "{classpath}" net.minecraft.client.main.Main ')
  589.             f.write(f'--username {settings["username"]} --version {version} --gameDir . ')
  590.             f.write(f'--assetsDir "{assets_path}" --assetIndex {asset_index} ')
  591.             f.write(f'--accessToken null --uuid 0 --userType legacy\n')
  592.             f.write(f'pause\n')
  593.         else:
  594.             f.write(f'@echo off\n')
  595.             f.write(f'title Minecraft {version}\n')
  596.             f.write(f'"{java_path}" -Xmx{settings["memory"]}G -cp "{version}.jar" net.minecraft.LauncherFrame {settings["username"]} player_session_id\n')
  597.             f.write(f'pause\n')
  598.     log_to_console(console, f"start.bat zregenerowany dla {version}", "SUCCESS")
  599.  
  600.     if all_valid:
  601.         log_to_console(console, f"Instancja {version} zweryfikowana poprawnie!", "SUCCESS")
  602.         messagebox.showinfo("Sukces", f"Instancja {version} jest kompletna!")
  603.     else:
  604.         log_to_console(console, f"Weryfikacja instancji {version} nieudana, ale start.bat zregenerowany!", "WARNING")
  605.         messagebox.showwarning("Uwaga", f"Instancja {version} miała problemy, ale start.bat został zregenerowany!")
  606.     return all_valid
  607.  
  608. def update_progress(progress_bar, status_label, progress, remaining, stage, current, total):
  609.     if progress_bar and status_label:
  610.         progress_bar["value"] = progress
  611.         remaining_str = humanize.naturalsize(remaining) if remaining else "0 B"
  612.         status_label.config(text=f"[{stage}] Postęp: {progress:.1f}% | Pozostało: {remaining_str} | {current}/{total}")
  613.         root.update()
  614.  
  615. def download_version(version, instance_settings, console):
  616.     global download_active
  617.     download_active = True
  618.     info = get_version_info(version)
  619.     if not info:
  620.         download_active = False
  621.         return
  622.  
  623.     java_major_version = get_required_java(version, info)
  624.     log_to_console(console, f"Wymagana Java: {java_major_version}", "INFO")
  625.  
  626.     java_paths = find_java(java_major_version)
  627.     if not java_paths:
  628.         if messagebox.askyesno("Brak Javy", f"Nie znaleziono Javy {java_major_version}. Pobrać automatycznie?"):
  629.             java_path, java_version = download_java(java_major_version, console)
  630.             if java_path:
  631.                 java_paths = [(java_path, java_version)]
  632.             else:
  633.                 log_to_console(console, f"Nie udało się pobrać Javy {java_major_version}!", "ERROR")
  634.                 messagebox.showerror("Błąd", f"Nie udało się pobrać Javy {java_major_version}!")
  635.                 download_active = False
  636.                 return
  637.         else:
  638.             log_to_console(console, f"Nie znaleziono Javy {java_major_version}! Wskaż ręcznie.", "ERROR")
  639.             messagebox.showerror("Błąd", f"Nie znaleziono Javy {java_major_version}! Pobierz z adoptium.net.")
  640.             download_active = False
  641.             return
  642.  
  643.     java_path, java_version = java_paths[0]
  644.     if instance_settings.get("java_path"):
  645.         java_path = instance_settings["java_path"]
  646.         java_version = get_java_version(java_path) or java_version
  647.     log_to_console(console, f"Używam Javy: {java_path} (wersja {java_version})", "INFO")
  648.  
  649.     version_dir = os.path.join(BASE_DIR, "instances", version)
  650.     os.makedirs(version_dir, exist_ok=True)
  651.     libraries_dir = LIBRARIES_DIR if instance_settings.get("shared_libraries", True) else os.path.join(version_dir, "libraries")
  652.     os.makedirs(libraries_dir, exist_ok=True)
  653.  
  654.     # Etap 1: Client.jar
  655.     update_progress(global_progress_bar, global_status_label, 0, 0, "client.jar", 1, 1)
  656.     global_status_label.config(text="[1/5] Pobieranie client.jar...")
  657.     client_url = info["downloads"]["client"]["url"]
  658.     client_sha1 = info["downloads"]["client"]["sha1"]
  659.     client_path = os.path.join(version_dir, f"{version}.jar")
  660.     log_to_console(console, f"Pobieranie client.jar dla {version}", "INFO")
  661.     if not download_file(
  662.         client_url, client_path,
  663.         lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "client.jar", 1, 1),
  664.         expected_sha1=client_sha1, console=console
  665.     ):
  666.         download_active = False
  667.         return
  668.  
  669.     # Etap 2: Biblioteki
  670.     update_progress(global_progress_bar, global_status_label, 0, 0, "biblioteki", 0, 0)
  671.     global_status_label.config(text="[2/5] Pobieranie bibliotek...")
  672.     libs = info.get("libraries", [])
  673.     total_libs_size = 0
  674.     libs_to_download = []
  675.     for lib in libs:
  676.         if "downloads" in lib and "artifact" in lib["downloads"]:
  677.             artifact = lib["downloads"]["artifact"]
  678.             lib_path = artifact["path"].replace("/", os.sep)
  679.             full_lib_path = os.path.join(libraries_dir, lib_path)
  680.             if not os.path.exists(full_lib_path):
  681.                 libs_to_download.append((lib, artifact))
  682.                 total_libs_size += artifact.get("size", 0)
  683.  
  684.     log_to_console(console, f"Pobieranie {len(libs_to_download)} bibliotek", "INFO")
  685.     downloaded_libs_size = 0
  686.     for i, (lib, artifact) in enumerate(libs_to_download):
  687.         lib_path = artifact["path"].replace("/", os.sep)
  688.         full_lib_path = os.path.join(libraries_dir, lib_path)
  689.         lib_url = artifact["url"]
  690.         lib_sha1 = artifact["sha1"]
  691.         lib_size = artifact.get("size", 0)
  692.         log_to_console(console, f"Pobieranie biblioteki: {lib_path}", "INFO")
  693.         if not download_file(
  694.             lib_url, full_lib_path,
  695.             lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "biblioteki", i+1, len(libs_to_download)),
  696.             expected_sha1=lib_sha1, console=console
  697.         ):
  698.             download_active = False
  699.         downloaded_libs_size += lib_size
  700.         update_progress(global_progress_bar, global_status_label, (downloaded_libs_size/total_libs_size)*100,
  701.                        total_libs_size-downloaded_libs_size, "biblioteki", i+1, len(libs_to_download))
  702.  
  703.     # Etap 3: Natives
  704.     update_progress(global_progress_bar, global_status_label, 0, 0, "natives", 0, 0)
  705.     global_status_label.config(text="[3/5] Pobieranie natywnych bibliotek...")
  706.     if not download_natives(version, info, version_dir, instance_settings.get("shared_natives", True), console):
  707.         download_active = False
  708.         return
  709.  
  710.     # Etap 4: Assets
  711.     update_progress(global_progress_bar, global_status_label, 0, 0, "assets", 0, 0)
  712.     global_status_label.config(text="[4/5] Pobieranie assetów...")
  713.     if not download_assets(version, info, version_dir, instance_settings.get("shared_assets", True), console):
  714.         download_active = False
  715.         return
  716.  
  717.     # Etap 5: Generowanie start.bat
  718.     update_progress(global_progress_bar, global_status_label, 0, 0, "start.bat", 1, 1)
  719.     global_status_label.config(text="[5/5] Generowanie start.bat...")
  720.     bat_path = os.path.join(version_dir, "start.bat")
  721.     classpath = f"{version}.jar"
  722.     for lib in libs:
  723.         if "downloads" in lib and "artifact" in lib["downloads"]:
  724.             lib_path = lib["downloads"]["artifact"]["path"].replace("/", os.sep)
  725.             classpath += f";{os.path.join(libraries_dir, lib_path)}"
  726.  
  727.     assets_path = ASSETS_DIR if instance_settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
  728.     natives_path = NATIVES_DIR if instance_settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
  729.     username = instance_settings.get("username", "Player").strip() or "Player"
  730.     memory = instance_settings.get("memory", "2")
  731.     asset_index = info.get("assetIndex", {}).get("id", version)
  732.  
  733.     with open(bat_path, "w", encoding="utf-8") as f:
  734.         if is_new_launcher(version):
  735.             f.write(f'@echo off\n')
  736.             f.write(f'title Minecraft {version}\n')
  737.             f.write(f'"{java_path}" -Xmx{memory}G -Djava.library.path="{natives_path}" -Dorg.lwjgl.util.Debug=true -cp "{classpath}" net.minecraft.client.main.Main ')
  738.             f.write(f'--username {username} --version {version} --gameDir . ')
  739.             f.write(f'--assetsDir "{assets_path}" --assetIndex {asset_index} ')
  740.             f.write(f'--accessToken null --uuid 0 --userType legacy\n')
  741.             f.write(f'pause\n')
  742.         else:
  743.             f.write(f'@echo off\n')
  744.             f.write(f'title Minecraft {version}\n')
  745.             f.write(f'"{java_path}" -Xmx{memory}G -cp "{version}.jar" net.minecraft.LauncherFrame {username} player_session_id\n')
  746.             f.write(f'pause\n')
  747.  
  748.     instances[version] = {
  749.         "path": version_dir,
  750.         "java_path": java_path,
  751.         "java_version": java_version,
  752.         "required_java": java_major_version,
  753.         "settings": instance_settings,
  754.         "ready": True,
  755.         "timestamp": datetime.now().isoformat()
  756.     }
  757.     save_config()
  758.     update_progress(global_progress_bar, global_status_label, 100, 0, "gotowe", 1, 1)
  759.     global_status_label.config(text="Gotowe!")
  760.     log_to_console(console, f"Instancja {version} pobrana!", "SUCCESS")
  761.     messagebox.showinfo("Sukces", f"Instancja {version} gotowa!")
  762.     refresh_instances()
  763.     download_active = False
  764.  
  765. def run_game(version):
  766.     instance = instances.get(version)
  767.     if not instance or not instance.get("ready"):
  768.         messagebox.showerror("Błąd", f"Instancja {version} nie jest gotowa!")
  769.         return
  770.     bat_path = os.path.join(instance["path"], "start.bat")
  771.     if os.path.exists(bat_path):
  772.         try:
  773.             subprocess.Popen(bat_path, cwd=instance["path"], shell=True)
  774.             log_to_console(console, f"Uruchomiono Minecraft {version}", "SUCCESS")
  775.         except Exception as e:
  776.             log_to_console(console, f"Nie udało się uruchomić gry: {e}", "ERROR")
  777.             messagebox.showerror("Błąd", f"Nie udało się uruchomić gry: {e}")
  778.     else:
  779.         messagebox.showerror("Błąd", f"Brak start.bat dla {version}!")
  780.  
  781. def delete_instance(version):
  782.     if messagebox.askyesno("Potwierdź", f"Na pewno usunąć instancję {version}?"):
  783.         instance = instances.get(version)
  784.         if instance:
  785.             import shutil
  786.             try:
  787.                 shutil.rmtree(instance["path"])
  788.                 del instances[version]
  789.                 save_config()
  790.                 log_to_console(console, f"Instancja {version} usunięta", "SUCCESS")
  791.                 refresh_instances()
  792.             except Exception as e:
  793.                 log_to_console(console, f"Błąd usuwania instancji {version}: {e}", "ERROR")
  794.  
  795. def copy_console(console):
  796.     console.config(state="normal")
  797.     content = console.get("1.0", tk.END).strip()
  798.     pyperclip.copy(content)
  799.     console.config(state="disabled")
  800.     messagebox.showinfo("Sukces", "Konsola skopiowana!")
  801.  
  802. def clear_console(console):
  803.     console.config(state="normal")
  804.     console.delete("1.0", tk.END)
  805.     console.config(state="disabled")
  806.  
  807. if __name__ == "__main__":
  808.     try:
  809.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}[Inicjalizacja Tkinter...")
  810.         root = tk.Tk()
  811.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}[Tkinter zainicjalizowany.")
  812.         root.title("Minecraft Launcher by Paffcio")
  813.         root.geometry("1000x750")
  814.         root.configure(bg=BG_COLOR)
  815.         root.minsize(900, 650)
  816.  
  817.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Ładowanie konfiguracji...")
  818.         # Inicjalizacja zmiennych Tkinter przed ładowaniem konfiguracji
  819.         username_var = tk.StringVar(value="Player")
  820.         memory_var = tk.StringVar(value="2")
  821.         shared_assets_var = tk.BooleanVar(value=True)
  822.         shared_libraries_var = tk.BooleanVar(value=True)
  823.         shared_natives_var = tk.BooleanVar(value=True)
  824.         snapshots_var = tk.BooleanVar(value=True)
  825.         releases_var = tk.BooleanVar(value=True)
  826.         alpha_var = tk.BooleanVar(value=False)
  827.         beta_var = tk.BooleanVar(value=False)
  828.         current_tab = tk.StringVar(value="Instancje")
  829.        
  830.         # Teraz ładujemy konfigurację, która zaktualizuje zmienne
  831.         instances, java_versions_cache = load_config()
  832.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Konfiguracja wczytana.")
  833.  
  834.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Tworzenie zmiennych Tkinter...")
  835.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Zmienne utworzone.")
  836.  
  837.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Inicjalizacja UI...")
  838.         style = ttk.Style()
  839.         style.theme_use('clam')
  840.         style.configure("TFrame", background=BG_COLOR)
  841.         style.configure("TLabel", background=BG_COLOR, foreground=FG_COLOR, font=("Segoe UI", 10))
  842.         style.configure("TButton", background=BUTTON_BG, foreground=FG_COLOR, font=("Segoe UI", 10), borderwidth=0)
  843.         style.map("TButton",
  844.                   background=[('active', BUTTON_HOVER), ('pressed', BUTTON_HOVER)],
  845.                   foreground=[('active', FG_COLOR), ('pressed', FG_COLOR)])
  846.         style.configure("TEntry", fieldbackground="#333333", foreground=FG_COLOR, insertcolor=FG_COLOR, borderwidth=1)
  847.         style.configure("TCombobox", fieldbackground="#333333", foreground=FG_COLOR, selectbackground=ACCENT_COLOR)
  848.         style.configure("Horizontal.TProgressbar", troughcolor="#333333", background=ACCENT_COLOR, thickness=10)
  849.         style.configure("Treeview", background="#333333", fieldbackground="#333333", foreground=FG_COLOR, rowheight=25)
  850.         style.map("Treeview", background=[('selected', ACCENT_COLOR)])
  851.         style.configure("Treeview.Heading", background=SIDEBAR_BG, foreground=FG_COLOR, font=("Segoe UI", 10, "bold"))
  852.  
  853.         progress_frame = ttk.Frame(root)
  854.         progress_frame.pack(side="bottom", fill="x", padx=10, pady=5)
  855.         global_progress_bar = ttk.Progressbar(progress_frame, length=400, mode="determinate", style="Horizontal.TProgressbar")
  856.         global_progress_bar.pack(fill="x")
  857.         global_status_label = ttk.Label(progress_frame, text="Gotowe do działania!", foreground=FG_COLOR, background=BG_COLOR, font=("Segoe UI", 9))
  858.         global_status_label.pack()
  859.  
  860.         sidebar = tk.Frame(root, bg=SIDEBAR_BG, width=220)
  861.         sidebar.pack(side="left", fill="y", padx=0, pady=0)
  862.  
  863.         logo_frame = tk.Frame(sidebar, bg=SIDEBAR_BG)
  864.         logo_frame.pack(fill="x", pady=(10, 20))
  865.         try:
  866.             print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Ładowanie logo...")
  867.             logo_img = Image.open(os.path.join(ICONS_DIR, "logo.png")) if os.path.exists(ICONS_DIR) else None
  868.             if logo_img:
  869.                 logo_img = logo_img.resize((180, 60), Image.LANCZOS)
  870.                 logo_photo = ImageTk.PhotoImage(logo_img)
  871.                 logo_label = tk.Label(logo_frame, image=logo_photo, bg=SIDEBAR_BG)
  872.                 logo_label.image = logo_photo
  873.                 logo_label.pack(pady=5)
  874.             print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Logo załadowane.")
  875.         except Exception as e:
  876.             print(f"{Fore.LIGHTCYAN_EX}Błąd ładowania logo: {e}")
  877.  
  878.         tabs = [
  879.             ("Instancje", "📋"),
  880.             ("Pobieranie", "⬇"),
  881.             ("Ustawienia", "⚙"),
  882.             ("Konsola", "📜")
  883.         ]
  884.         tab_buttons = {}
  885.         for tab_name, icon in tabs:
  886.             print(f"{Fore.LIGHTCYAN_EX}Tworzenie przycisku {tab_name}...")
  887.             btn = tk.Button(
  888.                 sidebar, text=f"  {icon} {tab_name}", bg=SIDEBAR_BG, fg=FG_COLOR,
  889.                 activebackground=ACTIVE_TAB_COLOR, activeforeground=FG_COLOR,
  890.                 font=("Segoe UI", 11), relief="flat", anchor="w", padx=15, pady=10,
  891.                 command=lambda t=tab_name: switch_tab(t)
  892.             )
  893.             btn.pack(fill="x", pady=0)
  894.             tab_buttons[tab_name] = btn
  895.             btn.bind("<Enter>", lambda e, b=btn: b.config(bg=HOVER_TAB_COLOR) if current_tab.get() != tab_name else None)
  896.             btn.bind("<Leave>", lambda e, b=btn: b.config(bg=SIDEBAR_BG) if current_tab.get() != tab_name else None)
  897.             print(f"{Fore.LIGHTCYAN_EX}Przycisk {tab_name} utworzony.")
  898.  
  899.         content_frame = ttk.Frame(root)
  900.         content_frame.pack(side="left", fill="both", expand=True, padx=10, pady=10)
  901.  
  902.         def switch_tab(tab_name):
  903.             global download_active
  904.             if download_active and tab_name != "Pobieranie":
  905.                 if not messagebox.askyesno("Ostrzeżenie", "Pobieranie w toku! Zmiana zakładki przerwie proces. Kontynuować?"):
  906.                     return
  907.                 download_active = False
  908.             current_tab.set(tab_name)
  909.             for name, btn in tab_buttons.items():
  910.                 if name == tab_name:
  911.                     btn.config(bg=ACTIVE_TAB_COLOR, relief="sunken")
  912.                 else:
  913.                     btn.config(bg=SIDEBAR_BG, relief="flat")
  914.             for widget in content_frame.winfo_children():
  915.                 widget.destroy()
  916.  
  917.             if tab_name == "Instancje":
  918.                 show_instances()
  919.             elif tab_name == "Pobieranie":
  920.                 show_download()
  921.             elif tab_name == "Ustawienia":
  922.                 show_settings()
  923.             elif tab_name == "Konsola":
  924.                 show_console()
  925.  
  926.         def show_instances():
  927.             frame = ttk.Frame(content_frame)
  928.             frame.pack(fill="both", expand=True)
  929.  
  930.             header_frame = ttk.Frame(frame)
  931.             header_frame.pack(fill="x", pady=(0, 10))
  932.             ttk.Label(header_frame, text="Twoje instancje Minecraft", font=("Segoe UI", 14, "bold")).pack(side="left")
  933.            
  934.             btn_frame = ttk.Frame(header_frame)
  935.             btn_frame.pack(side="right")
  936.            
  937.             refresh_btn = ttk.Button(btn_frame, text="Odśwież", command=refresh_instances, width=10)
  938.             refresh_btn.pack(side="left", padx=5)
  939.            
  940.             new_btn = ttk.Button(btn_frame, text="Nowa instancja", command=create_instance, width=15)
  941.             new_btn.pack(side="left", padx=5)
  942.  
  943.             tree_frame = ttk.Frame(frame)
  944.             tree_frame.pack(fill="both", expand=True)
  945.            
  946.             columns = ("version", "java", "status", "date")
  947.             tree = ttk.Treeview(
  948.                 tree_frame, columns=columns, show="headings", selectmode="browse",
  949.                 style="Treeview"
  950.             )
  951.            
  952.             tree.heading("version", text="Wersja", anchor="w")
  953.             tree.heading("java", text="Java", anchor="w")
  954.             tree.heading("status", text="Status", anchor="w")
  955.             tree.heading("date", text="Data utworzenia", anchor="w")
  956.            
  957.             tree.column("version", width=150, anchor="w")
  958.             tree.column("java", width=100, anchor="w")
  959.             tree.column("status", width=80, anchor="w")
  960.             tree.column("date", width=120, anchor="w")
  961.            
  962.             scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=tree.yview)
  963.             scrollbar.pack(side="right", fill="y")
  964.             tree.configure(yscrollcommand=scrollbar.set)
  965.             tree.pack(fill="both", expand=True)
  966.            
  967.             for version, data in instances.items():
  968.                 tree.insert("", "end", values=(
  969.                     version,
  970.                     data.get("java_version", "?"),
  971.                     "Gotowe" if data.get("ready", False) else "Błąd",
  972.                     datetime.fromisoformat(data["timestamp"]).strftime("%Y-%m-%d %H:%M")
  973.                 ))
  974.            
  975.             action_frame = ttk.Frame(frame)
  976.             action_frame.pack(fill="x", pady=(10, 0))
  977.            
  978.             run_btn = ttk.Button(action_frame, text="Uruchom", command=lambda: run_game(tree.item(tree.selection())["values"][0] if tree.selection() else ""), width=15)
  979.             run_btn.pack(side="left", padx=5)
  980.            
  981.             edit_btn = ttk.Button(action_frame, text="Edytuj", command=lambda: edit_instance(tree.item(tree.selection())["values"][0] if tree.selection() else ""), width=15)
  982.             edit_btn.pack(side="left", padx=5)
  983.            
  984.             verify_btn = ttk.Button(action_frame, text="Sprawdź", command=lambda: verify_instance(tree.item(tree.selection())["values"][0] if tree.selection() else "", console), width=15)
  985.             verify_btn.pack(side="left", padx=5)
  986.            
  987.             delete_btn = ttk.Button(action_frame, text="Usuń", command=lambda: delete_instance(tree.item(tree.selection())["values"][0] if tree.selection() else ""), width=15)
  988.             delete_btn.pack(side="left", padx=5)
  989.            
  990.             ttk.Label(frame, text="Wybierz instancję i kliknij odpowiedni przycisk", font=("Segoe UI", 9)).pack(pady=(10, 0))
  991.  
  992.         def refresh_instances():
  993.             for widget in content_frame.winfo_children():
  994.                 widget.destroy()
  995.             show_instances()
  996.  
  997.         def create_instance():
  998.             window = tk.Toplevel(root)
  999.             window.title("Nowa instancja Minecraft")
  1000.             window.geometry("500x650")
  1001.             window.configure(bg=BG_COLOR)
  1002.             window.resizable(False, False)
  1003.            
  1004.             style.configure("NewInstance.TFrame", background=BG_COLOR)
  1005.             style.configure("NewInstance.TLabel", background=BG_COLOR, foreground=FG_COLOR, font=("Segoe UI", 10))
  1006.             style.configure("NewInstance.TButton", background=BUTTON_BG, foreground=FG_COLOR, font=("Segoe UI", 10))
  1007.  
  1008.             frame = ttk.Frame(window, style="NewInstance.TFrame", padding="15")
  1009.             frame.pack(fill="both", expand=True)
  1010.            
  1011.             ttk.Label(frame, text="Tworzenie nowej instancji", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
  1012.            
  1013.             form_frame = ttk.Frame(frame)
  1014.             form_frame.pack(fill="both", expand=True)
  1015.            
  1016.             ttk.Label(form_frame, text="Filtry wersji:").grid(row=0, column=0, sticky="w", pady=(0, 5))
  1017.             filters_frame = ttk.Frame(form_frame)
  1018.             filters_frame.grid(row=0, column=1, sticky="w", pady=(0, 5))
  1019.            
  1020.             tk.Checkbutton(filters_frame, text="Snapshoty", variable=snapshots_var, bg=BG_COLOR, fg=FG_COLOR,
  1021.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1022.                            command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
  1023.             tk.Checkbutton(filters_frame, text="Release", variable=releases_var, bg=BG_COLOR, fg=FG_COLOR,
  1024.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1025.                            command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
  1026.             tk.Checkbutton(filters_frame, text="Alpha", variable=alpha_var, bg=BG_COLOR, fg=FG_COLOR,
  1027.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1028.                            command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
  1029.             tk.Checkbutton(filters_frame, text="Beta", variable=beta_var, bg=BG_COLOR, fg=FG_COLOR,
  1030.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1031.                            command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
  1032.            
  1033.             ttk.Label(form_frame, text="Wersja Minecrafta:").grid(row=1, column=0, sticky="w", pady=(10, 5))
  1034.             version_var = tk.StringVar()
  1035.             versions = get_versions()
  1036.             version_combo = ttk.Combobox(form_frame, textvariable=version_var, state="readonly", values=versions)
  1037.             version_combo.grid(row=1, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
  1038.             if versions:
  1039.                 version_combo.current(0)
  1040.             else:
  1041.                 messagebox.showwarning("Uwaga", "Włącz przynajmniej jeden filtr wersji (Snapshoty, Release, Alpha, Beta)!")
  1042.            
  1043.             ttk.Label(form_frame, text="Nazwa użytkownika:").grid(row=2, column=0, sticky="w", pady=(10, 5))
  1044.             username_entry = ttk.Entry(form_frame)
  1045.             username_entry.insert(0, username_var.get())
  1046.             username_entry.grid(row=2, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
  1047.            
  1048.             ttk.Label(form_frame, text="Pamięć RAM (GB):").grid(row=3, column=0, sticky="w", pady=(10, 5))
  1049.             memory_spin = tk.Spinbox(form_frame, from_=1, to=16, width=5, bg="#333333", fg=FG_COLOR, highlightthickness=0)
  1050.             memory_spin.delete(0, tk.END)
  1051.             memory_spin.insert(0, memory_var.get())
  1052.             memory_spin.grid(row=3, column=1, sticky="w", pady=(10, 5), padx=(5, 0))
  1053.            
  1054.             ttk.Label(form_frame, text="Ścieżka Java:").grid(row=4, column=0, sticky="w", pady=(10, 5))
  1055.             java_var = tk.StringVar()
  1056.             java_combo = ttk.Combobox(form_frame, textvariable=java_var, state="readonly")
  1057.             java_paths = find_java()
  1058.             java_combo['values'] = [f"{p} ({v})" for p, v in java_paths]
  1059.             java_combo.grid(row=4, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
  1060.            
  1061.             # Automatyczny wybór najlepszej Javy
  1062.             if versions and java_paths:
  1063.                 version_info = get_version_info(version_var.get())
  1064.                 required_java = get_required_java(version_var.get(), version_info)
  1065.                 best_java_index = 0
  1066.                 for i, (path, ver) in enumerate(java_paths):
  1067.                     if check_java_version(ver, required_java):
  1068.                         best_java_index = i
  1069.                         break
  1070.                 java_combo.current(best_java_index)
  1071.            
  1072.             # Aktualizacja Javy przy zmianie wersji Minecrafta
  1073.             def update_java_combo(event):
  1074.                 version_info = get_version_info(version_var.get())
  1075.                 required_java = get_required_java(version_var.get(), version_info)
  1076.                 for i, (path, ver) in enumerate(java_paths):
  1077.                     if check_java_version(ver, required_java):
  1078.                         java_combo.current(i)
  1079.                         break
  1080.                 else:
  1081.                     java_combo.current(0) if java_paths else java_combo.set("Brak Javy")
  1082.            
  1083.             version_combo.bind("<<ComboboxSelected>>", update_java_combo)
  1084.            
  1085.             options_frame = ttk.Frame(form_frame)
  1086.             options_frame.grid(row=5, column=0, columnspan=2, sticky="ew", pady=(10, 5))
  1087.            
  1088.             shared_assets = tk.BooleanVar(value=shared_assets_var.get())
  1089.             shared_libs = tk.BooleanVar(value=shared_libraries_var.get())
  1090.             shared_natives = tk.BooleanVar(value=shared_natives_var.get())
  1091.            
  1092.             tk.Checkbutton(options_frame, text="Współdziel assets", variable=shared_assets,
  1093.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1094.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1095.             tk.Checkbutton(options_frame, text="Współdziel biblioteki", variable=shared_libs,
  1096.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1097.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1098.             tk.Checkbutton(options_frame, text="Współdziel natywne biblioteki", variable=shared_natives,
  1099.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1100.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1101.            
  1102.             btn_frame = ttk.Frame(frame)
  1103.             btn_frame.pack(fill="x", pady=(15, 0))
  1104.            
  1105.             ttk.Button(btn_frame, text="Anuluj", command=window.destroy, width=15).pack(side="right", padx=5)
  1106.             ttk.Button(btn_frame, text="Utwórz", command=lambda: [
  1107.                 save_pending_instance({
  1108.                     "username": username_entry.get(),
  1109.                     "memory": memory_spin.get(),
  1110.                     "java_path": java_paths[int(java_combo.current())][0] if java_combo.current() != -1 else "",
  1111.                     "shared_assets": shared_assets.get(),
  1112.                     "shared_libraries": shared_libs.get(),
  1113.                     "shared_natives": shared_natives.get()
  1114.                 }, version_var.get()),
  1115.                 switch_tab("Pobieranie"),
  1116.                 window.destroy()
  1117.             ] if version_var.get() else messagebox.showwarning("Uwaga", "Wybierz wersję!")).pack(side="right", padx=5)
  1118.  
  1119.         def save_pending_instance(settings, version):
  1120.             global pending_instance_settings, pending_version
  1121.             pending_instance_settings = settings
  1122.             pending_version = version
  1123.  
  1124.         def edit_instance(version):
  1125.             if not version or version not in instances:
  1126.                 messagebox.showwarning("Uwaga", "Wybierz instancję!")
  1127.                 return
  1128.             instance = instances[version]
  1129.             settings = instance.get("settings", {})
  1130.  
  1131.             window = tk.Toplevel(root)
  1132.             window.title(f"Edytuj instancję {version}")
  1133.             window.geometry("500x550")
  1134.             window.configure(bg=BG_COLOR)
  1135.             window.resizable(False, False)
  1136.  
  1137.             frame = ttk.Frame(window, padding="15")
  1138.             frame.pack(fill="both", expand=True)
  1139.  
  1140.             ttk.Label(frame, text=f"Edycja instancji {version}", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
  1141.  
  1142.             form_frame = ttk.Frame(frame)
  1143.             form_frame.pack(fill="both", expand=True)
  1144.  
  1145.             ttk.Label(form_frame, text="Nazwa użytkownika:").grid(row=0, column=0, sticky="w", pady=(0, 5))
  1146.             username_entry = ttk.Entry(form_frame)
  1147.             username_entry.insert(0, settings.get("username", username_var.get()))
  1148.             username_entry.grid(row=0, column=1, sticky="ew", pady=(0, 5), padx=(5, 0))
  1149.  
  1150.             ttk.Label(form_frame, text="Pamięć RAM (GB):").grid(row=1, column=0, sticky="w", pady=(10, 5))
  1151.             memory_spin = tk.Spinbox(form_frame, from_=1, to=16, width=5, bg="#333333", fg=FG_COLOR, highlightthickness=0)
  1152.             memory_spin.delete(0, tk.END)
  1153.             memory_spin.insert(0, settings.get("memory", memory_var.get()))
  1154.             memory_spin.grid(row=1, column=1, sticky="w", pady=(10, 5), padx=(5, 0))
  1155.  
  1156.             ttk.Label(form_frame, text="Ścieżka Java:").grid(row=2, column=0, sticky="w", pady=(10, 5))
  1157.             java_var = tk.StringVar()
  1158.             java_combo = ttk.Combobox(form_frame, textvariable=java_var, state="readonly")
  1159.             java_paths = find_java()
  1160.             current_java = settings.get("java_path", instance.get("java_path", ""))
  1161.             java_values = [f"{p} ({v})" for p, v in java_paths]
  1162.            
  1163.             # Jeśli zapisana Java istnieje, dodaj ją do listy, jeśli nie ma jej w java_paths
  1164.             if current_java and not any(p == current_java for p, v in java_paths):
  1165.                 java_values.append(f"{current_java} (zapisana)")
  1166.                 java_paths.append((current_java, "zapisana"))
  1167.            
  1168.             java_combo['values'] = java_values
  1169.             java_combo.grid(row=2, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
  1170.            
  1171.             # Ustaw zapisana Javę, jeśli istnieje
  1172.             if current_java:
  1173.                 for i, (path, ver) in enumerate(java_paths):
  1174.                     if path == current_java:
  1175.                         java_combo.current(i)
  1176.                         break
  1177.                 else:
  1178.                     java_combo.current(len(java_paths) - 1)  # Wybierz "zapisana", jeśli dodana
  1179.             elif java_paths:
  1180.                 # Jeśli brak zapisanej Javy, wybierz najlepszą dla wersji
  1181.                 version_info = get_version_info(version)
  1182.                 required_java = get_required_java(version, version_info)
  1183.                 for i, (path, ver) in enumerate(java_paths):
  1184.                     if check_java_version(ver, required_java):
  1185.                         java_combo.current(i)
  1186.                         break
  1187.                 else:
  1188.                     java_combo.current(0)
  1189.  
  1190.             options_frame = ttk.Frame(form_frame)
  1191.             options_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(10, 5))
  1192.  
  1193.             shared_assets = tk.BooleanVar(value=settings.get("shared_assets", shared_assets_var.get()))
  1194.             shared_libs = tk.BooleanVar(value=settings.get("shared_libraries", shared_libraries_var.get()))
  1195.             shared_natives = tk.BooleanVar(value=settings.get("shared_natives", shared_natives_var.get()))
  1196.  
  1197.             tk.Checkbutton(options_frame, text="Współdziel assets", variable=shared_assets,
  1198.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1199.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1200.             tk.Checkbutton(options_frame, text="Współdziel biblioteki", variable=shared_libs,
  1201.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1202.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1203.             tk.Checkbutton(options_frame, text="Współdziel natywne biblioteki", variable=shared_natives,
  1204.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1205.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1206.  
  1207.             btn_frame = ttk.Frame(frame)
  1208.             btn_frame.pack(fill="x", pady=(15, 0))
  1209.  
  1210.             ttk.Button(btn_frame, text="Anuluj", command=window.destroy, width=15).pack(side="right", padx=5)
  1211.             ttk.Button(btn_frame, text="Zapisz", command=lambda: [
  1212.                 update_instance(version, {
  1213.                     "username": username_entry.get(),
  1214.                     "memory": memory_spin.get(),
  1215.                     "java_path": java_paths[int(java_combo.current())][0] if java_combo.current() != -1 else current_java,
  1216.                     "shared_assets": shared_assets.get(),
  1217.                     "shared_libraries": shared_libs.get(),
  1218.                     "shared_natives": shared_natives.get()
  1219.                 }),
  1220.                 window.destroy()
  1221.             ]).pack(side="right", padx=5)
  1222.  
  1223.         def update_instance(version, settings):
  1224.             instance = instances[version]
  1225.             instance["settings"] = settings
  1226.             info = get_version_info(version)
  1227.             if info:
  1228.                 version_dir = instance["path"]
  1229.                 bat_path = os.path.join(version_dir, "start.bat")
  1230.                 classpath = f"{version}.jar"
  1231.                 libraries_dir = LIBRARIES_DIR if settings.get("shared_libraries", True) else os.path.join(version_dir, "libraries")
  1232.                 for lib in info.get("libraries", []):
  1233.                     if "downloads" in lib and "artifact" in lib["downloads"]:
  1234.                         lib_path = lib["downloads"]["artifact"]["path"].replace("/", os.sep)
  1235.                         classpath += f";{os.path.join(libraries_dir, lib_path)}"
  1236.                 assets_path = ASSETS_DIR if settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
  1237.                 natives_path = NATIVES_DIR if settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
  1238.                 java_path = settings.get("java_path", instance.get("java_path", ""))
  1239.                 asset_index = info.get("assetIndex", {}).get("id", version)
  1240.                 with open(bat_path, "w", encoding="utf-8") as f:
  1241.                     if is_new_launcher(version):
  1242.                         f.write(f'@echo off\n')
  1243.                         f.write(f'title Minecraft {version}\n')
  1244.                         f.write(f'"{java_path}" -Xmx{settings["memory"]}G -Djava.library.path="{natives_path}" -Dorg.lwjgl.util.Debug=true -cp "{classpath}" net.minecraft.client.main.Main ')
  1245.                         f.write(f'--username {settings["username"]} --version {version} --gameDir . ')
  1246.                         f.write(f'--assetsDir "{assets_path}" --assetIndex {asset_index} ')
  1247.                         f.write(f'--accessToken null --uuid 0 --userType legacy\n')
  1248.                         f.write(f'pause\n')
  1249.                     else:
  1250.                         f.write(f'@echo off\n')
  1251.                         f.write(f'title Minecraft {version}\n')
  1252.                         f.write(f'"{java_path}" -Xmx{settings["memory"]}G -cp "{version}.jar" net.minecraft.LauncherFrame {settings["username"]} player_session_id\n')
  1253.                         f.write(f'pause\n')
  1254.             save_config()
  1255.             log_to_console(console, f"Instancja {version} zaktualizowana", "SUCCESS")
  1256.             refresh_instances()
  1257.  
  1258.         def show_download():
  1259.             frame = ttk.Frame(content_frame)
  1260.             frame.pack(fill="both", expand=True)
  1261.  
  1262.             ttk.Label(frame, text="Pobierz nową wersję Minecraft", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
  1263.  
  1264.             form_frame = ttk.Frame(frame)
  1265.             form_frame.pack(fill="both", expand=True)
  1266.  
  1267.             ttk.Label(form_frame, text="Filtry wersji:").grid(row=0, column=0, sticky="w", pady=(0, 5))
  1268.             filters_frame = ttk.Frame(form_frame)
  1269.             filters_frame.grid(row=0, column=1, sticky="w", pady=(0, 5))
  1270.            
  1271.             tk.Checkbutton(filters_frame, text="Snapshoty", variable=snapshots_var, bg=BG_COLOR, fg=FG_COLOR,
  1272.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1273.                            command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
  1274.             tk.Checkbutton(filters_frame, text="Release", variable=releases_var, bg=BG_COLOR, fg=FG_COLOR,
  1275.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1276.                            command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
  1277.             tk.Checkbutton(filters_frame, text="Alpha", variable=alpha_var, bg=BG_COLOR, fg=FG_COLOR,
  1278.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1279.                            command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
  1280.             tk.Checkbutton(filters_frame, text="Beta", variable=beta_var, bg=BG_COLOR, fg=FG_COLOR,
  1281.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1282.                            command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
  1283.  
  1284.             ttk.Label(form_frame, text="Wersja Minecrafta:").grid(row=1, column=0, sticky="w", pady=(10, 5))
  1285.             version_var = tk.StringVar()
  1286.             versions = get_versions()
  1287.             combo = ttk.Combobox(form_frame, textvariable=version_var, state="readonly", values=versions)
  1288.             combo.grid(row=1, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
  1289.             if versions and pending_version in versions:
  1290.                 combo.set(pending_version)
  1291.             elif versions:
  1292.                 combo.current(0)
  1293.             else:
  1294.                 messagebox.showwarning("Uwaga", "Włącz przynajmniej jeden filtr wersji (Snapshoty, Release, Alpha, Beta)!")
  1295.  
  1296.             console_frame = ttk.Frame(frame)
  1297.             console_frame.pack(fill="both", expand=True, pady=(15, 0))
  1298.  
  1299.             global console
  1300.             console = scrolledtext.ScrolledText(
  1301.                 console_frame, height=10, wrap=tk.WORD, bg=CONSOLE_BG, fg=CONSOLE_FG,
  1302.                 state="disabled", font=("Consolas", 9)
  1303.             )
  1304.             console.pack(fill="both", expand=True)
  1305.  
  1306.             btn_frame = ttk.Frame(frame)
  1307.             btn_frame.pack(fill="x", pady=(15, 0))
  1308.  
  1309.             ttk.Button(btn_frame, text="Pobierz", command=lambda: start_download({
  1310.                 "username": pending_instance_settings.get("username", username_var.get()),
  1311.                 "memory": pending_instance_settings.get("memory", memory_var.get()),
  1312.                 "java_path": pending_instance_settings.get("java_path", ""),
  1313.                 "shared_assets": pending_instance_settings.get("shared_assets", shared_assets_var.get()),
  1314.                 "shared_libraries": pending_instance_settings.get("shared_libraries", shared_libraries_var.get()),
  1315.                 "shared_natives": pending_instance_settings.get("shared_natives", shared_natives_var.get())
  1316.             }, version_var.get()) if version_var.get() else messagebox.showwarning("Uwaga", "Wybierz wersję!"), width=15).pack()
  1317.  
  1318.         def start_download(settings, version):
  1319.             global download_thread, download_active
  1320.             if download_active:
  1321.                 messagebox.showwarning("Uwaga", "Pobieranie już w toku!")
  1322.                 return
  1323.             if not version:
  1324.                 messagebox.showwarning("Uwaga", "Wybierz wersję!")
  1325.                 return
  1326.             download_active = True
  1327.             download_thread = threading.Thread(
  1328.                 target=download_version,
  1329.                 args=(version, settings, console),
  1330.                 daemon=True
  1331.             )
  1332.             download_thread.start()
  1333.  
  1334.         def show_settings():
  1335.             frame = ttk.Frame(content_frame)
  1336.             frame.pack(fill="both", expand=True)
  1337.  
  1338.             ttk.Label(frame, text="Ustawienia Launchera", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
  1339.  
  1340.             form_frame = ttk.Frame(frame)
  1341.             form_frame.pack(fill="both", expand=True)
  1342.  
  1343.             ttk.Label(form_frame, text="Domyślna nazwa użytkownika:").grid(row=0, column=0, sticky="w", pady=(0, 5))
  1344.             ttk.Entry(form_frame, textvariable=username_var).grid(row=0, column=1, sticky="ew", pady=(0, 5), padx=(5, 0))
  1345.  
  1346.             ttk.Label(form_frame, text="Domyślna pamięć RAM (GB):").grid(row=1, column=0, sticky="w", pady=(10, 5))
  1347.             memory_spin = tk.Spinbox(form_frame, from_=1, to=16, textvariable=memory_var, width=5,
  1348.                                      bg="#333333", fg=FG_COLOR, highlightthickness=0)
  1349.             memory_spin.grid(row=1, column=1, sticky="w", pady=(10, 5), padx=(5, 0))
  1350.  
  1351.             options_frame = ttk.Frame(form_frame)
  1352.             options_frame.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(10, 5))
  1353.  
  1354.             tk.Checkbutton(options_frame, text="Współdziel assets między instancjami", variable=shared_assets_var,
  1355.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1356.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1357.             tk.Checkbutton(options_frame, text="Współdziel biblioteki między instancjami", variable=shared_libraries_var,
  1358.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1359.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1360.             tk.Checkbutton(options_frame, text="Współdziel natywne biblioteki między instancjami", variable=shared_natives_var,
  1361.                            bg=BG_COLOR, fg=FG_COLOR, selectcolor=BG_COLOR, activebackground=BG_COLOR,
  1362.                            font=("Segoe UI", 9)).pack(anchor="w", pady=2)
  1363.  
  1364.             ttk.Label(form_frame, text="Domyślne filtry wersji:").grid(row=3, column=0, sticky="w", pady=(10, 5))
  1365.             filters_frame = ttk.Frame(form_frame)
  1366.             filters_frame.grid(row=3, column=1, sticky="w", pady=(10, 5))
  1367.  
  1368.             tk.Checkbutton(filters_frame, text="Snapshoty", variable=snapshots_var, bg=BG_COLOR, fg=FG_COLOR,
  1369.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1370.                            command=save_config).pack(anchor="w", pady=2)
  1371.             tk.Checkbutton(filters_frame, text="Release", variable=releases_var, bg=BG_COLOR, fg=FG_COLOR,
  1372.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1373.                            command=save_config).pack(anchor="w", pady=2)
  1374.             tk.Checkbutton(filters_frame, text="Alpha", variable=alpha_var, bg=BG_COLOR, fg=FG_COLOR,
  1375.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1376.                            command=save_config).pack(anchor="w", pady=2)
  1377.             tk.Checkbutton(filters_frame, text="Beta", variable=beta_var, bg=BG_COLOR, fg=FG_COLOR,
  1378.                            selectcolor=BG_COLOR, activebackground=BG_COLOR, font=("Segoe UI", 9),
  1379.                            command=save_config).pack(anchor="w", pady=2)
  1380.  
  1381.             btn_frame = ttk.Frame(frame)
  1382.             btn_frame.pack(fill="x", pady=(15, 0))
  1383.  
  1384.             ttk.Button(btn_frame, text="Zapisz ustawienia", command=save_config, width=20).pack()
  1385.  
  1386.         def show_console():
  1387.             frame = ttk.Frame(content_frame)
  1388.             frame.pack(fill="both", expand=True)
  1389.  
  1390.             ttk.Label(frame, text="Konsola Launchera", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
  1391.  
  1392.             console_frame = ttk.Frame(frame)
  1393.             console_frame.pack(fill="both", expand=True)
  1394.  
  1395.             global console
  1396.             console = scrolledtext.ScrolledText(
  1397.                 console_frame, height=20, wrap=tk.WORD, bg=CONSOLE_BG, fg=CONSOLE_FG,
  1398.                 state="disabled", font=("Consolas", 9)
  1399.             )
  1400.             console.pack(fill="both", expand=True)
  1401.  
  1402.             btn_frame = ttk.Frame(frame)
  1403.             btn_frame.pack(fill="x", pady=(10, 0))
  1404.  
  1405.             ttk.Button(btn_frame, text="Kopiuj", command=lambda: copy_console(console), width=15).pack(side="left", padx=5)
  1406.             ttk.Button(btn_frame, text="Wyczyść", command=lambda: clear_console(console), width=15).pack(side="left", padx=5)
  1407.  
  1408.         def refresh_version_combo(combo, version_var):
  1409.             versions = get_versions()
  1410.             combo['values'] = versions
  1411.             if versions:
  1412.                 version_var.set(versions[0])
  1413.             else:
  1414.                 version_var.set("")
  1415.                 messagebox.showwarning("Uwaga", "Włącz przynajmniej jeden filtr wersji (Snapshoty, Release, Alpha, Beta)!")
  1416.  
  1417.         print(f"{Fore.LIGHTCYAN_EX}{Fore.LIGHTCYAN_EX}Inicjalizacja zakończona, uruchamiam UI...")
  1418.         switch_tab("Instancje")
  1419.         root.mainloop()
  1420.     except Exception as e:
  1421.         print(f"{Fore.LIGHTCYAN_EX}CRASH: {e}")
  1422.         with open(os.path.join(LOGS_DIR, "crash.log"), "a", encoding="utf-8") as f:
  1423.             f.write(f"[{datetime.now()}] CRASH: {e}\n")
  1424.         raise
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement