添加插件之间的数据请求;补全插件示例 (#150)

This commit is contained in:
yeziuku 2026-04-05 17:09:39 +08:00 committed by GitHub
parent 5194fe39e9
commit 47fb717651
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 281 additions and 7 deletions

View File

@ -1,4 +1,5 @@
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Service;
@ -196,6 +197,29 @@ namespace Milimoe.FunGame.Core.Api.Utility
}
}
/// <summary>
/// 处理服务器广播的非标准 DataRequest 请求
/// </summary>
/// <param name="data"></param>
/// <param name="e"></param>
/// <returns></returns>
public Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
Dictionary<string, object> result = [];
if (ModuleServers.Count > 0)
{
foreach (GameModuleServer module in ModuleServers.Values)
{
Dictionary<string, object> moduleResult = module.HandleDataRequest(data, e);
foreach (string key in moduleResult.Keys)
{
result[key] = moduleResult[key];
}
}
}
return result;
}
/// <summary>
/// 获取对应名称的模组实例
/// <para>如果需要取得服务器模组的实例,请调用 <see cref="GetServerMode"/></para>

View File

@ -81,6 +81,26 @@ namespace Milimoe.FunGame.Core.Api.Utility
}
}
/// <summary>
/// 处理服务器广播的非标准 DataRequest 请求
/// </summary>
/// <param name="data"></param>
/// <param name="e"></param>
/// <returns></returns>
public virtual Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
Dictionary<string, object> result = [];
foreach (ServerPlugin plugin in Plugins.Values)
{
Dictionary<string, object> pluginResult = plugin.HandleDataRequest(data, e);
foreach (string key in pluginResult.Keys)
{
result[key] = pluginResult[key];
}
}
return result;
}
public ServerPlugin this[string name]
{
get

View File

@ -81,6 +81,26 @@ namespace Milimoe.FunGame.Core.Api.Utility
}
}
/// <summary>
/// 处理服务器广播的非标准 DataRequest 请求
/// </summary>
/// <param name="data"></param>
/// <param name="e"></param>
/// <returns></returns>
public Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
Dictionary<string, object> result = [];
foreach (WebAPIPlugin plugin in Plugins.Values)
{
Dictionary<string, object> pluginResult = plugin.HandleDataRequest(data, e);
foreach (string key in pluginResult.Keys)
{
result[key] = pluginResult[key];
}
}
return result;
}
public WebAPIPlugin this[string name]
{
get

View File

@ -98,6 +98,58 @@ namespace Milimoe.FunGame.Core.Controller
return MaskMethod_NewLongRunningGamingRequest(type);
}
/// <summary>
/// 基于本地已连接的Socket创建非标准的数据请求<see cref="DataRequestType.Addon_Module"/> 或 <see cref="DataRequestType.Addon_Plugin"/>,具体类型由 <see cref="BaseAddonController{T}.Addon"/> 决定)
/// <para/>可以指定 <paramref name="targetAddon"/>,以便发送给一个已知的服务器模组或服务器插件
/// </summary>
/// <param name="targetAddon"></param>
/// <param name="longRunning"></param>
/// <returns></returns>
/// <exception cref="ConnectFailedException"></exception>
public DataRequest NewDataRequest(string targetAddon = "", bool longRunning = false)
{
if (Addon is IPlugin)
{
DataRequest request;
if (longRunning)
{
request = NewLongRunningDataRequest(DataRequestType.Addon_Plugin);
}
else
{
request = NewDataRequest(DataRequestType.Addon_Plugin);
}
request.AddRequestData(SocketSet.AddonDataRequestMark_FromPlugin, Addon.Name);
request.AddRequestData(SocketSet.AddonDataRequestMark_TargetPlugin, targetAddon);
return request;
}
else if (Addon is IGameModule)
{
DataRequest request;
if (longRunning)
{
request = NewLongRunningDataRequest(DataRequestType.Addon_Module);
}
else
{
request = NewDataRequest(DataRequestType.Addon_Module);
}
request.AddRequestData(SocketSet.AddonDataRequestMark_FromModule, Addon.Name);
request.AddRequestData(SocketSet.AddonDataRequestMark_TargetModule, targetAddon);
return request;
}
throw new ConnectFailedException();
}
/// <summary>
/// 基于本地已连接的Socket创建长时间运行的、非标准的数据请求<see cref="DataRequestType.Addon_Module"/> 或 <see cref="DataRequestType.Addon_Plugin"/>,具体类型由 <see cref="BaseAddonController{T}.Addon"/> 决定)
/// <para/>可以指定 <paramref name="targetAddon"/>,以便发送给一个已知的服务器模组或服务器插件
/// </summary>
/// <param name="targetAddon"></param>
/// <returns></returns>
/// <exception cref="ConnectFailedException"></exception>
public DataRequest NewLongRunningDataRequest(string targetAddon = "") => NewDataRequest(targetAddon, true);
/// <summary>
/// 新建一个AddonController
/// </summary>

