#nullable enable using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; namespace ShrinkNetwork { public sealed class ShrinkLoopbackTransport : IShrinkNetworkAsyncTransport, IShrinkNetworkSessionControlTransport { private readonly HashSet _openedSessions = new(); private ShrinkLoopbackTransport? _peer; public bool IsStarted { get; private set; } public event Action? OnEvent; public void LinkPeer(ShrinkLoopbackTransport peer) { _peer = peer; if (peer._peer != this) peer.LinkPeer(this); } public void Start() { IsStarted = true; } public void Stop() { IsStarted = false; } public void OpenSession(long sessionId, string remoteAddress = "loopback") { _openedSessions.Add(sessionId); _peer?._openedSessions.Add(sessionId); OnEvent?.Invoke(ShrinkNetworkTransportEvent.Connected(sessionId, remoteAddress)); _peer?.OnEvent?.Invoke(ShrinkNetworkTransportEvent.Connected(sessionId, remoteAddress)); } public void CloseSession(long sessionId, string remoteAddress = "loopback") { _openedSessions.Remove(sessionId); _peer?._openedSessions.Remove(sessionId); OnEvent?.Invoke(ShrinkNetworkTransportEvent.Disconnected(sessionId, remoteAddress)); _peer?.OnEvent?.Invoke(ShrinkNetworkTransportEvent.Disconnected(sessionId, remoteAddress)); } public bool DisconnectSession(long sessionId, string? reason = null) { if (!_openedSessions.Contains(sessionId)) return false; CloseSession(sessionId); return true; } public void Send(long sessionId, byte[] packetData) { SendAsync(sessionId, packetData).Forget(); } public UniTask SendAsync(long sessionId, byte[] packetData) { if (!IsStarted) throw new InvalidOperationException("Transport has not been started."); if (_peer == null) throw new InvalidOperationException("Peer transport is not linked."); if (!_openedSessions.Contains(sessionId)) throw new InvalidOperationException($"Session {sessionId} is not open."); _peer.OnEvent?.Invoke(ShrinkNetworkTransportEvent.Packet(sessionId, packetData)); return UniTask.CompletedTask; } } }