Loot table API v2

- Deprecates all classes and methods that use outdated Yarn names.
- Adds FabricLootTable and FabricLootTableBuilder to replace
  the LootSupplier naming variants.
- Deprecates LootEntryTypeRegistry and LootJsonParser
  as their functionality is exposed in vanilla now.
- Adds methods to FabricLootPoolBuilder for working
  with collections as builder parameters.
- FabricLootPool and FabricLootTable/Supplier now return immutable lists
  instead of modifiable fixed-size ones.

  Co-authored-by: i509VCB <i509vcb@gmail.com>
This commit is contained in:
Juuxel
2022-05-31 12:12:10 +01:00
committed by modmuss50
parent 14b3ae007b
commit 9e7660c677
54 changed files with 1573 additions and 184 deletions

View File

@@ -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.

View File

@@ -0,0 +1,7 @@
archivesBaseName = "fabric-loot-api-v2"
version = getSubprojectVersion(project)
moduleDependencies(project, [
'fabric-api-base',
'fabric-resource-loader-v0'
])

View File

@@ -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.
*
* <p>This interface is automatically injected to {@link LootPool.Builder}.
*/
@ApiStatus.NonExtendable
public interface FabricLootPoolBuilder {
/**
* Adds an entry to this builder.
*
* @param entry the added loot entry
* @return this builder
*/
default LootPool.Builder with(LootPoolEntry entry) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds entries to this builder.
*
* @param entries the added loot entries
* @return this builder
*/
default LootPool.Builder with(Collection<? extends LootPoolEntry> entries) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds a condition to this builder.
*
* @param condition the added condition
* @return this builder
*/
default LootPool.Builder conditionally(LootCondition condition) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds conditions to this builder.
*
* @param conditions the added conditions
* @return this builder
*/
default LootPool.Builder conditionally(Collection<? extends LootCondition> conditions) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies a function to this builder.
*
* @param function the applied loot function
* @return this builder
*/
default LootPool.Builder apply(LootFunction function) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies loot functions to this builder.
*
* @param functions the applied loot functions
* @return this builder
*/
default LootPool.Builder apply(Collection<? extends LootFunction> functions) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Creates a builder copy of a loot pool.
*
* @param pool the loot pool
* @return the copied builder
*/
static LootPool.Builder copyOf(LootPool pool) {
LootPoolAccessor accessor = (LootPoolAccessor) pool;
return LootPool.builder()
.rolls(accessor.fabric_getRolls())
.bonusRolls(accessor.fabric_getBonusRolls())
.with(List.of(accessor.fabric_getEntries()))
.conditionally(List.of(accessor.fabric_getConditions()))
.apply(List.of(accessor.fabric_getFunctions()));
}
}

View File

@@ -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.
*
* <p>This interface is automatically injected to {@link LootTable.Builder}.
*/
@ApiStatus.NonExtendable
public interface FabricLootTableBuilder {
/**
* Adds a loot pool to this builder.
*
* @param pool the added pool
* @return this builder
*/
default LootTable.Builder pool(LootPool pool) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies a loot function to this builder.
*
* @param function the applied function
* @return this builder
*/
default LootTable.Builder apply(LootFunction function) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Adds loot pools to this builder.
*
* @param pools the added pools
* @return this builder
*/
default LootTable.Builder pools(Collection<? extends LootPool> pools) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Applies loot functions to this builder.
*
* @param functions the applied functions
* @return this builder
*/
default LootTable.Builder apply(Collection<? extends LootFunction> functions) {
throw new UnsupportedOperationException("Implemented via mixin");
}
/**
* Creates a builder copy of a loot table.
*
* @param table the loot table
* @return the copied builder
*/
static LootTable.Builder copyOf(LootTable table) {
LootTable.Builder builder = LootTable.builder();
LootTableAccessor accessor = (LootTableAccessor) table;
builder.type(table.getType());
builder.pools(List.of(accessor.fabric_getPools()));
builder.apply(List.of(accessor.fabric_getFunctions()));
return builder;
}
}

