Мануал Corsair - Client with Updater

orohimaru2

Выдающийся
Местный
Сообщения
57
Розыгрыши
0
Репутация
114
Реакции
199
Баллы
1 388
Автор не я взял с Англ сайта
Приветствую. Клиентов много, и я благодарен им за их труд. Я даю вам свой, потому что он работает вместе с клиентским обновлением.

Вам понадобятся 3 вещи:
1. Corsair_Launcher.py
2. bg.png (фон)
3. icon.ico

Шаги:
1. Скачайте bg и icon. Измените icon.png на icon.ico
2. Поместите их в ту же папку, что и Corsair_Launcher.py
3. Откройте лаунчер, внесите изменения, а затем с помощью PyInstaller создайте exe-файл.

Это довольно просто. Просто потратьте несколько минут и обновите файл Python (вверху URL-адрес сервера и код авторизации, чтобы они соответствовали вашему серверу обновлений).
Код:
# Corsair Launcher
# Pyinstaller: pyinstaller --onefile --noconsole --icon=icon.ico --add-data "bg.png;." --clean corsair_launcher.py


import tkinter as tk
from tkinter import messagebox, ttk
import os
import subprocess
import hashlib
import json
import requests
import sys
from PIL import Image, ImageTk
import time
import ctypes

VERSION = "1.0.0"
SECRET_AUTH_CODE = "Code8193App9038Password3726Auth4837"
PATCH_SERVER_URL = "http://164.188.432.15:8891" # Set to point to your secure patch server.
MANIFEST_FILE = "manifest.json"
VERSION_FILE = "version.txt"
CREDENTIALS_FILE = "credentials.json"
EXCLUDED_FILES = {"Manifest_Generator.py", "Secure_Patch_Server.py"}

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

if not is_admin():
    # Relaunch elevated and REPLACE the current process
    params = " ".join(f'"{arg}"' for arg in sys.argv)
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1)
    sys.exit(0)



class LauncherApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Black Desert Launcher")
        self.root.geometry("600x500")
        self.root.resizable(False, False)
        try:
            self.root.iconbitmap(self.resource_path("icon.ico"))
        except:
            pass

        original_bg = Image.open(self.resource_path("bg.png"))
        bg_image = original_bg.resize((600, 500), Image.Resampling.LANCZOS)
        self.bg_photo = ImageTk.PhotoImage(bg_image)
        bg_label = tk.Label(root, image=self.bg_photo)
        bg_label.place(relx=0, rely=0, relwidth=1, relheight=1)

        self.title_frame = tk.Frame(root, bg="black")
        self.title_frame.place(x=20, y=30)
        tk.Label(self.title_frame, text="Black Desert Corsair", font=("Helvetica", 20, "bold"), bg="black",
                 fg="white").pack(anchor="w")

        self.frame = tk.Frame(root, bg="black")
        self.frame.place(x=40, y=80)

        self.username_var = tk.StringVar()
        self.password_var = tk.StringVar()
        self.remember_var = tk.BooleanVar()
        self.load_credentials()  # Load value BEFORE checkbox is created


        tk.Label(self.frame, text="Username", bg="black", fg="white").pack(anchor="w")
        self.username_entry = tk.Entry(self.frame, textvariable=self.username_var, width=30)
        self.username_entry.pack(pady=5)

        tk.Label(self.frame, text="Password", bg="black", fg="white").pack(anchor="w")
        self.password_entry = tk.Entry(self.frame, textvariable=self.password_var, show="*", width=30)
        self.password_entry.pack(pady=5)

        self.remember_check = tk.Checkbutton(
            self.frame,
            text="Remember Me",
            variable=self.remember_var,
            onvalue=True,
            offvalue=False,
            bg="black",
            fg="white",
            selectcolor="black",  # <- This ensures the box doesn't go white
            activebackground="black",
            activeforeground="white",
            highlightbackground="black"
        )
        self.remember_check.pack(pady=5, anchor="w")



        self.launch_button = tk.Button(self.frame, text="Launch", command=self.launch_game)
        self.launch_button.pack(pady=10)

        self.version_frame = tk.Frame(root, bg="black")
        self.version_frame.place(x=40, y=470)
        tk.Label(self.version_frame, text="Version", font=("Helvetica", 10, "bold"), bg="black", fg="white").pack(
            side="left")

        version_text = ""
        if os.path.exists(VERSION_FILE):
            try:
                with open(VERSION_FILE, "r") as f:
                    version_text = f.read().strip()
            except:
                pass
        self.version_label = tk.Label(self.version_frame, text=version_text, font=("Helvetica", 10), bg="black",
                                      fg="white")
        self.version_label.pack(side="left", padx=(10, 0))

       

        # trace checkbox state changes
        self.remember_var.trace_add("write", self.trace_checkbox)

        self.root.after(100, self.check_for_updates)

    def trace_checkbox(self, *args):
        print("Checkbox toggled ->", self.remember_var.get())

    def resource_path(self, relative):
        try:
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")
        return os.path.join(base_path, relative)

    def load_credentials(self):
        if os.path.exists(CREDENTIALS_FILE):
            try:
                with open(CREDENTIALS_FILE, "r") as f:
                    data = json.load(f)
                    self.username_var.set(data.get("username", ""))
                    self.password_var.set(data.get("password", ""))

                    # Manually update checkbox state and force redraw
                    remember_value = data.get("remember", False)
                    self.remember_var.set(bool(remember_value))

                    # Recreate the checkbox after loading to force state update
                    self.remember_check.destroy()
                    self.remember_check = tk.Checkbutton(
                        self.frame,
                        text="Remember Me",
                        variable=self.remember_var,
                        onvalue=True,
                        offvalue=False,
                        bg="black",
                        fg="white",
                        activebackground="black",
                        activeforeground="white"
                    )
                    self.remember_check.pack(pady=5, anchor="w")
            except Exception as e:
                print(f"Failed to load credentials: {e}")


    def save_credentials(self):
        if self.remember_var.get():
            data = {
                "username": self.username_var.get(),
                "password": self.password_var.get(),
                "remember": True
            }
            with open(CREDENTIALS_FILE, "w") as f:
                json.dump(data, f)
        elif os.path.exists(CREDENTIALS_FILE):
            os.remove(CREDENTIALS_FILE)

    def check_for_updates(self):
        try:
            version_url = f"{PATCH_SERVER_URL}/version.txt"
            r = requests.get(version_url, headers={"Authorization": SECRET_AUTH_CODE}, timeout=5)
            server_version = r.text.strip()
            if not os.path.exists(VERSION_FILE):
                result = messagebox.askyesno("Install", "Game not installed. Install now?")
                if result:
                    self.download_patch()
                return
            with open(VERSION_FILE, "r") as f:
                local_version = f.read().strip()
            if local_version != server_version:
                result = messagebox.askyesno("Update", "A new version is available. Update now?")
                if result:
                    self.download_patch()
        except Exception as e:
            print(f"Update check failed: {e}")

    def sha256_file(self, filepath):
        try:
            sha256 = hashlib.sha256()
            with open(filepath, "rb") as f:
                for block in iter(lambda: f.read(8192), b""):
                    sha256.update(block)
            return sha256.hexdigest()
        except Exception as e:
            print(f"Hash error for {filepath}: {e}")
            return None

    def download_patch(self):
        try:
            manifest_url = f"{PATCH_SERVER_URL}/manifest.json"
            r = requests.get(manifest_url, headers={"Authorization": SECRET_AUTH_CODE}, timeout=10)
            manifest = r.json()

            if not hasattr(self, 'progress_bar'):
                self.progress_label = tk.Label(self.root, text="Preparing update...", bg="black", fg="white",
                                               font=("Helvetica", 10))
                self.progress_label.place(x=40, y=430)
                self.progress_bar = ttk.Progressbar(self.root, orient="horizontal", length=500, mode="determinate")
                self.progress_bar.place(x=40, y=450)

            total_files = len(manifest["files"])
            self.progress_bar["maximum"] = total_files
            self.progress_bar["value"] = 0
            self.root.update()

            for index, file in enumerate(manifest["files"], start=1):
                rel_path = file["path"]
                if rel_path.endswith("credentials.json") or rel_path.endswith("corsair_launcher.py"):
                    continue

                expected_hash = file.get("sha256", "")
                url = f"{PATCH_SERVER_URL}/{rel_path}"
                dest_path = os.path.join(".", rel_path.replace("/", os.sep))

                self.progress_label.config(text=f"Downloading: {rel_path}")
                self.progress_bar["value"] = index - 1
                self.root.update()

                if os.path.exists(dest_path) and self.sha256_file(dest_path) == expected_hash:
                    continue

                os.makedirs(os.path.dirname(dest_path), exist_ok=True)

                with requests.get(url, headers={"Authorization": SECRET_AUTH_CODE}, stream=True) as response:
                    response.raise_for_status()
                    with open(dest_path, "wb") as f:
                        for chunk in response.iter_content(chunk_size=1024 * 128):
                            if chunk:
                                f.write(chunk)
                                time.sleep(0.01)

                self.progress_bar["value"] = index
                self.root.update()

            with open(VERSION_FILE, "w") as f:
                f.write(manifest["version"])

            self.progress_label.config(text="Update completed.")
            messagebox.showinfo("Update", "Update completed.")
        except Exception as e:
            messagebox.showerror("Error", f"Patch failed: {e}")

    def launch_game(self):
        self.save_credentials()
        user = self.username_var.get().strip()
        pw = self.password_var.get().strip()
        if not user or not pw:
            messagebox.showwarning("Missing Info", "Username and password are required.")
            return

        try:
            subprocess.call("taskkill /f /im blackdesert64.exe", shell=True)
        except:
            pass

        try:
            bin_path = os.path.join(os.getcwd(), "bin64")
            exe_path = os.path.join(bin_path, "blackdesert64.exe")
            if not os.path.exists(exe_path):
                messagebox.showerror("Missing", "blackdesert64.exe not found in bin64.")
                return
            subprocess.Popen([exe_path, f"{user},{pw}"], cwd=bin_path)
            self.root.quit()
        except Exception as e:
            messagebox.showerror("Launch Failed", str(e))


if __name__ == "__main__":
    root = tk.Tk()
    app = LauncherApp(root)
    root.mainloop()
 

Похожие темы

Назад
Сверху