diff --git a/SVSim.BattleNode/Sessions/IBattleParticipant.cs b/SVSim.BattleNode/Sessions/IBattleParticipant.cs new file mode 100644 index 0000000..a129f56 --- /dev/null +++ b/SVSim.BattleNode/Sessions/IBattleParticipant.cs @@ -0,0 +1,46 @@ +using SVSim.BattleNode.Bridge; +using SVSim.BattleNode.Protocol; + +namespace SVSim.BattleNode.Sessions; + +/// +/// One side of a battle. Two of these are held by a BattleSession; the session +/// brokers between them. Concrete impls (added in subsequent Phase-1 tasks): +/// +/// RealParticipant — WS-backed. +/// NoOpBotParticipant — silent; for BattleType.Bot (AI-passive). +/// ScriptedBotParticipant — wraps the v1.2 lifecycle for +/// BattleType.Scripted (solo testing harness). +/// +/// +public interface IBattleParticipant : IAsyncDisposable +{ + /// Real viewer id, or a synthetic stable id for bots + /// (). + long ViewerId { get; } + + /// Per-battle MatchContext snapshot, used for building Matched/BattleStart + /// selfInfo when this participant is "self" in the perspective. + MatchContext Context { get; } + + /// Session calls this to deliver a frame from the OTHER participant + /// (or a server-synthesized broadcast). Real impl: encode + WS-send. + /// NoOp: swallow. Scripted: may emit a response via . + /// True for control frames (BattleFinish, JudgeResult, ack); + /// bypasses playSeq assignment + archive. + Task PushAsync(MsgEnvelope envelope, bool noStock, CancellationToken ct); + + /// Participant fires this when it has a frame to send TO the session + /// (its own gameplay action). Real impl: fires on WS recv. NoOp: never fires. + /// Scripted: fires from inside PushAsync when the scripted lifecycle wants to + /// respond to an inbound frame. + event Func? FrameEmitted; + + /// Drives the participant's inbound loop. For Real: the WS read loop + /// (returns when the WS closes). For NoOp/Scripted: completes immediately (the + /// session keeps running as long as the OTHER participant's RunAsync is alive). + Task RunAsync(CancellationToken ct); + + /// Called when the battle ends. Concrete impls clean up (close WS, etc.). + Task TerminateAsync(BattleFinishReason reason); +}