针对服务器端的新功能支持与改进 (#90)

* 添加SQLite模式

* 将Hashtable转为Dictionary<string, object>,因为它具有性能优势

* 添加GamingRequest用于区分Gaming

* 模组中AfterLoad方法现已移动至加载器完全加载完毕后触发

* 删除了服务器对GameModule的加载,现在只会加载GameModuleServer
This commit is contained in:
milimoe 2024-09-25 09:24:53 +08:00 committed by GitHub
parent 622dbeb765
commit 2de1e57e0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 566 additions and 364 deletions

View File

@ -1,5 +1,4 @@
using System.Collections;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.Exception;
@ -37,7 +36,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
}
set
{
AddRequestData(key, value);
if (value != null) AddRequestData(key, value);
}
}
@ -114,17 +113,15 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddRequestData(string key, object? value)
public void AddRequestData(string key, object value)
{
if (Worker != null)
{
if (Worker.RequestData.ContainsKey(key)) Worker.RequestData[key] = value;
else Worker.RequestData.Add(key, value);
if (!Worker.RequestData.TryAdd(key, value)) Worker.RequestData[key] = value;
}
else if (GamingWorker != null)
{
if (GamingWorker.RequestData.ContainsKey(key)) GamingWorker.RequestData[key] = value;
else GamingWorker.RequestData.Add(key, value);
if (!GamingWorker.RequestData.TryAdd(key, value)) GamingWorker.RequestData[key] = value;
}
}
@ -142,7 +139,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
/// <para/>警告:<see cref="HTTPClient"/> 调用此方法将抛出异常。请调用并等待 <see cref="SendRequestAsync"/>
/// </summary>
/// <returns></returns>
/// <exception cref="AsyncRequestException"></exception>
/// <exception cref="AsyncSendException"></exception>
public RequestResult SendRequest()
{
Worker?.SendRequest();
@ -177,11 +174,11 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
{
if (Worker != null)
{
return GetHashtableJsonObject<T>(Worker.ResultData, key);
return GetDictionaryJsonObject<T>(Worker.ResultData, key);
}
else if (GamingWorker != null)
{
return GetHashtableJsonObject<T>(GamingWorker.ResultData, key);
return GetDictionaryJsonObject<T>(GamingWorker.ResultData, key);
}
return default;
}
@ -191,8 +188,8 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
/// </summary>
private class SocketRequest : SocketHandlerController
{
public Hashtable RequestData { get; } = [];
public Hashtable ResultData => _ResultData;
public Dictionary<string, object> RequestData { get; } = [];
public Dictionary<string, object> ResultData => _ResultData;
public RequestResult Result => _Result;
public string Error => _Error;
@ -202,7 +199,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
private readonly Guid RequestID = Guid.Empty;
private readonly bool IsLongRunning = false;
private readonly SocketRuntimeType RuntimeType = SocketRuntimeType.Client;
private Hashtable _ResultData = [];
private Dictionary<string, object> _ResultData = [];
private RequestResult _Result = RequestResult.Missing;
private string _Error = "";
@ -229,7 +226,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
try
{
SetWorking();
if (RuntimeType == SocketRuntimeType.Addon || RuntimeType == SocketRuntimeType.Addon)
if (RuntimeType == SocketRuntimeType.Addon)
{
if (RequestData.ContainsKey(SocketSet.Plugins_Mark)) RequestData[SocketSet.Plugins_Mark] = "true";
else RequestData.Add(SocketSet.Plugins_Mark, true);
@ -241,7 +238,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
}
else if (WebSocket != null)
{
throw new AsyncRequestException();
throw new AsyncSendException();
}
else throw new ConnectFailedException();
}
@ -258,7 +255,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
try
{
SetWorking();
if (RuntimeType == SocketRuntimeType.Addon || RuntimeType == SocketRuntimeType.Addon)
if (RuntimeType == SocketRuntimeType.Addon)
{
if (RequestData.ContainsKey(SocketSet.Plugins_Mark)) RequestData[SocketSet.Plugins_Mark] = "true";
else RequestData.Add(SocketSet.Plugins_Mark, true);
@ -295,7 +292,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
if (!IsLongRunning) Dispose();
Work = SocketObject;
Working = false;
_ResultData = SocketObject.GetParam<Hashtable>(2) ?? [];
_ResultData = SocketObject.GetParam<Dictionary<string, object>>(2) ?? [];
_Result = RequestResult.Success;
}
}
@ -314,8 +311,8 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
/// </summary>
private class GamingRequest : SocketHandlerController
{
public Hashtable RequestData { get; } = [];
public Hashtable ResultData => _ResultData;
public Dictionary<string, object> RequestData { get; } = [];
public Dictionary<string, object> ResultData => _ResultData;
public RequestResult Result => _Result;
public string Error => _Error;
@ -325,7 +322,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
private readonly Guid RequestID = Guid.Empty;
private readonly bool IsLongRunning = false;
private readonly SocketRuntimeType RuntimeType = SocketRuntimeType.Client;
private Hashtable _ResultData = [];
private Dictionary<string, object> _ResultData = [];
private RequestResult _Result = RequestResult.Missing;
private string _Error = "";
@ -352,19 +349,19 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
try
{
SetWorking();
if (RuntimeType == SocketRuntimeType.Addon || RuntimeType == SocketRuntimeType.Addon)
if (RuntimeType == SocketRuntimeType.Addon)
{
if (RequestData.ContainsKey(SocketSet.Plugins_Mark)) RequestData[SocketSet.Plugins_Mark] = "true";
else RequestData.Add(SocketSet.Plugins_Mark, true);
}
else RequestData.Remove(SocketSet.Plugins_Mark);
if (Socket != null && Socket.Send(SocketMessageType.DataRequest, GamingType, RequestID, RequestData) == SocketResult.Success)
if (Socket != null && Socket.Send(SocketMessageType.GamingRequest, GamingType, RequestID, RequestData) == SocketResult.Success)
{
WaitForWorkDone();
}
else if (WebSocket != null)
{
throw new AsyncRequestException();
throw new AsyncSendException();
}
else throw new ConnectFailedException();
}
@ -381,17 +378,17 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
try
{
SetWorking();
if (RuntimeType == SocketRuntimeType.Addon || RuntimeType == SocketRuntimeType.Addon)
if (RuntimeType == SocketRuntimeType.Addon)
{
if (RequestData.ContainsKey(SocketSet.Plugins_Mark)) RequestData[SocketSet.Plugins_Mark] = "true";
else RequestData.Add(SocketSet.Plugins_Mark, true);
}
else RequestData.Remove(SocketSet.Plugins_Mark);
if (Socket != null && Socket.Send(SocketMessageType.DataRequest, GamingType, RequestID, RequestData) == SocketResult.Success)
if (Socket != null && Socket.Send(SocketMessageType.GamingRequest, GamingType, RequestID, RequestData) == SocketResult.Success)
{
await WaitForWorkDoneAsync();
}
else if (WebSocket != null && await WebSocket.Send(SocketMessageType.DataRequest, GamingType, RequestID, RequestData) == SocketResult.Success)
else if (WebSocket != null && await WebSocket.Send(SocketMessageType.GamingRequest, GamingType, RequestID, RequestData) == SocketResult.Success)
{
await WaitForWorkDoneAsync();
}
@ -409,7 +406,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
{
try
{
if (SocketObject.SocketType == SocketMessageType.DataRequest)
if (SocketObject.SocketType == SocketMessageType.GamingRequest)
{
GamingType type = SocketObject.GetParam<GamingType>(0);
Guid id = SocketObject.GetParam<Guid>(1);
@ -418,7 +415,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
if (!IsLongRunning) Dispose();
Work = SocketObject;
Working = false;
_ResultData = SocketObject.GetParam<Hashtable>(2) ?? [];
_ResultData = SocketObject.GetParam<Dictionary<string, object>>(2) ?? [];
_Result = RequestResult.Success;
}
}
@ -433,15 +430,15 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
}
/// <summary>
/// 反序列化Hashtable中的Json对象
/// 反序列化Dictionary中的Json对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="hashtable"></param>
/// <param name="dict"></param>
/// <param name="key"></param>
/// <returns></returns>
public static T? GetHashtableJsonObject<T>(Hashtable hashtable, string key)
public static T? GetDictionaryJsonObject<T>(Dictionary<string, object> dict, string key)
{
return Service.JsonManager.GetObject<T>(hashtable, key);
return Service.JsonManager.GetObject<T>(dict, key);
}
}
}

View File

@ -11,6 +11,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal
public abstract class SQLHelper : ISQLHelper
{
public abstract FunGameInfo.FunGame FunGameType { get; }
public abstract SQLMode Mode { get; }
public abstract string Script { get; set; }
public abstract CommandType CommandType { get; set; }
public abstract SQLResult Result { get; }

View File

@ -17,7 +17,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// 当前的行动顺序
/// </summary>
public List<Character> Queue => _queue;
/// <summary>
/// 当前已死亡的角色顺序(第一个是最早死的)
/// </summary>
@ -342,7 +342,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
// 技能列表
List<Skill> skills = [.. character.Skills.Where(s => s.Level > 0 && s.SkillType != SkillType.Passive && s.Enable && !s.IsInEffect && s.CurrentCD == 0 &&
(((s.SkillType == SkillType.SuperSkill || s.SkillType == SkillType.Skill) && s.RealEPCost <= character.EP) || (s.SkillType == SkillType.Magic && s.RealMPCost <= character.MP)))];
// 物品列表
List<Item> items = [.. character.Items.Where(i => i.IsActive && i.Skills.Active != null && i.Enable && i.IsInGameItem &&
i.Skills.Active.SkillType == SkillType.Item && i.Skills.Active.Enable && !i.Skills.Active.IsInEffect && i.Skills.Active.CurrentCD == 0 && i.Skills.Active.RealMPCost <= character.MP && i.Skills.Active.RealEPCost <= character.EP)];
@ -1260,7 +1260,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
{
return CharacterActionType.PreCastSkill;
}
if (rand < pUseItem + pCastSkill + pNormalAttack)
{
return CharacterActionType.NormalAttack;

View File

@ -15,7 +15,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary>
/// 适用于服务器的模组集
/// </summary>
public Dictionary<string, GameModuleServer> ServerModules { get; } = [];
public Dictionary<string, GameModuleServer> ModuleServers { get; } = [];
/// <summary>
/// 游戏地图集
@ -37,11 +37,6 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// </summary>
public Dictionary<string, ItemModule> Items { get; } = [];
/// <summary>
/// 客户端模组与服务器模组的关联字典
/// </summary>
public Dictionary<GameModule, GameModuleServer?> AssociatedServers { get; } = [];
/// <summary>
/// 已加载的模组DLL名称对应的路径
/// </summary>
@ -52,7 +47,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary>
/// 传入 <see cref="FunGameInfo.FunGame"/> 类型来创建指定端的模组读取器
/// <para>runtime = <see cref="FunGameInfo.FunGame.FunGame_Desktop"/> 时,仅读取 <seealso cref="Modules"/></para>
/// <para>runtime = <see cref="FunGameInfo.FunGame.FunGame_Server"/> 时,都会读取,并且生成关联字典 <see cref="AssociatedServers"/></para>
/// <para>runtime = <see cref="FunGameInfo.FunGame.FunGame_Server"/> 时,仅读取 <seealso cref="ModuleServers"/></para>
/// <seealso cref="Maps"/> 都会读取
/// </summary>
/// <param name="runtime">传入 <see cref="FunGameInfo.FunGame"/> 类型来创建指定端的模组读取器</param>
@ -66,20 +61,23 @@ namespace Milimoe.FunGame.Core.Api.Utility
{
AddonManager.LoadGameModules(loader.Modules, loader.Characters, loader.Skills, loader.Items, delegates, otherobjs);
AddonManager.LoadGameMaps(loader.Maps, otherobjs);
foreach (GameModule module in loader.Modules.Values)
{
// 读取模组的依赖集合
module.GameModuleDepend.GetDependencies(loader);
// 如果模组加载后需要执行代码请重写AfterLoad方法
module.AfterLoad(runtime, loader);
}
}
else if (runtime == FunGameInfo.FunGame.FunGame_Server)
{
AddonManager.LoadGameModulesForServer(loader.Modules, loader.ServerModules, loader.Characters, loader.Skills, loader.Items, delegates, otherobjs);
foreach (GameModule module in loader.Modules.Values)
{
// AssociatedServerModuleName 已经存包含 IsConnectToOtherServerModule 的判断,因此无需重复判断
if (loader.ServerModules.TryGetValue(module.AssociatedServerModuleName, out GameModuleServer? server) && server != null)
{
loader.AssociatedServers.Add(module, server);
}
else loader.AssociatedServers.Add(module, null); // 服务器获取GameModuleServer时需要判断是否存在模组。
}
AddonManager.LoadGameModulesForServer(loader.ModuleServers, loader.Characters, loader.Skills, loader.Items, delegates, otherobjs);
AddonManager.LoadGameMaps(loader.Maps, otherobjs);
foreach (GameModuleServer server in loader.ModuleServers.Values)
{
server.GameModuleDepend.GetDependencies(loader);
server.AfterLoad(loader);
}
}
return loader;
}
@ -109,7 +107,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns>
public GameModuleServer GetServerMode(string name)
{
return ServerModules[name];
return ModuleServers[name];
}
/// <summary>

View File

@ -217,6 +217,15 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns>
public static T? JsonDeserializeFromHashtable<T>(Hashtable hashtable, string key) => Service.JsonManager.GetObject<T>(hashtable, key);
/// <summary>
/// 反序列化Dictionary中的Json对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dict"></param>
/// <param name="key"></param>
/// <returns></returns>
public static T? JsonDeserializeFromDictionary<T>(Dictionary<string, object> dict, string key) => Service.JsonManager.GetObject<T>(dict, key);
/// <summary>
/// 反序列化IEnumerable中的Json对象
/// </summary>

View File

@ -75,6 +75,15 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns>
public T? GetObject<T>(Hashtable table, string key) => JsonManager.GetObject<T>(table, key, options);
/// <summary>
/// 反序列化Dictionary中Key对应的Json对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dict"></param>
/// <param name="key"></param>
/// <returns></returns>
public T? GetObject<T>(Dictionary<string, object> dict, string key) => JsonManager.GetObject<T>(dict, key, options);
/// <summary>
/// 反序列化IEnumerable中的Json对象 可指定反序列化选项
/// </summary>
@ -100,7 +109,8 @@ namespace Milimoe.FunGame.Core.Api.Utility
{
WriteIndented = true,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
Converters = { new DateTimeConverter(), new DataTableConverter(), new DataSetConverter(), new UserConverter(), new RoomConverter() }
Converters = { new DateTimeConverter(), new DataTableConverter(), new DataSetConverter(), new UserConverter(), new RoomConverter(),
new CharacterConverter(), new MagicResistanceConverter(), new EquipSlotConverter(), new SkillConverter(), new EffectConverter(), new ItemConverter() }
};
}
}

View File

@ -105,11 +105,17 @@ namespace Milimoe.FunGame.Core.Api.Utility
/**
* MySQL
*/
WriteINI("MySQL", "UseMySQL", "false");
WriteINI("MySQL", "DBServer", "localhost");
WriteINI("MySQL", "DBPort", "3306");
WriteINI("MySQL", "DBName", "fungame");
WriteINI("MySQL", "DBUser", "root");
WriteINI("MySQL", "DBPassword", "pass");
/**
* SQLite
*/
WriteINI("SQLite", "UseSQLite", "true");
WriteINI("SQLite", "DataSource", "FunGameDB");
/**
* Mailer
*/

View File

@ -1006,7 +1006,7 @@ namespace Milimoe.FunGame.Core.Entity
}
return str;
}
/// <summary>
/// 获取角色实例的名字、昵称
/// </summary>
@ -1122,7 +1122,7 @@ namespace Milimoe.FunGame.Core.Entity
builder.Append(skill.ToString());
}
}
if (EquipSlot.Any())
{
builder.AppendLine("== 装备栏 ==");

View File

@ -20,7 +20,7 @@ namespace Milimoe.FunGame.Core.Entity
/// 物品的通用描述
/// </summary>
public virtual string GeneralDescription { get; set; } = "";
/// <summary>
/// 物品的背景故事
/// </summary>
@ -45,7 +45,7 @@ namespace Milimoe.FunGame.Core.Entity
/// 装备槽位
/// </summary>
public virtual EquipSlotType EquipSlotType { get; set; } = EquipSlotType.None;
/// <summary>
/// 武器类型(如果是武器)
/// </summary>
@ -65,7 +65,7 @@ namespace Milimoe.FunGame.Core.Entity
/// 是否可用(涉及冷却和禁用等)
/// </summary>
public bool Enable { get; set; } = true;
/// <summary>
/// 是否是局内使用的物品(局内是指对角色生效的物品)
/// </summary>
@ -95,7 +95,7 @@ namespace Milimoe.FunGame.Core.Entity
/// 是否允许交易
/// </summary>
public bool IsTradable { get; set; } = true;
/// <summary>
/// 下次可交易的时间
/// </summary>
@ -160,7 +160,7 @@ namespace Milimoe.FunGame.Core.Entity
}
if (Character != null) OnItemEquipped(Character, this, type);
}
/// <summary>
/// 当取消装备物品时
/// </summary>
@ -236,7 +236,7 @@ namespace Milimoe.FunGame.Core.Entity
{
}
/// <summary>
/// 当物品被玩家使用时
/// </summary>

