如何通过AspectJ拦截所有Minecraft Forge Mod的IRecipe assemble()方法?
问题解答
可以用AspectJ拦截所有已安装mod中IRecipe.assemble()方法的调用,且不需要修改FML。以下是具体实现方案,结合Forge的ModLauncher类加载机制完成:
核心原理
Forge的ModLauncher本身支持字节码转换扩展,无需修改FML源码,只需要通过自定义Transformer集成AspectJ的加载时织入(LTW)能力,就能在所有mod类加载时自动织入切面逻辑。
具体实现步骤
1. Gradle依赖配置
在你的mod项目build.gradle中添加AspectJ的运行时和织入器依赖:
dependencies { // AspectJ运行时库 implementation 'org.aspectj:aspectjrt:1.9.9.1' // AspectJ加载时织入器 runtimeOnly 'org.aspectj:aspectjweaver:1.9.9.1' }
注:1.16版本MC基于Java 8,需选择支持Java 8的AspectJ版本(如1.9.x系列)
2. 编写AspectJ切面
创建切面类,拦截所有IRecipe.assemble()方法的调用,实现你的配方验证逻辑:
public aspect RecipeAssembleInterceptor { // 定义切点:匹配所有IRecipe接口assemble方法的调用 pointcut assembleCall(ContainerLevelAccess container, IInventory inventory): call(* net.minecraft.world.item.crafting.IRecipe.assemble(..)) && args(container, inventory); // 前置通知:在方法调用前执行验证 before(ContainerLevelAccess container, IInventory inventory): assembleCall(container, inventory) { IRecipe<?> targetRecipe = (IRecipe<?>) thisJoinPoint.getTarget(); // 替换为你的配方解锁验证逻辑 if (!isRecipeUnlocked(targetRecipe)) { throw new IllegalStateException("该配方未解锁,无法合成"); } } // 示例:自定义验证方法 private boolean isRecipeUnlocked(IRecipe<?> recipe) { // 这里实现你的配方阶段检查逻辑 return true; } }
使用call()切点而非execution(),确保拦截所有mod中对该方法的调用,不管调用方来自哪个模块
3. 配置AspectJ LTW规则
在项目resources/META-INF目录下创建aop.xml,声明切面和织入范围:
<aspectj> <weaver options="-verbose -showWeaveInfo"> <!-- 对所有类进行织入,包括其他mod的类 --> <include within="*"/> </weaver> <aspects> <!-- 替换为你的切面类全限定名 --> <aspect name="com.yourmod.aspect.RecipeAssembleInterceptor"/> </aspects> </aspectj>
4. 集成AspectJ到Forge的类加载流程
创建自定义Transformer,让ModLauncher在加载所有类时调用AspectJ织入器:
import net.minecraftforge.fml.loading.moddiscovery.IModTransformer; import org.aspectj.weaver.Weaver; public class AspectJWeaverTransformer implements IModTransformer { private final Weaver weaver; public AspectJWeaverTransformer() { // 初始化织入器,加载aop.xml配置 this.weaver = new Weaver(); weaver.setClassLoader(this.getClass().getClassLoader()); weaver.process(); } @Override public byte[] transform(String className, byte[] classBytes, ClassLoader classLoader) { // 让AspectJ处理当前类的字节码 return weaver.weave(className.replace('.', '/'), classBytes, classLoader); } }
然后在resources/META-INF/services目录下创建文件net.minecraftforge.fml.loading.moddiscovery.IModTransformer,写入你的Transformer全限定名:
com.yourmod.transformer.AspectJWeaverTransformer
这样ModLauncher会在加载所有mod类时自动触发AspectJ的织入逻辑。
关键注意事项
- 类加载器隔离:确保AspectJ织入器使用的类加载器能访问到所有mod的类,避免因类加载器隔离导致织入失败。
- 混淆兼容:若目标mod开启了代码混淆,只要其类仍实现
IRecipe接口,切点依然能匹配到assemble()方法。 - 调试:开启
aop.xml中的-verbose -showWeaveInfo选项,可在控制台查看织入日志,排查未拦截的情况。
内容的提问来源于stack exchange,提问作者user14573544
相关产品推荐
相关产品推荐

