第三方模组集成

基本信息

属性 值
依赖声明文件 dependencies.gradle、gradle.properties、@Mod(dependencies=...)
硬依赖 1 个第三方模组(GTNHLib)
软依赖 8 个第三方模组
运行期专有 1 个(Hodgepodge,反向读取其字段)
明确不兼容 1 个(FTB Utilities)
排除目标 1 个(Ultramine)
编译期库 1 个非模组依赖(JourneyMap API)

依赖分层

硬依赖(required-after,缺失则无法加载)

模组 版本 声明位置
GTNHLib 0.11.49 @Mod(dependencies = "required-after:gtnhlib;...")(ServerUtilities.java:33);dependencies.gradle:5 用 api(...);gradle.properties:172 required-project:gtnhlib;gradle.properties:191 requiredDependency:gtnhlib

GTNHLib 提供配置系统(@Config)、Mixin 框架(IMixins、ILateMixinLoader、IEarlyMixinLoader)与 LateMixin 注解。本模组完全依赖它的配置反射机制。

此外,RetroFuturaGradle 会在 usesMixins = true(gradle.properties:101)时自动把 UniMixins 加为必需依赖(gradle.properties:168 注释明确说明),故 UniMixins 也是事实上的硬依赖。

软依赖(after 或 optional,缺失则功能降级)

模组 版本 依赖声明 缺失后果
Navigator 1.1.10 after:navigator(ServerUtilities.java:33);optional-project:navigator(:172);optionalDependency:navigator(:191) 地图上不显示领地
NotEnoughItems 2.8.144-GTNH compileOnly(dependencies.gradle:6) 创造物品栏识别与侧边栏按钮隐藏逻辑失效
EnderIO 2.10.45 compileOnly(:7) 无法识别 EnderIO 的 FakeFarmPlayer
Random-Things 2.7.10 compileOnly(:8) 隐身玩家仍会被在线检测器看到
Witchery CurseMaven witchery-69673:2234410 compileOnly(rfg.deobf(...))(:18) 无吸血鬼/棺材判定
WAILA 1.19.34 runtimeOnlyNonPublishable(:24) 仅运行期需要,不随模组发布
Hodgepodge 未在 dependencies.gradle 声明 运行时 Loader.isModLoaded 探测 备份完成后不触发其世界数据刷新

[!NOTE] Hodgepodge 没有任何编译期依赖,却出现在 OtherMods 的探测列表里。它是反向集成:BackupTask.drainQueuedWrites()(task/backup/BackupTask.java:190-205)用 Class.forName("com.mitchej123.hodgepodge.config.TweaksConfig") 反射读取其 threadedWorldDataSaving 字段,为真时再反射调 com.mitchej123.hodgepodge.util.WorldDataSaver 的刷新方法。方法注释写明「without requiring that mod, or its newer flush API」—— 对 ClassNotFoundException 与 NoSuchFieldException 都直接 return,兼容 Hodgepodge 的新旧两版 API。

仅编译期(reference-only)

依赖 版本 用途
info.journeymap:journeymap-api-forge 1.7.10-2.0.0(dependencies.gradle:10) 只用其 API 编译,不产生运行期耦合
org.jetbrains:annotations 26.1.0(:11) @NotNull / @Nullable / @ApiStatus

invsee 背包专用(软依赖)

Baubles-Expanded、Battlegear2、Galacticraft、TinkersConstruct、AdventureBackpack2、Minecraft Backpack Mod —— 全部 compileOnly 且 transitive = false(dependencies.gradle:12-17)。详见 Invsee 背包查看。

集成实现细节

NavigatorIntegration(integration/navigator/)是集成最深的软依赖,共 8 个类。

类 作用
NavigatorIntegration 入口,init() 注册图层管理器;维护 CLAIMS 缓存
ClaimsLayerManager 实现 Navigator 的图层管理器
ClaimsLayerManager.INSTANCE 单例
ClaimsButtonManager 侧边栏按钮集成
ClaimsRenderStep 渲染步骤
ClaimsPolygonOverlay 多边形覆盖层绘制
ClaimsLocation 坐标转换

触发点 3 处:

位置 行 作用
client/ServerUtilitiesClient.java 81 postInit 时 NavigatorIntegration.init()
net/MessageClaimedChunksUpdate.java 150 收到领地更新后同步到 Navigator
handlers/ServerUtilitiesClientEventHandler.java 65 客户端事件时刷新

使用 Navigator 的 4 个封包(utilities_claim 通道):MessageNavigatorUpdate、MessageNavigatorRequest、MessageNavigatorUpdateKnown、MessageNavigatorValidateKnown。

NavigatorIntegration 静态字段 CLAIMS 是 Object2ObjectMap<ChunkDimPos, ClientClaimedChunks.ChunkData>(fastutil 映射,:24),另有 OWNTEAM 缓存与 mutablePos 复用对象。

Witchery

serverutils.compat.WitcheryCompat(compat/WitcheryCompat.java)只有 2 个方法:

方法 行 用途
isVampire(EntityPlayer) 11 ExtendedPlayer.get(player).isVampire()
isSleepInCoffin(World, EntityPlayer) 17 检查玩家所在方块是否 Witchery.Blocks.COFFIN

