Example #1
0
    def remove_shortcut(self):
        if not is_windows():
            logger.debug("Removing shortcut")
            return

        if self.shortcut_path.exists():
            self.shortcut_path.unlink()
Example #2
0
def s99_client_path():
    if IS_EXE:
        client_dir = BASE_DIR
    else:
        client_dir = BASE_DIR / "../../dist"

    if is_windows():
        return client_dir / "s99-client.exe"

    return client_dir / "s99-client"
Example #3
0
    def make_shortcut(self):
        version = self.modlunky_config.playlunky_version
        if not version:
            return

        exe_path = PLAYLUNKY_DATA_DIR / version / PLAYLUNKY_EXE

        if not is_windows():
            logger.debug("Making shortcut to %s", exe_path)
            return

        install_dir = self.modlunky_config.install_dir
        arguments = f'--exe_dir="{install_dir}"'

        with winshell.shortcut(f"{self.shortcut_path}") as link:
            link.path = f"{exe_path}"
            link.working_directory = f"{exe_path.parent}"
            link.arguments = arguments
            link.description = "Shortcut to playlunky"
Example #4
0
    def check_requirements():
        # Only check requirements in dev environments
        if IS_EXE:
            return

        all_req_files = [
            "requirements.txt",
            "requirements-dev.txt",
        ]
        if is_windows():
            all_req_files.append("requirements-win.txt")

        installed = pip_api.installed_distributions()
        missing_req_files = set()
        for req_file in all_req_files:
            requirements = pip_api.parse_requirements(req_file)

            for req in requirements.values():
                if req.name not in installed:
                    missing_req_files.add(req_file)
                    logger.warning("Missing required package '%s'", req.name)
                    continue

                installed_ver = installed[req.name].version
                if req.specifier.contains(installed_ver):
                    continue
                missing_req_files.add(req_file)
                logger.warning(
                    "Installed version of '%s' is %s, doesen't meet %s",
                    req.name,
                    installed_ver,
                    req.specifier,
                )

        if not missing_req_files:
            return
        r_args = "  ".join([f"-r {r}" for r in missing_req_files])
        logger.warning(
            "Some requirements aren't met. Run pip install --upgrade %s",
            r_args,
        )
Example #5
0
 def _unbind_from_mousewheel(self, _event):
     if is_windows():
         self.canvas.unbind_all("<MouseWheel>")
     else:
         self.canvas.unbind_all("<Button-4>")
         self.canvas.unbind_all("<Button-5>")
Example #6
0
 def _bind_to_mousewheel(self, _event):
     if is_windows():
         self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
     else:
         self.canvas.bind_all("<Button-4>", self._on_mousewheel)
         self.canvas.bind_all("<Button-5>", self._on_mousewheel)
Example #7
0
try:
    import winreg
except ImportError:
    winreg = None

from pathlib import Path
from shutil import copyfile
from urllib.parse import urlparse, urlunparse

from platformdirs import user_config_dir, user_data_dir, user_cache_dir
from serde import serialize, deserialize
import serde.json

from modlunky2.utils import is_windows

if is_windows():
    # Import for pyinstaller to detect this module
    import platformdirs.windows  # pylint: disable=unused-import

PROGRAMS_KEY = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
DEFAULT_PATH = Path("C:/Program Files (x86)/Steam/steamapps/common/Spelunky 2")
EXE_NAME = "Spel2.exe"

APP_AUTHOR = "spelunky.fyi"
APP_NAME = "modlunky2"
CONFIG_DIR = Path(user_config_dir(APP_NAME, APP_AUTHOR))
DATA_DIR = Path(user_data_dir(APP_NAME, APP_AUTHOR))
CACHE_DIR = Path(user_cache_dir(APP_NAME, APP_AUTHOR))
SHOW_PACKING_DEFAULT = False

