Additional functionality

This commit is contained in:
gamer147
2026-05-28 11:21:37 -04:00
parent b1206c874d
commit 8f2ddeab96
5 changed files with 53 additions and 19 deletions

View File

@@ -19,7 +19,8 @@ Built against the official Cygames build (decompiled source in `Shadowverse_Code
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/`
- `[Connection] DisableEncryption = true`
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/`.
@@ -37,7 +38,7 @@ Settings live in `BepInEx/config/SVSimLoader.cfg`, generated on first launch. Th
| 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. Local server understands plaintext; prod does not. |
| `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)
@@ -76,6 +77,8 @@ BepInEx/svsim-captures/<yyyy-MM-dd_HH-mm-ss>_<host>/
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/` is a curated paste of one such session, used as the seed source for `SVSim.Bootstrap/Data/prod-captures/`.
## Code layout

View File

@@ -85,25 +85,25 @@ internal static class CaptureWriter
public static void AppendTraffic(string direction, string url, bool encrypted, string body)
{
AppendNdjson(_trafficPath, new Dictionary<string, object>
string envelope = JsonMapper.ToJson(new Dictionary<string, object>
{
{ "ts", DateTime.UtcNow.ToString("o") },
{ "direction", direction },
{ "url", url },
{ "encrypted", encrypted },
{ "body", ParseBodyOrKeep(body) },
});
AppendLineWithBody(_trafficPath, envelope, body);
}
public static void AppendBattleTraffic(string direction, string uri, string body)
{
AppendNdjson(_battleTrafficPath, new Dictionary<string, object>
string envelope = JsonMapper.ToJson(new Dictionary<string, object>
{
{ "ts", DateTime.UtcNow.ToString("o") },
{ "direction", direction },
{ "uri", uri },
{ "body", ParseBodyOrKeep(body) },
});
AppendLineWithBody(_battleTrafficPath, envelope, body);
}
/// <summary>
@@ -278,20 +278,28 @@ internal static class CaptureWriter
if (classes.Count > 0) dump["classes"] = classes;
}
// Parse body JSON so it serializes nested in the NDJSON line rather than as an
// escaped string. Falls back to the original string on parse failure — no current
// caller produces non-JSON, but a future caller passing raw text shouldn't crash
// the line write.
private static object ParseBodyOrKeep(string body)
// Splice the body into the envelope as nested JSON (parseable) or escaped string
// (fallback). Cannot route this through Dictionary<string,object> → JsonMapper.ToJson:
// a LitJson.JsonData value inside such a dict makes the reflection serializer
// mis-bracket and throw "Can't close an object here".
private static void AppendLineWithBody(string path, string envelopeJson, string body)
{
if (string.IsNullOrEmpty(body)) return body;
try { return JsonMapper.ToObject(body); }
catch { return body; }
}
private static void AppendNdjson(string path, Dictionary<string, object> entry)
{
string line = JsonMapper.ToJson(entry);
string bodyJson;
if (body == null)
{
bodyJson = "null";
}
else if (body.Length == 0)
{
bodyJson = "\"\"";
}
else
{
try { bodyJson = JsonMapper.ToObject(body).ToJson(); }
catch { bodyJson = JsonMapper.ToJson(body); }
}
string trimmed = envelopeJson.Substring(0, envelopeJson.Length - 1);
string line = trimmed + ",\"body\":" + bodyJson + "}";
lock (_lock)
{
File.AppendAllText(path, line + "\n");

View File

@@ -0,0 +1,14 @@
extern alias game;
using PlayerPrefs = game::UnityEngine.PlayerPrefs;
namespace SVSimLoader;
public static class IdentityWipe
{
public static void Execute()
{
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
}
}

View File

@@ -60,8 +60,16 @@ namespace SVSimLoader
SvSimConfig.StorySectionIdFilter =
Config.Bind("Sweeps", "StorySectionIdFilter", "",
"Optional comma-separated list of section IDs to restrict the story sweep to (e.g. '14,19,20'). Empty = sweep all sections. Useful for resuming a previous run that hit MAX_PASSES_PER_PAIR on specific sections without re-sweeping everything.").Value;
SvSimConfig.NukeIdentityOnStartup =
Config.Bind("Identity", "NukeIdentityOnStartup", false,
"On plugin Awake (before the game reads PlayerPrefs), wipe all PlayerPrefs via PlayerPrefs.DeleteAll(). Clears the obscured UDID/VIEWER_ID/SHORT_UDID keys that Cute.Certification reads on login, so the next launch behaves like a brand-new install and re-runs SignUpTask. Use this when switching Steam accounts gives a linking error. SIDE EFFECT: also resets language/sound/RES_VER prefs — they're rebuilt from defaults next boot. Recovery files and capture sessions are NOT touched.").Value;
SvSimConfig.ApplicationUrl = _applicationUrl.Value;
SvSimConfig.DisableEncryption = _disableEncryption.Value;
if (SvSimConfig.NukeIdentityOnStartup)
{
Logger.LogWarning("NukeIdentityOnStartup is enabled — wiping all PlayerPrefs (identity + settings).");
IdentityWipe.Execute();
}
CaptureWriter.Initialize();
Logger.LogInfo($"Capture session directory: {CaptureWriter.SessionDirectory}");
Logger.LogInfo($"Connecting to application server at {_applicationUrl.Value}");

View File

@@ -16,4 +16,5 @@ public static class SvSimConfig
public static bool ProbeLimitedSection { get; set; }
public static bool ProbeEventSection { get; set; }
public static string StorySectionIdFilter { get; set; }
public static bool NukeIdentityOnStartup { get; set; }
}