From 40add5392c7b2f7c34ffbaeef0d4d5b11f8361fa Mon Sep 17 00:00:00 2001 From: octolinkyt Date: Sun, 7 Dec 2025 22:31:28 -0500 Subject: [PATCH] fun fact: during dev this file was titled "mariowantstokillyou.py" --- app.py | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 app.py diff --git a/app.py b/app.py new file mode 100644 index 0000000..5674f0c --- /dev/null +++ b/app.py @@ -0,0 +1,207 @@ +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 +import urllib.request +import zipfile +import sys +import platform + +# ---------------------------- +# 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") + +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 # keep reference + +def load_and_blur_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") + +root.bind("", lambda e: load_and_blur_bg()) + +# ---------------------------- +# Loading Overlay for Fade-in +# ---------------------------- +loading_overlay = ctk.CTkFrame(root, fg_color="#1f1f1f", corner_radius=0) +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 with curves + padding +# ---------------------------- +panel = ctk.CTkFrame(root, width=600, height=500, corner_radius=25, 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) + +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) + +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.configure(state="disabled") + +def log(msg): + log_box.configure(state="normal") + log_box.insert("end", msg + "\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(): + try: + versions = minecraft_launcher_lib.utils.get_available_versions(CUSTOM_DIR) + except Exception: + versions = [] + items = [] + 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 + +def update_progress(current, total): + if total > 0: + progress.set(current / total) + +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) + 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("Installation complete.") + else: + log(f"Version {actual_version} already installed.") + + 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) + + 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)) + progress.set(0) + +def launch_threaded(): + 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.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) + loading_overlay.destroy() + +threading.Thread(target=initialize_launcher, daemon=True).start() + +root.mainloop()