如何为Unity客户端-服务器多人游戏实现无需开放服务端源码的Mod系统
基于Unity服务端的类Spigot Mod API实现方案
1. 核心API层封装(不暴露底层源码的前提)
你需要先单独打包一个服务端API程序集,仅导出公共接口、抽象类、事件定义,完全不包含业务实现,该程序集是Mod开发者唯一需要引用的依赖,服务端底层源码不会对外暴露。
API层需要预先定义两个核心基础规范:
1.1 Mod主类契约
定义抽象基类ModBase,所有Mod的启动主类必须继承该类,固定生命周期方法,示例代码如下:
public abstract class ModBase { // Mod基础信息,开发者需在构造函数中赋值 public string ModName { get; protected set; } public string Author { get; protected set; } public string Version { get; protected set; } // 生命周期方法,Mod开发者重写实现自定义逻辑 public abstract void OnEnable(); public abstract void OnDisable(); }
1.2 事件系统规范
和Spigot的事件逻辑完全对齐,分为三个核心部分:
- 事件基类:所有可触发的业务事件都继承
EventBase,自带可取消属性供Mod控制服务端原流程
public abstract class EventBase { // 事件是否被取消,取消后服务端原逻辑不再执行 public bool IsCancelled { get; set; } = false; }
- 监听器标记:用Attribute标记监听方法,对应Spigot的
@EventHandler注解
[AttributeUsage(AttributeTargets.Method)] public class EventHandlerAttribute : Attribute { // 监听优先级,优先级越高越早收到事件 public EventPriority Priority { get; set; } = EventPriority.Normal; // 是否接收已经被其他Mod取消的事件 public bool IgnoreCancelled { get; set; } = false; } public enum EventPriority { Lowest, Low, Normal, High, Highest, Monitor }
- 事件管理器:服务端内部实现单例,仅向API层暴露三个公开方法,实现逻辑完全封装在服务端内部:
RegisterListener(ModBase mod, object listenerInstance):注册Mod的监听器实例UnregisterAll(ModBase mod):卸载Mod的所有监听器TriggerEvent(EventBase eventObj):触发指定事件,通知所有注册的监听器
2. Mod DLL创建流程
Mod开发者按照以下步骤即可制作符合规范的Mod:
- 新建C#类库项目,.NET版本与Unity服务端使用的.NET版本完全对齐(例如Unity 2021+对应.NET Standard 2.1或.NET 6)
- 项目仅引用你对外发布的服务端API程序集,无需引用任何服务端其他dll
- 新建主类继承
ModBase,重写OnEnable、OnDisable生命周期方法 - 新建监听器类,用
[EventHandler]标记需要触发的监听方法 - 编译项目得到的DLL文件即为成品Mod
Mod代码示例:
// Mod启动主类 public class MoveRestrictMod : ModBase { public MoveRestrictMod() { ModName = "移动区域限制"; Author = "Mod开发者"; Version = "1.0.0"; } public override void OnEnable() { // 注册当前类作为事件监听器 EventManager.Instance.RegisterListener(this, this); } public override void OnDisable() { // 卸载当前Mod的所有监听器 EventManager.Instance.UnregisterAll(this); } // 玩家移动事件监听方法 [EventHandler(Priority = EventPriority.Normal)] public void OnPlayerMove(PlayerMoveEvent e) { // 自定义逻辑:禁止玩家Y坐标低于0 if (e.NewPosition.y < 0) { e.IsCancelled = true; e.Player.SendMessage("你不能进入地下区域"); } } }
3. 服务端运行时Mod加载逻辑
服务端内部实现Mod加载器,执行固定流程:
- 服务端启动时扫描指定Mod目录下的所有DLL文件
- 调用
Assembly.LoadFrom()加载DLL程序集 - 反射遍历程序集中的所有类,匹配继承了
ModBase的启动主类 - 反射调用主类的无参构造函数,生成Mod实例存入全局管理列表
- 反射调用Mod实例的
OnEnable()方法完成Mod启动 - 触发事件时,事件管理器按优先级遍历所有注册的监听方法,反射调用传入事件实例即可
4. 玩家移动场景事件触发实现
在客户端发送移动包的处理流程中插入事件触发逻辑,示例代码如下:
// 服务端收到玩家移动包后的处理逻辑 public void HandlePlayerMovePacket(Player sender, PlayerMovePacket packet) { // 第一步:构造玩家移动事件实例 PlayerMoveEvent moveEvent = new PlayerMoveEvent() { Player = sender, OldPosition = sender.Position, NewPosition = packet.NewPosition, NewRotation = packet.NewRotation }; // 第二步:触发事件,通知所有注册的Mod监听器执行自定义逻辑 EventManager.Instance.TriggerEvent(moveEvent); // 第三步:判断事件是否被Mod取消,取消则不执行后续原同步逻辑 if (moveEvent.IsCancelled) { // 给发起请求的玩家回退当前位置 sender.SyncPosition(); return; } // 第四步:执行原有位置同步逻辑 sender.Position = moveEvent.NewPosition; sender.Rotation = moveEvent.NewRotation; // 同步位置给所有在线玩家 BroadcastPacket(new PlayerSyncPositionPacket() { PlayerId = sender.Id, Position = sender.Position, Rotation = sender.Rotation }); }
内容的提问来源于stack exchange,提问作者UnityGameDev99999
相关产品推荐
相关产品推荐

