MC百科社群
标题:
(搞七捻三)邪修是如何制作Minecraft模组的
[打印本页]
作者:
Author87668
时间:
2026-8-2 00:42
标题:
(搞七捻三)邪修是如何制作Minecraft模组的
本帖最后由 Author87668 于 2026-8-3 14:41 编辑
没人看准备删了,没意思 :(
作者声明:文章创作过程中使用了AI辅助
本文以搞怪风格为主,所述"邪修"技术纯属编程技巧的文学化表达,文中所有比喻(包括但不限于"修仙"、"炼丹"、"走火入魔"、"渡劫"等)均为修辞手法,请勿当真尝试在现实中修炼。
本文内容不代表Mojang Studios、Microsoft或任何模组加载器开发团队的官方立场、建议或认可。所有观点仅代表作者个人经验总结。
本文同步发布于:
https://blog.star-dust.link/post ... ds-dark-cultivation
本文的正经风格版本:
https://blog.star-dust.link/posts/note/fabric-mod-reflection-multi-version-compatibility
免责声明
作者郑重声明:
1. 本文内容仅供学习和参考,不构成任何形式的建议、保证或承诺
2. 作者不对因使用本文所述技术导致的任何直接或间接损失承担责任,包括但不限于:
- 数据丢失或损坏
- 服务器崩溃或服务中断
- 性能下降或用户体验恶化
- 法律纠纷或合规问题
- 时间浪费和精神损失
- 任何其他形式的损害
3. 用户使用本文所述技术的风险完全由用户自行承担
4. 作者保留随时修改、更新或删除本文内容的权利,恕不另行通知
5. 本文可能包含错误、遗漏或不准确的信息,请以实际测试为准
6. 本文提及的所有商标、产品名称均属于其各自所有者
本文最后更新于2026年8月,适用于Minecraft Java Edition 1.18-26.2。技术日新月异,请以实际情况为准。
第一章:为什么要当邪修?
因为正道太累了啊!
你看那些名门正派的Fabric弟子,每天对着Mixin注解磕头拜佛,AccessWidener写得比情书还长,Gradle配置调得比炼丹炉还复杂。结果呢?MC一更新,全部白给!1.21写的模组到26.1直接原地飞升,连个全尸都不留。
而我们邪修不一样。我们没有信仰,没有底线,没有import net.minecraft.*。我们只有一样东西——
反射。
这玩意儿就像修仙界的"万能钥匙",虽然脏、虽然慢、虽然随时可能炸膛,但它能用。编译期看不到MC的类?没关系,运行时硬抢!方法名改了?没关系,挨个试!参数类型不对?没关系,动态代理糊脸!
邪修的核心理念就八个字:
活着就行,管他姿势。
第二章:邪修三件套
2.1 常量层:你的"通缉令"
把所有版本里变来变去的类名、方法名、字段名,全部写进一个巨大的常量表里。这张表就是你的"仇人名单",上面记满了MC每次更新欠下的债。
// 邪修の仇人录
static final String C_LEVEL = "net.minecraft.world.level.Level";
static final String C_COMPONENT = "net.minecraft.network.chat.Component";
static final String M_GET_UUID = im("Entity", "getUUID", "()Ljava/util/UUID;", "method_5667");
static final String M_LITERAL = im("Component", "literal", "(Ljava/lang/String;)LComponent;", "method_43470");
复制代码
记住:业务代码里绝对不能出现任何硬编码的MC类名。一旦出现,你就破了戒,离走火入魔不远了。
2.2 反射层:你的"作案工具"
这一层封装所有脏活累活。Class.forName、Method.invoke、Field.get……这些见不得光的操作全部藏在这里,对外只提供人话接口。
关键技巧:
缓存一切:
反射调用一次就够了,第二次还用就是浪费生命。用ConcurrentHashMap存起来,找不到的也要存个NOT_FOUND哨兵,不然每次都重新扫描,JVM会骂你。
描述符必须带:
找方法不带描述符等于裸奔。getUUID()混淆后可能是method_5667也可能是method_5845,一个返回UUID一个返回String,猜错了当场社死。
重载方法要遍历:
别傻乎乎只缓存第一个匹配到的方法。把所有同名方法都捞出来,调用时用isInstance逐个验身,谁对得上就用谁。
2.3 业务层:你的"伪装身份"
这一层看起来和正常模组一模一样。getBlockState()、sendMessage()、checkPermission()……全是人话,干干净净。
但只有你自己知道,这些人话底下藏着多少反射。
第三章:邪修实战骚操作
3.1 环境探测:先搞清楚自己在哪
Fabric生产环境用intermediary名,MC 26+又改回named名。你得先知道自己身处哪个平行宇宙:
// 使用神识探查一下当前是不是 named 运行时
static final boolean IS_NAMED = tryLoad("net.minecraft.world.level.Level") != null;
复制代码
然后查表回退:先试named名,不行就翻"仇人录"找intermediary名。intermediary名跨版本稳定,是你最后的保命符:
static Class<?> forName(String name) {
// 1. 缓存命中直接返回(含 NOT_FOUND 哨兵)
Object cached = CACHE.get(name);
if (cached != null) return unwrap(cached);
// 2. 先试 named 名,再试 intermediary 兜底
Class<?> cls = tryLoad(name);
if (cls == null) cls = tryLoad(Constants.toInter(name));
// 3. 存结果,找不到也要存哨兵防无限重试
CACHE.put(name, cls != null ? cls : NOT_FOUND);
return cls;
}
复制代码
3.2 事件监听:动态代理捏脸术
Fabric的事件回调接口里全是MC原生类,编译期根本import不了。怎么办?不implements了,直接捏!
用JDK动态代理在运行时现场造一个假对象塞进去。代理对象收到回调后,把参数打包成Object[]扔给业务层慢慢拆:
// 运行时捏个假监听器,彻底斩断编译期类型依赖
Object proxy = Proxy.newProxyInstance(loader, new Class[]{listenerIfc}, (p, m, args) ->
m.equals(targetMethod) ? handler.handle(args) : handleObjectMethods(p, m, args)
);
registerMethod.invoke(eventObj, proxy);
复制代码
这招的精髓在于:只要我不承认依赖存在,依赖就不存在。我们把它叫做薛定谔的类型安全。
3.3 多策略兜底:一条路走到黑不如多条路走到亮
获取方块ID为例:
static String getBlockId(Object state) {
Object block = call(state, "getBlock");
if (block == null) return "minecraft:air";
// 策略1:toString 硬抠(全版本通用保底)
String s = block.toString(); // Block{minecraft:diorite}
int b = s.indexOf('{'), e = s.indexOf('}');
if (b >= 0 && e > b) return s.substring(b + 1, e);
// 策略2:反射查注册表(新旧API自动兼容)
String id = getFromRegistry(block);
return id != null ? id : "minecraft:air";
}
复制代码
先试新API BuiltInRegistries,抛异常了?换老API Registries,又抛了?用toString()硬抠字符串(Block{minecraft:diorite} → minecraft:diorite),还不行?返回minecraft:air,假装什么都没发生。
邪修的信条:
优雅地失败 > 粗暴地崩溃。
3.4 文本组件兼容:新旧两开花
老版本new TextComponent("hello"),新版本Component.literal("hello")。写个兼容方法:
// 新版 Component.literal() 失败就降级旧版 new TextComponent()
static Object text(String s) {
try { return invokeStatic("Component", "literal", s); }
catch (Exception e) { return newInstance("TextComponent", s); }
}
复制代码
注意:这里Component_literal和new_TextComponent都是反射拿到的,编译期依然干干净净。邪修的代码,永远不能被编译器抓到把柄。
第四章:邪修禁忌
忌到处散落反射调用:不封装的反射就像没穿衣服上街,迟早出事。
忌信任单一API:MC的承诺比渣男的誓言还不靠谱,永远准备Plan BCD。
忌忽略性能:不做缓存的反射修士,活不过筑基期。
忌忘记哨兵:ConcurrentHashMap不能存null,不设NOT_FOUND占位符,你会陷入无限重试的地狱轮回。
忌跟正道修士争论:他们不懂你的苦,你也不必解释。默默写完兼容代码,看他们的模组在下个版本集体暴毙,然后微微一笑。
终章:邪修的归宿
这套体系搭完,你就能实现:
✅ 编译期零MC依赖
✅ 单JAR通吃1.18到26+
✅ 不用Mixin、不用AW、不看官方脸色
✅ 在别人模组集体阵亡时独自存活
代价是什么?
❌ 代码可读性约等于天书
❌ 调试时堆栈信息像乱码
❌ 新人接手时会诅咒你十八代祖宗
❌ 你永远无法理直气壮地说"我写的是正经模组"
但没关系。
邪修不需要认可,只需要运行。
当你看到自己的模组在数个大版本更新后依然顽强地加载成功,而隔壁正道修士还在Discord里哭诉Mixin冲突时——
那一刻,你就知道,这条路,走对了。
(啊免责被截断了只能写这么多)
欢迎各路大神留下评论
作者:
QQ酱1274197
时间:
2026-8-3 10:53
“AI辅助”
作者:
Author87668
时间:
2026-8-3 14:24
本帖最后由 Author87668 于 2026-8-3 14:34 编辑
QQ酱1274197 发表于 2026-8-3 10:53
“AI辅助”
“:)”
欢迎光临 MC百科社群 (https://bbs.mcmod.cn/)
MC百科
|最大的MineCraft中文模组百科