diff --git a/fabric-loot-api-v2/README.md b/fabric-loot-api-v2/README.md
new file mode 100644
index 000000000..6003689ef
--- /dev/null
+++ b/fabric-loot-api-v2/README.md
@@ -0,0 +1,29 @@
+# Fabric Loot API (v2)
+
+This module includes APIs for modifying and creating loot tables.
+
+## [Loot table events](src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableEvents.java)
+
+This class provides two events for modifying loot tables.
+
+`LootTableEvents.REPLACE` runs first and lets you replace loot tables completely.
+
+`LootTableEvents.MODIFY` runs after and lets you modify loot tables, including the ones created in `REPLACE`,
+by adding new loot pools or loot functions to them.
+
+### Loot table sources
+
+Both events have access to a [loot table source](src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableSource.java)
+that you can use to check where a loot table is loaded from.
+
+For example, you can use this to check if a loot table is from a user data pack and
+not modify the user-provided data in your event.
+
+## Enhanced loot table and loot pool builders
+
+`LootTable.Builder` and `LootPool.Builder` implement
+injected interfaces ([`FabricLootTableBuilder`](src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootTableBuilder.java)
+and [`FabricLootPoolBuilder`](src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootPoolBuilder.java))
+which have additional methods for dealing with already-built objects and collections of objects.
+
+Those interfaces also have `copyOf` methods for creating copies of existing loot tables/pools as builders.
diff --git a/fabric-loot-api-v2/build.gradle b/fabric-loot-api-v2/build.gradle
new file mode 100644
index 000000000..5bda00956
--- /dev/null
+++ b/fabric-loot-api-v2/build.gradle
@@ -0,0 +1,7 @@
+archivesBaseName = "fabric-loot-api-v2"
+version = getSubprojectVersion(project)
+
+moduleDependencies(project, [
+ 'fabric-api-base',
+ 'fabric-resource-loader-v0'
+])
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootPoolBuilder.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootPoolBuilder.java
new file mode 100644
index 000000000..90d3db90d
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootPoolBuilder.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.loot.v2;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.condition.LootCondition;
+import net.minecraft.loot.entry.LootPoolEntry;
+import net.minecraft.loot.function.LootFunction;
+
+import net.fabricmc.fabric.mixin.loot.LootPoolAccessor;
+
+/**
+ * Convenience extensions to {@link LootPool.Builder}
+ * for adding pre-built objects or collections.
+ *
+ *
This interface is automatically injected to {@link LootPool.Builder}.
+ */
+@ApiStatus.NonExtendable
+public interface FabricLootPoolBuilder {
+ /**
+ * Adds an entry to this builder.
+ *
+ * @param entry the added loot entry
+ * @return this builder
+ */
+ default LootPool.Builder with(LootPoolEntry entry) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Adds entries to this builder.
+ *
+ * @param entries the added loot entries
+ * @return this builder
+ */
+ default LootPool.Builder with(Collection extends LootPoolEntry> entries) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Adds a condition to this builder.
+ *
+ * @param condition the added condition
+ * @return this builder
+ */
+ default LootPool.Builder conditionally(LootCondition condition) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Adds conditions to this builder.
+ *
+ * @param conditions the added conditions
+ * @return this builder
+ */
+ default LootPool.Builder conditionally(Collection extends LootCondition> conditions) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Applies a function to this builder.
+ *
+ * @param function the applied loot function
+ * @return this builder
+ */
+ default LootPool.Builder apply(LootFunction function) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Applies loot functions to this builder.
+ *
+ * @param functions the applied loot functions
+ * @return this builder
+ */
+ default LootPool.Builder apply(Collection extends LootFunction> functions) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Creates a builder copy of a loot pool.
+ *
+ * @param pool the loot pool
+ * @return the copied builder
+ */
+ static LootPool.Builder copyOf(LootPool pool) {
+ LootPoolAccessor accessor = (LootPoolAccessor) pool;
+ return LootPool.builder()
+ .rolls(accessor.fabric_getRolls())
+ .bonusRolls(accessor.fabric_getBonusRolls())
+ .with(List.of(accessor.fabric_getEntries()))
+ .conditionally(List.of(accessor.fabric_getConditions()))
+ .apply(List.of(accessor.fabric_getFunctions()));
+ }
+}
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootTableBuilder.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootTableBuilder.java
new file mode 100644
index 000000000..62b743ee0
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/FabricLootTableBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.loot.v2;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.LootTable;
+import net.minecraft.loot.function.LootFunction;
+
+import net.fabricmc.fabric.mixin.loot.LootTableAccessor;
+
+/**
+ * Convenience extensions to {@link LootTable.Builder}
+ * for adding pre-built objects or collections.
+ *
+ *
This interface is automatically injected to {@link LootTable.Builder}.
+ */
+@ApiStatus.NonExtendable
+public interface FabricLootTableBuilder {
+ /**
+ * Adds a loot pool to this builder.
+ *
+ * @param pool the added pool
+ * @return this builder
+ */
+ default LootTable.Builder pool(LootPool pool) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Applies a loot function to this builder.
+ *
+ * @param function the applied function
+ * @return this builder
+ */
+ default LootTable.Builder apply(LootFunction function) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Adds loot pools to this builder.
+ *
+ * @param pools the added pools
+ * @return this builder
+ */
+ default LootTable.Builder pools(Collection extends LootPool> pools) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Applies loot functions to this builder.
+ *
+ * @param functions the applied functions
+ * @return this builder
+ */
+ default LootTable.Builder apply(Collection extends LootFunction> functions) {
+ throw new UnsupportedOperationException("Implemented via mixin");
+ }
+
+ /**
+ * Creates a builder copy of a loot table.
+ *
+ * @param table the loot table
+ * @return the copied builder
+ */
+ static LootTable.Builder copyOf(LootTable table) {
+ LootTable.Builder builder = LootTable.builder();
+ LootTableAccessor accessor = (LootTableAccessor) table;
+
+ builder.type(table.getType());
+ builder.pools(List.of(accessor.fabric_getPools()));
+ builder.apply(List.of(accessor.fabric_getFunctions()));
+
+ return builder;
+ }
+}
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableEvents.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableEvents.java
new file mode 100644
index 000000000..6e861058b
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableEvents.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.loot.v2;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.loot.LootManager;
+import net.minecraft.loot.LootTable;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.util.Identifier;
+
+import net.fabricmc.fabric.api.event.Event;
+import net.fabricmc.fabric.api.event.EventFactory;
+
+/**
+ * Events for manipulating loot tables.
+ */
+public final class LootTableEvents {
+ /**
+ * This event can be used to replace loot tables.
+ * If a loot table is replaced, the iteration will stop for that loot table.
+ */
+ public static final Event REPLACE = EventFactory.createArrayBacked(Replace.class, listeners -> (resourceManager, lootManager, id, original, source) -> {
+ for (Replace listener : listeners) {
+ @Nullable LootTable replaced = listener.replaceLootTable(resourceManager, lootManager, id, original, source);
+
+ if (replaced != null) {
+ return replaced;
+ }
+ }
+
+ return null;
+ });
+
+ /**
+ * This event can be used to modify loot tables.
+ * The main use case is to add items to vanilla or mod loot tables (e.g. modded seeds to grass).
+ *
+ * You can also modify loot tables that are created by {@link #REPLACE}.
+ * They have the loot table source {@link LootTableSource#REPLACED}.
+ *
+ *
Example: adding diamonds to the cobblestone loot table
+ *
+ * {@code
+ * LootTableEvents.MODIFY.register((resourceManager, lootManager, id, tableBuilder, source) -> {
+ * // If the loot table is for the cobblestone block and it is not overridden by a user:
+ * if (Blocks.COBBLESTONE.getLootTableId().equals(id) && source.isBuiltin()) {
+ * // Create a new loot pool that will hold the diamonds.
+ * LootPool.Builder pool = LootPool.builder()
+ * // Add diamonds...
+ * .with(ItemEntry.builder(Items.DIAMOND))
+ * // ...only if the block would survive a potential explosion.
+ * .conditionally(SurvivesExplosionLootCondition.builder());
+ *
+ * // Add the loot pool to the loot table
+ * tableBuilder.pool(pool);
+ * }
+ * });
+ * }
+ *
+ */
+ public static final Event MODIFY = EventFactory.createArrayBacked(Modify.class, listeners -> (resourceManager, lootManager, id, tableBuilder, source) -> {
+ for (Modify listener : listeners) {
+ listener.modifyLootTable(resourceManager, lootManager, id, tableBuilder, source);
+ }
+ });
+
+ public interface Replace {
+ /**
+ * Replaces loot tables.
+ *
+ * @param resourceManager the server resource manager
+ * @param lootManager the loot manager
+ * @param id the loot table ID
+ * @param original the original loot table
+ * @param source the source of the original loot table
+ * @return the new loot table, or null if it wasn't replaced
+ */
+ @Nullable
+ LootTable replaceLootTable(ResourceManager resourceManager, LootManager lootManager, Identifier id, LootTable original, LootTableSource source);
+ }
+
+ public interface Modify {
+ /**
+ * Called when a loot table is loading to modify loot tables.
+ *
+ * @param resourceManager the server resource manager
+ * @param lootManager the loot manager
+ * @param id the loot table ID
+ * @param tableBuilder a builder of the loot table being loaded
+ * @param source the source of the loot table
+ */
+ void modifyLootTable(ResourceManager resourceManager, LootManager lootManager, Identifier id, LootTable.Builder tableBuilder, LootTableSource source);
+ }
+}
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableSource.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableSource.java
new file mode 100644
index 000000000..c2b48a4d8
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/LootTableSource.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.api.loot.v2;
+
+/**
+ * Describes where a loot table has been loaded from.
+ */
+public enum LootTableSource {
+ /**
+ * A loot table loaded from the default data pack.
+ */
+ VANILLA(true),
+
+ /**
+ * A loot table loaded from mods' bundled resources.
+ *
+ * This includes the additional builtin data packs registered by mods
+ * with Fabric Resource Loader.
+ */
+ MOD(true),
+
+ /**
+ * A loot table loaded from an external data pack.
+ */
+ DATA_PACK(false),
+
+ /**
+ * A loot table created in {@link LootTableEvents#REPLACE}.
+ */
+ REPLACED(false);
+
+ private final boolean builtin;
+
+ LootTableSource(boolean builtin) {
+ this.builtin = builtin;
+ }
+
+ /**
+ * Returns whether this loot table source is builtin
+ * and bundled in the vanilla or mod resources.
+ *
+ *
{@link #VANILLA} and {@link #MOD} are builtin.
+ *
+ * @return {@code true} if builtin, {@code false} otherwise
+ */
+ public boolean isBuiltin() {
+ return builtin;
+ }
+}
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/package-info.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/package-info.java
new file mode 100644
index 000000000..c1cd4106c
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/api/loot/v2/package-info.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The Fabric Loot API for manipulating and creating loot tables.
+ *
+ *
Events
+ * {@link net.fabricmc.fabric.api.loot.v2.LootTableEvents} has events to modify existing loot tables,
+ * or outright replace them with a new loot table.
+ *
+ * You can also check where loot tables are coming from in those events with
+ * {@link net.fabricmc.fabric.api.loot.v2.LootTableSource}. This is useful when you only want to modify
+ * loot tables from mods or vanilla, but not user-created data packs.
+ *
+ *
Extended loot table and pool builders
+ * This API has injected interfaces to add useful methods to
+ * {@linkplain net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder loot table} and
+ * {@linkplain net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder loot pool} builders.
+ * They let you add pre-built objects instead of builders, and collections of objects to the builder
+ * with one method call.
+ */
+package net.fabricmc.fabric.api.loot.v2;
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/impl/loot/LootUtil.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/impl/loot/LootUtil.java
new file mode 100644
index 000000000..05013020f
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/impl/loot/LootUtil.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.impl.loot;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.minecraft.resource.Resource;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.resource.ResourcePackSource;
+import net.minecraft.util.Identifier;
+
+import net.fabricmc.fabric.api.loot.v2.LootTableSource;
+import net.fabricmc.fabric.impl.resource.loader.BuiltinModResourcePackSource;
+import net.fabricmc.fabric.impl.resource.loader.FabricResource;
+import net.fabricmc.fabric.impl.resource.loader.ModResourcePackCreator;
+
+public final class LootUtil {
+ public static final Logger LOGGER = LoggerFactory.getLogger("fabric-loot-api-v2");
+
+ public static LootTableSource determineSource(Identifier lootTableId, ResourceManager resourceManager) {
+ Identifier resourceId = new Identifier(lootTableId.getNamespace(), "loot_tables/%s.json".formatted(lootTableId.getPath()));
+
+ try (Resource resource = resourceManager.getResource(resourceId)) {
+ ResourcePackSource packSource = ((FabricResource) resource).getFabricPackSource();
+
+ if (packSource == ResourcePackSource.PACK_SOURCE_BUILTIN) {
+ return LootTableSource.VANILLA;
+ } else if (packSource == ModResourcePackCreator.RESOURCE_PACK_SOURCE || packSource instanceof BuiltinModResourcePackSource) {
+ return LootTableSource.MOD;
+ }
+ } catch (IOException e) {
+ LOGGER.error("Could not open resource for loot table {} to check its source", lootTableId, e);
+ }
+
+ // If not builtin or mod, assume external data pack.
+ // It might also be a virtual loot table injected via mixin instead of being loaded
+ // from a resource, but we can't determine that here.
+ return LootTableSource.DATA_PACK;
+ }
+}
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootManagerMixin.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootManagerMixin.java
new file mode 100644
index 000000000..10dc732c4
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootManagerMixin.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.loot;
+
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonObject;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import net.minecraft.loot.LootManager;
+import net.minecraft.loot.LootTable;
+import net.minecraft.loot.LootTables;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.profiler.Profiler;
+
+import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
+import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
+import net.fabricmc.fabric.api.loot.v2.LootTableSource;
+import net.fabricmc.fabric.impl.loot.LootUtil;
+
+/**
+ * Implements the events from {@link LootTableEvents}.
+ */
+@Mixin(LootManager.class)
+abstract class LootManagerMixin {
+ @Shadow
+ private Map tables;
+
+ @Inject(method = "apply", at = @At("RETURN"))
+ private void apply(Map jsonMap, ResourceManager resourceManager, Profiler profiler, CallbackInfo info) {
+ // The builder for the new LootManager.tables map with modified loot tables.
+ // We're using an immutable map to match vanilla.
+ ImmutableMap.Builder newTables = ImmutableMap.builder();
+
+ tables.forEach((id, table) -> {
+ if (id.equals(LootTables.EMPTY)) {
+ // This is a special table and cannot be modified.
+ // Vanilla also warns about that.
+ return;
+ }
+
+ // noinspection ConstantConditions
+ LootManager lootManager = (LootManager) (Object) this;
+ LootTableSource source = LootUtil.determineSource(id, resourceManager);
+
+ // Invoke the REPLACE event for the current loot table.
+ LootTable replacement = LootTableEvents.REPLACE.invoker().replaceLootTable(resourceManager, lootManager, id, table, source);
+
+ if (replacement != null) {
+ // Set the loot table to MODIFY to be the replacement loot table.
+ // The MODIFY event will also see it as a replaced loot table via the source.
+ table = replacement;
+ source = LootTableSource.REPLACED;
+ }
+
+ // Turn the current table into a modifiable builder and invoke the MODIFY event.
+ LootTable.Builder builder = FabricLootTableBuilder.copyOf(table);
+ LootTableEvents.MODIFY.invoker().modifyLootTable(resourceManager, lootManager, id, builder, source);
+
+ // Turn the builder back into a loot table and store it in the new table.
+ newTables.put(id, builder.build());
+ });
+
+ // Finally, store the new loot table map in the field.
+ tables = newTables.build();
+ }
+}
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootPoolBuilderHooks.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootPoolAccessor.java
similarity index 57%
rename from fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootPoolBuilderHooks.java
rename to fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootPoolAccessor.java
index 30085cec3..dc7841e11 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootPoolBuilderHooks.java
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootPoolAccessor.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package net.fabricmc.fabric.mixin.loot.table;
-
-import java.util.List;
+package net.fabricmc.fabric.mixin.loot;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@@ -25,13 +23,26 @@ import net.minecraft.loot.LootPool;
import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction;
+import net.minecraft.loot.provider.number.LootNumberProvider;
-@Mixin(LootPool.Builder.class)
-public interface LootPoolBuilderHooks {
- @Accessor
- List getEntries();
- @Accessor
- List getConditions();
- @Accessor
- List getFunctions();
+/**
+ * Accesses loot pool fields for {@link net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder#copyOf(LootPool)}.
+ * These are normally available in the transitive access widener module.
+ */
+@Mixin(LootPool.class)
+public interface LootPoolAccessor {
+ @Accessor("rolls")
+ LootNumberProvider fabric_getRolls();
+
+ @Accessor("bonusRolls")
+ LootNumberProvider fabric_getBonusRolls();
+
+ @Accessor("entries")
+ LootPoolEntry[] fabric_getEntries();
+
+ @Accessor("conditions")
+ LootCondition[] fabric_getConditions();
+
+ @Accessor("functions")
+ LootFunction[] fabric_getFunctions();
}
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootPoolBuilderMixin.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootPoolBuilderMixin.java
new file mode 100644
index 000000000..d1124fac8
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootPoolBuilderMixin.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.loot;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.condition.LootCondition;
+import net.minecraft.loot.entry.LootPoolEntry;
+import net.minecraft.loot.function.LootFunction;
+
+import net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder;
+
+/**
+ * The implementation of the injected interface {@link FabricLootPoolBuilder}.
+ * Simply implements the new methods by adding the relevant objects inside the lists.
+ */
+@Mixin(LootPool.Builder.class)
+abstract class LootPoolBuilderMixin implements FabricLootPoolBuilder {
+ @Shadow
+ @Final
+ private List entries;
+
+ @Shadow
+ @Final
+ private List conditions;
+
+ @Shadow
+ @Final
+ private List functions;
+
+ @Unique
+ private LootPool.Builder self() {
+ // noinspection ConstantConditions
+ return (LootPool.Builder) (Object) this;
+ }
+
+ @Override
+ public LootPool.Builder with(LootPoolEntry entry) {
+ this.entries.add(entry);
+ return self();
+ }
+
+ @Override
+ public LootPool.Builder with(Collection extends LootPoolEntry> entries) {
+ this.entries.addAll(entries);
+ return self();
+ }
+
+ @Override
+ public LootPool.Builder conditionally(LootCondition condition) {
+ this.conditions.add(condition);
+ return self();
+ }
+
+ @Override
+ public LootPool.Builder conditionally(Collection extends LootCondition> conditions) {
+ this.conditions.addAll(conditions);
+ return self();
+ }
+
+ @Override
+ public LootPool.Builder apply(LootFunction function) {
+ this.functions.add(function);
+ return self();
+ }
+
+ @Override
+ public LootPool.Builder apply(Collection extends LootFunction> functions) {
+ this.functions.addAll(functions);
+ return self();
+ }
+}
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootSupplierBuilderHooks.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootTableAccessor.java
similarity index 66%
rename from fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootSupplierBuilderHooks.java
rename to fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootTableAccessor.java
index ad07efbcb..b9d8c95c4 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootSupplierBuilderHooks.java
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootTableAccessor.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package net.fabricmc.fabric.mixin.loot.table;
-
-import java.util.List;
+package net.fabricmc.fabric.mixin.loot;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@@ -25,10 +23,15 @@ import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.function.LootFunction;
-@Mixin(LootTable.Builder.class)
-public interface LootSupplierBuilderHooks {
- @Accessor
- List getPools();
- @Accessor
- List getFunctions();
+/**
+ * Accesses loot table fields for {@link net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder#copyOf(LootTable)}.
+ * These are normally available in the transitive access widener module.
+ */
+@Mixin(LootTable.class)
+public interface LootTableAccessor {
+ @Accessor("pools")
+ LootPool[] fabric_getPools();
+
+ @Accessor("functions")
+ LootFunction[] fabric_getFunctions();
}
diff --git a/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootTableBuilderMixin.java b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootTableBuilderMixin.java
new file mode 100644
index 000000000..f4f60e37b
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/java/net/fabricmc/fabric/mixin/loot/LootTableBuilderMixin.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.loot;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.LootTable;
+import net.minecraft.loot.function.LootFunction;
+
+import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
+
+/**
+ * The implementation of the injected interface {@link FabricLootTableBuilder}.
+ * Simply implements the new methods by adding the relevant objects inside the lists.
+ */
+@Mixin(LootTable.Builder.class)
+abstract class LootTableBuilderMixin implements FabricLootTableBuilder {
+ @Shadow
+ @Final
+ private List pools;
+
+ @Shadow
+ @Final
+ private List functions;
+
+ @Unique
+ private LootTable.Builder self() {
+ // noinspection ConstantConditions
+ return (LootTable.Builder) (Object) this;
+ }
+
+ @Override
+ public LootTable.Builder pool(LootPool pool) {
+ this.pools.add(pool);
+ return self();
+ }
+
+ @Override
+ public LootTable.Builder apply(LootFunction function) {
+ this.functions.add(function);
+ return self();
+ }
+
+ @Override
+ public LootTable.Builder pools(Collection extends LootPool> pools) {
+ this.pools.addAll(pools);
+ return self();
+ }
+
+ @Override
+ public LootTable.Builder apply(Collection extends LootFunction> functions) {
+ this.functions.addAll(functions);
+ return self();
+ }
+}
diff --git a/fabric-loot-api-v2/src/main/resources/assets/fabric-loot-api-v2/icon.png b/fabric-loot-api-v2/src/main/resources/assets/fabric-loot-api-v2/icon.png
new file mode 100644
index 000000000..2931efbf6
Binary files /dev/null and b/fabric-loot-api-v2/src/main/resources/assets/fabric-loot-api-v2/icon.png differ
diff --git a/fabric-loot-api-v2/src/main/resources/fabric-loot-api-v2.mixins.json b/fabric-loot-api-v2/src/main/resources/fabric-loot-api-v2.mixins.json
new file mode 100644
index 000000000..c70ccf809
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/resources/fabric-loot-api-v2.mixins.json
@@ -0,0 +1,15 @@
+{
+ "required": true,
+ "package": "net.fabricmc.fabric.mixin.loot",
+ "compatibilityLevel": "JAVA_8",
+ "mixins": [
+ "LootManagerMixin",
+ "LootPoolAccessor",
+ "LootPoolBuilderMixin",
+ "LootTableAccessor",
+ "LootTableBuilderMixin"
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ }
+}
diff --git a/fabric-loot-api-v2/src/main/resources/fabric.mod.json b/fabric-loot-api-v2/src/main/resources/fabric.mod.json
new file mode 100644
index 000000000..6d73a269f
--- /dev/null
+++ b/fabric-loot-api-v2/src/main/resources/fabric.mod.json
@@ -0,0 +1,34 @@
+{
+ "schemaVersion": 1,
+ "id": "fabric-loot-api-v2",
+ "name": "Fabric Loot API (v2)",
+ "version": "${version}",
+ "environment": "*",
+ "license": "Apache-2.0",
+ "icon": "assets/fabric-loot-api-v2/icon.png",
+ "contact": {
+ "homepage": "https://fabricmc.net",
+ "irc": "irc://irc.esper.net:6667/fabric",
+ "issues": "https://github.com/FabricMC/fabric/issues",
+ "sources": "https://github.com/FabricMC/fabric"
+ },
+ "authors": [
+ "FabricMC"
+ ],
+ "depends": {
+ "fabricloader": ">=0.4.0",
+ "fabric-api-base": "*",
+ "fabric-resource-loader-v0": "*"
+ },
+ "description": "Hooks for manipulating loot tables.",
+ "mixins": [
+ "fabric-loot-api-v2.mixins.json"
+ ],
+ "custom": {
+ "fabric-api:module-lifecycle": "stable",
+ "loom:injected_interfaces": {
+ "net/minecraft/class_52\u0024class_53": ["net/fabricmc/fabric/api/loot/v2/FabricLootTableBuilder"],
+ "net/minecraft/class_55\u0024class_56": ["net/fabricmc/fabric/api/loot/v2/FabricLootPoolBuilder"]
+ }
+ }
+}
diff --git a/fabric-loot-api-v2/src/testmod/java/net/fabricmc/fabric/test/loot/LootTest.java b/fabric-loot-api-v2/src/testmod/java/net/fabricmc/fabric/test/loot/LootTest.java
new file mode 100644
index 000000000..fa916ceef
--- /dev/null
+++ b/fabric-loot-api-v2/src/testmod/java/net/fabricmc/fabric/test/loot/LootTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.loot;
+
+import net.minecraft.block.Blocks;
+import net.minecraft.item.Items;
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.LootTable;
+import net.minecraft.loot.condition.SurvivesExplosionLootCondition;
+import net.minecraft.loot.entry.ItemEntry;
+import net.minecraft.loot.function.SetNameLootFunction;
+import net.minecraft.text.LiteralText;
+
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
+import net.fabricmc.fabric.api.loot.v2.LootTableSource;
+
+public class LootTest implements ModInitializer {
+ @Override
+ public void onInitialize() {
+ // Test loot table load event
+ // The LootTable.Builder LootPool.Builder methods here should use
+ // prebuilt entries and pools to test the injected methods.
+ LootTableEvents.REPLACE.register((resourceManager, lootManager, id, original, source) -> {
+ if (Blocks.BLACK_WOOL.getLootTableId().equals(id)) {
+ if (source != LootTableSource.VANILLA) {
+ throw new AssertionError("black wool loot table should have LootTableSource.VANILLA");
+ }
+
+ // Replace black wool drops with an iron ingot
+ LootPool pool = LootPool.builder()
+ .with(ItemEntry.builder(Items.IRON_INGOT).build())
+ .build();
+
+ return LootTable.builder().pool(pool).build();
+ }
+
+ return null;
+ });
+
+ // Test that the event is stopped when the loot table is replaced
+ LootTableEvents.REPLACE.register((resourceManager, lootManager, id, original, source) -> {
+ if (Blocks.BLACK_WOOL.getLootTableId().equals(id)) {
+ throw new AssertionError("Event should have been stopped from replaced loot table");
+ }
+
+ return null;
+ });
+
+ LootTableEvents.MODIFY.register((resourceManager, lootManager, id, tableBuilder, source) -> {
+ if (Blocks.BLACK_WOOL.getLootTableId().equals(id) && source != LootTableSource.REPLACED) {
+ throw new AssertionError("black wool loot table should have LootTableSource.REPLACED");
+ }
+
+ if (Blocks.WHITE_WOOL.getLootTableId().equals(id)) {
+ if (source != LootTableSource.VANILLA) {
+ throw new AssertionError("white wool loot table should have LootTableSource.VANILLA");
+ }
+
+ // Add gold ingot with custom name to white wool drops
+ LootPool pool = LootPool.builder()
+ .with(ItemEntry.builder(Items.GOLD_INGOT).build())
+ .conditionally(SurvivesExplosionLootCondition.builder().build())
+ .apply(SetNameLootFunction.builder(new LiteralText("Gold from White Wool")).build())
+ .build();
+
+ tableBuilder.pool(pool);
+ }
+
+ // We modify red wool to drop diamonds in the test mod resources.
+ if (Blocks.RED_WOOL.getLootTableId().equals(id) && source != LootTableSource.MOD) {
+ throw new AssertionError("red wool loot table should have LootTableSource.MOD");
+ }
+ });
+ }
+}
diff --git a/fabric-loot-api-v2/src/testmod/resources/data/minecraft/loot_tables/blocks/red_wool.json b/fabric-loot-api-v2/src/testmod/resources/data/minecraft/loot_tables/blocks/red_wool.json
new file mode 100644
index 000000000..33e614e16
--- /dev/null
+++ b/fabric-loot-api-v2/src/testmod/resources/data/minecraft/loot_tables/blocks/red_wool.json
@@ -0,0 +1,19 @@
+{
+ "type": "minecraft:block",
+ "pools": [
+ {
+ "rolls": 1,
+ "entries": [
+ {
+ "type": "minecraft:item",
+ "name": "minecraft:diamond"
+ }
+ ],
+ "conditions": [
+ {
+ "condition": "minecraft:survives_explosion"
+ }
+ ]
+ }
+ ]
+}
diff --git a/fabric-loot-api-v2/src/testmod/resources/fabric.mod.json b/fabric-loot-api-v2/src/testmod/resources/fabric.mod.json
new file mode 100644
index 000000000..1057a5106
--- /dev/null
+++ b/fabric-loot-api-v2/src/testmod/resources/fabric.mod.json
@@ -0,0 +1,16 @@
+{
+ "schemaVersion": 1,
+ "id": "fabric-loot-api-v2-testmod",
+ "name": "Fabric Loot Table API (v2) Test Mod",
+ "version": "1.0.0",
+ "environment": "*",
+ "license": "Apache-2.0",
+ "depends": {
+ "fabric-loot-api-v2": "*"
+ },
+ "entrypoints": {
+ "main": [
+ "net.fabricmc.fabric.test.loot.LootTest"
+ ]
+ }
+}
diff --git a/fabric-loot-tables-v1/build.gradle b/fabric-loot-tables-v1/build.gradle
index 9303f46c4..f9a445261 100644
--- a/fabric-loot-tables-v1/build.gradle
+++ b/fabric-loot-tables-v1/build.gradle
@@ -2,5 +2,10 @@ archivesBaseName = "fabric-loot-tables-v1"
version = getSubprojectVersion(project)
moduleDependencies(project, [
- 'fabric-api-base'
+ 'fabric-api-base',
+ 'fabric-loot-api-v2'
])
+
+dependencies {
+ testmodRuntimeOnly(project(path: ':fabric-resource-loader-v0', configuration: 'namedElements'))
+}
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPool.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPool.java
index 3cb664229..e310bdb03 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPool.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPool.java
@@ -19,15 +19,18 @@ package net.fabricmc.fabric.api.loot.v1;
import java.util.List;
import net.minecraft.loot.LootPool;
-import net.minecraft.loot.provider.number.LootNumberProvider;
import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction;
+import net.minecraft.loot.provider.number.LootNumberProvider;
/**
* An interface implemented by all {@code net.minecraft.loot.LootPool} instances when
* Fabric API is present. Contains accessors for various fields.
+ *
+ * @deprecated Replaced with transitive access wideners in Fabric Transitive Access Wideners (v1).
*/
+@Deprecated
public interface FabricLootPool {
default LootPool asVanilla() {
return (LootPool) this;
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPoolBuilder.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPoolBuilder.java
index 81744e41c..7f85a0baa 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPoolBuilder.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootPoolBuilder.java
@@ -22,17 +22,21 @@ import net.minecraft.loot.provider.number.LootNumberProvider;
import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.LootFunction;
-import net.fabricmc.fabric.mixin.loot.table.LootPoolBuilderHooks;
-
+/**
+ * @deprecated Replaced with {@link net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder}.
+ */
+@Deprecated
public class FabricLootPoolBuilder extends LootPool.Builder {
- private final LootPoolBuilderHooks extended = (LootPoolBuilderHooks) this;
-
private FabricLootPoolBuilder() { }
private FabricLootPoolBuilder(LootPool pool) {
copyFrom(pool, true);
}
+ private net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder asV2() {
+ return (net.fabricmc.fabric.api.loot.v2.FabricLootPoolBuilder) this;
+ }
+
@Override
public FabricLootPoolBuilder rolls(LootNumberProvider range) {
super.rolls(range);
@@ -58,17 +62,17 @@ public class FabricLootPoolBuilder extends LootPool.Builder {
}
public FabricLootPoolBuilder withEntry(LootPoolEntry entry) {
- extended.getEntries().add(entry);
+ asV2().with(entry);
return this;
}
public FabricLootPoolBuilder withCondition(LootCondition condition) {
- extended.getConditions().add(condition);
+ asV2().conditionally(condition);
return this;
}
public FabricLootPoolBuilder withFunction(LootFunction function) {
- extended.getFunctions().add(function);
+ asV2().apply(function);
return this;
}
@@ -89,13 +93,13 @@ public class FabricLootPoolBuilder extends LootPool.Builder {
* If {@code copyRolls} is true, the {@link FabricLootPool#getRolls rolls} of the pool are also copied.
*/
public FabricLootPoolBuilder copyFrom(LootPool pool, boolean copyRolls) {
- FabricLootPool extendedPool = (FabricLootPool) pool;
- extended.getConditions().addAll(extendedPool.getConditions());
- extended.getFunctions().addAll(extendedPool.getFunctions());
- extended.getEntries().addAll(extendedPool.getEntries());
+ FabricLootPool extended = (FabricLootPool) pool;
+ asV2().with(extended.getEntries());
+ asV2().conditionally(extended.getConditions());
+ asV2().apply(extended.getFunctions());
if (copyRolls) {
- rolls(extendedPool.getRolls());
+ rolls(extended.getRolls());
}
return this;
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplier.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplier.java
index d3188e0c4..4890d1cc0 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplier.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplier.java
@@ -24,9 +24,12 @@ import net.minecraft.loot.context.LootContextType;
import net.minecraft.loot.function.LootFunction;
/**
- * An interface implemented by all {@code net.minecraft.loot.LootSupplier} instances when
+ * An interface implemented by all {@link LootTable} instances when
* Fabric API is present. Contains accessors for various fields.
+ *
+ * @deprecated Replaced with transitive access wideners in Fabric Transitive Access Wideners (v1).
*/
+@Deprecated
public interface FabricLootSupplier {
default LootTable asVanilla() {
return (LootTable) this;
@@ -34,5 +37,7 @@ public interface FabricLootSupplier {
List getPools();
List getFunctions();
- LootContextType getType();
+ default LootContextType getType() {
+ return asVanilla().getType(); // Vanilla has this now
+ }
}
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplierBuilder.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplierBuilder.java
index 56d2971a8..657ce56a2 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplierBuilder.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/FabricLootSupplierBuilder.java
@@ -23,17 +23,23 @@ import net.minecraft.loot.LootTable;
import net.minecraft.loot.context.LootContextType;
import net.minecraft.loot.function.LootFunction;
-import net.fabricmc.fabric.mixin.loot.table.LootSupplierBuilderHooks;
+import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
+/**
+ * @deprecated Replaced with {@link FabricLootTableBuilder}.
+ */
+@Deprecated
public class FabricLootSupplierBuilder extends LootTable.Builder {
- private final LootSupplierBuilderHooks extended = (LootSupplierBuilderHooks) this;
-
protected FabricLootSupplierBuilder() { }
private FabricLootSupplierBuilder(LootTable supplier) {
copyFrom(supplier, true);
}
+ private FabricLootTableBuilder asV2() {
+ return (FabricLootTableBuilder) this;
+ }
+
@Override
public FabricLootSupplierBuilder pool(LootPool.Builder pool) {
super.pool(pool);
@@ -53,22 +59,22 @@ public class FabricLootSupplierBuilder extends LootTable.Builder {
}
public FabricLootSupplierBuilder withPool(LootPool pool) {
- extended.getPools().add(pool);
+ asV2().pool(pool);
return this;
}
public FabricLootSupplierBuilder withFunction(LootFunction function) {
- extended.getFunctions().add(function);
+ asV2().apply(function);
return this;
}
public FabricLootSupplierBuilder withPools(Collection pools) {
- pools.forEach(this::withPool);
+ asV2().pools(pools);
return this;
}
public FabricLootSupplierBuilder withFunctions(Collection functions) {
- functions.forEach(this::withFunction);
+ asV2().apply(functions);
return this;
}
@@ -86,8 +92,8 @@ public class FabricLootSupplierBuilder extends LootTable.Builder {
*/
public FabricLootSupplierBuilder copyFrom(LootTable supplier, boolean copyType) {
FabricLootSupplier extendedSupplier = (FabricLootSupplier) supplier;
- extended.getPools().addAll(extendedSupplier.getPools());
- extended.getFunctions().addAll(extendedSupplier.getFunctions());
+ asV2().pools(extendedSupplier.getPools());
+ asV2().apply(extendedSupplier.getFunctions());
if (copyType) {
type(extendedSupplier.getType());
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootEntryTypeRegistry.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootEntryTypeRegistry.java
index c3077123e..c2ad6a0a5 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootEntryTypeRegistry.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootEntryTypeRegistry.java
@@ -23,11 +23,13 @@ import net.minecraft.util.JsonSerializer;
import net.fabricmc.fabric.impl.loot.table.LootEntryTypeRegistryImpl;
/**
- * Fabric's extensions to {@code net.minecraft.loot.entry.LootEntries} for registering
+ * Fabric's extensions to {@link net.minecraft.loot.entry.LootPoolEntryTypes} for registering
* custom loot entry types.
*
* @see #register
+ * @deprecated Use {@link net.minecraft.util.registry.Registry#LOOT_POOL_ENTRY_TYPE} from vanilla instead.
*/
+@Deprecated
public interface LootEntryTypeRegistry {
LootEntryTypeRegistry INSTANCE = new LootEntryTypeRegistryImpl();
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootJsonParser.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootJsonParser.java
index 5ecb06322..86246980e 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootJsonParser.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/LootJsonParser.java
@@ -17,37 +17,26 @@
package net.fabricmc.fabric.api.loot.v1;
import java.io.Reader;
-import java.lang.reflect.Field;
-import java.util.stream.Stream;
import com.google.gson.Gson;
+import net.minecraft.loot.LootGsons;
import net.minecraft.util.JsonHelper;
-import net.minecraft.util.Lazy;
-import net.minecraft.loot.LootManager;
+/**
+ * @deprecated Use {@link LootGsons#getTableGsonBuilder()} from vanilla instead.
+ */
+@Deprecated
public final class LootJsonParser {
- /* Reading this from LootManager to access all serializers from vanilla. */
- private static final Lazy GSON = new Lazy<>(() -> {
- try {
- Field gsonField = Stream.of(LootManager.class.getDeclaredFields())
- .filter(field -> field.getType() == Gson.class)
- .findFirst()
- .orElseThrow(() -> new RuntimeException("Gson not found in LootManager!"));
- gsonField.setAccessible(true);
- return (Gson) gsonField.get(null);
- } catch (Exception e) {
- throw new RuntimeException("Exception while getting Gson instance from LootManager", e);
- }
- });
+ private static final Gson GSON = LootGsons.getTableGsonBuilder().create();
private LootJsonParser() { }
public static T read(Reader json, Class c) {
- return JsonHelper.deserialize(GSON.get(), json, c);
+ return JsonHelper.deserialize(GSON, json, c);
}
public static T read(String json, Class c) {
- return JsonHelper.deserialize(GSON.get(), json, c);
+ return JsonHelper.deserialize(GSON, json, c);
}
}
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/event/LootTableLoadingCallback.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/event/LootTableLoadingCallback.java
index 360efd54a..764c94ada 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/event/LootTableLoadingCallback.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/api/loot/v1/event/LootTableLoadingCallback.java
@@ -28,9 +28,13 @@ import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
/**
* An event handler that is called when loot tables are loaded.
* Use {@link #EVENT} to register instances.
+ *
+ * @deprecated Replaced with {@link net.fabricmc.fabric.api.loot.v2.LootTableEvents}.
*/
+@Deprecated
@FunctionalInterface
public interface LootTableLoadingCallback {
+ @Deprecated
@FunctionalInterface
interface LootTableSetter {
void set(LootTable supplier);
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/BufferingLootTableBuilder.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/BufferingLootTableBuilder.java
new file mode 100644
index 000000000..3e6ddd659
--- /dev/null
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/BufferingLootTableBuilder.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.impl.loot.table;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.LootTable;
+import net.minecraft.loot.context.LootContextType;
+import net.minecraft.loot.function.LootFunction;
+
+import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier;
+import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder;
+import net.fabricmc.fabric.api.loot.v2.FabricLootTableBuilder;
+
+/**
+ * A {@link FabricLootSupplierBuilder} that caches all methods so they can be applied to a v2 {@link FabricLootTableBuilder}.
+ * Used for hooking {@code LootTableLoadingCallback} to the two different {@link net.fabricmc.fabric.api.loot.v2.LootTableEvents}.
+ */
+public class BufferingLootTableBuilder extends FabricLootSupplierBuilder {
+ private final List> modifications = new ArrayList<>();
+
+ private FabricLootSupplierBuilder addAction(Consumer action) {
+ modifications.add(action);
+ return this;
+ }
+
+ private FabricLootSupplierBuilder addV2Action(Consumer action) {
+ return addAction(builder -> action.accept((FabricLootTableBuilder) builder));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder pool(LootPool.Builder pool) {
+ super.pool(pool);
+ return addAction(builder -> builder.pool(pool));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder type(LootContextType type) {
+ super.type(type);
+ return addAction(builder -> builder.type(type));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder apply(LootFunction.Builder function) {
+ super.apply(function);
+ return addAction(builder -> builder.apply(function));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder withPool(LootPool pool) {
+ super.withPool(pool);
+ return addV2Action(builder -> builder.pool(pool));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder withFunction(LootFunction function) {
+ super.withFunction(function);
+ return addV2Action(builder -> builder.apply(function));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder withPools(Collection pools) {
+ super.withPools(pools);
+ return addV2Action(builder -> builder.pools(pools));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder withFunctions(Collection functions) {
+ super.withFunctions(functions);
+ return addV2Action(builder -> builder.apply(functions));
+ }
+
+ @Override
+ public FabricLootSupplierBuilder copyFrom(LootTable supplier, boolean copyType) {
+ super.copyFrom(supplier, copyType);
+ return addV2Action(builder -> {
+ FabricLootSupplier extended = (FabricLootSupplier) supplier;
+ builder.pools(extended.getPools());
+ builder.apply(extended.getFunctions());
+
+ if (copyType) {
+ ((LootTable.Builder) builder).type(supplier.getType());
+ }
+ });
+ }
+
+ public void init(LootTable original) {
+ super.type(original.getType());
+ super.withPools(((FabricLootSupplier) original).getPools());
+ super.withFunctions(((FabricLootSupplier) original).getFunctions());
+ }
+
+ public void applyTo(LootTable.Builder builder) {
+ for (Consumer modification : modifications) {
+ modification.accept(builder);
+ }
+ }
+}
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/LootEntryTypeRegistryImpl.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/LootEntryTypeRegistryImpl.java
index e4594a3d3..af5effdfa 100644
--- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/LootEntryTypeRegistryImpl.java
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/LootEntryTypeRegistryImpl.java
@@ -17,16 +17,16 @@
package net.fabricmc.fabric.impl.loot.table;
import net.minecraft.loot.entry.LootPoolEntry;
+import net.minecraft.loot.entry.LootPoolEntryType;
import net.minecraft.util.Identifier;
import net.minecraft.util.JsonSerializer;
-
-import net.fabricmc.fabric.mixin.loot.table.LootPoolEntryTypesAccessor;
+import net.minecraft.util.registry.Registry;
public final class LootEntryTypeRegistryImpl implements net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry {
public LootEntryTypeRegistryImpl() { }
@Override
public void register(Identifier id, JsonSerializer extends LootPoolEntry> serializer) {
- LootPoolEntryTypesAccessor.register(id.toString(), serializer);
+ Registry.register(Registry.LOOT_POOL_ENTRY_TYPE, id, new LootPoolEntryType(serializer));
}
}
diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/LootTablesV1Init.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/LootTablesV1Init.java
new file mode 100644
index 000000000..d8c262e2c
--- /dev/null
+++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/impl/loot/table/LootTablesV1Init.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.impl.loot.table;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.minecraft.loot.LootTable;
+import net.minecraft.util.Identifier;
+
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
+import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
+
+public final class LootTablesV1Init implements ModInitializer {
+ private static final ThreadLocal