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

View File

@ -15,7 +15,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary> /// <summary>
/// 适用于服务器的模组集 /// 适用于服务器的模组集
/// </summary> /// </summary>
public Dictionary<string, GameModuleServer> ServerModules { get; } = []; public Dictionary<string, GameModuleServer> ModuleServers { get; } = [];
/// <summary> /// <summary>
/// 游戏地图集 /// 游戏地图集
@ -37,11 +37,6 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// </summary> /// </summary>
public Dictionary<string, ItemModule> Items { get; } = []; public Dictionary<string, ItemModule> Items { get; } = [];
/// <summary>
/// 客户端模组与服务器模组的关联字典
/// </summary>
public Dictionary<GameModule, GameModuleServer?> AssociatedServers { get; } = [];
/// <summary> /// <summary>
/// 已加载的模组DLL名称对应的路径 /// 已加载的模组DLL名称对应的路径
/// </summary> /// </summary>
@ -52,7 +47,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <summary> /// <summary>
/// 传入 <see cref="FunGameInfo.FunGame"/> 类型来创建指定端的模组读取器 /// 传入 <see cref="FunGameInfo.FunGame"/> 类型来创建指定端的模组读取器
/// <para>runtime = <see cref="FunGameInfo.FunGame.FunGame_Desktop"/> 时,仅读取 <seealso cref="Modules"/></para> /// <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"/> 都会读取 /// <seealso cref="Maps"/> 都会读取
/// </summary> /// </summary>
/// <param name="runtime">传入 <see cref="FunGameInfo.FunGame"/> 类型来创建指定端的模组读取器</param> /// <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.LoadGameModules(loader.Modules, loader.Characters, loader.Skills, loader.Items, delegates, otherobjs);
AddonManager.LoadGameMaps(loader.Maps, 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) else if (runtime == FunGameInfo.FunGame.FunGame_Server)
{ {
AddonManager.LoadGameModulesForServer(loader.Modules, loader.ServerModules, loader.Characters, loader.Skills, loader.Items, delegates, otherobjs); AddonManager.LoadGameModulesForServer(loader.ModuleServers, 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.LoadGameMaps(loader.Maps, otherobjs); AddonManager.LoadGameMaps(loader.Maps, otherobjs);
foreach (GameModuleServer server in loader.ModuleServers.Values)
{
server.GameModuleDepend.GetDependencies(loader);
server.AfterLoad(loader);
}
} }
return loader; return loader;
} }
@ -109,7 +107,7 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns> /// <returns></returns>
public GameModuleServer GetServerMode(string name) public GameModuleServer GetServerMode(string name)
{ {
return ServerModules[name]; return ModuleServers[name];
} }
/// <summary> /// <summary>

View File

@ -217,6 +217,15 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns> /// <returns></returns>
public static T? JsonDeserializeFromHashtable<T>(Hashtable hashtable, string key) => Service.JsonManager.GetObject<T>(hashtable, key); 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> /// <summary>
/// 反序列化IEnumerable中的Json对象 /// 反序列化IEnumerable中的Json对象
/// </summary> /// </summary>

View File

