diff --git a/patches/minecraft/net/minecraft/item/AxeItem.java.patch b/patches/minecraft/net/minecraft/item/AxeItem.java.patch index cac84ad6b9..7ab6c0905d 100644 --- a/patches/minecraft/net/minecraft/item/AxeItem.java.patch +++ b/patches/minecraft/net/minecraft/item/AxeItem.java.patch @@ -9,3 +9,29 @@ } public float func_150893_a(ItemStack p_150893_1_, BlockState p_150893_2_) { +@@ -34,12 +34,12 @@ + World world = p_195939_1_.func_195991_k(); + BlockPos blockpos = p_195939_1_.func_195995_a(); + BlockState blockstate = world.func_180495_p(blockpos); +- Block block = field_203176_a.get(blockstate.func_177230_c()); ++ BlockState block = blockstate.getToolModifiedState(world, blockpos, p_195939_1_.func_195999_j(), p_195939_1_.func_195996_i(), net.minecraftforge.common.ToolType.AXE); + if (block != null) { + PlayerEntity playerentity = p_195939_1_.func_195999_j(); + world.func_184133_a(playerentity, blockpos, SoundEvents.field_203255_y, SoundCategory.BLOCKS, 1.0F, 1.0F); + if (!world.field_72995_K) { +- world.func_180501_a(blockpos, block.func_176223_P().func_206870_a(RotatedPillarBlock.field_176298_M, blockstate.func_177229_b(RotatedPillarBlock.field_176298_M)), 11); ++ world.func_180501_a(blockpos, block, 11); + if (playerentity != null) { + p_195939_1_.func_195996_i().func_222118_a(1, playerentity, (p_220040_1_) -> { + p_220040_1_.func_213334_d(p_195939_1_.func_221531_n()); +@@ -52,4 +52,10 @@ + return ActionResultType.PASS; + } + } ++ ++ @javax.annotation.Nullable ++ public static BlockState getAxeStrippingState(BlockState originalState) { ++ Block block = field_203176_a.get(originalState.func_177230_c()); ++ return block != null ? block.func_176223_P().func_206870_a(RotatedPillarBlock.field_176298_M, originalState.func_177229_b(RotatedPillarBlock.field_176298_M)) : null; ++ } + } diff --git a/patches/minecraft/net/minecraft/item/HoeItem.java.patch b/patches/minecraft/net/minecraft/item/HoeItem.java.patch index 5825be9f2b..255d8c4368 100644 --- a/patches/minecraft/net/minecraft/item/HoeItem.java.patch +++ b/patches/minecraft/net/minecraft/item/HoeItem.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/item/HoeItem.java +++ b/net/minecraft/item/HoeItem.java -@@ -21,13 +21,15 @@ +@@ -21,14 +21,16 @@ protected static final Map field_195973_b = Maps.newHashMap(ImmutableMap.of(Blocks.field_196658_i, Blocks.field_150458_ak.func_176223_P(), Blocks.field_185774_da, Blocks.field_150458_ak.func_176223_P(), Blocks.field_150346_d, Blocks.field_150458_ak.func_176223_P(), Blocks.field_196660_k, Blocks.field_150346_d.func_176223_P())); public HoeItem(IItemTier p_i231595_1_, int p_i231595_2_, float p_i231595_3_, Item.Properties p_i231595_4_) { @@ -12,9 +12,21 @@ World world = p_195939_1_.func_195991_k(); BlockPos blockpos = p_195939_1_.func_195995_a(); - if (p_195939_1_.func_196000_l() != Direction.DOWN && world.func_180495_p(blockpos.func_177984_a()).func_196958_f()) { +- BlockState blockstate = field_195973_b.get(world.func_180495_p(blockpos).func_177230_c()); + int hook = net.minecraftforge.event.ForgeEventFactory.onHoeUse(p_195939_1_); + if (hook != 0) return hook > 0 ? ActionResultType.SUCCESS : ActionResultType.FAIL; + if (p_195939_1_.func_196000_l() != Direction.DOWN && world.func_175623_d(blockpos.func_177984_a())) { - BlockState blockstate = field_195973_b.get(world.func_180495_p(blockpos).func_177230_c()); ++ BlockState blockstate = world.func_180495_p(blockpos).getToolModifiedState(world, blockpos, p_195939_1_.func_195999_j(), p_195939_1_.func_195996_i(), net.minecraftforge.common.ToolType.HOE); if (blockstate != null) { PlayerEntity playerentity = p_195939_1_.func_195999_j(); + world.func_184133_a(playerentity, blockpos, SoundEvents.field_187693_cj, SoundCategory.BLOCKS, 1.0F, 1.0F); +@@ -47,4 +49,9 @@ + + return ActionResultType.PASS; + } ++ ++ @javax.annotation.Nullable ++ public static BlockState getHoeTillingState(BlockState originalState) { ++ return field_195973_b.get(originalState.func_177230_c()); ++ } + } diff --git a/patches/minecraft/net/minecraft/item/ShovelItem.java.patch b/patches/minecraft/net/minecraft/item/ShovelItem.java.patch index 14dc462f84..6f4d4934d3 100644 --- a/patches/minecraft/net/minecraft/item/ShovelItem.java.patch +++ b/patches/minecraft/net/minecraft/item/ShovelItem.java.patch @@ -9,12 +9,25 @@ } public boolean func_150897_b(BlockState p_150897_1_) { -@@ -39,7 +39,7 @@ +@@ -37,9 +37,9 @@ + return ActionResultType.PASS; + } else { PlayerEntity playerentity = p_195939_1_.func_195999_j(); - BlockState blockstate1 = field_195955_e.get(blockstate.func_177230_c()); +- BlockState blockstate1 = field_195955_e.get(blockstate.func_177230_c()); ++ BlockState blockstate1 = blockstate.getToolModifiedState(world, blockpos, playerentity, p_195939_1_.func_195996_i(), net.minecraftforge.common.ToolType.SHOVEL); BlockState blockstate2 = null; - if (blockstate1 != null && world.func_180495_p(blockpos.func_177984_a()).func_196958_f()) { + if (blockstate1 != null && world.func_175623_d(blockpos.func_177984_a())) { world.func_184133_a(playerentity, blockpos, SoundEvents.field_187771_eN, SoundCategory.BLOCKS, 1.0F, 1.0F); blockstate2 = blockstate1; } else if (blockstate.func_177230_c() instanceof CampfireBlock && blockstate.func_177229_b(CampfireBlock.field_220101_b)) { +@@ -67,4 +67,9 @@ + } + } + } ++ ++ @javax.annotation.Nullable ++ public static BlockState getShovelPathingState(BlockState originalState) { ++ return field_195955_e.get(originalState.func_177230_c()); ++ } + } diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java b/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java index 6820873bf4..dd23d2f225 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java @@ -42,8 +42,11 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.WitherSkullEntity; import net.minecraft.fluid.FluidState; import net.minecraft.potion.Effects; +import net.minecraft.item.AxeItem; import net.minecraft.item.DyeColor; +import net.minecraft.item.HoeItem; import net.minecraft.item.ItemStack; +import net.minecraft.item.ShovelItem; import net.minecraft.pathfinding.PathNodeType; import net.minecraft.state.Property; import net.minecraft.state.properties.BedPart; @@ -887,4 +890,24 @@ public interface IForgeBlock { return state.getBlock() instanceof BreakableBlock || state.getBlock() instanceof LeavesBlock; } + + /** + * Returns the state that this block should transform into when right clicked by a tool. + * For example: Used to determine if an axe can strip, a shovel can path, or a hoe can till. + * Return null if vanilla behavior should be disabled. + * + * @param state The current state + * @param world The world + * @param pos The block position in world + * @param player The player clicking the block + * @param stack The stack being used by the player + * @return The resulting state after the action has been performed + */ + @Nullable + default BlockState getToolModifiedState(BlockState state, World world, BlockPos pos, PlayerEntity player, ItemStack stack, ToolType toolType) + { + if (toolType == ToolType.AXE) return AxeItem.getAxeStrippingState(state); + else if(toolType == ToolType.HOE) return HoeItem.getHoeTillingState(state); + else return toolType == ToolType.SHOVEL ? ShovelItem.getShovelPathingState(state) : null; + } } diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java b/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java index 81b2a00789..46791f24a5 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java @@ -778,4 +778,23 @@ public interface IForgeBlockState { return getBlockState().getBlock().shouldDisplayFluidOverlay(getBlockState(), world, pos, fluidState); } + + /** + * Returns the state that this block should transform into when right clicked by a tool. + * For example: Used to determine if an axe can strip, a shovel can path, or a hoe can till. + * Return null if vanilla behavior should be disabled. + * + * @param world The world + * @param pos The block position in world + * @param player The player clicking the block + * @param stack The stack being used by the player + * @param toolTypes The tool types to be considered when performing the action + * @return The resulting state after the action has been performed + */ + @Nullable + default BlockState getToolModifiedState(World world, BlockPos pos, PlayerEntity player, ItemStack stack, ToolType toolType) + { + BlockState eventState = net.minecraftforge.event.ForgeEventFactory.onToolUse(getBlockState(), world, pos, player, stack, toolType); + return eventState != getBlockState() ? eventState : getBlockState().getBlock().getToolModifiedState(getBlockState(), world, pos, player, stack, toolType); + } } diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index ce29c1dfc5..35d3b1f9d3 100644 --- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -88,6 +88,7 @@ import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent.OverlayType; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.ToolType; import net.minecraftforge.common.capabilities.CapabilityDispatcher; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.BlockSnapshot; @@ -126,6 +127,7 @@ import net.minecraftforge.event.entity.player.SleepingTimeCheckEvent; import net.minecraftforge.event.entity.player.UseHoeEvent; import net.minecraftforge.event.furnace.FurnaceFuelBurnTimeEvent; import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.BlockEvent.BlockToolInteractEvent; import net.minecraftforge.event.world.BlockEvent.CreateFluidSourceEvent; import net.minecraftforge.event.world.BlockEvent.EntityMultiPlaceEvent; import net.minecraftforge.event.world.BlockEvent.EntityPlaceEvent; @@ -348,6 +350,8 @@ public class ForgeEventFactory return MinecraftForge.EVENT_BUS.post(event) ? "" : event.getMessage(); } + //TODO: 1.17 Remove + @Deprecated public static int onHoeUse(ItemUseContext context) { UseHoeEvent event = new UseHoeEvent(context); @@ -359,6 +363,13 @@ public class ForgeEventFactory } return 0; } + + @Nullable + public static BlockState onToolUse(BlockState originalState, World world, BlockPos pos, PlayerEntity player, ItemStack stack, ToolType toolType) + { + BlockToolInteractEvent event = new BlockToolInteractEvent(world, pos, originalState, player, stack, toolType); + return MinecraftForge.EVENT_BUS.post(event) ? null : event.getFinalState(); + } public static int onApplyBonemeal(@Nonnull PlayerEntity player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull ItemStack stack) { diff --git a/src/main/java/net/minecraftforge/event/entity/player/UseHoeEvent.java b/src/main/java/net/minecraftforge/event/entity/player/UseHoeEvent.java index bea0510cce..cfb3f97536 100644 --- a/src/main/java/net/minecraftforge/event/entity/player/UseHoeEvent.java +++ b/src/main/java/net/minecraftforge/event/entity/player/UseHoeEvent.java @@ -33,9 +33,12 @@ import javax.annotation.Nonnull; * and damage the hoe. * * setResult(ALLOW) is the same as the old setHandled(); + * + * TODO: 1.17 Remove */ @Cancelable @HasResult +@Deprecated public class UseHoeEvent extends PlayerEvent { private final ItemUseContext context;; diff --git a/src/main/java/net/minecraftforge/event/world/BlockEvent.java b/src/main/java/net/minecraftforge/event/world/BlockEvent.java index c27550af6a..95a024c707 100644 --- a/src/main/java/net/minecraftforge/event/world/BlockEvent.java +++ b/src/main/java/net/minecraftforge/event/world/BlockEvent.java @@ -35,6 +35,7 @@ import net.minecraft.util.Direction; import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.ToolType; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.eventbus.api.Cancelable; import net.minecraftforge.eventbus.api.Event; @@ -444,4 +445,68 @@ public class BlockEvent extends Event return size; } } + + /** + * Fired when when this block is right clicked by a tool to change its state. + * For example: Used to determine if an axe can strip, a shovel can path, or a hoe can till. + * + * This event is {@link Cancelable}. If canceled, this will prevent the tool + * from changing the block's state. + */ + @Cancelable + public static class BlockToolInteractEvent extends BlockEvent + { + + private final PlayerEntity player; + private final ItemStack stack; + private final ToolType toolType; + private BlockState state; + + public BlockToolInteractEvent(IWorld world, BlockPos pos, BlockState originalState, PlayerEntity player, ItemStack stack, ToolType toolType) + { + super(world, pos, originalState); + this.player = player; + this.stack = stack; + this.state = originalState; + this.toolType = toolType; + } + + /**Gets the player using the tool.*/ + public PlayerEntity getPlayer() + { + return player; + } + + /**Gets the tool being used.*/ + public ItemStack getHeldItemStack() + { + return stack; + } + + /**Gets the current type of the tool being compared against.*/ + public ToolType getToolType() + { + return toolType; + } + + /** + * Sets the transformed state after tool use. + * If not set, will return the original state. + * This will be bypassed if canceled returning null instead. + * */ + public void setFinalState(BlockState finalState) + { + this.state = finalState; + } + + /** + * Gets the transformed state after tool use. + * If setFinalState not called, will return the original state. + * This will be bypassed if canceled returning null instead. + * */ + public BlockState getFinalState() + { + return state; + } + } } diff --git a/src/test/java/net/minecraftforge/debug/block/ToolInteractTest.java b/src/test/java/net/minecraftforge/debug/block/ToolInteractTest.java new file mode 100644 index 0000000000..a277c6f3b7 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/block/ToolInteractTest.java @@ -0,0 +1,36 @@ +package net.minecraftforge.debug.block; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.BlockEvent.BlockToolInteractEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod("tool_interact_test") +public class ToolInteractTest +{ + + private static final Logger LOGGER = LogManager.getLogger(); + + public ToolInteractTest() + { + MinecraftForge.EVENT_BUS.addListener(this::onToolInteraction); + } + + private void onToolInteraction(final BlockToolInteractEvent event) + { + //Test 1: No Changes, just test if event is called. State and Final State should be the same + LOGGER.info("BlockState {} is modified to {} at position {} by {} with {}", event.getState(), event.getFinalState(), event.getPos(), event.getPlayer(), event.getHeldItemStack()); + + //Test 2: Canceling, nothing in game should change + /*event.setCanceled(true); + LOGGER.info("Event has been canceled: {}", event.isCanceled()); + */ + + //Test 3: Altering + /*event.setFinalState(Blocks.ACACIA_FENCE.getDefaultState()); + LOGGER.info("BlockState {} is modified to {} at position {} by {} with {}", event.getState(), event.getFinalState(), event.getPos(), event.getPlayer(), event.getHeldItemStack()); + */ + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 65b4b88fdb..a45f01f902 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -76,5 +76,7 @@ license="LGPL v2.1" modId="player_name_event_test" [[mods]] modId="modded_overworld_biomes_test" +[[mods]] + modId="tool_interact_test" [[mods]] modId="custom_elytra_test"