feat(mission): wire Tier 1 emits — rank/free/challenge match events
Adds IMissionProgressService to RankBattle, FreeBattle, ArenaTwoPickBattle, and ArenaTwoPick controllers. Each finish handler emits the appropriate MissionEventKeys builder result: - RankBattle.Finish (win) → Ranked.WinAll(classId) Includes AI-rank routes; on a private server AI wins count the same. - FreeBattle.Finish (win) → Free.WinAll() (aggregates only) - ArenaTwoPickBattle.Finish → Challenge.MatchPlayAll or MatchWinAll Always fires challenge_play; wins additionally fire challenge_win and the ranked/arena/daily aggregates. - ArenaTwoPick.Finish (5-win full clear) → Challenge.FullClearAll Gated on RunFinishOutcome.WasFullClear from the previous commit. This closes six of the eight remaining catalog wire-up gaps; class_level_up and rank_achieved follow in Tier 2 (need pre-state detection). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using SVSim.BattleNode.Bridge;
|
using SVSim.BattleNode.Bridge;
|
||||||
using SVSim.Database.Enums;
|
using SVSim.Database.Enums;
|
||||||
|
using SVSim.Database.Services;
|
||||||
using SVSim.Database.Services.Friend;
|
using SVSim.Database.Services.Friend;
|
||||||
using SVSim.Database.Services.Replay;
|
using SVSim.Database.Services.Replay;
|
||||||
using SVSim.EmulatedEntrypoint.Extensions;
|
using SVSim.EmulatedEntrypoint.Extensions;
|
||||||
@@ -20,6 +21,7 @@ public class ArenaTwoPickBattleController : SVSimController
|
|||||||
private readonly IBattleContextStore _battleContextStore;
|
private readonly IBattleContextStore _battleContextStore;
|
||||||
private readonly IBattleHistoryWriter _historyWriter;
|
private readonly IBattleHistoryWriter _historyWriter;
|
||||||
private readonly IPlayedTogetherWriter _playedTogetherWriter;
|
private readonly IPlayedTogetherWriter _playedTogetherWriter;
|
||||||
|
private readonly IMissionProgressService _missionProgress;
|
||||||
|
|
||||||
public ArenaTwoPickBattleController(
|
public ArenaTwoPickBattleController(
|
||||||
IArenaTwoPickService svc,
|
IArenaTwoPickService svc,
|
||||||
@@ -27,7 +29,8 @@ public class ArenaTwoPickBattleController : SVSimController
|
|||||||
IMatchingResolver resolver,
|
IMatchingResolver resolver,
|
||||||
IBattleContextStore battleContextStore,
|
IBattleContextStore battleContextStore,
|
||||||
IBattleHistoryWriter historyWriter,
|
IBattleHistoryWriter historyWriter,
|
||||||
IPlayedTogetherWriter playedTogetherWriter)
|
IPlayedTogetherWriter playedTogetherWriter,
|
||||||
|
IMissionProgressService missionProgress)
|
||||||
{
|
{
|
||||||
_svc = svc;
|
_svc = svc;
|
||||||
_matchContextBuilder = matchContextBuilder;
|
_matchContextBuilder = matchContextBuilder;
|
||||||
@@ -35,6 +38,7 @@ public class ArenaTwoPickBattleController : SVSimController
|
|||||||
_battleContextStore = battleContextStore;
|
_battleContextStore = battleContextStore;
|
||||||
_historyWriter = historyWriter;
|
_historyWriter = historyWriter;
|
||||||
_playedTogetherWriter = playedTogetherWriter;
|
_playedTogetherWriter = playedTogetherWriter;
|
||||||
|
_missionProgress = missionProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("do_matching")]
|
[HttpPost("do_matching")]
|
||||||
@@ -117,6 +121,14 @@ public class ArenaTwoPickBattleController : SVSimController
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = await _svc.RecordBattleResultAsync(vid, isWin);
|
var result = await _svc.RecordBattleResultAsync(vid, isWin);
|
||||||
|
|
||||||
|
// Mission counters — TK2 matches always advance challenge_play, wins additionally
|
||||||
|
// advance challenge_win + the ranked/arena/daily aggregates.
|
||||||
|
await _missionProgress.RecordEventAsync(
|
||||||
|
vid,
|
||||||
|
isWin ? MissionEventKeys.Challenge.MatchWinAll() : MissionEventKeys.Challenge.MatchPlayAll(),
|
||||||
|
ct: ct);
|
||||||
|
|
||||||
return Ok(new BattleFinishResponseDto
|
return Ok(new BattleFinishResponseDto
|
||||||
{
|
{
|
||||||
BattleResult = result.BattleResult,
|
BattleResult = result.BattleResult,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SVSim.Database.Services;
|
||||||
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.ArenaTwoPick;
|
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.ArenaTwoPick;
|
||||||
using SVSim.EmulatedEntrypoint.Services;
|
using SVSim.EmulatedEntrypoint.Services;
|
||||||
|
|
||||||
@@ -8,7 +9,13 @@ namespace SVSim.EmulatedEntrypoint.Controllers;
|
|||||||
public class ArenaTwoPickController : SVSimController
|
public class ArenaTwoPickController : SVSimController
|
||||||
{
|
{
|
||||||
private readonly IArenaTwoPickService _svc;
|
private readonly IArenaTwoPickService _svc;
|
||||||
public ArenaTwoPickController(IArenaTwoPickService svc) => _svc = svc;
|
private readonly IMissionProgressService _missionProgress;
|
||||||
|
|
||||||
|
public ArenaTwoPickController(IArenaTwoPickService svc, IMissionProgressService missionProgress)
|
||||||
|
{
|
||||||
|
_svc = svc;
|
||||||
|
_missionProgress = missionProgress;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("top")]
|
[HttpPost("top")]
|
||||||
public async Task<IActionResult> Top([FromBody] TopRequest _)
|
public async Task<IActionResult> Top([FromBody] TopRequest _)
|
||||||
@@ -46,12 +53,17 @@ public class ArenaTwoPickController : SVSimController
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("finish")]
|
[HttpPost("finish")]
|
||||||
public async Task<IActionResult> Finish([FromBody] FinishRequest _)
|
public async Task<IActionResult> Finish([FromBody] FinishRequest _, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (!TryGetViewerId(out var vid)) return Unauthorized();
|
if (!TryGetViewerId(out var vid)) return Unauthorized();
|
||||||
return await GuardAsync(async () =>
|
return await GuardAsync(async () =>
|
||||||
{
|
{
|
||||||
var outcome = await _svc.FinishAsync(vid);
|
var outcome = await _svc.FinishAsync(vid);
|
||||||
|
if (outcome.WasFullClear)
|
||||||
|
{
|
||||||
|
await _missionProgress.RecordEventAsync(
|
||||||
|
vid, MissionEventKeys.Challenge.FullClearAll(), ct: ct);
|
||||||
|
}
|
||||||
return outcome.Response;
|
return outcome.Response;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using SVSim.BattleNode.Bridge;
|
|||||||
using SVSim.Database;
|
using SVSim.Database;
|
||||||
using SVSim.Database.Enums;
|
using SVSim.Database.Enums;
|
||||||
using SVSim.Database.Repositories.Viewer;
|
using SVSim.Database.Repositories.Viewer;
|
||||||
|
using SVSim.Database.Services;
|
||||||
using SVSim.Database.Services.BattleXp;
|
using SVSim.Database.Services.BattleXp;
|
||||||
using SVSim.Database.Services.Friend;
|
using SVSim.Database.Services.Friend;
|
||||||
using SVSim.Database.Services.Replay;
|
using SVSim.Database.Services.Replay;
|
||||||
@@ -37,6 +38,7 @@ public sealed class FreeBattleController : ControllerBase
|
|||||||
private readonly IPlayedTogetherWriter _playedTogetherWriter;
|
private readonly IPlayedTogetherWriter _playedTogetherWriter;
|
||||||
private readonly IViewerRepository _viewers;
|
private readonly IViewerRepository _viewers;
|
||||||
private readonly IBattleXpService _xp;
|
private readonly IBattleXpService _xp;
|
||||||
|
private readonly IMissionProgressService _missionProgress;
|
||||||
private readonly SVSimDbContext _db;
|
private readonly SVSimDbContext _db;
|
||||||
private readonly ILogger<FreeBattleController> _log;
|
private readonly ILogger<FreeBattleController> _log;
|
||||||
|
|
||||||
@@ -48,6 +50,7 @@ public sealed class FreeBattleController : ControllerBase
|
|||||||
IPlayedTogetherWriter playedTogetherWriter,
|
IPlayedTogetherWriter playedTogetherWriter,
|
||||||
IViewerRepository viewers,
|
IViewerRepository viewers,
|
||||||
IBattleXpService xp,
|
IBattleXpService xp,
|
||||||
|
IMissionProgressService missionProgress,
|
||||||
SVSimDbContext db,
|
SVSimDbContext db,
|
||||||
ILogger<FreeBattleController> log)
|
ILogger<FreeBattleController> log)
|
||||||
{
|
{
|
||||||
@@ -58,6 +61,7 @@ public sealed class FreeBattleController : ControllerBase
|
|||||||
_playedTogetherWriter = playedTogetherWriter;
|
_playedTogetherWriter = playedTogetherWriter;
|
||||||
_viewers = viewers;
|
_viewers = viewers;
|
||||||
_xp = xp;
|
_xp = xp;
|
||||||
|
_missionProgress = missionProgress;
|
||||||
_db = db;
|
_db = db;
|
||||||
_log = log;
|
_log = log;
|
||||||
}
|
}
|
||||||
@@ -120,6 +124,13 @@ public sealed class FreeBattleController : ControllerBase
|
|||||||
level = xp.Level == 0 ? 1 : xp.Level;
|
level = xp.Level == 0 ? 1 : xp.Level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mission counters — unranked wins feed ranked_or_arena_win + daily_match_win.
|
||||||
|
if (isWin)
|
||||||
|
{
|
||||||
|
await _missionProgress.RecordEventAsync(
|
||||||
|
vid, MissionEventKeys.Free.WinAll(), ct: ct);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(new FreeBattleFinishResponseDto
|
return Ok(new FreeBattleFinishResponseDto
|
||||||
{
|
{
|
||||||
BattleResult = req.BattleResult,
|
BattleResult = req.BattleResult,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using SVSim.BattleNode.Sessions;
|
|||||||
using SVSim.Database;
|
using SVSim.Database;
|
||||||
using SVSim.Database.Enums;
|
using SVSim.Database.Enums;
|
||||||
using SVSim.Database.Repositories.Viewer;
|
using SVSim.Database.Repositories.Viewer;
|
||||||
|
using SVSim.Database.Services;
|
||||||
using SVSim.Database.Services.BattleXp;
|
using SVSim.Database.Services.BattleXp;
|
||||||
using SVSim.Database.Services.Friend;
|
using SVSim.Database.Services.Friend;
|
||||||
using SVSim.Database.Services.RankProgress;
|
using SVSim.Database.Services.RankProgress;
|
||||||
@@ -40,6 +41,7 @@ public sealed class RankBattleController : ControllerBase
|
|||||||
private readonly IViewerRepository _viewers;
|
private readonly IViewerRepository _viewers;
|
||||||
private readonly IBattleXpService _xp;
|
private readonly IBattleXpService _xp;
|
||||||
private readonly IRankProgressService _rankProgress;
|
private readonly IRankProgressService _rankProgress;
|
||||||
|
private readonly IMissionProgressService _missionProgress;
|
||||||
private readonly SVSimDbContext _db;
|
private readonly SVSimDbContext _db;
|
||||||
private readonly ILogger<RankBattleController> _log;
|
private readonly ILogger<RankBattleController> _log;
|
||||||
|
|
||||||
@@ -54,6 +56,7 @@ public sealed class RankBattleController : ControllerBase
|
|||||||
IViewerRepository viewers,
|
IViewerRepository viewers,
|
||||||
IBattleXpService xp,
|
IBattleXpService xp,
|
||||||
IRankProgressService rankProgress,
|
IRankProgressService rankProgress,
|
||||||
|
IMissionProgressService missionProgress,
|
||||||
SVSimDbContext db,
|
SVSimDbContext db,
|
||||||
ILogger<RankBattleController> log)
|
ILogger<RankBattleController> log)
|
||||||
{
|
{
|
||||||
@@ -67,6 +70,7 @@ public sealed class RankBattleController : ControllerBase
|
|||||||
_viewers = viewers;
|
_viewers = viewers;
|
||||||
_xp = xp;
|
_xp = xp;
|
||||||
_rankProgress = rankProgress;
|
_rankProgress = rankProgress;
|
||||||
|
_missionProgress = missionProgress;
|
||||||
_db = db;
|
_db = db;
|
||||||
_log = log;
|
_log = log;
|
||||||
}
|
}
|
||||||
@@ -161,6 +165,13 @@ public sealed class RankBattleController : ControllerBase
|
|||||||
level = xp.Level == 0 ? 1 : xp.Level;
|
level = xp.Level == 0 ? 1 : xp.Level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mission counters — AI-rank routes emit the same keys as human-rank on a private server.
|
||||||
|
if (isWin)
|
||||||
|
{
|
||||||
|
await _missionProgress.RecordEventAsync(
|
||||||
|
vid, MissionEventKeys.Ranked.WinAll(req.ClassId), ct: ct);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(new RankBattleFinishResponseDto
|
return Ok(new RankBattleFinishResponseDto
|
||||||
{
|
{
|
||||||
BattleResult = req.BattleResult,
|
BattleResult = req.BattleResult,
|
||||||
|
|||||||
Reference in New Issue
Block a user