You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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加载器,执行固定流程:

  1. 服务端启动时扫描指定Mod目录下的所有DLL文件
  2. 调用Assembly.LoadFrom()加载DLL程序集
  3. 反射遍历程序集中的所有类,匹配继承了ModBase的启动主类
  4. 反射调用主类的无参构造函数,生成Mod实例存入全局管理列表
  5. 反射调用Mod实例的OnEnable()方法完成Mod启动
  6. 触发事件时,事件管理器按优先级遍历所有注册的监听方法,反射调用传入事件实例即可

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 16:48:03