@ -75,6 +75,15 @@ namespace Milimoe.FunGame.Core.Api.Utility
/// <returns></returns> /// <returns></returns>
public T? GetObject<T>(Hashtable table, string key) => JsonManager.GetObject<T>(table, key, options); 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> /// <summary>
/// 反序列化IEnumerable中的Json对象 可指定反序列化选项 /// 反序列化IEnumerable中的Json对象 可指定反序列化选项
/// </summary> /// </summary>
@ -100,7 +109,8 @@ namespace Milimoe.FunGame.Core.Api.Utility
{ {
WriteIndented = true, WriteIndented = true,
ReferenceHandler = ReferenceHandler.IgnoreCycles, 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 * MySQL
*/ */
WriteINI("MySQL", "UseMySQL", "false");
WriteINI("MySQL", "DBServer", "localhost"); WriteINI("MySQL", "DBServer", "localhost");
WriteINI("MySQL", "DBPort", "3306"); WriteINI("MySQL", "DBPort", "3306");
WriteINI("MySQL", "DBName", "fungame"); WriteINI("MySQL", "DBName", "fungame");
WriteINI("MySQL", "DBUser", "root"); WriteINI("MySQL", "DBUser", "root");
WriteINI("MySQL", "DBPassword", "pass"); WriteINI("MySQL", "DBPassword", "pass");
/**
* SQLite
*/
WriteINI("SQLite", "UseSQLite", "true");
WriteINI("SQLite", "DataSource", "FunGameDB");
/** /**
* Mailer * Mailer
*/ */

View File

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

View File

@ -6,7 +6,8 @@ namespace Milimoe.FunGame.Core.Interface.Addons
IGamingRandomEventHandler, IGamingRoundEventHandler, IGamingLevelUpEventHandler, IGamingMoveEventHandler, IGamingAttackEventHandler, IGamingSkillEventHandler, IGamingItemEventHandler, IGamingMagicEventHandler, IGamingRandomEventHandler, IGamingRoundEventHandler, IGamingLevelUpEventHandler, IGamingMoveEventHandler, IGamingAttackEventHandler, IGamingSkillEventHandler, IGamingItemEventHandler, IGamingMagicEventHandler,
IGamingBuyEventHandler, IGamingSuperSkillEventHandler, IGamingPauseEventHandler, IGamingUnpauseEventHandler, IGamingSurrenderEventHandler, IGamingUpdateInfoEventHandler, IGamingPunishEventHandler, IGameModuleDepend IGamingBuyEventHandler, IGamingSuperSkillEventHandler, IGamingPauseEventHandler, IGamingUnpauseEventHandler, IGamingSurrenderEventHandler, IGamingUpdateInfoEventHandler, IGamingPunishEventHandler, IGameModuleDepend
{ {
public abstract void StartGame(Gaming instance, params object[] args); public bool HideMain { get; }
public abstract void StartUI(params object[] args); 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.Interface.Base;
using Milimoe.FunGame.Core.Library.Constant; 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 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 SocketRuntimeType Runtime { get; }
public Guid Token { get; } public Guid Token { get; }
public string ServerAddress { get; }
public int ServerPort { get; }
public string ServerName { get; }
public string ServerNotice { get; }
public void Close(); public void Close();
} }
} }

View File

