feat(replay): stash battle context at /ai_*/start time
AiStartInternal now writes a BattleContext keyed by viewer id; the next commit consumes it in /finish to write a ViewerBattleHistory row. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,9 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using SVSim.BattleNode.Bridge;
|
||||
using SVSim.BattleNode.Sessions;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Services.Replay;
|
||||
using SVSim.EmulatedEntrypoint.Constants;
|
||||
using SVSim.EmulatedEntrypoint.Extensions;
|
||||
using SVSim.EmulatedEntrypoint.Matching;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.RankBattle;
|
||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
|
||||
@@ -27,6 +29,7 @@ public sealed class RankBattleController : ControllerBase
|
||||
private readonly IBattleSessionStore _sessionStore;
|
||||
private readonly IMatchContextBuilder _ctxBuilder;
|
||||
private readonly IBotRoster _botRoster;
|
||||
private readonly IBattleContextStore _battleContextStore;
|
||||
private readonly ILogger<RankBattleController> _log;
|
||||
|
||||
public RankBattleController(
|
||||
@@ -34,12 +37,14 @@ public sealed class RankBattleController : ControllerBase
|
||||
IBattleSessionStore sessionStore,
|
||||
IMatchContextBuilder ctxBuilder,
|
||||
IBotRoster botRoster,
|
||||
IBattleContextStore battleContextStore,
|
||||
ILogger<RankBattleController> log)
|
||||
{
|
||||
_resolver = resolver;
|
||||
_sessionStore = sessionStore;
|
||||
_ctxBuilder = ctxBuilder;
|
||||
_botRoster = botRoster;
|
||||
_battleContextStore = battleContextStore;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
@@ -165,6 +170,33 @@ public sealed class RankBattleController : ControllerBase
|
||||
var bot = await _botRoster.PickAsync(selfCtx, pending.BattleId, ct);
|
||||
var seed = Random.Shared.Next();
|
||||
|
||||
// Stash battle context for the upcoming /finish so the replay-history hook can
|
||||
// compose a ViewerBattleHistory row. See docs/superpowers/specs/2026-06-10-replay-info-design.md.
|
||||
if (long.TryParse(pending.BattleId, out var battleIdLong))
|
||||
{
|
||||
_battleContextStore.Set(vid, new BattleContext(
|
||||
BattleId: battleIdLong,
|
||||
// Wire battle_type: 2 = rank battle (per docs/api-spec/common/types.ts.md
|
||||
// #battle-types). AI variant shares the rank-battle wire id.
|
||||
BattleType: 2,
|
||||
DeckFormat: format.ToApi(), // wire-int via existing converter
|
||||
TwoPickType: 0,
|
||||
SelfClassId: (int)selfCtx.ClassId, // CardClass enum
|
||||
SelfSubClassId: 0,
|
||||
SelfCharaId: int.TryParse(selfCtx.CharaId, out var ch) ? ch : 0, // CharaId is string on MatchContext
|
||||
SelfRotationId: "0",
|
||||
OpponentViewerId: 0, // AI bot — not a real viewer
|
||||
OpponentName: bot.UserName,
|
||||
OpponentClassId: bot.ClassId, // int on AIBotProfile
|
||||
OpponentSubClassId: 0,
|
||||
OpponentCharaId: bot.CharaId, // int on AIBotProfile
|
||||
OpponentCountryCode: bot.CountryCode,
|
||||
OpponentEmblemId: bot.EmblemId, // int → long widen
|
||||
OpponentDegreeId: bot.DegreeId, // int → long widen
|
||||
OpponentRotationId: "0",
|
||||
BattleStartTime: DateTime.UtcNow));
|
||||
}
|
||||
|
||||
// Per spec, ai-start.md TODO: turnState semantics unclear. Default 0 (player first).
|
||||
return Ok(new AiBattleStartResponseDto
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user