Mirror of the outer-repo data_dumps/ reorganization: README now points at data_dumps/captures/ for traffic_prod.ndjson; the GachaExchangeSweep comment naming the tradeables capture follows the same path rewrite. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
107 lines
7.0 KiB
Markdown
107 lines
7.0 KiB
Markdown
# SVSimLoader
|
|
|
|
BepInEx 5 / HarmonyX plugin injected into the Steam Shadowverse client. Two jobs:
|
|
|
|
1. **Redirect** the client to a local emulated server (or a capture proxy).
|
|
2. **Observe** the live client/server conversation and dump structured artifacts that feed `SVSim.Bootstrap` and the api-spec docs.
|
|
|
|
Built against the official Cygames build (decompiled source in `Shadowverse_Code_2026-05-23/`). Lives upstream of the rest of the SVSim project — everything else in the repo consumes what this plugin captures.
|
|
|
|
## Quick start
|
|
|
|
1. Install **BepInEx 5** (Mono build, matching the game's bitness) into the Shadowverse game directory. Launch the game once so BepInEx generates its folder tree, then close.
|
|
2. From this repo, build the plugin:
|
|
```sh
|
|
cd ClientLoader/SVSimLoader
|
|
dotnet build
|
|
```
|
|
3. Copy `SVSimLoader/bin/Debug/net46/SVSimLoader.dll` → `<game-dir>/BepInEx/plugins/`.
|
|
4. Launch the game once to generate `<game-dir>/BepInEx/config/SVSimLoader.cfg`, then close.
|
|
5. Edit the config. For local server work:
|
|
- `[Connection] ApplicationUrl = http://localhost:5148/`
|
|
|
|
That's it — DCGEngine speaks the same AES-encrypted wire format as the prod server, so the default `DisableEncryption = false` is correct against the local server and against any other compliant emulator. Leave `DisableEncryption` alone unless you're debugging the wire layer itself (see below).
|
|
|
|
For prod capture, leave `[Connection]` at defaults and toggle whichever `[Capture]` / `[Sweeps]` flags you need.
|
|
6. Launch the game. A capture session directory appears under `<game-dir>/BepInEx/svsim-captures/`.
|
|
|
|
## Build notes
|
|
|
|
Targets `net46` (Unity 5.6 / Mono). The csproj references `Assembly-CSharp.dll` and `UnityEngine.CoreModule.dll` out of `SVSimLoader/lib/` — populate from a working game install if those aren't already present. No `dotnet restore` quirks; a plain `dotnet build` is enough.
|
|
|
|
## Configuration
|
|
|
|
Settings live in `BepInEx/config/SVSimLoader.cfg`, generated on first launch. Three sections:
|
|
|
|
### `[Connection]` — where and how to talk to the server
|
|
|
|
| Key | Default | Purpose |
|
|
|---|---|---|
|
|
| `ApplicationUrl` | prod URL | Overrides `CustomPreference.GetApplicationServerURL`. Point at `http://localhost:5148/` for the local DCGEngine. |
|
|
| `DisableEncryption` | `false` | Forces the `encrypt` arg on `NetworkManager.Connect` to false, so request/response bodies go over the wire as plain base64(msgpack(...)) instead of base64(AES(msgpack(...))). **You do NOT need to enable this for any server — DCGEngine and any other compliant emulator handle the standard AES path the same way prod does.** It exists for wire-format debugging: makes `traffic.ndjson` and proxy-side inspection readable without round-tripping through `CryptAES`. Leave it at `false` for normal use. |
|
|
|
|
### `[Capture]` — passive observe-and-record (safe to leave on)
|
|
|
|
| Key | Default | Output file |
|
|
|---|---|---|
|
|
| `EnableTrafficCapture` | `true` | `traffic.ndjson` — every HTTP request + response body |
|
|
| `EnableBattleCapture` | `true` | `battle-traffic.ndjson` — every Socket.IO battle frame |
|
|
| `DumpCardDB` | `false` | `cards.json` — full card master, one-shot on load |
|
|
| `DumpUserData` | `false` | `user-data.json` — viewer fields from `/load/index`, shaped for `/admin/import_viewer` |
|
|
|
|
### `[Sweeps]` — active prod-API traffic (deliberate opt-in, hits prod)
|
|
|
|
Probes (one extra request) and sweeps (many) fire automatically once the gating screen loads. All responses land in `traffic.ndjson` via the normal capture hook.
|
|
|
|
| Key | Default | Effect |
|
|
|---|---|---|
|
|
| `ProbeLimitedSection` | `false` | Fires `/limited_story/section` once on first `/mypage/refresh` |
|
|
| `ProbeEventSection` | `false` | Same for `/event_story/section`, 1s after the limited probe |
|
|
| `SweepLeaderSkinPools` | `false` | Walks every parent gacha id from `/pack/info`; ~18s for 35 packs |
|
|
| `SweepMainStory` | `false` | Walks every (section, chara, chapter); captures master `special_battle_setting` payloads. **Side effect**: unfinished chapters become `is_skipped=true` (blue "Cleared", no rewards). Use a throwaway account. |
|
|
| `SweepLimitedStory` | `false` | Same shape for limited-story sections |
|
|
| `SweepEventStory` | `false` | Same shape for event-story sections |
|
|
| `StorySweepPacingSeconds` | `5.0` | Seconds between requests (min 1s). 5s = ~6h for full main-story tree. |
|
|
| `StorySectionIdFilter` | `""` | Comma-separated section IDs to scope down. Empty = sweep all. Used to resume runs that hit `MAX_PASSES_PER_PAIR`. |
|
|
|
|
## Capture output
|
|
|
|
Each game launch creates a fresh session directory:
|
|
|
|
```
|
|
BepInEx/svsim-captures/<yyyy-MM-dd_HH-mm-ss>_<host>/
|
|
traffic.ndjson # HTTP req/resp (always, when EnableTrafficCapture)
|
|
battle-traffic.ndjson # Socket.IO frames
|
|
cards.json # one-shot card master dump
|
|
user-data.json # /admin/import_viewer-shaped viewer extract
|
|
special-battle-settings.ndjson # deduped sbs payloads from story sweeps
|
|
```
|
|
|
|
The capture hook decrypts each response before writing, so `traffic.ndjson` is always readable JSON regardless of whether the underlying connection used AES. `DisableEncryption` is therefore not required to make captures inspectable; it only affects what flows over the wire itself.
|
|
|
|
The `traffic_prod.ndjson` checked into `data_dumps/captures/` is a curated paste of one such session, used as the seed source for `SVSim.Bootstrap/Data/prod-captures/`.
|
|
|
|
## Code layout
|
|
|
|
```
|
|
SVSimLoader/
|
|
Plugin.cs # BepInEx entry point, Config.Bind, Harmony.PatchAll
|
|
SvSimConfig.cs # plain static fields populated from Config.Bind
|
|
CaptureWriter.cs # session dir, ndjson writers, user-data extraction
|
|
Patches/
|
|
UrlPatches.cs # GetApplicationServerURL -> SvSimConfig.ApplicationUrl
|
|
DecryptPatch.cs # NetworkManager.Connect: optionally clear `encrypt` flag
|
|
ExaminationPatches.cs# NetworkTask.SetResponseData: traffic-capture fan-out hub
|
|
LeaderSkinPoolSweep.cs # /pack/info -> /pack/get_gacha_point_rewards sweep
|
|
StorySectionProbe.cs # /mypage/refresh -> /{limited,event}_story/section probes
|
|
StorySweep.cs # /*/section -> /{main,limited,event}_story/{start,finish} sweep
|
|
DummyLogging.cs # short-circuits LocalLog.MakeTreceLogToSend (no telemetry)
|
|
ExceptionLogging.cs # Unity exception/error -> BepInEx log, with last-response JSON
|
|
```
|
|
|
|
`ExaminationPatches.SetResponseData` is the central hub: every response goes through it, and it dispatches into the dump / probe / sweep modules based on URL + config. Adding a new passive extractor usually means adding one `if (...EndsWith("/some/url")) { ... }` block there plus a writer in `CaptureWriter`.
|
|
|
|
## Background
|
|
|
|
Live Cygames servers shut down end of June 2026 — this plugin's main reason for existing is to capture as much server-only data (special battle settings, gacha pool composition, reward tables) as possible before then. Spec fidelity downstream depends on the artifacts it dumps. See `docs/audits/` for the per-endpoint audit reports that drive what gets captured next.
|