Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions README.md

Large diffs are not rendered by default.

20 changes: 16 additions & 4 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,21 @@
├── src/
│ ├── main.js
│ ├── adapter/
│ │ ├── captureReplay.js
│ │ ├── majsoulAdapter.js
│ │ └── messageParser.js
│ │ ├── messageParser.js
│ │ └── protobuf.js
│ ├── core/
│ │ ├── analyzer.js
│ │ ├── config.js
│ │ ├── eventDiagnostics.js
│ │ ├── events.js
│ │ ├── gameState.js
│ │ ├── realPageReadiness.js
│ │ ├── shanten.js
│ │ ├── tile.js
│ │ └── ukeire.js
│ │ ├── ukeire.js
│ │ └── version.js
│ └── ui/
│ ├── overlay.js
│ └── styles.js
Expand All @@ -51,12 +56,17 @@
| --- | --- |
| `src/main.js` | Compose adapter, state, analyzer, overlay, config, and global debug handle. |
| `src/adapter/majsoulAdapter.js` | Observe WebSocket/runtime traffic, buffer capture samples, invoke parser, export diagnostics. |
| `src/adapter/messageParser.js` | Convert raw/decoded Mahjong Soul messages into standardized helper events. |
| `src/adapter/captureReplay.js` | Replay exported samples, de-duplicate live/raw events, and summarize captures. |
| `src/adapter/messageParser.js` | Decode Liqi envelopes, validate/decrypt Unity WebGL action payloads, and emit standardized helper events. |
| `src/adapter/protobuf.js` | Bounds-checked protobuf wire parsing and field access helpers. |
| `src/core/config.js` | Version and normalize persistent capture settings. |
| `src/core/eventDiagnostics.js` | Shared live/offline event ordering, state diagnostics, and MVP acceptance gate. |
| `src/core/events.js` | Define allowed standardized event types. |
| `src/core/gameState.js` | Apply events, maintain normalized visible state, emit consistency warnings. |
| `src/core/tile.js` | Parse/normalize/display tiles and convert to/from 34-index representation. |
| `src/core/shanten.js` | Calculate standard, seven-pairs, and thirteen-orphans shanten. |
| `src/core/ukeire.js` | Calculate effective tile types and remaining counts. |
| `src/core/version.js` | Single runtime helper version used by source and build validation. |
| `src/core/analyzer.js` | Analyze current hand and discard candidates. |
| `src/core/realPageReadiness.js` | Define safety, preflight, and capture verification criteria. |
| `src/ui/overlay.js` | Render overlay controls, state, analysis, debug events, and capture export. |
Expand All @@ -76,7 +86,8 @@ flowchart TD
D --> H["WebSocket + Unity runtime observation"]
H --> I["Raw samples and diagnostics"]
H --> J["messageParser"]
J --> K["Standardized events"]
J --> O["Validated WebGL XOR + protobuf fields"]
O --> K["Standardized events"]
K --> E
E --> L["Visible state + warnings"]
L --> F
Expand Down Expand Up @@ -107,6 +118,7 @@ flowchart TD
```mermaid
flowchart LR
Parser["messageParser"] --> Events["events"]
Parser --> Protobuf["protobuf"]
Parser --> Tile["tile"]
Adapter["majsoulAdapter"] --> Parser
Main["main"] --> Adapter
Expand Down
39 changes: 29 additions & 10 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
# CHANGELOG.md

## v0.3.0

### Changed

- Live overlay and offline replay now share one event-diagnostics and MVP acceptance implementation, including inbound-traffic proof.
- Event ordering prefers monotonic `eventId`, preventing same-millisecond draw/discard actions from being evaluated backwards.
- `GameState` now tracks an explicit non-zero `selfSeat`; all-seat replay hands no longer silently default to seat 0, dealer hands can be inferred from 14 opening tiles, and private live draws can identify non-dealers.
- Hidden opponent draws now update public turn, wall, and dora state without inventing a private tile.
- Capture replay and protobuf wire parsing were split into focused modules; duplicated readiness logic was removed.
- Capture configuration now has a one-time migration version, so old undersized defaults are upgraded once while later user choices persist.
- Overlay teardown removes adapter listeners, DOM, styles, and object URLs during userscript upgrades.
- Unity `ActionPrototype.data` now uses one payload-length- and position-dependent XOR decoder instead of three fixed-offset short-payload guesses. Decoded candidates must consume as valid protobuf and pass named-action field/value checks.
- A captured 232-byte `ActionNewRound` fixture now verifies 13 hand tiles, four scores, dora, round metadata, riichi sticks, and wall count; the observed draw/discard fixtures now use their correctly decoded seats and wall counts.
- Build tooling now declares esbuild directly, uses Vitest 4, supports Node 25 Web Storage behavior, reports zero npm audit vulnerabilities, and runs 317 automated tests.

### Fixed

- Seat-wind calculation now uses both self seat and dealer seat.
- Red-five discard/meld diagnostics compare normalized tiles.
- Replay and live gates no longer drift on optional calls, kans, riichi, round ends, or current turn.
- `Download capture` now uses an explicit programmatic download and delays object-URL cleanup, so a page capture-phase click listener or premature revocation cannot silently cancel the JSON download.
- A regressed action step without a decoded round start now invalidates stale round state instead of merging two rounds into an impossible hand.
- Capture import no longer mistakes bounded envelope/action diagnostic previews for truncation of the retained raw WebSocket sample.

### Known Issues

- A manual Unity session reached the live `20/20` MVP gate and `15/15` preflight and exported successfully, but the saved sample crossed a paused round boundary and is intentionally rejected by strict replay acceptance. A clean uninterrupted export is still required.
- Action schemas not yet represented by observed regression samples may still appear under `unmappedUnityPayloads` and must not be guessed.

## v0.2.13

### Changed
Expand Down Expand Up @@ -47,13 +76,3 @@
- Raised default capture limits to support larger live samples.
- Realtime discard-candidate advice is explicitly opt-in and disabled by default.
- Debug exports now include live state, runtime diagnostics, safety settings, and readiness information.

### Known Issues

- Full real-page game-state restoration is not complete.
- Unity `ActionNewRound` payloads are not yet decoded enough to recover initial hand, dora, scores, seat, and round metadata reliably.
- Several longer Unity action payloads remain encoded or unmapped.
- Current local ignored capture evidence is diagnostic-only and not ready for real-page MVP acceptance.
- Batch capture validation and direct replay disagree on the stale local capture's readiness.
- Large parser/adapter/overlay modules should be split once decoder behavior stabilizes.
- Some user-facing text contains mojibake around punctuation examples.
175 changes: 65 additions & 110 deletions docs/PROJECT_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,119 +2,74 @@

## Current Version

- Project version: `0.2.13`.
- Project version: `0.3.0`.
- Generated userscript: `majsoul-helper.user.js`.
- Main source entry: `src/main.js`.
- Target site: `https://game.maj-soul.com/1/`.

