EasyMod

游戏开发者指南:开放权限、SDK 定制、Mod Editor、地图编辑、运行时管理与联机同步。

v0.7.0 Win64 / Unreal Engine 5.x / UE 5.0 walkthrough Unreal EngineModdingBlueprint

概览

EasyMod Overview

EasyMod for Unreal Engine

EasyMod 是面向 Unreal Engine 游戏的 Mod 创作与运行框架。管理你愿意开放的类、方法与资源,定制游戏 SDK,并向社区提供专用 Mod Editor;游戏内负责 Mod 加载、管理与联机同步。

渐进接入现有项目

复用现有 C++、Blueprint 和资源,从一个小功能开始。原型、开发后期或长期维护项目均可逐步加入 Mod 支持。 查看教程

精细管理开放范围

控制类的引用、继承和实例化,以及变量、函数、事件和资源的访问与编辑权限。 查看教程

定制游戏 SDK

选择作者需要的 API、资源、地图和创作能力,导出属于当前游戏的 .emsdk。 查看教程

专用 Mod Editor

向作者提供匹配的独立 Blueprint 编辑器,用熟悉的工作流创建、调试和打包 Mods。 查看教程

玩法、资源与地图扩展

按批准的范围编辑 Blueprint 逻辑和默认值、扩展资源与地图,并分别授权场景、World Settings 和 Level Blueprint。 查看教程

自动联机 Mod 同步

准备服务器要求的 Mod 包,校验并匹配版本与加载顺序,支持下载缓存、断点续传和逐包进度。 查看教程

游戏内 Mod 管理

使用可复用 UMG 组件或 Blueprint API 接入导入、启停、排序、状态与冲突展示。 查看教程

存档与稳定内容 ID

为 Mod 保存独立数据,登记稳定内容 ID,并接入数据迁移和游戏自己的存储后端。 查看教程

社区翻译 Mod

选择开放的游戏文本,让社区制作与分发翻译 Mod,独立翻译包无需 Cook/Pak。 查看教程

EasyMod 的组成

EasyMod
|
+-- Developer Tools: Public Rules / Custom SDK Export
+-- Mod Editor:      Blueprints / Maps / Assets / Packaging
+-- Game Runtime:    Mod Loading / UMG Manager / Blueprint APIs
+-- Multiplayer:     Automatic Mod Package Synchronization

开发者配置开放规则并导出 SDK;作者在匹配的 Mod Editor 中创建和打包 Mod;玩家通过你的游戏加载和管理 Mod,联机时使用已接入的自动同步流程。

三种交付物与使用者

交付物谁使用用途
EasyMod 插件游戏开发者安装在正常 UE 工程中,配置开放规则、导出 SDK、集成运行时与管理 UI。
游戏 SDK(.emsdk)Mod 作者由开发者导出,包含这款游戏发布的 API、资源、地图、权限和创作信息。
EasyMod EditorMod 作者独立的 Blueprint Mod 编辑器,配合匹配的游戏 SDK 创建、测试和打包 Mods。

玩家运行你的游戏并安装 Mod 即可。只有需要制作 Mod 的玩家才需要 Mod Editor。这三种交付物分别分发;作者不需要购买或安装 FAB 的游戏开发者源码插件。

推荐接入路线

先跑通一条小流程:选择一个已有 Blueprint 或小地图,开放需要的权限,导出 SDK,制作一个简单 Mod,再回到游戏确认结果。之后逐步开放更多内容。

正文面向游戏开发者,使用英文界面名称。当前示例基于 UE 5.0 Win64 工作流;其他 UE 版本需要匹配的插件二进制、编辑器和 SDK。文中的 MyGame、BP_ModProp 等是教学名称,请替换为自己的工程和资产。

安装插件与确认入口

准备条件

  • 一个能够正常打开和打包的 Windows Unreal Engine 游戏工程,Blueprint 与 C++ 工程均可。
  • 与工程 UE 版本匹配的 EasyMod 插件。C++ 源码使用版本兼容宏,已编译 DLL 仍须与所用引擎匹配。
  • 游戏开发者保留项目正常所需的 UE 构建环境;Mod 作者使用独立编辑器,不需要安装 Visual Studio 或配置原生 C++ 编译环境。

安装步骤

  1. 关闭目标工程,将完整 EasyMod 文件夹放到 <Project>/Plugins/EasyMod。确认 EasyMod.uplugin 直接位于该目录,避免多套一层文件夹。
  2. 打开工程,在 Edit → Plugins 搜索 EasyMod,确认插件已启用。若通过 FAB 安装到引擎,使用对应引擎版本下的插件即可。
  3. 按 UE 提示重新打开工程。出现模块不匹配时,先检查引擎版本和插件包,不要把另一 UE 版本的 DLL 直接复制过来。
  4. 确认工具栏有 Mod settings、Plugin management 和 Documentation。开放规则入口也可从 Tools → EasyMod Developer 打开。
  5. 在 Content Browser 的 Settings 中启用 Show Plugin Content,找到 EasyMod Content / EasyMod / UI。
完成标志:工程能正常打开,Mod settings 能显示 Inheritable、Reference classes、Overridable、Maps、Assets 和 Localization 等页签。

新工程没有预选的公开类、资源或地图。安装插件后先配置你要开放的内容。插件提供可复用管理 UI;游戏地图、玩法框架、输入和启动流程由你的项目决定。

跑通第一个开放功能

下面使用一个游戏已有的 Actor Blueprint 演示最小开放范围。目标是让作者修改指定函数的实现,同时保持游戏原有类和调用方式。

1. 准备一个能观察到结果的游戏功能

  1. 选择已有的 Blueprint,或创建一个 Actor Blueprint,教学名称为 BP_ModProp。
  2. 添加字符串变量 DisplayLabel,再添加返回字符串的 Blueprint 函数 GetDisplayLabel。让函数返回该变量。
  3. 在自己的测试地图放置这个 Actor,并让游戏 UI 或测试逻辑实际调用 GetDisplayLabel 显示结果。编译、保存,先确认原始游戏功能正常。

2. 只开放这次需要的权限

  1. 打开 Mod settings,通过 Add Public Content... 添加该 Blueprint。
  2. 如果作者要创建子类,在 Inheritable 中允许继承;只想允许引用类型时使用 Reference classes。
  3. 本例要修改原函数,因此还要允许该 Blueprint 的 Editable original implementation,并单独将 GetDisplayLabel 设为 Public 和允许重写。继承许可不会自动开放原实现。
  4. 需要作者改默认值时,再开放 DisplayLabel 的相应编辑权限。不要因为本例需要一个函数就开放全部成员。

3. 导出、创作、回到游戏验证

  1. 按SDK 导出章节准备测试环境,点击 Export SDK。
  2. 在匹配的 EasyMod Editor 中加载 SDK,新建 Mod,打开获准修改的 Blueprint,将 GetDisplayLabel 返回值改成可辨认的文字。
  3. 编译、保存并导出 Mod。开发初次验证可选择公开 .umod,避免额外配置私有包加密。
  4. 通过游戏管理 UI 导入并启用 Mod,再冷启动游戏或重新启动 PIE,进入使用该 Actor 的地图。
  5. 确认游戏原有调用显示 Mod 的返回值,再停用 Mod 并重新启动,确认恢复游戏原结果。
完成标志:Mod Editor 只能编辑被批准的实现;游戏启用 Mod 后显示修改结果,停用并重新启动后恢复。这个例子完成后,再开放地图、更多资源或新的创作类型。

新增一个 Mod 子类不会自动替换游戏里所有原类实例。如果你的目标是让玩家选择作者新增的角色或物品,请使用派生类查询与实例化接入自己的选择和生成逻辑。

开放类、继承与实例化

Mod settings 左侧选择公开内容,右侧设置权限。修改会自动保存;新的设置会在 PIE 与 Cook 前准备为运行时配置。需要把变化交给作者时,再导出新版 SDK。