@ -1,4 +1,5 @@
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
@ -14,7 +15,7 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <summary> /// <summary>
/// 客户端的套接字实例 /// 客户端的套接字实例
/// </summary> /// </summary>
public abstract ClientSocket? Socket { get; } public abstract ISocketMessageProcessor? Socket { get; }
/// <summary> /// <summary>
/// 客户端的用户实例,在用户登录后有效 /// 客户端的用户实例,在用户登录后有效
@ -31,6 +32,11 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// </summary> /// </summary>
public bool IsDebugMode { get; } public bool IsDebugMode { get; }
/// <summary>
/// 客户端的游戏模组服务器
/// </summary>
public GameModuleServer? NowGamingServer { get; set; }
/// <summary> /// <summary>
/// 向客户端发送消息 /// 向客户端发送消息
/// </summary> /// </summary>
@ -38,7 +44,7 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="objs"></param> /// <param name="objs"></param>
/// <returns></returns> /// <returns></returns>
public bool Send(ClientSocket socket, SocketMessageType type, params object[] objs); public bool Send(ISocketMessageProcessor socket, SocketMessageType type, params object[] objs);
/// <summary> /// <summary>
/// 向客户端发送系统消息 /// 向客户端发送系统消息
@ -58,15 +64,15 @@ namespace Milimoe.FunGame.Core.Interface.Base
/// <summary> /// <summary>
/// 开始接收客户端消息 /// 开始接收客户端消息
/// <para>请勿在 <see cref="Library.Common.Addon.GameModuleServer"/> 中调用此方法</para> /// <para>请勿在 <see cref="GameModuleServer"/> 中调用此方法</para>
/// </summary> /// </summary>
/// <param name="socket"></param> /// <param name="socket"></param>
/// <returns></returns> /// <returns></returns>
public bool Read(ClientSocket socket); public bool Read(ISocketMessageProcessor socket);
/// <summary> /// <summary>
/// 启动对客户端的监听 /// 启动对客户端的监听
/// <para>请勿在 <see cref="Library.Common.Addon.GameModuleServer"/> 中调用此方法</para> /// <para>请勿在 <see cref="GameModuleServer"/> 中调用此方法</para>
/// </summary> /// </summary>
public void Start(); 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; using Milimoe.FunGame.Core.Library.Common.Event;
namespace Milimoe.FunGame.Core.Interface namespace Milimoe.FunGame.Core.Interface
@ -9,146 +8,146 @@ namespace Milimoe.FunGame.Core.Interface
/// </summary> /// </summary>
public interface IGamingEventHandler 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 interface IGamingConnectEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingConnect; 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 interface IGamingDisconnectEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingDisconnect; 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 interface IGamingReconnectEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingReconnect; 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 interface IGamingBanCharacterEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingBanCharacter; 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 interface IGamingPickCharacterEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingPickCharacter; 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 interface IGamingRandomEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingRandom; 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 interface IGamingRoundEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingRound; 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 interface IGamingLevelUpEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingLevelUp; 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 interface IGamingMoveEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingMove; 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 interface IGamingAttackEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingAttack; 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 interface IGamingSkillEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingSkill; 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 interface IGamingItemEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingItem; 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 interface IGamingMagicEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingMagic; 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 interface IGamingBuyEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingBuy; 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 interface IGamingSuperSkillEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingSuperSkill; 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 interface IGamingPauseEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingPause; 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 interface IGamingUnpauseEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingUnpause; 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 interface IGamingSurrenderEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingSurrender; 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 interface IGamingUpdateInfoEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingUpdateInfo; 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 interface IGamingPunishEventHandler : IGamingEventHandler
{ {
public event GamingEventHandler? GamingPunish; 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 namespace Milimoe.FunGame.Core.Interface
{ {
public interface IGamingConnectEvent 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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.Transmittal;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; 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 RoomType RoomType => RoomType.Mix;
public override bool HideMain => false;
public ExampleGameModule() 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字符串因此客户端要主动发起确认连接的请求。 // 在下方的Server示例中服务器发来的data中包含check字符串因此客户端要主动发起确认连接的请求。
if (data.ContainsKey("info_type")) if (data.ContainsKey("info_type"))
{ {
// 反序列化得到指定key的值 // 反序列化得到指定key的值
string info_type = DataRequest.GetHashtableJsonObject<string>(data, "info_type") ?? ""; string info_type = DataRequest.GetDictionaryJsonObject<string>(data, "info_type") ?? "";
if (info_type == "check") 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); 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 List<User> ConnectedUser = [];
private readonly Dictionary<string, Hashtable> UserData = []; private readonly Dictionary<string, Dictionary<string, object>> UserData = [];
private async Task Test() private async Task Test()
{ {
@ -154,7 +155,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
// 在FunGame项目中建议永远使用客户端主动发起请求因为服务器主动发起的实现难度较高 // 在FunGame项目中建议永远使用客户端主动发起请求因为服务器主动发起的实现难度较高
// 下面的演示基于综合的两种情况:服务器主动发送通知,客户端收到后,发起确认 // 下面的演示基于综合的两种情况:服务器主动发送通知,客户端收到后,发起确认
// UpdateInfo是一个灵活的类型。如果发送check字符串意味着服务器要求客户端发送确认 // UpdateInfo是一个灵活的类型。如果发送check字符串意味着服务器要求客户端发送确认
Hashtable data = []; Dictionary<string, object> data = [];
data.Add("info_type", "check"); data.Add("info_type", "check");
// 进阶示例传递一个token让客户端返回 // 进阶示例传递一个token让客户端返回
@ -164,7 +165,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
// 我们保存到字典UserData中这样可以方便跨方法检查变量 // 我们保存到字典UserData中这样可以方便跨方法检查变量
foreach (string username in Users.Select(u => u.Username).Distinct()) 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); value.Add("connect_token", token);
} }
@ -174,7 +175,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
UserData[username].Add("connect_token", token); UserData[username].Add("connect_token", token);
} }
} }
SendAllGamingMessage(GamingType.UpdateInfo, data); SendGamingMessage(All.Values, GamingType.UpdateInfo, data);
// 新建一个线程等待所有玩家确认 // 新建一个线程等待所有玩家确认
while (true) while (true)
@ -186,18 +187,18 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
Controller.WriteLine("所有玩家都已经连接。"); 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) switch (type)
{ {
case GamingType.Connect: case GamingType.Connect:
// 编写处理“连接”命令的逻辑 // 编写处理“连接”命令的逻辑
// 如果需要处理客户端传递的参数获取与客户端约定好的参数key对应的值 // 如果需要处理客户端传递的参数获取与客户端约定好的参数key对应的值
string un = NetworkUtility.JsonDeserializeFromHashtable<string>(data, "username") ?? ""; string un = NetworkUtility.JsonDeserializeFromDictionary<string>(data, "username") ?? "";
Guid token = NetworkUtility.JsonDeserializeFromHashtable<Guid>(data, "connect_token"); Guid token = NetworkUtility.JsonDeserializeFromDictionary<Guid>(data, "connect_token");
if (un == username && UserData.TryGetValue(username, out Hashtable? value) && value != null && (value["connect_token"]?.Equals(token) ?? false)) 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()); ConnectedUser.Add(Users.Where(u => u.Username == username).First());
Controller.WriteLine(username + " 已经连接。"); Controller.WriteLine(username + " 已经连接。");
@ -208,52 +209,6 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
return result; 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> /// <summary>

View File

@ -1,4 +1,3 @@
using System.Collections;
using Milimoe.FunGame.Core.Controller; using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Interface; using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Interface.Addons;
@ -45,6 +44,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// </summary> /// </summary>
public abstract RoomType RoomType { get; } public abstract RoomType RoomType { get; }
/// <summary>
/// 是否隐藏主界面
/// </summary>
public abstract bool HideMain { get; }
/// <summary> /// <summary>
/// 是否连接其他的服务器模组 /// 是否连接其他的服务器模组
/// </summary> /// </summary>
@ -116,7 +120,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
return false; return false;
} }
// BeforeLoad可以阻止加载此模组 // BeforeLoad可以阻止加载此模组
if (BeforeLoad()) if (BeforeLoad(objs))
{ {
// 模组加载后,不允许再次加载此模组 // 模组加载后,不允许再次加载此模组
IsLoaded = true; IsLoaded = true;
@ -124,16 +128,14 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
Init(objs); Init(objs);
// 触发绑定事件 // 触发绑定事件
BindEvent(); BindEvent();
// 如果加载后需要执行代码请重写AfterLoad方法
AfterLoad();
} }
return IsLoaded; return IsLoaded;
} }
/// <summary> /// <summary>
/// 模组加载后需要做的事 /// 模组完全加载后需要做的事
/// </summary> /// </summary>
protected virtual void AfterLoad() public virtual void AfterLoad(params object[] args)
{ {
// override // override
} }
@ -142,7 +144,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// 允许返回false来阻止加载此模组 /// 允许返回false来阻止加载此模组
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
protected virtual bool BeforeLoad() protected virtual bool BeforeLoad(params object[] objs)
{ {
return true; return true;
} }
@ -318,102 +320,102 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
public event IGamingEventHandler.GamingEventHandler? GamingUpdateInfo; public event IGamingEventHandler.GamingEventHandler? GamingUpdateInfo;
public event IGamingEventHandler.GamingEventHandler? GamingPunish; 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); GamingPunish?.Invoke(sender, e, data);
} }

