Автор не я взял с Англ сайта
Приветствую. Клиентов много, и я благодарен им за их труд. Я даю вам свой, потому что он работает вместе с клиентским обновлением.
Вам понадобятся 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-адрес сервера и код авторизации, чтобы они соответствовали вашему серверу обновлений).
Приветствую. Клиентов много, и я благодарен им за их труд. Я даю вам свой, потому что он работает вместе с клиентским обновлением.
Вам понадобятся 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()