页签或权限适用情况不会自动授予
Inheritable作者需要以游戏类为父类创建 Blueprint。修改原 Blueprint、全部成员写入或原事件重写。
Reference classes允许使用类型、读取公开变量或调用公开函数,但不允许继承。实例化、成员写入和原资产覆盖。
Allow instantiation允许在对应的创建或放置流程中创建该公开类型的实例。抽象类实例化或不符合 UE 实际能力的创建。
Derived queries / Mod descendants让获准的 Mod 后代进入可查询的类型目录。自动导出全部游戏内部子类。

添加内容的三种方式

  1. Add Public Content...:批量选择 Blueprint、原生类、地图和资源。
  2. Add Current Selection:从 Content Browser 的当前资源选择,或从关卡所选 Actor 的类添加。
  3. 在 Content Browser 右键资源,选择 Add to public SDK 后返回 Mod settings 检查权限。此快捷入口会给予默认公开和编辑状态,发布前应逐项收紧。

让游戏使用作者新增的类

在运行时获取 EasyMod Subsystem,等待准备完成后使用 Find Derived Mod Classes,传入游戏批准的基类和目标 Mod ID。把返回的实际 Class 放入你自己的角色选择、物品目录或 Spawn 流程;按对象类型使用正常 UE 创建方式。查询本身不会自动生成或替换对象。

检查:作者可以选择获准父类;Reference classes 中的类不能作为作者新 Blueprint 的父类;未授权的创建和成员访问应被拒绝。

实践案例:通过扩展基类收集 Mod 地图,由游戏查询作者子类并调用约定方法,汇总各个 Mod 的独立配置。

管理变量、函数与事件

选中类或 Blueprint 后,右侧按变量、函数、资源字段和事件分组。使用成员搜索框定位目标,分别检查 Public 与 Editable / Overridable。可编辑或可重写必须同时满足公开条件和 UE 本身的能力。

希望作者做什么需要开放什么
读取变量公开对应属性的读取能力。
运行时写变量明确开放写入,且属性本身允许该访问。
修改获准默认值开放资产/原实现编辑与对应默认值字段。
调用函数公开函数调用能力。
修改现有 Blueprint 函数或事件实现允许原 Blueprint 编辑,并单独批准函数或事件重写。

调用与重写分别配置

一个函数可以允许调用而不允许修改实现。公开父类也不会自动允许改写 BeginPlay、Tick 或所有输入事件。事件图按照具体事件分支授权;共享节点、入口声明、变量与函数签名仍受保护。作者可以修改获准实现,但不能删除、改名或改类型来改变游戏公开接口。

C++ 项目如何准备 API

UFUNCTION(BlueprintCallable, Category = "Game|Mod")
bool GiveItem(FName ItemId, int32 Count);

UFUNCTION(BlueprintNativeEvent, Category = "Game|Mod")
void OnModAction();

BlueprintCallable 提供调用能力;需要 Blueprint 事件能力的可重写接口,应使用合适的 UE 声明。普通 C++ virtual 不会因勾选权限自动变成可覆盖的 Blueprint 事件。以上声明完成后,仍需在 Mod settings 明确批准类和成员。

处理多个 Mod 的修改

不同 Mod 可以修改同一个 Blueprint 的不同函数。同一函数或默认属性发生竞争时,按当前依赖与加载顺序选取首个有效贡献,界面可通过 Get Mod Conflicts 显示获选项。已经创建的对象和已加载的整包资源不会被强制刷新成新内容。

检查:分别测试允许调用、禁止调用、允许重写、禁止重写;修改权限后重新导出 SDK,再用作者编辑器验证。

发布资源与开放地图编辑

公开资源与只读依赖

  1. 在 Assets 中添加要向作者提供的材质、网格、动画、音频或数据资源。
  2. 为每个资源区分 Public 与 Editable;只需要引用的资源保留只读。
  3. 按需要逐字段开放,而不是将整个资源的全部配置交给作者。
  4. 导出 SDK 后检查依赖是否齐全,尤其是运行时按字符串拼接路径加载的内容。

SDK 会收集公开内容所需的依赖。例如公开 Blueprint 引用了一个网格和材质,这些必要依赖可能随 SDK 提供,但默认只读,不会额外获得继承、编辑或覆盖权限。完全无关的游戏内容不会因此自动导出。

地图的三个独立权限

权限层控制内容
地图公开与场景编辑作者能否打开地图,以及是否能移动、删除、替换或新增获准 Actor。
World Settings单独决定 GameMode 等世界设置字段是否能修改。
Level Blueprint单独决定变量、函数及具体事件分支的访问和重写。
  1. 把游戏地图加入 Maps。
  2. 需要作者改场景时,开启 Map / Editable scene。
  3. 继续检查下方 World Settings、字段与 Events,不要把场景编辑理解为全部地图设置和关卡蓝图都开放。
  4. 需要向地图新增游戏 Actor 时,另外开放相应类的实例化。Pawn、HUD 等 GameMode 类默认值在对应类或 Blueprint 权限中设置。
  5. 在作者编辑器修改并导出后,通过游戏冷启动进入目标地图验证。
地图替换按地图及相关烹饪包组处理。切换已经加载的地图或覆盖资源时,应重新启动 PIE 或游戏;不要依赖运行中强制替换已驻留对象。

新增地图也需要在 Mod 中实际发布,并满足游戏自己的选图或入口逻辑。游戏不会因为安装了一个地图包就自动把它设为默认地图。

需要区分可玩地图与测试地图时,参见通过扩展基类收集 Mod 地图:让作者用自己的 DataAsset 显式登记地图,再由游戏汇总到选图界面。

定制并导出游戏 SDK

导出前准备

  1. 在 Project Settings → Game → EasyMod SDK Export 设置唯一且稳定的 GameId,维护 ApiVersion。在 Game → EasyMod 设置游戏自己的 GameVersion;它与 UE 版本不同。
  2. 在 Mod settings 完成公开类、成员、资源、地图和文本选择。需要限制资源根目录时,设置 AllowedSDKAssetRoots。
  3. 在 EnabledAuthoringCapabilities 中选择创作能力,例如 blueprint、widget、animation、mesh、material、data、audio、map。宿主必须同时具备这些能力所需的 Runtime 模块和插件。
  4. 准备作者可离线运行的真实测试环境:检查 bOfflineTestingSupported、OfflineTestGameMode、OfflineTestGameInstance 和需要的 OfflineTestProfiles。动态加载依赖放入 OfflineTestAdditionalAssets;额外行为测试使用 OfflineAutomationTests。
  5. 编译、保存游戏资产。导出会核验宿主能力与原生构建证据;若提示缺少 RuntimeCapabilityProofs 或测试依赖,补充当前游戏/服务器的真实构建和运行证据后重试。

通过界面导出

  1. 打开 Mod settings,点击右上角 Export SDK。
  2. 工具会保存工程变化,准备 Mod 配置并后台执行导出。查看独立进度窗口;需要时使用 View Progress 返回进度,或使用 Cancel Export 取消。
  3. 等待明确成功结果。默认文件位于工程根目录,名称为 <GameProjectName>.emsdk,例如 MyGame.emsdk。
  4. 把成功导出的 SDK 交给匹配的 Mod Editor 测试,再交付作者。失败时先修复进度窗口指出的具体问题,不要把中间目录当作 SDK 发出去。

当前界面使用游戏开发者实际的 UE 引擎进行发布验证,不要求为了点击导出先安装作者用的 Mod Editor。作者随后仍需获取匹配该 SDK 的独立编辑器。旧版 EasyModSDK.emsdk 文件名保留兼容,更新时以本次导出的实际文件为准。

命令行导出(可选)

& ".\Plugins\EasyMod\Tools\BuildProjectSDK.ps1" -Project ".\MyGame.uproject" -EngineRoot "F:\UE_5.0" -OutputFile ".\MyGame.emsdk"

这个例子使用游戏开发者引擎验证。只有显式开放项目原生 Runtime 模块时,才需要按照脚本的 ApiModules / ApiPlugins 和匹配的构建环境准备批准模块;只开放项目 Blueprint 不等于必须把游戏原生源码交给作者。

