Mora:Skyrim 次世代脚本与补丁工具编写规则,而不是修补程序。什么是摩拉?Mora是一种小型声明式语言,用于描述《上古卷轴5:天际》的数据应如何呈现以及在运行时如何响应。你编写
规则, 而Mora编译器会针对你真实的ESP/ESM加载顺序,提前尽可能评估一切内容,并将结果烘焙成一个扁平的、可内存映射的补丁文件。附带的SKSE插件(
MoraRuntime.dll) 该文件被映射,并在游戏加载时应用补丁。依赖实时游戏状态的规则通过一个小型差分数据流引擎运行,当条件不再成立时,该引擎会自动撤销其效果。
游戏加载时,250万处场地级补丁在10毫秒内应用完毕。 无ESP,无插件槽位,无主文件依赖。
链接快速示例namespace my_mod.balance
使用表单:作为 f
# 所有铁制武器获得20点伤害。完全静态,已内置于补丁文件中。
铁制武器(W)
f/武器(W)
f/关键字(W, @武器材质铁)
=> 设置形态/伤害(W, 20)
计算值,跨关系联接:
# 所有铁制武器的伤害都会随玩家等级提升而增加。
scaled_damage(W, D):
武器(W)
铁质武器
D = form/damage(W) * 1.2 + player/level()
=> 设置形态/伤害(W, D)
自动收回的动态规则:
当玩家处于寒冷区域时,施加一个冰霜斗篷增益效果。
维持frost_cloak():
玩家/位于区域(@ColdRegions)
=> add_effect(player, @FrostCloakSpell)
当玩家离开该区域时,引擎会自动移除效果。无需Papyrus脚本,也无需手动清理。
工作原理(技术概述)莫拉有三层结构:
- 编译器。 解析你的规则,并根据生成的覆盖记录数据的关系表对它们进行类型检查(form/), 实时引用 (ref/), 玩家 (玩家/), 世界 (世界/), 以及事件 (event/). 每条规则被拆分为 静态 (仅从ESP数据即可推导得出)和 动态 (取决于游戏实时状态)。
- 静态评估器。 遍历您的实际加载顺序,在合并记录集上评估静态规则,并输出一个扁平排序表。 (FormID, field, op, value) 元组到 mora_patches.bin该文件设计用于在加载时进行单次mmap加memcpy操作。
- 运行时(SKSE插件)。 加载游戏时,将补丁文件映射到内存,并针对每个元组执行一次单一内存写入来应用所有静态补丁。对于动态规则,则运行一个小型差分数据流引擎。规则输出会随时间被追踪,边会在事件触发时激活(开启 规则),且连续真值在条件变化时会自动撤销其效果(维持 规则)。
规则间的冲突在此交汇
田野 等级。两个模组修改同一个NPC上的不同字段完全不会冲突。两条规则修改同一字段时,通过显式操作符语义(
设置,
添加,
乘以, 等等)。
为什么这么快游戏加载时,在10毫秒内应用了250万个场景级补丁。 这是运行时应用完整加载补丁文件后的实际数据,大约相当于《天际》全部DLC加上繁重加载顺序的合并记录总数,在字段级别进行编辑,耗时不到一帧的渲染时间。
速度源于架构。Papyrus、SkyPatcher、KID 和 SPID 在运行时所要做的,Mora 在编译时已完成。运行时剩下的,只是 CPU 最廉价的运算:对紧凑数组进行一次有序遍历,每个条目执行一次表单查找和一次内存写入。
其他动态系统在加载时会做什么:- 帕皮鲁斯 在单个VM线程中解释字节码,按时间片分块调度。每次字段写入都是通过VM的虚拟调用,经由脚本到引擎的桥接层进行编组,并在其上叠加垃圾回收和事件队列。从Papyrus访问250万个字段需要数分钟,这就是没人这么做的原因。你写的Papyrus只触及玩家和附近少数几个引用,绝不会是整个游戏世界。
- SkyPatcher、KID 和 SPID 在加载时解析INI文件,遍历每个已加载的表单,并对每一个表单求值一串基于字符串的过滤谓词:该表单的EditorID是否匹配此正则表达式、是否拥有此关键字、是否属于此FormList。过滤求值按表单、按规则反复进行。计算量随表单数乘以规则数再乘以每条规则的谓词数而扩展,且在加载画面期间全部在单线程上完成。因此,繁重的KID/SPID配置会明显延长加载时间。
莫拉在加载时做什么:- 映射补丁文件。 一次系统调用。操作系统将文件映射到运行时的地址空间中。不复制任何内容,也不解析任何内容。
- 遍历一个已排序的、紧凑的(FormID,字段,操作符,值)元组数组。 无字符串。无正则。无过滤器评估。无规则引擎。编译器已根据您的加载顺序评估了所有静态规则,并将整个决策树简化为一个扁平列表:“将此值写入此表单的此字段。”
- 对于每个元组,按ID查找表单(对CommonLib的表单映射进行O(1)哈希)并写入字段。 大多数操作都是单次内存写入。热循环适配L1缓存,对预取器和分支预测器友好。
架构上的回报:更重的规则集并不会拖慢加载界面。在SkyPatcher/KID配置中,规则数量翻倍,加载界面过滤的工作量大约也翻倍。而在Mora中,规则数量翻倍只会让补丁文件略微变大,每个元组的代价不变,应用时仍只需几毫秒。编译期吸收了复杂性,因此运行期保持线性且开销极小。
对于动态规则,当输入发生变化时,differential-dataflow引擎仅重新计算规则中受影响的部分,而非像Papyrus轮询循环那样在每个周期对每条规则重新求值。
Mora vs. SkyPatcher / KID / SPID这些是最接近的同类:同样不需要ESP,同样在加载时由SKSE插件应用。区别在于
表现力。他们的INI是平面过滤器列表。Mora是一种真正的语言,拥有变量、跨关系连接、算术运算和计算值。
将关键词应用于所有铁制武器
KID:
Keyword = MyMod_IronTag|武器|*|材料铁
相同于此
摩拉:
tag_iron(W)
武器(W)
f/keyword(W, @WeapMaterialIron)
=> 添加表单/关键词(W, @MyMod_IronTag)
目前为止,两者相当。现在考虑:“所有铁制武器的伤害变为
基础值 * 1.2 + 玩家等级,而银质武器对不死目标额外造成+10点伤害。”在KID/SPID/SkyPatcher中,你根本无法表达这种计算逻辑。它们没有变量,没有算术运算,也没有拼接功能。而在Mora中,这只需在同一个文件、用同一套词汇再添加两条规则即可。
Mora还在同一个词汇表下统一了记录编辑、分发和关键词附加功能,并新增了
维护/
开启 基于实时游戏状态的行为规则。
Mora vs. Synthesis(祈使式 vs. 陈述式)Synthesis是一个很棒的工具包,但每个patcher都是一个命令式C#程序,由你编写、编译并编排顺序。你需要遍历记录、修改它们,然后输出一个ESP。其输出是一个真实的《天际》插件:它会占用一个加载顺序槽位,需要主文件依赖,在记录层面存在冲突,且每当加载顺序变化时都必须重新生成。
合成补丁增强铁制武器伤害(草图):
foreach (var weap in state.LoadOrder.PriorityOrder.Weapon().WinningOverrides()) {
if (weap.Keywords?.Any(k => k.FormKey == Keywords.WeapMaterialIron) == true) {
var copy = state.PatchMod.Weapons.GetOrAddAsOverride(weap);
copy.BasicStats!.Damage = 20;
}
}
莫拉这边也一样:
铁制武器(W)
f/武器(W)
铁
=> 设置形态/伤害(W, 20)
你描述
什么 应为真,编译器会处理迭代、顺序和合并。输出是直接由SKSE插件应用于活动表单的扁平元组表。无需插件槽位,无需主文件,无记录级冲突,加载顺序改变时也无需重新生成。
安装- 安装 SKSE64.
- 掉落 MoraRuntime.dll 进入 Data/SKSE/Plugins/.
- 拖放您编译好的文件 mora_patches.bin 与它并驾齐驱。
- 启动游戏。
要编写自己的规则,请抓住
摩拉 从GitHub releases页面获取CLI,并指向你的Skyrim Data目录:
mora compile balance.mora --data-dir "C:\...\Skyrim Special Edition\Data"
状态Mora尚处于1.0之前的版本,正在积极开发中。语言表面、补丁文件格式及运行时ABI均可能发生变化。详见
文档 用于每个子系统的当前状态。
致谢与鸣谢Mora建立在模组社区开源工作的基石之上:
- 这个 SKSE 队伍。 没有Skyrim Script Extender,这一切都不可能实现。数十年的逆向工程和运行时挂钩将《天际》变成了一个平台。
- 这个 CommonLibSSE-NG 维护者 (Alandtse、Ryan-rsm-McKenzie 及贡献者)。CommonLibVR 嗯 branch 是一个 C++ 库,它让编写适用于 SE、AE 和 VR 的现代 SKSE 插件变得简单可行。Mora 的运行时 DLL 就是基于它构建的。
- 这 力量3 生态系统。 SPID、KID、SkyPatcher 及其伙伴们开创了无需 ESP 的运行时分发模式,Mora 正是对此的延伸。
- xEdit、Mutagen 和 Synthesis 团队。 这些项目所生成的记录格式文档与工具,正是我们能够对这一切进行推理的原因所在。
- Bethesda,为可模组化引擎的十五年。
许可证MPL 2.0。请参阅
许可证 文件。