From c335a929af58a312e77951ee9b5b7bd724c85209 Mon Sep 17 00:00:00 2001 From: TelepathicGrunt <40846040+TelepathicGrunt@users.noreply.github.com> Date: Tue, 28 May 2024 11:40:10 -0400 Subject: [PATCH] Move LootTableLoadEvent hook to proper spot (#1025) Closes #1021 --- .../storage/loot/LootDataType.java.patch | 21 +-- ...loot_table.json => test_loot_table_1.json} | 2 +- .../loot_tables/test_loot_table_2.json | 15 ++ .../loot_tables/test_loot_table_3.json | 15 ++ .../loot_tables/test_loot_table_4.json | 15 ++ .../neoforge/debug/loot/LootPoolTest.java | 142 +++++++++++++++++- 6 files changed, 189 insertions(+), 21 deletions(-) rename tests/src/generated/resources/data/neoforge/loot_tables/{test_loot_table.json => test_loot_table_1.json} (89%) create mode 100644 tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_2.json create mode 100644 tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_3.json create mode 100644 tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_4.json diff --git a/patches/net/minecraft/world/level/storage/loot/LootDataType.java.patch b/patches/net/minecraft/world/level/storage/loot/LootDataType.java.patch index cad72480b4..1f9b59ad0e 100644 --- a/patches/net/minecraft/world/level/storage/loot/LootDataType.java.patch +++ b/patches/net/minecraft/world/level/storage/loot/LootDataType.java.patch @@ -9,7 +9,7 @@ private static final Logger LOGGER = LogUtils.getLogger(); public static final LootDataType PREDICATE = new LootDataType<>( Registries.PREDICATE, LootItemConditions.DIRECT_CODEC, "predicates", createSimpleValidator() -@@ -26,17 +_,32 @@ +@@ -26,17 +_,34 @@ Registries.ITEM_MODIFIER, LootItemFunctions.ROOT_CODEC, "item_modifiers", createSimpleValidator() ); public static final LootDataType TABLE = new LootDataType<>( @@ -40,23 +40,10 @@ - return dataresult.result(); + return dataresult.result().map(it -> { + it.ifPresent(val -> idSetter.accept(val, p_279253_)); -+ return it.orElse(defaultValue); ++ T value = it.orElse(defaultValue); ++ if (value instanceof LootTable lootTable) value = (T) net.neoforged.neoforge.event.EventHooks.loadLootTable(p_279253_, lootTable); ++ return value; + }); } public static Stream> values() { -@@ -50,9 +_,12 @@ - } - - private static LootDataType.Validator createLootTableValidator() { -- return (p_339557_, p_339558_, p_339559_) -> p_339559_.validate( -- p_339557_.setParams(p_339559_.getParamSet()).enterElement("{" + p_339558_.registry() + "/" + p_339558_.location() + "}", p_339558_) -+ return (p_279333_, p_279227_, p_279406_) -> { -+ p_279406_ = net.neoforged.neoforge.event.EventHooks.loadLootTable(p_279406_.getLootTableId(), p_279406_); -+ p_279406_.validate( -+ p_279333_.setParams(p_279406_.getParamSet()).enterElement("{" + p_279227_.registry() + ":" + p_279227_.location() + "}", p_279227_) - ); -+ }; - } - - @FunctionalInterface diff --git a/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table.json b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_1.json similarity index 89% rename from tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table.json rename to tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_1.json index 177dba9b57..b3a18072cd 100644 --- a/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table.json +++ b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_1.json @@ -22,5 +22,5 @@ "rolls": 1.0 } ], - "random_sequence": "neoforge:test_loot_table" + "random_sequence": "neoforge:test_loot_table_1" } \ No newline at end of file diff --git a/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_2.json b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_2.json new file mode 100644 index 0000000000..761003e7df --- /dev/null +++ b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_2.json @@ -0,0 +1,15 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:pink_concrete" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "neoforge:test_loot_table_2" +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_3.json b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_3.json new file mode 100644 index 0000000000..4906240029 --- /dev/null +++ b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_3.json @@ -0,0 +1,15 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:orange_concrete" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "neoforge:test_loot_table_3" +} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_4.json b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_4.json new file mode 100644 index 0000000000..c440af02ae --- /dev/null +++ b/tests/src/generated/resources/data/neoforge/loot_tables/test_loot_table_4.json @@ -0,0 +1,15 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:yellow_concrete" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "neoforge:test_loot_table_4" +} \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java b/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java index b06f9e80fc..af0724ac67 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/loot/LootPoolTest.java @@ -5,18 +5,30 @@ package net.neoforged.neoforge.debug.loot; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Set; +import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.data.loot.LootTableProvider; import net.minecraft.gametest.framework.GameTest; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.storage.loot.LootParams; import net.minecraft.world.level.storage.loot.LootPool; import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.level.storage.loot.entries.LootItem; +import net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer; import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition; +import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.LootTableLoadEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -25,7 +37,10 @@ @ForEachTest(groups = "loot") public class LootPoolTest { - private static final ResourceKey TEST_LOOT_TABLE = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation("neoforge", "test_loot_table")); + private static final ResourceKey TEST_LOOT_TABLE_1 = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation("neoforge", "test_loot_table_1")); + private static final ResourceKey TEST_LOOT_TABLE_2 = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation("neoforge", "test_loot_table_2")); + private static final ResourceKey TEST_LOOT_TABLE_3 = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation("neoforge", "test_loot_table_3")); + private static final ResourceKey TEST_LOOT_TABLE_4 = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation("neoforge", "test_loot_table_4")); @GameTest @EmptyTemplate @@ -37,7 +52,7 @@ public static void testPoolLoading(DynamicTest test, RegistrationHelper reg) { List.of( new LootTableProvider.SubProviderEntry(() -> (provider, consumer) -> { consumer.accept( - TEST_LOOT_TABLE, + TEST_LOOT_TABLE_1, LootTable.lootTable() .withPool(LootPool.lootPool() .add(LootItem.lootTableItem(Items.DIAMOND)) @@ -48,7 +63,7 @@ public static void testPoolLoading(DynamicTest test, RegistrationHelper reg) { event.getLookupProvider())); test.onGameTest(helper -> { - var testTable = helper.getLevel().getServer().reloadableRegistries().getLootTable(TEST_LOOT_TABLE); + var testTable = helper.getLevel().getServer().reloadableRegistries().getLootTable(TEST_LOOT_TABLE_1); helper.assertTrue(testTable.getPool("custom_name") != null, "Expected custom_name pool"); helper.assertTrue(testTable.getPool("pool1") != null, "Expected unnamed pool pool1"); @@ -56,4 +71,125 @@ public static void testPoolLoading(DynamicTest test, RegistrationHelper reg) { helper.succeed(); }); } + + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if the LootTableLoadEvent can cancel a Loot Table") + static void pinkConcreteLootTableCanceled(final DynamicTest test, final RegistrationHelper reg) { + ResourceKey lootTableToUse = TEST_LOOT_TABLE_2; + + reg.addProvider(event -> new LootTableProvider( + event.getGenerator().getPackOutput(), + Set.of(), + List.of( + new LootTableProvider.SubProviderEntry(() -> (provider, consumer) -> { + consumer.accept( + lootTableToUse, + LootTable.lootTable() + .withPool(LootPool.lootPool() + .add(LootItem.lootTableItem(Items.PINK_CONCRETE)))); + }, LootContextParamSets.ALL_PARAMS)), + event.getLookupProvider())); + + NeoForge.EVENT_BUS.addListener((final LootTableLoadEvent event) -> { + if (event.getName().equals(lootTableToUse.location())) { + event.setCanceled(true); + } + }); + + test.onGameTest(helper -> helper.startSequence() + .thenExecute(() -> { + LootTable lootTable = helper.getLevel().getServer().reloadableRegistries().getLootTable(ResourceKey.create(Registries.LOOT_TABLE, lootTableToUse.location())); + LootParams.Builder lootParamsBuilder = new LootParams.Builder(helper.getLevel()) + .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(helper.absolutePos(BlockPos.ZERO))) + .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) + .withOptionalParameter(LootContextParams.BLOCK_ENTITY, null); + LootParams lootparams = lootParamsBuilder.withParameter(LootContextParams.BLOCK_STATE, Blocks.PINK_CONCRETE.defaultBlockState()).create(LootContextParamSets.BLOCK); + ObjectArrayList collectedItems = lootTable.getRandomItems(lootparams); + helper.assertTrue(collectedItems.isEmpty(), "neoforge:test_loot_table_2 Loot Table should be canceled and empty"); + }) + .thenSucceed()); + } + + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if the LootTableLoadEvent can replace a Loot Table with another") + static void orangeConcreteLootTableReplaced(final DynamicTest test, final RegistrationHelper reg) { + ResourceKey lootTableToUse = TEST_LOOT_TABLE_3; + + reg.addProvider(event -> new LootTableProvider( + event.getGenerator().getPackOutput(), + Set.of(), + List.of( + new LootTableProvider.SubProviderEntry(() -> (provider, consumer) -> { + consumer.accept( + lootTableToUse, + LootTable.lootTable() + .withPool(LootPool.lootPool() + .add(LootItem.lootTableItem(Items.ORANGE_CONCRETE)))); + }, LootContextParamSets.ALL_PARAMS)), + event.getLookupProvider())); + + NeoForge.EVENT_BUS.addListener((final LootTableLoadEvent event) -> { + if (event.getName().equals(lootTableToUse.location())) { + LootPoolSingletonContainer.Builder entry = LootItem.lootTableItem(Items.BLUE_CONCRETE); + LootPool.Builder pool = LootPool.lootPool().setRolls(ConstantValue.exactly(1)).add(entry).when(ExplosionCondition.survivesExplosion()); + event.setTable(new LootTable.Builder().withPool(pool).build()); + } + }); + + test.onGameTest(helper -> helper.startSequence() + .thenExecute(() -> { + LootTable lootTable = helper.getLevel().getServer().reloadableRegistries().getLootTable(ResourceKey.create(Registries.LOOT_TABLE, lootTableToUse.location())); + LootParams.Builder lootParamsBuilder = new LootParams.Builder(helper.getLevel()) + .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(helper.absolutePos(BlockPos.ZERO))) + .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) + .withOptionalParameter(LootContextParams.BLOCK_ENTITY, null); + LootParams lootparams = lootParamsBuilder.withParameter(LootContextParams.BLOCK_STATE, Blocks.PINK_CONCRETE.defaultBlockState()).create(LootContextParamSets.BLOCK); + ObjectArrayList collectedItems = lootTable.getRandomItems(lootparams); + helper.assertTrue(collectedItems.size() == 1 && collectedItems.get(0).getItem().equals(Items.BLUE_CONCRETE), "neoforge:test_loot_table_3 Loot Table should be replaced and drops Blue Concrete"); + }) + .thenSucceed()); + } + + @GameTest + @EmptyTemplate + @TestHolder(description = "Tests if the LootTableLoadEvent can add a new pool to an existing loot table") + static void yellowConcreteLootTableAppended(final DynamicTest test, final RegistrationHelper reg) { + ResourceKey lootTableToUse = TEST_LOOT_TABLE_4; + + reg.addProvider(event -> new LootTableProvider( + event.getGenerator().getPackOutput(), + Set.of(), + List.of( + new LootTableProvider.SubProviderEntry(() -> (provider, consumer) -> { + consumer.accept( + lootTableToUse, + LootTable.lootTable() + .withPool(LootPool.lootPool() + .add(LootItem.lootTableItem(Items.YELLOW_CONCRETE)))); + }, LootContextParamSets.ALL_PARAMS)), + event.getLookupProvider())); + + NeoForge.EVENT_BUS.addListener((final LootTableLoadEvent event) -> { + if (event.getName().equals(lootTableToUse.location())) { + LootPoolSingletonContainer.Builder entry = LootItem.lootTableItem(Items.YELLOW_CONCRETE); + LootPool.Builder pool = LootPool.lootPool().setRolls(ConstantValue.exactly(1)).add(entry).when(ExplosionCondition.survivesExplosion()); + event.getTable().addPool(pool.build()); + } + }); + + test.onGameTest(helper -> helper.startSequence() + .thenExecute(() -> { + LootTable lootTable = helper.getLevel().getServer().reloadableRegistries().getLootTable(ResourceKey.create(Registries.LOOT_TABLE, lootTableToUse.location())); + LootParams.Builder lootParamsBuilder = new LootParams.Builder(helper.getLevel()) + .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(helper.absolutePos(BlockPos.ZERO))) + .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) + .withOptionalParameter(LootContextParams.BLOCK_ENTITY, null); + LootParams lootparams = lootParamsBuilder.withParameter(LootContextParams.BLOCK_STATE, Blocks.PINK_CONCRETE.defaultBlockState()).create(LootContextParamSets.BLOCK); + ObjectArrayList collectedItems = lootTable.getRandomItems(lootparams); + helper.assertTrue(collectedItems.size() == 2 && collectedItems.stream().allMatch(itemStack -> itemStack.getItem().equals(Items.YELLOW_CONCRETE)), "neoforge:test_loot_table_4 Loot Table should drop 2 Yellow Concrete"); + }) + .thenSucceed()); + } }