feat(envelope): push required_res_ver from ResourceConfig on game_start
A wiped/fresh client (NukeIdentityOnStartup, new install, or any path that clears PlayerPrefs) defaults its stored RES_VER to "00000000" per Cute/SavedataManager.GetResourceVersion. The client builds the Akamai manifest URL as dl/Manifest/<RES_VER>/<lang>/<Platform>/, and Akamai 404s the "00000000" path -> Toolbox.AssetManager.InitializeManifest fails -> the title screen shows "Connection Error / Reconnect" before any tutorial UI loads. Fix: - New ResourceConfig [ConfigSection] in SVSim.Database — single field RequiredResVer defaulting to "4670rPsPMVlRTd2" (the value prod returned in data_dumps/traffic_prod_tutorial.ndjson and was still returning at 2026-05-28 21:00 UTC). Lives in GameConfigs so it can be tuned via DB / appsettings without code edits. - ShadowverseTranslationMiddleware injects IGameConfigService and emits required_res_ver in data_headers ONLY on /check/game_start responses. NetworkTask.Parse opens a "new data is available" popup whenever required_res_ver is present and the URL is anything other than GameStartCheck (NetworkTask.cs:128-138); the suppression on game_start is what lets us silently bump PlayerPrefs["RES_VER"] before ResourceDownloader runs. - DataHeaders gains a nullable RequiredResVer field. DataWrapper.DataHeaders is now Dictionary<string, object?> instead of the typed DataHeaders POCO directly — the construction site stays type-safe (the middleware builds the typed POCO, then projects through the same STJ + ConvertJsonTreeToPlainObject pipeline that DataWrapper.Data uses) so null-valued optional fields are absent from the wire instead of being written as "key":null. Without this, MessagePack's ContractlessStandardResolver walked the typed properties and wrote required_res_ver=null on every non-game_start response, tripping the popup on every boot. - GameConfigurationJsonbTests updated to expect the 9th config section. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -25,14 +25,19 @@ public class GameConfigurationJsonbTests
|
||||
var rows = await db.GameConfigs.AsNoTracking().ToListAsync();
|
||||
var byName = rows.ToDictionary(r => r.SectionName);
|
||||
|
||||
// One row per [ConfigSection]-marked POCO (8 sections today: Player, DefaultGrants,
|
||||
// DefaultLoadout, Challenge, Rotation, PackRates, MyRotationSchedule, Story).
|
||||
// One row per [ConfigSection]-marked POCO (9 sections today: Player, DefaultGrants,
|
||||
// DefaultLoadout, Challenge, Rotation, PackRates, MyRotationSchedule, Story, ResourceConfig).
|
||||
Assert.That(byName.Keys, Is.EquivalentTo(new[]
|
||||
{
|
||||
"Player", "DefaultGrants", "DefaultLoadout", "Challenge", "Rotation", "PackRates",
|
||||
"MyRotationSchedule", "Story",
|
||||
"MyRotationSchedule", "Story", "ResourceConfig",
|
||||
}));
|
||||
|
||||
var resources = JsonSerializer.Deserialize<ResourceConfig>(byName["ResourceConfig"].ValueJson)!;
|
||||
Assert.That(resources.RequiredResVer, Is.EqualTo("4670rPsPMVlRTd2"),
|
||||
"ShippedDefaults RES_VER is the prod-captured (2026-05-28) Akamai manifest path " +
|
||||
"— required by the client to load the asset manifest after a wiped/fresh install.");
|
||||
|
||||
var mrSchedule = JsonSerializer.Deserialize<MyRotationScheduleConfig>(byName["MyRotationSchedule"].ValueJson)!;
|
||||
Assert.That(mrSchedule.FreeBattle.Begin, Is.EqualTo(new DateTime(2024, 5, 1, 20, 0, 0, DateTimeKind.Utc)),
|
||||
"ShippedDefaults reproduces the 2026-05-23 prod capture so a fresh install ships with Custom Rotation enabled");
|
||||
|
||||
Reference in New Issue
Block a user