完成标志:导出报告成功,工程根目录生成完整单文件 SDK;作者编辑器可以加载,公开内容和离线测试环境符合预期。

向社区提供专用 Mod Editor

EasyMod Editor 是提供给 Mod 作者的独立编辑器。游戏开发者在正常 UE 工程里决定开放规则;作者在编辑器中使用你的 SDK,不需要购买或安装 FAB 的开发者源码插件。

准备编辑器与 SDK

  1. 通过 Plugin management 中的编辑器资源入口,或你提供的正式下载渠道,获取与游戏 SDK 匹配的安装版。
  2. 开发者可在 Project Settings → Game → EasyMod 配置游戏显示名称、图标和发行方式;编辑器的推荐与实际安装选择由安装/资源管理流程维护。
  3. 向作者交付 SDK 和编辑器获取方式。可以随游戏附带,也可以分别下载,具体见游戏打包。
  4. 用作者实际收到的文件测试一次。不要用只存在于开发机的私有工程路径代替交付验证。

作者的基本工作流程

  1. 启动 EasyModEditor.exe,选择游戏 SDK,或选择能自动找到 SDK 的游戏目录。
  2. 新建 Mod,填写名称、作者等信息,选择自己的 .umod 保存位置。
  3. 在 Content Browser 中创建自己的 Blueprint、Widget、资源或地图。SDK 内容按照开发者权限显示为可继承、只读或可覆盖。
  4. 使用原生 Blueprint 的 Compile、Save 和获准的 PIE/Simulate 测试。编辑的是每个 Mod 独立的工作区,不会直接改写共享 SDK。
  5. 从 Mod tools → Build and export... 导出。通过 Export format 选择公开源码包 .umod 或私有运行包 .emod;重复导出可使用 Export again。
  6. 将导出的包导入游戏,验证真实加载与玩法效果。

作者不需要配置 Visual Studio 或原生 C++ 工具链。独立编辑器支持的具体资源类型仍由游戏 SDK 声明;安装编辑器并不会绕过游戏权限。公开 SDK 内的原类声明、成员签名和身份不能随意改名或删除。

交付检查:在独立安装中完成加载 SDK、新建 Mod、编译保存、测试、导出和游戏导入。SDK 或编辑器发生更新后重新走一次。

开放游戏文本与翻译 Mod

翻译 Mod 面向游戏内容本地化。你决定公开哪些原文、文本身份和语言,作者在自己的 Mod 中维护译文,不会因此修改编辑器的界面语言。

开发者配置

  1. 在 Project Settings → Game → EasyMod → Localization 检查 Publish Game Localization Text。关闭后,新导出的 SDK 不提供对应游戏原文。
  2. 在 Mod settings → Localization 配置源语言和可收集语言。已有 UE Localization Target 时沿用真实源语言;没有 Target 的项目设置实际的 Game source language。
  3. 通过 Text Publishing Rules 按 Namespace、Key 控制公开范围。未设置逐条规则的文本遵循默认公开规则;发布前检查不应提供的内容。
  4. 先完成游戏本身的 UE 文本收集。C++ 文本需要真实的 localization manifest,不能期待插件从任意字符串代码中自动猜出所有本地化文本。
  5. 重新导出 SDK,将新的公开文本与语言配置交给作者。

作者流程

  1. 在 Mod Editor 新建独立翻译 Mod,选择目标语言。
  2. 在 Localization 工作台编辑、收集或导入/导出支持的翻译文件,预览目标语言。
  3. 保持 Namespace、Key 和原文身份与 SDK 一致,保存后导出公开 .umod。
  4. 在游戏中安装并启用,切换到目标游戏语言,确认翻译应用于实际 UI 和玩法文本。

停用译文 Mod 后,游戏恢复原文或其他有效翻译层。SDK 更新导致原文身份变化时,先重新检查和匹配译文,再发布新版 Mod。

检查:被允许的文本可供翻译,被禁止的身份或语言不会进入 SDK;目标语言生效,停用后能恢复正确文本。

接入本地 Mod 加载与启动

本地 Mod 功能可以使用你现有的 GameMode 和 GameInstance。按项目需要选择自动加载或手动加载;自动联机同步是另外一条接入路径。

自动加载

Project Settings → Game → EasyMod → Mods → Load Mods on Startup 默认开启。存在有效 Mod 配置后,EasyMod 会发现、验证并准备本地 Mods。若游戏玩法必须依赖这些内容,先在轻量菜单/加载地图等待准备完成,再进入实际玩法地图。

手动控制加载时机

  1. 关闭 Load Mods on Startup。
  2. 在 GameInstance、菜单或加载流程中获取 EasyMod Subsystem。
  3. 先绑定 On Ready 和 On Startup Progress,再调用 Load Mods。
  4. 通常保持 Activate When Ready 为 true。需要自己安排入口时设为 false,收到准备完成后调用 Activate Mods。
  5. 调用立即失败时读取 Get Last Error;过程中显示 Get Startup Progress,检查全局错误和被拒绝的个别 Mod。
  6. 需要停止入口生命周期时调用 Stop Mod Entries;用 Are Mod Entries Active 检查当前世界是否已经激活入口。
GameInstance / Loading Menu
  -> Get EasyMod Subsystem
  -> Bind On Ready + On Startup Progress
  -> Load Mods (Activate When Ready = true)
  -> Inspect status and errors
  -> Continue to gameplay when your readiness conditions pass
Load Mods 返回 true 表示异步请求已被接受,不代表所有 Mod 都已经加载成功。On Ready 表示准备阶段就绪;还要检查实际 Mod 状态与入口激活结果。

何时使用 EasyMod 的基类

EasyModGameModeBase 提供标准引擎下的 Actor BeginPlay 就绪屏障和会话准入;EasyModGameInstance 额外协调首图等待与进程重启恢复。普通 GameMode 不会自动获得这些启动屏障。接入现有框架时,按需要保留或实现等效的等待与恢复逻辑。

Stop Mod Entries 负责结束入口及其拥有对象,不等同于卸载所有 Pak 或撤销所有补丁。切换已加载的内容仍需安全菜单或重启。地图/资源覆盖应在目标地图和资源加载前准备。

需要在加载后发现并执行游戏自己的扩展类时,参见使用技巧:通过扩展基类收集 Mod 地图,包含状态筛选、重复通知和作用域清理。

接入游戏内 Mod 管理界面

使用现成 UMG 面板

  1. 在 Content Browser 启用 Show Plugin Content,打开 EasyMod Content / EasyMod / UI / Widgets。
  2. 找到 WBP_EasyModDemoPanel。在你的 PlayerController 或菜单 Widget 中创建它,设置合适的 Owning Player,并加入 Viewport 或自己的容器。
  3. 根据界面流程设置鼠标显示、键盘/手柄焦点和 Input Mode。关闭面板时恢复游戏原有输入方式。
  4. 参考 UI/Examples/WBP_DemoHUD 与 WBP_DemoMenu 的嵌入方式,按自己的游戏改外观和打开时机。

完整面板包路径为 /EasyMod/EasyMod/UI/Widgets/WBP_EasyModDemoPanel。单行条目为 WBP_EasyModModRow,拖拽与辅助逻辑位于 UI/Blueprints。这些 UI 可以嵌入现有菜单,不要求使用某个固定的游戏地图或 GameMode。

需要自定义 UI 时

操作主要接口界面应如何反馈
显示列表Get Installed Mods / On Changed显示状态、版本、错误及是否需要重启。
导入Choose Mod Archive → Import Mod只在安装验证成功后刷新;失败显示 Out Error。
启用/停用Request Set Mod Enabled / On Enable Request Completed等待实际完成结果,不把点击状态当作已生效。
修改顺序Set Mod Order使用返回的 Applied Order;依赖可能影响最终顺序。
冲突提示Get Mod Conflicts显示竞争项和当前获选 Mod。
启动创作工具Install or Update Mod Editor / Launch Mod Editor检查可用性并展示返回错误。