## One-Line State

The project has a working modular helper shell, analysis engine, debug overlay, WebSocket/Unity capture layer, and partial Unity action decoding, but full real-page game-state restoration is not complete because several Unity WebGL payloads remain encoded or unmapped.

## Implemented Features

- Tampermonkey userscript generation and install metadata.
- Draggable, collapsible overlay with training/review warning.
- Manual hand input for shanten, ukeire, and discard-candidate analysis.
- Realtime discard advice toggle is present and off by default.
- Normalized tile utilities with red-five support.
- Shanten calculation:
- standard 4 melds + pair
- seven pairs
- thirteen orphans
- open-meld standard handling
- Ukeire calculation over 34 tile types with visible-tile exclusion.
- Candidate discard analysis sorted by after-discard shanten and ukeire.
- Standardized `GameState` model and event application.
- WebSocket observation without outbound message mutation.
- Raw text/binary/blob capture with bounded sample bytes and event limits.
- Unity WebGL runtime diagnostics:
- loader observation
- `createUnityInstance` shape checks
- Unity/applicability markers
- Message parser for readable JSON, decoded objects, Liqi-style binary envelopes, and selected Unity encoded action payloads.
- Replay, capture doctor, capture validation, audit, smoke, and full verify scripts.
- Safety tests that reject autoplay/click/anti-cheat patterns.

## Current Development Status

- Local goal audit is not fully complete.
- `npm run audit` proves most static/MVP requirements but still reports real-page validation as needing capture evidence.
- `captures/capture-real.json` is an ignored local sample from helper `0.2.6`, not current `0.2.13`.
- The latest ignored live capture from helper `0.2.11` preserved 1227 events with no buffer drop and parsed draw/discard traffic, but still lacked decoded `ActionNewRound` hand, dora, score, and round metadata.
- Replay now reports unmapped Unity payload families explicitly, long encrypted `ActionDealTile` payloads no longer create false riichi state, stale live parsed events from replayable raw samples are skipped, and decoded Record-style NewRound objects understand the mjai-reviewer `tiles0..tiles3` field contract.

