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); }