运行时变更需要满足游戏的安全地图与加载状态要求。同步联机会话中的有效 Mod 集合由服务器控制,不能直接用普通本地开关修改。

检查:导入成功/失败、依赖联动、顺序变化、需要重启以及编辑器未安装等情况,都能在界面上得到明确反馈。

接入 Mod 存档与稳定内容 ID

如果 Mods 会创建需要长期保存的物品、进度或数据,可以使用 EasyMod Save Subsystem。它按游戏身份、Mod GUID 和 Slot 隔离记录,并在读取前检查依赖与数据版本。

基本接入

  1. 定义自己的 USaveGame/SaveGame Blueprint 数据对象,只把需要序列化的字段标记为 SaveGame。
  2. 确定所属 Mod GUID、Slot 名称与 Data Version。
  3. 保存对象引用对应的 Mod 内容前,使用运行时 Register Mod Content 注册稳定 Key,并记录返回的稳定内容 ID。
  4. 简单同步流程使用 Save Mod Data / Load Mod Data。在游戏交互流程中需要后台 I/O 时,优先使用 Request Save Mod Data / Request Load Mod Data。
  5. 异步请求保存返回的 Request ID,绑定 On Save Request Completed、On Save Request Failed 和取消事件;不要在请求刚返回时就显示保存成功。

缺少 Mod 和数据升级

必要 Mod、稳定内容 ID 或数据版本不匹配时,默认拒绝读取并保留原记录。可选降级需要游戏在 OptionalSaveModGuids 中明确允许,同时保存方声明该依赖可选。拥有该存档的 Mod 本身不能当作可选内容。

需要迁移时,提供 EasyModSaveMigration 子类,实现 Migrate,在 Mod 激活时通过 Register Migration 注册。迁移失败应保留原记录,并向玩家提供可理解的错误。

接入自己的存储系统

游戏可以实现宿主拥有的 EasyModSaveStorageProvider,通过 Set Host Storage Provider 接入其他存储。异步提供器通过对应 Request ID 完成请求,取消后不能再提交旧结果。

联机客户端不能保存或加载权威 Mod 存档。权威保存应由服务器/宿主执行。要让 Mod 作者调用这些 API,仍需在游戏的公开规则中批准相关类和成员,再重新导出 SDK。

配置自动联机 Mod 同步

自动同步让客户端获取服务器要求的 Mod 包,完成校验,并以匹配的版本和顺序进入游戏。它支持缓存复用和断点续传,也保留玩家自己的单机 Mod 偏好。

接入自动会话

  1. 先确认相同 Mod 在打包游戏的本地流程中可以加载。
  2. 让实际运行地图使用的 GameMode 继承 EasyModGameModeBase,或在你的框架中实现对应的准入与就绪适配。检查地图自己的 World Settings,避免它覆盖项目默认 GameMode 后没有使用适配器。
  3. 保持 Automatic Mod Session Sync 开启。当前实现根据实际 GameMode 判断自动集成;普通 GameMode 继续使用本地 Mods 和正常网络,不会因安装插件自动接管所有会话。
  4. 接入首图与恢复流程。可以使用 EasyModGameInstance,或在现有 GameInstance 中保留等效的就绪等待和重启恢复处理。
  5. 通过 EasyMod Session Subsystem 在加入前调用 Prepare Mod Session(GameUrl, SyncPort),绑定进度与失败事件。
  6. On Completed 后检查阶段为 Ready,再用 Get Prepared Connection Url 连接。不要把下载完成直接当作游戏准入成功。
Server Mod Set
  -> Download missing packages / Reuse verified cache
  -> Verify packages, compatibility and dependencies
  -> Apply matching versions and load order
  -> Connect with the prepared URL

端口与传输设置

设置作用
Mod Sync Port Offset同步使用独立 TCP 端口,默认为游戏端口 + 2。游戏端口为 7777 时,同步端口为 7779;服务器网络需允许对应端口。
Max Session Download MiB限制本次会话所需 Mod 总下载量。
Mod Download Rate KiB / Mod Upload Rate KiB分别限制下载和上传速率;0 表示不限速。

服务器改集与退出

服务器先调用 Begin Server Mod Change,等待玩家离开玩法并进入安全地图。完成启停与顺序操作后,调用 Complete Server Mod Change 发布新集合,客户端重新同步并恢复连接。离服调用 Leave Mod Session。需要重启时使用恢复流程;若游戏必须先存档,处理 On Before Session Recovery,保存完成后以相同 Request ID 调用 Complete Mod Session Recovery。

Mod 包与配置同步不等于自动实现全部玩法复制。Actor replication、RPC 权限、权威存档和游戏规则仍由宿主游戏设计。用独立客户端/服务器进程验证监听服、专用服务器、缺包、断点续传、改集和重启恢复。

理解 .umod、.emod 与信任边界

格式包含内容适合用途
.umod可编辑源码;一般内容 Mod 正式导出后还包含未加密的 runtime.zip。公开创作、协作、继续编辑和初期验证。
.emod加密运行内容,不附作者源码。希望发布运行包并保留本地源码的作者。

作者保存源码后,需要重新 Build and export,游戏才会得到与源码匹配的新运行载荷。仅保存过的工作文件不等于已完成游戏运行包。相同 GUID 的源码/私有变体可按管理流程选择;不要把两者当作两个独立内容同时启用。

接入私有运行包

  1. 由游戏开发者为自己的游戏准备 RSA-3072 私有包密钥。
  2. 在 Project Settings → Game → EasyMod → Private Mods 配置私钥,派生公钥进入 SDK。
  3. 重新导出 SDK,再让作者以 Encrypted package (.emod) 导出。
  4. 按正式游戏打包流程准备宿主密钥模块并检查发布配置。游戏私钥和宿主专用模块不能进入公共 SDK、通用 Mod Editor 或文档附件。

公开 .umod 不需要这套运行内容加密密钥。独立翻译 Mod 使用公开 .umod,不需要 Cook/Pak,当前不使用私有 .emod 译文载荷。

验证与安全

安装和加载会检查签名、摘要、游戏身份、兼容性、权限与依赖。签名说明包内容及作者身份的一致性,不能保证作者逻辑安全。Blueprint 与资产在引擎进程中执行;权限控制与校验不构成恶意代码沙箱或反作弊保证。只向玩家推荐可信来源,并在服务器端校验重要玩法请求。

私有包加密用于运行内容分发,不能承诺绝对防提取。游戏客户端需要持有解密能力,产品和授权说明应如实描述这一点。

打包游戏、SDK 与编辑器

先确认游戏运行配置

  1. 使用支持的 Win64 Pak 工作流:启用 Pak,关闭 Mod 载荷流程中的 IoStore。先验证 Development 包,再验证实际发行配置。
  2. 保证正常启动地图、玩法地图及需要的公开内容按游戏自己的打包配置被 Cook。
  3. 配置 SafeMenuMap,需要等待首图准备时配置轻量 StartupLoadingMap。
  4. 保存 Mod settings,重新导出当前 SDK。运行时配置由插件在 PIE/Cook 前自动准备;不需要手动创建或编辑内部 Contract 资产。

选择是否随游戏附带创作工具

在 Project Settings → Game → EasyMod → Game Packaging 中,分别设置 Bundle SDK with Game 和 Bundle Mod Editor with Game。

SDKEditor发行方式
开启开启随游戏附带当前 SDK 与已安装、匹配的编辑器。
开启关闭附带 SDK,作者另外下载安装编辑器。
关闭开启附带选中的编辑器,SDK 通过其他渠道提供。
关闭关闭游戏正常发行,SDK 与编辑器分别分发。

附带编辑器复制的是当前工程管理的已安装发行版,不会在游戏打包时自动下载或重新构建完整编辑器。开启组件缺失或 SDK/安装版不匹配时,先修复报告的问题。当前编辑器随游戏附带的详细归档流程以已验证的 UE 5.0 Win64 配置为基础,其他引擎版本应单独验收。