View File

@ -1,13 +1,18 @@
using Milimoe.FunGame.Core.Interface;
using System.Data;
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Interface;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.SQLScript.Entity;
namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
{
/// <summary>
/// 必须继承基类:<see cref="Plugin"/><para/>
/// 继承事件接口并实现其方法来使插件生效。例如继承:<seealso cref="ILoginEvent"/>
/// 客户端插件,必须继承基类:<see cref="Plugin"/><para/>
/// 继承事件接口并实现其方法来使插件生效。例如继承:<seealso cref="IConnectEvent"/>
/// </summary>
public class ExamplePlugin : Plugin, ILoginEvent
public class ExamplePlugin : Plugin, IConnectEvent
{
public override string Name => "fungame.example.plugin";
@ -17,6 +22,77 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
public override string Author => "FunGamer";
public void AfterConnectEvent(object sender, ConnectEventArgs e)
{
// 每个事件接口都有 Before/After 两个介入点,根据自身需求实现即可
// 可以在连接服务器完毕后做一些处理...
}
public void BeforeConnectEvent(object sender, ConnectEventArgs e)
{
// 和服务器插件的联动示例
// 假设服务器上有个名为“fungame.example.serverplugin”的服务器插件当然也可以不指定不指定时服务器会广播所有插件性能可能较低
DataRequest request = Controller.NewDataRequest(targetAddon: "fungame.example.serverplugin");
long tick = DateTime.Now.Ticks;
request.AddRequestData("event", "ping");
request.AddRequestData("tick", tick);
request.SendRequest();
if (request.Result == RequestResult.Success)
{
long newTick = request.GetResult<long>("tick");
Controller.WriteLine($"服务器延迟:{Math.Min(999, new TimeSpan(newTick - tick).TotalMilliseconds)}ms");
}
}
}
/// <summary>
/// 服务器插件,必须继承基类:<see cref="ServerPlugin"/><para/>
/// 同样需继承事件接口并实现其方法来使插件生效。例如继承:<seealso cref="ILoginEvent"/>
/// </summary>
public class ExampleServerPlugin : ServerPlugin, ILoginEvent
{
public override string Name => "fungame.example.serverplugin";
public override string Description => "My First Server Plugin";
public override string Version => "1.0.0";
public override string Author => "FunGamer";
public override void ProcessInput(string input)
{
// 该方法用于接收服务器控制台的输入并处理
if (input.Equals("info", StringComparison.CurrentCultureIgnoreCase))
{
// 使用PluginController的输出方式
Controller.WriteLine($"This is {nameof(ExampleServerPlugin)}!!");
}
}
public override void AfterLoad(ServerPluginLoader loader, params object[] objs)
{
// 该方法可在插件加载完毕后执行代码
}
public override Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
// 此方法接收并处理数据请求,我们先前在客户端插件中发送了数据请求,这里就要处理它
// 需要注意data 是通用的,如果服务器将数据请求广播到所有的插件,这些插件是无法区分客户端的请求类型是什么的,所以这一部分更偏向于“约定”
// 可以约定好插件的名称也可以约定好event一切都完全由插件开发者掌控
if (e.From == "fungame.example.plugin" && e.Target == Name)
{
string ent = NetworkUtility.JsonDeserializeFromDictionary<string>(data, "event") ?? "";
if (ent.Equals("ping", StringComparison.CurrentCultureIgnoreCase))
{
return new()
{
{ "tick", DateTime.Now.Ticks }
};
}
}
return [];
}
public void AfterLoginEvent(object sender, LoginEventArgs e)
{
@ -24,8 +100,18 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example
public void BeforeLoginEvent(object sender, LoginEventArgs e)
{
// 如果这里设置Cancel = true将终止登录
e.Cancel = true;
// 如果这里设置Cancel = true将终止登录可以加入自定义的验证机制等
// 例如SQLHelper的使用
if (Controller.SQLHelper != null)
{
// 服务器的标准功能会有登录检查,这里仅做一个示例
DataRow? row = Controller.SQLHelper.ExecuteDataRow(UserQuery.Select_UserByUsername(Controller.SQLHelper, e.Username));
if (row is null)
{
e.Cancel = true;
e.EventMsg = "用户名不存在。";
}
}
}
}
}

View File

