Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import subprocess
- import sys
- import importlib.metadata
- import tkinter as tk
- from tkinter import ttk, messagebox, scrolledtext, filedialog, Menu
- import requests
- import os
- import json
- import threading
- import re
- import hashlib
- import pyperclip
- import zipfile
- from datetime import datetime
- import platform
- import webbrowser
- from packaging import version as pkg_version
- import humanize
- from colorama import init, Fore, Style
- from PIL import Image, ImageTk
- import shutil
- from uuid import uuid4
- import time
- # Inicjalizacja colorama
- init(autoreset=True)
- # Instalacja zależności
- def install_requirements():
- required_libraries = ['requests', 'pyperclip', 'packaging', 'humanize', 'colorama', 'pillow']
- for library in required_libraries:
- try:
- importlib.metadata.version(library)
- print(f"{Fore.GREEN}[OK] {library} już zainstalowane.")
- except importlib.metadata.PackageNotFoundError:
- try:
- subprocess.check_call([sys.executable, "-m", "pip", "install", library])
- print(f"{Fore.CYAN}[INFO] {library} zainstalowane.")
- except subprocess.CalledProcessError:
- print(f"{Fore.RED}[ERROR] Nie udało się zainstalować {library}")
- install_requirements()
- # Ścieżki
- BASE_DIR = os.path.join(os.getcwd(), "minecraft")
- CONFIG_FILE = os.path.join(BASE_DIR, "config.json")
- ASSETS_DIR = os.path.join(BASE_DIR, "assets")
- LIBRARIES_DIR = os.path.join(BASE_DIR, "libraries")
- NATIVES_DIR = os.path.join(BASE_DIR, "natives")
- LOGS_DIR = os.path.join(BASE_DIR, "logs")
- JAVA_DIR = os.path.join(BASE_DIR, "java")
- ICONS_DIR = os.path.join(BASE_DIR, "icons")
- MODPACKS_DIR = os.path.join(BASE_DIR, "modpacks")
- CACHE_DIR = os.path.join(BASE_DIR, "cache")
- # Kolory i style
- LIGHT_THEME = {
- "BG_COLOR": "#f0f0f0",
- "FG_COLOR": "#333333",
- "ACCENT_COLOR": "#2a9fd6",
- "CONSOLE_BG": "#ffffff",
- "CONSOLE_FG": "#006400",
- "BUTTON_BG": "#e0e0e0",
- "BUTTON_HOVER": "#d0d0d0",
- "SIDEBAR_BG": "#e8e8e8",
- "ACTIVE_TAB_COLOR": "#1e6f9f",
- "HOVER_TAB_COLOR": "#d8d8d8",
- "ENTRY_BG": "#ffffff",
- "TREEVIEW_BG": "#ffffff",
- "TREEVIEW_FG": "#333333",
- "TREEVIEW_SELECTED": "#2a9fd6"
- }
- DARK_THEME = {
- "BG_COLOR": "#1a1a1a",
- "FG_COLOR": "#ffffff",
- "ACCENT_COLOR": "#2a9fd6",
- "CONSOLE_BG": "#0d0d0d",
- "CONSOLE_FG": "#00ff00",
- "BUTTON_BG": "#333333",
- "BUTTON_HOVER": "#555555",
- "SIDEBAR_BG": "#222222",
- "ACTIVE_TAB_COLOR": "#1e6f9f",
- "HOVER_TAB_COLOR": "#3a3a3a",
- "ENTRY_BG": "#333333",
- "TREEVIEW_BG": "#333333",
- "TREEVIEW_FG": "#ffffff",
- "TREEVIEW_SELECTED": "#2a9fd6"
- }
- # Globalne zmienne
- pending_instance_settings = {}
- pending_version = ""
- download_thread = None
- download_active = False
- global_progress_bar = None
- global_status_label = None
- instances = {} # Globalna zmienna dla instancji
- java_versions_cache = {} # Globalna zmienna dla wersji Javy
- console = None # Globalna zmienna dla konsoli
- current_theme = "dark" # 'dark' lub 'light'
- theme_colors = DARK_THEME
- mod_cache = {} # Cache dla modów z CurseForge
- modpack_cache = {} # Cache dla modpacków
- mod_loader_versions = {"fabric": {}, "forge": {}, "neoforge": {}} # Cache wersji mod loaderów
- # Funkcje narzędziowe
- def log_to_console(console, message, level="INFO"):
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- try:
- console.config(state="normal")
- if level == "ERROR":
- console.tag_config("error", foreground="red")
- console.insert(tk.END, f"[{timestamp}] {level}: {message}\n", "error")
- elif level == "WARNING":
- console.tag_config("warning", foreground="yellow")
- console.insert(tk.END, f"[{timestamp}] {level}: {message}\n", "warning")
- elif level == "SUCCESS":
- console.tag_config("success", foreground="green")
- console.insert(tk.END, f"[{timestamp}] {level}: {message}\n", "success")
- else:
- console.insert(tk.END, f"[{timestamp}] {level}: {message}\n")
- console.see(tk.END)
- console.config(state="disabled")
- except:
- os.makedirs(LOGS_DIR, exist_ok=True)
- with open(os.path.join(LOGS_DIR, "error.log"), "a", encoding="utf-8") as f:
- f.write(f"[{timestamp}] {level}: {message}\n")
- def verify_sha1(file_path, expected_sha1):
- sha1 = hashlib.sha1()
- with open(file_path, "rb") as f:
- for chunk in iter(lambda: f.read(4096), b""):
- sha1.update(chunk)
- return sha1.hexdigest() == expected_sha1
- def save_config():
- config = {
- "default_settings": {
- "username": username_var.get(),
- "memory": memory_var.get(),
- "shared_assets": shared_assets_var.get(),
- "shared_libraries": shared_libraries_var.get(),
- "shared_natives": shared_natives_var.get(),
- "theme": current_theme
- },
- "version_filters": {
- "snapshots": snapshots_var.get(),
- "releases": releases_var.get(),
- "alpha": alpha_var.get(),
- "beta": beta_var.get()
- },
- "instances": instances,
- "java_versions": java_versions_cache,
- "mod_cache": mod_cache,
- "modpack_cache": modpack_cache,
- "mod_loader_versions": mod_loader_versions
- }
- os.makedirs(BASE_DIR, exist_ok=True)
- with open(CONFIG_FILE, "w", encoding="utf-8") as f:
- json.dump(config, f, indent=4)
- def load_config():
- global instances, java_versions_cache, username_var, memory_var, shared_assets_var, shared_libraries_var, shared_natives_var
- global snapshots_var, releases_var, alpha_var, beta_var, current_theme, theme_colors, mod_cache, modpack_cache, mod_loader_versions
- if os.path.exists(CONFIG_FILE):
- try:
- with open(CONFIG_FILE, "r", encoding="utf-8") as f:
- config = json.load(f)
- # Aktualizuj globalne zmienne zamiast tworzyć nowe
- if "default_settings" in config:
- default_settings = config["default_settings"]
- username_var.set(default_settings.get("username", "Player"))
- memory_var.set(default_settings.get("memory", "2"))
- shared_assets_var.set(default_settings.get("shared_assets", True))
- shared_libraries_var.set(default_settings.get("shared_libraries", True))
- shared_natives_var.set(default_settings.get("shared_natives", True))
- current_theme = default_settings.get("theme", "dark")
- theme_colors = DARK_THEME if current_theme == "dark" else LIGHT_THEME
- if "version_filters" in config:
- version_filters = config["version_filters"]
- snapshots_var.set(version_filters.get("snapshots", True))
- releases_var.set(version_filters.get("releases", True))
- alpha_var.set(version_filters.get("alpha", False))
- beta_var.set(version_filters.get("beta", False))
- if "instances" in config:
- instances = config["instances"]
- if "java_versions" in config:
- java_versions_cache = config["java_versions"]
- if "mod_cache" in config:
- mod_cache = config["mod_cache"]
- if "modpack_cache" in config:
- modpack_cache = config["modpack_cache"]
- if "mod_loader_versions" in config:
- mod_loader_versions = config["mod_loader_versions"]
- return instances, java_versions_cache
- except Exception as e:
- log_to_console(console, f"Błąd wczytywania konfiguracji: {e}", "ERROR")
- # Przywróć domyślne wartości w przypadku błędu
- instances = {}
- java_versions_cache = {}
- return instances, java_versions_cache
- # Domyślne wartości jeśli plik nie istnieje
- instances = {}
- java_versions_cache = {}
- return instances, java_versions_cache
- def apply_theme():
- global theme_colors
- theme_colors = DARK_THEME if current_theme == "dark" else LIGHT_THEME
- style = ttk.Style()
- style.theme_use('clam')
- style.configure(".", background=theme_colors["BG_COLOR"], foreground=theme_colors["FG_COLOR"])
- style.configure("TFrame", background=theme_colors["BG_COLOR"])
- style.configure("TLabel", background=theme_colors["BG_COLOR"], foreground=theme_colors["FG_COLOR"], font=("Segoe UI", 10))
- style.configure("TButton", background=theme_colors["BUTTON_BG"], foreground=theme_colors["FG_COLOR"], font=("Segoe UI", 10), borderwidth=0)
- style.map("TButton",
- background=[('active', theme_colors["BUTTON_HOVER"]), ('pressed', theme_colors["BUTTON_HOVER"])],
- foreground=[('active', theme_colors["FG_COLOR"]), ('pressed', theme_colors["FG_COLOR"])])
- style.configure("TEntry", fieldbackground=theme_colors["ENTRY_BG"], foreground=theme_colors["FG_COLOR"], insertcolor=theme_colors["FG_COLOR"], borderwidth=1)
- style.configure("TCombobox", fieldbackground=theme_colors["ENTRY_BG"], foreground=theme_colors["FG_COLOR"], selectbackground=theme_colors["ACCENT_COLOR"])
- style.configure("Horizontal.TProgressbar", troughcolor="#333333", background=theme_colors["ACCENT_COLOR"], thickness=10)
- style.configure("Treeview", background=theme_colors["TREEVIEW_BG"], fieldbackground=theme_colors["TREEVIEW_BG"], foreground=theme_colors["TREEVIEW_FG"], rowheight=25)
- style.map("Treeview", background=[('selected', theme_colors["TREEVIEW_SELECTED"])])
- style.configure("Treeview.Heading", background=theme_colors["SIDEBAR_BG"], foreground=theme_colors["FG_COLOR"], font=("Segoe UI", 10, "bold"))
- # Aktualizuj kolory dla istniejących widgetów
- for widget in root.winfo_children():
- update_widget_colors(widget)
- def update_widget_colors(widget):
- if isinstance(widget, tk.Menu):
- widget.config(bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"])
- elif isinstance(widget, (tk.Frame, ttk.Frame)):
- widget.config(bg=theme_colors["BG_COLOR"])
- elif isinstance(widget, (tk.Label, ttk.Label)):
- widget.config(bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"])
- elif isinstance(widget, (tk.Button, ttk.Button)):
- widget.config(bg=theme_colors["BUTTON_BG"], fg=theme_colors["FG_COLOR"],
- activebackground=theme_colors["BUTTON_HOVER"], activeforeground=theme_colors["FG_COLOR"])
- elif isinstance(widget, tk.Entry):
- widget.config(bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"], insertbackground=theme_colors["FG_COLOR"])
- elif isinstance(widget, tk.Text):
- widget.config(bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"])
- elif isinstance(widget, tk.Listbox):
- widget.config(bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"])
- # Rekurencyjnie aktualizuj dzieci widgetu
- for child in widget.winfo_children():
- update_widget_colors(child)
- def toggle_theme():
- global current_theme
- current_theme = "light" if current_theme == "dark" else "dark"
- save_config()
- apply_theme()
- def get_versions():
- try:
- url = "https://launchermeta.mojang.com/mc/game/version_manifest.json"
- resp = requests.get(url, timeout=10)
- resp.raise_for_status()
- manifest = resp.json()
- versions = []
- allowed_types = []
- if snapshots_var.get():
- allowed_types.append("snapshot")
- if releases_var.get():
- allowed_types.append("release")
- if alpha_var.get():
- allowed_types.append("old_alpha")
- if beta_var.get():
- allowed_types.append("old_beta")
- for v in manifest["versions"]:
- if v["type"] in allowed_types:
- versions.append(v["id"])
- log_to_console(None, f"Pobrano {len(versions)} wersji (filtry: {allowed_types})", "INFO")
- return sorted(versions, key=lambda x: x, reverse=True)
- except Exception as e:
- log_to_console(None, f"Nie udało się pobrać wersji: {e}", "ERROR")
- return []
- def get_version_info(version):
- try:
- url = "https://launchermeta.mojang.com/mc/game/version_manifest.json"
- resp = requests.get(url, timeout=10)
- resp.raise_for_status()
- manifest = resp.json()
- for v in manifest["versions"]:
- if v["id"] == version:
- version_url = v["url"]
- return requests.get(version_url, timeout=10).json()
- return None
- except Exception as e:
- log_to_console(None, f"Nie udało się pobrać info o wersji: {e}", "ERROR")
- return None
- def download_java(java_version, console):
- try:
- system = platform.system().lower()
- arch = "x64"
- base_url = "https://api.adoptium.net/v3/binary/latest/"
- java_map = {
- "1.8": "8",
- "16": "16",
- "17": "17",
- "21": "21"
- }
- feature_version = java_map.get(java_version, java_version)
- url = f"{base_url}{feature_version}/ga/{system}/{arch}/jdk/hotspot/normal/eclipse"
- log_to_console(console, f"Pobieranie Javy {java_version} z {url}", "INFO")
- resp = requests.get(url, stream=True, timeout=10)
- resp.raise_for_status()
- total_size = int(resp.headers.get('content-length', 0))
- java_dir = os.path.join(JAVA_DIR, f"jdk-{feature_version}")
- os.makedirs(java_dir, exist_ok=True)
- zip_path = os.path.join(java_dir, f"jdk-{feature_version}.zip")
- downloaded_size = 0
- with open(zip_path, "wb") as f:
- for chunk in resp.iter_content(chunk_size=8192):
- if chunk:
- f.write(chunk)
- downloaded_size += len(chunk)
- if total_size > 0 and global_progress_bar:
- update_progress(global_progress_bar, global_status_label,
- (downloaded_size/total_size)*100, total_size-downloaded_size, "java", 1, 1)
- log_to_console(console, f"Rozpakowywanie Javy do {java_dir}", "INFO")
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
- zip_ref.extractall(java_dir)
- os.remove(zip_path)
- for root, _, files in os.walk(java_dir):
- if "java.exe" in files:
- java_path = os.path.join(root, "java.exe")
- version = get_java_version(java_path)
- if version:
- log_to_console(console, f"Pobrano Javę: {java_path} (wersja: {version})", "SUCCESS")
- return java_path, version
- log_to_console(console, "Nie znaleziono java.exe w pobranym archiwum!", "ERROR")
- return None, None
- except Exception as e:
- log_to_console(console, f"Błąd pobierania Javy {java_version}: {e}", "ERROR")
- return None, None
- def find_java(required_version=None):
- possible_paths = []
- log_to_console(None, f"Szukam Javy {required_version or 'dowolnej'}...", "INFO")
- if os.path.exists(JAVA_DIR):
- for java_folder in os.listdir(JAVA_DIR):
- java_path = os.path.join(JAVA_DIR, java_folder, "bin", "java.exe")
- if os.path.exists(java_path):
- version = get_java_version(java_path)
- if version and (not required_version or check_java_version(version, required_version)):
- possible_paths.append((java_path, version))
- log_to_console(None, f"Znaleziono Javę w {java_path} (wersja: {version})", "INFO")
- java_home = os.environ.get("JAVA_HOME")
- if java_home:
- java_path = os.path.join(java_home, "bin", "java.exe" if platform.system() == "Windows" else "java")
- if os.path.exists(java_path):
- version = get_java_version(java_path)
- if version and (not required_version or check_java_version(version, required_version)):
- possible_paths.append((java_path, version))
- log_to_console(None, f"Znaleziono Javę w JAVA_HOME: {java_path} (wersja: {version})", "INFO")
- for base in [os.environ.get('ProgramFiles'), os.environ.get('ProgramFiles(x86)'), "C:\\Program Files\\Java", "C:\\Program Files (x86)\\Java"]:
- if base:
- java_dir = os.path.join(base, "Java")
- if os.path.isdir(java_dir):
- for item in os.listdir(java_dir):
- java_path = os.path.join(java_dir, item, "bin", "java.exe")
- if os.path.exists(java_path):
- version = get_java_version(java_path)
- if version and (not required_version or check_java_version(version, required_version)):
- possible_paths.append((java_path, version))
- log_to_console(None, f"Znaleziono Javę w {java_path} (wersja: {version})", "INFO")
- try:
- out = subprocess.check_output(["where", "java"], stderr=subprocess.DEVNULL).decode()
- for line in out.splitlines():
- line = line.strip()
- if os.path.exists(line) and line not in [p[0] for p in possible_paths]:
- version = get_java_version(line)
- if version and (not required_version or check_java_version(version, required_version)):
- possible_paths.append((line, version))
- log_to_console(None, f"Znaleziono Javę w {line} (wersja: {version})", "INFO")
- except:
- pass
- if not possible_paths:
- log_to_console(None, f"Nie znaleziono Javy {required_version}! Spróbuj pobrać automatycznie.", "ERROR")
- return possible_paths
- def get_java_version(java_path):
- try:
- result = subprocess.run([java_path, "-version"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
- version_line = result.stderr.split('\n')[0]
- version_match = re.search(r'version "([^"]+)"', version_line)
- if version_match:
- version = version_match.group(1)
- is_64bit = "64-Bit" in result.stderr
- log_to_console(None, f"Java {java_path}: {version}, 64-bit: {is_64bit}", "INFO")
- return version if is_64bit else None
- except Exception as e:
- log_to_console(None, f"Błąd sprawdzania wersji Javy {java_path}: {e}", "ERROR")
- return None
- def check_java_version(installed_version, required_version):
- try:
- installed = installed_version.split("_")[0]
- if required_version == "1.8":
- return installed.startswith("1.8.")
- if required_version == "16":
- return installed.startswith("16.")
- if required_version == "17":
- return installed.startswith("17.")
- if required_version == "21":
- return installed.startswith("21.")
- return pkg_version.parse(installed) >= pkg_version.parse(required_version)
- except:
- return False
- def is_new_launcher(version):
- try:
- ver_match = re.match(r'1\.(\d+)', version)
- if ver_match:
- ver_float = float(ver_match.group(1))
- return ver_float >= 6
- return True
- except:
- return True
- def get_required_java(version, version_info):
- if version_info and "javaVersion" in version_info:
- major_version = version_info["javaVersion"].get("majorVersion", 8)
- return str(major_version)
- try:
- if re.match(r'(\d+w\d+[a-z])', version): # Snapshoty
- return "1.8" # Większość starych snapshotów używa Javy 8
- ver = pkg_version.parse(version)
- if ver >= pkg_version.parse("1.21"):
- return "21"
- if ver >= pkg_version.parse("1.17"):
- return "17" # 1.18 też używa Javy 17
- return "1.8"
- except pkg_version.InvalidVersion:
- return "1.8" # Domyślnie dla niestandardowych wersji
- def download_file(url, path, progress_callback, expected_sha1=None, console=None):
- try:
- resp = requests.get(url, stream=True, timeout=10)
- resp.raise_for_status()
- total_size = int(resp.headers.get('content-length', 0))
- downloaded_size = 0
- os.makedirs(os.path.dirname(path), exist_ok=True)
- with open(path, "wb") as f:
- for chunk in resp.iter_content(chunk_size=8192):
- if chunk:
- f.write(chunk)
- downloaded_size += len(chunk)
- if total_size > 0 and progress_callback:
- progress_callback(downloaded_size, total_size)
- if expected_sha1 and not verify_sha1(path, expected_sha1):
- log_to_console(console, f"Błąd SHA1 dla {path}", "ERROR")
- return False
- return True
- except Exception as e:
- log_to_console(console, f"Błąd pobierania {path}: {e}", "ERROR")
- return False
- def download_assets(version, version_info, version_dir, shared_assets, console):
- try:
- asset_index = version_info.get("assetIndex", {})
- asset_url = asset_index.get("url")
- asset_id = asset_index.get("id")
- if not asset_url:
- log_to_console(console, "Brak assetIndex dla tej wersji", "WARNING")
- return True
- resp = requests.get(asset_url, timeout=10)
- resp.raise_for_status()
- assets_data = resp.json()
- assets_dir = ASSETS_DIR if shared_assets else os.path.join(version_dir, "assets")
- objects_dir = os.path.join(assets_dir, "objects")
- indexes_dir = os.path.join(assets_dir, "indexes")
- os.makedirs(objects_dir, exist_ok=True)
- os.makedirs(indexes_dir, exist_ok=True)
- index_file = os.path.join(indexes_dir, f"{asset_id}.json")
- log_to_console(console, f"Zapisywanie indeksu assetów: {index_file}", "INFO")
- with open(index_file, "w", encoding="utf-8") as f:
- json.dump(assets_data, f)
- total_assets = len(assets_data["objects"])
- total_size = sum(asset["size"] for asset in assets_data["objects"].values())
- downloaded_size = 0
- for i, (asset_name, asset_info) in enumerate(assets_data["objects"].items()):
- asset_hash = asset_info["hash"]
- asset_size = asset_info["size"]
- asset_subpath = f"{asset_hash[:2]}/{asset_hash}"
- asset_path = os.path.join(objects_dir, asset_subpath)
- asset_url = f"https://resources.download.minecraft.net/{asset_subpath}"
- if not os.path.exists(asset_path):
- log_to_console(console, f"Pobieranie assetu: {asset_name}", "INFO")
- if not download_file(
- asset_url, asset_path,
- lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "assets", i+1, total_assets),
- expected_sha1=asset_hash, console=console
- ):
- return False
- downloaded_size += asset_size
- update_progress(global_progress_bar, global_status_label, (downloaded_size/total_size)*100,
- total_size-downloaded_size, "assets", i+1, total_assets)
- return True
- except Exception as e:
- log_to_console(console, f"Błąd pobierania assetów: {e}", "ERROR")
- return False
- def download_natives(version, version_info, version_dir, shared_natives, console):
- try:
- libraries = version_info.get("libraries", [])
- natives_dir = NATIVES_DIR if shared_natives else os.path.join(version_dir, "natives")
- os.makedirs(natives_dir, exist_ok=True)
- system = platform.system().lower()
- natives_classifier = "natives-windows" if system == "windows" else "natives-osx" if system == "darwin" else "natives-linux"
- total_size = 0
- natives_to_download = []
- for lib in libraries:
- if "downloads" in lib and "classifiers" in lib["downloads"]:
- classifiers = lib["downloads"]["classifiers"]
- if natives_classifier in classifiers:
- artifact = classifiers[natives_classifier]
- natives_to_download.append(artifact)
- total_size += artifact.get("size", 0)
- log_to_console(console, f"Pobieranie {len(natives_to_download)} natywnych bibliotek", "INFO")
- downloaded_size = 0
- for i, artifact in enumerate(natives_to_download):
- native_url = artifact["url"]
- native_sha1 = artifact["sha1"]
- native_size = artifact.get("size", 0)
- native_path = os.path.join(version_dir, f"temp_native_{i}.jar")
- log_to_console(console, f"Pobieranie natywnej biblioteki: {native_url}", "INFO")
- if not download_file(
- native_url, native_path,
- lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "natives", i+1, len(natives_to_download)),
- expected_sha1=native_sha1, console=console
- ):
- return False
- with zipfile.ZipFile(native_path, 'r') as zip_ref:
- zip_ref.extractall(natives_dir)
- os.remove(native_path)
- downloaded_size += native_size
- update_progress(global_progress_bar, global_status_label, (downloaded_size/total_size)*100,
- total_size-downloaded_size, "natives", i+1, len(natives_to_download))
- return True
- except Exception as e:
- log_to_console(console, f"Błąd pobierania natywnych bibliotek: {e}", "ERROR")
- return False
- def verify_instance(version, console):
- instance = instances.get(version)
- if not instance:
- log_to_console(console, f"Instancja {version} nie istnieje!", "ERROR")
- messagebox.showerror("Błąd", f"Instancja {version} nie istnieje!")
- return False
- version_dir = instance["path"]
- settings = instance["settings"]
- info = get_version_info(version)
- if not info:
- log_to_console(console, f"Nie udało się pobrać info o wersji {version}", "ERROR")
- return False
- log_to_console(console, f"Weryfikacja instancji {version}...", "INFO")
- all_valid = True
- # Sprawdź client.jar
- client_path = os.path.join(version_dir, f"{version}.jar")
- client_sha1 = info["downloads"]["client"]["sha1"]
- if not os.path.exists(client_path):
- log_to_console(console, f"Brak client.jar dla {version}", "ERROR")
- all_valid = False
- elif not verify_sha1(client_path, client_sha1):
- log_to_console(console, f"Błąd SHA1 dla client.jar ({version})", "ERROR")
- all_valid = False
- else:
- log_to_console(console, f"client.jar OK", "INFO")
- # Sprawdź biblioteki
- libraries_dir = LIBRARIES_DIR if settings.get("shared_libraries", True) else os.path.join(version_dir, "libraries")
- for lib in info.get("libraries", []):
- if "downloads" in lib and "artifact" in lib["downloads"]:
- artifact = lib["downloads"]["artifact"]
- lib_path = artifact["path"].replace("/", os.sep)
- full_lib_path = os.path.join(libraries_dir, lib_path)
- lib_sha1 = artifact["sha1"]
- if not os.path.exists(full_lib_path):
- log_to_console(console, f"Brak biblioteki {lib_path}", "ERROR")
- all_valid = False
- elif not verify_sha1(full_lib_path, lib_sha1):
- log_to_console(console, f"Błąd SHA1 dla biblioteki {lib_path}", "ERROR")
- all_valid = False
- else:
- log_to_console(console, f"Biblioteka {lib_path} OK", "INFO")
- # Sprawdź natywne biblioteki
- natives_dir = NATIVES_DIR if settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
- system = platform.system().lower()
- natives_classifier = "natives-windows" if system == "windows" else "natives-osx" if system == "darwin" else "natives-linux"
- for lib in info.get("libraries", []):
- if "downloads" in lib and "classifiers" in lib["downloads"]:
- classifiers = lib["downloads"]["classifiers"]
- if natives_classifier in classifiers:
- artifact = classifiers[natives_classifier]
- native_path = os.path.join(version_dir, f"temp_native_check.jar")
- if not download_file(artifact["url"], native_path, None, artifact["sha1"], console):
- log_to_console(console, f"Błąd weryfikacji natywnej biblioteki {artifact['url']}", "ERROR")
- all_valid = False
- else:
- log_to_console(console, f"Natywna biblioteka {artifact['url']} OK", "INFO")
- if os.path.exists(native_path):
- os.remove(native_path)
- # Sprawdź assets
- assets_dir = ASSETS_DIR if settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
- asset_index = info.get("assetIndex", {})
- if asset_index.get("url"):
- resp = requests.get(asset_index["url"], timeout=10)
- if resp.status_code == 200:
- assets_data = resp.json()
- for asset_name, asset_info in assets_data["objects"].items():
- asset_hash = asset_info["hash"]
- asset_subpath = f"{asset_hash[:2]}/{asset_hash}"
- asset_path = os.path.join(assets_dir, "objects", asset_subpath)
- if not os.path.exists(asset_path):
- log_to_console(console, f"Brak assetu {asset_name}", "ERROR")
- all_valid = False
- elif not verify_sha1(asset_path, asset_hash):
- log_to_console(console, f"Błąd SHA1 dla assetu {asset_name}", "ERROR")
- all_valid = False
- else:
- log_to_console(console, f"Asset {asset_name} OK", "INFO")
- else:
- log_to_console(console, f"Nie udało się pobrać indeksu assetów", "ERROR")
- all_valid = False
- # Sprawdź i zregeneruj start.bat
- bat_path = os.path.join(version_dir, "start.bat")
- if not os.path.exists(bat_path):
- log_to_console(console, f"Brak start.bat dla {version}, regeneruję...", "WARNING")
- all_valid = False
- else:
- log_to_console(console, f"start.bat OK, regeneruję dla pewności...", "INFO")
- # Regeneracja start.bat
- classpath = f"{version}.jar"
- for lib in info.get("libraries", []):
- if "downloads" in lib and "artifact" in lib["downloads"]:
- lib_path = lib["downloads"]["artifact"]["path"].replace("/", os.sep)
- classpath += f";{os.path.join(libraries_dir, lib_path)}"
- assets_path = ASSETS_DIR if settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
- natives_path = NATIVES_DIR if settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
- java_path = instance.get("java_path", "")
- asset_index = info.get("assetIndex", {}).get("id", version)
- with open(bat_path, "w", encoding="utf-8") as f:
- if is_new_launcher(version):
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version}\n')
- 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 ')
- f.write(f'--username {settings["username"]} --version {version} --gameDir . ')
- f.write(f'--assetsDir "{assets_path}" --assetIndex {asset_index} ')
- f.write(f'--accessToken null --uuid 0 --userType legacy\n')
- f.write(f'pause\n')
- else:
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version}\n')
- f.write(f'"{java_path}" -Xmx{settings["memory"]}G -cp "{version}.jar" net.minecraft.LauncherFrame {settings["username"]} player_session_id\n')
- f.write(f'pause\n')
- log_to_console(console, f"start.bat zregenerowany dla {version}", "SUCCESS")
- if all_valid:
- log_to_console(console, f"Instancja {version} zweryfikowana poprawnie!", "SUCCESS")
- messagebox.showinfo("Sukces", f"Instancja {version} jest kompletna!")
- else:
- log_to_console(console, f"Weryfikacja instancji {version} nieudana, ale start.bat zregenerowany!", "WARNING")
- messagebox.showwarning("Uwaga", f"Instancja {version} miała problemy, ale start.bat został zregenerowany!")
- return all_valid
- def update_progress(progress_bar, status_label, progress, remaining, stage, current, total):
- if progress_bar and status_label:
- progress_bar["value"] = progress
- remaining_str = humanize.naturalsize(remaining) if remaining else "0 B"
- status_label.config(text=f"[{stage}] Postęp: {progress:.1f}% | Pozostało: {remaining_str} | {current}/{total}")
- root.update()
- def download_version(version, instance_settings, console):
- global download_active
- download_active = True
- info = get_version_info(version)
- if not info:
- download_active = False
- return
- java_major_version = get_required_java(version, info)
- log_to_console(console, f"Wymagana Java: {java_major_version}", "INFO")
- java_paths = find_java(java_major_version)
- if not java_paths:
- if messagebox.askyesno("Brak Javy", f"Nie znaleziono Javy {java_major_version}. Pobrać automatycznie?"):
- java_path, java_version = download_java(java_major_version, console)
- if java_path:
- java_paths = [(java_path, java_version)]
- else:
- log_to_console(console, f"Nie udało się pobrać Javy {java_major_version}!", "ERROR")
- messagebox.showerror("Błąd", f"Nie udało się pobrać Javy {java_major_version}!")
- download_active = False
- return
- else:
- log_to_console(console, f"Nie znaleziono Javy {java_major_version}! Wskaż ręcznie.", "ERROR")
- messagebox.showerror("Błąd", f"Nie znaleziono Javy {java_major_version}! Pobierz z adoptium.net.")
- download_active = False
- return
- java_path, java_version = java_paths[0]
- if instance_settings.get("java_path"):
- java_path = instance_settings["java_path"]
- java_version = get_java_version(java_path) or java_version
- log_to_console(console, f"Używam Javy: {java_path} (wersja {java_version})", "INFO")
- version_dir = os.path.join(BASE_DIR, "instances", version)
- os.makedirs(version_dir, exist_ok=True)
- libraries_dir = LIBRARIES_DIR if instance_settings.get("shared_libraries", True) else os.path.join(version_dir, "libraries")
- os.makedirs(libraries_dir, exist_ok=True)
- # Etap 1: Client.jar
- update_progress(global_progress_bar, global_status_label, 0, 0, "client.jar", 1, 1)
- global_status_label.config(text="[1/5] Pobieranie client.jar...")
- client_url = info["downloads"]["client"]["url"]
- client_sha1 = info["downloads"]["client"]["sha1"]
- client_path = os.path.join(version_dir, f"{version}.jar")
- log_to_console(console, f"Pobieranie client.jar dla {version}", "INFO")
- if not download_file(
- client_url, client_path,
- lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "client.jar", 1, 1),
- expected_sha1=client_sha1, console=console
- ):
- download_active = False
- return
- # Etap 2: Biblioteki
- update_progress(global_progress_bar, global_status_label, 0, 0, "biblioteki", 0, 0)
- global_status_label.config(text="[2/5] Pobieranie bibliotek...")
- libs = info.get("libraries", [])
- total_libs_size = 0
- libs_to_download = []
- for lib in libs:
- if "downloads" in lib and "artifact" in lib["downloads"]:
- artifact = lib["downloads"]["artifact"]
- lib_path = artifact["path"].replace("/", os.sep)
- full_lib_path = os.path.join(libraries_dir, lib_path)
- if not os.path.exists(full_lib_path):
- libs_to_download.append((lib, artifact))
- total_libs_size += artifact.get("size", 0)
- log_to_console(console, f"Pobieranie {len(libs_to_download)} bibliotek", "INFO")
- downloaded_libs_size = 0
- for i, (lib, artifact) in enumerate(libs_to_download):
- lib_path = artifact["path"].replace("/", os.sep)
- full_lib_path = os.path.join(libraries_dir, lib_path)
- lib_url = artifact["url"]
- lib_sha1 = artifact["sha1"]
- lib_size = artifact.get("size", 0)
- log_to_console(console, f"Pobieranie biblioteki: {lib_path}", "INFO")
- if not download_file(
- lib_url, full_lib_path,
- lambda d, t: update_progress(global_progress_bar, global_status_label, (d/t)*100, t-d, "biblioteki", i+1, len(libs_to_download)),
- expected_sha1=lib_sha1, console=console
- ):
- download_active = False
- downloaded_libs_size += lib_size
- update_progress(global_progress_bar, global_status_label, (downloaded_libs_size/total_libs_size)*100,
- total_libs_size-downloaded_libs_size, "biblioteki", i+1, len(libs_to_download))
- # Etap 3: Natives
- update_progress(global_progress_bar, global_status_label, 0, 0, "natives", 0, 0)
- global_status_label.config(text="[3/5] Pobieranie natywnych bibliotek...")
- if not download_natives(version, info, version_dir, instance_settings.get("shared_natives", True), console):
- download_active = False
- return
- # Etap 4: Assets
- update_progress(global_progress_bar, global_status_label, 0, 0, "assets", 0, 0)
- global_status_label.config(text="[4/5] Pobieranie assetów...")
- if not download_assets(version, info, version_dir, instance_settings.get("shared_assets", True), console):
- download_active = False
- return
- # Etap 5: Generowanie start.bat
- update_progress(global_progress_bar, global_status_label, 0, 0, "start.bat", 1, 1)
- global_status_label.config(text="[5/5] Generowanie start.bat...")
- bat_path = os.path.join(version_dir, "start.bat")
- classpath = f"{version}.jar"
- for lib in libs:
- if "downloads" in lib and "artifact" in lib["downloads"]:
- lib_path = lib["downloads"]["artifact"]["path"].replace("/", os.sep)
- classpath += f";{os.path.join(libraries_dir, lib_path)}"
- assets_path = ASSETS_DIR if instance_settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
- natives_path = NATIVES_DIR if instance_settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
- username = instance_settings.get("username", "Player").strip() or "Player"
- memory = instance_settings.get("memory", "2")
- asset_index = info.get("assetIndex", {}).get("id", version)
- with open(bat_path, "w", encoding="utf-8") as f:
- if is_new_launcher(version):
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version}\n')
- 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 ')
- f.write(f'--username {username} --version {version} --gameDir . ')
- f.write(f'--assetsDir "{assets_path}" --assetIndex {asset_index} ')
- f.write(f'--accessToken null --uuid 0 --userType legacy\n')
- f.write(f'pause\n')
- else:
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version}\n')
- f.write(f'"{java_path}" -Xmx{memory}G -cp "{version}.jar" net.minecraft.LauncherFrame {username} player_session_id\n')
- f.write(f'pause\n')
- instances[version] = {
- "path": version_dir,
- "java_path": java_path,
- "java_version": java_version,
- "required_java": java_major_version,
- "settings": instance_settings,
- "ready": True,
- "timestamp": datetime.now().isoformat(),
- "mod_loader": None,
- "mods": [],
- "notes": "",
- "play_time": 0
- }
- save_config()
- update_progress(global_progress_bar, global_status_label, 100, 0, "gotowe", 1, 1)
- global_status_label.config(text="Gotowe!")
- log_to_console(console, f"Instancja {version} pobrana!", "SUCCESS")
- messagebox.showinfo("Sukces", f"Instancja {version} gotowa!")
- refresh_instances()
- download_active = False
- def run_game(version):
- instance = instances.get(version)
- if not instance or not instance.get("ready"):
- messagebox.showerror("Błąd", f"Instancja {version} nie jest gotowa!")
- return
- # Zaktualizuj czas grania
- start_time = time.time()
- bat_path = os.path.join(instance["path"], "start.bat")
- if os.path.exists(bat_path):
- try:
- subprocess.Popen(bat_path, cwd=instance["path"], shell=True)
- log_to_console(console, f"Uruchomiono Minecraft {version}", "SUCCESS")
- # Po zamknięciu gry zaktualizuj czas grania
- def update_play_time():
- end_time = time.time()
- play_time = end_time - start_time
- instances[version]["play_time"] += play_time
- save_config()
- # Uruchom wątek do monitorowania procesu gry
- def monitor_process(process):
- process.wait()
- update_play_time()
- process = subprocess.Popen(bat_path, cwd=instance["path"], shell=True)
- threading.Thread(target=monitor_process, args=(process,), daemon=True).start()
- except Exception as e:
- log_to_console(console, f"Nie udało się uruchomić gry: {e}", "ERROR")
- messagebox.showerror("Błąd", f"Nie udało się uruchomić gry: {e}")
- else:
- messagebox.showerror("Błąd", f"Brak start.bat dla {version}!")
- def delete_instance(version):
- if messagebox.askyesno("Potwierdź", f"Na pewno usunąć instancję {version}?"):
- instance = instances.get(version)
- if instance:
- import shutil
- try:
- shutil.rmtree(instance["path"])
- del instances[version]
- save_config()
- log_to_console(console, f"Instancja {version} usunięta", "SUCCESS")
- refresh_instances()
- except Exception as e:
- log_to_console(console, f"Błąd usuwania instancji {version}: {e}", "ERROR")
- def copy_console(console):
- console.config(state="normal")
- content = console.get("1.0", tk.END).strip()
- pyperclip.copy(content)
- console.config(state="disabled")
- messagebox.showinfo("Sukces", "Konsola skopiowana!")
- def clear_console(console):
- console.config(state="normal")
- console.delete("1.0", tk.END)
- console.config(state="disabled")
- def get_mod_loader_versions(loader, mc_version=None):
- try:
- if loader == "fabric":
- if mc_version:
- url = f"https://meta.fabricmc.net/v2/versions/loader/{mc_version}"
- resp = requests.get(url, timeout=10)
- resp.raise_for_status()
- return resp.json()
- else:
- url = "https://meta.fabricmc.net/v2/versions/loader"
- resp = requests.get(url, timeout=10)
- resp.raise_for_status()
- return resp.json()
- elif loader == "forge":
- url = "https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json"
- resp = requests.get(url, timeout=10)
- resp.raise_for_status()
- data = resp.json()
- versions = []
- for mc_ver, builds in data["promos"].items():
- if "-" in mc_ver: # Pomijamy wersje typu "1.16.5-latest"
- continue
- versions.append({
- "mc_version": mc_ver,
- "loader_version": builds.get("latest", ""),
- "recommended": builds.get("recommended", "")
- })
- return versions
- elif loader == "neoforge":
- url = "https://maven.neoforged.net/api/maven/latest/version/releases/net%2Fneoforged%2Fneoforge"
- resp = requests.get(url, timeout=10)
- resp.raise_for_status()
- return resp.json()
- else:
- return []
- except Exception as e:
- log_to_console(console, f"Błąd pobierania wersji {loader}: {e}", "ERROR")
- return []
- def install_mod_loader(version, loader, loader_version, console):
- instance = instances.get(version)
- if not instance:
- log_to_console(console, f"Instancja {version} nie istnieje!", "ERROR")
- return False
- version_dir = instance["path"]
- java_path = instance.get("java_path", "")
- try:
- if loader == "fabric":
- # Pobierz installer Fabric
- installer_url = f"https://meta.fabricmc.net/v2/versions/loader/{version}/{loader_version}/profile/json"
- resp = requests.get(installer_url, timeout=10)
- resp.raise_for_status()
- profile = resp.json()
- # Zapisz profil jako fabric_profile.json
- profile_path = os.path.join(version_dir, "fabric_profile.json")
- with open(profile_path, "w", encoding="utf-8") as f:
- json.dump(profile, f)
- # Zaktualizuj start.bat
- with open(os.path.join(version_dir, "start.bat"), "w", encoding="utf-8") as f:
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version} (Fabric)\n')
- f.write(f'"{java_path}" -Xmx{instance["settings"]["memory"]}G -Dfabric.gameJarPath={version}.jar -jar fabric-loader-{loader_version}.jar\n')
- f.write(f'pause\n')
- instances[version]["mod_loader"] = {"type": "fabric", "version": loader_version}
- save_config()
- return True
- elif loader == "forge" or loader == "neoforge":
- # Pobierz installer Forge/NeoForge
- installer_url = ""
- if loader == "forge":
- installer_url = f"https://maven.minecraftforge.net/net/minecraftforge/forge/{version}-{loader_version}/forge-{version}-{loader_version}-installer.jar"
- else:
- installer_url = f"https://maven.neoforged.net/releases/net/neoforged/forge/{version}-{loader_version}/forge-{version}-{loader_version}-installer.jar"
- installer_path = os.path.join(version_dir, f"{loader}_installer.jar")
- if not download_file(installer_url, installer_path, None, None, console):
- return False
- # Uruchom installer
- log_to_console(console, f"Uruchamianie installera {loader}...", "INFO")
- process = subprocess.Popen([java_path, "-jar", installer_path, "--installServer"], cwd=version_dir)
- process.wait()
- # Zaktualizuj start.bat
- with open(os.path.join(version_dir, "start.bat"), "w", encoding="utf-8") as f:
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version} ({loader.capitalize()})\n')
- f.write(f'"{java_path}" -Xmx{instance["settings"]["memory"]}G -jar {loader}-{version}-{loader_version}.jar\n')
- f.write(f'pause\n')
- instances[version]["mod_loader"] = {"type": loader, "version": loader_version}
- save_config()
- return True
- return False
- except Exception as e:
- log_to_console(console, f"Błąd instalacji {loader}: {e}", "ERROR")
- return False
- def search_mods(query, mc_version=None, loader=None):
- try:
- url = "https://api.curseforge.com/v1/mods/search"
- params = {
- "gameId": 432, # Minecraft
- "searchFilter": query,
- "pageSize": 50
- }
- if mc_version:
- params["gameVersion"] = mc_version
- if loader:
- params["modLoaderType"] = 1 if loader == "forge" else 4 if loader == "fabric" else 0
- headers = {
- "x-api-key": "$2a$10$6BpZQZ8Z3Z3Z3Z3Z3Z3Z3u" # Publiczny klucz API (może wymagać aktualizacji)
- }
- resp = requests.get(url, params=params, headers=headers, timeout=10)
- resp.raise_for_status()
- mods = resp.json()["data"]
- # Cache modów
- for mod in mods:
- mod_cache[mod["id"]] = mod
- return mods
- except Exception as e:
- log_to_console(console, f"Błąd wyszukiwania modów: {e}", "ERROR")
- return []
- def download_mod(mod_id, version, console):
- try:
- instance = instances.get(version)
- if not instance:
- log_to_console(console, f"Instancja {version} nie istnieje!", "ERROR")
- return False
- mod_dir = os.path.join(instance["path"], "mods")
- os.makedirs(mod_dir, exist_ok=True)
- # Pobierz informacje o modzie
- mod = mod_cache.get(mod_id)
- if not mod:
- url = f"https://api.curseforge.com/v1/mods/{mod_id}"
- headers = {
- "x-api-key": "$2a$10$6BpZQZ8Z3Z3Z3Z3Z3Z3Z3u" # Publiczny klucz API
- }
- resp = requests.get(url, headers=headers, timeout=10)
- resp.raise_for_status()
- mod = resp.json()["data"]
- mod_cache[mod_id] = mod
- # Znajdź odpowiednią wersję moda
- for file in mod["latestFiles"]:
- if instance["mod_loader"] and instance["mod_loader"]["type"]:
- if instance["mod_loader"]["type"] == "forge" and not any("forge" in f.lower() for f in file["gameVersions"]):
- continue
- if instance["mod_loader"]["type"] == "fabric" and not any("fabric" in f.lower() for f in file["gameVersions"]):
- continue
- if version in file["gameVersions"]:
- mod_url = file["downloadUrl"]
- if not mod_url:
- mod_url = f"https://edge.forgecdn.net/files/{file['id']//{file['fileName']}"
- mod_path = os.path.join(mod_dir, file["fileName"])
- if download_file(mod_url, mod_path, None, None, console):
- instances[version]["mods"].append({
- "id": mod_id,
- "name": mod["name"],
- "file": file["fileName"],
- "version": file["displayName"]
- })
- save_config()
- return True
- log_to_console(console, f"Nie znaleziono odpowiedniej wersji moda {mod['name']} dla {version}", "ERROR")
- return False
- except Exception as e:
- log_to_console(console, f"Błąd pobierania moda: {e}", "ERROR")
- return False
- def install_modpack(modpack_path, version=None):
- try:
- # Rozpakuj modpack
- with zipfile.ZipFile(modpack_path, 'r') as zip_ref:
- zip_ref.extractall(MODPACKS_DIR)
- # Znajdź manifest
- manifest_path = os.path.join(MODPACKS_DIR, "manifest.json")
- if not os.path.exists(manifest_path):
- log_to_console(console, "Nie znaleziono manifest.json w paczce modów!", "ERROR")
- return False
- with open(manifest_path, "r", encoding="utf-8") as f:
- manifest = json.load(f)
- # Sprawdź wersję Minecrafta
- mc_version = manifest["minecraft"]["version"]
- if version and version != mc_version:
- log_to_console(console, f"Wersja Minecrafta w paczce ({mc_version}) nie zgadza się z wybraną ({version})!", "ERROR")
- return False
- # Sprawdź loader modów
- loader = None
- loader_version = None
- if "forge" in manifest["minecraft"]["modLoaders"][0]["id"]:
- loader = "forge"
- loader_version = manifest["minecraft"]["modLoaders"][0]["id"].split("-")[-1]
- elif "fabric" in manifest["minecraft"]["modLoaders"][0]["id"]:
- loader = "fabric"
- loader_version = manifest["minecraft"]["modLoaders"][0]["id"].split("-")[-1]
- # Utwórz instancję jeśli nie istnieje
- if version not in instances:
- settings = {
- "username": username_var.get(),
- "memory": memory_var.get(),
- "shared_assets": shared_assets_var.get(),
- "shared_libraries": shared_libraries_var.get(),
- "shared_natives": shared_natives_var.get()
- }
- download_version(mc_version, settings, console)
- # Zainstaluj loader jeśli potrzebny
- if loader and not instances[version].get("mod_loader"):
- install_mod_loader(mc_version, loader, loader_version, console)
- # Pobierz mody
- mod_dir = os.path.join(instances[version]["path"], "mods")
- os.makedirs(mod_dir, exist_ok=True)
- for mod in manifest["files"]:
- mod_id = mod["projectID"]
- file_id = mod["fileID"]
- # Pobierz informacje o pliku moda
- url = f"https://api.curseforge.com/v1/mods/{mod_id}/files/{file_id}"
- headers = {
- "x-api-key": "$2a$10$6BpZQZ8Z3Z3Z3Z3Z3Z3Z3u" # Publiczny klucz API
- }
- resp = requests.get(url, headers=headers, timeout=10)
- resp.raise_for_status()
- file_info = resp.json()["data"]
- # Pobierz mod
- mod_url = file_info["downloadUrl"]
- if not mod_url:
- mod_url = f"https://edge.forgecdn.net/files/{file_id//1000}/{file_id % 1000}/{file_info['fileName']}"
- mod_path = os.path.join(mod_dir, file_info["fileName"])
- if download_file(mod_url, mod_path, None, None, console):
- instances[version]["mods"].append({
- "id": mod_id,
- "name": file_info["displayName"],
- "file": file_info["fileName"],
- "version": file_info["fileName"]
- })
- save_config()
- return True
- except Exception as e:
- log_to_console(console, f"Błąd instalacji paczki modów: {e}", "ERROR")
- return False
- def export_instance(version):
- instance = instances.get(version)
- if not instance:
- messagebox.showerror("Błąd", f"Instancja {version} nie istnieje!")
- return
- export_path = filedialog.asksaveasfilename(
- defaultextension=".zip",
- filetypes=[("ZIP Archive", "*.zip")],
- initialfile=f"minecraft_{version}.zip"
- )
- if not export_path:
- return
- try:
- with zipfile.ZipFile(export_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
- for root, _, files in os.walk(instance["path"]):
- for file in files:
- file_path = os.path.join(root, file)
- arcname = os.path.relpath(file_path, instance["path"])
- zipf.write(file_path, arcname)
- log_to_console(console, f"Eksportowano instancję {version} do {export_path}", "SUCCESS")
- messagebox.showinfo("Sukces", f"Pomyślnie wyeksportowano instancję do {export_path}")
- except Exception as e:
- log_to_console(console, f"Błąd eksportu instancji: {e}", "ERROR")
- messagebox.showerror("Błąd", f"Nie udało się wyeksportować instancji: {e}")
- def duplicate_instance(version):
- instance = instances.get(version)
- if not instance:
- messagebox.showerror("Błąd", f"Instancja {version} nie istnieje!")
- return
- new_version = f"{version}_copy"
- while new_version in instances:
- new_version = f"{new_version}_copy"
- new_path = os.path.join(BASE_DIR, "instances", new_version)
- try:
- shutil.copytree(instance["path"], new_path)
- # Zaktualizuj start.bat z nową nazwą
- with open(os.path.join(new_path, "start.bat"), "r", encoding="utf-8") as f:
- content = f.read()
- content = content.replace(version, new_version)
- with open(os.path.join(new_path, "start.bat"), "w", encoding="utf-8") as f:
- f.write(content)
- # Dodaj nową instancję
- instances[new_version] = {
- "path": new_path,
- "java_path": instance["java_path"],
- "java_version": instance["java_version"],
- "required_java": instance["required_java"],
- "settings": instance["settings"].copy(),
- "ready": True,
- "timestamp": datetime.now().isoformat(),
- "mod_loader": instance["mod_loader"].copy() if instance["mod_loader"] else None,
- "mods": [mod.copy() for mod in instance["mods"]] if "mods" in instance else [],
- "notes": instance.get("notes", ""),
- "play_time": 0
- }
- save_config()
- log_to_console(console, f"Duplikowano instancję {version} jako {new_version}", "SUCCESS")
- refresh_instances()
- except Exception as e:
- log_to_console(console, f"Błąd duplikowania instancji: {e}", "ERROR")
- messagebox.showerror("Błąd", f"Nie udało się zduplikować instancji: {e}")
- def show_instance_context_menu(event, tree):
- item = tree.identify_row(event.y)
- if not item:
- return
- version = tree.item(item)["values"][0]
- instance = instances.get(version)
- if not instance:
- return
- menu = tk.Menu(root, tearoff=0, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"])
- menu.add_command(label=f"Instancja: {version}", state="disabled")
- menu.add_separator()
- menu.add_command(label="Uruchom", command=lambda: run_game(version))
- menu.add_command(label="Edytuj", command=lambda: edit_instance(version))
- menu.add_command(label="Zmień nazwę", command=lambda: rename_instance(version))
- menu.add_command(label="Eksportuj do .zip", command=lambda: export_instance(version))
- menu.add_command(label="Otwórz folder", command=lambda: os.startfile(instance["path"]))
- menu.add_command(label="Duplikuj", command=lambda: duplicate_instance(version))
- if "notes" in instance and instance["notes"]:
- menu.add_separator()
- menu.add_command(label=f"Notatka: {instance['notes'][:30]}...", state="disabled")
- menu.tk_popup(event.x_root, event.y_root)
- def rename_instance(old_version):
- instance = instances.get(old_version)
- if not instance:
- messagebox.showerror("Błąd", f"Instancja {old_version} nie istnieje!")
- return
- new_version = simpledialog.askstring("Zmień nazwę", "Nowa nazwa instancji:", initialvalue=old_version)
- if not new_version or new_version == old_version:
- return
- if new_version in instances:
- messagebox.showerror("Błąd", f"Instancja o nazwie {new_version} już istnieje!")
- return
- try:
- new_path = os.path.join(BASE_DIR, "instances", new_version)
- os.rename(instance["path"], new_path)
- # Zaktualizuj start.bat z nową nazwą
- with open(os.path.join(new_path, "start.bat"), "r", encoding="utf-8") as f:
- content = f.read()
- content = content.replace(old_version, new_version)
- with open(os.path.join(new_path, "start.bat"), "w", encoding="utf-8") as f:
- f.write(content)
- # Zaktualizuj instancję
- instances[new_version] = instance
- instances[new_version]["path"] = new_path
- del instances[old_version]
- save_config()
- log_to_console(console, f"Zmieniono nazwę instancji z {old_version} na {new_version}", "SUCCESS")
- refresh_instances()
- except Exception as e:
- log_to_console(console, f"Błąd zmiany nazwy instancji: {e}", "ERROR")
- messagebox.showerror("Błąd", f"Nie udało się zmienić nazwy instancji: {e}")
- def show_mod_loader_installer(version):
- instance = instances.get(version)
- if not instance:
- messagebox.showerror("Błąd", f"Instancja {version} nie istnieje!")
- return
- window = tk.Toplevel(root)
- window.title(f"Instalacja mod loadera dla {version}")
- window.geometry("600x400")
- window.configure(bg=theme_colors["BG_COLOR"])
- frame = ttk.Frame(window)
- frame.pack(fill="both", expand=True, padx=10, pady=10)
- ttk.Label(frame, text=f"Wybierz mod loader dla {version}", font=("Segoe UI", 12, "bold")).pack(pady=(0, 10))
- loader_var = tk.StringVar(value="fabric")
- loaders_frame = ttk.Frame(frame)
- loaders_frame.pack(fill="x", pady=(0, 10))
- ttk.Radiobutton(loaders_frame, text="Fabric", variable=loader_var, value="fabric").pack(side="left", padx=5)
- ttk.Radiobutton(loaders_frame, text="Forge", variable=loader_var, value="forge").pack(side="left", padx=5)
- ttk.Radiobutton(loaders_frame, text="NeoForge", variable=loader_var, value="neoforge").pack(side="left", padx=5)
- versions_frame = ttk.Frame(frame)
- versions_frame.pack(fill="both", expand=True)
- ttk.Label(versions_frame, text="Dostępne wersje:").pack(anchor="w")
- versions_tree = ttk.Treeview(versions_frame, columns=("version", "type"), show="headings", height=10)
- versions_tree.heading("version", text="Wersja")
- versions_tree.heading("type", text="Typ")
- versions_tree.column("version", width=150)
- versions_tree.column("type", width=100)
- scrollbar = ttk.Scrollbar(versions_frame, orient="vertical", command=versions_tree.yview)
- scrollbar.pack(side="right", fill="y")
- versions_tree.configure(yscrollcommand=scrollbar.set)
- versions_tree.pack(fill="both", expand=True)
- def refresh_versions():
- loader = loader_var.get()
- versions_tree.delete(*versions_tree.get_children())
- if loader not in mod_loader_versions or not mod_loader_versions[loader]:
- log_to_console(console, f"Pobieranie wersji {loader}...", "INFO")
- versions = get_mod_loader_versions(loader, version)
- mod_loader_versions[loader] = versions
- save_config()
- else:
- versions = mod_loader_versions[loader]
- if loader == "fabric":
- for ver in versions:
- if isinstance(ver, dict) and ver["loader"]:
- versions_tree.insert("", "end", values=(ver["loader"]["version"], "Loader"))
- elif isinstance(ver, dict) and ver["version"]:
- versions_tree.insert("", "end", values=(ver["version"], "API"))
- elif loader == "forge":
- for ver in versions:
- if ver["mc_version"] == version:
- versions_tree.insert("", "end", values=(ver["loader_version"], "Latest"))
- if ver["recommended"]:
- versions_tree.insert("", "end", values=(ver["recommended"], "Recommended"))
- elif loader == "neoforge":
- versions_tree.insert("", "end", values=(versions, "Latest"))
- refresh_versions()
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(10, 0))
- ttk.Button(btn_frame, text="Odśwież", command=refresh_versions).pack(side="left", padx=5)
- ttk.Button(btn_frame, text="Zainstaluj", command=lambda: install_selected_version()).pack(side="right", padx=5)
- def install_selected_version():
- selected = versions_tree.selection()
- if not selected:
- messagebox.showwarning("Uwaga", "Wybierz wersję do instalacji!")
- return
- loader_version = versions_tree.item(selected[0])["values"][0]
- loader = loader_var.get()
- if install_mod_loader(version, loader, loader_version, console):
- messagebox.showinfo("Sukces", f"Pomyślnie zainstalowano {loader.capitalize()} {loader_version}!")
- window.destroy()
- refresh_instances()
- else:
- messagebox.showerror("Błąd", f"Nie udało się zainstalować {loader.capitalize()}!")
- def show_mod_browser():
- window = tk.Toplevel(root)
- window.title("Przeglądarka modów")
- window.geometry("800x600")
- window.configure(bg=theme_colors["BG_COLOR"])
- frame = ttk.Frame(window)
- frame.pack(fill="both", expand=True, padx=10, pady=10)
- search_frame = ttk.Frame(frame)
- search_frame.pack(fill="x", pady=(0, 10))
- ttk.Label(search_frame, text="Wyszukaj mody:").pack(side="left", padx=5)
- search_var = tk.StringVar()
- search_entry = ttk.Entry(search_frame, textvariable=search_var)
- search_entry.pack(side="left", fill="x", expand=True, padx=5)
- def perform_search():
- query = search_var.get()
- if not query:
- return
- mc_version = version_combo.get()
- loader = loader_combo.get()
- mods = search_mods(query, mc_version, loader)
- mods_tree.delete(*mods_tree.get_children())
- for mod in mods:
- mods_tree.insert("", "end", values=(
- mod["name"],
- mod["summary"][:50] + "..." if mod["summary"] else "",
- ", ".join(mod["authors"][:3]) if mod["authors"] else "",
- mod["downloadCount"]
- ))
- ttk.Button(search_frame, text="Szukaj", command=perform_search).pack(side="left", padx=5)
- filters_frame = ttk.Frame(frame)
- filters_frame.pack(fill="x", pady=(0, 10))
- ttk.Label(filters_frame, text="Wersja MC:").pack(side="left", padx=5)
- version_combo = ttk.Combobox(filters_frame, values=list(instances.keys()), state="readonly")
- version_combo.pack(side="left", padx=5)
- if instances:
- version_combo.current(0)
- ttk.Label(filters_frame, text="Mod loader:").pack(side="left", padx=5)
- loader_combo = ttk.Combobox(filters_frame, values=["Fabric", "Forge", "Any"], state="readonly")
- loader_combo.pack(side="left", padx=5)
- loader_combo.current(2)
- mods_tree = ttk.Treeview(frame, columns=("name", "description", "authors", "downloads"), show="headings", height=15)
- mods_tree.heading("name", text="Nazwa")
- mods_tree.heading("description", text="Opis")
- mods_tree.heading("authors", text="Autorzy")
- mods_tree.heading("downloads", text="Pobrań")
- mods_tree.column("name", width=200)
- mods_tree.column("description", width=250)
- mods_tree.column("authors", width=150)
- mods_tree.column("downloads", width=80)
- scrollbar = ttk.Scrollbar(frame, orient="vertical", command=mods_tree.yview)
- scrollbar.pack(side="right", fill="y")
- mods_tree.configure(yscrollcommand=scrollbar.set)
- mods_tree.pack(fill="both", expand=True)
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(10, 0))
- def install_selected_mod():
- selected = mods_tree.selection()
- if not selected:
- messagebox.showwarning("Uwaga", "Wybierz mod do instalacji!")
- return
- mod_name = mods_tree.item(selected[0])["values"][0]
- mc_version = version_combo.get()
- # Znajdź ID moda w cache
- mod_id = None
- for id, mod in mod_cache.items():
- if mod["name"] == mod_name:
- mod_id = id
- break
- if not mod_id:
- messagebox.showerror("Błąd", "Nie znaleziono wybranego moda!")
- return
- if download_mod(mod_id, mc_version, console):
- messagebox.showinfo("Sukces", f"Pomyślnie zainstalowano mod {mod_name}!")
- else:
- messagebox.showerror("Błąd", f"Nie udało się zainstalować moda {mod_name}!")
- ttk.Button(btn_frame, text="Zainstaluj", command=install_selected_mod).pack(side="right", padx=5)
- ttk.Button(btn_frame, text="Pokaż szczegóły", command=lambda: show_mod_details()).pack(side="right", padx=5)
- def show_mod_details():
- selected = mods_tree.selection()
- if not selected:
- return
- mod_name = mods_tree.item(selected[0])["values"][0]
- # Znajdź mod w cache
- mod = None
- for m in mod_cache.values():
- if m["name"] == mod_name:
- mod = m
- break
- if not mod:
- return
- details_window = tk.Toplevel(window)
- details_window.title(f"Szczegóły: {mod_name}")
- details_window.geometry("600x400")
- details_window.configure(bg=theme_colors["BG_COLOR"])
- frame = ttk.Frame(details_window)
- frame.pack(fill="both", expand=True, padx=10, pady=10)
- ttk.Label(frame, text=mod_name, font=("Segoe UI", 14, "bold")).pack(pady=(0, 10))
- desc_frame = ttk.Frame(frame)
- desc_frame.pack(fill="x", pady=(0, 10))
- ttk.Label(desc_frame, text="Opis:", font=("Segoe UI", 10, "bold")).pack(anchor="w")
- desc_text = scrolledtext.ScrolledText(
- desc_frame, wrap=tk.WORD, height=10,
- bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"]
- )
- desc_text.insert(tk.END, mod["summary"] or "Brak opisu")
- desc_text.config(state="disabled")
- desc_text.pack(fill="both", expand=True)
- info_frame = ttk.Frame(frame)
- info_frame.pack(fill="x", pady=(0, 10))
- ttk.Label(info_frame, text=f"Autorzy: {', '.join([a['name'] for a in mod['authors']]) if mod['authors'] else 'Brak informacji'}").pack(anchor="w")
- ttk.Label(info_frame, text=f"Pobrań: {humanize.intcomma(mod['downloadCount'])}").pack(anchor="w")
- ttk.Label(info_frame, text=f"Data wydania: {mod['dateReleased']}").pack(anchor="w")
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(10, 0))
- ttk.Button(btn_frame, text="Strona projektu", command=lambda: webbrowser.open(mod["links"]["websiteUrl"])).pack(side="left", padx=5)
- def show_modpack_installer():
- window = tk.Toplevel(root)
- window.title("Instalator paczek modów")
- window.geometry("600x400")
- window.configure(bg=theme_colors["BG_COLOR"])
- frame = ttk.Frame(window)
- frame.pack(fill="both", expand=True, padx=10, pady=10)
- ttk.Label(frame, text="Wybierz paczkę modów do instalacji", font=("Segoe UI", 12, "bold")).pack(pady=(0, 10))
- options_frame = ttk.Frame(frame)
- options_frame.pack(fill="x", pady=(0, 10))
- ttk.Radiobutton(options_frame, text="Z pliku (.zip)", value="file", variable=tk.StringVar(value="file")).pack(anchor="w")
- ttk.Radiobutton(options_frame, text="Z URL", value="url", variable=tk.StringVar(value="file")).pack(anchor="w")
- file_frame = ttk.Frame(frame)
- file_frame.pack(fill="x", pady=(0, 10))
- ttk.Button(file_frame, text="Wybierz plik...", command=lambda: select_modpack_file()).pack(side="left", padx=5)
- file_path_var = tk.StringVar()
- ttk.Label(file_frame, textvariable=file_path_var).pack(side="left", padx=5)
- url_frame = ttk.Frame(frame)
- url_frame.pack(fill="x", pady=(0, 10))
- ttk.Label(url_frame, text="URL:").pack(side="left", padx=5)
- url_entry = ttk.Entry(url_frame)
- url_entry.pack(side="left", fill="x", expand=True, padx=5)
- version_frame = ttk.Frame(frame)
- version_frame.pack(fill="x", pady=(0, 10))
- ttk.Label(version_frame, text="Dla wersji MC:").pack(side="left", padx=5)
- version_combo = ttk.Combobox(version_frame, values=list(instances.keys()), state="readonly")
- version_combo.pack(side="left", fill="x", expand=True, padx=5)
- if instances:
- version_combo.current(0)
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(10, 0))
- def select_modpack_file():
- file_path = filedialog.askopenfilename(
- filetypes=[("ZIP Archive", "*.zip")],
- title="Wybierz paczkę modów"
- )
- if file_path:
- file_path_var.set(file_path)
- def install_modpack():
- mc_version = version_combo.get()
- if not mc_version:
- messagebox.showwarning("Uwaga", "Wybierz wersję Minecrafta!")
- return
- if install_modpack(file_path_var.get() if file_path_var.get() else url_entry.get(), mc_version):
- messagebox.showinfo("Sukces", "Pomyślnie zainstalowano paczkę modów!")
- window.destroy()
- refresh_instances()
- else:
- messagebox.showerror("Błąd", "Nie udało się zainstalować paczki modów!")
- ttk.Button(btn_frame, text="Zainstaluj", command=install_modpack).pack(side="right", padx=5)
- def show_instance_stats(version):
- instance = instances.get(version)
- if not instance:
- messagebox.showerror("Błąd", f"Instancja {version} nie istnieje!")
- return
- window = tk.Toplevel(root)
- window.title(f"Statystyki instancji {version}")
- window.geometry("400x300")
- window.configure(bg=theme_colors["BG_COLOR"])
- frame = ttk.Frame(window)
- frame.pack(fill="both", expand=True, padx=10, pady=10)
- ttk.Label(frame, text=f"Statystyki: {version}", font=("Segoe UI", 14, "bold")).pack(pady=(0, 10))
- stats_frame = ttk.Frame(frame)
- stats_frame.pack(fill="both", expand=True)
- # Oblicz rozmiar instancji
- total_size = 0
- for dirpath, _, filenames in os.walk(instance["path"]):
- for f in filenames:
- fp = os.path.join(dirpath, f)
- total_size += os.path.getsize(fp)
- # Oblicz ilość modów
- mods_count = len(instance.get("mods", []))
- # Czas grania
- play_time = instance.get("play_time", 0)
- play_time_str = f"{int(play_time // 3600)}h {int((play_time % 3600) // 60)}m"
- ttk.Label(stats_frame, text=f"Wersja Minecrafta: {version}", font=("Segoe UI", 10)).pack(anchor="w", pady=2)
- ttk.Label(stats_frame, text=f"Mod loader: {instance['mod_loader']['type'].capitalize() + ' ' + instance['mod_loader']['version'] if instance['mod_loader'] else 'Brak'}", font=("Segoe UI", 10)).pack(anchor="w", pady=2)
- ttk.Label(stats_frame, text=f"Ilość modów: {mods_count}", font=("Segoe UI", 10)).pack(anchor="w", pady=2)
- ttk.Label(stats_frame, text=f"Rozmiar instancji: {humanize.naturalsize(total_size)}", font=("Segoe UI", 10)).pack(anchor="w", pady=2)
- ttk.Label(stats_frame, text=f"Wersja Javy: {instance['java_version']}", font=("Segoe UI", 10)).pack(anchor="w", pady=2)
- ttk.Label(stats_frame, text=f"Data utworzenia: {datetime.fromisoformat(instance['timestamp']).strftime('%Y-%m-%d %H:%M')}", font=("Segoe UI", 10)).pack(anchor="w", pady=2)
- ttk.Label(stats_frame, text=f"Czas grania: {play_time_str}", font=("Segoe UI", 10)).pack(anchor="w", pady=2)
- if instance.get("notes", ""):
- notes_frame = ttk.Frame(frame)
- notes_frame.pack(fill="x", pady=(10, 0))
- ttk.Label(notes_frame, text="Notatki:", font=("Segoe UI", 10, "bold")).pack(anchor="w")
- notes_text = scrolledtext.ScrolledText(
- notes_frame, wrap=tk.WORD, height=4,
- bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"]
- )
- notes_text.insert(tk.END, instance["notes"])
- notes_text.config(state="disabled")
- notes_text.pack(fill="both", expand=True)
- def add_instance_note(version):
- instance = instances.get(version)
- if not instance:
- messagebox.showerror("Błąd", f"Instancja {version} nie istnieje!")
- return
- note = simpledialog.askstring("Dodaj notatkę", "Wpisz notatkę dla tej instancji:", initialvalue=instance.get("notes", ""))
- if note is not None:
- instance["notes"] = note
- save_config()
- log_to_console(console, f"Dodano notatkę do instancji {version}", "SUCCESS")
- if __name__ == "__main__":
- try:
- print("Inicjalizacja Tkinter...")
- root = tk.Tk()
- print("Tkinter zainicjalizowany.")
- root.title("Minecraft Launcher by Paffcio")
- root.geometry("1000x750")
- root.minsize(900, 650)
- print("Ładowanie konfiguracji...")
- # Inicjalizacja zmiennych Tkinter przed ładowaniem konfiguracji
- username_var = tk.StringVar(value="Player")
- memory_var = tk.StringVar(value="2")
- shared_assets_var = tk.BooleanVar(value=True)
- shared_libraries_var = tk.BooleanVar(value=True)
- shared_natives_var = tk.BooleanVar(value=True)
- snapshots_var = tk.BooleanVar(value=True)
- releases_var = tk.BooleanVar(value=True)
- alpha_var = tk.BooleanVar(value=False)
- beta_var = tk.BooleanVar(value=False)
- current_tab = tk.StringVar(value="Instancje")
- # Teraz ładujemy konfigurację, która zaktualizuje zmienne
- instances, java_versions_cache = load_config()
- print("Konfiguracja wczytana.")
- print("Inicjalizacja UI...")
- apply_theme()
- progress_frame = ttk.Frame(root)
- progress_frame.pack(side="bottom", fill="x", padx=10, pady=5)
- global_progress_bar = ttk.Progressbar(progress_frame, length=400, mode="determinate", style="Horizontal.TProgressbar")
- global_progress_bar.pack(fill="x")
- global_status_label = ttk.Label(progress_frame, text="Gotowe do działania!", foreground=theme_colors["FG_COLOR"], background=theme_colors["BG_COLOR"], font=("Segoe UI", 9))
- global_status_label.pack()
- sidebar = tk.Frame(root, bg=theme_colors["SIDEBAR_BG"], width=220)
- sidebar.pack(side="left", fill="y", padx=0, pady=0)
- logo_frame = tk.Frame(sidebar, bg=theme_colors["SIDEBAR_BG"])
- logo_frame.pack(fill="x", pady=(10, 20))
- try:
- print("Ładowanie logo...")
- logo_img = Image.open(os.path.join(ICONS_DIR, "logo.png")) if os.path.exists(ICONS_DIR) else None
- if logo_img:
- logo_img = logo_img.resize((180, 60), Image.LANCZOS)
- logo_photo = ImageTk.PhotoImage(logo_img)
- logo_label = tk.Label(logo_frame, image=logo_photo, bg=theme_colors["SIDEBAR_BG"])
- logo_label.image = logo_photo
- logo_label.pack(pady=5)
- print("Logo załadowane.")
- except Exception as e:
- print(f"Błąd ładowania logo: {e}")
- tabs = [
- ("Instancje", "📋"),
- ("Pobieranie", "⬇"),
- ("Mod Loadery", "⚙"),
- ("Przeglądarka modów", "🌐"),
- ("Paczki modów", "📦"),
- ("Ustawienia", "⚙"),
- ("Konsola", "📜")
- ]
- tab_buttons = {}
- for tab_name, icon in tabs:
- print(f"Tworzenie przycisku {tab_name}...")
- btn = tk.Button(
- sidebar, text=f" {icon} {tab_name}", bg=theme_colors["SIDEBAR_BG"], fg=theme_colors["FG_COLOR"],
- activebackground=theme_colors["ACTIVE_TAB_COLOR"], activeforeground=theme_colors["FG_COLOR"],
- font=("Segoe UI", 11), relief="flat", anchor="w", padx=15, pady=10,
- command=lambda t=tab_name: switch_tab(t)
- )
- btn.pack(fill="x", pady=0)
- tab_buttons[tab_name] = btn
- btn.bind("<Enter>", lambda e, b=btn: b.config(bg=theme_colors["HOVER_TAB_COLOR"]) if current_tab.get() != tab_name else None)
- btn.bind("<Leave>", lambda e, b=btn: b.config(bg=theme_colors["SIDEBAR_BG"]) if current_tab.get() != tab_name else None)
- print(f"Przycisk {tab_name} utworzony.")
- content_frame = ttk.Frame(root)
- content_frame.pack(side="left", fill="both", expand=True, padx=10, pady=10)
- def switch_tab(tab_name):
- global download_active
- if download_active and tab_name != "Pobieranie":
- if not messagebox.askyesno("Ostrzeżenie", "Pobieranie w toku! Zmiana zakładki przerwie proces. Kontynuować?"):
- return
- download_active = False
- current_tab.set(tab_name)
- for name, btn in tab_buttons.items():
- if name == tab_name:
- btn.config(bg=theme_colors["ACTIVE_TAB_COLOR"], relief="sunken")
- else:
- btn.config(bg=theme_colors["SIDEBAR_BG"], relief="flat")
- for widget in content_frame.winfo_children():
- widget.destroy()
- if tab_name == "Instancje":
- show_instances()
- elif tab_name == "Pobieranie":
- show_download()
- elif tab_name == "Mod Loadery":
- show_mod_loader_tab()
- elif tab_name == "Przeglądarka modów":
- show_mod_browser()
- elif tab_name == "Paczki modów":
- show_modpack_installer()
- elif tab_name == "Ustawienia":
- show_settings()
- elif tab_name == "Konsola":
- show_console()
- def show_instances():
- frame = ttk.Frame(content_frame)
- frame.pack(fill="both", expand=True)
- header_frame = ttk.Frame(frame)
- header_frame.pack(fill="x", pady=(0, 10))
- ttk.Label(header_frame, text="Twoje instancje Minecraft", font=("Segoe UI", 14, "bold")).pack(side="left")
- btn_frame = ttk.Frame(header_frame)
- btn_frame.pack(side="right")
- refresh_btn = ttk.Button(btn_frame, text="Odśwież", command=refresh_instances, width=10)
- refresh_btn.pack(side="left", padx=5)
- new_btn = ttk.Button(btn_frame, text="Nowa instancja", command=create_instance, width=15)
- new_btn.pack(side="left", padx=5)
- tree_frame = ttk.Frame(frame)
- tree_frame.pack(fill="both", expand=True)
- columns = ("version", "java", "status", "date", "mod_loader", "mods")
- tree = ttk.Treeview(
- tree_frame, columns=columns, show="headings", selectmode="browse",
- style="Treeview"
- )
- tree.heading("version", text="Wersja", anchor="w")
- tree.heading("java", text="Java", anchor="w")
- tree.heading("status", text="Status", anchor="w")
- tree.heading("date", text="Data utworzenia", anchor="w")
- tree.heading("mod_loader", text="Mod Loader", anchor="w")
- tree.heading("mods", text="Mody", anchor="w")
- tree.column("version", width=150, anchor="w")
- tree.column("java", width=100, anchor="w")
- tree.column("status", width=80, anchor="w")
- tree.column("date", width=120, anchor="w")
- tree.column("mod_loader", width=120, anchor="w")
- tree.column("mods", width=80, anchor="w")
- scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=tree.yview)
- scrollbar.pack(side="right", fill="y")
- tree.configure(yscrollcommand=scrollbar.set)
- tree.pack(fill="both", expand=True)
- for version, data in instances.items():
- tree.insert("", "end", values=(
- version,
- data.get("java_version", "?"),
- "Gotowe" if data.get("ready", False) else "Błąd",
- datetime.fromisoformat(data["timestamp"]).strftime("%Y-%m-%d %H:%M"),
- f"{data['mod_loader']['type'].capitalize()} {data['mod_loader']['version']}" if data.get("mod_loader") else "Brak",
- len(data.get("mods", []))
- ))
- # Menu kontekstowe
- tree.bind("<Button-3>", lambda e: show_instance_context_menu(e, tree))
- action_frame = ttk.Frame(frame)
- action_frame.pack(fill="x", pady=(10, 0))
- run_btn = ttk.Button(action_frame, text="Uruchom", command=lambda: run_game(tree.item(tree.selection())["values"][0] if tree.selection() else ""), width=15)
- run_btn.pack(side="left", padx=5)
- edit_btn = ttk.Button(action_frame, text="Edytuj", command=lambda: edit_instance(tree.item(tree.selection())["values"][0] if tree.selection() else ""), width=15)
- edit_btn.pack(side="left", padx=5)
- 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)
- verify_btn.pack(side="left", padx=5)
- delete_btn = ttk.Button(action_frame, text="Usuń", command=lambda: delete_instance(tree.item(tree.selection())["values"][0] if tree.selection() else ""), width=15)
- delete_btn.pack(side="left", padx=5)
- ttk.Label(frame, text="Wybierz instancję i kliknij odpowiedni przycisk", font=("Segoe UI", 9)).pack(pady=(10, 0))
- def refresh_instances():
- for widget in content_frame.winfo_children():
- widget.destroy()
- show_instances()
- def create_instance():
- window = tk.Toplevel(root)
- window.title("Nowa instancja Minecraft")
- window.geometry("500x650")
- window.resizable(False, False)
- style.configure("NewInstance.TFrame", background=theme_colors["BG_COLOR"])
- style.configure("NewInstance.TLabel", background=theme_colors["BG_COLOR"], foreground=theme_colors["FG_COLOR"], font=("Segoe UI", 10))
- style.configure("NewInstance.TButton", background=theme_colors["BUTTON_BG"], foreground=theme_colors["FG_COLOR"], font=("Segoe UI", 10))
- frame = ttk.Frame(window, style="NewInstance.TFrame", padding="15")
- frame.pack(fill="both", expand=True)
- ttk.Label(frame, text="Tworzenie nowej instancji", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
- form_frame = ttk.Frame(frame)
- form_frame.pack(fill="both", expand=True)
- ttk.Label(form_frame, text="Filtry wersji:").grid(row=0, column=0, sticky="w", pady=(0, 5))
- filters_frame = ttk.Frame(form_frame)
- filters_frame.grid(row=0, column=1, sticky="w", pady=(0, 5))
- tk.Checkbutton(filters_frame, text="Snapshoty", variable=snapshots_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Release", variable=releases_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Alpha", variable=alpha_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Beta", variable=beta_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(version_combo, version_var)]).pack(anchor="w", pady=2)
- ttk.Label(form_frame, text="Wersja Minecrafta:").grid(row=1, column=0, sticky="w", pady=(10, 5))
- version_var = tk.StringVar()
- versions = get_versions()
- version_combo = ttk.Combobox(form_frame, textvariable=version_var, state="readonly", values=versions)
- version_combo.grid(row=1, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
- if versions:
- version_combo.current(0)
- else:
- messagebox.showwarning("Uwaga", "Włącz przynajmniej jeden filtr wersji (Snapshoty, Release, Alpha, Beta)!")
- ttk.Label(form_frame, text="Nazwa użytkownika:").grid(row=2, column=0, sticky="w", pady=(10, 5))
- username_entry = ttk.Entry(form_frame)
- username_entry.insert(0, username_var.get())
- username_entry.grid(row=2, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
- ttk.Label(form_frame, text="Pamięć RAM (GB):").grid(row=3, column=0, sticky="w", pady=(10, 5))
- memory_spin = tk.Spinbox(form_frame, from_=1, to=16, width=5, bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"], highlightthickness=0)
- memory_spin.delete(0, tk.END)
- memory_spin.insert(0, memory_var.get())
- memory_spin.grid(row=3, column=1, sticky="w", pady=(10, 5), padx=(5, 0))
- ttk.Label(form_frame, text="Ścieżka Java:").grid(row=4, column=0, sticky="w", pady=(10, 5))
- java_var = tk.StringVar()
- java_combo = ttk.Combobox(form_frame, textvariable=java_var, state="readonly")
- java_paths = find_java()
- java_combo['values'] = [f"{p} ({v})" for p, v in java_paths]
- java_combo.grid(row=4, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
- # Automatyczny wybór najlepszej Javy
- if versions and java_paths:
- version_info = get_version_info(version_var.get())
- required_java = get_required_java(version_var.get(), version_info)
- best_java_index = 0
- for i, (path, ver) in enumerate(java_paths):
- if check_java_version(ver, required_java):
- best_java_index = i
- break
- java_combo.current(best_java_index)
- # Aktualizacja Javy przy zmianie wersji Minecrafta
- def update_java_combo(event):
- version_info = get_version_info(version_var.get())
- required_java = get_required_java(version_var.get(), version_info)
- for i, (path, ver) in enumerate(java_paths):
- if check_java_version(ver, required_java):
- java_combo.current(i)
- break
- else:
- java_combo.current(0) if java_paths else java_combo.set("Brak Javy")
- version_combo.bind("<<ComboboxSelected>>", update_java_combo)
- options_frame = ttk.Frame(form_frame)
- options_frame.grid(row=5, column=0, columnspan=2, sticky="ew", pady=(10, 5))
- shared_assets = tk.BooleanVar(value=shared_assets_var.get())
- shared_libs = tk.BooleanVar(value=shared_libraries_var.get())
- shared_natives = tk.BooleanVar(value=shared_natives_var.get())
- tk.Checkbutton(options_frame, text="Współdziel assets", variable=shared_assets,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- tk.Checkbutton(options_frame, text="Współdziel biblioteki", variable=shared_libs,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- tk.Checkbutton(options_frame, text="Współdziel natywne biblioteki", variable=shared_natives,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(15, 0))
- ttk.Button(btn_frame, text="Anuluj", command=window.destroy, width=15).pack(side="right", padx=5)
- ttk.Button(btn_frame, text="Utwórz", command=lambda: [
- save_pending_instance({
- "username": username_entry.get(),
- "memory": memory_spin.get(),
- "java_path": java_paths[int(java_combo.current())][0] if java_combo.current() != -1 else "",
- "shared_assets": shared_assets.get(),
- "shared_libraries": shared_libs.get(),
- "shared_natives": shared_natives.get()
- }, version_var.get()),
- switch_tab("Pobieranie"),
- window.destroy()
- ] if version_var.get() else messagebox.showwarning("Uwaga", "Wybierz wersję!")).pack(side="right", padx=5)
- def save_pending_instance(settings, version):
- global pending_instance_settings, pending_version
- pending_instance_settings = settings
- pending_version = version
- def edit_instance(version):
- if not version or version not in instances:
- messagebox.showwarning("Uwaga", "Wybierz instancję!")
- return
- instance = instances[version]
- settings = instance.get("settings", {})
- window = tk.Toplevel(root)
- window.title(f"Edytuj instancję {version}")
- window.geometry("500x550")
- window.resizable(False, False)
- frame = ttk.Frame(window, padding="15")
- frame.pack(fill="both", expand=True)
- ttk.Label(frame, text=f"Edycja instancji {version}", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
- form_frame = ttk.Frame(frame)
- form_frame.pack(fill="both", expand=True)
- ttk.Label(form_frame, text="Nazwa użytkownika:").grid(row=0, column=0, sticky="w", pady=(0, 5))
- username_entry = ttk.Entry(form_frame)
- username_entry.insert(0, settings.get("username", username_var.get()))
- username_entry.grid(row=0, column=1, sticky="ew", pady=(0, 5), padx=(5, 0))
- ttk.Label(form_frame, text="Pamięć RAM (GB):").grid(row=1, column=0, sticky="w", pady=(10, 5))
- memory_spin = tk.Spinbox(form_frame, from_=1, to=16, width=5, bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"], highlightthickness=0)
- memory_spin.delete(0, tk.END)
- memory_spin.insert(0, settings.get("memory", memory_var.get()))
- memory_spin.grid(row=1, column=1, sticky="w", pady=(10, 5), padx=(5, 0))
- ttk.Label(form_frame, text="Ścieżka Java:").grid(row=2, column=0, sticky="w", pady=(10, 5))
- java_var = tk.StringVar()
- java_combo = ttk.Combobox(form_frame, textvariable=java_var, state="readonly")
- java_paths = find_java()
- current_java = settings.get("java_path", instance.get("java_path", ""))
- java_values = [f"{p} ({v})" for p, v in java_paths]
- # Jeśli zapisana Java istnieje, dodaj ją do listy, jeśli nie ma jej w java_paths
- if current_java and not any(p == current_java for p, v in java_paths):
- java_values.append(f"{current_java} (zapisana)")
- java_paths.append((current_java, "zapisana"))
- java_combo['values'] = java_values
- java_combo.grid(row=2, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
- # Ustaw zapisana Javę, jeśli istnieje
- if current_java:
- for i, (path, ver) in enumerate(java_paths):
- if path == current_java:
- java_combo.current(i)
- break
- else:
- java_combo.current(len(java_paths) - 1) # Wybierz "zapisana", jeśli dodana
- elif java_paths:
- # Jeśli brak zapisanej Javy, wybierz najlepszą dla wersji
- version_info = get_version_info(version)
- required_java = get_required_java(version, version_info)
- for i, (path, ver) in enumerate(java_paths):
- if check_java_version(ver, required_java):
- java_combo.current(i)
- break
- else:
- java_combo.current(0)
- options_frame = ttk.Frame(form_frame)
- options_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(10, 5))
- shared_assets = tk.BooleanVar(value=settings.get("shared_assets", shared_assets_var.get()))
- shared_libs = tk.BooleanVar(value=settings.get("shared_libraries", shared_libraries_var.get()))
- shared_natives = tk.BooleanVar(value=settings.get("shared_natives", shared_natives_var.get()))
- tk.Checkbutton(options_frame, text="Współdziel assets", variable=shared_assets,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- tk.Checkbutton(options_frame, text="Współdziel biblioteki", variable=shared_libs,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- tk.Checkbutton(options_frame, text="Współdziel natywne biblioteki", variable=shared_natives,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(15, 0))
- ttk.Button(btn_frame, text="Anuluj", command=window.destroy, width=15).pack(side="right", padx=5)
- ttk.Button(btn_frame, text="Zapisz", command=lambda: [
- update_instance(version, {
- "username": username_entry.get(),
- "memory": memory_spin.get(),
- "java_path": java_paths[int(java_combo.current())][0] if java_combo.current() != -1 else current_java,
- "shared_assets": shared_assets.get(),
- "shared_libraries": shared_libs.get(),
- "shared_natives": shared_natives.get()
- }),
- window.destroy()
- ]).pack(side="right", padx=5)
- def update_instance(version, settings):
- instance = instances[version]
- instance["settings"] = settings
- info = get_version_info(version)
- if info:
- version_dir = instance["path"]
- bat_path = os.path.join(version_dir, "start.bat")
- classpath = f"{version}.jar"
- libraries_dir = LIBRARIES_DIR if settings.get("shared_libraries", True) else os.path.join(version_dir, "libraries")
- for lib in info.get("libraries", []):
- if "downloads" in lib and "artifact" in lib["downloads"]:
- lib_path = lib["downloads"]["artifact"]["path"].replace("/", os.sep)
- classpath += f";{os.path.join(libraries_dir, lib_path)}"
- assets_path = ASSETS_DIR if settings.get("shared_assets", True) else os.path.join(version_dir, "assets")
- natives_path = NATIVES_DIR if settings.get("shared_natives", True) else os.path.join(version_dir, "natives")
- java_path = settings.get("java_path", instance.get("java_path", ""))
- asset_index = info.get("assetIndex", {}).get("id", version)
- with open(bat_path, "w", encoding="utf-8") as f:
- if is_new_launcher(version):
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version}\n')
- 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 ')
- f.write(f'--username {settings["username"]} --version {version} --gameDir . ')
- f.write(f'--assetsDir "{assets_path}" --assetIndex {asset_index} ')
- f.write(f'--accessToken null --uuid 0 --userType legacy\n')
- f.write(f'pause\n')
- else:
- f.write(f'@echo off\n')
- f.write(f'title Minecraft {version}\n')
- f.write(f'"{java_path}" -Xmx{settings["memory"]}G -cp "{version}.jar" net.minecraft.LauncherFrame {settings["username"]} player_session_id\n')
- f.write(f'pause\n')
- save_config()
- log_to_console(console, f"Instancja {version} zaktualizowana", "SUCCESS")
- refresh_instances()
- def show_download():
- frame = ttk.Frame(content_frame)
- frame.pack(fill="both", expand=True)
- ttk.Label(frame, text="Pobierz nową wersję Minecraft", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
- form_frame = ttk.Frame(frame)
- form_frame.pack(fill="both", expand=True)
- ttk.Label(form_frame, text="Filtry wersji:").grid(row=0, column=0, sticky="w", pady=(0, 5))
- filters_frame = ttk.Frame(form_frame)
- filters_frame.grid(row=0, column=1, sticky="w", pady=(0, 5))
- tk.Checkbutton(filters_frame, text="Snapshoty", variable=snapshots_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Release", variable=releases_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Alpha", variable=alpha_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Beta", variable=beta_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=lambda: [save_config(), refresh_version_combo(combo, version_var)]).pack(anchor="w", pady=2)
- ttk.Label(form_frame, text="Wersja Minecrafta:").grid(row=1, column=0, sticky="w", pady=(10, 5))
- version_var = tk.StringVar()
- versions = get_versions()
- combo = ttk.Combobox(form_frame, textvariable=version_var, state="readonly", values=versions)
- combo.grid(row=1, column=1, sticky="ew", pady=(10, 5), padx=(5, 0))
- if versions and pending_version in versions:
- combo.set(pending_version)
- elif versions:
- combo.current(0)
- else:
- messagebox.showwarning("Uwaga", "Włącz przynajmniej jeden filtr wersji (Snapshoty, Release, Alpha, Beta)!")
- console_frame = ttk.Frame(frame)
- console_frame.pack(fill="both", expand=True, pady=(15, 0))
- global console
- console = scrolledtext.ScrolledText(
- console_frame, height=10, wrap=tk.WORD, bg=theme_colors["CONSOLE_BG"], fg=theme_colors["CONSOLE_FG"],
- state="disabled", font=("Consolas", 9)
- )
- console.pack(fill="both", expand=True)
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(15, 0))
- ttk.Button(btn_frame, text="Pobierz", command=lambda: start_download({
- "username": pending_instance_settings.get("username", username_var.get()),
- "memory": pending_instance_settings.get("memory", memory_var.get()),
- "java_path": pending_instance_settings.get("java_path", ""),
- "shared_assets": pending_instance_settings.get("shared_assets", shared_assets_var.get()),
- "shared_libraries": pending_instance_settings.get("shared_libraries", shared_libraries_var.get()),
- "shared_natives": pending_instance_settings.get("shared_natives", shared_natives_var.get())
- }, version_var.get()) if version_var.get() else messagebox.showwarning("Uwaga", "Wybierz wersję!"), width=15).pack()
- def start_download(settings, version):
- global download_thread, download_active
- if download_active:
- messagebox.showwarning("Uwaga", "Pobieranie już w toku!")
- return
- if not version:
- messagebox.showwarning("Uwaga", "Wybierz wersję!")
- return
- download_active = True
- download_thread = threading.Thread(
- target=download_version,
- args=(version, settings, console),
- daemon=True
- )
- download_thread.start()
- def show_mod_loader_tab():
- frame = ttk.Frame(content_frame)
- frame.pack(fill="both", expand=True)
- ttk.Label(frame, text="Instalacja mod loaderów", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
- form_frame = ttk.Frame(frame)
- form_frame.pack(fill="both", expand=True)
- ttk.Label(form_frame, text="Wybierz instancję:").grid(row=0, column=0, sticky="w", pady=(0, 5))
- instance_var = tk.StringVar()
- instances_combo = ttk.Combobox(form_frame, textvariable=instance_var, state="readonly", values=list(instances.keys()))
- instances_combo.grid(row=0, column=1, sticky="ew", pady=(0, 5), padx=(5, 0))
- if instances:
- instances_combo.current(0)
- btn_frame = ttk.Frame(form_frame)
- btn_frame.grid(row=1, column=0, columnspan=2, sticky="ew", pady=(10, 0))
- ttk.Button(btn_frame, text="Zainstaluj loader", command=lambda: show_mod_loader_installer(instance_var.get()) if instance_var.get() else messagebox.showwarning("Uwaga", "Wybierz instancję!")).pack(side="left", padx=5)
- ttk.Button(btn_frame, text="Pokaż statystyki", command=lambda: show_instance_stats(instance_var.get()) if instance_var.get() else messagebox.showwarning("Uwaga", "Wybierz instancję!")).pack(side="left", padx=5)
- ttk.Button(btn_frame, text="Dodaj notatkę", command=lambda: add_instance_note(instance_var.get()) if instance_var.get() else messagebox.showwarning("Uwaga", "Wybierz instancję!")).pack(side="left", padx=5)
- info_frame = ttk.Frame(frame)
- info_frame.pack(fill="both", expand=True, pady=(15, 0))
- ttk.Label(info_frame, text="Obecny mod loader:").pack(anchor="w")
- loader_label = ttk.Label(info_frame, text="", font=("Segoe UI", 10, "bold"))
- loader_label.pack(anchor="w")
- ttk.Label(info_frame, text="Zainstalowane mody:").pack(anchor="w", pady=(10, 0))
- mods_tree = ttk.Treeview(info_frame, columns=("name", "version"), show="headings", height=5)
- mods_tree.heading("name", text="Nazwa")
- mods_tree.heading("version", text="Wersja")
- mods_tree.column("name", width=200)
- mods_tree.column("version", width=100)
- mods_tree.pack(fill="both", expand=True)
- def update_info():
- version = instance_var.get()
- if not version or version not in instances:
- return
- instance = instances[version]
- if instance.get("mod_loader"):
- loader_label.config(text=f"{instance['mod_loader']['type'].capitalize()} {instance['mod_loader']['version']}")
- else:
- loader_label.config(text="Brak")
- mods_tree.delete(*mods_tree.get_children())
- for mod in instance.get("mods", []):
- mods_tree.insert("", "end", values=(mod["name"], mod.get("version", "")))
- instances_combo.bind("<<ComboboxSelected>>", lambda e: update_info())
- update_info()
- def show_settings():
- frame = ttk.Frame(content_frame)
- frame.pack(fill="both", expand=True)
- ttk.Label(frame, text="Ustawienia Launchera", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
- form_frame = ttk.Frame(frame)
- form_frame.pack(fill="both", expand=True)
- ttk.Label(form_frame, text="Domyślna nazwa użytkownika:").grid(row=0, column=0, sticky="w", pady=(0, 5))
- ttk.Entry(form_frame, textvariable=username_var).grid(row=0, column=1, sticky="ew", pady=(0, 5), padx=(5, 0))
- ttk.Label(form_frame, text="Domyślna pamięć RAM (GB):").grid(row=1, column=0, sticky="w", pady=(10, 5))
- memory_spin = tk.Spinbox(form_frame, from_=1, to=16, textvariable=memory_var, width=5,
- bg=theme_colors["ENTRY_BG"], fg=theme_colors["FG_COLOR"], highlightthickness=0)
- memory_spin.grid(row=1, column=1, sticky="w", pady=(10, 5), padx=(5, 0))
- options_frame = ttk.Frame(form_frame)
- options_frame.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(10, 5))
- tk.Checkbutton(options_frame, text="Współdziel assets między instancjami", variable=shared_assets_var,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- tk.Checkbutton(options_frame, text="Współdziel biblioteki między instancjami", variable=shared_libraries_var,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- tk.Checkbutton(options_frame, text="Współdziel natywne biblioteki między instancjami", variable=shared_natives_var,
- bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"], selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"],
- font=("Segoe UI", 9)).pack(anchor="w", pady=2)
- ttk.Label(form_frame, text="Domyślne filtry wersji:").grid(row=3, column=0, sticky="w", pady=(10, 5))
- filters_frame = ttk.Frame(form_frame)
- filters_frame.grid(row=3, column=1, sticky="w", pady=(10, 5))
- tk.Checkbutton(filters_frame, text="Snapshoty", variable=snapshots_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=save_config).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Release", variable=releases_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=save_config).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Alpha", variable=alpha_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=save_config).pack(anchor="w", pady=2)
- tk.Checkbutton(filters_frame, text="Beta", variable=beta_var, bg=theme_colors["BG_COLOR"], fg=theme_colors["FG_COLOR"],
- selectcolor=theme_colors["BG_COLOR"], activebackground=theme_colors["BG_COLOR"], font=("Segoe UI", 9),
- command=save_config).pack(anchor="w", pady=2)
- theme_frame = ttk.Frame(form_frame)
- theme_frame.grid(row=4, column=0, columnspan=2, sticky="ew", pady=(10, 5))
- ttk.Label(theme_frame, text="Motyw:").pack(side="left", padx=5)
- ttk.Button(theme_frame, text="Ciemny/Jasny", command=toggle_theme).pack(side="left", padx=5)
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(15, 0))
- ttk.Button(btn_frame, text="Zapisz ustawienia", command=save_config, width=20).pack()
- def show_console():
- frame = ttk.Frame(content_frame)
- frame.pack(fill="both", expand=True)
- ttk.Label(frame, text="Konsola Launchera", font=("Segoe UI", 14, "bold")).pack(pady=(0, 15))
- console_frame = ttk.Frame(frame)
- console_frame.pack(fill="both", expand=True)
- global console
- console = scrolledtext.ScrolledText(
- console_frame, height=20, wrap=tk.WORD, bg=theme_colors["CONSOLE_BG"], fg=theme_colors["CONSOLE_FG"],
- state="disabled", font=("Consolas", 9)
- )
- console.pack(fill="both", expand=True)
- btn_frame = ttk.Frame(frame)
- btn_frame.pack(fill="x", pady=(10, 0))
- ttk.Button(btn_frame, text="Kopiuj", command=lambda: copy_console(console), width=15).pack(side="left", padx=5)
- ttk.Button(btn_frame, text="Wyczyść", command=lambda: clear_console(console), width=15).pack(side="left", padx=5)
- def refresh_version_combo(combo, version_var):
- versions = get_versions()
- combo['values'] = versions
- if versions:
- version_var.set(versions[0])
- else:
- version_var.set("")
- messagebox.showwarning("Uwaga", "Włącz przynajmniej jeden filtr wersji (Snapshoty, Release, Alpha, Beta)!")
- print("Inicjalizacja zakończona, uruchamiam UI...")
- switch_tab("Instancje")
- root.mainloop()
- except Exception as e:
- print(f"CRASH: {e}")
- with open(os.path.join(LOGS_DIR, "crash.log"), "a", encoding="utf-8") as f:
- f.write(f"[{datetime.now()}] CRASH: {e}\n")
- raise
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement