mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-08-28 23:01:13 +00:00
9.6 KiB
9.6 KiB
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 C# and frontend constants aligned. - The embedded panel must stay small because the desktop patcher embeds it and then injects it into Wand's
app.asar. - 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 modular CommonJS source under
web-panel/bridge/source.cjsandweb-panel/bridge/bridge-modules/, but production runtime must be bundled/minified intoweb-panel/dist/bridge.cjsbypnpm run build:bridge. Do not copybridge-modulesinto Wand or embed them as ASAR resources. - Mock/demo data is dev-only and must be reached through
import.meta.env.DEVdynamic 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 shadcn-style local primitives under
web-panel/src/components/ui/. - Default renderer script sources live in
web-panel/bridge/scripts/default/and are bundled/minified intoweb-panel/dist/renderer-scripts/bypnpm run build:bridge. Custom user scripts are selected in the WPF patch modal and copied fromPatchConfig.CustomScriptPaths; only existing.jsfiles are accepted. A localrenderer-scripts/folder next to the patcher exe is still copied as an advanced fallback. web-panel/bridge/scripts/default/installed-apps-sync.jsresolves Wand's renderer services/store and publishesMy Gamessnapshots through thewand-remote-installed-appsIPC channel. The synced list must mirror Wand'smy_gamessource criteria: catalog games come frominstalledGameVersions, and extra installed unsupported titles come fromcorrelatedUnavailableTitleswhosegames[].correlationIdsmatchinstalledApps.- If the injected renderer cannot read a populated
correlatedUnavailableTitlesslice from the live store,installed-apps-sync.jsmust fall back to Wand's/v3/unavailable_titlescorrelation lookup through the renderer API client instead of degrading to raw install entries or an emptyMy Gameslist. - Installed app snapshots should include game artwork in
imageUrlwhen possible.installed-apps-sync.jsmust prefer Wand's own client icon CDN shapehttps://api-cdn.wemod.com/steam_community/<steamAppId>/client_icon/96.webpwhenever the matched title/game/version metadata contains a Steam AppID, regardless of install platform. Do not assumesteamAppIdis a flat property; search nestedsteam*metadata before falling back to installed Steamsku. If metadata still does not expose the icon, fall back to the rendered Wand sidebar DOM (.sidebar-game-row-imagebackground-image) keyed bytitleIdparsed fromdata-tooltip-trigger-for. The web panelGameCovermust 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-endedcome from Wand's launch monitor service, and trainer runtime comes from the running-trainer visibility service. The web panel consumes this as thegame_statuswebsocket message. - When Wand does not emit a
game-launchedevent but a trainer is already active,wand-remote-game-statusmust 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
hellosnapshot must still send cachedinstalled_appsandgame_statuseven when no trainer snapshot is active yet; do not reintroduce a handshake path that returns early aftertrainer_changed. - Remote Play/Stop uses the websocket
remote_commandmessage. The bridge forwards it overwand-remote-command/wand-remote-command-response, andinstalled-apps-sync.jsresolves Wand's trainer API + trainer service to launch a trainer for agameIdor end the current trainer. - Remote Play must construct Wand's real trainer launch request class (
69482.vO) before callingtrainerService.launch(...). Passing a plain object launches the game process but breaks Wand'sgetMetadata(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 injectsubscription:{period:"yearly",state:"active"}into the response before it reaches the store:getUserAccountandsetAccountWandBrandExperience(Resolver-style, service field via<service_name>placeholder) andsetAccountLanguage(BuildSetAccountLanguagePatchPatchFactory — captures the real param names + the originalpost("/v3/account/language",{...})expr and wraps.then). Pro isam(account) = !!account.subscription(flags/512 are irrelevant).setAccountLanguageis 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 liveapp-*.bundle.js(do NOT trust.source/new— it is a different version).
ASAR Patch Pipeline
- Preserve and restore both
resources/app.asarandresources/app.asar.unpackedbackups. - Inject
web-panel/distasremote-panel/; it must already containbridge.cjsand generated default renderer scripts underrenderer-scripts/. Selected/local custom renderer scripts are then copied underremote-panel/renderer-scripts. - Do not commit extracted
.source/or.sources/output. Recreate it only for reverse-engineering sessions. AsarSharp.AsarExtractor.ExtractAllmust skip unpacked entries when their source path equals the destination (in-place extraction is a self-copy that fails on locked files likeTrainerLib_x64.dll) and silently skip unpacked entries whose source is missing on disk (e.g.auxiliary/GameLauncher.exeremoved by an installer). Do not reintroduce hard failure on either case.- The
DevToolsOnF12patch anchors on the Electron main-process<app>.whenReady().then(site and attaches abefore-input-eventhook to everyBrowserWindow.webContents. Do not patch the renderer keydown listener — the minifiedACTION_OPEN_DEV_TOOLSdispatch site is not stable across Wand releases. - Cheats can be pinned per game in the web panel via
pinned-storage.ts(localStoragekeywand-remote.pinned-cheats.v1:<gameId>). Pinned cheats render as a virtualpinnedcategory at the top of the list; their normal category placement is preserved. - Custom quick presets are per trainer/game and stored by
preset-storage.tsunderlocalStoragekeywand-remote.presets.v1:<gameId-or-trainerId>. Presets capture persistent cheat values only; do not includebuttonone-shot cheats in saved presets. - All
localStorageaccess inweb-panel/src/features/remote-panel/MUST go through the shared helpers instorage.ts(loadJson/saveJson/loadStringSet/saveStringSet). Do not reintroduce per-moduletry/catch+JSON.parseduplication inpinned-storage,preset-storage, orgame-pin-storage. Trainer/game storage IDs are derived through the sharedgetTrainerStorageId(trainer)helper instorage.ts; do not re-implement thegameId → titleId → trainerId → 'global'precedence inline. - All shared bridge port/path/IPC channel/WS-opcode/protocol-version constants live in
web-panel/bridge/bridge-modules/constants.cjs(exportsIPC_CHANNEL,WS_OPCODE,BRIDGE_PROTOCOL_VERSION,BRIDGE_SERVER_VERSION,RENDERER_INJECTION_DELAYS_MS). Do not redeclare3223,/remote/*, IPC channel strings, raw WS opcode numbers (1/8/9/10), or the 500/2000 ms injection delays inline. The renderer-script-side equivalents (e.g.vO/TRAINER_LAUNCH_REQUEST_EXPORT_KEY, snapshot key prefixes, bootstrap log throttle) live inweb-panel/bridge/scripts/default/installed-apps-sync/constants.js. - UI string-union types follow the
E*enum convention from.claude/rules/frontend-conventions.md(currentlyECheatTypeinprotocol.ts,EConnectionStatusinstate.ts); the wire string values must remain on the right-hand side of the enum members. ReducerPanelActiontypetags stay as discriminated-union string literals (the union itself provides the discrimination — converting it to an enum loses pattern matching). - Cheat input controls live one-per-file under
web-panel/src/features/remote-panel/controls/(ToggleControl,SliderControl,ScalarControl,NumberControl,ActionButton,SelectionControl,IncrementalControl); sharedSliderTrack/StepButton/ControlInternalPropsare incontrols/shared.tsxand number formatting helpers incontrols/format-number.ts.controls/CheatControl.tsxis a thin dispatcher map keyed byECheatType— do not inline new control bodies into it. - 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-rowbackdrop-blur-*inside drawer lists.
Validation
- Web panel build:
cd web-panel && pnpm run build(runs type-check, Vite build, thenbuild:bridgeintodist). - Bridge/script syntax checks after build:
node --check web-panel/dist/bridge.cjsandnode --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, orclsx.