View File

@@ -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> 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).
*
* <p>You can also modify loot tables that are created by {@link #REPLACE}.
* They have the loot table source {@link LootTableSource#REPLACED}.
*
* <h2>Example: adding diamonds to the cobblestone loot table</h2>
* <pre>
* {@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);
* }
* });
* }
* </pre>
*/
public static final Event<Modify> 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);
}
}

View File

@@ -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.
*
* <p>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.
*
* <p>{@link #VANILLA} and {@link #MOD} are builtin.
*
* @return {@code true} if builtin, {@code false} otherwise
*/
public boolean isBuiltin() {
return builtin;
}
}

View File

@@ -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.
*
* <h2>Events</h2>
* {@link net.fabricmc.fabric.api.loot.v2.LootTableEvents} has events to modify existing loot tables,
* or outright replace them with a new loot table.
*
* <p>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.
*
* <h2>Extended loot table and pool builders</h2>
* 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;

View File

@@ -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.impl.loot;
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 LootTableSource determineSource(Identifier lootTableId, ResourceManager resourceManager) {
Identifier resourceId = new Identifier(lootTableId.getNamespace(), "loot_tables/%s.json".formatted(lootTableId.getPath()));
Resource resource = resourceManager.getResource(resourceId).orElse(null);
if (resource != null) {
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;
}
}
// 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;
}
}

View File

@@ -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<Identifier, LootTable> tables;
@Inject(method = "apply", at = @At("RETURN"))
private void apply(Map<Identifier, JsonObject> 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<Identifier, LootTable> 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();
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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 org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
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;
/**
* 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();
}

View File

@@ -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<LootPoolEntry> entries;
@Shadow
@Final
private List<LootCondition> conditions;
@Shadow
@Final
private List<LootFunction> functions;
@Unique
private LootPool.Builder self() {
// noinspection ConstantConditions
return (LootPool.Builder) (Object) this;
}
@Override
public LootPool.Builder with(LootPoolEntry entry) {
this.entries.add(entry);
return self();
}
@Override
public LootPool.Builder with(Collection<? extends LootPoolEntry> entries) {
this.entries.addAll(entries);
return self();
}
@Override
public LootPool.Builder conditionally(LootCondition condition) {
this.conditions.add(condition);
return self();
}
@Override
public LootPool.Builder conditionally(Collection<? extends LootCondition> conditions) {
this.conditions.addAll(conditions);
return self();
}
@Override
public LootPool.Builder apply(LootFunction function) {
this.functions.add(function);
return self();
}
@Override
public LootPool.Builder apply(Collection<? extends LootFunction> functions) {
this.functions.addAll(functions);
return self();
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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 org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.function.LootFunction;
/**
* 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();
}

View File

@@ -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<LootPool> pools;
@Shadow
@Final
private List<LootFunction> functions;
@Unique
private LootTable.Builder self() {
// noinspection ConstantConditions
return (LootTable.Builder) (Object) this;
}
@Override
public LootTable.Builder pool(LootPool pool) {
this.pools.add(pool);
return self();
}
@Override
public LootTable.Builder apply(LootFunction function) {
this.functions.add(function);
return self();
}
@Override
public LootTable.Builder pools(Collection<? extends LootPool> pools) {
this.pools.addAll(pools);
return self();
}
@Override
public LootTable.Builder apply(Collection<? extends LootFunction> functions) {
this.functions.addAll(functions);
return self();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,15 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.loot",
"compatibilityLevel": "JAVA_8",
"mixins": [
"LootManagerMixin",
"LootPoolAccessor",
"LootPoolBuilderMixin",
"LootTableAccessor",
"LootTableBuilderMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -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"]
}
}
}

View File

@@ -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.Text;
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(Text.literal("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");
}
});
}
}

View File

@@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:diamond"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View File

@@ -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"
]
}
}