95 lines
3.5 KiB
C#
95 lines
3.5 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|