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每次更新欠下的债。

  1. // 邪修の仇人录
  2. static final String C_LEVEL       = "net.minecraft.world.level.Level";
  3. static final String C_COMPONENT   = "net.minecraft.network.chat.Component";
  4. static final String M_GET_UUID    = im("Entity", "getUUID", "()Ljava/util/UUID;", "method_5667");
  5. static final String M_LITERAL     = im("Component", "literal", "(Ljava/lang/String;)LComponent;", "method_43470");
复制代码


记住:业务代码里绝对不能出现任何硬编码的MC类名。一旦出现,你就破了戒,离走火入魔不远了。

2.2 反射层:你的"作案工具"

这一层封装所有脏活累活。Class.forName、Method.invoke、Field.get……这些见不得光的操作全部藏在这里,对外只提供人话接口。

关键技巧:


2.3 业务层:你的"伪装身份"

这一层看起来和正常模组一模一样。getBlockState()、sendMessage()、checkPermission()……全是人话,干干净净。

但只有你自己知道,这些人话底下藏着多少反射。



第三章:邪修实战骚操作

3.1 环境探测:先搞清楚自己在哪

Fabric生产环境用intermediary名,MC 26+又改回named名。你得先知道自己身处哪个平行宇宙:

  1. // 使用神识探查一下当前是不是 named 运行时
  2. static final boolean IS_NAMED = tryLoad("net.minecraft.world.level.Level") != null;
复制代码


然后查表回退:先试named名,不行就翻"仇人录"找intermediary名。intermediary名跨版本稳定,是你最后的保命符:

  1. static Class<?> forName(String name) {
  2.     // 1. 缓存命中直接返回(含 NOT_FOUND 哨兵)
  3.     Object cached = CACHE.get(name);
  4.     if (cached != null) return unwrap(cached);

  5.     // 2. 先试 named 名,再试 intermediary 兜底
  6.     Class<?> cls = tryLoad(name);
  7.     if (cls == null) cls = tryLoad(Constants.toInter(name));

  8.     // 3. 存结果,找不到也要存哨兵防无限重试
  9.     CACHE.put(name, cls != null ? cls : NOT_FOUND);
  10.     return cls;
  11. }
复制代码


3.2 事件监听:动态代理捏脸术

Fabric的事件回调接口里全是MC原生类,编译期根本import不了。怎么办?不implements了,直接捏!

用JDK动态代理在运行时现场造一个假对象塞进去。代理对象收到回调后,把参数打包成Object[]扔给业务层慢慢拆:

  1. // 运行时捏个假监听器,彻底斩断编译期类型依赖
  2. Object proxy = Proxy.newProxyInstance(loader, new Class[]{listenerIfc}, (p, m, args) ->
  3.     m.equals(targetMethod) ? handler.handle(args) : handleObjectMethods(p, m, args)
  4. );
  5. registerMethod.invoke(eventObj, proxy);
复制代码


这招的精髓在于:只要我不承认依赖存在,依赖就不存在。我们把它叫做薛定谔的类型安全。

3.3 多策略兜底:一条路走到黑不如多条路走到亮

获取方块ID为例:

  1. static String getBlockId(Object state) {
  2.     Object block = call(state, "getBlock");
  3.     if (block == null) return "minecraft:air";

  4.     // 策略1:toString 硬抠(全版本通用保底)
  5.     String s = block.toString(); // Block{minecraft:diorite}
  6.     int b = s.indexOf('{'), e = s.indexOf('}');
  7.     if (b >= 0 && e > b) return s.substring(b + 1, e);

  8.     // 策略2:反射查注册表(新旧API自动兼容)
  9.     String id = getFromRegistry(block);
  10.     return id != null ? id : "minecraft:air";
  11. }
复制代码


先试新API BuiltInRegistries,抛异常了?换老API Registries,又抛了?用toString()硬抠字符串(Block{minecraft:diorite} → minecraft:diorite),还不行?返回minecraft:air,假装什么都没发生。

邪修的信条:优雅地失败 > 粗暴地崩溃。

3.4 文本组件兼容:新旧两开花

老版本new TextComponent("hello"),新版本Component.literal("hello")。写个兼容方法:

  1. // 新版 Component.literal() 失败就降级旧版 new TextComponent()
  2. static Object text(String s) {
  3.     try { return invokeStatic("Component", "literal", s); }
  4.     catch (Exception e) { return newInstance("TextComponent", s); }
  5. }
复制代码


注意:这里Component_literal和new_TextComponent都是反射拿到的,编译期依然干干净净。邪修的代码,永远不能被编译器抓到把柄。



第四章:邪修禁忌





终章:邪修的归宿

这套体系搭完,你就能实现:

✅ 编译期零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中文模组百科