diff --git a/AGENTS.md b/AGENTS.md index 590d5b3..8766e1f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ This repository patches the Wand Electron app from a .NET Framework WPF desktop - 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 `` placeholder) and `setAccountLanguage` (`BuildSetAccountLanguagePatch` PatchFactory — captures the real param names + the original `post("/v3/account/language",{...})` expr and wraps `.then`). 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). +- 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 `` 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 diff --git a/WandEnhancer/Core/EnhancerConfig.cs b/WandEnhancer/Core/EnhancerConfig.cs index ec8d985..a4d08a3 100644 --- a/WandEnhancer/Core/EnhancerConfig.cs +++ b/WandEnhancer/Core/EnhancerConfig.cs @@ -50,6 +50,17 @@ namespace WandEnhancer.Core return $"setAccountLanguage({parameters}){{return ({expr}).then(response=>{{response&&\"object\"==typeof response&&(response.subscription={{period:\"yearly\",state:\"active\"}});return response;}})}}"; } + private static string BuildSetAccountReducerPatch(Match match) + { + var decl = RequireGroup(match, "decl", "setAccountReducer"); + var fn = RequireGroup(match, "fn", "setAccountReducer"); + var parameters = RequireGroup(match, "params", "setAccountReducer"); + var state = RequireGroup(match, "state", "setAccountReducer"); + var account = RequireGroup(match, "account", "setAccountReducer"); + return + $"const {decl}=\"ACTION_SET_ACCOUNT\";function {fn}({parameters}){{const a={account};return a&&\"object\"==typeof a&&(a={{...a,subscription:{{period:\"yearly\",state:\"active\"}}}}),{{...{state},account:a}}}}"; + } + private static string BuildRemoteBridgeResetPatch(Match match) { var source = match.Value; @@ -143,6 +154,19 @@ namespace WandEnhancer.Core @"setAccountLanguage\((?[^)]*)\)\{\s*return\s+(?this\.#\w+\.post\(""/v3/account/language"",\{[^}]*\}\))\s*;?\s*\}", RegexOptions.Singleline), PatchFactory = BuildSetAccountLanguagePatch + }, + new PatchEntry + { + // Last-resort guard: any code path that dispatches ACTION_SET_ACCOUNT + // (periodic refreshAccount, push updates, profile edits, etc.) must keep + // subscription on the store object even when it bypasses the account API + // service methods patched above. + Name = "setAccountReducer", + SearchHints = new[] { "ACTION_SET_ACCOUNT" }, + Target = new Regex( + @"const (?\w+)=""ACTION_SET_ACCOUNT"";function (?\w+)\((?[^)]*)\)\{return\{\.\.\.(?\w+),account:(?\w+)\}\}", + RegexOptions.Singleline), + PatchFactory = BuildSetAccountReducerPatch } } },