feat(load-index): emit data.card_master_hash on mismatch (tier-1 freshness gate)
This commit is contained in:
@@ -48,11 +48,13 @@ public class LoadController : SVSimController
|
||||
private readonly IViewerMissionStateService _missionState;
|
||||
private readonly SVSimDbContext _db;
|
||||
private readonly IInventoryService _inv;
|
||||
private readonly ICardMasterPayloadProvider _cardMaster;
|
||||
|
||||
public LoadController(IViewerRepository viewerRepository, IGlobalsRepository globalsRepository,
|
||||
IGameConfigService config,
|
||||
IBattlePassService battlePass, IViewerMissionStateService missionState,
|
||||
SVSimDbContext db, IInventoryService inv)
|
||||
SVSimDbContext db, IInventoryService inv,
|
||||
ICardMasterPayloadProvider cardMaster)
|
||||
{
|
||||
_viewerRepository = viewerRepository;
|
||||
_globalsRepository = globalsRepository;
|
||||
@@ -61,6 +63,7 @@ public class LoadController : SVSimController
|
||||
_missionState = missionState;
|
||||
_db = db;
|
||||
_inv = inv;
|
||||
_cardMaster = cardMaster;
|
||||
}
|
||||
|
||||
[HttpPost("index")]
|
||||
@@ -163,7 +166,7 @@ public class LoadController : SVSimController
|
||||
var deviceHeader = Request.Headers["DEVICE"].FirstOrDefault();
|
||||
int deviceType = int.TryParse(deviceHeader, out int parsed) ? parsed : 0;
|
||||
|
||||
return new IndexResponse
|
||||
var response = new IndexResponse
|
||||
{
|
||||
UserTutorial = new UserTutorial { TutorialStep = viewer.MissionData.TutorialState },
|
||||
UserInfo = new UserInfo(deviceType, viewer),
|
||||
@@ -259,6 +262,21 @@ public class LoadController : SVSimController
|
||||
DeckFormat = Format.Rotation,
|
||||
CardSetIdForResourceDlView = rotation.CardSetIdForResourceDlView,
|
||||
};
|
||||
|
||||
// Emit card_master_hash only when the client's local copy differs from the configured
|
||||
// hash (presence-only client check — Wizard/CardMaster.cs:20). Emitting on every boot
|
||||
// would force a 1.27 MB redownload every boot. Empty request hash = fresh client = mismatch.
|
||||
if (_cardMaster.IsAvailable)
|
||||
{
|
||||
var cardMasterCfg = _config.Get<CardMasterConfig>();
|
||||
if (cardMasterCfg.EnableServing &&
|
||||
!string.Equals(request.CardMasterHash, cardMasterCfg.CurrentHash, StringComparison.Ordinal))
|
||||
{
|
||||
response.CardMasterHash = cardMasterCfg.CurrentHash;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -42,6 +42,23 @@ public class IndexResponse
|
||||
[Key("deck_format")]
|
||||
public Format DeckFormat { get; set; } = Format.Rotation;
|
||||
|
||||
/// <summary>
|
||||
/// Freshness trigger for the card-master refresh flow (Wizard/CardMaster.cs:18-30).
|
||||
/// Nullable + global <c>WhenWritingNull</c> means absence on the wire when the request
|
||||
/// already matches <c>CardMasterConfig.CurrentHash</c>. Presence (any value) tells the
|
||||
/// client to call <c>POST /immutable_data/card_master</c> with this echoed back; the
|
||||
/// client treats the string as opaque.
|
||||
/// <para>
|
||||
/// Lives on the inner <c>data</c> payload, NOT <c>data_headers</c> — verified by
|
||||
/// <c>LoadDetail.cs:414</c> constructing <c>new CardMaster.UpdateInfo(jsonData)</c>
|
||||
/// from the inner data, and the 2026-06-03 capture at
|
||||
/// <c>data_dumps/captures/traffic_prod_allstars_freepack.ndjson</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[JsonPropertyName("card_master_hash")]
|
||||
[Key("card_master_hash")]
|
||||
public string? CardMasterHash { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Basic User Data
|
||||
|
||||
Reference in New Issue
Block a user