import os import subprocess import threading import time import customtkinter as ctk import minecraft_launcher_lib from tkinter import messagebox from PIL import Image, ImageTk, ImageFilter # -------------------------------- # Setup paths # -------------------------------- CUSTOM_DIR = os.path.join(os.getcwd(), "blockbreak") os.makedirs(CUSTOM_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") root = ctk.CTk() 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 def reload_bg(): global bg_image if not os.path.exists(BG_PATH): return pil_img = Image.open(BG_PATH).convert("RGB") w = root.winfo_width() h = root.winfo_height() 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.place(relx=0.5, rely=0.5, anchor="center") # -------------------------------- # 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="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)) username_entry = ctk.CTkEntry(inner_panel, placeholder_text="Username", width=500) username_entry.pack(pady=10) version_var = ctk.StringVar() version_dropdown = ctk.CTkComboBox(inner_panel, values=[], variable=version_var, width=500) version_dropdown.pack(pady=10) progress = ctk.CTkProgressBar(inner_panel, width=500) progress.set(0) progress.pack(pady=10) log_box = ctk.CTkTextbox(inner_panel, width=500, height=140) log_box.pack(pady=10) log_box.configure(state="disabled") def log(text): log_box.configure(state="normal") log_box.insert("end", text + "\n") log_box.see("end") log_box.configure(state="disabled") # -------------------------------- # Version handling # -------------------------------- def get_versions(): try: versions = minecraft_launcher_lib.utils.get_available_versions(CUSTOM_DIR) except: versions = [] result = [] for v in versions: 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 # -------------------------------- # 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: 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 Minecraft {actual_version}...") minecraft_launcher_lib.install.install_minecraft_version( actual_version, CUSTOM_DIR, callback=CALLBACKS ) log("Installation complete.") else: log("Version already installed — skipping download.") log("Generating launch command...") 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!") except Exception as e: messagebox.showerror("Error", str(e)) log(f"ERROR: {str(e)}") finally: progress.set(0) 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 threading.Thread(target=download_and_launch, args=(username, version), daemon=True).start() play_button = ctk.CTkButton( inner_panel, text="Play", command=start_launch, width=500, height=50 ) play_button.pack(pady=10) # -------------------------------- # 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=full_fade_in, daemon=True).start() root.mainloop()