使用正常 UE 打包入口

通过 Platforms → Windows → Package Project 进行打包。以 MyGame 为例,SDK 与编辑器作为独立文件放在发行包的项目子目录中,实际名称以本次导出和打包日志为准:

Windows/
  MyGame.exe
  MyGame/
    Binaries/Win64/...
    MyGame.emsdk
    EasyModEditor/...
  Mods/

SDK 和编辑器不是游戏 Pak 中的普通游戏资产。只给玩家运行的游戏可以关闭这两个附带选项,再通过你的网站给创作者提供下载。

发行前:在目标目录冷启动游戏,分别验证无 Mod、公开 Mod、私有 Mod、停用/排序/重启,再用实际交付 SDK 和编辑器完成一次作者流程。需要联机时额外验证独立服务器与客户端。

维护 SDK、API 与编辑器版本

什么时候需要重新导出 SDK

  • 新增、删除或调整公开类、成员、资源、地图或权限。
  • 修改公开资源内容、输入映射、本地化文本或离线测试环境。
  • 升级游戏公开 API、引擎发行或影响作者环境的原生模块。

保持 GameId 稳定,分别维护游戏版本和 API 版本。新增内容与删除或更改现有成员签名对旧 Mod 的影响不同;不要只改显示版本号就假定兼容。

推荐更新流程

  1. 保存游戏资源和 Mod settings,点击 Export SDK。
  2. 等待发布验证完成,再替换对外下载的当前 SDK。失败时保留之前可用的 SDK。
  3. 在作者编辑器中打开已有 Mod,观察 SDK 更新与权限变化。编辑器会在合适时机同步对应 Mod 的工作区;PIE 或构建期间的同步会等待操作结束。
  4. 原生 DLL、模块描述或工程描述变化时,按提示关闭并重新打开 Mod Editor。
  5. 重新编译、测试并导出受影响的 Mods,再用实际游戏包验证。
  6. 发布简短的迁移说明:新增内容、移除内容、需要作者重新导出的原因和目标版本。

SDK 更新不会授权作者修改更多内容;新的权限仍由开发者配置。作者已经选择的本地覆盖和未保存修改也需要保留与检查,不能把共享 SDK 更新理解为覆盖全部作者作品。

开放原生模块时多检查一步

只有批准的项目 Runtime 模块/API 插件才进入 SDK。批准 DLL 或插件需要审查它的实现、配置和依赖,并满足作者编辑器的二进制兼容要求。不要把游戏私有模块、宿主密钥或未审查的配置顺手放进 SDK。

回归检查:至少保留一个旧 Mod 和一个新 Mod,分别测试允许的兼容路径、应拒绝的签名/权限变化,以及 SDK 更新后作者继续创作的流程。

通过扩展基类收集 Mod 地图

本章收集可以组合使用的接入技巧,后续案例会继续作为独立页面补充。第一篇用“游戏定义扩展基类,Mod 创建子类,加载后由游戏统一发现和调用”的方式,把各个 Mod 自带的可玩地图加入选图界面。同样的结构也适合物品、角色或任务目录。

分工:EasyMod 已提供派生类查询、状态和生命周期作用域;本例中的扩展基类、地图配置和管理器由游戏自己实现。无需修改 Mod Editor,也无需编辑隐藏的托管入口;游戏的公开接口变化后,仍需重新导出 SDK。

整体结构

Game
|
+-- BP_ModExtension (game-defined base class)
|   +-- Initialize(Context, ModId)
|   +-- GetPlayableMaps()
|   +-- Shutdown()
|
+-- BP_ModExtensionManager
    +-- Find Derived Mod Classes
    +-- Construct instances and call Initialize
    +-- Map registry: ModId + MapId

Mod A                         Mod B
+-- BP_IslandExtension        +-- BP_ArenaExtension
    +-- DA_IslandMaps             +-- DA_ArenaMaps
        +-- Island.umap              +-- Arena.umap

每个 Mod 可以有一个或多个扩展子类。游戏查询到实际的 Class 后创建对象,调用基类约定的方法;作者重写的方法由正常的 Blueprint 多态分派执行。仅创建子类不会让它自动执行,下面的管理器负责这一步。

1. 在游戏中准备扩展接口

创建可继承的 UObject Blueprint 基类 BP_ModExtension。示例接口如下;这些名称都是游戏自己的教学接口,不是插件新增节点。

接口约定
Initialize(Context, ModId)保存管理器引用和所属 Mod ID,需要时执行初始化。通过 Context 访问游戏功能,不依赖普通 UObject 自动获得世界上下文。
GetPlayableMaps()返回地图配置中的数组;可以在基类实现默认读取,也可由作者重写。
Shutdown()撤销本实例创建的游戏注册、监听与运行逻辑;允许重复清理而不产生副作用。

再创建当前世界使用的 Actor 管理器 BP_ModExtensionManager,在游戏启动流程中放置或生成一个。由它保存扩展对象引用、调用接口并维护地图列表。不要在 Class Default Object 上调用带运行逻辑的初始化。

2. 用 DataAsset 决定哪些地图进入列表

创建游戏结构体 ST_ModMapInfo 和配置类 PDA_ModMapList(PrimaryDataAsset)。配置类保存地图数组,扩展基类保存该配置的引用。

字段类型用途
MapIdName同一个 Mod 内稳定且唯一的地图标识。
DisplayNameText选图界面的显示名称。
MapWorld Soft Object Reference实际地图的软引用,避免打开列表时加载所有世界。

Mod 作者在自己的目录创建配置实例,例如 DA_IslandMaps,只填入想向玩家开放的地图,再赋给自己的扩展子类。测试地图不放入数组即可;需要更多分类时,再增加游戏自己的标签或分类字段。

每个 Mod 提交自己的配置:让所有 Mod 修改同一份游戏 DataAsset 不会自动合并数组。这里由游戏读取不同路径的配置,再汇总到运行时列表。游戏定义的 RegisterMap 使用 ModId + MapId 作为键;重复键需要拒绝并报告,不能悄悄覆盖其他记录。

3. 在 SDK 中开放必要内容

  1. 在 Mod settings 的类权限中公开扩展基类,允许继承、Mod 后代、派生查询,以及该 UObject 创建流程需要的实例化权限。
  2. 开放结构体、配置类型及地图数组等必要字段,允许作者创建和填写自己的配置资源;原游戏配置仍可保留只读。
  3. 在成员权限中批准所需的函数调用、函数重写和默认值编辑。公开父类不会自动开放所有成员。
  4. 如果作者的初始化需要调用管理器,将管理器类型作为可引用类型公开,只开放需要的函数。按本例内容启用 Blueprint、数据和地图创作能力,然后导出更新后的 SDK。

作者在现有 Mod Editor 中创建扩展子类、独立配置和地图。扩展基类的默认读取逻辑足够时,只配置默认值即可;需要自定义行为时再重写方法。这不涉及替换 EasyMod 管理的入口。

4. 加载后查询子类并执行

管理器先绑定 On Ready 和 On Changed,然后立刻刷新一次,兼容管理器创建前 Mod 已准备好的情况。两种通知都用于触发重新检查;实际按 Get Installed Mods 返回的启用标志和状态筛选。

BP_ModExtensionManager: BeginPlay
  -> Get EasyMod Subsystem
  -> Bind On Ready + On Changed
  -> RefreshExtensions()

RefreshExtensions()  // game-defined, guarded against re-entry
  -> Get Installed Mods
  -> For each enabled Mod with State == Active:
       Scope = Get Mod Execution Scope(ModId)
       Classes = Find Derived Mod Classes(
           BP_ModExtension, ModId, Recursive=true, IncludeAbstract=false)
       For each Class:
         Key = (ModId, ClassPath)
         If Key is not registered and Scope is valid:
           Construct Object from Class (Outer = this manager)
           Cast to BP_ModExtension
           Store instance; register with Scope
           Initialize(Context = this manager, ModId)
           GetPlayableMaps() -> RegisterMap(ModId, MapInfo)
  -> Remove stale instances and their map records

