Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
import os import re import sys import time import json import ctypes import logging from datetime import datetime from urllib.parse import urlparse, urljoin, quote from colorama import init, Fore, Style # =================== Setup & Logging =================== init() def _env(key, default=None): return os.environ.get(key, default) LOG_LEVEL_NAME = _env("MYTHMU_LOG_LEVEL", "INFO").upper() LOG_TO_FILE = _env("MYTHMU_LOG_FILE", "").strip() LOG_LEVEL = getattr(logging, LOG_LEVEL_NAME, logging.INFO) class ClientFormatter(logging.Formatter): def format(self, record): ts = datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] color_map = { "DEBUG": Fore.BLUE, "INFO": Fore.YELLOW, "WARNING": Fore.MAGENTA, "ERROR": Fore.RED, "CRITICAL": Fore.RED + Style.BRIGHT, } color = color_map.get(record.levelname, Fore.WHITE) return f"{color}{ts} - CLIENT - {record.levelname} - {record.getMessage()}{Style.RESET_ALL}" log = logging.getLogger("MythMuClient") log.setLevel(logging.DEBUG) _stream = logging.StreamHandler() _stream.setLevel(LOG_LEVEL) _stream.setFormatter(ClientFormatter()) log.handlers.clear() log.addHandler(_stream) if LOG_TO_FILE: try: _fh = logging.FileHandler(LOG_TO_FILE, encoding="utf-8") _fh.setLevel(logging.DEBUG) _fh.setFormatter(logging.Formatter("%(asctime)s - CLIENT - %(levelname)s - %(message)s")) log.addHandler(_fh) log.info(f"File logging enabled -> {LOG_TO_FILE}") except Exception as e: log.error(f"Could not open log file '{LOG_TO_FILE}': {e}") log.info(f"Log level: {LOG_LEVEL_NAME}") ENABLE_DEV_FEATURES = _env("MYTHMU_DEV_TOOLS", "1") not in ("0", "false", "False") try: from cefpython3 import cefpython as cef except Exception as e: log.critical(f"CEF IMPORT ERROR: {e}") sys.exit(1) # =================== API (ONLY login + game/gl) =================== def _norm_base(s: str) -> str: return (s or "").strip().rstrip("/") API_BASE = _env("MYTHMU_API_BASE", "http://127.0.0.1") if API_BASE and not API_BASE.startswith(("http://", "https://")): API_BASE = "http://" + API_BASE API_BASE = _norm_base(API_BASE) API_LOGIN_URL = urljoin(API_BASE + "/", "api/auth/login") API_GAME_GL_URL = urljoin(API_BASE + "/", "api/game/gl") API_ORIGIN = f"{urlparse(API_BASE).scheme}://{urlparse(API_BASE).netloc}" log.info(f"API_BASE = {API_BASE}") log.info(f"API_LOGIN_URL = {API_LOGIN_URL}") log.info(f"API_GAME_GL = {API_GAME_GL_URL}") log.info(f"API_ORIGIN = {API_ORIGIN}") # =================== Paths & helpers =================== def resource_path(rel: str) -> str: try: base = sys._MEIPASS log.debug(f"resource_path: _MEIPASS={base}") except Exception: base = os.path.abspath(".") log.debug(f"resource_path: cwd={base}") if hasattr(sys, "frozen") and not hasattr(sys, "_MEIPASS"): base = os.path.join(os.path.abspath(os.path.dirname(sys.executable)), "LauncherData") log.debug(f"resource_path: frozen -> LauncherData={base}") p = os.path.join(base, rel) log.debug(f"resource_path('{rel}') -> {p}") return p def _file_url(path: str) -> str: ap = os.path.abspath(path).replace("\\", "/") furl = "file:///" + quote(ap) log.debug(f"_file_url({path}) -> {furl}") return furl def set_icon(browser): try: if os.name != "nt": log.debug("set_icon: non-Windows, skip") return base = os.path.abspath(os.path.dirname(sys.executable)) if hasattr(sys, "frozen") else os.path.abspath(".") icon = os.path.join(base, "icon.ico") if not os.path.exists(icon): icon2 = os.path.join(os.path.abspath("."), "icon.ico") if os.path.exists(icon2): icon = icon2 else: log.debug("set_icon: icon.ico not found, skip") return hwnd = browser.GetWindowHandle() user32 = ctypes.windll.user32 WM_SETICON = 0x0080 ICON_SMALL, ICON_BIG = 0, 1 LR_LOADFROMFILE, IMAGE_ICON = 0x0010, 1 hbig = user32.LoadImageW(0, icon, IMAGE_ICON, 0, 0, LR_LOADFROMFILE) if hbig: user32.SendMessageW(hwnd, WM_SETICON, ICON_BIG, hbig) hsm = user32.LoadImageW(0, icon, IMAGE_ICON, 0, 0, LR_LOADFROMFILE) if hsm: user32.SendMessageW(hwnd, WM_SETICON, ICON_SMALL, hsm) log.info(f"Window icon set: {icon}") except Exception as e: log.warning(f"Icon warn: {e}") def _base_dir(): if hasattr(sys, "_MEIPASS"): p = sys._MEIPASS log.debug(f"_base_dir: _MEIPASS={p}") return p if not hasattr(sys, "frozen"): try: cef_dir = os.path.dirname(cef.__file__) if os.path.exists(os.path.join(cef_dir, "subprocess.exe")): log.debug(f"_base_dir: cef dir={cef_dir}") return cef_dir else: log.debug("_base_dir: no subprocess.exe in cef dir, using cwd") return os.path.abspath(".") except NameError: log.debug("_base_dir: NameError on cef.__file__, using cwd") return os.path.abspath(".") exec_dir = os.path.join(os.path.abspath(os.path.dirname(sys.executable)), "LauncherData") log.debug(f"_base_dir: frozen LauncherData={exec_dir}") return exec_dir def get_flash_dll(): try: p = resource_path(os.path.join("Plugins", "pepflashplayer.dll")) if not os.path.exists(p): here = os.path.dirname(os.path.realpath(__file__)) p2 = os.path.join(here, "Plugins", "pepflashplayer.dll") if os.path.exists(p2): log.info(f"PepperFlash DLL (local): {p2}") return p2 log.error("Missing Plugins\\pepflashplayer.dll") return None log.info(f"PepperFlash DLL (bundle): {p}") return p except Exception as e: log.error(f"pepflash error: {e}") return None # =================== (rest of your code is unchanged until main) =================== # ... all your functions (inline login, open_game_from_redirect_url, rewrite, guards, handlers, etc.) ... # =================== Main =================== def main(): cef_inited = False try: base = _base_dir() subproc = os.path.join(base, "subprocess.exe") log.info(f"CEF base dir: {base}") if not os.path.exists(subproc): log.critical(f"Missing CEF subprocess: {subproc}") sys.exit(1) dll = get_flash_dll() if not dll: sys.exit(1) settings = { "resources_dir_path": base, "locales_dir_path": os.path.join(base, "locales"), "browser_subprocess_path": subproc, "log_severity": cef.LOGSEVERITY_DISABLE, } switches = { "ppapi-flash-path": dll, "allow-outdated-plugins": True, "no-sandbox": "", "disable-gpu-vsync": "", "disable-features": "VizDisplayCompositor", "allow-file-access-from-files": "", "allow-file-access": "", "disable-gpu": "", } log.info("CEF settings: " + json.dumps(settings, indent=2)) log.info("CEF switches: " + json.dumps(switches, indent=2)) cef.Initialize(settings=settings, switches=switches) cef_inited = True log.info("CEF initialized") browser = cef.CreateBrowserSync(url="about:blank", window_title="MythMu") set_icon(browser) # JS bridge bindings = cef.JavascriptBindings(bindToFrames=False, bindToPopups=False) def _open_game_from_redirect_url(final_url=None): log.info(f"[JS bridge] openGameFromRedirectUrl called: {final_url}") try: open_game_from_redirect_url(browser, final_url or "") except Exception as e: log.error(f"openGameFromRedirectUrl error: {e}") bindings.SetFunction("openGameFromRedirectUrl", _open_game_from_redirect_url) browser.SetJavascriptBindings(bindings) log.info("Javascript bridge registered") handler = AppHandler(browser) browser.SetClientHandler(handler) if ENABLE_DEV_FEATURES: try: browser.ShowDevTools() log.info("DevTools opened (ENABLE_DEV_FEATURES=1)") except Exception as e: log.warning(f"DevTools warn: {e}") else: log.info("DevTools disabled (ENABLE_DEV_FEATURES=0)") # === Fix: Reset resize cursor when refocusing window === import threading def force_cursor_reset(hwnd=None): try: user32 = ctypes.windll.user32 if not hwnd: hwnd = browser.GetWindowHandle() user32.SetCursor(user32.LoadCursorW(0, 32512)) # IDC_ARROW log.debug("Cursor reset to IDC_ARROW") except Exception as e: log.warning(f"Cursor reset failed: {e}") def on_got_focus(browser, **_): log.debug("Browser gained focus — resetting cursor") threading.Timer(0.05, lambda: force_cursor_reset(browser.GetWindowHandle())).start() focus_handler = type("FocusHandler", (), {"OnGotFocus": on_got_focus})() browser.SetClientHandler(focus_handler) log.info("Focus handler for cursor reset registered") # === End fix === log_local_assets_presence() load_local_login(browser) log.info("Entering CEF message loop") cef.MessageLoop() except Exception as e: log.critical(f"CRITICAL in main: {e}") finally: try: if cef_inited: cef.Shutdown() log.info("CEF shutdown completed") except Exception as e: log.error(f"Shutdown error: {e}") if __name__ == "__main__": main()
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)