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
@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace XWorld.Server.Gateway.Tests
{
internal sealed class FakeConnection : IClientConnection
{
public int PlayerId { get; }
public List<byte[]> Sent { get; } = new List<byte[]>();
public FakeConnection(int playerId) { PlayerId = playerId; }
public void Send(byte[] frame) => Sent.Add(frame);
}
}
+11
View File
@@ -0,0 +1,11 @@
using System;
using System.IO;
namespace XWorld.Server.Gateway.Tests
{
internal static class TestGames
{
// 前置条件:先运行 build-test-games.sh 生成 Server.Host.Tests/TestGames(本工程复用之)
public static string Root => Path.Combine(AppContext.BaseDirectory, "TestGames");
}
}
@@ -0,0 +1,51 @@
using System;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using XWorld.Framework.Protocol;
namespace XWorld.Server.Gateway.Tests
{
// 集成测试用 WS 客户端:连接、发帧、带超时收一帧并解码
internal sealed class WsTestClient : IDisposable
{
private readonly ClientWebSocket _ws = new ClientWebSocket();
public async Task ConnectAsync(string wsBaseUrl, int pid)
{
await _ws.ConnectAsync(new Uri($"{wsBaseUrl}/ws?pid={pid}"), CancellationToken.None);
}
public async Task SendFrameAsync(Frame frame)
{
byte[] bytes = FrameCodec.Encode(frame);
await _ws.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, true, CancellationToken.None);
}
// 在 timeout 内收一条 WS 消息并解码为 Frame;超时抛 TimeoutException
public async Task<Frame> ReceiveFrameAsync(int timeoutMs = 5000)
{
using var cts = new CancellationTokenSource(timeoutMs);
var buf = new byte[8192];
using var ms = new MemoryStream();
WebSocketReceiveResult res;
do
{
res = await _ws.ReceiveAsync(new ArraySegment<byte>(buf), cts.Token);
if (res.MessageType == WebSocketMessageType.Close)
throw new InvalidOperationException("server closed");
ms.Write(buf, 0, res.Count);
} while (!res.EndOfMessage);
return FrameCodec.Decode(ms.ToArray());
}
public async Task CloseAsync()
{
try { await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "bye", CancellationToken.None); }
catch { /* 忽略 */ }
}
public void Dispose() => _ws.Dispose();
}
}