Remove IForgeShearable Closes #10826

Entities use vanilla's Shearable interface
 Blocks use loot table conditions or useItemOn if you need positional information
This commit is contained in:
LexManos committed 2026-06-25 08:58:58 -07:00
1 parent 3da1b644d0
commit dabe919492
25 files changed
+186 -424

No files matched your search

+1
View File
@@ -33,6 +33,7 @@
/projects/**/run/
/projects/**/*.launch
/repo/
/rejects-*/
src/*/generated/**/.cache/
# Generated by gradle every import
@@ -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<ItemStack> shearItems(ServerLevel level, SoundSource soundSource, ItemStack tool) {
+ var self = (Entity)this;
+ var entities = new java.util.ArrayList<net.minecraft.world.entity.item.ItemEntity>();
+ self.captureDrops(entities);
+ shear(level, soundSource, tool);
+ self.captureDrops(null);
+ var items = new java.util.ArrayList<ItemStack>(entities.size());
+ for (var entity : entities)
+ items.add(entity.getItem());
+ return items;
+ }
}
@@ -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<ItemStack> shearInternal(ServerLevel level, SoundSource soundSource, ItemStack tool) {
+ var ret = new java.util.ArrayList<ItemStack>();
+ 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<ItemStack> 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 {
@@ -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<ItemStack> 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<ItemStack>();
+ ret.add(this.getItemBySlot(EQUIPMENT_SLOT_ANTENNA));
+ this.setItemSlot(EQUIPMENT_SLOT_ANTENNA, ItemStack.EMPTY);
+ return ret;
+ }
+ return java.util.Collections.emptyList();
}
@Override
@@ -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<Byte> 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<ItemStack> 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<ItemStack>();
+ this.dropFromShearingLootTable(server, BuiltInLootTables.SHEAR_SNOW_GOLEM, item, (slevel, stack) -> ret.add(stack));
+ return ret;
+ }
+ return java.util.Collections.emptyList();
+ }
}
@@ -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<ItemStack> shearInternal(ServerLevel level, SoundSource soundSource, ItemStack tool) {
+ var ret = new java.util.ArrayList<ItemStack>();
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<ItemStack> 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<ItemStack> 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
@@ -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);
@@ -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<ItemStack> 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<ItemStack>();
+ this.dropFromShearingLootTable(server, BuiltInLootTables.BOGGED_SHEAR, item, (slevel, stack) -> ret.add(stack));
+ return ret;
+ }
+ return java.util.Collections.emptyList();
}
}
@@ -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;
+ }
@@ -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<DryVegetationBlock> CODEC = simpleCodec(DryVegetationBlock::new);
private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 13.0);
@@ -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;
@@ -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<SeagrassBlock> CODEC = simpleCodec(SeagrassBlock::new);
private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 12.0);
@@ -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<TallGrassBlock> CODEC = simpleCodec(TallGrassBlock::new);
private static final VoxelShape SHAPE = Block.column(12.0, 0.0, 13.0);
@@ -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<VineBlock> 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) {
@@ -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<WebBlock> CODEC = simpleCodec(WebBlock::new);
@Override
@@ -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<ModelLayerLocation> getKnownLocations() {
++++ END HUNK
@@ -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);
@@ -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<String, RenderSetup.TextureAndSampler> 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<String, RenderSetup.TextureAndSampler> 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<String, RenderSetup.TextureBinding> textures = new HashMap<>();
+ private final Map<String, RenderSetup.TextureAndSampler> 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
);
}
}
@@ -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<ItemStack> onSheared(@Nullable Player player, @NotNull ItemStack item, Level level, BlockPos pos, int fortune) {
return Collections.emptyList();
}
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:shears_behavior/custom_shears_shear_bogged",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:shears_behavior/custom_shears_shear_mooshroom",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:shears_behavior/custom_shears_shear_sheep",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:shears_behavior/custom_shears_shear_snowgolem",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -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"
}
@@ -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);