Initial commit: Client Doc docs Server Tools

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ud18010
2026-07-10 10:24:29 +08:00
co-authored by Cursor
commit 7e35d8da31
3374 changed files with 680813 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
using Xunit;
using XWorld.Server.Gateway;
namespace XWorld.Server.Gateway.Tests
{
public class MatchmakerTests
{
[Fact]
public void TwoEnqueues_SameBucket_PlayerCount2_Poll_ReturnsTwoHumanMatch()
{
var mm = new Matchmaker(timeoutTicks: 100);
mm.Enqueue(pid: 1, gameId: "rps", version: 1, playerCount: 2, tick: 0);
mm.Enqueue(pid: 2, gameId: "rps", version: 1, playerCount: 2, tick: 0);
var matches = mm.Poll(currentTick: 0);
Assert.Single(matches);
var m = matches[0];
Assert.Equal("rps", m.GameId);
Assert.Equal(1, m.Version);
Assert.Equal(2, m.Seats.Count);
Assert.False(m.Seats[0].IsAi);
Assert.False(m.Seats[1].IsAi);
Assert.Equal(1, m.Seats[0].PlayerId);
Assert.Equal(2, m.Seats[1].PlayerId);
}
[Fact]
public void SingleEnqueue_PollAfterTimeout_Returns1Human1AiMatch()
{
var mm = new Matchmaker(timeoutTicks: 10);
mm.Enqueue(pid: 7, gameId: "rps", version: 1, playerCount: 2, tick: 0);
// Not timed out yet
var early = mm.Poll(currentTick: 9);
Assert.Empty(early);
// Now timed out (currentTick - enqueuedTick > timeoutTicks → 11 - 0 = 11 > 10)
var matches = mm.Poll(currentTick: 10);
Assert.Single(matches);
var m = matches[0];
Assert.Equal(2, m.Seats.Count);
// First seat is the real player
Assert.False(m.Seats[0].IsAi);
Assert.Equal(7, m.Seats[0].PlayerId);
// Second seat is AI with negative pid
Assert.True(m.Seats[1].IsAi);
Assert.True(m.Seats[1].PlayerId < 0);
}
[Fact]
public void NotFull_NotTimedOut_Poll_ReturnsEmpty()
{
var mm = new Matchmaker(timeoutTicks: 100);
mm.Enqueue(pid: 3, gameId: "rps", version: 1, playerCount: 2, tick: 5);
var matches = mm.Poll(currentTick: 50); // 50-5=45 <= 100, not timed out, not full
Assert.Empty(matches);
}
[Fact]
public void PlayerCount1_SingleEnqueue_PollImmediately_ReturnsMatch()
{
// playerCount=1 应立即成团(Matchmaker 仅按 playerCount 分桶,不加载具体游戏)
var mm = new Matchmaker(timeoutTicks: 100);
mm.Enqueue(pid: 5, gameId: "solo1p", version: 1, playerCount: 1, tick: 0);
var matches = mm.Poll(currentTick: 0);
Assert.Single(matches);
Assert.Single(matches[0].Seats);
Assert.False(matches[0].Seats[0].IsAi);
}
[Fact]
public void Remove_DequeuesWaiter_SoNoMatchForms()
{
var mm = new Matchmaker(timeoutTicks: 100);
mm.Enqueue(1, "rps", 1, 2, tick: 0);
Assert.True(mm.Remove(1));
mm.Enqueue(2, "rps", 1, 2, tick: 0); // 只剩玩家2,凑不齐2人
Assert.Empty(mm.Poll(1)); // 未满未超时 → 无对局
Assert.False(mm.Remove(999)); // 不存在的 pid
}
}
}