From c97eabc0c4a155f5159085e0245915ddd6131b78 Mon Sep 17 00:00:00 2001 From: cech12 Date: Tue, 18 Feb 2025 03:06:56 +0100 Subject: [PATCH] Fix Hanging Pale Moss not dropping with modded shears (#10414) --- .../loot_table/blocks/pale_hanging_moss.json | 42 ++++++ .../common/data/ForgeLootTableProvider.java | 4 +- .../extensions/IForgeGameTestHelper.java | 37 +++++- .../debug/gameplay/loot/ShearsLootTests.java | 124 ++++++++++++++++++ 4 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 src/main/generated/data/minecraft/loot_table/blocks/pale_hanging_moss.json create mode 100644 src/test/java/net/minecraftforge/debug/gameplay/loot/ShearsLootTests.java diff --git a/src/main/generated/data/minecraft/loot_table/blocks/pale_hanging_moss.json b/src/main/generated/data/minecraft/loot_table/blocks/pale_hanging_moss.json new file mode 100644 index 0000000000..a186e96ba4 --- /dev/null +++ b/src/main/generated/data/minecraft/loot_table/blocks/pale_hanging_moss.json @@ -0,0 +1,42 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:any_of", + "terms": [ + { + "action": "shears_dig", + "condition": "forge:can_tool_perform_action" + }, + { + "condition": "minecraft:match_tool", + "predicate": { + "predicates": { + "minecraft:enchantments": [ + { + "enchantments": "minecraft:silk_touch", + "levels": { + "min": 1 + } + } + ] + } + } + } + ] + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:pale_hanging_moss" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecraft:blocks/pale_hanging_moss" +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/data/ForgeLootTableProvider.java b/src/main/java/net/minecraftforge/common/data/ForgeLootTableProvider.java index 8b0189c691..ec98b4b4d5 100644 --- a/src/main/java/net/minecraftforge/common/data/ForgeLootTableProvider.java +++ b/src/main/java/net/minecraftforge/common/data/ForgeLootTableProvider.java @@ -130,11 +130,11 @@ public final class ForgeLootTableProvider extends LootTableProvider { } else if (invLootCondition instanceof CompositeLootItemCondition compositeLootItemCondition && findAndReplaceInComposite(compositeLootItemCondition, from, toolAction)) { found = true; } + } else if (lootCondition instanceof CompositeLootItemCondition compositeLootItemCondition && findAndReplaceInComposite(compositeLootItemCondition, from, toolAction)) { + found = true; } } - - return found; } diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeGameTestHelper.java b/src/main/java/net/minecraftforge/common/extensions/IForgeGameTestHelper.java index 1f2cc57569..19e8875284 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeGameTestHelper.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeGameTestHelper.java @@ -16,6 +16,11 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.ChatFormatting; import net.minecraft.Util; import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; import org.jetbrains.annotations.Nullable; import com.mojang.authlib.GameProfile; @@ -119,6 +124,10 @@ public interface IForgeGameTestHelper { } default ServerPlayer makeMockServerPlayer() { + return makeMockServerPlayer(true); + } + + default ServerPlayer makeMockServerPlayer(boolean creative) { var level = self().getLevel(); var cookie = CommonListenerCookie.createInitial(new GameProfile(UUID.randomUUID(), "test-mock-player"), false); var player = new ServerPlayer(level.getServer(), level, cookie.gameProfile(), cookie.clientInformation()) { @@ -127,7 +136,7 @@ public interface IForgeGameTestHelper { } public boolean isCreative() { - return true; + return creative; } }; var connection = new Connection(PacketFlow.SERVERBOUND); @@ -186,6 +195,32 @@ public interface IForgeGameTestHelper { } } + default void setAndAssertBlock(int x, int y, int z, Block block) { + this.setAndAssertBlock(x, y, z, block.defaultBlockState()); + } + + default void setAndAssertBlock(int x, int y, int z, BlockState state) { + this.setAndAssertBlock(new BlockPos(x, y, z), state); + } + + default void setAndAssertBlock(BlockPos pos, Block block) { + this.setAndAssertBlock(pos, block.defaultBlockState()); + } + + default void setAndAssertBlock(BlockPos pos, BlockState state) { + this.assertTrue( + this.self().getLevel().setBlock(this.self().absolutePos(pos), state, Block.UPDATE_ALL), + () -> "Failed to set block at pos %s : %s".formatted(pos, state.getBlock()) + ); + } + + default void removeAllItemEntitiesInRange(BlockPos pos, double range) { + BlockPos blockpos = this.self().absolutePos(pos); + for (ItemEntity itemEntity : this.self().getLevel().getEntities(EntityType.ITEM, new AABB(blockpos).inflate(range), Entity::isAlive)) { + itemEntity.remove(Entity.RemovalReason.DISCARDED); + } + } + default Flag flag(String name) { return new Flag<>(name); } diff --git a/src/test/java/net/minecraftforge/debug/gameplay/loot/ShearsLootTests.java b/src/test/java/net/minecraftforge/debug/gameplay/loot/ShearsLootTests.java new file mode 100644 index 0000000000..6db182654a --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/gameplay/loot/ShearsLootTests.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.gameplay.loot; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.component.DataComponents; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.ShearsItem; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.DoublePlantBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.gametest.GameTestHolder; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; +import net.minecraftforge.test.BaseTestMod; + +@Mod(ShearsLootTests.MODID) +@GameTestHolder("forge." + ShearsLootTests.MODID) +public class ShearsLootTests extends BaseTestMod { + public static final String MODID = "shears_loot"; + + private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); + private static final RegistryObject MODDED_SHEARS = ITEMS.register("modded_shears", () -> new ShearsItem(new Item.Properties() + .component(DataComponents.TOOL, ShearsItem.createToolProperties()) + .setId(ITEMS.key("modded_shears")) + )); + + public ShearsLootTests(FMLJavaModLoadingContext context) { + super(context); + this.testItem(lookup -> MODDED_SHEARS.get().getDefaultInstance()); + } + + @GameTest(template = "forge:empty3x3x3") + public static void test_shears_drop_blocks(GameTestHelper helper) { + Test[] tests = { + //leaves + Test.of(Blocks.ACACIA_LEAVES), + Test.of(Blocks.AZALEA_LEAVES), + Test.of(Blocks.BIRCH_LEAVES), + Test.of(Blocks.CHERRY_LEAVES), + Test.of(Blocks.DARK_OAK_LEAVES), + Test.of(Blocks.FLOWERING_AZALEA_LEAVES), + Test.of(Blocks.JUNGLE_LEAVES), + Test.of(Blocks.MANGROVE_LEAVES), + Test.of(Blocks.OAK_LEAVES), + Test.of(Blocks.PALE_OAK_LEAVES), + Test.of(Blocks.SPRUCE_LEAVES), + + //misc + Test.of(Blocks.COBWEB), + Test.of(Blocks.DEAD_BUSH), + Test.of(Blocks.FERN), + Test.of(Blocks.HANGING_ROOTS), + Test.of(Blocks.NETHER_SPROUTS), + Test.of(Blocks.PALE_HANGING_MOSS), + Test.of(Blocks.SEAGRASS), + Test.of(Blocks.SHORT_GRASS), + Test.of(Blocks.TWISTING_VINES), + Test.of(Blocks.VINE), + Test.of(Blocks.WEEPING_VINES), + + // Tall things that need surrounding blocks + Test.tall(Blocks.LARGE_FERN, Items.FERN), + Test.tall(Blocks.TALL_GRASS, Items.SHORT_GRASS), + Test.tall(Blocks.TALL_SEAGRASS, Items.SEAGRASS), + + // Things that need a state different then default + Test.of(Blocks.GLOW_LICHEN.defaultBlockState().setValue(BlockStateProperties.NORTH, true), Items.GLOW_LICHEN), + }; + + helper.makeFloor(); // Seagrass makes water + var player = helper.makeMockServerPlayer(false); // Plants prevent loot for creative players + var center = new BlockPos(1, 1, 1); + + player.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(MODDED_SHEARS.get())); + + for (Test test : tests) { + helper.setAndAssertBlock(center, test.state()); + if (test.below() != null) + helper.setAndAssertBlock(center.below(), test.below()); + + player.gameMode.destroyBlock(helper.absolutePos(center)); + + helper.assertItemEntityPresent(test.item(), center, 1.0); + + // Cleanup after ourselves + helper.setBlock(center, Blocks.AIR); + helper.setBlock(center.below(), Blocks.AIR); + + helper.removeAllItemEntitiesInRange(center, 1.0); + } + + helper.succeed(); + } + + + private record Test(BlockState state, @Nullable BlockState below, Item item) { + private static Test of(Block block) { + return new Test(block.defaultBlockState(), null, block.asItem()); + } + private static Test of(BlockState state, Item item) { + return new Test(state, null, item); + } + private static Test tall(Block block, Item item) { + return new Test(block.defaultBlockState().setValue(DoublePlantBlock.HALF, DoubleBlockHalf.UPPER), block.defaultBlockState().setValue(DoublePlantBlock.HALF, DoubleBlockHalf.LOWER), item); + } + } +}