Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using XWorld.Framework;
|
||||
using XWorld.Framework.Protocol;
|
||||
using XWorld.Server.Host;
|
||||
using RPS.Core;
|
||||
|
||||
namespace XWorld.Server.Host.Tests
|
||||
{
|
||||
public class RoomHostTests
|
||||
{
|
||||
private sealed class Capture : IRoomOutput
|
||||
{
|
||||
public List<NetMessage> Broadcasts = new List<NetMessage>();
|
||||
public void Broadcast(NetMessage m) => Broadcasts.Add(m);
|
||||
public void SendTo(int playerId, NetMessage m) { }
|
||||
}
|
||||
|
||||
// 1 真人(seat 0) + 1 AI(seat 1)
|
||||
private static readonly IReadOnlyList<PlayerInfo> Players = new[]
|
||||
{
|
||||
new PlayerInfo { PlayerId = 1, Name = "H", IsAI = false },
|
||||
new PlayerInfo { PlayerId = -1, Name = "AI", IsAI = true },
|
||||
};
|
||||
|
||||
private static RpsState LastState(Capture cap)
|
||||
=> new RpsLogic().Decode(cap.Broadcasts[cap.Broadcasts.Count - 1].Payload);
|
||||
|
||||
private static RoomHost NewHost()
|
||||
=> new RoomHost(new GameModuleRegistry(new GameModuleLoader(), TestGames.Root), new ConsoleLogger());
|
||||
|
||||
private static RoomConfig Cfg()
|
||||
=> new RoomConfig { GameId = "rps", Version = 1, Seed = 1, PlayerCount = 2 };
|
||||
|
||||
[Fact]
|
||||
public void CreateRoom_TickToEnd_ReleasesModule()
|
||||
{
|
||||
var host = NewHost();
|
||||
var cap = new Capture();
|
||||
|
||||
var room = host.CreateRoom("A", "rps", 1, Cfg(), Players, cap);
|
||||
Assert.Equal(1, host.ActiveRoomCount);
|
||||
|
||||
// 大 dt 推进:60s 对局在数个大 tick 内结束
|
||||
for (int i = 0; i < 10 && host.ActiveRoomCount > 0; i++) host.TickAll(10f);
|
||||
|
||||
Assert.Equal(0, host.ActiveRoomCount);
|
||||
Assert.False(room.IsActive);
|
||||
Assert.Equal(RpsPhase.Finished, LastState(cap).Phase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deliver_RoutesChoice_AffectsState()
|
||||
{
|
||||
var host = NewHost();
|
||||
var cap = new Capture();
|
||||
host.CreateRoom("A", "rps", 1, Cfg(), Players, cap);
|
||||
|
||||
// seat 0(玩家 1)出 Rock:opcode=1(ChoiceOpcode),payload=单字节 choice
|
||||
var w = new PacketWriter();
|
||||
w.WriteByte((byte)Choice.Rock);
|
||||
host.DeliverTo("A", 1, new NetMessage(1, w.ToArray()));
|
||||
host.TickAll(0.1f); // 小 dt,仍在 Choosing,广播快照
|
||||
|
||||
Assert.Equal(Choice.Rock, LastState(cap).Choices[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateRoom_DuplicateRoomId_Throws()
|
||||
{
|
||||
var host = NewHost();
|
||||
host.CreateRoom("dup", "rps", 1, Cfg(), Players, new Capture());
|
||||
Assert.Throws<System.InvalidOperationException>(
|
||||
() => host.CreateRoom("dup", "rps", 1, Cfg(), Players, new Capture()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TickAll_ReturnsEndedRoomIds()
|
||||
{
|
||||
var host = NewHost();
|
||||
host.CreateRoom("E", "rps", 1, Cfg(), Players, new Capture());
|
||||
|
||||
var first = host.TickAll(10f); // 第一大 tick:TotalElapsed=10 < 60,不结束
|
||||
Assert.Empty(first);
|
||||
|
||||
var ended = new List<EndedRoom>();
|
||||
for (int i = 0; i < 10 && !ended.Exists(e => e.RoomId == "E"); i++)
|
||||
ended.AddRange(host.TickAll(10f));
|
||||
Assert.Contains(ended, e => e.RoomId == "E"); // 累计超过 60s 后该 tick 返回结束房间
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TickAll_EndedRoom_CarriesResult()
|
||||
{
|
||||
var host = NewHost();
|
||||
host.CreateRoom("R", "rps", 1, Cfg(), Players, new Capture());
|
||||
|
||||
RoomEndResult result = null;
|
||||
bool found = false;
|
||||
for (int i = 0; i < 10 && !found; i++)
|
||||
foreach (var e in host.TickAll(10f))
|
||||
if (e.RoomId == "R") { found = true; result = e.Result; }
|
||||
|
||||
Assert.True(found);
|
||||
Assert.NotNull(result); // 夹具已重建,RPS 走 EndRoom(result)
|
||||
Assert.NotNull(result.ResultBlob);
|
||||
Assert.NotEmpty(result.ResultBlob); // "最终比分 x : y" 或 "平局 x : y"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user