配合 Mixins.CANCEL_VAMPIRE_WAKEUP_EVENT(LATE 阶段,需 world.enable_player_sleeping_percentage 为真)与 world.vampire_sleep_percent(默认 50)实现「棺材中达到一定比例的吸血鬼才能跳过夜晚」。

[!NOTE] OtherMods.isWitcheryLoaded() 被 OtherMods.init() 赋值(lib/OtherMods.java:19),但全仓没有任何地方调用它 —— Witchery 集成完全通过 Mixin 的 addRequiredMod(TargetedMod.WITCHERY) 与直接类引用判断,不走 isWitcheryLoaded() 标志。

NotEnoughItems

两处使用:

位置 行 作用
lib/client/ClientUtils.java 66 isCreativePlusGui():NEI 加载时把 GuiExtendedCreativeInv 识别为创造物品栏
client/gui/SidebarButton.java 33 NEI_NOT_LOADED 布尔供给器

SidebarButton.NEI_NOT_LOADED 驱动 sidebar_buttons.json 中 5 个 hide_with_nei: true 的作弊按钮(heal、toggle.gamemode、toggle.rain、toggle.day、toggle.night)。

EnderIO

唯一使用点 ServerUtils.isFakeFarmPlayer()(lib/util/ServerUtils.java:50-53):

private static boolean isFakeFarmPlayer(EntityPlayerMP player) {
    if (!OtherMods.isEnderIOLoaded()) return false;
    return player instanceof FakeFarmPlayer;
}

结果被 isFake()(:46-48)合并,后者控制 world.logging.include_fake_players 是否生效。

Random-Things

Mixins.HIDE_VANISHED_FROM_DETECTOR(mixin/Mixins.java)—— LATE 阶段,addRequiredMod(RANDOMTHINGS),条件 commands.vanish 为真,向 randomthings.MixinWorldUtils 注入。作用是把隐身玩家从 Random-Things 的在线检测器中隐藏。

TargetedMod.RANDOMTHINGS 定义为 RANDOMTHINGS("RandomThings")(mixin/TargetedMod.java:9),即按 modid RandomThings 匹配。

JourneyMap

只通过 journeymap-api-forge 编译依赖接入。功能上体现为两个权限节点(ServerUtilitiesPermissions.java:131-132):

节点 含义
serverutilities.journeymap.enable 在 JourneyMap 覆盖层上看自己队伍的领地
serverutilities.journeymap.other 看其他队伍的领地

明确不兼容:FTB Utilities

gradle.properties:191 声明 incompatible\:ftb-utilities-forge(仅 CurseForge 元数据)。

运行期强制在 client/ServerUtilitiesClient.java:90-92:

if (Loader.isModLoaded("FTBU") || Loader.isModLoaded("FTBL")) {
    throw new IncompatibleModException();
}

检测的是 modid FTBU 或 FTBL(FTB Utilities / FTB Library),而非 CurseForge slug ftb-utilities-forge。

IncompatibleModException(lib/client/IncompatibleModException.java:12)继承 cpw.mods.fml.client.CustomModLoadingErrorDisplayException(该 FML 类在 :9 被 import),错误文案硬编码为 "FTBUtilities/FTBLibrary detected during load."(:13),并弹出一个只有「Close Game」按钮的错误界面(:25-33),点按后 Minecraft.getMinecraft().shutdown()。

[!IMPORTANT] 这是模组历史造成的冲突,不是功能依赖问题。ja_JP.lang 中仍保留 7 个 ftbutilities_client.* 语言键(resources/assets/serverutilities/lang/ja_JP.lang:66-72,含 show_shutdown_timer、button_daytime、button_nighttime、shutdown_timer_start 等),说明侧边栏按钮功能原本源自 FTB Utilities,本模组 fork 后接管了这部分功能,因此互斥。本条目只陈述代码与资源中的事实。

排除目标:Ultramine

TargetedMod.ULTRAMINE(null, null, "org.ultramine.server.UltraminePlugin")(mixin/TargetedMod.java:8)—— 按类 org.ultramine.server.UltraminePlugin 匹配(无 modid、无 coreModClass)。

Mixins 中有 2 个条目 addExcludedMod(TargetedMod.ULTRAMINE):

条目 说明
PAUSE_WHEN_EMPTY 空服暂停
MAX_TICK_TIME 单 tick 超时保护

Ultramine 自身实现了这两项服务器级功能,同时启用会冲突。

失败被静默吞掉的集成点

InvSeeRegistry.DefaultInventories 构造函数(invsee/inventories/InvSeeRegistry.java:36-48)在尝试实例化某个 mod 背包时:

try {
    registerInventory(inventory.getDeclaredConstructor().newInstance());
} catch (Exception ignored) {}

空 catch,异常被完全吞掉(:46)。若某个可选 mod 加载了但其背包类结构不兼容,invsee 页签会静默消失,没有任何日志。同样的空 catch 模式还出现在:

位置 行 场景
task/ShutdownTask.java 48 关机流程
lib/data/ForgePlayer.java 126、137 玩家数据读写
lib/icon/PlayerHeadIcon.java 98、109 玩家头颅图标渲染
aurora/AuroraServer.java 98、107 Web 服务器绑定/关闭(见 Aurora 配置)

相关条目