Files
Modernization-Tool/setup_tool_dynamic.py
T

204 lines
7.5 KiB
Python

import os
import shutil
import tkinter as tk
from tkinter import ttk, messagebox
import remote_packages
from setup_tool import WowSetupTool, ToolTip, get_base_path
class ModernWowSetupTool(WowSetupTool):
"""Feature-branch entry point adding live GitHub package updates."""
def __init__(self, root):
# Define this before WowSetupTool.__init__ because the parent constructor
# calls self.build_ui(), which dispatches to our overridden Plugins tab.
self.client_tweak = tk.StringVar(master=root, value="none")
super().__init__(root)
def build_plugins_tab(self, parent):
container = ttk.Frame(parent)
container.pack(fill="both", expand=True, padx=10, pady=10)
top = ttk.Frame(container)
top.pack(fill="both", expand=True)
left_frame = ttk.LabelFrame(top, text="Recommended Core")
left_frame.pack(side="left", fill="both", expand=True, padx=(0, 5))
ttk.Label(
left_frame,
text="These are highly recommended for performance\nand client stability.",
font=("", 8, "italic"),
).pack(anchor="w", padx=10, pady=10)
for dll, var in self.core_plugins.items():
cb = ttk.Checkbutton(left_frame, text=dll, variable=var)
cb.pack(anchor="w", padx=10, pady=4)
ToolTip(cb, self.descriptions.get(dll, ""))
right_frame = ttk.LabelFrame(top, text="Optional WeirdUtils")
right_frame.pack(side="right", fill="both", expand=True, padx=(5, 0))
ttk.Label(
right_frame,
text="Additional quality-of-life adjustments.",
font=("", 8, "italic"),
).pack(anchor="w", padx=10, pady=10)
for dll, var in self.optional_plugins.items():
cb = ttk.Checkbutton(right_frame, text=dll, variable=var)
cb.pack(anchor="w", padx=10, pady=4)
ToolTip(cb, self.descriptions.get(dll, ""))
tweaks_frame = ttk.LabelFrame(container, text="Tweaks")
tweaks_frame.pack(fill="x", pady=(10, 0))
ttk.Label(
tweaks_frame,
text="Choose one. ClassicAPI and Auction Query Throttle are mutually exclusive.",
font=("", 8, "italic"),
).pack(anchor="w", padx=10, pady=(7, 3))
choices = ttk.Frame(tweaks_frame)
choices.pack(fill="x", padx=10, pady=(0, 8))
rb_none = ttk.Radiobutton(
choices, text="None", variable=self.client_tweak, value="none"
)
rb_none.pack(side="left", padx=(0, 16))
rb_classicapi = ttk.Radiobutton(
choices,
text="ClassicAPI",
variable=self.client_tweak,
value="classicapi",
)
rb_classicapi.pack(side="left", padx=(0, 16))
ToolTip(
rb_classicapi,
"Downloads and installs the latest stable ClassicAPI.dll release from "
"brues-code/ClassicAPI when setup is applied.",
)
rb_auction = ttk.Radiobutton(
choices,
text="Auction Query Throttle",
variable=self.client_tweak,
value="auction",
)
rb_auction.pack(side="left")
ToolTip(
rb_auction,
"Downloads and installs the latest stable AuctionQueryThrottle.dll release "
"from brues-code/AuctionQueryThrottle when setup is applied.",
)
def clean_unselected_files(self, target):
super().clean_unselected_files(target)
choice = self.client_tweak.get()
managed = {
"classicapi": "ClassicAPI.dll",
"auction": "AuctionQueryThrottle.dll",
}
# Always remove the non-selected alternative. Selecting None removes both.
for key, filename in managed.items():
if choice != key:
path = os.path.join(target, filename)
if os.path.exists(path):
try:
os.remove(path)
except OSError:
pass
def _fallback_core_dll(self, payload_base, target, dll_name, error):
source_dll = os.path.join(payload_base, dll_name)
if not os.path.exists(source_dll):
raise RuntimeError(
f"Could not download the latest {dll_name}, and no bundled fallback exists.\n\n{error}"
) from error
shutil.copy2(source_dll, target)
messagebox.showwarning(
"Online update unavailable",
f"Could not download the latest {dll_name}.\n\n"
f"The bundled version will be used instead.\n\nDetails: {error}",
)
def configure_plugins(self, target):
payload_base = os.path.join(get_base_path(), "Payload")
payload_weirdu = os.path.join(payload_base, "WeirdUtils")
dlls_text_lines = []
if self.gpu_type.get() != "AMD":
dlls_text_lines.append("dxvk")
# Core plugins. UnitXP and SuperWoW are refreshed from their upstream
# sources every time setup is applied. Their bundled copies remain an
# offline fallback so this feature does not reduce current reliability.
for dll_name, var in self.core_plugins.items():
if not var.get():
continue
if dll_name == "UnitXP_SP3.dll":
try:
remote_packages.install_unitxp(target)
except Exception as exc:
self._fallback_core_dll(payload_base, target, dll_name, exc)
elif dll_name == "SuperWoWhook.dll":
try:
remote_packages.install_superwow(target)
except Exception as exc:
self._fallback_core_dll(payload_base, target, dll_name, exc)
else:
source_dll = os.path.join(payload_base, dll_name)
if os.path.exists(source_dll):
shutil.copy2(source_dll, target)
dlls_text_lines.append(dll_name)
# Clean dependent addons when the matching core DLL is disabled.
for dll_name, addon_folder in self.addon_dependencies.items():
core_var = self.core_plugins.get(dll_name)
if core_var is not None and not core_var.get():
addon_path = os.path.join(target, "Interface", "AddOns", addon_folder)
if os.path.exists(addon_path):
shutil.rmtree(addon_path, ignore_errors=True)
# Optional WeirdUtils.
for dll_name, var in self.optional_plugins.items():
if var.get():
source_dll = os.path.join(payload_weirdu, dll_name)
if os.path.exists(source_dll):
shutil.copy2(source_dll, target)
dlls_text_lines.append(dll_name)
# Mutually-exclusive live Tweaks.
choice = self.client_tweak.get()
if choice == "classicapi":
try:
remote_packages.install_classicapi(target)
except Exception as exc:
raise RuntimeError(f"ClassicAPI update failed:\n{exc}") from exc
dlls_text_lines.append("ClassicAPI.dll")
elif choice == "auction":
try:
remote_packages.install_auction_query_throttle(target)
except Exception as exc:
raise RuntimeError(f"Auction Query Throttle update failed:\n{exc}") from exc
dlls_text_lines.append("AuctionQueryThrottle.dll")
with open(os.path.join(target, "dlls.txt"), "w") as f:
f.write("\n".join(dlls_text_lines))
if __name__ == "__main__":
root = tk.Tk()
app = ModernWowSetupTool(root)
root.mainloop()