View File

@ -19,7 +19,7 @@ namespace Milimoe.FunGame.Core.Entity
/// 技能描述
/// </summary>
public virtual string Description { get; set; } = "";
/// <summary>
/// 技能的通用描述
/// </summary>
@ -145,7 +145,7 @@ namespace Milimoe.FunGame.Core.Entity
/// 游戏中的行动顺序表实例,在技能效果被触发时,此实例会获得赋值,使用时需要判断其是否存在
/// </summary>
public ActionQueue? ActionQueue { get; set; } = null;
/// <summary>
/// 技能是否属于某个物品
/// </summary>
@ -302,7 +302,7 @@ namespace Milimoe.FunGame.Core.Entity
{
return Id + "." + Name;
}
/// <summary>
/// 判断两个技能是否相同 检查Id.Name
/// </summary>

View File

@ -21,12 +21,12 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>embedded</DebugType>
<NoWarn>1701;1702;CS1591;CS1587</NoWarn>
<NoWarn>1701;1702;CS1591;CS1587;IDE0130</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>embedded</DebugType>
<NoWarn>1701;1702;CS1591;CS1587</NoWarn>
<NoWarn>1701;1702;CS1591;CS1587;IDE0130</NoWarn>
</PropertyGroup>
</Project>

View File

@ -6,7 +6,8 @@ namespace Milimoe.FunGame.Core.Interface.Addons
IGamingRandomEventHandler, IGamingRoundEventHandler, IGamingLevelUpEventHandler, IGamingMoveEventHandler, IGamingAttackEventHandler, IGamingSkillEventHandler, IGamingItemEventHandler, IGamingMagicEventHandler,
IGamingBuyEventHandler, IGamingSuperSkillEventHandler, IGamingPauseEventHandler, IGamingUnpauseEventHandler, IGamingSurrenderEventHandler, IGamingUpdateInfoEventHandler, IGamingPunishEventHandler, IGameModuleDepend
{
public abstract void StartGame(Gaming instance, params object[] args);
public abstract void StartUI(params object[] args);
public bool HideMain { get; }
public void StartGame(Gaming instance, params object[] args);
public void StartUI(params object[] args);
}
}

