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 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 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 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 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 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 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 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 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 pools) { + this.pools.addAll(pools); + return self(); + } + + @Override + public LootTable.Builder apply(Collection 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 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> BUFFERS = ThreadLocal.withInitial(HashMap::new); + + @Override + public void onInitialize() { + LootTableEvents.REPLACE.register((resourceManager, lootManager, id, original, source) -> { + BufferingLootTableBuilder builder = new BufferingLootTableBuilder(); + builder.init(original); + BUFFERS.get().put(id, builder); + + LootTable[] result = new LootTable[1]; + LootTableLoadingCallback.EVENT.invoker().onLootTableLoading( + resourceManager, + lootManager, + id, + builder, + table -> result[0] = table + ); + + return result[0]; + }); + + LootTableEvents.MODIFY.register((resourceManager, lootManager, id, tableBuilder, source) -> { + Map buffers = BUFFERS.get(); + + if (buffers.containsKey(id)) { + try { + buffers.get(id).applyTo(tableBuilder); + } finally { + buffers.remove(id); + + if (buffers.isEmpty()) { + BUFFERS.remove(); + } + } + } + }); + } +} diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootManager.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootManager.java deleted file mode 100644 index 16881e34e..000000000 --- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootManager.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.table; - -import java.util.HashMap; -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.resource.ResourceManager; -import net.minecraft.util.Identifier; -import net.minecraft.util.profiler.Profiler; -import net.minecraft.loot.LootManager; -import net.minecraft.loot.LootTable; - -import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder; -import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback; - -@Mixin(LootManager.class) -public class MixinLootManager { - @Shadow private Map tables; - - @Inject(method = "apply", at = @At("RETURN")) - private void apply(Map objectMap, ResourceManager manager, Profiler profiler, CallbackInfo info) { - Map newSuppliers = new HashMap<>(); - - tables.forEach((id, supplier) -> { - FabricLootSupplierBuilder builder = FabricLootSupplierBuilder.of(supplier); - - //noinspection ConstantConditions - LootTableLoadingCallback.EVENT.invoker().onLootTableLoading( - manager, (LootManager) (Object) this, id, builder, (s) -> newSuppliers.put(id, s) - ); - - newSuppliers.computeIfAbsent(id, (i) -> builder.build()); - }); - - tables = ImmutableMap.copyOf(newSuppliers); - } -} diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootPool.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootPool.java index 1d58e9d69..055336395 100644 --- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootPool.java +++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootPool.java @@ -16,19 +16,18 @@ package net.fabricmc.fabric.mixin.loot.table; -import java.util.Arrays; import java.util.List; +import com.google.common.collect.ImmutableList; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.gen.Accessor; 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; import net.fabricmc.fabric.api.loot.v1.FabricLootPool; @@ -36,32 +35,37 @@ import net.fabricmc.fabric.api.loot.v1.FabricLootPool; public abstract class MixinLootPool implements FabricLootPool { @Shadow @Final - private LootPoolEntry[] entries; + LootPoolEntry[] entries; @Shadow @Final - private LootCondition[] conditions; + LootCondition[] conditions; @Shadow @Final - private LootFunction[] functions; + LootFunction[] functions; + + @Shadow + @Final + LootNumberProvider rolls; @Override public List getEntries() { - return Arrays.asList(entries); + return ImmutableList.copyOf(entries); } @Override public List getConditions() { - return Arrays.asList(conditions); + return ImmutableList.copyOf(conditions); } @Override public List getFunctions() { - return Arrays.asList(functions); + return ImmutableList.copyOf(functions); } - @Accessor @Override - public abstract LootNumberProvider getRolls(); + public LootNumberProvider getRolls() { + return rolls; + } } diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootSupplier.java b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootSupplier.java index 584994f4e..10cb2fa90 100644 --- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootSupplier.java +++ b/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/MixinLootSupplier.java @@ -16,17 +16,15 @@ package net.fabricmc.fabric.mixin.loot.table; -import java.util.Arrays; import java.util.List; +import com.google.common.collect.ImmutableList; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.gen.Accessor; 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; @@ -35,22 +33,19 @@ import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier; public abstract class MixinLootSupplier implements FabricLootSupplier { @Shadow @Final - private LootPool[] pools; + LootPool[] pools; + @Shadow @Final - private LootFunction[] functions; + LootFunction[] functions; @Override public List getPools() { - return Arrays.asList(pools); + return ImmutableList.copyOf(pools); } @Override public List getFunctions() { - return Arrays.asList(functions); + return ImmutableList.copyOf(functions); } - - @Accessor - @Override - public abstract LootContextType getType(); } diff --git a/fabric-loot-tables-v1/src/main/resources/fabric-loot-tables-v1.mixins.json b/fabric-loot-tables-v1/src/main/resources/fabric-loot-tables-v1.mixins.json index 064f4c5bd..9b813056e 100644 --- a/fabric-loot-tables-v1/src/main/resources/fabric-loot-tables-v1.mixins.json +++ b/fabric-loot-tables-v1/src/main/resources/fabric-loot-tables-v1.mixins.json @@ -3,10 +3,6 @@ "package": "net.fabricmc.fabric.mixin.loot.table", "compatibilityLevel": "JAVA_16", "mixins": [ - "LootPoolBuilderHooks", - "LootPoolEntryTypesAccessor", - "LootSupplierBuilderHooks", - "MixinLootManager", "MixinLootPool", "MixinLootSupplier" ], diff --git a/fabric-loot-tables-v1/src/main/resources/fabric.mod.json b/fabric-loot-tables-v1/src/main/resources/fabric.mod.json index ecd00ec3c..453c93cd3 100644 --- a/fabric-loot-tables-v1/src/main/resources/fabric.mod.json +++ b/fabric-loot-tables-v1/src/main/resources/fabric.mod.json @@ -17,13 +17,17 @@ ], "depends": { "fabricloader": ">=0.4.0", - "fabric-api-base": "*" + "fabric-api-base": "*", + "fabric-loot-api-v2": "*" + }, + "entrypoints": { + "main": ["net.fabricmc.fabric.impl.loot.table.LootTablesV1Init"] }, "description": "Hooks for manipulating loot tables.", "mixins": [ "fabric-loot-tables-v1.mixins.json" ], "custom": { - "fabric-api:module-lifecycle": "stable" + "fabric-api:module-lifecycle": "deprecated" } } diff --git a/fabric-loot-tables-v1/src/testmod/java/net/fabricmc/fabric/test/loot/LootTest.java b/fabric-loot-tables-v1/src/testmod/java/net/fabricmc/fabric/test/loot/LootV1Test.java similarity index 83% rename from fabric-loot-tables-v1/src/testmod/java/net/fabricmc/fabric/test/loot/LootTest.java rename to fabric-loot-tables-v1/src/testmod/java/net/fabricmc/fabric/test/loot/LootV1Test.java index 945f43f1b..e87b06a37 100644 --- a/fabric-loot-tables-v1/src/testmod/java/net/fabricmc/fabric/test/loot/LootTest.java +++ b/fabric-loot-tables-v1/src/testmod/java/net/fabricmc/fabric/test/loot/LootV1Test.java @@ -20,28 +20,31 @@ import com.google.gson.Gson; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; -import org.slf4j.LoggerFactory; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import net.minecraft.block.Blocks; import net.minecraft.item.Items; -import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.LootGsons; import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; import net.minecraft.loot.condition.LootCondition; import net.minecraft.loot.condition.SurvivesExplosionLootCondition; import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.entry.LootPoolEntry; import net.minecraft.loot.entry.TagEntry; +import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.loot.v1.FabricLootPoolBuilder; +import net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder; import net.fabricmc.fabric.api.loot.v1.LootEntryTypeRegistry; import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback; -public class LootTest implements ModInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger(LootTest.class); +public class LootV1Test implements ModInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(LootV1Test.class); private static final Gson LOOT_GSON = LootGsons.getTableGsonBuilder().create(); private static final String LOOT_ENTRY_JSON = "{\"type\":\"minecraft:item\",\"name\":\"minecraft:apple\"}"; @@ -53,7 +56,9 @@ public class LootTest implements ModInitializer { // Test loot table load event LootTableLoadingCallback.EVENT.register((resourceManager, manager, id, supplier, setter) -> { - if ("minecraft:blocks/dirt".equals(id.toString())) { + // Add feathers and apples to dirt (only one drops at the time since they're in the same pool), + // and replace grass block drops with wheat + if (Blocks.DIRT.getLootTableId().equals(id)) { LootPoolEntry entryFromString = LOOT_GSON.fromJson(LOOT_ENTRY_JSON, LootPoolEntry.class); LootPool pool = FabricLootPoolBuilder.builder() @@ -64,6 +69,11 @@ public class LootTest implements ModInitializer { .build(); supplier.withPool(pool); + } else if (Blocks.GRASS_BLOCK.getLootTableId().equals(id)) { + LootTable table = FabricLootSupplierBuilder.builder() + .pool(FabricLootPoolBuilder.builder().with(ItemEntry.builder(Items.WHEAT))) + .build(); + setter.set(table); } }); } diff --git a/fabric-loot-tables-v1/src/testmod/resources/fabric.mod.json b/fabric-loot-tables-v1/src/testmod/resources/fabric.mod.json index 46cf8eabc..4a2f37cfb 100644 --- a/fabric-loot-tables-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-loot-tables-v1/src/testmod/resources/fabric.mod.json @@ -10,7 +10,7 @@ }, "entrypoints": { "main": [ - "net.fabricmc.fabric.test.loot.LootTest" + "net.fabricmc.fabric.test.loot.LootV1Test" ] } } diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/FabricResource.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/FabricResource.java new file mode 100644 index 000000000..a937b11b9 --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/FabricResource.java @@ -0,0 +1,41 @@ +/* + * 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.resource.loader; + +import org.slf4j.LoggerFactory; + +import net.minecraft.resource.ResourcePackSource; + +/** + * Extensions to {@link net.minecraft.resource.Resource}. + * Automatically implemented there via a mixin. + * Currently, this is only for use in other Fabric API modules. + */ +public interface FabricResource { + /** + * Gets the resource pack source of this resource. + * The source is used to separate vanilla/mod resources from user resources in Fabric API. + * + *

Custom {@link net.minecraft.resource.Resource} implementations should override this method. + * + * @return the resource pack source + */ + default ResourcePackSource getFabricPackSource() { + LoggerFactory.getLogger(FabricResource.class).error("Unknown Resource implementation {}, returning PACK_SOURCE_NONE as the source", getClass().getName()); + return ResourcePackSource.PACK_SOURCE_NONE; + } +} diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/FabricResourceImpl.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/FabricResourceImpl.java new file mode 100644 index 000000000..4efb25e1c --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/FabricResourceImpl.java @@ -0,0 +1,29 @@ +/* + * 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.resource.loader; + +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourcePackSource; + +/** + * An extended version of {@link FabricResource} that supports + * setting the pack resource. Only for use from within this module. + * Note that not all resources are instances of this interface. + */ +public interface FabricResourceImpl extends Resource, FabricResource { + void setFabricPackSource(ResourcePackSource packSource); +} diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/GroupResourcePack.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/GroupResourcePack.java index bfb449b2c..877936f91 100644 --- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/GroupResourcePack.java +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/GroupResourcePack.java @@ -130,7 +130,9 @@ public abstract class GroupResourcePack implements ResourcePack { for (ModResourcePack pack : packs) { if (pack.contains(manager.getType(), id)) { InputStream metadataInputStream = pack.contains(manager.getType(), metadataId) ? manager.fabric$accessor_open(metadataId, pack) : null; - resources.add(new ResourceImpl(pack.getName(), id, manager.fabric$accessor_open(id, pack), metadataInputStream)); + ResourceImpl resource = new ResourceImpl(pack.getName(), id, manager.fabric$accessor_open(id, pack), metadataInputStream); + ((FabricResourceImpl) resource).setFabricPackSource(ModResourcePackCreator.RESOURCE_PACK_SOURCE); + resources.add(resource); } } } diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ResourcePackSourceTracker.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ResourcePackSourceTracker.java new file mode 100644 index 000000000..87132ac74 --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ResourcePackSourceTracker.java @@ -0,0 +1,56 @@ +/* + * 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.resource.loader; + +import java.util.WeakHashMap; + +import net.minecraft.resource.ResourcePack; +import net.minecraft.resource.ResourcePackSource; + +/** + * Tracks the sources of resource packs in a global weak hash map. + * {@link ResourcePack} doesn't hold a reference to its {@link ResourcePackSource} + * so we store the source in the map when the resource packs are created. + * See {@link net.fabricmc.fabric.mixin.resource.loader.ResourcePackProfileMixin ResourcePackProfileMixin}. + * + *

The sources are later read for use in {@link FabricResource} and {@link FabricResourceImpl}. + * See {@link net.fabricmc.fabric.mixin.resource.loader.NamespaceResourceManagerMixin NamespaceResourceManagerMixin}. + */ +public final class ResourcePackSourceTracker { + // Use a weak hash map so that if resource packs would be deleted, this won't keep them alive. + private static final WeakHashMap SOURCES = new WeakHashMap<>(); + + /** + * Gets the source of a pack. + * + * @param pack the resource pack + * @return the source, or {@link ResourcePackSource#PACK_SOURCE_NONE} if not tracked + */ + public static ResourcePackSource getSource(ResourcePack pack) { + return SOURCES.getOrDefault(pack, ResourcePackSource.PACK_SOURCE_NONE); + } + + /** + * Sets the source of a pack. + * + * @param pack the resource pack + * @param source the source + */ + public static void setSource(ResourcePack pack, ResourcePackSource source) { + SOURCES.put(pack, source); + } +} diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/DefaultResourcePackResourceMixin.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/DefaultResourcePackResourceMixin.java new file mode 100644 index 000000000..5f0f12d30 --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/DefaultResourcePackResourceMixin.java @@ -0,0 +1,36 @@ +/* + * 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.resource.loader; + +import org.spongepowered.asm.mixin.Mixin; + +import net.minecraft.resource.ResourcePackSource; + +import net.fabricmc.fabric.impl.resource.loader.FabricResource; + +/** + * Implements {@link FabricResource} for the anonymous resource implementation + * in {@link net.minecraft.resource.DefaultResourcePack#getResource}. + */ +@Mixin(targets = "net/minecraft/resource/DefaultResourcePack$1") +abstract class DefaultResourcePackResourceMixin implements FabricResource { + @Override + public ResourcePackSource getFabricPackSource() { + // The default resource pack only contains built-in vanilla resources. + return ResourcePackSource.PACK_SOURCE_BUILTIN; + } +} diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/NamespaceResourceManagerMixin.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/NamespaceResourceManagerMixin.java index 3daf64904..12ec3714d 100644 --- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/NamespaceResourceManagerMixin.java +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/NamespaceResourceManagerMixin.java @@ -17,6 +17,7 @@ package net.fabricmc.fabric.mixin.resource.loader; import java.io.IOException; +import java.util.Iterator; import java.util.List; import org.spongepowered.asm.mixin.Mixin; @@ -32,7 +33,9 @@ import net.minecraft.resource.ResourcePack; import net.minecraft.resource.ResourceType; import net.minecraft.util.Identifier; +import net.fabricmc.fabric.impl.resource.loader.FabricResourceImpl; import net.fabricmc.fabric.impl.resource.loader.GroupResourcePack; +import net.fabricmc.fabric.impl.resource.loader.ResourcePackSourceTracker; @Mixin(NamespaceResourceManager.class) public class NamespaceResourceManagerMixin { @@ -66,4 +69,32 @@ public class NamespaceResourceManagerMixin { return pack.contains(type, id); } + + /* The two injectors below set the resource pack sources (see FabricResourceImpl) + * for resources created in NamespaceResourceManager.getAllResources and NamespaceResourceManager.getResource. + * + * Since (in 1.18.2) ResourceImpl doesn't hold a reference to its resource pack, + * we have to get the source from the resource pack when the resource is created. + * These are the main creation sites for resources in 1.18.2 + * along with DefaultResourcePack.getResource and Fabric API's GroupResourcePack, + * which also either track the source similarly or provide other types of Resource instances + * that have a different FabricResource implementation. + */ + + @Inject(method = "getAllResources", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", remap = false, shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD) + private void trackSourceOnGetAllResources(Identifier id, CallbackInfoReturnable> cir, List resources, Identifier metadataPath, Iterator packs, ResourcePack resourcePack) { + // After the created resource has been added, read it from the list and set its source + // to match the tracked source of its resource pack. + if (resources.get(resources.size() - 1) instanceof FabricResourceImpl resource) { + resource.setFabricPackSource(ResourcePackSourceTracker.getSource(resourcePack)); + } + } + + @Inject(method = "getResource", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILHARD) + private void trackSourceOnGetResource(Identifier id, CallbackInfoReturnable cir, ResourcePack metaResourcePack, Identifier metadataPath, int i, ResourcePack resourcePack) { + // Set the resource's source to match the tracked source of its resource pack. + if (cir.getReturnValue() instanceof FabricResourceImpl resource) { + resource.setFabricPackSource(ResourcePackSourceTracker.getSource(resourcePack)); + } + } } diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourceImplMixin.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourceImplMixin.java new file mode 100644 index 000000000..dd0d039db --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourceImplMixin.java @@ -0,0 +1,50 @@ +/* + * 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.resource.loader; + +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +import net.minecraft.resource.ResourceImpl; +import net.minecraft.resource.ResourcePackSource; + +import net.fabricmc.fabric.impl.resource.loader.FabricResourceImpl; + +/** + * Implements {@link FabricResourceImpl} (resource source getter/setter) + * for vanilla's basic {@link ResourceImpl} used for most game resources. + * + * @see NamespaceResourceManagerMixin the usage site for this mixin + */ +@Mixin(ResourceImpl.class) +abstract class ResourceImplMixin implements FabricResourceImpl { + @Unique + private @Nullable ResourcePackSource fabric_packSource; + + @Override + public ResourcePackSource getFabricPackSource() { + return Objects.requireNonNullElse(fabric_packSource, ResourcePackSource.PACK_SOURCE_NONE); + } + + @Override + public void setFabricPackSource(ResourcePackSource packSource) { + this.fabric_packSource = packSource; + } +} diff --git a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootPoolEntryTypesAccessor.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourceMixin.java similarity index 54% rename from fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootPoolEntryTypesAccessor.java rename to fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourceMixin.java index 9264ca0a9..3b929a5fc 100644 --- a/fabric-loot-tables-v1/src/main/java/net/fabricmc/fabric/mixin/loot/table/LootPoolEntryTypesAccessor.java +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourceMixin.java @@ -14,20 +14,15 @@ * limitations under the License. */ -package net.fabricmc.fabric.mixin.loot.table; +package net.fabricmc.fabric.mixin.resource.loader; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; -import net.minecraft.loot.entry.LootPoolEntry; -import net.minecraft.loot.entry.LootPoolEntryType; -import net.minecraft.loot.entry.LootPoolEntryTypes; -import net.minecraft.util.JsonSerializer; +import net.minecraft.resource.Resource; -@Mixin(LootPoolEntryTypes.class) -public interface LootPoolEntryTypesAccessor { - @Invoker("register") - static LootPoolEntryType register(String id, JsonSerializer serializer) { - throw new UnsupportedOperationException("Mixin dummy"); - } +import net.fabricmc.fabric.impl.resource.loader.FabricResource; + +// Add FabricResource to Resource's superinterfaces. +@Mixin(Resource.class) +interface ResourceMixin extends FabricResource { } diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourcePackProfileMixin.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourcePackProfileMixin.java new file mode 100644 index 000000000..05f8c413a --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/ResourcePackProfileMixin.java @@ -0,0 +1,49 @@ +/* + * 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.resource.loader; + +import org.spongepowered.asm.mixin.Final; +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.CallbackInfoReturnable; + +import net.minecraft.resource.ResourcePack; +import net.minecraft.resource.ResourcePackProfile; +import net.minecraft.resource.ResourcePackSource; + +import net.fabricmc.fabric.impl.resource.loader.ResourcePackSourceTracker; + +/** + * Implements resource pack source tracking (for {@link net.fabricmc.fabric.impl.resource.loader.FabricResource}). + * {@link ResourcePack} doesn't hold a reference to its {@link ResourcePackSource} + * so we store the source in a global tracker when the resource packs are created. + * + * @see ResourcePackSourceTracker + */ +@Mixin(ResourcePackProfile.class) +abstract class ResourcePackProfileMixin { + @Shadow + @Final + private ResourcePackSource source; + + @Inject(method = "createResourcePack", at = @At("RETURN")) + private void onCreateResourcePack(CallbackInfoReturnable info) { + ResourcePackSourceTracker.setSource(info.getReturnValue(), source); + } +} diff --git a/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json b/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json index feff26275..fef2a712b 100644 --- a/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json +++ b/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json @@ -5,14 +5,18 @@ "mixins": [ "FileResourcePackProviderAccessor", "DefaultResourcePackMixin", + "DefaultResourcePackResourceMixin", "KeyedResourceReloadListenerMixin", "LifecycledResourceManagerImplMixin", "MinecraftServerMixin", "NamespaceResourceManagerAccessor", "NamespaceResourceManagerMixin", "ReloadableResourceManagerImplMixin", + "ResourceImplMixin", + "ResourceMixin", "ResourcePackManagerMixin", "ResourcePackManagerAccessor", + "ResourcePackProfileMixin", "SimpleResourceReloadMixin" ], "client": [ diff --git a/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener b/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener index 5b0188121..16c277af4 100644 --- a/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener +++ b/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener @@ -15,6 +15,15 @@ transitive-accessible method net/minecraft/screen/ScreenHandlerType (Lnet transitive-accessible class net/minecraft/client/gui/screen/ingame/HandledScreens$Provider transitive-accessible method net/minecraft/client/gui/screen/ingame/HandledScreens register (Lnet/minecraft/screen/ScreenHandlerType;Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;)V +# Data contained in loot tables and pools +transitive-accessible field net/minecraft/loot/LootPool entries [Lnet/minecraft/loot/entry/LootPoolEntry; +transitive-accessible field net/minecraft/loot/LootPool conditions [Lnet/minecraft/loot/condition/LootCondition; +transitive-accessible field net/minecraft/loot/LootPool functions [Lnet/minecraft/loot/function/LootFunction; +transitive-accessible field net/minecraft/loot/LootPool rolls Lnet/minecraft/loot/provider/number/LootNumberProvider; +transitive-accessible field net/minecraft/loot/LootPool bonusRolls Lnet/minecraft/loot/provider/number/LootNumberProvider; +transitive-accessible field net/minecraft/loot/LootTable pools [Lnet/minecraft/loot/LootPool; +transitive-accessible field net/minecraft/loot/LootTable functions [Lnet/minecraft/loot/function/LootFunction; + ### Generated access wideners below # Constructors of non-abstract block classes transitive-accessible method net/minecraft/block/AirBlock (Lnet/minecraft/block/AbstractBlock$Settings;)V diff --git a/fabric-transitive-access-wideners-v1/template.accesswidener b/fabric-transitive-access-wideners-v1/template.accesswidener index 257dcd183..afb4b6371 100644 --- a/fabric-transitive-access-wideners-v1/template.accesswidener +++ b/fabric-transitive-access-wideners-v1/template.accesswidener @@ -15,4 +15,13 @@ transitive-accessible method net/minecraft/screen/ScreenHandlerType (Lnet transitive-accessible class net/minecraft/client/gui/screen/ingame/HandledScreens$Provider transitive-accessible method net/minecraft/client/gui/screen/ingame/HandledScreens register (Lnet/minecraft/screen/ScreenHandlerType;Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;)V +# Data contained in loot tables and pools +transitive-accessible field net/minecraft/loot/LootPool entries [Lnet/minecraft/loot/entry/LootPoolEntry; +transitive-accessible field net/minecraft/loot/LootPool conditions [Lnet/minecraft/loot/condition/LootCondition; +transitive-accessible field net/minecraft/loot/LootPool functions [Lnet/minecraft/loot/function/LootFunction; +transitive-accessible field net/minecraft/loot/LootPool rolls Lnet/minecraft/loot/provider/number/LootNumberProvider; +transitive-accessible field net/minecraft/loot/LootPool bonusRolls Lnet/minecraft/loot/provider/number/LootNumberProvider; +transitive-accessible field net/minecraft/loot/LootTable pools [Lnet/minecraft/loot/LootPool; +transitive-accessible field net/minecraft/loot/LootTable functions [Lnet/minecraft/loot/function/LootFunction; + ### Generated access wideners below diff --git a/gradle.properties b/gradle.properties index fe26237df..71b7bfc02 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,6 +29,7 @@ fabric-item-groups-v0-version=0.3.12 fabric-key-binding-api-v1-version=1.0.12 fabric-keybindings-v0-version=0.2.10 fabric-lifecycle-events-v1-version=2.0.4 +fabric-loot-api-v2-version=1.0.0 fabric-loot-tables-v1-version=1.0.11 fabric-mining-level-api-v1-version=2.1.1 fabric-models-v0-version=0.3.6 diff --git a/settings.gradle b/settings.gradle index 6028689eb..4c5513d80 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,6 +33,7 @@ include 'fabric-item-groups-v0' include 'fabric-keybindings-v0' include 'fabric-key-binding-api-v1' include 'fabric-lifecycle-events-v1' +include 'fabric-loot-api-v2' include 'fabric-loot-tables-v1' include 'fabric-mining-level-api-v1' include 'fabric-models-v0'