using System; using System.Collections.Generic; namespace XWorld.Server.Gateway { // pid -> Session。只在 actor 线程被改,故无锁。 public sealed class SessionManager { private readonly Dictionary _byPid = new Dictionary(); public Session OnConnect(int pid, IClientConnection conn) { if (_byPid.TryGetValue(pid, out var s)) { s.Connection = conn; // 重连:重绑新连接,保留 RoomId s.Connected = true; return s; } s = new Session { PlayerId = pid, Connection = conn, Connected = true }; _byPid[pid] = s; return s; } public void OnDisconnect(int pid, IClientConnection conn, long currentTick) { // 仅当断的是"当前连接"才标离线,避免旧连接的迟到断线清掉已重连的新连接 if (_byPid.TryGetValue(pid, out var s) && ReferenceEquals(s.Connection, conn)) { s.Connected = false; s.DisconnectedAtTick = currentTick; } } public IClientConnection GetConnection(int pid) => _byPid.TryGetValue(pid, out var s) && s.Connected ? s.Connection : null; public bool IsConnected(int pid) => _byPid.TryGetValue(pid, out var s) && s.Connected; public Session Get(int pid) => _byPid.TryGetValue(pid, out var s) ? s : null; /// 移除断线且"严格越过"重连窗口的会话(currentTick - DisconnectedAtTick > windowTicks)。返回被移除的会话;无则空数组。 public IReadOnlyList SweepExpired(long currentTick, long windowTicks) { List removed = null; foreach (var kv in _byPid) { var s = kv.Value; if (!s.Connected && currentTick - s.DisconnectedAtTick > windowTicks) (removed ??= new List()).Add(s); } if (removed != null) foreach (var s in removed) _byPid.Remove(s.PlayerId); return removed ?? (IReadOnlyList)Array.Empty(); } } }