Skip to content

Latest commit

 

History

History
47 lines (39 loc) · 9.57 KB

File metadata and controls

47 lines (39 loc) · 9.57 KB

INFO ./docs/*

Wand Enhancer Agent Notes

This repository patches the Wand Electron app from a .NET Framework WPF desktop tool. Keep changes narrow and preserve the patch pipeline invariants.

Remote Web Panel

  • The default local remote port is 3223. Keep bridge and frontend constants aligned; C# must not duplicate the presentation URL or port.
  • The embedded panel must stay small because the desktop patcher embeds it and then injects it into Wand's app.asar.
  • Remote tooltip links and every rendered remote-qr-code are redirected by web-panel/bridge/scripts/default/remote-popup-cleanup.js. It reuses Wand's loaded QR renderer through the webpack runtime, keeps the local URL visible as a fallback, and hides the Pro onboarding remote mobile app card. Do not reintroduce C# ASAR patches for the tooltip URL or QR component; a changed UI bundle must not make the whole remote-panel patch fail.
  • Production builds must not include mock data, debug routes, sourcemaps, local fonts, heavy icon libraries, or runtime class helper packages.
  • The Electron bridge is authored as TypeScript under web-panel/bridge/src/, but production runtime must be bundled/minified into web-panel/dist/bridge.cjs by pnpm run build:bridge. Do not copy bridge source into Wand or embed it as ASAR resources.
  • Mock/demo data is dev-only and must be reached through import.meta.env.DEV dynamic imports.
  • Source can use React-compatible imports, but production runtime resolves them to Preact aliases in web-panel/vite.config.ts.
  • UI uses Tailwind CSS and lightweight local primitives under web-panel/src/shared/ui/.
  • Default renderer script sources live in web-panel/bridge/scripts/default/ and are bundled/minified into web-panel/dist/renderer-scripts/ by pnpm run build:bridge. Custom user scripts are selected in the WPF patch modal and copied from PatchConfig.CustomScriptPaths; only existing .js files are accepted. A local renderer-scripts/ folder next to the patcher exe is still copied as an advanced fallback.
  • web-panel/bridge/scripts/default/installed-apps-sync.js resolves Wand's renderer services/store and publishes My Games snapshots through the wand-remote-installed-apps IPC channel. The synced list must mirror Wand's my_games source criteria: catalog games come from installedGameVersions, and extra installed unsupported titles come from correlatedUnavailableTitles whose games[].correlationIds match installedApps.
  • If the injected renderer cannot read a populated correlatedUnavailableTitles slice from the live store, installed-apps-sync.js must fall back to Wand's /v3/unavailable_titles correlation lookup through the renderer API client instead of degrading to raw install entries or an empty My Games list.
  • Installed app snapshots should include game artwork in imageUrl when possible. installed-apps-sync.js must prefer Wand's own client icon CDN shape https://api-cdn.wemod.com/steam_community/<steamAppId>/client_icon/96.webp whenever the matched title/game/version metadata contains a Steam AppID, regardless of install platform. Do not assume steamAppId is a flat property; search nested steam* metadata before falling back to installed Steam sku. If metadata still does not expose the icon, fall back to the rendered Wand sidebar DOM (.sidebar-game-row-image background-image) keyed by titleId parsed from data-tooltip-trigger-for. The web panel GameCover must tolerate broken artwork URLs and fall back to its text cover.
  • The same renderer sync script also forwards lifecycle state through wand-remote-game-status: game-launched / game-ended come from Wand's launch monitor service, and trainer runtime comes from the running-trainer visibility service. The web panel consumes this as the game_status websocket message.
  • When Wand does not emit a game-launched event but a trainer is already active, wand-remote-game-status must synthesize a running session from the running-trainer visibility payload so the remote panel does not show an idle game session next to a running trainer.
  • The websocket hello snapshot must still send cached installed_apps and game_status even when no trainer snapshot is active yet; do not reintroduce a handshake path that returns early after trainer_changed.
  • Remote Play/Stop uses the websocket remote_command message. The bridge forwards it over wand-remote-command / wand-remote-command-response, and installed-apps-sync.js resolves Wand's trainer API + trainer service to launch a trainer for a gameId or end the current trainer.
  • Remote Play must construct Wand's real trainer launch request class (69482.vO) before calling trainerService.launch(...). Passing a plain object launches the game process but breaks Wand's getMetadata(vO)-based trainer state, causing missing status, disappearing play/close buttons, and stuck loading behavior.
  • Pro activation is a C# asar patch (EPatchType.ActivatePro, independent of the remote panel / bridge). It rewrites three account-returning service methods to inject subscription:{period:"yearly",state:"active"} into the response before it reaches the store: getUserAccount and setAccountWandBrandExperience (Resolver-style, service field via <service_name> placeholder) and setAccountLanguage (BuildSetAccountLanguagePatch PatchFactory — captures the real param names + the original post("/v3/account/language",{...}) expr and wraps .then). A fourth patch (setAccountReducer) rewrites the ACTION_SET_ACCOUNT store reducer so any account write (periodic refreshAccount, push/profile updates, etc.) keeps Pro even when it bypasses those API methods. Pro is am(account) = !!account.subscription (flags/512 are irrelevant). setAccountLanguage is the one the original two patches missed, which is why Pro dropped on language change. If a future Wand build changes these method bodies, re-derive the regexes against the live app-*.bundle.js (do NOT trust .source/new — it is a different version).

ASAR Patch Pipeline

  • Preserve and restore both resources/app.asar and resources/app.asar.unpacked backups.
  • Inject web-panel/dist as remote-panel/; it must already contain bridge.cjs and generated default renderer scripts under renderer-scripts/. Selected/local custom renderer scripts are then copied under remote-panel/renderer-scripts.
  • Do not commit extracted .source/ or .sources/ output. Recreate it only for reverse-engineering sessions.
  • AsarSharp.AsarExtractor.ExtractAll must skip unpacked entries when their source path equals the destination (in-place extraction is a self-copy that fails on locked files like TrainerLib_x64.dll) and silently skip unpacked entries whose source is missing on disk (e.g. auxiliary/GameLauncher.exe removed by an installer). Do not reintroduce hard failure on either case.
  • The DevToolsOnF12 patch anchors on the Electron main-process <app>.whenReady().then( site and attaches a before-input-event hook to every BrowserWindow.webContents. Do not patch the renderer keydown listener — the minified ACTION_OPEN_DEV_TOOLS dispatch site is not stable across Wand releases.
  • Cheats can be pinned per game in the web panel via pinned-storage.ts (localStorage key wand-remote.pinned-cheats.v1:<gameId>). Pinned cheats render as a virtual pinned category at the top of the list; their normal category placement is preserved.
  • Custom quick presets are per trainer/game and stored by preset-storage.ts under localStorage key wand-remote.presets.v1:<gameId-or-trainerId>. Presets capture persistent cheat values only; do not include button one-shot cheats in saved presets.
  • All localStorage access in web-panel/src/ MUST go through web-panel/src/shared/storage.ts (loadJson / saveJson / loadStringSet / saveStringSet). Do not reintroduce per-capability try/catch + JSON.parse duplication. Trainer/game storage IDs are derived through the shared getTrainerStorageId(trainer) helper; do not re-implement the gameId → titleId → trainerId → 'global' precedence inline.
  • Shared web protocol version, port, and HTTP/WS paths live in web-panel/protocol/web-contract.json. Bridge-only IPC channels, WS opcodes, and renderer injection delays live in web-panel/bridge/src/constants.ts. Do not redeclare these values inline.
  • UI string-union types follow the E* enum convention from .claude/rules/frontend-conventions.md (currently ECheatType in protocol/messages.ts, EConnectionStatus in remote-session/remote-session.reducer.ts); wire string values must remain on the right-hand side of enum members. Reducer action tags stay as discriminated-union string literals.
  • Cheat input controls live one-per-file under web-panel/src/trainer/controls/; shared SliderTrack / StepButton / ControlInternalProps are in controls/shared.tsx and number formatting helpers in controls/format-number.ts. controls/CheatControl.tsx remains a thin dispatcher keyed by ECheatType.
  • Mobile drawer performance is sensitive to backdrop-filter. Keep drawer panels and nested glass controls blur-free under coarse pointers, and do not add per-row backdrop-blur-* inside drawer lists.

Validation

  • Web panel build: cd web-panel && pnpm run build (runs type-check, Vite build, then build:bridge into dist).
  • Bridge/script syntax checks after build: node --check web-panel/dist/bridge.cjs and node --check web-panel/dist/renderer-scripts/remote-popup-cleanup.js.
  • Production dist should contain only static assets and should not contain mock-instance, Mock Adventure, Simulation, Debug session, mock=1, demo-session, vite.svg, tailwind-merge, class-variance-authority, or clsx.