@ -3,6 +3,7 @@ using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Controller;
using Milimoe.FunGame.Core.Interface.Addons;
using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant;
namespace Milimoe.FunGame.Core.Library.Common.Addon
@ -129,6 +130,19 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
return [];
}
/// <summary>
/// 此方法为可选实现,接收并处理非标准 DataRequest 请求<para/>
/// 此方法效率可能低于匿名服务器<para/>
/// <para/>请使用 <see cref="NetworkUtility.JsonDeserializeFromDictionary{T}(Dictionary{string, object}, string)"/> 方法获取 <paramref name="data"/> 成员
/// </summary>
/// <param name="data"></param>
/// <param name="e"></param>
/// <returns>返回一个无需对成员序列化的字典</returns>
public virtual Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
return [];
}
/// <summary>
/// 加载标记
/// </summary>

View File

@ -96,6 +96,18 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// <param name="input"></param>
public abstract void ProcessInput(string input);
/// <summary>
/// 处理服务器广播的非标准 DataRequest 请求
/// <para/>请使用 <see cref="NetworkUtility.JsonDeserializeFromDictionary{T}(Dictionary{string, object}, string)"/> 方法获取 <paramref name="data"/> 成员
/// </summary>
/// <param name="data"></param>
/// <param name="e"></param>
/// <returns>返回一个无需对成员序列化的字典</returns>
public virtual Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
return [];
}
/// <summary>
/// 插件完全加载后需要做的事
/// </summary>

View File

@ -96,6 +96,18 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon
/// <param name="input"></param>
public abstract void ProcessInput(string input);
/// <summary>
/// 处理服务器广播的非标准 DataRequest 请求
/// <para/>请使用 <see cref="NetworkUtility.JsonDeserializeFromDictionary{T}(Dictionary{string, object}, string)"/> 方法获取 <paramref name="data"/> 成员
/// </summary>
/// <param name="data"></param>
/// <param name="e"></param>
/// <returns>返回一个无需对成员序列化的字典</returns>
public virtual Dictionary<string, object> HandleDataRequest(Dictionary<string, object> data, AddonDataRequestEventArgs e)
{
return [];
}
/// <summary>
/// 插件完全加载后需要做的事
/// </summary>

View File

@ -0,0 +1,11 @@
using Milimoe.FunGame.Core.Interface.Base;
namespace Milimoe.FunGame.Core.Library.Common.Event
{
public class AddonDataRequestEventArgs(IServerModel client, string from, string target) : GeneralEventArgs
{
public IServerModel Client { get; set; } = client;
public string From { get; set; } = from;
public string Target { get; set; } = target;
}
}

View File

@ -1,4 +1,5 @@
using System.Text;
using Milimoe.FunGame.Core.Interface.Addons;
using Milimoe.FunGame.Core.Model;
/**
@ -104,7 +105,11 @@ namespace Milimoe.FunGame.Core.Library.Constant
public const int MaxConnection_1C2G = 10;
public const int MaxConnection_2C2G = 20;
public const int MaxConnection_4C4G = 40;
public const string Plugins_Mark = "plugins_mark";
public const string Plugins_Mark = "$*plugins_mark*";
public const string AddonDataRequestMark_FromPlugin = "$*from_plugin*";
public const string AddonDataRequestMark_TargetPlugin = "$*target_plugin*";
public const string AddonDataRequestMark_FromModule = "$*from_module*";
public const string AddonDataRequestMark_TargetModule = "$*target_module*";
public const string Socket = "Socket";
public const string Unknown = "Unknown";
@ -211,6 +216,11 @@ namespace Milimoe.FunGame.Core.Library.Constant
public const string Inventory_MakeOffer = "Inventory::MakeOffer";
public const string Inventory_ReviseOffer = "Inventory::ReviseOffer";
public const string Inventory_RespondOffer = "Inventory::RespondOffer";
/**
* Addon
*/
public const string Addon_Plugin = "Addon::Plugin";
public const string Addon_Module = "Addon::Module";
/// <summary>
/// 获取Type的等效字符串
@ -256,6 +266,8 @@ namespace Milimoe.FunGame.Core.Library.Constant
DataRequestType.Inventory_MakeOffer => Inventory_MakeOffer,
DataRequestType.Inventory_ReviseOffer => Inventory_ReviseOffer,
DataRequestType.Inventory_RespondOffer => Inventory_RespondOffer,
DataRequestType.Addon_Module => Addon_Module,
DataRequestType.Addon_Plugin => Addon_Plugin,
_ => UnKnown
};
}

View File

@ -122,6 +122,8 @@ namespace Milimoe.FunGame.Core.Library.Constant
Inventory_MakeOffer,
Inventory_ReviseOffer,
Inventory_RespondOffer,
Addon_Plugin,
Addon_Module
}
/// <summary>
@ -1118,4 +1120,13 @@ namespace Milimoe.FunGame.Core.Library.Constant
NumberInput,
Custom
}
public enum PositionType
{
Center,
Left,
Right,
Top,
Bottom
}
}