## Architecture

- `src/main.js`
- Initializes adapter, state, analyzer, and overlay.
- Persists safe UI/capture config in `localStorage`.
- Exposes `window.__majsoulHelper` for debug.
- `src/adapter/majsoulAdapter.js`
- Installs WebSocket and runtime hooks.
- Records raw samples and parsed events.
- Exports capture diagnostics.
- `src/adapter/messageParser.js`
- Converts raw/decoded traffic into standardized events.
- Contains current Unity payload decoder knowledge.
- `src/core/gameState.js`
- Applies standardized events.
- Produces visible state and consistency warnings.
- `src/core/analyzer.js`
- Runs shanten, ukeire, and discard simulation.
- `src/core/tile.js`
- Tile parsing, normalization, indices, dora mapping.
- `src/core/shanten.js`
- Shanten algorithms.
- `src/core/ukeire.js`
- Ukeire enumeration and remaining-count logic.
- `src/core/realPageReadiness.js`
- Real-page gate and safety/readiness checks.
- `src/ui/overlay.js`
- DOM rendering, controls, copy/download, debug panels.
- `src/ui/styles.js`
- Overlay CSS.
- `scripts/`
- Build, replay, import, validation, audit, smoke, and verification utilities.
- `tests/`
- Unit, integration, parser, UI, safety, build, docs, replay, and runtime tests.

## Data Flow

1. Tampermonkey injects the generated userscript at document start.
2. `src/main.js` initializes `MajsoulAdapter`, `GameState`, `Analyzer`, and `Overlay`.
3. `MajsoulAdapter` observes WebSocket/runtime traffic and stores bounded raw samples.
4. `messageParser` parses supported messages into normalized events.
5. `GameState.applyEvent()` updates hand/table state.
6. `Overlay` reads visible state and optional analyzer output.
7. Debug export includes raw capture, parsed events, live state, runtime diagnostics, safety settings, and readiness checks.

## Known Issues

- Full Unity WebGL state restoration is incomplete.
- `ActionNewRound` initial hand, dora, scores, and round metadata are not reliably decoded from current Unity payloads.
- Some longer `ActionDiscardTile`, `ActionDealTile`, `ActionChiPengGang`, `ActionAnGangAddGang`, `ActionHule`, and restore/sync payloads remain unmapped.
- The current blocker is mostly interpretation, not raw capture. WebSocket traffic and action names are visible; important payload fields are still encoded or unknown.
- `GameState.hand` is now treated as a decoded base hand; own draw/discard traffic without a decoded initial hand no longer invents a partial hand. Unity captures still need `ActionNewRound` decoding before full hand analysis can be trusted.
- Local ignored capture data is diagnostic-only. `captures/capture-real.json` is helper `0.2.6`; the latest user-supplied live capture was helper `0.2.11` and remains not ready.
- `npm run validate-captures -- --summary` reports the local capture as failed/not ready, while direct replay can produce diagnostics. This validation path needs investigation before being used as a release gate.
- `messageParser.js`, `majsoulAdapter.js`, and `overlay.js` are large modules with multiple responsibilities. Future decoder work risks regressions unless tested narrowly.
- Acceptance/readiness logic exists in both UI/runtime exports and replay/audit scripts, creating drift risk.
- Overlay render is mostly full re-render on updates. Event buffers are capped, but high-frequency live sessions could still expose UI performance issues.
- Some text/regex examples show mojibake around punctuation cleanup in hand parsing examples. Clean before user-facing release.
- Safety boundary tests are regex-based and may need tuning if legitimate code triggers false positives.

## Current Priorities

1. Capture a fresh `0.2.13` real-page session from round start with safe settings and large binary samples, then import/replay it.
2. Decode Unity payload fields for `ActionNewRound` first, especially initial hand, dora indicators, scores, seat, round, honba, riichi sticks, and wall count.
3. Use `diagnostics.unmappedUnityPayloads` from replay/doctor to choose the next action family instead of trusting accidental protobuf-looking bytes.
4. Make capture validation and replay agree on readiness/failure reasons.
5. Add regression tests for every newly decoded Unity action shape.
6. Keep no-automation safety boundaries intact while expanding live-state parsing.

## Risk Assessment

