IC2 作物 Mixin
[!INFO] Git Commit:
d3dbd5f| Updated: 2026-10-01
本 mod 通过 GTNH 的 LateMixin 机制(com.gtnewhorizon.gtnhmixins)改写 IC2 的两个类。
Mixin 只在 IC2 存在时应用,usesMixins = true、mixinsPackage = mixins。
装载机制:为什么 mixin JSON 里没有 mixins 列表
资源目录下有两个 mixin 配置:
| 文件 | package |
mixins 数组 |
被谁引用 |
|---|---|---|---|
mixins.croploadcore.json |
❌ 无 | ❌ 无 | 无 |
mixins.croploadcore.late.json |
com.github.bartimaeusnek.mixins.late |
❌ 无 | CropsLateMixinsLoader.getMixinConfig() |
两个 JSON 都不含 mixins 数组,看似空配置,但实际生效的是下面这个加载器:
@LateMixin
public class CropsLateMixinsLoader implements ILateMixinLoader {
public String getMixinConfig() { return "mixins.croploadcore.late.json"; }
@Nonnull
public List<String> getMixins(Set<String> loadedMods) {
List<String> mixins = new ArrayList<>();
if (loadedMods.contains("IC2")) {
mixins.add("MixinBlockCrop");
mixins.add("MixinItemWeedingTrowel");
}
return mixins;
}
}
CropsLateMixinsLoader.java:12-29。mixin 列表在运行期由 getMixins(loadedMods) 动态产出,
因此 JSON 里不需要静态列表。@LateMixin 意味着这些 mixin 在 mod 类加载之后才应用——
这正是改写 IC2 自身类的前提。
mixins.croploadcore.json(非 late 版)在整个仓库中没有任何引用(已 grep 确认,
Java / gradle / json 中均无 croploadcore.json 字符串),且它既无 package 也无 mixins,
即便被加载也是空操作。属于遗留文件。
两个 JSON 的共同配置:required: true、minVersion: 0.8.5-GTNH、
refmap: mixins.croploadcore.refmap.json、target: @env(DEFAULT)、
compatibilityLevel: JAVA_8。
MixinBlockCrop —— 作物方块
@Mixin(BlockCrop.class),@Overwrite 两个方法。目标类为 ic2.core.crop.BlockCrop。
canPlaceBlockAt(MixinBlockCrop.java:25-33)
@Overwrite
public boolean canPlaceBlockAt(World world, int x, int y, int z) {
return world.getBlock(x, y - 1, z).canSustainPlant(
world, x, y, z, ForgeDirection.UP,
new ItemSeedFood(4, 0.6F, Blocks.carrots, Blocks.farmland));
}
把原版"必须是 farmland"的硬判定换成 canSustainPlant(UP) 查询——任何声明能支撑
ItemSeedFood 向上生长的方块都可以承载 IC2 作物,这就是 Mods O’ Plenty 的植物、
Thaumcraft 的污秽纤维、模组化的土壤等能种作物的技术前提。
new ItemSeedFood(4, 0.6F, Blocks.carrots, Blocks.farmland) 每次调用都新建一个实例,
其中的 4 与 0.6F 是 BlockCrops 的原版常量(4 阶段成熟、0.6 生长速度),
传入 Blocks.carrots 与 Blocks.farmland 是为了匹配原版萝卜作物的 canSustainPlant 签名。
注意这里只传了 Blocks.carrots(萝卜)一种——理论上只声明能种萝卜的方块会被认可;
写死是刻意为之,与 MixinItemWeedingTrowel 中支持自定义杂草的做法形成对比。
onNeighborBlockChange(MixinBlockCrop.java:41-52)
@Overwrite
public void onNeighborBlockChange(World world, int x, int y, int z, Block neighbor) {
if (!this.canPlaceBlockAt(world, x, y, z)) {
world.setBlockToAir(x, y, z);
GameRegistry.findBlock("IC2", "blockCrop").dropBlockAsItem(world, x, y, z, 0, 0);
} else {
TileEntity te = world.getTileEntity(x, y, z);
if (!(te instanceof TileEntityCrop)) return;
((TileEntityCrop) te).onNeighbourChange();
}
}
行为分两支:
- 下方方块不再支撑植物 → 移除方块并以物品形式掉落(
dropBlockAsItem(..., 0, 0),无随机/玩家破坏加成); - 仍支撑 → 只把变更通知 TileEntity,由
TileEntityCrop.onNeighbourChange()处理。
dropBlockAsItem 通过 GameRegistry.findBlock("IC2", "blockCrop") 按名字反射查表取方块实例,
而不是持有静态引用——这是 mixin 无法注入字段时的折中写法。
方法上的 Javadoc 写的是 Patching CropBlock to not pop off when neighbours change and not placed on farmland, but on any other soil。需注意:代码仍会在土壤失效时把作物弹出(第一分支),
注释强调的"不弹出"指的是不再因为非土壤邻居变化而误判——判定依据已从"邻居是什么"
换成"正下方是否支撑植物"。
MixinItemWeedingTrowel —— 作物锄
@Mixin(ItemWeedingTrowel.class),@Overwrite(remap = false) 一个方法。
目标类为 ic2.core.item.tool.ItemWeedingTrowel。remap = false 表示方法体中的
IC2 成员名不做混淆映射——因为方法已在 mixin 侧显式声明为 IC2 的签名。
@Overwrite(remap = false)
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
float hitX, float hitY, float hitZ) {
if (world.isRemote) return false;
TileEntity te = world.getTileEntity(x, y, z);
if (te instanceof TileEntityCrop) {
TileEntityCrop crop = (TileEntityCrop) te;
if (crop.getCrop() != null && crop.getCrop().tier() < 1) {
StackUtil.dropAsEntity(world, x, y, z, new ItemStack(Ic2Items.weed.getItem(), crop.size));
crop.reset();
return true;
}
if (crop.weedlevel > 0) {
StackUtil.dropAsEntity(world, x, y, z, new ItemStack(Ic2Items.weed.getItem(), crop.weedlevel));
crop.weedlevel = 0;
crop.updateState();
return true;
}
}
return false;
}
MixinItemWeedingTrowel.java:24-45。Javadoc 的理由是 for WeedingTrowel to accept custom Weeds。
两条路径都把 IC2 原生的 Ic2Items.weed 掉落物换成等价物并清零状态:
| 条件 | 掉落数量 | 之后 |
|---|---|---|
作物的 tier() < 1 |
crop.size(作物大小) |
crop.reset() |
crop.weedlevel > 0 |
crop.weedlevel(杂草等级) |
weedlevel = 0 + updateState() |
第一分支处理的是"尚未成熟的作物本体"(tier 0),第二分支处理已成熟作物上的杂草层。
两条路径互斥且按顺序判定。客户端短路(if (world.isRemote) return false;)保证掉落只发生一次。
非 TileEntityCrop 或无杂草时返回 false,交回原版逻辑。
由于本 mod 是核心库而非独立玩法 mod,这个 mixin 的效果对所有安装了 IC2 的整合包生效, 不依赖任何 BerriesPP 等下游 mod 是否存在。