MIN_WIDTH = 1280
Example #8
0
    def __init__(self, modlunky_config: Config, log_level=logging.INFO):
        logger.debug("Initializing UI")
        self.modlunky_config = modlunky_config

        self.current_version = current_version()
        self.latest_version = None
        self.needs_update = False

        self._shutdown_handlers = []
        self._shutting_down = False

        self.log_queue = Queue()
        self.queue_handler = QueueHandler(self.log_queue)
        register_queue_handler(self.queue_handler, log_level)
        self.task_manager = TaskManager(self.log_queue, log_level)
        self.task_manager.register_task(
            "modlunky2:update_start",
            update_start,
            True,
            on_complete="modlunky2:update_complete",
        )
        self.task_manager.register_handler("modlunky2:update_complete",
                                           self.update_complete)

        self.task_manager.register_task(
            "modlunky2:check_for_latest",
            check_for_latest,
            True,
        )
        self.task_manager.register_handler("modlunky2:latest_version",
                                           self.handle_modlunky_latest_version)

        self.root = tk.Tk(className="Modlunky2")
        self.load_themes()
        style = ttk.Style(self.root)
        self.root.default_theme = style.theme_use()
        valid_themes = self.root.call("ttk::themes")

        self.root.title("Modlunky 2")
        self.root.geometry(modlunky_config.geometry)
        self.last_geometry = modlunky_config.geometry
        self.root.bind("<Configure>", self.handle_resize)
        if modlunky_config.theme and modlunky_config.theme in valid_themes:
            style.theme_use(modlunky_config.theme)
        self.root.event_add("<<ThemeChange>>", "None")

        style.configure(
            "ModList.TCheckbutton",
            font=("Segoe UI", 12, "bold"),
            # TODO: dynamic sizing for larger windows
            wraplength="640",
        )
        style.configure("Update.TButton", font="sans 12 bold")
        style.configure("TOptionMenu", anchor="w")
        style.configure("Link.TLabel", foreground="royal blue")
        style.configure(
            "Thicc.TButton",
            font=("Arial", 16, "bold"),
        )

        default_background = style.lookup("TFrame", "background")
        self.root.configure(bg=default_background)

        self.root.minsize(MIN_WIDTH, MIN_HEIGHT)
        self.icon_png = PhotoImage(file=BASE_DIR / "static/images/icon.png")
        self.root.iconphoto(False, self.icon_png)

        sys.excepthook = exception_logger
        self.root.report_callback_exception = exception_logger

        self.root.grid_columnconfigure(0, weight=1)
        self.root.grid_rowconfigure(0, weight=1)

        self.top_frame = ttk.Frame(self.root)
        self.top_frame.grid(row=0, column=0, sticky="nsew")

        self.top_frame.grid_columnconfigure(0, weight=1)
        self.top_frame.grid_rowconfigure(0, weight=0)
        self.top_frame.grid_rowconfigure(1, weight=1)
        self.top_frame.grid_rowconfigure(2, weight=0)

        self.update_frame = ttk.LabelFrame(self.top_frame,
                                           text="Modlunky2 Update Available")
        self.update_frame.columnconfigure(0, weight=1)
        self.update_frame.rowconfigure(0, weight=1)

        self.update_button = ttk.Button(
            self.update_frame,
            text="Update Now!",
            command=self.update,
            style="Update.TButton",
        )

        # Handle shutting down cleanly
        self.root.protocol("WM_DELETE_WINDOW", self.quit)
        self.root.bind("<Escape>", self.quit)

        self.tabs = {}
        self.tab_control = ttk.Notebook(self.top_frame)

        logger.debug("Registering Tabs")
        self.register_tab(
            "Playlunky",
            PlayTab,
            tab_control=self.tab_control,
            modlunky_config=modlunky_config,
            task_manager=self.task_manager,
        )
        self.register_tab(
            "Install Mods",
            InstallTab,
            tab_control=self.tab_control,
            modlunky_config=modlunky_config,
            task_manager=self.task_manager,
        )
        self.register_tab(
            "Overlunky",
            OverlunkyTab,
            tab_control=self.tab_control,
            modlunky_config=modlunky_config,
            task_manager=self.task_manager,
        )
        self.register_tab(
            "Extract Assets",
            ExtractTab,
            tab_control=self.tab_control,
            modlunky_config=modlunky_config,
            task_manager=self.task_manager,
        )
        if self.modlunky_config.show_packing:
            self.register_tab(
                "Pack Assets (Deprecated)",
                PackTab,
                tab_control=self.tab_control,
                modlunky_config=modlunky_config,
                task_manager=self.task_manager,
            )
        self.register_tab(
            "Level Editor",
            LevelsTab,
            tab_control=self.tab_control,
            modlunky_ui=self,
            modlunky_config=modlunky_config,
        )
        if is_windows():
            self.register_tab(
                "Trackers",
                TrackersTab,
                tab_control=self.tab_control,
                ml_config=modlunky_config,
            )
        self.register_tab(
            "Settings",
            SettingsTab,
            tab_control=self.tab_control,
            modlunky_config=modlunky_config,
        )

        self.select_last_tab()
        self.tab_control.bind("<<NotebookTabChanged>>", self.on_tab_change)
        self.tab_control.grid(column=0,
                              row=1,
                              padx=2,
                              pady=(4, 0),
                              sticky="nsew")

        self.console_frame = ttk.LabelFrame(self.top_frame, text="Console")
        self.console_frame.columnconfigure(0, weight=1)
        self.console_frame.rowconfigure(0, weight=1)

        self.console = ConsoleWindow(self.queue_handler, self.console_frame)
        self.console.grid(column=0, row=0, padx=2, pady=2, sticky="ew")

        self.version_label = ttk.Label(self.top_frame,
                                       text=f"v{self.current_version}",
                                       font="Helvitica 9 italic")
        self.version_label.grid(column=0, row=3, padx=5, sticky="e")

        self.ws_thread = None
        self.task_manager.start_process()
        self.last_ping = time.time()
        self.root.after(100, self.after_task_manager)
        self.root.after(1000, self.after_ws_thread)
        self.root.after(1000, self.after_record_win)
        self.check_for_updates()
        self.check_requirements()