前言
什么是 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,建议单独开一个工作区使用。