Scope.OnCancelled / Manager.EndPlay
  -> Shutdown() once per instance
  -> Remove map records; release references; unbind delegates

Find Derived Mod Classes 只返回当前已挂载、通过目录及继承检查的 Mod 类,还要求游戏基类允许派生查询。按每个 Mod ID 查询可保留来源信息;递归查询支持多层继承,排除抽象类可避免无法实例化的结果。

准备完成与运行初始化分开:On Ready 在原生入口激活之前广播。若只是读取地图配置,可在已启用 Mod 的 Mounted 阶段处理;本例包含运行初始化,统一等到 Active,并取得当前世界的有效 Get Mod Execution Scope。不能只收到准备通知就假定所有 Mod 已激活。

用 ModId + ClassPath 记录已创建的扩展对象并保留强引用。再次通知时只补充缺失实例,移除不再有效的实例;刷新期间出现新通知时合并为一次后续刷新,避免重复初始化和回调重入。管理器读取 GetPlayableMaps() 的结果后调用 RegisterMap 汇总。

5. 配对处理停用和切换地图

通过当前 Mod 的作用域调用 Register Owned Object,登记新建的扩展实例;登记失败就放弃本次初始化。作用域的 On Cancelled 通知和管理器的 EndPlay 都进入同一套幂等清理流程:先调用 Shutdown(),再移除所属地图记录、解除监听并释放引用。

作用域取消时会先发出取消通知,再清理所拥有的对象;它不会自动知道游戏自定义的地图列表。管理器必须自行撤销这些记录。切换世界后重新创建管理器和扩展实例;GameInstance 可以保存软地图信息,不要长期保存上一世界的扩展对象。

6. 接到选图界面并验证发布结果

UI 读取汇总后的地图列表,保留每一项的 Mod ID 和地图标识。单机选中后可将软地图引用交给 Open Level (by Object Reference);联机由服务器决定切图,并结合自动 Mod 同步确保参与者准备好同一套内容。

地图仍按现有地图发布和打包流程进入 Mod。软引用用于配置和加载,并不等于已验证 Cook 完成;用导出的实际运行包检查地图及依赖是否包含在内。

验收这个技巧:
  • 安装两个 Mod,各自配置一张可玩地图和一张未登记的测试地图;列表只出现两张可玩地图。
  • 两个 Mod 使用相同的局部地图标识仍能共存;同一 Mod 内的重复键会被明确报告。
  • 重复状态通知不会重复添加;停用、重启和切换地图后没有重复条目或旧世界引用。
  • 使用实际发布包进入每张登记地图;需要联机时再验证服务端切图与客户端准备流程。

后续可复用这一机制,增加物品、角色、任务等使用技巧:先由游戏定义稳定的接口和数据,再让各个 Mod 独立提供内容,由游戏控制发现、执行、汇总与清理。

Blueprint API

Blueprint API

蓝图 API

按实际源码列出 4 个主要接口类的 63 个 Blueprint 函数。每张卡片包含 C++ 名称、输入参数、返回值、用途、场景与节点预览。点击“复制节点”后可粘贴到启用了当前版本 EasyMod 的 Blueprint 图表,再连接 Target 和其他引脚。

前三个 Subsystem 从 GameInstance 获取;EasyMod UI Blueprint Library 的静态函数直接搜索调用。蓝图节点标题和引脚保留英文,中文仅翻译说明。

启动与加载 9

加载 Mods

C++LoadMods返回值bool参数bool bActivateWhenReady = true用途排队执行验证、挂载及补丁准备,可选择就绪后自动激活入口。场景手动加载时先绑定 OnStartupProgress 和 OnReady;返回值表示请求是否被接受。

是否已准备就绪

C++IsReady返回值bool参数无输入参数用途检查全局 Mod 准备状态是否为 Ready。场景准备完成后再开放依赖 Mod 内容的操作;这不代表所有 Mod 都已成功激活。

获取启动进度

C++GetStartupProgress返回值FEasyModStartupProgress参数无输入参数用途读取当前准备状态、阶段、进度和错误信息。场景在启动页展示验证、挂载和准备结果。

激活 Mod 入口

C++ActivateMods返回值bool参数无输入参数用途为当前世界启动已经准备好的 Mod 入口。场景在游戏自己的准备流程结束后启动 Mod;普通 GameMode 也能使用。

停止 Mod 入口

C++StopModEntries返回值bool参数无输入参数用途结束入口生命周期及其管理的对象。场景返回菜单或停止玩法内容;已挂载的包和已应用补丁仍可能需要重启才能移除。

入口是否已激活

C++AreModEntriesActive返回值bool参数无输入参数用途查询当前 Mod 入口的激活状态。场景让管理界面区分准备就绪与入口运行状态。

刷新 Mod 发现流程

C++DiscoverMods返回值bool参数无输入参数用途排队刷新元数据、验证和分阶段挂载。场景需要重新扫描时发起请求;用 OnReady 接收准备完成通知。

重试启动准备

C++RetryStartup返回值bool参数无输入参数用途请求重新执行失败后的 Mod 准备流程。场景显示错误后让玩家重试,并继续监听启动进度。

取消启动准备

C++CancelStartup返回值void参数无输入参数用途取消当前启动准备操作。场景玩家取消加载时结束当前请求,并显示最终状态。

Mod 管理 9

获取已安装 Mods

C++GetInstalledMods返回值TArray<FEasyModInfo>参数无输入参数用途返回当前已安装 Mod 的信息列表。场景创建 Mod 列表,并在 OnChanged 后刷新。

导入 Mod 包

C++ImportMod返回值bool
OutModId: FString
OutError: FString
参数const FString& ArchivePath用途验证并安装本地 .umod 或 .emod 到当前游戏的 Mods 目录。场景通过文件选择器获得 ArchivePath,显示安装结果;新安装内容在重启后生效。

请求启用或停用 Mod

C++RequestSetModEnabled返回值bool参数const FString& ModId
bool bEnabled
用途提交一个逻辑启停请求,并处理相关依赖 Mods。场景勾选列表项后等待 OnEnableRequestCompleted,不能只用立即返回值判断结果。

设置 Mod 顺序

C++SetModOrder返回值bool
OutAppliedOrder: TArray<FString>
OutError: FString
参数const TArray<FString>& RequestedOrder用途提交排序请求,并返回依赖优先的实际顺序和错误。场景拖放结束后一次提交,再按 OutAppliedOrder 刷新列表。

获取 Mod 冲突

C++GetModConflicts返回值TArray<FEasyModConflictInfo>参数无输入参数用途读取运行时汇总的内容贡献冲突信息。场景在管理界面展示冲突来源与当前选择。

选择源码或私有包变体

C++SelectVariant返回值bool
OutError: FString
参数FGuid ModGuid
bool bUseSourceProject
用途为同一 Mod GUID 选择作者工程或私有包变体。场景切换工作区与发行包时显示 OutError;内容已加载时可能需要重启。

Mod 是否已登记

C++IsModRegistered返回值bool参数const FString& ModId用途检查指定 Mod ID 是否存在于当前登记记录。场景在执行依赖该 Mod 的操作前检查记录。

获取 Mods 目录

C++GetModsDirectory返回值FString参数无输入参数用途返回当前游戏使用的本地 Mods 目录。场景向玩家显示安装位置,或用于打开目录按钮。

获取最近错误

C++GetLastError返回值FString参数无输入参数用途读取运行时记录的最近错误文本。场景请求失败后补充诊断;异步操作仍应优先使用对应完成事件。

公开内容与稳定 ID 12

获取公开类型

C++GetPublicTypes返回值TArray<FEasyModTypeInfo>参数无输入参数用途读取 SDK 与运行时登记的公开类型信息。场景构建公开内容浏览器,或查看当前游戏允许访问的类型。

查找派生类型信息

