FunGame-Core/Library/Common/Addon/GameModuleServer.cs
2026-04-09 01:03:09 +08:00

348 lines
12 KiB
C#

using System.Collections.Concurrent;
using Milimoe.FunGame.Core.Api.Utility;
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.Common.Event;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Library.Common.Addon
{
public abstract class GameModuleServer : IGameModuleServer
{
/// <summary>
/// 服务器模组的名称<para/>
/// 如果服务器模组配合一个相关联的模组使用,那么它们的 <see cref="GameModule.Name"/> 名称必须相同。
/// </summary>
public abstract string Name { get; }
/// <summary>
/// 模组描述
/// </summary>
public abstract string Description { get; }
/// <summary>
/// 模组版本
/// </summary>
public abstract string Version { get; }
/// <summary>
/// 模组作者
/// </summary>
public abstract string Author { get; }
/// <summary>
/// 默认地图
/// </summary>
public abstract string DefaultMap { get; }
/// <summary>
/// 模组的依赖集合
/// </summary>
public abstract GameModuleDepend GameModuleDepend { get; }
/// <summary>
/// 是否是匿名服务器
/// </summary>
public virtual bool IsAnonymous { get; set; } = false;
/// <summary>
/// 记录该模组的加载器
/// </summary>
public GameModuleLoader? ModuleLoader { get; set; } = null;
/// <summary>
/// 包含了一些常用方法的控制器
/// </summary>
public ServerAddonController<IGameModuleServer> Controller
{
get => _controller ?? throw new NotImplementedException();
internal set => _controller = value;
}
/// <summary>
/// base控制器
/// </summary>
BaseAddonController<IGameModuleServer> IAddonController<IGameModuleServer>.Controller
{
get => Controller;
set => _controller = (ServerAddonController<IGameModuleServer>?)value;
}
/// <summary>
/// 控制器内部变量
/// </summary>
private ServerAddonController<IGameModuleServer>? _controller;
/// <summary>
/// 此模组所有正在运行的游戏对象
/// </summary>
public ConcurrentDictionary<string, GamingObject> GamingObjects { get; } = [];
/// <summary>
/// 启动服务器监听 请在此处实现服务器逻辑。注意,此方法必须立即返回
/// </summary>
/// <param name="obj"></param>
/// <param name="args"></param>
/// <returns></returns>
public abstract bool StartServer(GamingObject obj, params object[] args);
/// <summary>
/// 接收并处理GamingMessage
/// </summary>
/// <param name="model">发送此消息的客户端</param>
/// <param name="type">消息类型</param>
/// <param name="data">消息参数</param>
/// <returns>底层会将字典中的数据发送给客户端</returns>
public abstract Task<Dictionary<string, object>> GamingMessageHandler(IServerModel model, GamingType type, Dictionary<string, object> data);
/// <summary>
/// 启动匿名服务器监听
/// </summary>
/// <param name="model"></param>
/// <param name="data"></param>
/// <returns></returns>
public virtual bool StartAnonymousServer(IServerModel model, Dictionary<string, object> data)
{
return true;
}
/// <summary>
/// 结束匿名服务器监听
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public virtual void CloseAnonymousServer(IServerModel model)
{
}
/// <summary>
/// 接收并处理匿名服务器监听消息<para/>
/// 此方法为可选实现,可以帮助 RESTful API 处理不需要验证的 WebSocket 请求
/// </summary>
/// <param name="model">发送此消息的客户端</param>
/// <param name="data">消息参数</param>
/// <returns>底层会将字典中的数据发送给客户端</returns>
public virtual async Task<Dictionary<string, object>> AnonymousGameServerHandler(IServerModel model, Dictionary<string, object> data)
{
await Task.Delay(1);
return [];
}
/// <summary>
/// 此方法为可选实现,接收并处理非标准 DataRequest 请求<para/>
/// 此方法效率可能低于匿名服务器<para/>
/// <para/>请使用 <see cref="NetworkUtility.JsonDeserializeFromDictionary{T}(Dictionary{string, object}, string)"/> 方法获取 <paramref name="data"/> 成员
/// </summary>
/// <param name="data"></param>
/// <param name="e"></param>
/// <returns>返回一个无需对成员序列化的字典</returns>
public virtual Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
return [];
}
/// <summary>
/// 加载标记
/// </summary>
private bool _isLoaded = false;
/// <summary>
/// 加载模组
/// </summary>
public bool Load(params object[] objs)
{
if (_isLoaded)
{
return false;
}
// BeforeLoad可以阻止加载此模组
if (BeforeLoad())
{
// 模组加载后,不允许再次加载此模组
_isLoaded = true;
}
return _isLoaded;
}
/// <summary>
/// 卸载模组
/// </summary>
/// <param name="objs"></param>
public void UnLoad(params object[] objs)
{
}
/// <summary>
/// 模组完全加载后需要做的事
/// </summary>
public virtual void AfterLoad(GameModuleLoader loader, params object[] args)
{
// override
}
/// <summary>
/// 允许返回false来阻止加载此模组
/// </summary>
/// <returns></returns>
protected virtual bool BeforeLoad()
{
return true;
}
/// <summary>
/// 给所有客户端发送游戏结束通知
/// </summary>
/// <param name="obj"></param>
public virtual async void SendEndGame(GamingObject obj)
{
obj.Running = false;
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;
}
}
/// <summary>
/// 获取玩家所在的游戏对象
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public GamingObject? GetGamingObjectOfUser(long id)
{
GamingObject? obj = GamingObjects.Values.FirstOrDefault(obj => obj.HasUser(id));
return obj;
}
/// <summary>
/// 获取玩家所在的房间
/// </summary>
/// <param name="id"></param>
/// <param name="obj"></param>
/// <returns></returns>
public Room GetRoomOfUser(long id, GamingObject? obj = null)
{
obj ??= GamingObjects.Values.FirstOrDefault(obj => obj.HasUser(id));
return obj?.Room ?? Room.Empty;
}
/// <summary>
/// 这是一个用于等待的通用辅助方法
/// </summary>
/// <param name="waitSeconds">等待时间(秒)</param>
/// <param name="waitSomething">等待的条件</param>
/// <param name="delay">检查间隔(毫秒)</param>
/// <param name="onTimeout">任务超时则...</param>
/// <param name="onCompleted">任务完成则...</param>
/// <returns></returns>
protected virtual async Task WaitForUsers(int waitSeconds, Func<Task<bool>> waitSomething, int delay, Func<Task> onTimeout, Func<Task> onCompleted)
{
using CancellationTokenSource cts = new(TimeSpan.FromSeconds(waitSeconds));
CancellationToken ct = cts.Token;
while (!ct.IsCancellationRequested)
{
try
{
if (await waitSomething())
{
await onCompleted();
return;
}
await Task.Delay(delay, ct);
}
catch (System.Exception e) when (e is not OperationCanceledException)
{
Controller.Error(e);
await onTimeout();
return;
}
}
// 异常和超时都走超时逻辑
await onTimeout();
}
/// <summary>
/// 给客户端发送局内消息
/// </summary>
/// <param name="clients"></param>
/// <param name="type"></param>
/// <param name="data"></param>
/// <returns>发送失败的客户端</returns>
protected virtual async Task<List<IServerModel>> SendGamingMessage(IEnumerable<IServerModel> clients, GamingType type, Dictionary<string, object> data)
{
// 发送局内消息
List<IServerModel> failedModels = [];
foreach (IServerModel s in clients)
{
try
{
await s.Send(SocketMessageType.Gaming, type, data);
}
catch (System.Exception e)
{
Controller.Error(e);
failedModels.Add(s);
}
}
return failedModels;
}
/// <summary>
/// 给客户端发送消息
/// </summary>
/// <param name="clients"></param>
/// <param name="type"></param>
/// <param name="args"></param>
/// <returns>发送失败的客户端</returns>
protected virtual async Task<List<IServerModel>> Send(IEnumerable<IServerModel> clients, SocketMessageType type, params object[] args)
{
// 发送消息
List<IServerModel> failedModels = [];
foreach (IServerModel s in clients)
{
try
{
await s.Send(type, args);
}
catch (System.Exception e)
{
Controller.Error(e);
failedModels.Add(s);
}
}
return failedModels;
}
/// <summary>
/// 给客户端发送匿名服务器消息
/// </summary>
/// <param name="clients"></param>
/// <param name="data"></param>
/// <returns>发送失败的客户端</returns>
protected virtual async Task<List<IServerModel>> SendAnonymousGameServerMessage(IEnumerable<IServerModel> clients, Dictionary<string, object> data)
{
List<IServerModel> failedModels = [];
foreach (IServerModel s in clients)
{
try
{
await s.Send(SocketMessageType.AnonymousGameServer, data);
}
catch (System.Exception e)
{
Controller.Error(e);
failedModels.Add(s);
}
}
return failedModels;
}
}
}