CoreModAPI
模组属性评比

距离显示结果还剩3票~

路过的这位少侠,你觉得这款Mod怎么样,可否愿意来评一评它呢?登录并评比
更新日志更多
历史编辑记录更多
    管理组

      暂无管理组..

    编辑组

      暂无编辑组..

    活跃
    开源

    CoreModAPI

    • 前言

      什么是 CoreMod?

      CoreMod(核心模组)是 Minecraft Forge 模组开发中的一种高级技术,它允许开发者在类加载阶段直接修改游戏的字节码。

      这意味着你可以改变 Minecraft 原版类的行为,而无需替换源代码。


      CoreMod 能做什么?

      • 修改原版游戏逻辑;

      • 修复游戏 Bug;

      • 添加新功能到原版类等等。


      工作原理

      游戏启动 → 类加载器读取 .class 文件 → CoreMod 介入 → 修改字节码 → JVM 加载修改后的类。


      和 Mixin 有什么区别?

      CoreMod 是“底层硬核派”- 直接操作字节码,给你完全的掌控权,但需要自己处理所有细节,就像用汇编语言编程。

      Mixin 是“现代优雅派” - 通过注解和反射实现注入,代码更简洁安全,就像用高级语言编程,让框架处理底层细节。

      简单说 CoreMod 是手动挡,性能极致但复杂;Mixin 是自动挡,开发高效又安全。

      而现在大部分模组都推荐用 Mixin,除非你有非常特殊的底层需求。

      此模组和原生 CoreMod 有什么区别?

      • 使用此模组编写的 CoreMod 可以与正常 Mod 共存;

      • 比原生 CoreMod 更加方便使用,旧版 CoreMod 开发者会更加容易适应。


      模组介绍

      一个为 Minecraft 高版本 Forge 设计的轻量级 CoreMod 框架,基本与旧版本 CoreMod 用法相同。

      理论支持 1.17 及以上所有版本。


      添加本模组依赖

      下载本模组,将模组放入工作区中(通常放在 src 同级文件夹)。

      在 build.gradle 中添加:

      dependencies {
          implementation files("!coremodapi-1.0.jar")
      }

      如果是在 lib 文件夹就改为:

      dependencies {
          implementation files("lib/!coremodapi-1.0.jar")
      }

      再点击同步 Gradle 即可。


      创建 CorePlugin

      编写一个类继承 com.wzz.coremodapi.api.IFMLLoadingPlugin 接口,实现 getASMTransformerClass(转换器)和 injectData(可留空)方法。

      示例代码:

      import com.wzz.coremodapi.api.IFMLLoadingPlugin;
      import java.util.Map;
      @IFMLLoadingPlugin.Name("TestCoreMod") // CoreMod 名字
      public class TestCoreMod implements IFMLLoadingPlugin {
          @Override
          public String[] getASMTransformerClass() {
          //转换器
              return new String[] { TestTransformer.class.getName() };
          }
          @Override
          public void injectData(Map<String, Object> map) {
              // 可以获取 Minecraft 位置等信息
              File mcLocation = (File) data.get("mcLocation");
              File coremodLocation = (File) data.get("coremodLocation");
          }
      }

      此代码创建了一个基础 CoreMod 插件。


      创建类 Transformer 转换器

      编写一个类继承 com.wzz.coremodapi.api.IClassTransformer 接口,实现 transform(转换核心)方法。

      示例代码:

      import com.wzz.coremodapi.api.IClassTransformer;
      import org.objectweb.asm.*;
      public class TestTransformer implements IClassTransformer {
          @Override
          public byte[] transform(String name, byte[] bytes) {
              if (name.equals("net.minecraft.world.entity.LivingEntity") || name.contains("class_1309")) {
                  ClassReader reader = new ClassReader(bytes);
                  ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
                  ClassVisitor visitor = new LivingEntityClassVisitor(Opcodes.ASM9, writer);
                  reader.accept(visitor, 0);
                  return writer.toByteArray();
              }
              return bytes;
          }
          private static class LivingEntityClassVisitor extends ClassVisitor {
              public LivingEntityClassVisitor(int api, ClassVisitor cv) {
                  super(api, cv);
              }
              @Override
              public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                  MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
                  // 使用混淆后的名字
                  if ((name.equals("m_21223_") || name.equals("getHealth") || name.equals("method_6032")) && descriptor.equals("()F")) {
                      return new MethodVisitor(Opcodes.ASM9, mv) {
                          @Override
                          public void visitCode() {
                              super.visitCode();
                              mv.visitLdcInsn(10.0f); // 让血量返回 10 测试
                              mv.visitInsn(Opcodes.FRETURN);
                          }
                          @Override
                          public void visitMaxs(int maxStack, int maxLocals) {
                              mv.visitMaxs(1, 1);
                          }
                      };
                  }
                  return mv;
              }
          }
      }

      此代码实现了让所有生物血量强制返回 10。


      在 MANIFEST.MF 添加 CoreMod 插件

      跟旧版本 CoreMod 相同,你只需添加一行 FMLCorePlugin: ClassPath 即可,ClasPath 为 CoreMod 插件类路径。

      在 build.gradle 添加:

      jar {
          manifest {
              attributes(
                      'FMLCorePlugin': 'classpath' // 类路径
              )
          }
      }


      注意事项

      • 本模组不保证多个类转换器的兼容性,仅提供基础框架;

      • CoreMod 部分报错是没有日志的,莫名其妙直接崩溃。


      使用 Javassist 转换(不推荐)

      由于 MC 本身 没有 Javassist 的依赖,你需要手动在 build.gradle 添加。

      dependencies {
          implementation 'org.javassist:javassist:3.30.2-GA'
      }


      Transformer 转换器示例

      import com.wzz.coremodapi.api.IClassTransformer;
      import javassist.*;
      public class TestTransformer implements IClassTransformer {
          @Override
          public byte[] transform(String name, byte[] bytes) {
              if (name.equals("net.minecraft.world.entity.LivingEntity")) {
                  try {
                      return modifyLivingEntity(bytes);
                  } catch (Exception e) {
                      System.err.println("Failed to transform LivingEntity with Javassist: " + e.getMessage());
                      e.printStackTrace();
                  }
              }
              return bytes;
          }
          private byte[] modifyLivingEntity(byte[] classBytes) throws Exception {
              ClassPool classPool = ClassPool.getDefault();
              
              CtClass ctClass = classPool.makeClass(new java.io.ByteArrayInputStream(classBytes));
              
              // 修改getHealth方法
              modifyGetHealthMethod(ctClass);
              
              // 返回修改后的字节码
              byte[] modifiedBytes = ctClass.toBytecode();
              ctClass.detach();
              return modifiedBytes;
          }
          private void modifyGetHealthMethod(CtClass ctClass) throws Exception {
              try {
                  // 尝试获取getHealth方法
                  CtMethod getHealthMethod = null;
                  
                  // 先尝试正式方法名
                  try {
                      getHealthMethod = ctClass.getDeclaredMethod("getHealth");
                  } catch (NotFoundException e) {
                      // 如果正式名不存在,尝试混淆名
                      try {
                          getHealthMethod = ctClass.getDeclaredMethod("m_21223_");
                      } catch (NotFoundException e2) {
                          System.err.println("getHealth method not found in LivingEntity");
                          return;
                      }
                  }
                  
                  // 完全替换方法体,直接返回 10.0f
                  getHealthMethod.setBody("{ return 10.0F; }");
                  
                  System.out.println("Successfully modified getHealth method to return 10.0f");
                  
              } catch (Exception e) {
                  System.err.println("Error modifying getHealth method: " + e.getMessage());
                  throw e;
              }
          }
      }


      在方法前后插入代码

      import com.wzz.coremodapi.api.IClassTransformer;
      import javassist.*;
      public class TestTransformer implements IClassTransformer {
          @Override
          public byte[] transform(String name, byte[] bytes) {
              if (name.equals("net.minecraft.world.entity.LivingEntity")) {
                  try {
                      return modifyLivingEntity(bytes);
                  } catch (Exception e) {
                      System.err.println("Failed to transform LivingEntity: " + e.getMessage());
                      e.printStackTrace();
                  }
              }
              return bytes;
          }
          private byte[] modifyLivingEntity(byte[] classBytes) throws Exception {
              ClassPool classPool = ClassPool.getDefault();
              CtClass ctClass = classPool.makeClass(new java.io.ByteArrayInputStream(classBytes));
              
              try {
                  // 在getHealth方法前插入日志
                  insertLoggingBeforeGetHealth(ctClass);
                  
                  // 修改返回值(如果血量大于10则返回10)
                  modifyGetHealthReturnValue(ctClass);
                  
                  byte[] modifiedBytes = ctClass.toBytecode();
                  ctClass.detach();
                  return modifiedBytes;
                  
              } catch (Exception e) {
                  ctClass.detach();
                  throw e;
              }
          }
          private void insertLoggingBeforeGetHealth(CtClass ctClass) throws Exception {
              try {
                  CtMethod getHealthMethod = getGetHealthMethod(ctClass);
                  if (getHealthMethod != null) {
                      // 在方法开始处插入日志
                      getHealthMethod.insertBefore(
                          "System.out.println(\"[Javassist] getHealth called for: \" + this.getDisplayName().getString());"
                      );
                  }
              } catch (NotFoundException e) {
                  System.err.println("getHealth method not found for logging");
              }
          }
          private void modifyGetHealthReturnValue(CtClass ctClass) throws Exception {
              try {
                  CtMethod getHealthMethod = getGetHealthMethod(ctClass);
                  if (getHealthMethod != null) {
                      // 替换方法体,实现逻辑:如果原血量 >10 则返回 10,否则返回原值
                      getHealthMethod.setBody(
                          "{" +
                          "  float originalHealth = $proceed($$);" +
                          "  System.out.println(\"[Javassist] Original health: \" + originalHealth);" +
                          "  return originalHealth > 10.0F ? 10.0F : originalHealth;" +
                          "}"
                      );
                      System.out.println("Modified getHealth return value logic");
                  }
              } catch (NotFoundException e) {
                  System.err.println("getHealth method not found for modification");
              }
          }
          private CtMethod getGetHealthMethod(CtClass ctClass) {
              try {
                  return ctClass.getDeclaredMethod("getHealth");
              } catch (NotFoundException e1) {
                  try {
                      return ctClass.getDeclaredMethod("m_21223_");
                  } catch (NotFoundException e2) {
                      return null;
                  }
              }
          }
      }


      原生 CoreMod 与本模组使用对比

      注意:在 Fabric 和 高版本(>=1.21.9) NeoForge 没有 CoreMod,由本 API 独立实现。


      创建转换服务接口

      编写一个类继承 cpw.mods.modlauncher.api.ITransformationService。

      需要实现:name,onLoad,transformers,initialize 方法。

      代码示例:

      public class ExampleTransformationService implements ITransformationService {
          private static final Logger LOGGER = LogManager.getLogger();
          @Override
          public String name() {
              return "example_transform_service"; // 服务名称
          }
          @Override
          public void initialize(IEnvironment environment) {
              LOGGER.info("Initializing ExampleTransformationService");
              // 在这里可以进行环境初始化
              // 可以获取游戏目录、配置目录等
          }
          @Override
          public void onLoad(IEnvironment env, Set<String> otherServices) {
              LOGGER.info("ExampleTransformationService loading, other services: {}", otherServices);
              // 在这里可以检查其他服务,处理依赖关系
          }
          @Override
          public @NotNull List<ITransformer> transformers() {
              LOGGER.info("Registering transformers");
              // 返回你的转换器列表
              return List.of(
                  new ExampleClassTransformer(),
                  new ExampleMethodTransformer()
              );
          }
      }


      创建字节码转换器

      编写一个类继承 cpw.mods.modlauncher.api.ITransformer。

      需要实现:transform,castVote,targets 方法。

      代码示例:

      public class ExampleClassTransformer implements ITransformer<ClassNode> {
          
          @Override
          public @NotNull ClassNode transform(ClassNode input, ITransformerVotingContext context) {
              // 在这里修改 ClassNode
              // 例如:添加字段、修改方法等
              
              // 示例:打印被转换的类名
              System.out.println("Transforming class: " + input.name);
              
              // 返回修改后的 ClassNode
              return input;
          }
          @Override
          public @NotNull TransformerVoteResult castVote(ITransformerVotingContext context) {
              // 决定是否要处理这个类
              // 通常根据类名来决定
              return TransformerVoteResult.YES;
          }
          @Override
          public @NotNull Set<Target> targets() {
              // 指定要转换的目标类
              return Set.of(
                  Target.targetClass("net.minecraft.server.MinecraftServer"),
                  Target.targetClass("net.minecraft.world.level.Level")
              );
          }
      }

      投票机制详解

      在 castVote 方法中,你需要返回适当的 TransformerVoteResult:

      • YES: 希望转换这个目标,愿意参与投票;

      • NO: 不希望转换这个目标,退出投票;

      • DEFER: 暂时不决定,等待后续投票轮次;

      • REJECT: 强烈反对转换(可能导致游戏崩溃,慎用)。


      服务注册

      在 src/main/resources/META-INF/services/ 目录下创建文件:

      • 文件名: cpw.mods.modlauncher.api.ITransformationService;

      • 文件内容:com.example.yourmod.core.ExampleTransformationService。

      此路径填写你的创建转换服务接口路径。


      原生 CoreMod 注意事项

      使用原生 CoreMod 会导致正常模组无法加载,请勿在正常 Mod 中创建 CoreMod,建议单独开一个工作区使用。

    短评加载中..