- Biggest product risk: without `ActionNewRound` and restore/sync decoding, the overlay cannot reliably know the player's full current hand after page refresh, reconnect, or mid-round start.
- Biggest engineering risk: overfitting decoders to one capture shape and silently producing plausible but wrong game state.
- Biggest compliance/safety risk: realtime advice could be misused. It is currently opt-in; keep it disabled by default and clearly labeled.
The local helper, analysis engine, capture/replay pipeline, shared acceptance gate, and safety checks are complete and verified; final real-page acceptance still needs a fresh `0.3.0` Unity WebGL capture because the repository intentionally contains no private live capture.

## Implemented

- Tampermonkey userscript generation and early page-context injection.
- Draggable/collapsible overlay with manual hand analysis and debug export.
- Standard, seven-pairs, and thirteen-orphans shanten.
- Ukeire and discard-candidate ranking with visible-tile exclusion.
- Normalized state for hand, self seat, draw, rivers, melds, dora, scores, round metadata, turn, riichi, and warnings.
- Explicit non-zero `selfSeat` handling:
- decoded self seat is preserved;
- all-seat replay hands never default to seat 0;
- a 14-tile opening hand identifies the dealer;
- a private live draw can identify a non-dealer.
- Hidden opponent draws update public turn, wall, and dora state without inventing a tile.
- Passive WebSocket, decoded JS/Laya, and Unity boot/runtime observation.
- Readable JSON, Liqi binary envelope, selected Action/Record protobuf, and the current length/position-dependent Unity WebGL action XOR transform.
- Strict decoded-candidate validation: the full payload must be valid protobuf and contain plausible schema fields for the named action before decoded values are trusted.
- A captured 232-byte `ActionNewRound` regression sample restores the 13-tile hand, four scores, dora, round metadata, riichi sticks, and wall count.
- Capture export, replay, de-duplication, doctor, validation, audit, smoke, and real-page gates.
- Browser-resilient capture downloads use an explicit download action and delayed object-URL cleanup.
- One shared live/offline MVP gate and one shared real-page readiness implementation.
- Versioned capture configuration migration that preserves later user choices.
- Clean overlay teardown during userscript upgrades.
- Node 25-compatible test launcher, declared esbuild dependency, current Vitest, 317 passing automated tests, and zero npm audit vulnerabilities.

## Architecture State

- `src/adapter/protobuf.js` owns protobuf wire parsing.
- `src/adapter/messageParser.js` owns Mahjong Soul event decoding.
- `src/adapter/majsoulAdapter.js` owns page observation and capture buffering.
- `src/adapter/captureReplay.js` owns offline replay and capture summaries.
- `src/core/gameState.js` owns normalized state transitions.
- `src/core/eventDiagnostics.js` owns event ordering, state diagnostics, and MVP checks.
- `src/core/realPageReadiness.js` owns export and real-page readiness rules.
- `src/core/config.js` owns persistent capture settings and migration.
- `src/ui/overlay.js` renders controls and delegates core decisions to shared modules.

## Remaining External Validation

The repository has no real capture under `captures/` that passes strict acceptance, so `npm run audit` correctly reports the real-page evidence requirement as incomplete. A manual browser session confirmed installation, Unity detection, live WebSocket traffic, real `ActionNewRound` decoding, a `20/20` live MVP gate, a `15/15` preflight, and successful JSON download. The exported sample later crossed a paused round boundary (action step `53` to `1` without a captured `ActionNewRound`), so replay now clears stale state and correctly rejects the sample. It is useful regression evidence, but not final acceptance evidence.

A fresh real-page sample must prove:

1. the `0.3.0` helper is installed in page context;
2. inbound live traffic is retained without truncation or buffer loss;
3. `ActionNewRound`, draw, discard, and any observed optional actions decode into correct standard events;
4. overlay state matches the visible table;
5. offline replay matches the exported live snapshot;
6. realtime advice is off and all no-automation/no-mutation safety flags are clean.

## Known Protocol Risk

The current XOR transform is decoded generically, but Unity action schemas may change and not every action family has an observed regression sample. Payloads that fail complete protobuf or named-action plausibility checks remain unmapped. Any new field mapping must be based on a sanitized real sample and receive a focused regression fixture before it can be trusted.

## Next Action

Install the generated `0.3.0` userscript, collect one safe session from round start, then run:

```bash
npm run import-capture -- path/to/majsoul-helper-capture.json
npm run capture-doctor -- captures/capture-real.json
npm run real-page-gate
```
Loading