feat(loader): Connection.ResourceUrl patch for GetResourceServerURL

Adds a Harmony prefix on CustomPreference.GetResourceServerURL mirroring
the existing GetApplicationServerURL pattern, gated on a new BepInEx
config key Connection.ResourceUrl. Default mirrors prod
(https://shadowverse.akamaized.net/) so the patch is functionally a
no-op until the user opts in.

To redirect at a local SVSim.ContentServer (typically on :5149 per the
project's launchSettings), set:
    [Connection]
    ResourceUrl = http://localhost:5149/

The stock client composes manifest / asset-bundle / sound / movie URLs
as GetResourceServerURL() + "dl/Manifest/{resVer}/{lang}/{plat}/..."
(and similar for Resource/, Sound/) — the trailing slash on the
returned host is load-bearing. Sidestepping the scheme accessor
(GetCDNScheme) is intentional: we supply the full URL with scheme
ourselves, the same shape the ApplicationUrl patch returns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-11 11:44:40 -04:00
parent 44c370d7a8
commit b544f0f9eb
3 changed files with 32 additions and 0 deletions

View File

@@ -30,4 +30,28 @@ public static class UrlPatches
__result = SvSimConfig.ApplicationUrl;
return false;
}
/// <summary>
/// Redirects the asset CDN ("resource server" — #3 of the 4-server topology, shadowverse.
/// akamaized.net in prod) to a configured URL, typically a local SVSim.ContentServer. The
/// stock client composes manifest / asset-bundle / sound / movie URLs as
/// <c>GetResourceServerURL() + "dl/Manifest/{resVer}/{lang}/{plat}/..."</c> (and similar
/// for Resource/, Sound/), so this prefix needs to return the bare host root WITH a
/// trailing slash. Stock <c>GetResourceServerURL</c> returns <c>GetCDNScheme() +
/// _resourceServerUrl</c> — we sidestep both the scheme accessor and the stored host by
/// supplying the full URL ourselves.
/// <para>
/// Default value mirrors prod so this patch is functionally a no-op until the user opts
/// in by changing the BepInEx config. To point at a local content server populated by
/// <c>data_dumps/scripts/content_cdn_mirror.py</c>, set
/// <c>Connection.ResourceUrl=http://localhost:5149/</c>.
/// </para>
/// </summary>
[HarmonyPatch(typeof(CustomPreference), nameof(CustomPreference.GetResourceServerURL))]
[HarmonyPrefix]
public static bool GetResourceServerURL(ref string __result)
{
__result = SvSimConfig.ResourceUrl;
return false;
}
}

View File

@@ -13,6 +13,7 @@ namespace SVSimLoader
internal static ManualLogSource Log;
public static Plugin Instance { get; private set; }
private ConfigEntry<string> _applicationUrl;
private ConfigEntry<string> _resourceUrl;
private ConfigEntry<bool> _disableEncryption;
private void Awake()
{
@@ -22,6 +23,10 @@ namespace SVSimLoader
Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!");
_applicationUrl = Config.Bind("Connection", "ApplicationUrl", "https://utoongaize.shadowverse.jp/shadowverse/",
"The URL to the application server.");
_resourceUrl = Config.Bind("Connection", "ResourceUrl", "https://shadowverse.akamaized.net/",
"The URL to the asset CDN (resource server #3). Must end with a trailing slash. " +
"Default points at the prod Akamai CDN — change to e.g. http://localhost:5149/ to redirect " +
"to a local SVSim.ContentServer populated by data_dumps/scripts/content_cdn_mirror.py.");
_disableEncryption = Config.Bind("Connection", "DisableEncryption", false,
"Whether to disable encrypting HTTP requests");
SvSimConfig.EnableTrafficCapture =
@@ -70,6 +75,7 @@ namespace SVSimLoader
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.ResourceUrl = _resourceUrl.Value;
SvSimConfig.DisableEncryption = _disableEncryption.Value;
if (SvSimConfig.NukeIdentityOnStartup)
{
@@ -79,6 +85,7 @@ namespace SVSimLoader
CaptureWriter.Initialize();
Logger.LogInfo($"Capture session directory: {CaptureWriter.SessionDirectory}");
Logger.LogInfo($"Connecting to application server at {_applicationUrl.Value}");
Logger.LogInfo($"Fetching assets from resource server at {_resourceUrl.Value}");
ExceptionLogging.Install();
var harmony = new Harmony(PluginInfo.PLUGIN_GUID);
harmony.PatchAll(Assembly.GetExecutingAssembly());

View File

@@ -3,6 +3,7 @@ namespace SVSimLoader;
public static class SvSimConfig
{
public static string ApplicationUrl { get; set; }
public static string ResourceUrl { get; set; }
public static bool DisableEncryption { get; set; }
public static bool DumpCardDB { get; set; }
public static bool EnableTrafficCapture { get; set; }