Add detailed ride diagnostics to match logging
CI / test (push) Successful in 2m26s Details

This commit is contained in:
MpakobecEx 2026-10-23 17:37:34 +05:00
parent 5a6e96b678
commit 7ad486f382
1 changed files with 103 additions and 2 deletions

View File

@ -109,6 +109,14 @@ public sealed class MatchGrain : IMatchGrain
_engine.Start(setup.FirstPlayer); _engine.Start(setup.FirstPlayer);
RefreshSummary(); RefreshSummary();
_logger.LogInformation(
"Match {MatchId}: created {Seat0} vs {Seat1}, first={FirstPlayer}. {State}",
_matchId,
_seats[0].PlayerId,
_seats[1].PlayerId,
setup.FirstPlayer,
DescribeTurnState());
} }
public string MatchId => _matchId; public string MatchId => _matchId;
@ -172,8 +180,8 @@ public sealed class MatchGrain : IMatchGrain
await SendClockAsync(seat, now); await SendClockAsync(seat, now);
_logger.LogInformation( _logger.LogInformation(
"Match {MatchId}: seat {Seat} attached (resume from {Seq}, now {Now}).", "Match {MatchId}: seat {Seat} attached (resume from {Seq}, now {Now}). {State}",
_matchId, seat, resumeFromSeq, to); _matchId, seat, resumeFromSeq, to, DescribeTurnState());
await PersistHeaderAsync(); await PersistHeaderAsync();
@ -314,6 +322,10 @@ public sealed class MatchGrain : IMatchGrain
var decisionBefore = _engine.State.ActivePlayer; var decisionBefore = _engine.State.ActivePlayer;
_logger.LogInformation(
"Match {MatchId}: seat {Seat} intent {IntentType} ({IntentId}) received. {State}",
_matchId, seat, DescribeIntent(intent.Intent), intentId, DescribeTurnState());
CommandResult result; CommandResult result;
var commandText = string.Empty; var commandText = string.Empty;
@ -349,6 +361,20 @@ public sealed class MatchGrain : IMatchGrain
await SendResultAsync( await SendResultAsync(
seat, intentId, record.Accepted, record.Code, record.Reason, record.EventSeq, duplicate: false); seat, intentId, record.Accepted, record.Code, record.Reason, record.EventSeq, duplicate: false);
if (record.Accepted)
{
_logger.LogInformation(
"Match {MatchId}: seat {Seat} intent {IntentType} ({IntentId}) accepted. {State}",
_matchId, seat, DescribeIntent(intent.Intent), intentId, DescribeTurnState());
}
else
{
_logger.LogWarning(
"Match {MatchId}: seat {Seat} intent {IntentType} ({IntentId}) rejected {Code}: {Reason}. {State}",
_matchId, seat, DescribeIntent(intent.Intent), intentId, record.Code, record.Reason,
DescribeTurnState());
}
if (!record.Accepted) if (!record.Accepted)
return; return;
@ -462,6 +488,10 @@ public sealed class MatchGrain : IMatchGrain
IntentRejectCode code, IntentRejectCode code,
string reason) string reason)
{ {
_logger.LogWarning(
"Match {MatchId}: seat {Seat} intent {IntentId} rejected before execution {Code}: {Reason}. {State}",
_matchId, seat, intentId, code, reason, DescribeTurnState());
_intents[intentId] = new IntentRecord(false, code, reason, _engine.Events.Count); _intents[intentId] = new IntentRecord(false, code, reason, _engine.Events.Count);
await PersistIntentAsync(new PersistedIntent( await PersistIntentAsync(new PersistedIntent(
@ -510,6 +540,15 @@ public sealed class MatchGrain : IMatchGrain
var from = Math.Max(1, fromSeq); var from = Math.Max(1, fromSeq);
var events = EventProjection.ProjectRange(_engine.Events, seat, from); var events = EventProjection.ProjectRange(_engine.Events, seat, from);
_logger.LogInformation(
"Match {MatchId}: events {FromSeq}..{ToSeq} to seat {Seat}: [{EventTypes}]. {State}",
_matchId,
from,
to,
seat,
string.Join(", ", events.Select(e => e.GetType().Name)),
DescribeTurnState());
_seats[seat].SentSeq = to; _seats[seat].SentSeq = to;
await SendToSeatAsync(seat, new ServerEvents await SendToSeatAsync(seat, new ServerEvents
@ -524,6 +563,22 @@ public sealed class MatchGrain : IMatchGrain
{ {
var to = _engine.Events.Count; var to = _engine.Events.Count;
var view = MatchProjection.ForViewer(_engine, seat, _matchId, to, BuildClock(now)); var view = MatchProjection.ForViewer(_engine, seat, _matchId, to, BuildClock(now));
var legalRides = string.Join(
", ",
view.LegalIntents.OfType<RideIntent>().Select(i => i.PrintingId));
_logger.LogInformation(
"Match {MatchId}: snapshot to seat {Seat} at seq {Seq}, you={You}, active={Active}, turn={Turn}, phase={Phase}, legal={LegalCount}, rides=[{Rides}]. {State}",
_matchId,
seat,
to,
view.You,
view.ActivePlayer,
view.TurnNumber,
view.Phase,
view.LegalIntents.Count,
legalRides,
DescribeTurnState());
_seats[seat].SentSeq = to; _seats[seat].SentSeq = to;
@ -780,6 +835,52 @@ public sealed class MatchGrain : IMatchGrain
} }
} }
private static string DescribeIntent(IntentPayload? intent) =>
intent is RideIntent ride
? $"Ride({ride.PrintingId})"
: intent?.GetType().Name ?? "null";
/// <summary>
/// Diagnostic snapshot for ride issues: active player, phase, vanguard grade,
/// hand grades and cards that should be offered for a ride.
/// </summary>
private string DescribeTurnState()
{
var state = _engine.State;
var active = state.ActivePlayer;
var player = state.Players[active];
var rideGrade = _engine.VanguardGrade(active);
var vanguard = player.Vanguard is null
? "none"
: $"{player.Vanguard.PrintingId}(g{rideGrade})";
var hand = string.Join(
", ",
player.Hand.Select(card =>
{
var grade = state.Catalog.CardOf(card.PrintingId).Grade;
return $"{card.PrintingId}(g{grade})";
}));
var rideReady = state.Phase == Phase.Ride
? string.Join(
", ",
player.Hand
.Where(card =>
{
var grade = state.Catalog.CardOf(card.PrintingId).Grade;
return grade == rideGrade || grade == rideGrade + 1;
})
.Select(card => card.PrintingId)
.Distinct())
: string.Empty;
return $"active={active} turn={state.TurnNumber} phase={state.Phase} " +
$"step={state.Step} vanguard={vanguard} hand=[{hand}] " +
$"rideReady=[{rideReady}]";
}
private void RefreshSummary() private void RefreshSummary()
{ {
_summary = new MatchSummary( _summary = new MatchSummary(