C++FindDerivedTypes返回值TArray<FEasyModTypeInfo>参数const FString& BaseClassPath
bool bRecursive = true
bool bIncludeAbstract = false
用途按基类路径查找派生类型,可控制递归与抽象类筛选。场景展示类型目录;需要实际 UClass 时使用 FindDerivedModClasses。

按 Mod 查找派生类型

C++FindDerivedTypesForMod返回值TArray<FEasyModTypeInfo>参数const FString& BaseClassPath
const FString& ModId
bool bRecursive = true
bool bIncludeAbstract = false
用途在指定 Mod 范围内查询某个基类的派生类型。场景为选中的 Mod 展示可用内容类型。

查找 Mod 派生类

C++FindDerivedModClasses返回值TArray<UClass*>参数UClass* BaseClass
const FString& ModId
bool bRecursive = true
bool bIncludeAbstract = false
用途验证已挂载包及实际继承关系后返回匹配的 UClass。场景从批准的基类查询作者扩展,再由游戏决定如何生成实例。

运行配置是否已加载

C++HasContract返回值bool参数无输入参数用途检查内部生成的运行时公开规则配置是否已加载。场景排查配置加载问题;正常接入由插件自动生成配置,无需手工编辑内部资产。

获取 Mod 执行作用域

C++GetModExecutionScope返回值UEasyModExecutionScope*参数const FString& ModId用途取得指定 Mod 的执行作用域对象。场景需要与 Mod 生命周期关联的操作时使用对应作用域。

注册稳定内容 ID

C++RegisterModContent返回值bool
StableId: FString
Error: FString
参数const FString& ModId
const FString& Key
FSoftObjectPath Asset
int32 DataVersion
用途用 Mod ID、作者 Key、资源和数据版本登记内容,输出稳定 ID。场景存档记录物品或其他内容时保留稳定标识,减少资源改名和移动带来的影响。

内容是否已注册

C++IsContentRegistered返回值bool参数const FString& StableId用途检查稳定内容 ID 是否已登记。场景读取存档后判断引用内容当前是否可用。

获取已注册内容信息

C++GetRegisteredContentInfo返回值bool
ModGuid: FGuid
DataVersion: int32
参数const FString& StableId用途按稳定 ID 获取 Mod GUID 和内容数据版本。场景执行内容兼容性检查或为存档显示来源。

解析 Mod 内容

C++ResolveModContent返回值UObject*参数const FString& StableId用途把已登记的稳定内容 ID 解析为 UObject。场景读取存档内容后获取实际资源,并检查返回对象是否有效。

解析公开对象

C++ResolveExposedObject返回值UObject*参数FEasyModObjectHandle Handle用途把公开对象句柄解析为当前可用的 UObject。场景使用宿主已公开的实例,并在使用前检查有效性。

查找公开对象

C++FindExposedObjects返回值TArray<FEasyModObjectHandle>参数const FString& PublicClassPath
FName Channel
用途按公开类路径和通道查找已公开实例的句柄。场景定位游戏通过公开机制提供的对象集合。

联机同步与恢复 15

自动会话同步是否启用

C++IsAutomaticModSessionSyncEnabled返回值bool参数无输入参数用途查询当前实际 GameMode 和配置是否启用自动联机 Mod 同步。场景区分普通本地 Mod 流程与自动网络准入流程。

准备 Mod 会话

C++PrepareModSession返回值bool参数const FString& GameUrl
int32 SyncPort = 0
用途根据游戏连接地址和同步端口准备服务器要求的 Mod 集合。场景连接前下载、校验并应用会话配置;准备完成后使用返回的已准备连接地址。

取消会话准备

C++CancelModSession返回值void参数无输入参数用途取消当前 Mod 会话准备操作。场景玩家离开下载界面时取消,并处理 OnCancelled。

启动 Mod 同步服务

C++StartModSessionServer返回值bool参数int32 SyncPort = 0用途按指定端口启动当前服务器的 Mod 包同步服务。场景手动管理同步服务;自动接入通常由实际 GameMode 流程协调。

停止 Mod 同步服务

C++StopModSessionServer返回值void参数无输入参数用途停止当前服务器的 Mod 包同步服务。场景关闭服务器或结束手动托管的同步流程。

离开 Mod 会话

C++LeaveModSession返回值bool
Error: FString
参数无输入参数用途请求结束当前同步会话并恢复本地配置流程。场景退出服务器时调用,并显示错误或后续重启要求。

开始服务器 Mod 变更

C++BeginServerModChange返回值bool
Error: FString
参数无输入参数用途停止新准入,请求玩家离开玩法,并让宿主进入配置的安全大厅。场景服务器修改 Mod 集合前先调用,等待安全流程完成。

完成服务器 Mod 变更

C++CompleteServerModChange返回值bool
Error: FString
参数无输入参数用途在 Mod 操作完成后,从安全大厅发布新的完整集合。场景完成启停与排序后提交,让后续连接按新版本集合重新准备。

恢复 Mod 会话

C++RecoverModSession返回值bool
Error: FString
参数bool bRestoreSinglePlayer用途请求恢复会话,可选择恢复单人配置。场景遇到需要重启的配置变化时使用;绑定恢复前事件的宿主必须完成或取消请求。

完成恢复前确认

C++CompleteModSessionRecovery返回值bool
Error: FString
参数FGuid RequestId
bool bProceed
用途使用 RequestId 批准或取消仍在等待的会话恢复。场景宿主完成必要存档后响应 OnBeforeSessionRecovery,避免批准已过期请求。

获取待处理恢复请求

C++GetPendingRecoveryRequest返回值FGuid参数无输入参数用途读取当前等待宿主处理的恢复请求 ID。场景将保存结果与准确的恢复请求关联。

获取会话同步进度

C++GetSessionProgress返回值FEasyModSessionProgress参数无输入参数用途返回当前会话阶段、总体进度、信息与逐 Mod 文件进度。场景展示下载、缓存命中、校验、失败和重启状态。

获取已准备连接地址

C++GetPreparedConnectionUrl返回值FString参数无输入参数用途返回完成准备后使用的游戏连接 URL。场景会话进入 Ready 后用于连接,不要改回未经准备的原始地址。

获取服务器 Mod 集合 ID

C++GetHostedSetId返回值FString参数无输入参数用途读取当前宿主发布的 Mod 集合标识。场景为状态面板或诊断记录标识本次集合。

获取恢复配置路径

C++GetRecoveryProfilePath返回值FString参数无输入参数用途读取会话恢复使用的配置文件路径。场景提供恢复诊断信息;让插件管理恢复文件内容。

Mod Editor 与界面工具 10

本地化内置文本

C++LocalizedText返回值FText参数const FString& InString用途通过插件本地化目标解析英文内置 UI 文本。场景复用管理界面标签;游戏和 Mod 自有文本使用各自的本地化流程。

是否为游戏开发工程

C++IsGameDeveloperProject返回值bool参数无输入参数用途判断当前是否为未 Cook 的游戏开发工程,而非作者 SDK 工作区。场景区分开发者与作者侧的工具入口。

检查 Mod Editor 可用性

C++IsModEditorAvailable返回值bool
OutError: FString
参数bool bDeveloperProject = false用途检查当前上下文中是否存在可用的 Mod Editor。场景启用编辑器按钮前查询,并向用户显示 OutError。

启动 Mod Editor

C++LaunchModEditor返回值bool
OutError: FString
参数bool bDeveloperProject = false用途启动当前游戏或开发工程对应的独立 Mod Editor。场景在游戏菜单中提供创建 Mod 的入口,失败时显示 OutError。

打开指定 Mod 工程

C++LaunchModEditorForMod返回值bool
OutError: FString
参数const FString& ModId
bool bDeveloperProject = false
用途为指定 Mod ID 启动对应的 Mod Editor 工作流。场景在列表中提供编辑按钮;公开源码与私有包的可编辑性不同。

选择 Mod Editor 目录

C++ChooseModEditorDirectory返回值bool
OutDirectory: FString
OutError: FString
参数bool bDeveloperProject = false用途打开原生选择界面并返回编辑器目录或错误。场景配置手动安装的独立编辑器位置。