View File

@ -1,5 +1,4 @@
using System.Collections;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Constant;
@ -9,6 +8,6 @@ namespace Milimoe.FunGame.Core.Interface.Addons
{
public bool StartServer(string GameModule, Room Room, List<User> Users, IServerModel RoomMasterServerModel, Dictionary<string, IServerModel> ServerModels, params object[] args);
public Hashtable GamingMessageHandler(string username, GamingType type, Hashtable data);
public Dictionary<string, object> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data);
}
}

View File

@ -6,10 +6,6 @@ namespace Milimoe.FunGame.Core.Interface.Base
{
public SocketRuntimeType Runtime { get; }
public Guid Token { get; }
public string ServerAddress { get; }
public int ServerPort { get; }
public string ServerName { get; }
public string ServerNotice { get; }
public void Close();
}
}

View File

@ -1,4 +1,5 @@
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant;
@ -14,7 +15,7 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <summary>
/// 客户端的套接字实例
/// </summary>
public abstract ClientSocket? Socket { get; }
public abstract ISocketMessageProcessor? Socket { get; }
/// <summary>
/// 客户端的用户实例,在用户登录后有效
@ -31,6 +32,11 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// </summary>
public bool IsDebugMode { get; }
/// <summary>
/// 客户端的游戏模组服务器
/// </summary>
public GameModuleServer? NowGamingServer { get; set; }
/// <summary>
/// 向客户端发送消息
/// </summary>
@ -38,7 +44,7 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="type"></param>
/// <param name="objs"></param>
/// <returns></returns>
public bool Send(ClientSocket socket, SocketMessageType type, params object[] objs);
public bool Send(ISocketMessageProcessor socket, SocketMessageType type, params object[] objs);
/// <summary>
/// 向客户端发送系统消息
@ -58,15 +64,15 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <summary>
/// 开始接收客户端消息
/// <para>请勿在 <see cref="Library.Common.Addon.GameModuleServer"/> 中调用此方法</para>
/// <para>请勿在 <see cref="GameModuleServer"/> 中调用此方法</para>
/// </summary>
/// <param name="socket"></param>
/// <returns></returns>
public bool Read(ClientSocket socket);
public bool Read(ISocketMessageProcessor socket);
/// <summary>
/// 启动对客户端的监听
/// <para>请勿在 <see cref="Library.Common.Addon.GameModuleServer"/> 中调用此方法</para>
/// <para>请勿在 <see cref="GameModuleServer"/> 中调用此方法</para>
/// </summary>
public void Start();
}

View File

@ -0,0 +1,15 @@
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Interface.Base
{
public interface ISocketMessageProcessor
{
public Type InstanceType { get; }
public string ClientIP { get; }
public string ClientName { get; }
public SocketResult Send(SocketMessageType type, params object[] objs);
public Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs);
public void Close();
}
}

View File

@ -0,0 +1,10 @@
using Milimoe.FunGame.Core.Interface.Base;
namespace Milimoe.FunGame.Core.Interface.Sockets
{
public interface IWebSocket : IBaseSocket
{
public System.Net.WebSockets.WebSocket Instance { get; }
public bool Connected => Instance != null && Instance.State == System.Net.WebSockets.WebSocketState.Open;
}
}

View File

