feat(battle-node): RealParticipant session-finished signal + Pvp cascade
RealParticipant gains _sessionFinished TCS + MarkSessionFinished / AwaitSessionFinishedAsync. PvP first-arriver's handler awaits the signal instead of calling self.RunAsync (which the session does internally on the same instance — double-call would race the WS read). BattleSession.RunAsync branches on Type: Pvp uses WhenAny + synthesize BattleFinish(Win) to survivor + WhenAll(drain); Scripted/Bot keep Phase 1's WhenAll-everything semantics. Disconnect cascade now drives end-of-battle when a WS drops without a graceful Retire.
This commit is contained in:
@@ -57,6 +57,33 @@ public sealed class RealParticipant : IBattleParticipant, IHasHandshakePhase
|
||||
|
||||
public event Func<MsgEnvelope, CancellationToken, Task>? FrameEmitted;
|
||||
|
||||
private readonly TaskCompletionSource<bool> _sessionFinished
|
||||
= new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
/// <summary>Called by the second arriver's handler (in a finally block) after
|
||||
/// session.RunAsync completes. Signals the first arriver's handler that it can
|
||||
/// return and let the HTTP request complete (which closes the WS).</summary>
|
||||
internal void MarkSessionFinished() => _sessionFinished.TrySetResult(true);
|
||||
|
||||
/// <summary>Awaited by the first arriver's handler instead of calling RunAsync
|
||||
/// (the session already calls RunAsync on this instance from the second arriver's
|
||||
/// handler context — calling it twice would race the WS read loop). Returns when
|
||||
/// either MarkSessionFinished fires or the passed CT cancels.</summary>
|
||||
internal Task AwaitSessionFinishedAsync(CancellationToken ct)
|
||||
{
|
||||
if (_sessionFinished.Task.IsCompleted) return _sessionFinished.Task;
|
||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var reg = ct.Register(() => tcs.TrySetCanceled(ct));
|
||||
_sessionFinished.Task.ContinueWith(t =>
|
||||
{
|
||||
reg.Dispose();
|
||||
if (t.IsCompletedSuccessfully) tcs.TrySetResult(true);
|
||||
else if (t.IsFaulted) tcs.TrySetException(t.Exception!.InnerExceptions);
|
||||
else tcs.TrySetCanceled();
|
||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public RealParticipant(WebSocket ws, long viewerId, MatchContext context,
|
||||
ILogger<RealParticipant> log)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user