From c5040dd2e2787837b50d2c56e40f52a2dabf84f3 Mon Sep 17 00:00:00 2001 From: octolinkyt Date: Mon, 8 Dec 2025 06:24:33 -0500 Subject: [PATCH] Update app.py --- app.py | 247 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 128 insertions(+), 119 deletions(-) diff --git a/app.py b/app.py index 5674f0c..56367e9 100644 --- a/app.py +++ b/app.py @@ -6,23 +6,18 @@ import customtkinter as ctk import minecraft_launcher_lib from tkinter import messagebox from PIL import Image, ImageTk, ImageFilter -import urllib.request -import zipfile -import sys -import platform -# ---------------------------- -# Paths -# ---------------------------- +# -------------------------------- +# Setup paths +# -------------------------------- CUSTOM_DIR = os.path.join(os.getcwd(), "blockbreak") -JAVA_DIR = os.path.join(CUSTOM_DIR, "java") os.makedirs(CUSTOM_DIR, exist_ok=True) -os.makedirs(JAVA_DIR, exist_ok=True) + BG_PATH = os.path.join(os.getcwd(), "bg.png") -# ---------------------------- +# -------------------------------- # GUI Setup -# ---------------------------- +# -------------------------------- ctk.set_appearance_mode("dark") ctk.set_default_color_theme("dark-blue") @@ -31,45 +26,54 @@ root.title("BlockBreak") root.geometry("800x600") root.minsize(600, 450) -# ---------------------------- +# -------------------------------- # Background Canvas -# ---------------------------- +# -------------------------------- bg_canvas = ctk.CTkCanvas(root, width=800, height=600, highlightthickness=0) bg_canvas.pack(fill="both", expand=True) -bg_image = None # keep reference +bg_image = None -def load_and_blur_bg(): +def reload_bg(): global bg_image - if os.path.exists(BG_PATH): - pil_img = Image.open(BG_PATH).convert("RGB") - w, h = root.winfo_width(), root.winfo_height() - pil_img = pil_img.resize((w, h)) - pil_img = pil_img.filter(ImageFilter.GaussianBlur(4)) - bg_image = ImageTk.PhotoImage(pil_img) - bg_canvas.delete("bg") - bg_canvas.create_image(0, 0, anchor="nw", image=bg_image, tags="bg") + if not os.path.exists(BG_PATH): + return + pil_img = Image.open(BG_PATH).convert("RGB") -root.bind("", lambda e: load_and_blur_bg()) + w = root.winfo_width() + h = root.winfo_height() -# ---------------------------- -# Loading Overlay for Fade-in -# ---------------------------- -loading_overlay = ctk.CTkFrame(root, fg_color="#1f1f1f", corner_radius=0) + pil_img = pil_img.resize((w, h)) + pil_img = pil_img.filter(ImageFilter.GaussianBlur(5)) + + bg_image = ImageTk.PhotoImage(pil_img) + bg_canvas.delete("bg") + bg_canvas.create_image(0, 0, anchor="nw", image=bg_image, tags="bg") + +root.bind("", lambda e: reload_bg()) + +# -------------------------------- +# Fade-in loading overlay +# -------------------------------- +loading_overlay = ctk.CTkFrame(root, fg_color="#000000") loading_overlay.place(relx=0, rely=0, relwidth=1, relheight=1) -loading_label = ctk.CTkLabel(loading_overlay, text="Loading...", font=("Arial", 36)) + +loading_label = ctk.CTkLabel( + loading_overlay, text="Loading...", font=("Arial", 36) +) loading_label.place(relx=0.5, rely=0.5, anchor="center") -# ---------------------------- -# Main Panel with curves + padding -# ---------------------------- -panel = ctk.CTkFrame(root, width=600, height=500, corner_radius=25, fg_color="#1f1f1f") +# -------------------------------- +# Main Panel UI +# -------------------------------- +panel = ctk.CTkFrame(root, corner_radius=25, width=600, height=500, fg_color="#1f1f1f") panel.place(relx=0.5, rely=0.5, anchor="center") -inner_panel = ctk.CTkFrame(panel, fg_color=None, corner_radius=0) -inner_panel.pack(padx=30, pady=20, fill="both", expand=True) +inner_panel = ctk.CTkFrame(panel, fg_color="transparent") +inner_panel.pack(padx=30, pady=30, fill="both", expand=True) + +ctk.CTkLabel(inner_panel, text="BlockBreak", font=("Arial", 28)).pack(pady=(0, 20)) -ctk.CTkLabel(inner_panel, text="BlockBreak", font=("Arial", 28)).pack(pady=(0, 15)) username_entry = ctk.CTkEntry(inner_panel, placeholder_text="Username", width=500) username_entry.pack(pady=10) @@ -77,131 +81,136 @@ version_var = ctk.StringVar() version_dropdown = ctk.CTkComboBox(inner_panel, values=[], variable=version_var, width=500) version_dropdown.pack(pady=10) -# RAM slider -ram_var = ctk.StringVar(value="2G") -ctk.CTkLabel(inner_panel, text="RAM Allocation").pack(pady=(10,0)) -ram_slider = ctk.CTkSegmentedButton(inner_panel, values=["2G - Default (fast + safe)","4G - More mods","6G - Maximum sensible"], command=lambda v: ram_var.set(v.split(" ")[0])) -ram_slider.pack(pady=5) - progress = ctk.CTkProgressBar(inner_panel, width=500) progress.set(0) progress.pack(pady=10) -log_box = ctk.CTkTextbox(inner_panel, width=500, height=150) -log_box.pack(pady=(10, 0)) +log_box = ctk.CTkTextbox(inner_panel, width=500, height=140) +log_box.pack(pady=10) log_box.configure(state="disabled") -def log(msg): +def log(text): log_box.configure(state="normal") - log_box.insert("end", msg + "\n") + log_box.insert("end", text + "\n") log_box.see("end") log_box.configure(state="disabled") -# ---------------------------- -# Java installation (simple bundled version) -# ---------------------------- -def ensure_java(): - java_exe = os.path.join(JAVA_DIR, "bin", "java.exe" if os.name == "nt" else "java") - if os.path.exists(java_exe): - log("Java found.") - return java_exe - - log("Downloading Java...") - # We'll use a lightweight OpenJDK zip for Windows only here for simplicity - # For cross-platform, you'd need proper handling - java_url = "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8%2B7/OpenJDK17U-jre_x64_windows_hotspot_17.0.8_7.zip" - zip_path = os.path.join(JAVA_DIR, "java.zip") - urllib.request.urlretrieve(java_url, zip_path) - with zipfile.ZipFile(zip_path, "r") as zip_ref: - zip_ref.extractall(JAVA_DIR) - os.remove(zip_path) - - # Find java.exe in extracted folder - for root_dir, dirs, files in os.walk(JAVA_DIR): - if "java.exe" in files: - java_exe = os.path.join(root_dir, "java.exe") - log("Java installed successfully.") - return java_exe - raise FileNotFoundError("Java installation failed") - -# ---------------------------- -# Launcher Functions -# ---------------------------- -def get_versions_with_status(): +# -------------------------------- +# Version handling +# -------------------------------- +def get_versions(): try: versions = minecraft_launcher_lib.utils.get_available_versions(CUSTOM_DIR) - except Exception: + except: versions = [] - items = [] + + result = [] for v in versions: - v_id = v["id"] - path = os.path.join(CUSTOM_DIR, "versions", v_id) - if os.path.exists(path): - items.append(f"{v_id} [Installed]") - else: - items.append(v_id) - return items + vid = v["id"] + installed = os.path.exists(os.path.join(CUSTOM_DIR, "versions", vid)) + label = f"{vid} [Installed]" if installed else vid + result.append(label) + return result -def update_progress(current, total): - if total > 0: - progress.set(current / total) +# -------------------------------- +# Callback handler for minecraft-launcher-lib +# -------------------------------- +max_steps = 1 +def cb_set_status(status: str): + log(f"[STATUS] {status}") + +def cb_set_max(value: int): + global max_steps + max_steps = max(value, 1) + progress.set(0) + +def cb_set_progress(value: int): + if max_steps != 0: + progress.set(value / max_steps) + +CALLBACKS = { + "setStatus": cb_set_status, + "setProgress": cb_set_progress, + "setMax": cb_set_max +} + +# -------------------------------- +# Download + Launch Minecraft +# -------------------------------- def download_and_launch(username, version): try: - java_path = ensure_java() actual_version = version.replace(" [Installed]", "") + version_path = os.path.join(CUSTOM_DIR, "versions", actual_version) + + # Install if needed if not os.path.exists(version_path): - log(f"Installing version {actual_version}...") - minecraft_launcher_lib.install.install_minecraft_version(actual_version, CUSTOM_DIR, progress_callback=update_progress) + log(f"Installing Minecraft {actual_version}...") + minecraft_launcher_lib.install.install_minecraft_version( + actual_version, + CUSTOM_DIR, + callback=CALLBACKS + ) log("Installation complete.") else: - log(f"Version {actual_version} already installed.") + log("Version already installed — skipping download.") log("Generating launch command...") - options = minecraft_launcher_lib.utils.generate_test_options() - options["username"] = username - options["uuid"] = options.get("uuid", "") - options["token"] = "" - options["java_path"] = java_path - options["max_memory"] = ram_var.get() - cmd = minecraft_launcher_lib.command.get_minecraft_command(actual_version, CUSTOM_DIR, options) + + opts = minecraft_launcher_lib.utils.generate_test_options() + opts["username"] = username + opts["token"] = "" + opts["uuid"] = opts.get("uuid", "") + + cmd = minecraft_launcher_lib.command.get_minecraft_command( + actual_version, + CUSTOM_DIR, + opts + ) log("Launching Minecraft...") subprocess.Popen(cmd, cwd=CUSTOM_DIR) + log("Minecraft launched!") - progress.set(0) + except Exception as e: - log(f"Error: {e}") - messagebox.showerror("Launch Failed", str(e)) + messagebox.showerror("Error", str(e)) + log(f"ERROR: {str(e)}") + finally: progress.set(0) -def launch_threaded(): +def start_launch(): username = username_entry.get().strip() version = version_var.get().strip() + if not username or not version: messagebox.showerror("Error", "Username and version required!") return - progress.set(0) + threading.Thread(target=download_and_launch, args=(username, version), daemon=True).start() -play_button = ctk.CTkButton(inner_panel, text="Play", command=launch_threaded, width=500, height=50) +play_button = ctk.CTkButton( + inner_panel, text="Play", command=start_launch, width=500, height=50 +) play_button.pack(pady=10) -# ---------------------------- -# Initialization + fade-in -# ---------------------------- -def initialize_launcher(): - version_dropdown.configure(values=get_versions_with_status()) - load_and_blur_bg() - # Fade-in simulation using overlay - for i in range(20, -1, -1): - gray = int(i * 12.75) - hex_gray = f"{gray:02x}" - loading_overlay.configure(fg_color=f"#{hex_gray}{hex_gray}{hex_gray}") - time.sleep(0.03) +# -------------------------------- +# Fade-in (restored properly) +# -------------------------------- +def full_fade_in(): + reload_bg() + version_dropdown.configure(values=get_versions()) + + # Simulated fade-in by brightening overlay + for i in range(0, 25): + shade = 255 - int(i * (255 / 25)) + hexcol = f"{shade:02x}" + loading_overlay.configure(fg_color="#" + hexcol * 3) + time.sleep(0.015) + loading_overlay.destroy() -threading.Thread(target=initialize_launcher, daemon=True).start() +threading.Thread(target=full_fade_in, daemon=True).start() root.mainloop()