@ -1,5 +1,4 @@
using System.Collections;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Event;
namespace Milimoe.FunGame.Core.Interface
@ -9,146 +8,146 @@ namespace Milimoe.FunGame.Core.Interface
/// </summary>
public interface IGamingEventHandler
{
public delegate void GamingEventHandler(object sender, GamingEventArgs e, Hashtable data);
public delegate void GamingEventHandler(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingConnectEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingConnect;
public void OnGamingConnectEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingConnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingDisconnectEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingDisconnect;
public void OnGamingDisconnectEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingDisconnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingReconnectEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingReconnect;
public void OnGamingReconnectEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingReconnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingBanCharacterEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingBanCharacter;
public void OnGamingBanCharacterEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingBanCharacterEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingPickCharacterEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingPickCharacter;
public void OnGamingPickCharacterEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingPickCharacterEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingRandomEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingRandom;
public void OnGamingRandomEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingRandomEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingRoundEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingRound;
public void OnGamingRoundEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingRoundEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingLevelUpEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingLevelUp;
public void OnGamingLevelUpEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingLevelUpEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingMoveEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingMove;
public void OnGamingMoveEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingMoveEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingAttackEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingAttack;
public void OnGamingAttackEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingAttackEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingSkillEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingSkill;
public void OnGamingSkillEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingSkillEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingItemEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingItem;
public void OnGamingItemEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingItemEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingMagicEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingMagic;
public void OnGamingMagicEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingMagicEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingBuyEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingBuy;
public void OnGamingBuyEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingBuyEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingSuperSkillEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingSuperSkill;
public void OnGamingSuperSkillEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingSuperSkillEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingPauseEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingPause;
public void OnGamingPauseEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingPauseEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingUnpauseEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingUnpause;
public void OnGamingUnpauseEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingUnpauseEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingSurrenderEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingSurrender;
public void OnGamingSurrenderEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingSurrenderEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingUpdateInfoEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingUpdateInfo;
public void OnGamingUpdateInfoEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingUpdateInfoEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingPunishEventHandler : IGamingEventHandler
{
public event GamingEventHandler? GamingPunish;
public void OnGamingPunishEvent(object sender, GamingEventArgs e, Hashtable data);
public void OnGamingPunishEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
}

View File

@ -1,106 +1,105 @@
using System.Collections;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Common.Event;
// 模组需要实现什么事件就继承什么接口
namespace Milimoe.FunGame.Core.Interface
{
public interface IGamingConnectEvent
{
public void GamingConnectEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingConnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingDisconnectEvent
{
public void GamingDisconnectEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingDisconnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingReconnectEvent
{
public void GamingReconnectEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingReconnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingBanCharacterEvent
{
public void GamingBanCharacterEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingBanCharacterEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingPickCharacterEvent
{
public void GamingPickCharacterEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingPickCharacterEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingRandomEvent
{
public void GamingRandomEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingRandomEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingRoundEvent
{
public void GamingRoundEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingRoundEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingLevelUpEvent
{
public void GamingLevelUpEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingLevelUpEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingMoveEvent
{
public void GamingMoveEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingMoveEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingAttackEvent
{
public void GamingAttackEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingAttackEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingSkillEvent
{
public void GamingSkillEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingSkillEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingItemEvent
{
public void GamingItemEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingItemEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingMagicEvent
{
public void GamingMagicEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingMagicEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingBuyEvent
{
public void GamingBuyEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingBuyEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingSuperSkillEvent
{
public void GamingSuperSkillEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingSuperSkillEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingPauseEvent
{
public void GamingPauseEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingPauseEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingUnpauseEvent
{
public void GamingUnpauseEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingUnpauseEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingSurrenderEvent
{
public void GamingSurrenderEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingSurrenderEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingUpdateInfoEvent
{
public void GamingUpdateInfoEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingUpdateInfoEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
public interface IGamingPunishEvent
{
public void GamingPunishEvent(object sender, GamingEventArgs e, Hashtable data);
public void GamingPunishEvent(object sender, GamingEventArgs e, Dictionary<string, object> data);
}
}

View File

@ -1,4 +1,3 @@
using System.Collections;
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
@ -49,6 +48,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
public override RoomType RoomType => RoomType.Mix;
public override bool HideMain => false;
public ExampleGameModule()
{
// 构造函数中可以指定模组连接到哪个模组服务器。
@ -78,16 +79,16 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
// 如果没有,则不需要重写此方法
}
public void GamingUpdateInfoEvent(object sender, GamingEventArgs e, Hashtable data)
public void GamingUpdateInfoEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
// 在下方的Server示例中服务器发来的data中包含check字符串因此客户端要主动发起确认连接的请求。
if (data.ContainsKey("info_type"))
{
// 反序列化得到指定key的值
string info_type = DataRequest.GetHashtableJsonObject<string>(data, "info_type") ?? "";
string info_type = DataRequest.GetDictionaryJsonObject<string>(data, "info_type") ?? "";
if (info_type == "check")
{
Guid token = DataRequest.GetHashtableJsonObject<Guid>(data, "connect_token");
Guid token = DataRequest.GetDictionaryJsonObject<Guid>(data, "connect_token");
// 发起连接确认请求
DataRequest request = Controller.NewDataRequest(GamingType.Connect);
// 传递参数
@ -143,7 +144,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
}
private readonly List<User> ConnectedUser = [];
private readonly Dictionary<string, Hashtable> UserData = [];
private readonly Dictionary<string, Dictionary<string, object>> UserData = [];
private async Task Test()
{
@ -154,7 +155,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
// 在FunGame项目中建议永远使用客户端主动发起请求因为服务器主动发起的实现难度较高
// 下面的演示基于综合的两种情况:服务器主动发送通知,客户端收到后,发起确认
// UpdateInfo是一个灵活的类型。如果发送check字符串意味着服务器要求客户端发送确认
Hashtable data = [];
Dictionary<string, object> data = [];
data.Add("info_type", "check");
// 进阶示例传递一个token让客户端返回
@ -164,7 +165,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
// 我们保存到字典UserData中这样可以方便跨方法检查变量
foreach (string username in Users.Select(u => u.Username).Distinct())
{
if (UserData.TryGetValue(username, out Hashtable? value))
if (UserData.TryGetValue(username, out Dictionary<string, object>? value))
{
value.Add("connect_token", token);
}
@ -174,7 +175,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
UserData[username].Add("connect_token", token);
}
}
SendAllGamingMessage(GamingType.UpdateInfo, data);
SendGamingMessage(All.Values, GamingType.UpdateInfo, data);
// 新建一个线程等待所有玩家确认
while (true)
@ -186,18 +187,18 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
Controller.WriteLine("所有玩家都已经连接。");
}
public override Hashtable GamingMessageHandler(string username, GamingType type, Hashtable data)
public override Dictionary<string, object> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data)
{
Hashtable result = [];
Dictionary<string, object> result = [];
switch (type)
{
case GamingType.Connect:
// 编写处理“连接”命令的逻辑
// 如果需要处理客户端传递的参数获取与客户端约定好的参数key对应的值
string un = NetworkUtility.JsonDeserializeFromHashtable<string>(data, "username") ?? "";
Guid token = NetworkUtility.JsonDeserializeFromHashtable<Guid>(data, "connect_token");
if (un == username && UserData.TryGetValue(username, out Hashtable? value) && value != null && (value["connect_token"]?.Equals(token) ?? false))
string un = NetworkUtility.JsonDeserializeFromDictionary<string>(data, "username") ?? "";
Guid token = NetworkUtility.JsonDeserializeFromDictionary<Guid>(data, "connect_token");
if (un == username && UserData.TryGetValue(username, out Dictionary<string, object>? value) && value != null && (value["connect_token"]?.Equals(token) ?? false))
{
ConnectedUser.Add(Users.Where(u => u.Username == username).First());
Controller.WriteLine(username + " 已经连接。");
@ -208,52 +209,6 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
return result;
}
// === 下面是一些常用的工具方法,用于服务器给客户端发送消息,可以直接添加到你的项目中 === //
protected void SendAllGamingMessage(GamingType type, Hashtable data)
{
// 循环服务线程,向所有玩家发送局内消息
foreach (IServerModel s in All.Values)
{
if (s != null && s.Socket != null)
{
s.Send(s.Socket, SocketMessageType.Gaming, type, data);
}
}
}
protected void SendGamingMessage(string username, GamingType type, Hashtable data)
{
// 向指定玩家发送局内消息
IServerModel s = All[username];
if (s != null && s.Socket != null)
{
s.Send(s.Socket, SocketMessageType.Gaming, type, data);
}
}
protected void SendAll(SocketMessageType type, params object[] args)
{
// 循环服务线程,向所有玩家发送消息
foreach (IServerModel s in All.Values)
{
if (s != null && s.Socket != null)
{
s.Send(s.Socket, type, args);
}
}
}
protected void Send(string username, SocketMessageType type, params object[] args)
{
// 向指定玩家发送消息
IServerModel s = All[username];
if (s != null && s.Socket != null)
{
s.Send(s.Socket, type, args);
}
}
}
/// <summary>

View File

@ -1,4 +1,3 @@
using System.Collections;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons;
@ -45,6 +44,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// </summary>
public abstract RoomType RoomType { get; }
/// <summary>
/// 是否隐藏主界面
/// </summary>
public abstract bool HideMain { get; }
/// <summary>
/// 是否连接其他的服务器模组
/// </summary>
@ -116,7 +120,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
return false;
}
// BeforeLoad可以阻止加载此模组
if (BeforeLoad())
if (BeforeLoad(objs))
{
// 模组加载后,不允许再次加载此模组
IsLoaded = true;
@ -124,16 +128,14 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
Init(objs);
// 触发绑定事件
BindEvent();
// 如果加载后需要执行代码请重写AfterLoad方法
AfterLoad();
}
return IsLoaded;
}
/// <summary>
/// 模组加载后需要做的事
/// 模组完全加载后需要做的事
/// </summary>
protected virtual void AfterLoad()
public virtual void AfterLoad(params object[] args)
{
// override
}
@ -142,7 +144,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// 允许返回false来阻止加载此模组
/// </summary>
/// <returns></returns>
protected virtual bool BeforeLoad()
protected virtual bool BeforeLoad(params object[] objs)
{
return true;
}
@ -318,102 +320,102 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
public event IGamingEventHandler.GamingEventHandler? GamingUpdateInfo;
public event IGamingEventHandler.GamingEventHandler? GamingPunish;
public void OnGamingConnectEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingConnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingConnect?.Invoke(sender, e, data);
}
public void OnGamingDisconnectEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingDisconnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingDisconnect?.Invoke(sender, e, data);
}
public void OnGamingReconnectEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingReconnectEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingReconnect?.Invoke(sender, e, data);
}
public void OnGamingBanCharacterEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingBanCharacterEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingBanCharacter?.Invoke(sender, e, data);
}
public void OnGamingPickCharacterEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingPickCharacterEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingPickCharacter?.Invoke(sender, e, data);
}
public void OnGamingRandomEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingRandomEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingRandom?.Invoke(sender, e, data);
}
public void OnGamingRoundEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingRoundEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingRound?.Invoke(sender, e, data);
}
public void OnGamingLevelUpEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingLevelUpEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingLevelUp?.Invoke(sender, e, data);
}
public void OnGamingMoveEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingMoveEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingMove?.Invoke(sender, e, data);
}
public void OnGamingAttackEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingAttackEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingAttack?.Invoke(sender, e, data);
}
public void OnGamingSkillEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingSkillEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingSkill?.Invoke(sender, e, data);
}
public void OnGamingItemEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingItemEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingItem?.Invoke(sender, e, data);
}
public void OnGamingMagicEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingMagicEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingMagic?.Invoke(sender, e, data);
}
public void OnGamingBuyEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingBuyEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingBuy?.Invoke(sender, e, data);
}
public void OnGamingSuperSkillEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingSuperSkillEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingSuperSkill?.Invoke(sender, e, data);
}
public void OnGamingPauseEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingPauseEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingPause?.Invoke(sender, e, data);
}
public void OnGamingUnpauseEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingUnpauseEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingUnpause?.Invoke(sender, e, data);
}
public void OnGamingSurrenderEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingSurrenderEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingSurrender?.Invoke(sender, e, data);
}
public void OnGamingUpdateInfoEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingUpdateInfoEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingUpdateInfo?.Invoke(sender, e, data);
}
public void OnGamingPunishEvent(object sender, GamingEventArgs e, Hashtable data)
public void OnGamingPunishEvent(object sender, GamingEventArgs e, Dictionary<string, object> data)
{
GamingPunish?.Invoke(sender, e, data);
}

