Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace XWorld.Server.Gateway
|
||||
{
|
||||
public sealed class Matchmaker
|
||||
{
|
||||
public sealed class Seat { public int PlayerId; public bool IsAi; }
|
||||
public sealed class Match { public string GameId; public int Version; public List<Seat> Seats = new List<Seat>(); }
|
||||
|
||||
private sealed class Waiter { public int Pid; public long EnqueuedTick; }
|
||||
private sealed class Bucket { public int PlayerCount; public List<Waiter> Waiters = new List<Waiter>(); }
|
||||
|
||||
private readonly long _timeoutTicks;
|
||||
private int _aiSeq;
|
||||
private readonly Dictionary<string, Bucket> _buckets = new Dictionary<string, Bucket>();
|
||||
|
||||
public Matchmaker(long timeoutTicks) { _timeoutTicks = timeoutTicks; }
|
||||
private static string Key(string g, int v) => $"{g}@{v}";
|
||||
|
||||
public void Enqueue(int pid, string gameId, int version, int playerCount, long tick)
|
||||
{
|
||||
string k = Key(gameId, version);
|
||||
if (!_buckets.TryGetValue(k, out var b)) { b = new Bucket { PlayerCount = playerCount }; _buckets[k] = b; }
|
||||
b.Waiters.Add(new Waiter { Pid = pid, EnqueuedTick = tick });
|
||||
}
|
||||
|
||||
// 从等待队列移除某玩家(断线时调用);返回是否移除
|
||||
public bool Remove(int pid)
|
||||
{
|
||||
foreach (var kv in _buckets)
|
||||
{
|
||||
var ws = kv.Value.Waiters;
|
||||
for (int i = 0; i < ws.Count; i++)
|
||||
if (ws[i].Pid == pid) { ws.RemoveAt(i); return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返回本次可成形的对局(凑齐或超时 AI 补足)
|
||||
public IReadOnlyList<Match> Poll(long currentTick)
|
||||
{
|
||||
List<Match> formed = null;
|
||||
foreach (var kv in _buckets)
|
||||
{
|
||||
var b = kv.Value;
|
||||
string[] gv = kv.Key.Split('@');
|
||||
while (b.Waiters.Count > 0 &&
|
||||
(b.Waiters.Count >= b.PlayerCount ||
|
||||
currentTick - b.Waiters[0].EnqueuedTick >= _timeoutTicks))
|
||||
{
|
||||
var m = new Match { GameId = gv[0], Version = int.Parse(gv[1]) };
|
||||
int take = b.Waiters.Count >= b.PlayerCount ? b.PlayerCount : b.Waiters.Count;
|
||||
for (int i = 0; i < take; i++)
|
||||
m.Seats.Add(new Seat { PlayerId = b.Waiters[i].Pid, IsAi = false });
|
||||
b.Waiters.RemoveRange(0, take);
|
||||
while (m.Seats.Count < b.PlayerCount)
|
||||
m.Seats.Add(new Seat { PlayerId = -(++_aiSeq), IsAi = true }); // AI 用负 pid
|
||||
(formed ??= new List<Match>()).Add(m);
|
||||
}
|
||||
}
|
||||
return formed ?? (IReadOnlyList<Match>)System.Array.Empty<Match>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user