You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Mixin中定位i++语句并按需取消其执行

Minecraft Forge 字节码注入:取消特定条件下的i++执行

问题场景

我在AnvilScreenHandler类的updateResult()方法中有如下逻辑:

public void updateResult() {
    ItemStack itemStack = this.input.getStack(0);
    int i = 0;
    int j = 0;
    int k = 0;
    /***/
    for (int m = 0; l > 0 && m < itemStack3.getCount(); m++) {
       int n = itemStack2.getDamage() - l;
       itemStack2.setDamage(n);
       i++;
       l = Math.min(itemStack2.getDamage(), itemStack2.getMaxDamage() / 4);
    }
/***/
}

需要实现:当itemStack2.getItem() instanceof MyInterface时,跳过循环内的i++执行,但无法准确定位这个自增语句。

尝试过的失败方案

  1. @Inject定位失败
    尝试在setDamage调用后注入逻辑,编译报错:

    @Inject(method = "updateResult", at = @At(value = "INVOKE", target = 
        "Lnet/minecraft/item/ItemStack;setDamage(I)V", shift = At.Shift.BY, by = 2))
    private void resetRepair(CallbackInfo ci, @Local(ordinal = 1) ItemStack itemStack2,
        @Local(ordinal = 0) LocalRef<Integer> i) {
            if (itemStack2.getItem() instanceof MyInterface) i.set(0);
    }
    

    错误信息:

    @Local does not match any or matched multiple local variables in the target method

  2. @Redirect找不到目标
    不清楚i++对应的字节码target,写法陷入困境:

    @Redirect(method = "updateResult", at = @At(value = "INVOKE", target = "WHAT IS THE TARGET OF i++"))
    
  3. @ModifyConstant/@ModifyVariable无效

    • 使用@ModifyConstant(method = "updateResult", constant = @Constant(intValue = 1))无法定位目标行
    • 使用@ModifyVariable(method = "updateResult", at = @At("STORE"))无法定位,日志输出变量值为0或15,非预期的1

正确解决方案

方案一:用@ModifyVariable修改自增增量

i++对应的字节码是IINC指令,直接针对这个指令修改增量值:

import net.minecraft.item.ItemStack;
import net.minecraft.screen.AnvilScreenHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(AnvilScreenHandler.class)
public class AnvilScreenHandlerMixin {
    @ModifyVariable(
        method = "updateResult",
        at = @At(value = "INC", ordinal = 0), // ordinal需根据实际字节码确认,对应循环内的i++
        argsOnly = true
    )
    private int cancelIncrement(int increment, @Local ItemStack itemStack2) {
        // 满足条件时返回0(不增加),否则返回原增量1
        return itemStack2.getItem() instanceof MyInterface ? 0 : increment;
    }
}

注意:ordinal的值需要通过查看updateResult的字节码确认,因为方法中可能存在多个IINC指令,要确保选中循环里的i++对应的那一个。

方案二:用@Redirect拦截IINC操作

直接拦截自增指令,根据条件决定是否执行:

import net.minecraft.item.ItemStack;
import net.minecraft.screen.AnvilScreenHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(AnvilScreenHandler.class)
public class AnvilScreenHandlerMixin {
    @Redirect(
        method = "updateResult",
        at = @At(value = "IINC", ordinal = 0)
    )
    private void redirectIncrement(AnvilScreenHandler instance, int index, int increment, @Local ItemStack itemStack2) {
        // 仅当不满足条件时执行自增
        if (!(itemStack2.getItem() instanceof MyInterface)) {
            // 由于i是局部变量,方案一的ModifyVariable更简便,此例仅作演示
            try {
                var field = AnvilScreenHandler.class.getDeclaredField("i"); // 注意:如果i是局部变量,此方法无效
                field.setAccessible(true);
                field.setInt(instance, field.getInt(instance) + increment);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

注意:如果i是方法内的局部变量,反射字段的方式不可行,此时优先选择方案一。

原错误原因解析

  • 之前@Inject的LocalRef<Integer> i写法错误,局部变量不能用LocalRef包装,且shift = At.Shift.BY的偏移量计算不准确,导致无法正确捕获变量。
  • i++不是方法调用,因此不能用@Redirect的INVOKE类型,必须针对IINC字节码指令。

内容的提问来源于stack exchange,提问作者DanilMK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:03:14