View File

@ -1,4 +1,3 @@
using System.Collections;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Addons;
@ -72,8 +71,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// <param name="username">发送此消息的账号</param>
/// <param name="type">消息类型</param>
/// <param name="data">消息参数</param>
/// <returns>底层会将哈希表中的数据发送给客户端</returns>
public abstract Hashtable GamingMessageHandler(string username, GamingType type, Hashtable data);
/// <returns>底层会将字典中的数据发送给客户端</returns>
public abstract Dictionary<string, object> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data);
/// <summary>
/// 加载标记
@ -94,16 +93,14 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
{
// 模组加载后,不允许再次加载此模组
IsLoaded = true;
// 如果加载后需要执行代码请重写AfterLoad方法
AfterLoad();
}
return IsLoaded;
}
/// <summary>
/// 模组加载后需要做的事
/// 模组完全加载后需要做的事
/// </summary>
protected virtual void AfterLoad()
public virtual void AfterLoad(params object[] args)
{
// override
}
@ -116,5 +113,41 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
{
return true;
}
/// <summary>
/// 给客户端发送局内消息
/// </summary>
/// <param name="clients"></param>
/// <param name="type"></param>
/// <param name="data"></param>
protected virtual void SendGamingMessage(IEnumerable<IServerModel> clients, GamingType type, Dictionary<string, object> data)
{
// 发送局内消息
foreach (IServerModel s in clients)
{
if (s != null && s.Socket != null)
{
s.Send(s.Socket, SocketMessageType.Gaming, type, data);
}
}
}
/// <summary>
/// 给客户端发送消息
/// </summary>
/// <param name="clients"></param>
/// <param name="type"></param>
/// <param name="args"></param>
protected virtual void Send(IEnumerable<IServerModel> clients, SocketMessageType type, params object[] args)
{
// 发送消息
foreach (IServerModel s in clients)
{
if (s != null && s.Socket != null)
{
s.Send(s.Socket, type, args);
}
}
}
}
}

View File

