From dabe919492cea8dec022afadd944bfc186a9053c Mon Sep 17 00:00:00 2001 From: LexManos Date: Thu, 25 Jun 2026 08:46:22 -0700 Subject: [PATCH] Remove IForgeShearable Closes #10826 Entities use vanilla's Shearable interface Blocks use loot table conditions or useItemOn if you need positional information --- .gitignore | 1 + .../world/entity/Shearable.java.patch | 22 +-- .../entity/animal/cow/MushroomCow.java.patch | 41 +----- .../animal/golem/CopperGolem.java.patch | 21 +-- .../entity/animal/golem/SnowGolem.java.patch | 29 +--- .../entity/animal/sheep/Sheep.java.patch | 47 +------ .../monster/cubemob/SulfurCube.java.patch | 9 ++ .../entity/monster/skeleton/Bogged.java.patch | 20 +-- .../world/item/ShearsItem.java.patch | 27 ++-- .../level/block/DryVegetationBlock.java.patch | 11 -- .../world/level/block/LeavesBlock.java.patch | 11 -- .../level/block/SeagrassBlock.java.patch | 11 -- .../level/block/TallGrassBlock.java.patch | 11 -- .../world/level/block/VineBlock.java.patch | 9 -- .../world/level/block/WebBlock.java.patch | 11 -- .../model/geom/ModelLayers.java.patch.rej | 24 ---- .../feature/BlockFeatureRenderer.java.patch | 12 -- .../rendertype/RenderSetup.java.patch.rej | 67 --------- .../common/IForgeShearable.java | 62 --------- .../custom_shears_shear_bogged.json | 7 + .../custom_shears_shear_mooshroom.json | 7 + .../custom_shears_shear_sheep.json | 7 + .../custom_shears_shear_snowgolem.json | 7 + ...custom_shears_shear_sulfur_cube_block.json | 7 + .../gameplay/item/ShearsBehaviorTest.java | 129 +++++++++++++++--- 25 files changed, 186 insertions(+), 424 deletions(-) delete mode 100644 patches/minecraft/net/minecraft/world/level/block/DryVegetationBlock.java.patch delete mode 100644 patches/minecraft/net/minecraft/world/level/block/LeavesBlock.java.patch delete mode 100644 patches/minecraft/net/minecraft/world/level/block/SeagrassBlock.java.patch delete mode 100644 patches/minecraft/net/minecraft/world/level/block/TallGrassBlock.java.patch delete mode 100644 patches/minecraft/net/minecraft/world/level/block/WebBlock.java.patch delete mode 100644 rejects/net/minecraft/client/model/geom/ModelLayers.java.patch.rej delete mode 100644 rejects/net/minecraft/client/renderer/feature/BlockFeatureRenderer.java.patch delete mode 100644 rejects/net/minecraft/client/renderer/rendertype/RenderSetup.java.patch.rej delete mode 100644 src/main/java/net/minecraftforge/common/IForgeShearable.java create mode 100644 src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_bogged.json create mode 100644 src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_mooshroom.json create mode 100644 src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sheep.json create mode 100644 src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_snowgolem.json create mode 100644 src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sulfur_cube_block.json diff --git a/.gitignore b/.gitignore index 4dcbfe411a..d3754c4c8f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ /projects/**/run/ /projects/**/*.launch /repo/ +/rejects-*/ src/*/generated/**/.cache/ # Generated by gradle every import diff --git a/patches/minecraft/net/minecraft/world/entity/Shearable.java.patch b/patches/minecraft/net/minecraft/world/entity/Shearable.java.patch index cefe59c316..5dcb3d2016 100644 --- a/patches/minecraft/net/minecraft/world/entity/Shearable.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/Shearable.java.patch @@ -1,18 +1,20 @@ --- a/net/minecraft/world/entity/Shearable.java +++ b/net/minecraft/world/entity/Shearable.java -@@ -4,8 +_,14 @@ - import net.minecraft.sounds.SoundSource; - import net.minecraft.world.item.ItemStack; - --public interface Shearable { -+public interface Shearable extends net.minecraftforge.common.IForgeShearable { -+ /** @deprecated Use {@link net.minecraftforge.common.IForgeShearable#onSheared} */ +@@ -8,4 +_,17 @@ void shear(ServerLevel level, SoundSource soundSource, ItemStack tool); -+ /** @deprecated Use {@link net.minecraftforge.common.IForgeShearable#isShearable} */ boolean readyForShearing(); + -+ default boolean isShearable(net.minecraft.world.item.ItemStack item, net.minecraft.world.level.Level level, net.minecraft.core.BlockPos pos) { -+ return readyForShearing(); ++ /** Equivalent to {@link shear} but returns a list of ItemStacks without actually spawning the entity into the world */ ++ default java.util.List shearItems(ServerLevel level, SoundSource soundSource, ItemStack tool) { ++ var self = (Entity)this; ++ var entities = new java.util.ArrayList(); ++ self.captureDrops(entities); ++ shear(level, soundSource, tool); ++ self.captureDrops(null); ++ var items = new java.util.ArrayList(entities.size()); ++ for (var entity : entities) ++ items.add(entity.getItem()); ++ return items; + } } diff --git a/patches/minecraft/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch b/patches/minecraft/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch index 53566746b4..9bb7975d6a 100644 --- a/patches/minecraft/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/animal/cow/MushroomCow.java.patch @@ -5,53 +5,24 @@ this.playSound(milkSound, 1.0F, 1.0F); return InteractionResult.SUCCESS; - } else if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { -+ } else if (false && itemStack.is(Items.SHEARS) && this.readyForShearing()) { ++ } else if (itemStack.canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST) && this.readyForShearing()) { if (this.level() instanceof ServerLevel level) { this.shear(level, SoundSource.PLAYERS, itemStack); this.gameEvent(GameEvent.SHEAR, player); -@@ -178,15 +_,26 @@ +@@ -178,6 +_,8 @@ @Override public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { -+ for (var stack : shearInternal(level, soundSource, tool)) { -+ for (int i = 0; i < stack.getCount(); i++) { -+ this.level().addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), stack.copyWithCount(1))); -+ } -+ } -+ } -+ -+ private java.util.List shearInternal(ServerLevel level, SoundSource soundSource, ItemStack tool) { -+ var ret = new java.util.ArrayList(); + if (!net.minecraftforge.event.ForgeEventFactory.canLivingConvert(this, EntityTypes.COW, time -> {})) -+ return ret; ++ return; level.playSound(null, this, SoundEvents.MOOSHROOM_SHEAR, soundSource, 1.0F, 1.0F); this.convertTo(EntityTypes.COW, ConversionParams.single(this, false, false), cow -> { level.sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(0.5), this.getZ(), 1, 0.0, 0.0, 0.0, 0.0); - this.dropFromShearingLootTable(level, BuiltInLootTables.SHEAR_MOOSHROOM, tool, (l, drop) -> { -- for (int i = 0; i < drop.getCount(); i++) { -- l.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), drop.copyWithCount(1))); -- } -+ ret.add(tool); +@@ -186,6 +_,7 @@ + l.addFreshEntity(new ItemEntity(this.level(), this.getX(), this.getY(1.0), this.getZ(), drop.copyWithCount(1))); + } }); + net.minecraftforge.event.ForgeEventFactory.onLivingConvert(this, cow); }); -+ return ret; } - @Override -@@ -267,6 +_,15 @@ - } - - return babyVariant; -+ } -+ -+ @Override -+ public java.util.List onSheared(@org.jetbrains.annotations.Nullable Player player, @org.jetbrains.annotations.NotNull ItemStack item, Level world, BlockPos pos, int fortune) { -+ if (world instanceof ServerLevel server) { -+ this.gameEvent(GameEvent.SHEAR, player); -+ return shearInternal(server, player == null ? SoundSource.BLOCKS : SoundSource.PLAYERS, item); -+ } -+ return java.util.Collections.emptyList(); - } - - public enum Variant implements StringRepresentable { diff --git a/patches/minecraft/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch b/patches/minecraft/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch index 09badef295..4c1a2e6a52 100644 --- a/patches/minecraft/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/animal/golem/CopperGolem.java.patch @@ -5,26 +5,7 @@ Level level = this.level(); - if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { -+ if (false && itemStack.is(Items.SHEARS) && this.readyForShearing()) { // Forge: Moved to onSheared ++ if (itemStack.canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST) && this.readyForShearing()) { if (level instanceof ServerLevel serverLevel) { this.shear(serverLevel, SoundSource.PLAYERS, itemStack); this.gameEvent(GameEvent.SHEAR, player); -@@ -435,6 +_,18 @@ - ItemStack itemStack = this.getItemBySlot(EQUIPMENT_SLOT_ANTENNA); - this.setItemSlot(EQUIPMENT_SLOT_ANTENNA, ItemStack.EMPTY); - this.spawnAtLocation(level, itemStack, 1.5F); -+ } -+ -+ @Override -+ public java.util.List onSheared(@Nullable Player player, ItemStack item, Level world, net.minecraft.core.BlockPos pos, int fortune) { -+ if (world instanceof ServerLevel server) { -+ server.playSound(null, this, SoundEvents.COPPER_GOLEM_SHEAR, player == null ? SoundSource.BLOCKS : SoundSource.PLAYERS, 1.0F, 1.0F); -+ var ret = new java.util.ArrayList(); -+ ret.add(this.getItemBySlot(EQUIPMENT_SLOT_ANTENNA)); -+ this.setItemSlot(EQUIPMENT_SLOT_ANTENNA, ItemStack.EMPTY); -+ return ret; -+ } -+ return java.util.Collections.emptyList(); - } - - @Override diff --git a/patches/minecraft/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch b/patches/minecraft/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch index efa1ec8305..cf1375a26d 100644 --- a/patches/minecraft/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch @@ -1,14 +1,5 @@ --- a/net/minecraft/world/entity/animal/golem/SnowGolem.java +++ b/net/minecraft/world/entity/animal/golem/SnowGolem.java -@@ -42,7 +_,7 @@ - import net.minecraft.world.phys.Vec3; - import org.jspecify.annotations.Nullable; - --public class SnowGolem extends AbstractGolem implements RangedAttackMob, Shearable { -+public class SnowGolem extends AbstractGolem implements RangedAttackMob, Shearable, net.minecraftforge.common.IForgeShearable { - private static final EntityDataAccessor DATA_PUMPKIN_ID = SynchedEntityData.defineId(SnowGolem.class, EntityDataSerializers.BYTE); - private static final byte PUMPKIN_FLAG = 16; - private static final boolean DEFAULT_PUMPKIN = true; @@ -95,7 +_,7 @@ this.hurtServer(serverLevel, this.damageSources().onFire(), 1.0F); } @@ -32,25 +23,7 @@ protected InteractionResult mobInteract(final Player player, final InteractionHand hand) { ItemStack itemStack = player.getItemInHand(hand); - if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { -+ if (false && itemStack.is(Items.SHEARS) && this.readyForShearing()) { //Forge: Moved to onSheared ++ if (itemStack.canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST) && this.readyForShearing()) { if (this.level() instanceof ServerLevel level) { this.shear(level, SoundSource.PLAYERS, itemStack); this.gameEvent(GameEvent.SHEAR, player); -@@ -193,4 +_,17 @@ - public Vec3 getLeashOffset() { - return new Vec3(0.0, 0.75F * this.getEyeHeight(), this.getBbWidth() * 0.4F); - } -+ -+ @Override -+ public java.util.@org.jspecify.annotations.NonNull List onSheared(@Nullable Player player, @org.jspecify.annotations.NonNull ItemStack item, Level world, BlockPos pos, int fortune) { -+ world.playSound(null, this, SoundEvents.SNOW_GOLEM_SHEAR, player == null ? SoundSource.BLOCKS : SoundSource.PLAYERS, 1.0F, 1.0F); -+ this.gameEvent(GameEvent.SHEAR, player); -+ if (!world.isClientSide() && world instanceof ServerLevel server) { -+ setPumpkin(false); -+ var ret = new java.util.ArrayList(); -+ this.dropFromShearingLootTable(server, BuiltInLootTables.SHEAR_SNOW_GOLEM, item, (slevel, stack) -> ret.add(stack)); -+ return ret; -+ } -+ return java.util.Collections.emptyList(); -+ } - } diff --git a/patches/minecraft/net/minecraft/world/entity/animal/sheep/Sheep.java.patch b/patches/minecraft/net/minecraft/world/entity/animal/sheep/Sheep.java.patch index 7c488e36d6..7d90cbab8d 100644 --- a/patches/minecraft/net/minecraft/world/entity/animal/sheep/Sheep.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/animal/sheep/Sheep.java.patch @@ -5,52 +5,7 @@ public InteractionResult mobInteract(final Player player, final InteractionHand hand) { ItemStack itemStack = player.getItemInHand(hand); - if (itemStack.is(Items.SHEARS)) { -+ if (false && itemStack.is(Items.SHEARS)) { // Forge: Moved to onSheared ++ if (itemStack.canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST)) { if (this.level() instanceof ServerLevel level && this.readyForShearing()) { this.shear(level, SoundSource.PLAYERS, itemStack); this.gameEvent(GameEvent.SHEAR, player); -@@ -161,12 +_,27 @@ - - @Override - public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { -+ dropItems(level, shearInternal(level, soundSource, tool)); -+ } -+ -+ private java.util.List shearInternal(ServerLevel level, SoundSource soundSource, ItemStack tool) { -+ var ret = new java.util.ArrayList(); - level.playSound(null, this, SoundEvents.SHEEP_SHEAR, soundSource, 1.0F, 1.0F); - this.dropFromShearingLootTable( - level, - BuiltInLootTables.SHEAR_SHEEP, - tool, - (l, drop) -> { -+ ret.add(drop); -+ } -+ ); -+ this.setSheared(true); -+ return ret; -+ } -+ -+ private void dropItems(ServerLevel l, java.util.Collection items) { -+ // double indented to make the patch look nicer -+ for (var drop : items) { - for (int i = 0; i < drop.getCount(); i++) { - ItemEntity entity = this.spawnAtLocation(l, drop.copyWithCount(1), 1.0F); - if (entity != null) { -@@ -181,8 +_,14 @@ - } - } - } -- ); -- this.setSheared(true); -+ } -+ -+ @Override -+ public java.util.List onSheared(@Nullable Player player, ItemStack item, Level level, BlockPos pos, int fortune) { -+ if (level instanceof ServerLevel server) { -+ return shearInternal(server, player == null ? SoundSource.BLOCKS : SoundSource.PLAYERS, item); -+ } -+ return java.util.Collections.emptyList(); - } - - @Override diff --git a/patches/minecraft/net/minecraft/world/entity/monster/cubemob/SulfurCube.java.patch b/patches/minecraft/net/minecraft/world/entity/monster/cubemob/SulfurCube.java.patch index 58c6c94b25..53a3f70a64 100644 --- a/patches/minecraft/net/minecraft/world/entity/monster/cubemob/SulfurCube.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/monster/cubemob/SulfurCube.java.patch @@ -11,3 +11,12 @@ if (this.hasBodyItem() && this.floatsInLiquids) { float vibeAmount = 0.2F * Mth.sin(this.tickCount * 0.4F); double immersion = this.getFluidHeight(this.isInWater() ? FluidTags.WATER : FluidTags.LAVA) - this.getFluidJumpThreshold() + vibeAmount; +@@ -452,7 +_,7 @@ + } + + if (!this.canExplode() || !heldItem.is(Items.FLINT_AND_STEEL) && !heldItem.is(Items.FIRE_CHARGE)) { +- if (heldItem.is(Items.SHEARS) && this.readyForShearing()) { ++ if (heldItem.canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST) && this.readyForShearing()) { + if (this.level() instanceof ServerLevel level) { + ItemStack itemStackToShear = this.getItemBySlot(EquipmentSlot.BODY); + this.shear(level, SoundSource.PLAYERS, heldItem); diff --git a/patches/minecraft/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch b/patches/minecraft/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch index aa17c59f6c..711d74d763 100644 --- a/patches/minecraft/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch +++ b/patches/minecraft/net/minecraft/world/entity/monster/skeleton/Bogged.java.patch @@ -5,25 +5,7 @@ protected InteractionResult mobInteract(final Player player, final InteractionHand hand) { ItemStack itemStack = player.getItemInHand(hand); - if (itemStack.is(Items.SHEARS) && this.readyForShearing()) { -+ if (false && itemStack.is(Items.SHEARS) && this.readyForShearing()) { // Forge: move to onSheared ++ if (itemStack.canPerformAction(net.minecraftforge.common.ToolActions.SHEARS_HARVEST) && this.readyForShearing()) { if (this.level() instanceof ServerLevel level) { this.shear(level, SoundSource.PLAYERS, itemStack); this.gameEvent(GameEvent.SHEAR, player); -@@ -137,5 +_,17 @@ - @Override - public boolean readyForShearing() { - return !this.isSheared(); -+ } -+ -+ @Override -+ public java.util.List onSheared(@org.jetbrains.annotations.Nullable Player player, @org.jetbrains.annotations.NotNull ItemStack item, Level world, net.minecraft.core.BlockPos pos, int fortune) { -+ if (world instanceof ServerLevel server) { -+ server.playSound(null, this, SoundEvents.BOGGED_SHEAR, SoundSource.PLAYERS, 1.0F, 1.0F); -+ this.setSheared(true); -+ var ret = new java.util.ArrayList(); -+ this.dropFromShearingLootTable(server, BuiltInLootTables.BOGGED_SHEAR, item, (slevel, stack) -> ret.add(stack)); -+ return ret; -+ } -+ return java.util.Collections.emptyList(); - } - } diff --git a/patches/minecraft/net/minecraft/world/item/ShearsItem.java.patch b/patches/minecraft/net/minecraft/world/item/ShearsItem.java.patch index b11010ee40..188a0fb9fe 100644 --- a/patches/minecraft/net/minecraft/world/item/ShearsItem.java.patch +++ b/patches/minecraft/net/minecraft/world/item/ShearsItem.java.patch @@ -1,31 +1,20 @@ --- a/net/minecraft/world/item/ShearsItem.java +++ b/net/minecraft/world/item/ShearsItem.java -@@ -83,4 +_,34 @@ +@@ -83,4 +_,23 @@ return super.useOn(context); } } + + @Override + public InteractionResult interactLivingEntity(ItemStack stack, Player playerIn, LivingEntity entity, net.minecraft.world.InteractionHand hand) { -+ if (entity instanceof net.minecraftforge.common.IForgeShearable target) { -+ if (entity.level().isClientSide()) { -+ return InteractionResult.SUCCESS; -+ } ++ if (entity instanceof net.minecraft.world.entity.Shearable target) { ++ if (entity.level().isClientSide() || !target.readyForShearing()) ++ return InteractionResult.CONSUME; + var serverLevel = (net.minecraft.server.level.ServerLevel)entity.level(); -+ -+ BlockPos pos = BlockPos.containing(entity.position()); -+ if (target.isShearable(stack, entity.level(), pos)) { -+ var key = net.minecraft.world.item.enchantment.Enchantments.FORTUNE; -+ var drops = target.onSheared(playerIn, stack, entity.level(), pos, net.minecraft.world.item.enchantment.EnchantmentHelper.getItemEnchantmentLevel(entity.level().holderLookup(key.registryKey()).getOrThrow(key), stack)); -+ var rand = new java.util.Random(); -+ for (var drop : drops) { -+ var ent = entity.spawnAtLocation(serverLevel, drop, 1.0F); -+ ent.setDeltaMovement(ent.getDeltaMovement().add((double)((rand.nextFloat() - rand.nextFloat()) * 0.1F), (double)(rand.nextFloat() * 0.05F), (double)((rand.nextFloat() - rand.nextFloat()) * 0.1F))); -+ } -+ if (!drops.isEmpty()) -+ stack.hurtAndBreak(1, playerIn, hand.asEquipmentSlot()); -+ } -+ return InteractionResult.SUCCESS; ++ target.shear(serverLevel, SoundSource.PLAYERS, stack); ++ serverLevel.gameEvent(playerIn, GameEvent.SHEAR, entity.position()); ++ stack.hurtAndBreak(1, playerIn, hand.asEquipmentSlot()); ++ return InteractionResult.SUCCESS_SERVER; + } + return InteractionResult.PASS; + } diff --git a/patches/minecraft/net/minecraft/world/level/block/DryVegetationBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/DryVegetationBlock.java.patch deleted file mode 100644 index 026a05e152..0000000000 --- a/patches/minecraft/net/minecraft/world/level/block/DryVegetationBlock.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/world/level/block/DryVegetationBlock.java -+++ b/net/minecraft/world/level/block/DryVegetationBlock.java -@@ -12,7 +_,7 @@ - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - --public class DryVegetationBlock extends VegetationBlock { -+public class DryVegetationBlock extends VegetationBlock implements net.minecraftforge.common.IForgeShearable { - public static final MapCodec CODEC = simpleCodec(DryVegetationBlock::new); - private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 13.0); - diff --git a/patches/minecraft/net/minecraft/world/level/block/LeavesBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/LeavesBlock.java.patch deleted file mode 100644 index 4e47c3c6e6..0000000000 --- a/patches/minecraft/net/minecraft/world/level/block/LeavesBlock.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/world/level/block/LeavesBlock.java -+++ b/net/minecraft/world/level/block/LeavesBlock.java -@@ -26,7 +_,7 @@ - import net.minecraft.world.phys.shapes.Shapes; - import net.minecraft.world.phys.shapes.VoxelShape; - --public abstract class LeavesBlock extends Block implements SimpleWaterloggedBlock { -+public abstract class LeavesBlock extends Block implements SimpleWaterloggedBlock, net.minecraftforge.common.IForgeShearable { - public static final int DECAY_DISTANCE = 7; - public static final IntegerProperty DISTANCE = BlockStateProperties.DISTANCE; - public static final BooleanProperty PERSISTENT = BlockStateProperties.PERSISTENT; diff --git a/patches/minecraft/net/minecraft/world/level/block/SeagrassBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/SeagrassBlock.java.patch deleted file mode 100644 index f8b570e664..0000000000 --- a/patches/minecraft/net/minecraft/world/level/block/SeagrassBlock.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/world/level/block/SeagrassBlock.java -+++ b/net/minecraft/world/level/block/SeagrassBlock.java -@@ -24,7 +_,7 @@ - import net.minecraft.world.phys.shapes.VoxelShape; - import org.jspecify.annotations.Nullable; - --public class SeagrassBlock extends VegetationBlock implements BonemealableBlock, LiquidBlockContainer { -+public class SeagrassBlock extends VegetationBlock implements BonemealableBlock, LiquidBlockContainer, net.minecraftforge.common.IForgeShearable { - public static final MapCodec CODEC = simpleCodec(SeagrassBlock::new); - private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 12.0); - diff --git a/patches/minecraft/net/minecraft/world/level/block/TallGrassBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/TallGrassBlock.java.patch deleted file mode 100644 index ae8704af63..0000000000 --- a/patches/minecraft/net/minecraft/world/level/block/TallGrassBlock.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/world/level/block/TallGrassBlock.java -+++ b/net/minecraft/world/level/block/TallGrassBlock.java -@@ -12,7 +_,7 @@ - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - --public class TallGrassBlock extends VegetationBlock implements BonemealableBlock { -+public class TallGrassBlock extends VegetationBlock implements BonemealableBlock, net.minecraftforge.common.IForgeShearable { - public static final MapCodec CODEC = simpleCodec(TallGrassBlock::new); - private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 13.0); - diff --git a/patches/minecraft/net/minecraft/world/level/block/VineBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/VineBlock.java.patch index 0fffedd154..c9fe2eac9c 100644 --- a/patches/minecraft/net/minecraft/world/level/block/VineBlock.java.patch +++ b/patches/minecraft/net/minecraft/world/level/block/VineBlock.java.patch @@ -1,14 +1,5 @@ --- a/net/minecraft/world/level/block/VineBlock.java +++ b/net/minecraft/world/level/block/VineBlock.java -@@ -23,7 +_,7 @@ - import net.minecraft.world.phys.shapes.VoxelShape; - import org.jspecify.annotations.Nullable; - --public class VineBlock extends Block { -+public class VineBlock extends Block implements net.minecraftforge.common.IForgeShearable { - public static final MapCodec CODEC = simpleCodec(VineBlock::new); - public static final BooleanProperty UP = PipeBlock.UP; - public static final BooleanProperty NORTH = PipeBlock.NORTH; @@ -168,7 +_,7 @@ @Override protected void randomTick(final BlockState state, final ServerLevel level, final BlockPos pos, final RandomSource random) { diff --git a/patches/minecraft/net/minecraft/world/level/block/WebBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/WebBlock.java.patch deleted file mode 100644 index dd6b57ba4c..0000000000 --- a/patches/minecraft/net/minecraft/world/level/block/WebBlock.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/world/level/block/WebBlock.java -+++ b/net/minecraft/world/level/block/WebBlock.java -@@ -11,7 +_,7 @@ - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.phys.Vec3; - --public class WebBlock extends Block { -+public class WebBlock extends Block implements net.minecraftforge.common.IForgeShearable { - public static final MapCodec CODEC = simpleCodec(WebBlock::new); - - @Override diff --git a/rejects/net/minecraft/client/model/geom/ModelLayers.java.patch.rej b/rejects/net/minecraft/client/model/geom/ModelLayers.java.patch.rej deleted file mode 100644 index ae7bea2237..0000000000 --- a/rejects/net/minecraft/client/model/geom/ModelLayers.java.patch.rej +++ /dev/null @@ -1,24 +0,0 @@ -++++ REJECTED HUNK: 1 -@@ -324,15 +324,18 @@ - } - - public static ModelLayerLocation createStandingSignModelName(final WoodType type) { -- return createLocation("sign/standing/" + type.name(), "main"); -+ Identifier location = Identifier.parse(type.name()); -+ return new ModelLayerLocation(Identifier.fromNamespaceAndPath(location.getNamespace(), "sign/standing/" + location.getPath()), "main"); - } - - public static ModelLayerLocation createWallSignModelName(final WoodType type) { -- return createLocation("sign/wall/" + type.name(), "main"); -+ Identifier location = Identifier.parse(type.name()); -+ return new ModelLayerLocation(Identifier.fromNamespaceAndPath(location.getNamespace(), "sign/wall/" + location.getPath()), "main"); - } - - public static ModelLayerLocation createHangingSignModelName(final WoodType type, final HangingSignBlock.Attachment attachmentType) { -- return createLocation("hanging_sign/" + type.name() + "/" + attachmentType.getSerializedName(), "main"); -+ Identifier location = Identifier.parse(type.name()); -+ return new ModelLayerLocation(Identifier.fromNamespaceAndPath(location.getNamespace(), "hanging_sign/" + location.getPath() + "/" + attachmentType.getSerializedName()), "main"); - } - - public static Stream getKnownLocations() { -++++ END HUNK \ No newline at end of file diff --git a/rejects/net/minecraft/client/renderer/feature/BlockFeatureRenderer.java.patch b/rejects/net/minecraft/client/renderer/feature/BlockFeatureRenderer.java.patch deleted file mode 100644 index 560848f58a..0000000000 --- a/rejects/net/minecraft/client/renderer/feature/BlockFeatureRenderer.java.patch +++ /dev/null @@ -1,12 +0,0 @@ -++++ Target missing ---- a/net/minecraft/client/renderer/feature/BlockFeatureRenderer.java -+++ b/net/minecraft/client/renderer/feature/BlockFeatureRenderer.java -@@ -153,7 +153,7 @@ - this.random.setSeed(submit.seed()); - - try { -- submit.model().collectParts(this.random, this.parts); -+ submit.model().collectParts(this.random, this.parts, submit.data()); - - for (BlockStateModelPart part : this.parts) { - putPartQuads(part, submit.pose(), this.quadInstance, NO_TINT, buffer, null); \ No newline at end of file diff --git a/rejects/net/minecraft/client/renderer/rendertype/RenderSetup.java.patch.rej b/rejects/net/minecraft/client/renderer/rendertype/RenderSetup.java.patch.rej deleted file mode 100644 index c53b513c7b..0000000000 --- a/rejects/net/minecraft/client/renderer/rendertype/RenderSetup.java.patch.rej +++ /dev/null @@ -1,67 +0,0 @@ ---- a/net/minecraft/client/renderer/rendertype/RenderSetup.java -+++ b/net/minecraft/client/renderer/rendertype/RenderSetup.java -@@ -32,6 +_,7 @@ - final boolean sortOnUpload; - final int bufferSize; - final LayeringTransform layeringTransform; -+ final Map texturesWithSamplers; - - private RenderSetup( - final RenderPipeline pipeline, -@@ -44,7 +_,8 @@ - final RenderSetup.OutlineProperty outlineProperty, - final boolean affectsCrumbling, - final boolean sortOnUpload, -- final int bufferSize -+ final int bufferSize, -+ Map texturesWithSamplers - ) { - this.pipeline = pipeline; - this.textures = textures; -@@ -57,6 +_,7 @@ - this.affectsCrumbling = affectsCrumbling; - this.sortOnUpload = sortOnUpload; - this.bufferSize = bufferSize; -+ this.texturesWithSamplers = texturesWithSamplers; - } - - @Override -@@ -115,6 +_,8 @@ - ); - } - -+ result.putAll(this.texturesWithSamplers); -+ - return result; - } - } -@@ -150,6 +_,7 @@ - private int bufferSize = 1536; - private RenderSetup.OutlineProperty outlineProperty = RenderSetup.OutlineProperty.NONE; - private final Map textures = new HashMap<>(); -+ private final Map texturesWithSamplers = new HashMap<>(); - - private RenderSetupBuilder(final RenderPipeline pipeline) { - this.pipeline = pipeline; -@@ -165,6 +_,11 @@ - return this; - } - -+ public RenderSetup.RenderSetupBuilder withTexture(String name, GpuTextureView textureView, GpuSampler sampler) { -+ this.texturesWithSamplers.put(name, new RenderSetup.TextureAndSampler(textureView, sampler)); -+ return this; -+ } -+ - public RenderSetup.RenderSetupBuilder useLightmap() { - this.useLightmap = true; - return this; -@@ -222,7 +_,8 @@ - this.outlineProperty, - this.affectsCrumbling, - this.sortOnUpload, -- this.bufferSize -+ this.bufferSize, -+ this.texturesWithSamplers - ); - } - } diff --git a/src/main/java/net/minecraftforge/common/IForgeShearable.java b/src/main/java/net/minecraftforge/common/IForgeShearable.java deleted file mode 100644 index 8e671d63db..0000000000 --- a/src/main/java/net/minecraftforge/common/IForgeShearable.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.minecraftforge.common; - -import java.util.Collections; -import java.util.List; - -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * - * This allows for mods to create there own Shear-like items - * and have them interact with Blocks/Entities without extra work. - * Also, if your block/entity supports the Shears, this allows you - * to support mod-shears as well. - * - */ -public interface IForgeShearable { - /** - * Checks if the object is currently shearable - * Example: Sheep return false when they have no wool - * - * @param item The ItemStack that is being used, may be empty. - * @param level The current level. - * @param pos Block's position in level. - * @return If this is shearable, and onSheared should be called. - */ - default boolean isShearable(@NotNull ItemStack item, Level level, BlockPos pos) { - return true; - } - - /** - * Performs the shear function on this object. - * This is called for both client, and server. - * The object should perform all actions related to being sheared, - * except for dropping of the items, and removal of the block. - * As those are handled by ItemShears itself. - * - * Returns a list of items that resulted from the shearing process. - * - * For entities, they should trust there internal location information - * over the values passed into this function. - * - * @param item The ItemStack that is being used, may be empty. - * @param level The current level. - * @param pos If this is a block, the block's position in level. - * @param fortune The fortune level of the shears being used. - * @return A List containing all items from this shearing. May be empty. - */ - @NotNull - default List onSheared(@Nullable Player player, @NotNull ItemStack item, Level level, BlockPos pos, int fortune) { - return Collections.emptyList(); - } -} diff --git a/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_bogged.json b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_bogged.json new file mode 100644 index 0000000000..524b689a40 --- /dev/null +++ b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_bogged.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "environment": "minecraft:default", + "function": "forge:shears_behavior/custom_shears_shear_bogged", + "max_ticks": 100, + "structure": "forge:empty3x3x3" +} \ No newline at end of file diff --git a/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_mooshroom.json b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_mooshroom.json new file mode 100644 index 0000000000..a3feb9f485 --- /dev/null +++ b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_mooshroom.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "environment": "minecraft:default", + "function": "forge:shears_behavior/custom_shears_shear_mooshroom", + "max_ticks": 100, + "structure": "forge:empty3x3x3" +} \ No newline at end of file diff --git a/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sheep.json b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sheep.json new file mode 100644 index 0000000000..233ee31e38 --- /dev/null +++ b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sheep.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "environment": "minecraft:default", + "function": "forge:shears_behavior/custom_shears_shear_sheep", + "max_ticks": 100, + "structure": "forge:empty3x3x3" +} \ No newline at end of file diff --git a/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_snowgolem.json b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_snowgolem.json new file mode 100644 index 0000000000..a064501d08 --- /dev/null +++ b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_snowgolem.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "environment": "minecraft:default", + "function": "forge:shears_behavior/custom_shears_shear_snowgolem", + "max_ticks": 100, + "structure": "forge:empty3x3x3" +} \ No newline at end of file diff --git a/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sulfur_cube_block.json b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sulfur_cube_block.json new file mode 100644 index 0000000000..c7d041cc68 --- /dev/null +++ b/src/test/generated/shears_behavior/data/forge/test_instance/shears_behavior/custom_shears_shear_sulfur_cube_block.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "environment": "minecraft:default", + "function": "forge:shears_behavior/custom_shears_shear_sulfur_cube_block", + "max_ticks": 100, + "structure": "forge:empty3x3x3" +} \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/gameplay/item/ShearsBehaviorTest.java b/src/test/java/net/minecraftforge/debug/gameplay/item/ShearsBehaviorTest.java index e38a9de241..77ac4ad264 100644 --- a/src/test/java/net/minecraftforge/debug/gameplay/item/ShearsBehaviorTest.java +++ b/src/test/java/net/minecraftforge/debug/gameplay/item/ShearsBehaviorTest.java @@ -6,11 +6,16 @@ package net.minecraftforge.debug.gameplay.item; import net.minecraft.core.BlockPos; +import net.minecraft.core.component.DataComponents; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.animal.cow.MushroomCow; import net.minecraft.world.entity.animal.golem.CopperGolem; import net.minecraft.world.entity.decoration.LeashFenceKnotEntity; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -48,6 +53,14 @@ public class ShearsBehaviorTest extends BaseTestMod { this.testItem(_ -> CUSTOM_SHEARS_HARVEST_ITEM.get().getDefaultInstance()); } + private static void shear(GameTestHelper helper, Entity entity) { + var player = helper.makeMockServerPlayer(GameType.SURVIVAL); + var shears = CUSTOM_SHEARS_HARVEST_ITEM.get().getDefaultInstance(); + player.setItemInHand(InteractionHand.MAIN_HAND, shears); + var result = player.interactOn(entity, InteractionHand.MAIN_HAND, Vec3.ZERO); + helper.assertTrue(result.consumesAction(), "Using entity should result in consume action"); + } + @GameTest public static void custom_shears_unleash_entity(GameTestHelper helper) { helper.makeFloor(); @@ -61,14 +74,9 @@ public class ShearsBehaviorTest extends BaseTestMod { cow.setLeashedTo(knot, true); helper.assertTrue(cow.isLeashed(), "Cow should start leashed"); - // act: interact with cow using custom shears - var player = helper.makeMockPlayer(GameType.SURVIVAL); - var shears = CUSTOM_SHEARS_HARVEST_ITEM.get().getDefaultInstance(); - player.setItemInHand(InteractionHand.MAIN_HAND, shears); - var result = player.interactOn(cow, InteractionHand.MAIN_HAND, Vec3.ZERO); + shear(helper, cow); // assert - helper.assertTrue(result.consumesAction(), "Using custom shears on leashed cow should result in consume action"); helper.assertTrue(!cow.isLeashed(), "Cow should be unleashed by custom shears"); helper.assertItemEntityPresent(Items.LEAD); @@ -89,14 +97,9 @@ public class ShearsBehaviorTest extends BaseTestMod { cow.setLeashedTo(knot, true); helper.assertTrue(cow.isLeashed(), "Cow should start leashed"); - // act: interact with knot using custom shears - var player = helper.makeMockPlayer(GameType.SURVIVAL); - var shears = CUSTOM_SHEARS_HARVEST_ITEM.get().getDefaultInstance(); - player.setItemInHand(InteractionHand.MAIN_HAND, shears); - var result = player.interactOn(knot, InteractionHand.MAIN_HAND, Vec3.ZERO); + shear(helper, knot); // assert - helper.assertTrue(result.consumesAction(), "Using custom shears on leash knot should result in consume action"); helper.assertTrue(!cow.isLeashed(), "Cow should be unleashed by sheared knot"); helper.assertItemEntityPresent(Items.LEAD); @@ -113,20 +116,110 @@ public class ShearsBehaviorTest extends BaseTestMod { golem.setItemSlot(CopperGolem.EQUIPMENT_SLOT_ANTENNA, new ItemStack(Items.POPPY)); helper.assertTrue(golem.readyForShearing(), "Golem should start shearable (has poppy)"); - // act: interact with copper golem using custom shears - var player = helper.makeMockPlayer(GameType.SURVIVAL); - var shears = CUSTOM_SHEARS_ITEM.get().getDefaultInstance(); - player.setItemInHand(InteractionHand.MAIN_HAND, shears); - var result = player.interactOn(golem, InteractionHand.MAIN_HAND, Vec3.ZERO); + shear(helper, golem); // assert - helper.assertTrue(result.consumesAction(), "Using custom shears on copper golem should result in consume action"); helper.assertTrue(golem.getItemBySlot(CopperGolem.EQUIPMENT_SLOT_ANTENNA).isEmpty(), "Copper golem poppy should be sheared off with custom shears"); helper.assertItemEntityPresent(Items.POPPY); helper.succeed(); } + @GameTest + public static void custom_shears_shear_sulfur_cube_block(GameTestHelper helper) { + helper.makeFloor(); + + // setup: sulfur cube with block + var pos = new BlockPos(1, 1, 1); + var sulfurCube = helper.spawnWithNoFreeWill(EntityTypes.SULFUR_CUBE, pos); + sulfurCube.setItemSlot(EquipmentSlot.BODY, new ItemStack(Items.DIRT)); + helper.assertTrue(sulfurCube.readyForShearing(), "Sulfur cube should start shearable (has dirt block)"); + + shear(helper, sulfurCube); + + // assert + helper.assertTrue(sulfurCube.getItemBySlot(EquipmentSlot.BODY).isEmpty(), "Sulfur cube block should be sheared off with custom shears"); + helper.assertItemEntityPresent(Items.DIRT); + + helper.succeed(); + } + + @GameTest + public static void custom_shears_shear_bogged(GameTestHelper helper) { + helper.makeFloor(); + + var pos = new BlockPos(1, 1, 1); + var entity = helper.spawnWithNoFreeWill(EntityTypes.BOGGED, pos); + helper.assertTrue(entity.readyForShearing(), "Bogged start shearable"); + + shear(helper, entity); + + // assert + helper.assertTrue(!entity.readyForShearing(), "Bogged should no longer be shearable"); + int found = 0; + for (var item : helper.getEntities(EntityTypes.ITEM, BlockPos.ZERO, 3)) { + if (item.isAlive() && (item.getItem().is(Items.BROWN_MUSHROOM) || item.getItem().is(Items.RED_MUSHROOM))) + found += item.getItem().count(); + } + helper.assertValueEqual(found, 2, "Mushroom loot not found"); + + helper.succeed(); + } + + @GameTest + public static void custom_shears_shear_mooshroom(GameTestHelper helper) { + helper.makeFloor(); + + var pos = new BlockPos(1, 1, 1); + var entity = helper.spawnWithNoFreeWill(EntityTypes.MOOSHROOM, pos); + entity.setComponent(DataComponents.MOOSHROOM_VARIANT, MushroomCow.Variant.RED); + helper.assertTrue(entity.readyForShearing(), "Mooshroom start shearable"); + + shear(helper, entity); + + // assert + helper.assertTrue(!entity.isAlive(), "Mooshroom should no longer be alive"); + helper.assertEntityPresent(EntityTypes.COW); + helper.assertItemEntityPresent(Items.RED_MUSHROOM); + + helper.succeed(); + } + + @GameTest + public static void custom_shears_shear_sheep(GameTestHelper helper) { + helper.makeFloor(); + + var pos = new BlockPos(1, 1, 1); + var entity = helper.spawnWithNoFreeWill(EntityTypes.SHEEP, pos); + entity.setColor(DyeColor.WHITE); + helper.assertTrue(entity.readyForShearing(), "Sheep should start shearable"); + + shear(helper, entity); + + // assert + helper.assertTrue(!entity.readyForShearing(), "Sheep should no longer be shearable"); + helper.assertItemEntityPresent(Items.WOOL.white()); + + helper.succeed(); + } + + @GameTest + public static void custom_shears_shear_snowgolem(GameTestHelper helper) { + helper.makeFloor(); + + var pos = new BlockPos(1, 1, 1); + var entity = helper.spawnWithNoFreeWill(EntityTypes.SNOW_GOLEM, pos); + helper.assertTrue(entity.readyForShearing(), "Snow golem should start shearable"); + + shear(helper, entity); + + // assert + helper.assertTrue(!entity.readyForShearing(), "Snow golem should no longer be shearable"); + helper.assertItemEntityPresent(Items.CARVED_PUMPKIN); + + helper.succeed(); + } + private static final class ShearsHarvestItem extends Item { ShearsHarvestItem(Item.Properties properties) { super(properties);