#[Solved] Correct way to make a custom `FarmBlock` on Fabric? (1.21.10)

29 messages · Page 1 of 1 (latest)

wispy aspen
#

Hello! I'm porting my custom farmland block to Fabric (from NeoForge) and I noticed that by default crops (as defined in CropBlock) can't be placed and don't grow on any Child class of FarmBlock.
After checking the source, it's obviously because CropBlock.mayPlaceOn() and CropBlock.getGrowthSpeed() don't check for instanceof net.minecraft.world.level.block.FarmBlock like it does on NeoForge, but rather check if the BlockState itself is Blocks.FARMLAND through blockState.is(Blocks.FARMLAND).

What is the most correct way, then, to ensure crops can grow correctly on my own Farmland? (In order of what I think is most correct to least):

  1. Mixin into CropBlock, replacing these individual lines with proper instanceof checks? (I'd like to avoid Mixin if possible)
  2. Naively @Override getGrowthSpeed() and mayPlaceOn() in my Child class? (Duplicating code like this is bad, and also isn't compatible with other mods) (edit-- this doesn't make any sense. I don't have a child class of CropBlock, I have a child of FarmBlock.)

Let me know if I am missing something obvious! I am new to the Fabric ecosystem so I'd appreciate some patience if I said something completely off-base. Thanks!

tulip plaza
#

fabric might already mixin there and add a block tag check

wispy aspen
#

editted because i messed up some function names

#

Do you know what the Mixin is called?

tulip plaza
#

just had a look i don’t think it does, mixin will be your best bet here

wispy aspen
#

Dang, okay. Thanks for the help! Was trying to avoid Mixin but I guess I'll have to pony up and actually try and understand the non-accesstransformer part of those docs

tulip plaza
#

i’d use a WrapMethod from mixin extras and return original || your own check to make sure other peoples stuff works too

wispy aspen
#

Okay so I got as far to make CropBlock.mayPlaceOn() work (yippee!), however I am currently stuck on writing my Mixin to inject into CropBlock.getGrowthSpeed()

#
...
@ModifyExpressionValue(method = "getGrowthSpeed", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;is(Lnet/minecraft/world/level/block/Block;)Z"))
    private static boolean getGrowthSpeedCheckFarmland(boolean original, @Local BlockState blockState) {
        return original || blockState.getBlock() instanceof net.minecraft.world.level.block.FarmBlock;
    }
#

The issue is-- I can't narrow down @Local BlockState blockState to reference the only blockState local variable of the function ("@Local does not match any or matched multiple local variables in the target method")

#

but neither ordinal=0, index=0, or even name = "blockState" work

#

And it's definitely something to do with the fact I'm trying to reference a local variable within a (2-nested) for loop

#

whats confusing me though is if we follow the rules of local variables there should only be 1 blockState in the table for each iteration of the loop-- so is my @At not scoped correctly?

#

hmm, java bytecode says there are indeed blockState local variables as I suspected: LOCALVARIABLE blockState Lnet/minecraft/world/level/block/state/BlockState; L10 L17 8

#

dunno what L10 L17 8 means at the end though

#

they are labels duh

#

labels 10 and 17

#

and 8 is the registry we stored my first blockState in

wispy aspen
#

okay im officially lost in the sauce of MixinExtras and i'm calling it for the night

#
            from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/BlockGetter;getBlockState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;")
    ))
    private static boolean getGrowthSpeedCheckFarmland(boolean original, @Local BlockState blockState) {
        return original || blockState.getBlock() instanceof net.minecraft.world.level.block.FarmBlock;
    }```
#

tried adding an @Slice annotation to help Mixin lean into the correct direction and correctly find my @Local but alas it's no use

#

surely someone has Mixin'd and wants access to a local variable in a for loop and will see this post winktato

#

the function is CropBlock.getGrowthSpeed() smol

wispy aspen
#

after running @(local print = true)-- I can see that the blockstate I want to have as my local is only available in the LVT during my mixin on the first iteration of the for loop (of 9 total):

wispy aspen
#

Yippee-- Figured it out:

#

had to use an @Expression

#
@Definition(id = "blockState", local = @Local(type = BlockState.class))
@Definition(id = "is", method = "Lnet/minecraft/world/level/block/state/BlockState;is(Lnet/minecraft/world/level/block/Block;)Z")
@Expression("blockState.is(?)")
@ModifyExpressionValue(method = "getGrowthSpeed", at = @At(value = "MIXINEXTRAS:EXPRESSION"))
private static boolean getGrowthSpeedCheckFarmland(boolean original, @Local BlockState blockState) {
   return original || blockState.getBlock() instanceof net.minecraft.world.level.block.FarmBlock;
}