Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions patches/net/minecraft/world/level/storage/loot/LootPool.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
+ Holder<ContextFloatProvider> bonusRolls,
+ java.util.Optional<String> name
) {
this.entries = entries;
- this.entries = entries;
+ this.entries = Lists.newArrayList(entries); // Neo: copy into a mutable list so entries can be added or removed
this.condition = condition;
this.modifier = modifier;
this.rolls = rolls;
Expand All @@ -27,7 +28,7 @@
}

private void addRandomItem(Consumer<ItemStack> result, LootContext context) {
@@ -110,6 +_,57 @@
@@ -110,6 +_,97 @@
Validatable.validateHolder(output, "bonus_rolls", this.bonusRolls);
}

Expand Down Expand Up @@ -81,6 +82,46 @@
+ checkFrozen();
+ this.bonusRolls = v;
+ }
+
+ // Neo: Add accessors and mutators for the entries of this pool
+ /**
+ * {@return an unmodifiable view of the entries of this pool}
+ */
+ public List<LootPoolEntryContainer> getEntries() {
+ return java.util.Collections.unmodifiableList(this.entries);
+ }
+
+ /**
+ * Adds an entry to the end of this pool.
+ *
+ * @throws RuntimeException if this pool is frozen
+ */
+ public void addEntry(LootPoolEntryContainer entry) {
+ checkFrozen();
+ this.entries.add(java.util.Objects.requireNonNull(entry));
+ }
+
+ /**
+ * Removes the given entry from this pool.
+ *
+ * @return {@code true} if the entry was part of this pool
+ * @throws RuntimeException if this pool is frozen
+ */
+ public boolean removeEntry(LootPoolEntryContainer entry) {
+ checkFrozen();
+ return this.entries.remove(entry);
+ }
+
+ /**
+ * Removes all entries of this pool that match the given predicate.
+ *
+ * @return {@code true} if any entry was removed
+ * @throws RuntimeException if this pool is frozen
+ */
+ public boolean removeEntriesIf(java.util.function.Predicate<? super LootPoolEntryContainer> filter) {
+ checkFrozen();
+ return this.entries.removeIf(filter);
+ }
+
public static LootPool.Builder lootPool() {
return new LootPool.Builder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"pools": [
{
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:diamond"
},
{
"type": "minecraft:item",
"name": "minecraft:gold_nugget"
}
],
"name": "test_pool",
"rolls": 1
}
],
"random_sequence": "neoforge:test_loot_table_5"
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class LootPoolTest {
private static final ResourceKey<LootTable> TEST_LOOT_TABLE_2 = ResourceKey.create(Registries.LOOT_TABLE, Identifier.fromNamespaceAndPath("neoforge", "test_loot_table_2"));
private static final ResourceKey<LootTable> TEST_LOOT_TABLE_3 = ResourceKey.create(Registries.LOOT_TABLE, Identifier.fromNamespaceAndPath("neoforge", "test_loot_table_3"));
private static final ResourceKey<LootTable> TEST_LOOT_TABLE_4 = ResourceKey.create(Registries.LOOT_TABLE, Identifier.fromNamespaceAndPath("neoforge", "test_loot_table_4"));
private static final ResourceKey<LootTable> TEST_LOOT_TABLE_5 = ResourceKey.create(Registries.LOOT_TABLE, Identifier.fromNamespaceAndPath("neoforge", "test_loot_table_5"));

@GameTest
@EmptyTemplate
Expand Down Expand Up @@ -184,6 +185,76 @@ static void yellowConcreteLootTableAppended(final DynamicTest test, final Regist
.thenSucceed());
}

@GameTest
@EmptyTemplate
@TestHolder(description = "Tests if the LootTableLoadEvent can add entries to and remove entries from an existing loot pool")
static void lootPoolEntriesModified(final DynamicTest test, final RegistrationHelper reg) {
ResourceKey<LootTable> lootTableToUse = TEST_LOOT_TABLE_5;
// The entry added by the listener, so that it can be identified later
LootPoolEntryContainer[] addedEntry = new LootPoolEntryContainer[1];

reg.registries(registries -> registries.gatherFor("neoforge").lootTable(
new LootTableProvider.SubProviderEntry(context -> () -> {
context.accept(
lootTableToUse,
LootTable.lootTable()
.withPool(LootPool.lootPool()
.add(LootItem.lootTableItem(Items.DIAMOND))
.add(LootItem.lootTableItem(Items.GOLD_NUGGET))
.name("test_pool")));
}, LootContextParamSets.ALL_PARAMS)));

NeoForge.EVENT_BUS.addListener((final LootTableLoadEvent event) -> {
if (event.getKey() == lootTableToUse) {
LootPool pool = event.getTable().getPool("test_pool");
LootPoolEntryContainer goldNugget = pool.getEntries().get(1);
addedEntry[0] = LootItem.lootTableItem(Items.EMERALD).build();
pool.addEntry(addedEntry[0]);
pool.removeEntriesIf(entry -> entry == goldNugget);
}
});

test.onGameTest(helper -> helper.startSequence()
.thenExecute(() -> {
LootTable lootTable = helper.getLevel().getServer().reloadableRegistries().getLootTable(ResourceKey.create(Registries.LOOT_TABLE, lootTableToUse.identifier()));
LootPool pool = lootTable.getPool("test_pool");
helper.assertNotNull(pool, "Expected test_pool pool");
helper.assertTrue(pool.getEntries().size() == 2 && pool.getEntries().get(1) == addedEntry[0], "Expected pool entries to be the original first entry followed by the added entry");

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.CONCRETE.pink().defaultBlockState()).create(LootContextParamSets.BLOCK);
for (int i = 0; i < 64; i++) {
ObjectArrayList<ItemStack> collectedItems = lootTable.getRandomItems(lootparams);
helper.assertTrue(collectedItems.size() == 1 && (collectedItems.getFirst().is(Items.DIAMOND) || collectedItems.getFirst().is(Items.EMERALD)), "Expected only diamonds or emeralds to be dropped, but got " + collectedItems);
}

helper.assertTrue(pool.removeEntry(addedEntry[0]), "Expected removing an existing entry to succeed");
helper.assertFalse(pool.removeEntry(addedEntry[0]), "Expected removing an absent entry to fail");
helper.assertTrue(pool.getEntries().size() == 1, "Expected one remaining entry");

boolean unmodifiable = false;
try {
pool.getEntries().clear();
} catch (UnsupportedOperationException e) {
unmodifiable = true;
}
helper.assertTrue(unmodifiable, "Expected the entries view to be unmodifiable");

pool.freeze();
boolean frozen = false;
try {
pool.addEntry(LootItem.lootTableItem(Items.DIAMOND).build());
} catch (RuntimeException e) {
frozen = true;
}
helper.assertTrue(frozen, "Expected adding an entry to a frozen pool to fail");
})
.thenSucceed());
}

@TestHolder(description = "Tests that loot tables correctly generate with loading conditions", enabledByDefault = true)
static void conditionalLootTable(final DynamicTest test, final RegistrationHelper reg) {
ResourceKey<LootTable> tableOne = ResourceKey.create(Registries.LOOT_TABLE, Identifier.fromNamespaceAndPath(test.createModId(), "table_1"));
Expand Down
Loading