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
+94
View File
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using XWorld.Framework;
namespace XWorld.Server.Host
{
// TickAll 的结束房间条目:携带小游戏经 EndRoom(result) 给出的结算结果(可为 null)
public readonly struct EndedRoom
{
public readonly string RoomId;
public readonly RoomEndResult Result;
public EndedRoom(string roomId, RoomEndResult result) { RoomId = roomId; Result = result; }
}
public sealed class RoomHost
{
private readonly GameModuleRegistry _registry;
private readonly ILogger _logger;
private readonly Dictionary<string, RoomEntry> _rooms = new Dictionary<string, RoomEntry>();
private struct RoomEntry { public Room Room; public LoadedGameModule Module; }
public RoomHost(GameModuleRegistry registry, ILogger logger)
{
_registry = registry; _logger = logger;
}
public int ActiveRoomCount
{
get { int n = 0; foreach (var kv in _rooms) if (kv.Value.Room.IsActive) n++; return n; }
}
public Room CreateRoom(string roomId, string gameId, int version, RoomConfig config,
IReadOnlyList<PlayerInfo> players, IRoomOutput output)
{
if (_rooms.ContainsKey(roomId))
throw new InvalidOperationException($"房间 ID 已存在: {roomId}");
var module = _registry.Acquire(gameId, version);
try
{
IGameServerRoom logic = module.CreateRoom();
var clock = new ManualClock();
Room room = null;
var ctx = new RoomCtx(
new DeterministicRandom(config.Seed),
clock, _logger, new InMemoryStorage(),
output, r => room.RequestEnd(r));
room = new Room(roomId, logic, clock, ctx, _logger);
_rooms[roomId] = new RoomEntry { Room = room, Module = module };
room.Start(players);
if (!room.IsActive) ReleaseRoom(roomId); // Start 内即结束/故障则立即回收
return room;
}
catch
{
// 构建失败(如游戏构造函数抛异常):释放已 Acquire 的模块,避免引用泄漏
if (_rooms.ContainsKey(roomId)) ReleaseRoom(roomId);
else _registry.Release(module);
throw;
}
}
public IReadOnlyList<EndedRoom> TickAll(float dt)
{
List<EndedRoom> ended = null;
foreach (var kv in _rooms)
{
var room = kv.Value.Room;
if (!room.IsActive) { (ended ??= new List<EndedRoom>()).Add(new EndedRoom(kv.Key, room.TrustedEndResult)); continue; }
room.Tick(dt);
if (!room.IsActive) (ended ??= new List<EndedRoom>()).Add(new EndedRoom(kv.Key, room.TrustedEndResult));
}
if (ended != null)
foreach (var e in ended) ReleaseRoom(e.RoomId);
return ended ?? (IReadOnlyList<EndedRoom>)System.Array.Empty<EndedRoom>();
}
public void DeliverTo(string roomId, int playerId, NetMessage message)
{
if (_rooms.TryGetValue(roomId, out var entry))
entry.Room.Deliver(playerId, message);
}
private void ReleaseRoom(string roomId)
{
if (!_rooms.TryGetValue(roomId, out var entry)) return;
_rooms.Remove(roomId);
_registry.Release(entry.Module);
}
}
}