最近翻到一个已经停止维护、并被公开源码的《饥荒联机版》全皮肤 mod。最开始我以为这类项目的实现方式都差不多:把官方皮肤资源打包进模组目录,改几个表,最后在游戏里“显示出来”就算完事。
但事实上它根本不是一个普通的资源包。更准确地说,它是一套运行时皮肤注入系统。它不是单纯把官方皮肤搬进来,而是尽量复刻了《饥荒联机版》原本的皮肤工作方式,然后在这套体系上额外加了一层 custom_ 命名空间和一批运行时 Hook,让本地皮肤看起来像官方皮肤一样参与名称查询、图标查询、所有权判断、实体生成和换肤流程。
接管皮肤系统
这个项目并没有绕开官方皮肤体系,它尽量贴着官方的结构走,只在必要的地方插入一层自己的运行时改写。
比如在 modmain.lua 里,模组首先定义了 custom_ 前缀的判断和剥离逻辑,建立了一层稳定的映射关系:
- 官方皮肤
wilson_formal - 模组皮肤
custom_wilson_formal
模组可以在运行时把 custom_wilson_formal 识别成自己的皮肤,又可以在需要调用官方数据的地方,把它重新还原成 wilson_formal 去查名称、描述、图标和稀有度。
复用官方查询能力
在 skinloader/skinloader.lua 中,它没有去维护一份巨大的本地化文本表,也没有把官方图标名、稀有度字符串整套复制出来,而是直接拦截查询函数。
它会把:
GetSkinDescriptionGetSkinNameGetSkinInvIconNameGetModifiedRarityStringForItemGetColorForItem
这类全局函数统一包一层代理。如果传入的项目带 custom_ 前缀,就先把前缀剥掉,再把请求交还给官方函数。
模组不需要自己维护一大坨显示层数据。游戏在查询 custom_wilson_formal 时,最后拿到的仍然是官方 wilson_formal 的名字、描述、图标和稀有度逻辑。
所有权伪造
在 skinloader/skinloader.lua 里,这个模组重写了和库存所有权相关的多组接口,比如:
InventoryProxy.CheckOwnershipInventoryProxy.CheckOwnershipGetLatestInventoryProxy.GetOwnedItemCountInventoryProxy.GetFullInventory
它的核心思路很简单:只要某个皮肤被注册进模组自己的 SKINS 表,就让官方逻辑把它当成“已拥有”。
这一步非常关键。因为 DST 的皮肤系统不是“资源在本地就能直接穿上”,它中间还有一层所有权检查。
接管生成和换肤流程
这个项目在运行时接管了几段皮肤流程:
CreatePrefabSkin(...)SpawnPrefab(...)Sim:ReskinEntity(...)AnimState:GetSkinBuild(...)
模组不是把资源丢在目录里,等引擎自己偶然发现;而是在实体生成、换肤和动画构建名解析这些关键节点上,明确告诉游戏“现在该用的是这个 custom_ 皮肤”。