From 7561c2c7e21ac014b28d9e05ea7cc022354b6c31 Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Fri, 14 Mar 2025 19:35:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20GamingObject=EF=BC=9B?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=95=B4=E4=BD=93=E6=A8=A1=E7=BB=84=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E7=9A=84=E5=BA=95=E5=B1=82=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=20(#113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新示例代码(添加超时示例) --- Interface/Base/Addons/IGameModuleServer.cs | 6 +- .../Common/Addon/Example/ExampleGameModule.cs | 78 ++++++++++++++----- Library/Common/Addon/GameModuleServer.cs | 33 +++++--- Library/Common/Addon/GamingObject.cs | 13 ++++ 4 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 Library/Common/Addon/GamingObject.cs diff --git a/Interface/Base/Addons/IGameModuleServer.cs b/Interface/Base/Addons/IGameModuleServer.cs index 59fcea2..11e1632 100644 --- a/Interface/Base/Addons/IGameModuleServer.cs +++ b/Interface/Base/Addons/IGameModuleServer.cs @@ -1,12 +1,12 @@ -using Milimoe.FunGame.Core.Entity; -using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Interface.Addons { public interface IGameModuleServer : IAddon, IAddonController, IGameModuleDepend { - public bool StartServer(string GameModule, Room Room, List Users, IServerModel RoomMasterServerModel, Dictionary ServerModels, params object[] args); + public bool StartServer(GamingObject obj, params object[] args); public Task> GamingMessageHandler(IServerModel model, GamingType type, Dictionary data); } diff --git a/Library/Common/Addon/Example/ExampleGameModule.cs b/Library/Common/Addon/Example/ExampleGameModule.cs index 05b7e4d..289279c 100644 --- a/Library/Common/Addon/Example/ExampleGameModule.cs +++ b/Library/Common/Addon/Example/ExampleGameModule.cs @@ -129,31 +129,31 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example public override GameModuleDepend GameModuleDepend => ExampleGameModuleConstant.GameModuleDepend; - private readonly struct ModuleServerWorker(Room room, List users, IServerModel roomMaster, Dictionary serverModels) + /// + /// 创建一个工作类,接收服务器启动参数的同时,还能定义一些需要的属性 + /// + /// + private readonly struct ModuleServerWorker(GamingObject obj) { - public Room Room { get; } = room; - public List Users { get; } = users; - public IServerModel RoomMaster { get; } = roomMaster; - public Dictionary All { get; } = serverModels; + public GamingObject GamingObject { get; } = obj; public List ConnectedUser { get; } = []; public Dictionary> UserData { get; } = []; } private ConcurrentDictionary Workers { get; } = []; - public override bool StartServer(string GameModule, Room Room, List Users, IServerModel RoomMasterServerModel, Dictionary ServerModels, params object[] Args) + public override bool StartServer(GamingObject obj, params object[] args) { // 因为模组是单例的,需要为这个房间创建一个工作类接收参数,不能直接用本地变量处理 - ModuleServerWorker worker = new(Room, Users, RoomMasterServerModel, ServerModels); - Workers[Room.Roomid] = worker; - // 创建一个线程执行Test() - TaskUtility.NewTask(async () => await Test(worker)).OnError(Controller.Error); + ModuleServerWorker worker = new(obj); + // 创建一个线程执行Test(),因为这个方法必须立即返回 + TaskUtility.NewTask(async () => await Test(obj, worker)).OnError(Controller.Error); return true; } - private async Task Test(ModuleServerWorker worker) + private async Task Test(GamingObject obj, ModuleServerWorker worker) { - Controller.WriteLine("欢迎各位玩家进入房间 " + worker.Room.Roomid + " 。"); + Controller.WriteLine("欢迎各位玩家进入房间 " + obj.Room.Roomid + " 。"); // 通常,我们可以对客户端的连接状态进行确认,此方法展示如何确认客户端的连接 // 有两种确认的方式,1是服务器主动确认,2是客户端发起确认 @@ -168,7 +168,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example data.Add("connect_token", token); // 我们保存到字典UserData中,这样可以方便跨方法检查变量 - foreach (string username in worker.Users.Select(u => u.Username).Distinct()) + foreach (string username in obj.Users.Select(u => u.Username).Distinct()) { if (worker.UserData.TryGetValue(username, out Dictionary? value)) { @@ -180,16 +180,51 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example worker.UserData[username].Add("connect_token", token); } } - await SendGamingMessage(worker.All.Values, GamingType.UpdateInfo, data); + await SendGamingMessage(obj.All.Values, GamingType.UpdateInfo, data); - // 新建一个线程等待所有玩家确认 - while (true) + // 新建一个线程等待所有玩家确认,如果超时则取消游戏,30秒 + CancellationTokenSource cts = new(); + CancellationToken ct = cts.Token; + Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(30), ct); + + Task completionTask = Task.Run(async () => { - if (worker.ConnectedUser.Count == worker.Users.Count) break; - // 每200ms确认一次,不需要太频繁 - await Task.Delay(200); + while (!ct.IsCancellationRequested) + { + if (worker.ConnectedUser.Count == obj.Users.Count) + { + Controller.WriteLine("所有玩家都已经连接。"); + return; + } + // 每200ms确认一次,不需要太频繁 + await Task.Delay(200); + } + }, ct); + + // 等待完成或超时 + Task completedTask = await Task.WhenAny(completionTask, timeoutTask); + + if (completedTask == timeoutTask) + { + Controller.WriteLine("等待玩家连接超时,放弃该局游戏!", LogLevel.Warning); + cts.Cancel(); + + // 通知已连接的玩家 + Dictionary timeoutData = new() + { + { "msg", "由于等待超时,游戏已取消!" } + }; + // 结束 + SendEndGame(obj); + worker.ConnectedUser.Clear(); + Workers.Remove(obj.Room.Roomid, out _); } - Controller.WriteLine("所有玩家都已经连接。"); + else + { + cts.Cancel(); + } + + cts.Dispose(); } public override async Task> GamingMessageHandler(IServerModel model, GamingType type, Dictionary data) @@ -197,6 +232,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example Dictionary result = []; // 获取model所在的房间工作类 ModuleServerWorker worker = Workers[model.InRoom.Roomid]; + GamingObject obj = worker.GamingObject; string username = model.User.Username; switch (type) @@ -208,7 +244,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example Guid token = NetworkUtility.JsonDeserializeFromDictionary(data, "connect_token"); if (un == username && worker.UserData.TryGetValue(username, out Dictionary? value) && value != null && (value["connect_token"]?.Equals(token) ?? false)) { - worker.ConnectedUser.Add(worker.Users.Where(u => u.Username == username).First()); + worker.ConnectedUser.Add(obj.Users.Where(u => u.Username == username).First()); Controller.WriteLine(username + " 已经连接。"); } else Controller.WriteLine(username + " 确认连接失败!", LogLevel.Warning); diff --git a/Library/Common/Addon/GameModuleServer.cs b/Library/Common/Addon/GameModuleServer.cs index 287d252..f30533c 100644 --- a/Library/Common/Addon/GameModuleServer.cs +++ b/Library/Common/Addon/GameModuleServer.cs @@ -1,5 +1,5 @@ +using System.Collections.Concurrent; using Milimoe.FunGame.Core.Controller; -using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; @@ -68,16 +68,17 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon private ServerAddonController? _Controller; /// - /// 启动服务器监听 请在此处实现服务器逻辑 + /// 此模组所有正在运行的游戏对象 /// - /// - /// - /// - /// - /// - /// + public ConcurrentDictionary GamingObjects { get; } = []; + + /// + /// 启动服务器监听 请在此处实现服务器逻辑。注意,此方法必须立即返回 + /// + /// + /// /// - public abstract bool StartServer(string GameModule, Room Room, List Users, IServerModel RoomMasterServerModel, Dictionary ServerModels, params object[] Args); + public abstract bool StartServer(GamingObject obj, params object[] args); /// /// 接收并处理GamingMessage @@ -162,6 +163,20 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon return true; } + /// + /// 给所有客户端发送游戏结束通知 + /// + /// + public virtual async void SendEndGame(GamingObject obj) + { + GamingObjects.TryRemove(obj.Room.Roomid, out _); + await Send(obj.All.Values, SocketMessageType.EndGame, obj.Room, obj.Users); + foreach (IServerModel model in obj.All.Values) + { + model.NowGamingServer = null; + } + } + /// /// 给客户端发送局内消息 /// diff --git a/Library/Common/Addon/GamingObject.cs b/Library/Common/Addon/GamingObject.cs new file mode 100644 index 0000000..2067c83 --- /dev/null +++ b/Library/Common/Addon/GamingObject.cs @@ -0,0 +1,13 @@ +using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Interface.Base; + +namespace Milimoe.FunGame.Core.Library.Common.Addon +{ + public readonly struct GamingObject(Room room, List users, IServerModel roomMaster, Dictionary serverModels) + { + public Room Room { get; } = room; + public List Users { get; } = users; + public IServerModel RoomMaster { get; } = roomMaster; + public Dictionary All { get; } = serverModels; + } +}