@ -17,6 +17,9 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
{
switch (propertyName)
{
case nameof(Character.Id):
result.Id = reader.GetInt64();
break;
case nameof(Character.Name):
result.Name = reader.GetString() ?? "";
break;
@ -190,6 +193,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
public override void Write(Utf8JsonWriter writer, Character value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteNumber(nameof(Character.Id), value.Id);
writer.WriteString(nameof(Character.Name), value.Name);
writer.WriteString(nameof(Character.FirstName), value.FirstName);
writer.WriteString(nameof(Character.NickName), value.NickName);

View File

@ -1,5 +1,4 @@
using System;
using System.Text.Json;
using System.Text.Json;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Architecture;

View File

@ -1,27 +1,25 @@
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class ClientSocket(System.Net.Sockets.Socket Instance, int ServerPort, string ClientIP, string ClientName, Guid Token) : IClientSocket
public class ClientSocket(System.Net.Sockets.Socket instance, string clientIP, string clientName, Guid token) : IClientSocket, ISocketMessageProcessor
{
public System.Net.Sockets.Socket Instance { get; } = Instance;
public System.Net.Sockets.Socket Instance { get; } = instance;
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Token;
public string ServerAddress { get; } = "";
public int ServerPort { get; } = ServerPort;
public string ServerName { get; } = "";
public string ServerNotice { get; } = "";
public string ClientIP { get; } = ClientIP;
public Guid Token { get; } = token;
public string ClientIP { get; } = clientIP;
public string ClientName => _ClientName;
public bool Connected => Instance != null && Instance.Connected;
public bool Receiving => _Receiving;
public Type InstanceType => typeof(ClientSocket);
private Task? ReceivingTask;
private bool _Receiving;
private readonly string _ClientName = ClientName;
private readonly string _ClientName = clientName;
public void Close()
{
@ -54,6 +52,19 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
return SocketResult.NotSent;
}
public async Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs)
{
if (Instance != null)
{
if (await SocketManager.SendAsync(Instance, new(type, Token, objs)) == SocketResult.Success)
{
return SocketResult.Success;
}
else return SocketResult.Fail;
}
return SocketResult.NotSent;
}
public void BindEvent(Delegate method, bool remove = false)
{
if (!remove)

View File

@ -0,0 +1,43 @@
using System.Net.WebSockets;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Interface.Sockets;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class ClientWebSocket(HTTPListener listener, WebSocket instance, string clientIP, string clientName, Guid token) : IWebSocket, ISocketMessageProcessor
{
public HTTPListener Listener => listener;
public WebSocket Instance => instance;
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token => token;
public string ClientIP => clientIP;
public string ClientName => clientName;
public Type InstanceType => typeof(ClientWebSocket);
public void Close()
{
TaskUtility.NewTask(async () =>
{
if (Instance.State == WebSocketState.Open)
{
// 安全关闭 WebSocket 连接
await Instance.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None);
Listener.ClientSockets.Remove(Token);
}
});
}
public SocketResult Send(SocketMessageType type, params object[] objs)
{
throw new AsyncSendException();
}
public async Task<SocketResult> SendAsync(SocketMessageType type, params object[] objs)
{
return await HTTPManager.Send(Instance, new(type, token, objs));
}
}
}

View File

@ -9,7 +9,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
{
public class HTTPClient : IHTTPClient
{
public ClientWebSocket? Instance { get; } = null;
public System.Net.WebSockets.ClientWebSocket? Instance { get; } = null;
public SocketRuntimeType Runtime => SocketRuntimeType.Client;
public Guid Token { get; } = Guid.Empty;
public string ServerAddress { get; } = "";
@ -20,7 +20,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
private bool _Listening = false;
private readonly HeartBeat HeartBeat;
private HTTPClient(ClientWebSocket Instance, string ServerAddress, int ServerPort, params object[] args)
private HTTPClient(System.Net.WebSockets.ClientWebSocket Instance, string ServerAddress, int ServerPort, params object[] args)
{
this.Instance = Instance;
this.ServerAddress = ServerAddress;
@ -34,7 +34,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
{
string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(ServerAddress);
Uri uri = new((SSL ? "wss://" : "ws://") + ServerIP + ":" + ServerPort + "/" + SubDirectory);
ClientWebSocket? socket = await HTTPManager.Connect(uri);
System.Net.WebSockets.ClientWebSocket? socket = await HTTPManager.Connect(uri);
if (socket != null && socket.State == WebSocketState.Open)
{
HTTPClient client = new(socket, ServerAddress, ServerPort, args);

View File

@ -1,5 +1,6 @@
using System.Net;
using System.Net.WebSockets;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Interface.HTTP;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
@ -11,24 +12,20 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public HttpListener Instance { get; }
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty;
public string ServerAddress { get; } = "";
public int ServerPort { get; } = 0;
public string ServerName { get; } = "";
public string ServerNotice { get; } = "";
public Dictionary<Guid, WebSocket> ClientSockets { get; } = [];
private HTTPListener(HttpListener Instance, int ServerPort)
private HTTPListener(HttpListener instance)
{
this.Instance = Instance;
this.ServerPort = ServerPort;
Instance = instance;
Token = Guid.NewGuid();
}
public static HTTPListener StartListening(int Port, bool SSL = false)
public static HTTPListener StartListening(int port, bool ssl = false)
{
HttpListener? socket = HTTPManager.StartListening(Port, SSL);
HttpListener? socket = HTTPManager.StartListening(port, ssl);
if (socket != null)
{
HTTPListener instance = new(socket, Port);
HTTPListener instance = new(socket);
Task t = Task.Run(async () => await HTTPManager.Receiving(instance));
return instance;
}
@ -55,7 +52,27 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public void Close()
{
Instance?.Close();
bool closing = true;
TaskUtility.NewTask(async () =>
{
// 关闭所有 WebSocket 连接
foreach (WebSocket socket in ClientSockets.Values)
{
if (socket.State == WebSocketState.Open)
{
// 安全关闭 WebSocket 连接
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None);
}
}
ClientSockets.Clear();
Instance?.Close();
closing = true;
});
while (closing)
{
if (!closing) break;
Thread.Sleep(100);
}
}
}
}

View File

@ -10,10 +10,6 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public System.Net.Sockets.Socket Instance { get; }
public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty;
public string ServerAddress { get; } = "";
public int ServerPort { get; } = 0;
public string ServerName { get; } = "";
public string ServerNotice { get; } = "";
public bool Connected => Instance != null && Instance.Connected;
public List<IServerModel> ClientList => OnlineClients.GetList();
public List<IServerModel> UserList => OnlineUsers.GetList();
@ -25,38 +21,38 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
private readonly ModelManager<IServerModel> OnlineClients;
private readonly ModelManager<IServerModel> OnlineUsers;
private ServerSocket(System.Net.Sockets.Socket Instance, int ServerPort, int MaxConnection = 0)
private ServerSocket(System.Net.Sockets.Socket instance, int maxConnection = 0)
{
this.Instance = Instance;
this.ServerPort = ServerPort;
if (MaxConnection <= 0)
Token = Guid.NewGuid();
Instance = instance;
if (maxConnection <= 0)
{
OnlineClients = [];
OnlineUsers = [];
}
else
{
OnlineClients = new(MaxConnection);
OnlineUsers = new(MaxConnection);
OnlineClients = new(maxConnection);
OnlineUsers = new(maxConnection);
}
}
public static ServerSocket StartListening(int Port = 22222, int MaxConnection = 0)
public static ServerSocket StartListening(int port = 22222, int maxConnection = 0)
{
if (MaxConnection <= 0) MaxConnection = SocketSet.MaxConnection_2C2G;
System.Net.Sockets.Socket? socket = SocketManager.StartListening(Port, MaxConnection);
if (socket != null) return new ServerSocket(socket, Port);
if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G;
System.Net.Sockets.Socket? socket = SocketManager.StartListening(port, maxConnection);
if (socket != null) return new ServerSocket(socket, port);
else throw new SocketCreateListenException();
}
public ClientSocket Accept(Guid Token)
public static ClientSocket Accept(Guid token)
{
object[] result = SocketManager.Accept();
if (result != null && result.Length == 2)
{
string ClientIP = (string)result[0];
System.Net.Sockets.Socket Client = (System.Net.Sockets.Socket)result[1];
return new ClientSocket(Client, ServerPort, ClientIP, ClientIP, Token);
string clientIP = (string)result[0];
System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)result[1];
return new ClientSocket(client, clientIP, clientIP, token);
}
throw new SocketGetClientException();
}

View File

@ -22,19 +22,19 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
private readonly HeartBeat HeartBeat;
private bool _Receiving = false;
private Socket(System.Net.Sockets.Socket Instance, string ServerAddress, int ServerPort)
private Socket(System.Net.Sockets.Socket instance, string serverAddress, int serverPort)
{
this.Instance = Instance;
this.ServerAddress = ServerAddress;
this.ServerPort = ServerPort;
this.Instance = instance;
this.ServerAddress = serverAddress;
this.ServerPort = serverPort;
HeartBeat = new(this);
HeartBeat.StartSendingHeartBeat();
}
public static Socket Connect(string Address, int Port = 22222)
public static Socket Connect(string address, int port = 22222)
{
System.Net.Sockets.Socket? socket = SocketManager.Connect(Address, Port);
if (socket != null) return new Socket(socket, Address, Port);
System.Net.Sockets.Socket? socket = SocketManager.Connect(address, port);
if (socket != null) return new Socket(socket, address, port);
else throw new ConnectFailedException();
}

View File

@ -37,6 +37,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
public const string Socket = "Socket";
public const string Unknown = "Unknown";
public const string DataRequest = "DataRequest";
public const string GamingRequest = "GamingRequest";
public const string Connect = "Connect";
public const string Disconnect = "Disconnect";
public const string System = "System";

View File

@ -44,7 +44,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
/// 默认的时间格式 yyyy-MM-dd HH:mm:ss.fff
/// </summary>
public static string GeneralDateTimeFormat => "yyyy-MM-dd HH:mm:ss.fff";
/// <summary>
/// yyyy年MM月dd日 HH:mm:ss
/// </summary>

View File

@ -66,6 +66,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
{
Unknown,
DataRequest,
GamingRequest,
Connect,
Disconnect,
System,
@ -217,7 +218,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
/// 无特殊效果
/// </summary>
None,
/// <summary>
/// 这是来自装备的特效
/// </summary>
@ -844,4 +845,11 @@ namespace Milimoe.FunGame.Core.Library.Constant
Plugin,
GameModule
}
public enum SQLMode
{
None,
MySQL,
SQLite
}
}

View File

@ -165,8 +165,13 @@
public override string Message => "试图在不支持的类中创建数据请求 (#10033)";
}
public class AsyncRequestException : Exception
public class AsyncSendException : Exception
{
public override string Message => "数据请求必须以异步方式发送 (#10034)";
public override string Message => "必须以异步方式发送数据 (#10034)";
}
public class SQLServiceException : Exception
{
public override string Message => "SQL服务遇到错误请检查SQL配置 (#10035)";
}
}

View File

@ -1,5 +1,4 @@
using System.Collections;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Event;
@ -51,10 +50,7 @@ namespace Milimoe.FunGame.Core.Model
// 读取模组的依赖集合
module.GameModuleDepend.GetDependencies(loader);
// 新建线程来启动模组的界面
TaskUtility.NewTask(() =>
{
module.StartUI();
});
TaskUtility.NewTask(() => module.StartUI());
// 启动模组主线程
module.StartGame(instance, args);
return instance;
@ -67,7 +63,7 @@ namespace Milimoe.FunGame.Core.Model
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="data">接收到的数据</param>
public void GamingHandler(GamingType type, Hashtable data)
public void GamingHandler(GamingType type, Dictionary<string, object> data)
{
switch (type)
{
@ -137,102 +133,102 @@ namespace Milimoe.FunGame.Core.Model
}
}
private void Connect(Hashtable data)
private void Connect(Dictionary<string, object> data)
{
GameModule.OnGamingConnectEvent(this, EventArgs, data);
}
private void Disconnect(Hashtable data)
private void Disconnect(Dictionary<string, object> data)
{
GameModule.OnGamingDisconnectEvent(this, EventArgs, data);
}
private void Reconnect(Hashtable data)
private void Reconnect(Dictionary<string, object> data)
{
GameModule.OnGamingReconnectEvent(this, EventArgs, data);
}
private void BanCharacter(Hashtable data)
private void BanCharacter(Dictionary<string, object> data)
{
GameModule.OnGamingBanCharacterEvent(this, EventArgs, data);
}
private void PickCharacter(Hashtable data)
private void PickCharacter(Dictionary<string, object> data)
{
GameModule.OnGamingPickCharacterEvent(this, EventArgs, data);
}
private void Random(Hashtable data)
private void Random(Dictionary<string, object> data)
{
GameModule.OnGamingRandomEvent(this, EventArgs, data);
}
private void Round(Hashtable data)
private void Round(Dictionary<string, object> data)
{
GameModule.OnGamingRoundEvent(this, EventArgs, data);
}
private void LevelUp(Hashtable data)
private void LevelUp(Dictionary<string, object> data)
{
GameModule.OnGamingLevelUpEvent(this, EventArgs, data);
}
private void Move(Hashtable data)
private void Move(Dictionary<string, object> data)
{
GameModule.OnGamingMoveEvent(this, EventArgs, data);
}
private void Attack(Hashtable data)
private void Attack(Dictionary<string, object> data)
{
GameModule.OnGamingAttackEvent(this, EventArgs, data);
}
private void Skill(Hashtable data)
private void Skill(Dictionary<string, object> data)
{
GameModule.OnGamingSkillEvent(this, EventArgs, data);
}
private void Item(Hashtable data)
private void Item(Dictionary<string, object> data)
{
GameModule.OnGamingItemEvent(this, EventArgs, data);
}
private void Magic(Hashtable data)
private void Magic(Dictionary<string, object> data)
{
GameModule.OnGamingMagicEvent(this, EventArgs, data);
}
private void Buy(Hashtable data)
private void Buy(Dictionary<string, object> data)
{
GameModule.OnGamingBuyEvent(this, EventArgs, data);
}
private void SuperSkill(Hashtable data)
private void SuperSkill(Dictionary<string, object> data)
{
GameModule.OnGamingSuperSkillEvent(this, EventArgs, data);
}
private void Pause(Hashtable data)
private void Pause(Dictionary<string, object> data)
{
GameModule.OnGamingPauseEvent(this, EventArgs, data);
}
private void Unpause(Hashtable data)
private void Unpause(Dictionary<string, object> data)
{
GameModule.OnGamingUnpauseEvent(this, EventArgs, data);
}
private void Surrender(Hashtable data)
private void Surrender(Dictionary<string, object> data)
{
GameModule.OnGamingSurrenderEvent(this, EventArgs, data);
}
private void UpdateInfo(Hashtable data)
private void UpdateInfo(Dictionary<string, object> data)
{
GameModule.OnGamingUpdateInfoEvent(this, EventArgs, data);
}
private void Punish(Hashtable data)
private void Punish(Dictionary<string, object> data)
{
GameModule.OnGamingPunishEvent(this, EventArgs, data);
}

View File

@ -118,7 +118,6 @@ namespace Milimoe.FunGame.Core.Service
/// <summary>
/// 从modules目录加载所有适用于服务器的模组
/// </summary>
/// <param name="modules"></param>
/// <param name="servers"></param>
/// <param name="characters"></param>
/// <param name="skills"></param>
@ -126,7 +125,7 @@ namespace Milimoe.FunGame.Core.Service
/// <param name="delegates"></param>
/// <param name="otherobjs"></param>
/// <returns></returns>
internal static Dictionary<string, GameModuleServer> LoadGameModulesForServer(Dictionary<string, GameModule> modules, Dictionary<string, GameModuleServer> servers, Dictionary<string, CharacterModule> characters, Dictionary<string, SkillModule> skills, Dictionary<string, ItemModule> items, Hashtable delegates, params object[] otherobjs)
internal static Dictionary<string, GameModuleServer> LoadGameModulesForServer(Dictionary<string, GameModuleServer> servers, Dictionary<string, CharacterModule> characters, Dictionary<string, SkillModule> skills, Dictionary<string, ItemModule> items, Hashtable delegates, params object[] otherobjs)
{
if (!Directory.Exists(ReflectionSet.GameModuleFolderPath)) return servers;
@ -140,19 +139,7 @@ namespace Milimoe.FunGame.Core.Service
{
bool isAdded = false;
if (type.IsSubclassOf(typeof(GameModule)))
{
isAdded = AddAddonInstances(type, modules, (instance) =>
{
if (instance.Load(otherobjs))
{
instance.Controller = new(instance, delegates);
return true;
}
return false;
});
}
else if (type.IsSubclassOf(typeof(GameModuleServer)))
if (type.IsSubclassOf(typeof(GameModuleServer)))
{
isAdded = AddAddonInstances(type, servers, (instance) =>
{

View File

@ -13,17 +13,17 @@ namespace Milimoe.FunGame.Core.Service
internal static HttpListener? ServerSocket => _ServerSocket;
private static HttpListener? _ServerSocket = null;
internal static HttpListener StartListening(int Port = 22227, bool SSL = false)
internal static HttpListener StartListening(int port = 22227, bool ssl = false)
{
HttpListener listener = new();
listener.Prefixes.Add((SSL ? "https://" : "http://") + "localhost:" + Port + "/");
listener.Prefixes.Add((ssl ? "https://" : "http://") + "localhost:" + port + "/ws");
listener.Start();
return listener;
}
internal static async Task<ClientWebSocket?> Connect(Uri uri)
internal static async Task<System.Net.WebSockets.ClientWebSocket?> Connect(Uri uri)
{
ClientWebSocket socket = new();
System.Net.WebSockets.ClientWebSocket socket = new();
await socket.ConnectAsync(uri, CancellationToken.None);
if (socket.State == WebSocketState.Open)
{
@ -32,7 +32,7 @@ namespace Milimoe.FunGame.Core.Service
return null;
}
internal static async Task<SocketResult> Send(ClientWebSocket socket, SocketObject obj)
internal static async Task<SocketResult> Send(System.Net.WebSockets.ClientWebSocket socket, SocketObject obj)
{
if (socket != null)
{
@ -79,7 +79,7 @@ namespace Milimoe.FunGame.Core.Service
HttpListenerContext context = await ServerSocket.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
await AddClientWebSocket(listener, context);
TaskUtility.NewTask(async () => await AddClientWebSocket(listener, context));
}
else
{
@ -154,12 +154,19 @@ namespace Milimoe.FunGame.Core.Service
listener.ClientSockets.TryAdd(token, socket);
await Send(socket, sendobject);
while (!result.CloseStatus.HasValue)
while (socket.State == WebSocketState.Open)
{
try
{
buffer = new byte[General.SocketByteSize];
result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.CloseStatus.HasValue)
{
await socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
return;
}
msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim();
objs = await GetSocketObjects(socket, result, msg);
foreach (SocketObject obj in objs)
@ -167,7 +174,7 @@ namespace Milimoe.FunGame.Core.Service
sendobject = listener.SocketObject_Handler(obj);
if (obj.SocketType == SocketMessageType.Disconnect)
{
await socket.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None);
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Disconnect received", CancellationToken.None);
return;
}
await Send(socket, sendobject);
@ -175,8 +182,10 @@ namespace Milimoe.FunGame.Core.Service
}
catch (Exception e)
{
// 处理其他异常
TXTHelper.AppendErrorLog(e.GetErrorInfo());
await socket.CloseAsync(WebSocketCloseStatus.InternalServerError, result.CloseStatusDescription, CancellationToken.None);
await socket.CloseAsync(WebSocketCloseStatus.InternalServerError, "Server Error", CancellationToken.None);
return;
}
}
}

View File

@ -138,6 +138,27 @@ namespace Milimoe.FunGame.Core.Service
return default;
}
/// <summary>
/// 反序列化Dictionary中Key对应的Json对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dict"></param>
/// <param name="key"></param>
/// <returns></returns>
internal static T? GetObject<T>(Dictionary<string, object> dict, string key)
{
if (dict.TryGetValue(key, out object? value))
{
JsonElement? element = (JsonElement?)value;
if (element != null)
{
T? result = ((JsonElement)element).Deserialize<T>(GeneralOptions);
return result;
}
}
return default;
}
/// <summary>
/// 反序列化IEnumerable中的Json对象
/// </summary>
@ -205,6 +226,28 @@ namespace Milimoe.FunGame.Core.Service
return default;
}
/// <summary>
/// 反序列化Dictionary中Key对应的Json对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dict"></param>
/// <param name="key"></param>
/// <param name="options"></param>
/// <returns></returns>
internal static T? GetObject<T>(Dictionary<string, object> dict, string key, JsonSerializerOptions options)
{
if (dict.TryGetValue(key, out object? value))
{
JsonElement? element = (JsonElement?)value;
if (element != null)
{
T? result = ((JsonElement)element).Deserialize<T>(options);
return result;
}
}
return default;
}
/// <summary>
/// 反序列化多个Json对象
/// 注意必须是相同的Json对象才可以使用此方法解析

View File

@ -1,6 +1,8 @@
using System.Net;
using System.Net.Sockets;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.Exception;
namespace Milimoe.FunGame.Core.Service
{
@ -145,6 +147,25 @@ namespace Milimoe.FunGame.Core.Service
return SocketResult.NotSent;
}
/// <summary>
/// 用于服务器端向客户端Socket发送信息 [ 异步版 ]
/// </summary>
/// <param name="ClientSocket">客户端Socket</param>
/// <param name="SocketObject">Socket信息容器</param>
/// <returns>通信结果</returns>
internal static async Task<SocketResult> SendAsync(Socket ClientSocket, Library.Common.Network.SocketObject SocketObject)
{
if (ClientSocket != null)
{
if (await ClientSocket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0)
{
return SocketResult.Success;
}
else return SocketResult.Fail;
}
return SocketResult.NotSent;
}
/// <summary>
/// 用于客户端向服务器Socket发送信息
/// </summary>
@ -163,6 +184,24 @@ namespace Milimoe.FunGame.Core.Service
return SocketResult.NotSent;
}
/// <summary>
/// 用于客户端向服务器Socket发送信息 [ 异步版 ]
/// </summary>
/// <param name="SocketObject">Socket信息容器</param>
/// <returns>通信结果</returns>
internal static async Task<SocketResult> SendAsync(Library.Common.Network.SocketObject SocketObject)
{
if (Socket != null)
{
if (await Socket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0)
{
return SocketResult.Success;
}
else return SocketResult.Fail;
}
return SocketResult.NotSent;
}
/// <summary>
/// 接收数据流中的信息
/// <para/>如果是服务器接收信息需要传入客户端Socket <paramref name="ClientSocket"/>
@ -171,54 +210,62 @@ namespace Milimoe.FunGame.Core.Service
/// <returns>SocketObjects</returns>
internal static Library.Common.Network.SocketObject[] Receive(Socket? ClientSocket = null)
{
List<Library.Common.Network.SocketObject> result = [];
Socket? tempSocket = ClientSocket is null ? Socket : ClientSocket;
if (tempSocket != null)
try
{
// 从服务器接收消息
byte[] buffer = new byte[General.SocketByteSize];
int length = tempSocket.Receive(buffer, buffer.Length, SocketFlags.None);
string msg = "";
if (length > 0)
List<Library.Common.Network.SocketObject> result = [];
Socket? tempSocket = ClientSocket is null ? Socket : ClientSocket;
if (tempSocket != null)
{
msg = General.DefaultEncoding.GetString(buffer, 0, length);
if (JsonManager.IsCompleteJson<Library.Common.Network.SocketObject>(msg))
// 从服务器接收消息
byte[] buffer = new byte[General.SocketByteSize];
int length = tempSocket.Receive(buffer, buffer.Length, SocketFlags.None);
string msg = "";
if (length > 0)
{
foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects<Library.Common.Network.SocketObject>(msg))
msg = General.DefaultEncoding.GetString(buffer, 0, length);
if (JsonManager.IsCompleteJson<Library.Common.Network.SocketObject>(msg))
{
result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel
if (ClientSocket is null) OnSocketReceive(obj);
}
return [.. result];
}
else
{
Thread.Sleep(20);
while (true)
{
if (tempSocket.Available > 0)
foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects<Library.Common.Network.SocketObject>(msg))
{
length = tempSocket.Receive(buffer, buffer.Length, SocketFlags.None);
msg += General.DefaultEncoding.GetString(buffer, 0, length);
if (JsonManager.IsCompleteJson<Library.Common.Network.SocketObject>(msg))
{
break;
}
Thread.Sleep(20);
result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel
if (ClientSocket is null) OnSocketReceive(obj);
}
return [.. result];
}
else
{
Thread.Sleep(20);
while (true)
{
if (tempSocket.Available > 0)
{
length = tempSocket.Receive(buffer, buffer.Length, SocketFlags.None);
msg += General.DefaultEncoding.GetString(buffer, 0, length);
if (JsonManager.IsCompleteJson<Library.Common.Network.SocketObject>(msg))
{
break;
}
Thread.Sleep(20);
}
else break;
}
else break;
}
}
foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects<Library.Common.Network.SocketObject>(msg))
{
result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel
if (ClientSocket is null) OnSocketReceive(obj);
}
}
foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects<Library.Common.Network.SocketObject>(msg))
{
result.Add(obj);
// 客户端接收消息广播ScoketObject到每个UIModel
if (ClientSocket is null) OnSocketReceive(obj);
}
return [.. result];
}
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.GetErrorInfo());
return [];
}
return [.. result];
}
#endregion