选择游戏 SDK

C++ChooseModEditorSDK返回值bool
OutProjectPath: FString
OutError: FString
参数bool bDeveloperProject = false用途打开 SDK 选择流程并返回相应工程路径或错误。场景将已有 .emsdk 接入编辑器工作流。

打开编辑器下载页

C++OpenModEditorDownloadPage返回值bool
OutError: FString
参数bool bDeveloperProject = false用途打开配置的 Mod Editor 下载页面。场景为尚未安装编辑器的作者提供下载入口。

安装或更新 Mod Editor

C++InstallOrUpdateModEditor返回值bool
OutError: FString
参数bool bDeveloperProject = false用途用游戏导出的 SDK 和更新策略启动独立安装器。场景在管理界面提供安装与更新按钮,并显示失败原因。

选择 Mod 包

C++ChooseModArchive返回值bool
OutPath: FString
OutError: FString
参数无输入参数用途打开可选的原生文件选择器,返回 Mod 包路径和错误。场景把 OutPath 传给 ImportMod;也可以由游戏自己的文件选择界面提供路径。

Mod 存档 8

同步保存 Mod 数据

C++SaveModData返回值bool
Error: FString
参数FGuid ModGuid
const FString& Slot
int32 DataVersion
USaveGame* Data
const TArray<FEasyModSaveRequirement>& Requirements
const TArray<FString>& ContentIds
用途验证并保存指定 Mod 的 SaveGame 数据、依赖和内容 ID。场景少量同步存储;数据字段需要 SaveGame,网络客户端不能执行权威写入。

同步读取 Mod 数据

C++LoadModData返回值USaveGame*
Error: FString
参数FGuid ModGuid
const FString& Slot
int32 ExpectedDataVersion
用途读取并验证指定 Mod、存档槽和目标数据版本的存档。场景同步读取时检查返回对象与 Error,配置迁移以处理版本变化。

请求异步保存 Mod 数据

C++RequestSaveModData返回值FGuid参数FGuid ModGuid
const FString& Slot
int32 DataVersion
USaveGame* Data
const TArray<FEasyModSaveRequirement>& Requirements
const TArray<FString>& ContentIds
用途发起保存请求并返回 RequestId,本地磁盘 I/O 在后台执行。场景先绑定 OnSaveRequestCompleted、OnSaveRequestFailed 和 OnSaveRequestCancelled。

请求异步读取 Mod 数据

C++RequestLoadModData返回值FGuid参数FGuid ModGuid
const FString& Slot
int32 ExpectedDataVersion
用途发起读取请求并返回 RequestId。场景按请求 ID 处理完成事件中的 Data,不把请求返回值当作存档对象。

取消存档请求

C++CancelSaveRequest返回值bool参数FGuid RequestId用途请求取消指定 RequestId 的存档操作。场景关闭读写界面或取消恢复流程时,处理对应取消通知。

完成宿主存储请求

C++CompleteStorageRequest返回值bool参数FGuid RequestId
UEasyModSaveStorageProvider* Provider
bool bSucceeded
const FString& Record
const FString& Error
用途由当前宿主存储提供者报告异步读取或写入结果。场景接入云存档等自有后端;请求和 Provider 必须与当前操作匹配。

注册存档迁移

C++RegisterMigration返回值bool参数FGuid ModGuid
UEasyModSaveMigration* Migration
用途为指定 Mod 注册数据版本迁移对象。场景在 Mod 激活时登记迁移;迁移失败会保留原始文件。

设置宿主存储后端

C++SetHostStorageProvider返回值bool
Error: FString
参数UEasyModSaveStorageProvider* Provider用途设置当前 GameInstance 的宿主存储提供者;空对象恢复文件存储。场景将已验证的存档流程接入游戏自己的存储系统。

常用事件

TargetEvent用途
UEasyModSubsystemOnReady
OnStartupProgress
启动准备完成、状态与进度;不等同于所有 Mod 成功激活。
UEasyModSubsystemOnChanged
OnVariantChanged
OnEnableRequestCompleted
列表与变体变化,以及启停操作的最终结果。
UEasyModSessionSubsystemOnProgress
OnCompleted
OnFailed
OnCancelled
OnRestartRequired
同步进度、完成、失败、取消和需要重启的状态。
UEasyModSessionSubsystemOnServerPeersReleased
OnBeforeSessionRecovery
服务器变更的玩家释放通知;恢复前宿主存档与确认流程。
UEasyModSaveSubsystemOnSaveRequestCompleted
OnSaveRequestFailed
OnSaveRequestCancelled
按 RequestId 匹配异步存档结果。
UEasyModSaveSubsystemOnValidatedSave
OnValidatedLoad
OnOptionalContentUnavailable
已验证读写的观察通知,以及可选内容缺失信息。
异步接口返回 bool 或 RequestId 时,表示本次请求的受理情况或标识;实际完成结果由对应事件报告。游戏开发者应按用途选择对 Mod 作者公开的 API,宿主管理和网络准入接口不需要全部开放。

常见问题与发行检查

现象先检查处理方式
没有 Mod settings 按钮插件是否启用、当前是否游戏开发者工程、引擎版本是否匹配。重新打开匹配工程,从 Tools → EasyMod Developer 检查入口。
编辑器看不到插件 UI 资源Show Plugin Content 和当前内容目录。打开 EasyMod Content / EasyMod / UI。
添加了 Blueprint,作者仍不能选作父类是否在 Reference classes、是否允许继承、类是否具备真实 Blueprintable 能力。修正具体类权限,重新导出 SDK。
函数可以调用但不能重写调用与重写权限、原实现编辑、UE 事件/函数能力。分别批准需要的能力,不把 Callable 当作可重写保证。
地图能开但不能修改场景或 GameMode地图场景、World Settings、Level Blueprint 是独立权限。逐层开放需要的字段和事件;重新导出 SDK。
Export SDK 失败进度窗口与 Saved/Logs 中的首个实际错误。修复未保存/未编译资产、缺失依赖、离线测试环境、模块或构建证据,再导出。
找不到 EasyModSDK.emsdk是否已使用当前版本默认文件名。查看工程根目录的 <GameProjectName>.emsdk 与导出成功路径。
源码已改,游戏效果没变是否只保存而未重新导出,是否仍启用另一变体,目标内容是否已加载。重新 Build and export,确认包和选中变体,再冷启动验证。
Load Mods 返回 true 但内容未就绪异步进度、被拒绝的 Mod、入口状态。等待对应事件并检查错误,不直接开始依赖 Mod 的玩法。
本地正常,联机不同步实际地图 GameMode、Automatic Mod Session Sync、同步 TCP 端口。检查适配、网络可达性和准备后的连接 URL;使用独立进程测试。
需要重启才能改变 Mod 集合已有包/地图是否驻留。遵循安全菜单和恢复流程,不强制卸载正在引用的资源。
打包因缺少编辑器或 SDK 停止Game Packaging 中两个 Bundle 选项及选择的安装版。准备开启的组件,或关闭本次不随游戏附带的组件并另行分发。

每次发行至少检查这些结果

  1. 新安装工程能正常打开,公开内容和权限与设计一致。
  2. 使用实际交付的 SDK 和 Mod Editor 创建、编译、保存和导出一个 Mod。
  3. 打包游戏冷启动:无 Mod、公开 Mod、私有 Mod(如果支持)均按预期运行。
  4. 启停、排序、依赖、冲突和需要重启等状态有正确 UI 反馈。
  5. 开放地图和资源在真正加载目标内容之前准备完成。
  6. 联机项目额外测试服务器改集、缺包、版本冲突、缓存/续传、离服和重启恢复。
  7. SDK、编辑器与公开下载目录不包含游戏私钥、宿主专用密钥模块或未批准的游戏内容。

反馈问题时提供 UE 版本、EasyMod 版本、游戏开发者/作者/玩家身份、操作步骤、首个错误和相关日志片段。不要附上游戏私钥、登录凭据或整个含机密内容的工程。