204 lines
7.5 KiB
Python
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()
|