View File

@ -1,4 +1,3 @@
using System.Collections;
using Milimoe.FunGame.Core.Controller; using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Interface.Addons;
@ -72,8 +71,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// <param name="username">发送此消息的账号</param> /// <param name="username">发送此消息的账号</param>
/// <param name="type">消息类型</param> /// <param name="type">消息类型</param>
/// <param name="data">消息参数</param> /// <param name="data">消息参数</param>
/// <returns>底层会将哈希表中的数据发送给客户端</returns> /// <returns>底层会将字典中的数据发送给客户端</returns>
public abstract Hashtable GamingMessageHandler(string username, GamingType type, Hashtable data); public abstract Dictionary<string, object> GamingMessageHandler(string username, GamingType type, Dictionary<string, object> data);
/// <summary> /// <summary>
/// 加载标记 /// 加载标记
@ -94,16 +93,14 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
{ {
// 模组加载后,不允许再次加载此模组 // 模组加载后,不允许再次加载此模组
IsLoaded = true; IsLoaded = true;
// 如果加载后需要执行代码请重写AfterLoad方法
AfterLoad();
} }
return IsLoaded; return IsLoaded;
} }
/// <summary> /// <summary>
/// 模组加载后需要做的事 /// 模组完全加载后需要做的事
/// </summary> /// </summary>
protected virtual void AfterLoad() public virtual void AfterLoad(params object[] args)
{ {
// override // override
} }
@ -116,5 +113,41 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
{ {
return true; 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) switch (propertyName)
{ {
case nameof(Character.Id):
result.Id = reader.GetInt64();
break;
case nameof(Character.Name): case nameof(Character.Name):
result.Name = reader.GetString() ?? ""; result.Name = reader.GetString() ?? "";
break; break;
@ -190,6 +193,7 @@ namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
public override void Write(Utf8JsonWriter writer, Character value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, Character value, JsonSerializerOptions options)
{ {
writer.WriteStartObject(); writer.WriteStartObject();
writer.WriteNumber(nameof(Character.Id), value.Id);
writer.WriteString(nameof(Character.Name), value.Name); writer.WriteString(nameof(Character.Name), value.Name);
writer.WriteString(nameof(Character.FirstName), value.FirstName); writer.WriteString(nameof(Character.FirstName), value.FirstName);
writer.WriteString(nameof(Character.NickName), value.NickName); 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.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Library.Common.Architecture; 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.Library.Constant;
using Milimoe.FunGame.Core.Service; using Milimoe.FunGame.Core.Service;
namespace Milimoe.FunGame.Core.Library.Common.Network 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 SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Token; public Guid Token { get; } = token;
public string ServerAddress { get; } = ""; public string ClientIP { get; } = clientIP;
public int ServerPort { get; } = ServerPort;
public string ServerName { get; } = "";
public string ServerNotice { get; } = "";
public string ClientIP { get; } = ClientIP;
public string ClientName => _ClientName; public string ClientName => _ClientName;
public bool Connected => Instance != null && Instance.Connected; public bool Connected => Instance != null && Instance.Connected;
public bool Receiving => _Receiving; public bool Receiving => _Receiving;
public Type InstanceType => typeof(ClientSocket);
private Task? ReceivingTask; private Task? ReceivingTask;
private bool _Receiving; private bool _Receiving;
private readonly string _ClientName = ClientName; private readonly string _ClientName = clientName;
public void Close() public void Close()
{ {
@ -54,6 +52,19 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
return SocketResult.NotSent; 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) public void BindEvent(Delegate method, bool remove = false)
{ {
if (!remove) 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 class HTTPClient : IHTTPClient
{ {
public ClientWebSocket? Instance { get; } = null; public System.Net.WebSockets.ClientWebSocket? Instance { get; } = null;
public SocketRuntimeType Runtime => SocketRuntimeType.Client; public SocketRuntimeType Runtime => SocketRuntimeType.Client;
public Guid Token { get; } = Guid.Empty; public Guid Token { get; } = Guid.Empty;
public string ServerAddress { get; } = ""; public string ServerAddress { get; } = "";
@ -20,7 +20,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
private bool _Listening = false; private bool _Listening = false;
private readonly HeartBeat HeartBeat; 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.Instance = Instance;
this.ServerAddress = ServerAddress; this.ServerAddress = ServerAddress;
@ -34,7 +34,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
{ {
string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(ServerAddress); string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(ServerAddress);
Uri uri = new((SSL ? "wss://" : "ws://") + ServerIP + ":" + ServerPort + "/" + SubDirectory); 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) if (socket != null && socket.State == WebSocketState.Open)
{ {
HTTPClient client = new(socket, ServerAddress, ServerPort, args); HTTPClient client = new(socket, ServerAddress, ServerPort, args);

View File

@ -1,5 +1,6 @@
using System.Net; using System.Net;
using System.Net.WebSockets; using System.Net.WebSockets;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Interface.HTTP; using Milimoe.FunGame.Core.Interface.HTTP;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service; using Milimoe.FunGame.Core.Service;
@ -11,24 +12,20 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public HttpListener Instance { get; } public HttpListener Instance { get; }
public SocketRuntimeType Runtime => SocketRuntimeType.Server; public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty; 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; } = []; public Dictionary<Guid, WebSocket> ClientSockets { get; } = [];
private HTTPListener(HttpListener Instance, int ServerPort) private HTTPListener(HttpListener instance)
{ {
this.Instance = Instance; Instance = instance;
this.ServerPort = ServerPort; 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) if (socket != null)
{ {
HTTPListener instance = new(socket, Port); HTTPListener instance = new(socket);
Task t = Task.Run(async () => await HTTPManager.Receiving(instance)); Task t = Task.Run(async () => await HTTPManager.Receiving(instance));
return instance; return instance;
} }
@ -55,7 +52,27 @@ namespace Milimoe.FunGame.Core.Library.Common.Network
public void Close() public void 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(); 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 System.Net.Sockets.Socket Instance { get; }
public SocketRuntimeType Runtime => SocketRuntimeType.Server; public SocketRuntimeType Runtime => SocketRuntimeType.Server;
public Guid Token { get; } = Guid.Empty; 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 bool Connected => Instance != null && Instance.Connected;
public List<IServerModel> ClientList => OnlineClients.GetList(); public List<IServerModel> ClientList => OnlineClients.GetList();
public List<IServerModel> UserList => OnlineUsers.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> OnlineClients;
private readonly ModelManager<IServerModel> OnlineUsers; 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; Token = Guid.NewGuid();
this.ServerPort = ServerPort; Instance = instance;
if (MaxConnection <= 0) if (maxConnection <= 0)
{ {
OnlineClients = []; OnlineClients = [];
OnlineUsers = []; OnlineUsers = [];
} }
else else
{ {
OnlineClients = new(MaxConnection); OnlineClients = new(maxConnection);
OnlineUsers = 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; if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G;
System.Net.Sockets.Socket? socket = SocketManager.StartListening(Port, MaxConnection); System.Net.Sockets.Socket? socket = SocketManager.StartListening(port, maxConnection);
if (socket != null) return new ServerSocket(socket, Port); if (socket != null) return new ServerSocket(socket, port);
else throw new SocketCreateListenException(); else throw new SocketCreateListenException();
} }
public ClientSocket Accept(Guid Token) public static ClientSocket Accept(Guid token)
{ {
object[] result = SocketManager.Accept(); object[] result = SocketManager.Accept();
if (result != null && result.Length == 2) if (result != null && result.Length == 2)
{ {
string ClientIP = (string)result[0]; string clientIP = (string)result[0];
System.Net.Sockets.Socket Client = (System.Net.Sockets.Socket)result[1]; System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)result[1];
return new ClientSocket(Client, ServerPort, ClientIP, ClientIP, Token); return new ClientSocket(client, clientIP, clientIP, token);
} }
throw new SocketGetClientException(); throw new SocketGetClientException();
} }

View File

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

View File

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

View File

@ -66,6 +66,7 @@ namespace Milimoe.FunGame.Core.Library.Constant
{ {
Unknown, Unknown,
DataRequest, DataRequest,
GamingRequest,
Connect, Connect,
Disconnect, Disconnect,
System, System,
@ -844,4 +845,11 @@ namespace Milimoe.FunGame.Core.Library.Constant
Plugin, Plugin,
GameModule GameModule
} }
public enum SQLMode
{
None,
MySQL,
SQLite
}
} }

View File

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

View File

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

View File

@ -13,17 +13,17 @@ namespace Milimoe.FunGame.Core.Service
internal static HttpListener? ServerSocket => _ServerSocket; internal static HttpListener? ServerSocket => _ServerSocket;
private static HttpListener? _ServerSocket = null; 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(); HttpListener listener = new();
listener.Prefixes.Add((SSL ? "https://" : "http://") + "localhost:" + Port + "/"); listener.Prefixes.Add((ssl ? "https://" : "http://") + "localhost:" + port + "/ws");
listener.Start(); listener.Start();
return listener; 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); await socket.ConnectAsync(uri, CancellationToken.None);
if (socket.State == WebSocketState.Open) if (socket.State == WebSocketState.Open)
{ {
@ -32,7 +32,7 @@ namespace Milimoe.FunGame.Core.Service
return null; 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) if (socket != null)
{ {
@ -79,7 +79,7 @@ namespace Milimoe.FunGame.Core.Service
HttpListenerContext context = await ServerSocket.GetContextAsync(); HttpListenerContext context = await ServerSocket.GetContextAsync();
if (context.Request.IsWebSocketRequest) if (context.Request.IsWebSocketRequest)
{ {
await AddClientWebSocket(listener, context); TaskUtility.NewTask(async () => await AddClientWebSocket(listener, context));
} }
else else
{ {
@ -154,12 +154,19 @@ namespace Milimoe.FunGame.Core.Service
listener.ClientSockets.TryAdd(token, socket); listener.ClientSockets.TryAdd(token, socket);
await Send(socket, sendobject); await Send(socket, sendobject);
while (!result.CloseStatus.HasValue) while (socket.State == WebSocketState.Open)
{ {
try try
{ {
buffer = new byte[General.SocketByteSize]; buffer = new byte[General.SocketByteSize];
result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); 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(); msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim();
objs = await GetSocketObjects(socket, result, msg); objs = await GetSocketObjects(socket, result, msg);
foreach (SocketObject obj in objs) foreach (SocketObject obj in objs)
@ -167,7 +174,7 @@ namespace Milimoe.FunGame.Core.Service
sendobject = listener.SocketObject_Handler(obj); sendobject = listener.SocketObject_Handler(obj);
if (obj.SocketType == SocketMessageType.Disconnect) 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; return;
} }
await Send(socket, sendobject); await Send(socket, sendobject);
@ -175,8 +182,10 @@ namespace Milimoe.FunGame.Core.Service
} }
catch (Exception e) catch (Exception e)
{ {
// 处理其他异常
TXTHelper.AppendErrorLog(e.GetErrorInfo()); 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; 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> /// <summary>
/// 反序列化IEnumerable中的Json对象 /// 反序列化IEnumerable中的Json对象
/// </summary> /// </summary>
@ -205,6 +226,28 @@ namespace Milimoe.FunGame.Core.Service
return default; 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> /// <summary>
/// 反序列化多个Json对象 /// 反序列化多个Json对象
/// 注意必须是相同的Json对象才可以使用此方法解析 /// 注意必须是相同的Json对象才可以使用此方法解析

View File

@ -1,6 +1,8 @@
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.Exception;
namespace Milimoe.FunGame.Core.Service namespace Milimoe.FunGame.Core.Service
{ {
@ -145,6 +147,25 @@ namespace Milimoe.FunGame.Core.Service
return SocketResult.NotSent; 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> /// <summary>
/// 用于客户端向服务器Socket发送信息 /// 用于客户端向服务器Socket发送信息
/// </summary> /// </summary>
@ -163,6 +184,24 @@ namespace Milimoe.FunGame.Core.Service
return SocketResult.NotSent; 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> /// <summary>
/// 接收数据流中的信息 /// 接收数据流中的信息
/// <para/>如果是服务器接收信息需要传入客户端Socket <paramref name="ClientSocket"/> /// <para/>如果是服务器接收信息需要传入客户端Socket <paramref name="ClientSocket"/>
@ -170,6 +209,8 @@ namespace Milimoe.FunGame.Core.Service
/// <param name="ClientSocket">如果是服务器接收信息需要传入客户端Socket</param> /// <param name="ClientSocket">如果是服务器接收信息需要传入客户端Socket</param>
/// <returns>SocketObjects</returns> /// <returns>SocketObjects</returns>
internal static Library.Common.Network.SocketObject[] Receive(Socket? ClientSocket = null) internal static Library.Common.Network.SocketObject[] Receive(Socket? ClientSocket = null)
{
try
{ {
List<Library.Common.Network.SocketObject> result = []; List<Library.Common.Network.SocketObject> result = [];
Socket? tempSocket = ClientSocket is null ? Socket : ClientSocket; Socket? tempSocket = ClientSocket is null ? Socket : ClientSocket;
@ -220,6 +261,12 @@ namespace Milimoe.FunGame.Core.Service
} }
return [.. result]; return [.. result];
} }
catch (Exception e)
{
TXTHelper.AppendErrorLog(e.GetErrorInfo());
return [];
}
}
#endregion #endregion