Fully port to 24w12a ()

* Bump yarn

* Apply yarn update

* Sync map decorations type registry

* Remove Mining Level API

* Port loot API

* whoops

* Rename LootManagerMixin

* Fix javadoc

* Fix registerGiftLootTable

* RegistryKey is traditionally compared using identity

* Fix wrong loot source being used
This commit is contained in:
apple502j 2024-03-28 01:47:29 +09:00 committed by GitHub
parent e9d2a72b4f
commit 9b3069f652
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 199 additions and 786 deletions
fabric-content-registries-v0/src
main/java/net/fabricmc/fabric
testmod/java/net/fabricmc/fabric/test/content/registry
fabric-data-generation-api-v1/src
main/java/net/fabricmc/fabric
testmod/java/net/fabricmc/fabric/test/datagen
fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item
fabric-lifecycle-events-v1/src/main/java/net/fabricmc/fabric/mixin/event/lifecycle
fabric-loot-api-v2/src
main
testmod/java/net/fabricmc/fabric/test/loot
fabric-mining-level-api-v1
build.gradle
src
main
testmod
java/net/fabricmc/fabric/test/mininglevel
resources
fabric-object-builder-api-v1/src/client/java/net/fabricmc/fabric/api/object/builder/v1/client/model
fabric-registry-sync-v0/src/main/java/net/fabricmc/fabric/impl/registry/sync
gradle.propertiessettings.gradle

View file

@ -27,6 +27,9 @@ import org.slf4j.LoggerFactory;
import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemConvertible;
import net.minecraft.loot.LootTable;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import net.minecraft.village.VillagerProfession;
@ -79,15 +82,23 @@ public final class VillagerInteractionRegistries {
}
}
/**
* @deprecated Use {@link #registerGiftLootTable(VillagerProfession, RegistryKey)} instead.
*/
@Deprecated
public static void registerGiftLootTable(VillagerProfession profession, Identifier lootTable) {
registerGiftLootTable(profession, RegistryKey.of(RegistryKeys.LOOT_TABLE, lootTable));
}
/**
* Registers a hero of the village gifts loot table to a profession.
* @param profession the profession to modify
* @param lootTable the loot table to associate with the profession
*/
public static void registerGiftLootTable(VillagerProfession profession, Identifier lootTable) {
public static void registerGiftLootTable(VillagerProfession profession, RegistryKey<LootTable> lootTable) {
Objects.requireNonNull(profession, "Profession cannot be null!");
Objects.requireNonNull(lootTable, "Loot table identifier cannot be null!");
Identifier oldValue = GiveGiftsToHeroTaskAccessor.fabric_getGifts().put(profession, lootTable);
RegistryKey<LootTable> oldValue = GiveGiftsToHeroTaskAccessor.fabric_getGifts().put(profession, lootTable);
if (oldValue != null) {
LOGGER.info("Overriding previous gift loot table of {} profession, was: {}, now: {}", profession.id(), oldValue, lootTable);

View file

@ -22,13 +22,14 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.entity.ai.brain.task.GiveGiftsToHeroTask;
import net.minecraft.util.Identifier;
import net.minecraft.loot.LootTable;
import net.minecraft.registry.RegistryKey;
import net.minecraft.village.VillagerProfession;
@Mixin(GiveGiftsToHeroTask.class)
public interface GiveGiftsToHeroTaskAccessor {
@Accessor("GIFTS")
static Map<VillagerProfession, Identifier> fabric_getGifts() {
static Map<VillagerProfession, RegistryKey<LootTable>> fabric_getGifts() {
throw new AssertionError("Untransformed @Accessor");
}
}

View file

@ -34,6 +34,8 @@ import net.minecraft.potion.Potions;
import net.minecraft.recipe.Ingredient;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.registry.tag.ItemTags;
@ -137,7 +139,7 @@ public final class ContentRegistryTest implements ModInitializer {
VillagerInteractionRegistries.registerCollectable(Items.OAK_SAPLING);
VillagerInteractionRegistries.registerGiftLootTable(VillagerProfession.NITWIT, new Identifier("fake_loot_table"));
VillagerInteractionRegistries.registerGiftLootTable(VillagerProfession.NITWIT, RegistryKey.of(RegistryKeys.LOOT_TABLE, new Identifier("fake_loot_table")));
Registry.register(Registries.BLOCK, TEST_EVENT_ID, new TestEventBlock(AbstractBlock.Settings.copy(Blocks.STONE)));
SculkSensorFrequencyRegistry.register(TEST_EVENT.registryKey(), 2);

View file

@ -79,7 +79,7 @@ public abstract class FabricBlockLootTableProvider extends BlockLootTableGenerat
for (Map.Entry<RegistryKey<LootTable>, LootTable.Builder> entry : lootTables.entrySet()) {
RegistryKey<LootTable> registryKey = entry.getKey();
if (registryKey.equals(LootTables.EMPTY)) {
if (registryKey == LootTables.EMPTY) {
continue;
}

View file

@ -72,7 +72,7 @@ public final class FabricLootTableProviderImpl {
final List<CompletableFuture<?>> futures = new ArrayList<>();
for (Map.Entry<Identifier, LootTable> entry : builders.entrySet()) {
JsonObject tableJson = (JsonObject) Util.getResult(LootTable.field_50021.encodeStart(ops, entry.getValue()), IllegalStateException::new);
JsonObject tableJson = (JsonObject) Util.getResult(LootTable.CODEC.encodeStart(ops, entry.getValue()), IllegalStateException::new);
ConditionJsonProvider.write(tableJson, conditionMap.remove(entry.getKey()));
futures.add(DataProvider.writeToPath(writer, tableJson, getOutputPath(fabricDataOutput, entry.getKey())));
}

View file

@ -435,7 +435,7 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
private static class TestPredicateProvider extends FabricCodecDataProvider<LootCondition> {
private TestPredicateProvider(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, "predicates", LootConditionTypes.field_50031);
super(dataOutput, registriesFuture, DataOutput.OutputType.DATA_PACK, "predicates", LootConditionTypes.CODEC);
}
@Override

View file

@ -76,7 +76,7 @@ public class UpdatingItem extends Item {
}
@Override
public float method_58404(ItemStack stack, BlockState state) {
return isEnabled(stack) ? 20 : super.method_58404(stack, state);
public float getMiningSpeed(ItemStack stack, BlockState state) {
return isEnabled(stack) ? 20 : super.getMiningSpeed(stack, state);
}
}

View file

@ -23,7 +23,7 @@ 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.class_9383;
import net.minecraft.registry.ReloadableRegistries;
import net.minecraft.server.DataPackContents;
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
@ -32,10 +32,10 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
public class DataPackContentsMixin {
@Shadow
@Final
private class_9383.class_9385 field_49921;
private ReloadableRegistries.Lookup reloadableRegistries;
@Inject(method = "refresh", at = @At("TAIL"))
private void hookRefresh(CallbackInfo ci) {
CommonLifecycleEvents.TAGS_LOADED.invoker().onTagsLoaded(field_49921.method_58289(), false);
CommonLifecycleEvents.TAGS_LOADED.invoker().onTagsLoaded(this.reloadableRegistries.getRegistryManager(), false);
}
}

View file

@ -18,10 +18,10 @@ 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.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@ -37,9 +37,9 @@ 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) -> {
public static final Event<Replace> REPLACE = EventFactory.createArrayBacked(Replace.class, listeners -> (key, original, source) -> {
for (Replace listener : listeners) {
@Nullable LootTable replaced = listener.replaceLootTable(resourceManager, lootManager, id, original, source);
@Nullable LootTable replaced = listener.replaceLootTable(key, original, source);
if (replaced != null) {
return replaced;
@ -66,9 +66,9 @@ public final class LootTableEvents {
*
* <pre>
* {@code
* LootTableEvents.MODIFY.register((resourceManager, lootManager, id, tableBuilder, source) -> {
* LootTableEvents.MODIFY.register((key, 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()) {
* if (Blocks.COBBLESTONE.getLootTableId() == key && source.isBuiltin()) {
* // Create a new loot pool that will hold the diamonds.
* LootPool.Builder pool = LootPool.builder()
* // Add diamonds...
@ -83,9 +83,9 @@ public final class LootTableEvents {
* }
* </pre>
*/
public static final Event<Modify> MODIFY = EventFactory.createArrayBacked(Modify.class, listeners -> (resourceManager, lootManager, id, tableBuilder, source) -> {
public static final Event<Modify> MODIFY = EventFactory.createArrayBacked(Modify.class, listeners -> (key, tableBuilder, source) -> {
for (Modify listener : listeners) {
listener.modifyLootTable(resourceManager, lootManager, id, tableBuilder, source);
listener.modifyLootTable(key, tableBuilder, source);
}
});
@ -102,28 +102,24 @@ public final class LootTableEvents {
/**
* Replaces loot tables.
*
* @param resourceManager the server resource manager
* @param lootManager the loot manager
* @param id the loot table ID
* @param key the loot table key
* @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);
LootTable replaceLootTable(RegistryKey<LootTable> key, 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 key the loot table key
* @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);
void modifyLootTable(RegistryKey<LootTable> key, LootTable.Builder tableBuilder, LootTableSource source);
}
public interface Loaded {
@ -131,8 +127,8 @@ public final class LootTableEvents {
* Called when all loot tables have been loaded and {@link LootTableEvents#REPLACE} and {@link LootTableEvents#MODIFY} have been invoked.
*
* @param resourceManager the server resource manager
* @param lootManager the loot manager
* @param lootRegistry the loot registry
*/
void onLootTablesLoaded(ResourceManager resourceManager, LootManager lootManager);
void onLootTablesLoaded(ResourceManager resourceManager, Registry<LootTable> lootRegistry);
}
}

View file

@ -16,8 +16,10 @@
package net.fabricmc.fabric.impl.loot;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourcePackSource;
import net.minecraft.util.Identifier;
@ -27,11 +29,9 @@ 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);
public static final ThreadLocal<Map<Identifier, LootTableSource>> SOURCES = ThreadLocal.withInitial(HashMap::new);
public static LootTableSource determineSource(Resource resource) {
if (resource != null) {
ResourcePackSource packSource = ((FabricResource) resource).getFabricPackSource();

View file

@ -0,0 +1,45 @@
/*
* 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.gson.Gson;
import com.google.gson.JsonElement;
import com.llamalad7.mixinextras.sugar.Local;
import org.spongepowered.asm.mixin.Mixin;
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.LootDataType;
import net.minecraft.resource.JsonDataLoader;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.impl.loot.LootUtil;
@Mixin(JsonDataLoader.class)
public class JsonDataLoaderMixin {
@Inject(method = "load", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/resource/ResourceFinder;toResourceId(Lnet/minecraft/util/Identifier;)Lnet/minecraft/util/Identifier;", shift = At.Shift.AFTER))
private static void fillSourceMap(ResourceManager manager, String dataType, Gson gson, Map<Identifier, JsonElement> results, CallbackInfo ci, @Local Map.Entry<Identifier, Resource> entry, @Local(ordinal = 1) Identifier id) {
if (!LootDataType.LOOT_TABLES.directory().equals(dataType)) return;
LootUtil.SOURCES.get().put(id, LootUtil.determineSource(entry.getValue()));
}
}

View file

@ -1,100 +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;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import com.google.common.collect.ImmutableMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
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.loot.LootDataKey;
import net.minecraft.loot.LootManager;
import net.minecraft.loot.LootTable;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceReloader;
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<LootDataKey<?>, ?> keyToValue;
@Inject(method = "reload", at = @At("RETURN"), cancellable = true)
private void reload(ResourceReloader.Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor, CallbackInfoReturnable<CompletableFuture<Void>> cir) {
//noinspection DataFlowIssue
LootManager lootManager = (LootManager) (Object) this;
cir.setReturnValue(cir.getReturnValue().thenRun(() -> applyLootTableEvents(manager, lootManager)));
}
@Unique
private void applyLootTableEvents(ResourceManager resourceManager, LootManager lootManager) {
// The builder for the new LootManager.tables map with modified loot tables.
// We're using an immutable map to match vanilla.
ImmutableMap.Builder<LootDataKey<?>, Object> newTables = ImmutableMap.builder();
this.keyToValue.forEach((dataKey, entry) -> {
if (dataKey == LootManager.EMPTY_LOOT_TABLE) {
// This is a special table and cannot be modified.
// Vanilla also warns about that.
newTables.put(dataKey, entry);
return;
}
if (!(entry instanceof LootTable table)) {
// We only want to modify loot tables
newTables.put(dataKey, entry);
return;
}
LootTableSource source = LootUtil.determineSource(dataKey.id(), resourceManager);
// Invoke the REPLACE event for the current loot table.
LootTable replacement = LootTableEvents.REPLACE.invoker().replaceLootTable(resourceManager, lootManager, dataKey.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, dataKey.id(), builder, source);
// Turn the builder back into a loot table and store it in the new table.
newTables.put(dataKey, builder.build());
});
this.keyToValue = newTables.build();
LootTableEvents.ALL_LOADED.invoker().onLootTablesLoaded(resourceManager, lootManager);
}
}

View file

@ -0,0 +1,84 @@
/*
* 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 com.llamalad7.mixinextras.sugar.Local;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.loot.LootDataType;
import net.minecraft.loot.LootTable;
import net.minecraft.registry.MutableRegistry;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryOps;
import net.minecraft.registry.ReloadableRegistries;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
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(ReloadableRegistries.class)
abstract class ReloadableRegistriesMixin {
@ModifyArg(method = "method_58286", at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/MutableRegistry;add(Lnet/minecraft/registry/RegistryKey;Ljava/lang/Object;Lnet/minecraft/registry/entry/RegistryEntryInfo;)Lnet/minecraft/registry/entry/RegistryEntry$Reference;"), index = 1)
private static Object modifyLootTable(Object value, @Local(argsOnly = true) Identifier id) {
if (!(value instanceof LootTable table)) return value;
if (table == LootTable.EMPTY) {
// This is a special table and cannot be modified.
return value;
}
RegistryKey<LootTable> key = RegistryKey.of(RegistryKeys.LOOT_TABLE, id);
// Populated inside JsonDataLoaderMixin
LootTableSource source = LootUtil.SOURCES.get().getOrDefault(id, LootTableSource.DATA_PACK);
// Invoke the REPLACE event for the current loot table.
LootTable replacement = LootTableEvents.REPLACE.invoker().replaceLootTable(key, 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(key, builder, source);
return builder.build();
}
@SuppressWarnings("unchecked")
@Inject(method = "method_58279", at = @At("RETURN"))
private static void onLootTablesLoaded(LootDataType lootDataType, ResourceManager resourceManager, RegistryOps registryOps, CallbackInfoReturnable<MutableRegistry> cir) {
if (lootDataType != LootDataType.LOOT_TABLES) return;
LootTableEvents.ALL_LOADED.invoker().onLootTablesLoaded(resourceManager, (Registry<LootTable>) cir.getReturnValue());
LootUtil.SOURCES.remove();
}
}

View file

@ -3,7 +3,8 @@
"package": "net.fabricmc.fabric.mixin.loot",
"compatibilityLevel": "JAVA_17",
"mixins": [
"LootManagerMixin",
"JsonDataLoaderMixin",
"ReloadableRegistriesMixin",
"LootPoolAccessor",
"LootPoolBuilderMixin",
"LootTableAccessor",

View file

@ -35,10 +35,10 @@ public class LootTest implements ModInitializer {
// 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)) {
LootTableEvents.REPLACE.register((key, original, source) -> {
if (Blocks.BLACK_WOOL.getLootTableId() == key) {
if (source != LootTableSource.VANILLA) {
throw new AssertionError("black wool loot table should have LootTableSource.VANILLA");
throw new AssertionError("black wool loot table should have LootTableSource.VANILLA, got " + source);
}
// Replace black wool drops with an iron ingot
@ -53,22 +53,22 @@ public class LootTest implements ModInitializer {
});
// 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)) {
LootTableEvents.REPLACE.register((key, original, source) -> {
if (Blocks.BLACK_WOOL.getLootTableId() == key) {
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");
LootTableEvents.MODIFY.register((key, tableBuilder, source) -> {
if (Blocks.BLACK_WOOL.getLootTableId() == key && source != LootTableSource.REPLACED) {
throw new AssertionError("black wool loot table should have LootTableSource.REPLACED, got " + source);
}
if (Blocks.WHITE_WOOL.getLootTableId().equals(id)) {
if (Blocks.WHITE_WOOL.getLootTableId() == key) {
if (source != LootTableSource.VANILLA) {
throw new AssertionError("white wool loot table should have LootTableSource.VANILLA");
throw new AssertionError("white wool loot table should have LootTableSource.VANILLA, got " + source);
}
// Add gold ingot with custom name to white wool drops
@ -82,19 +82,19 @@ public class LootTest implements ModInitializer {
}
// 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");
if (Blocks.RED_WOOL.getLootTableId() == key && source != LootTableSource.MOD) {
throw new AssertionError("red wool loot table should have LootTableSource.MOD, got " + source);
}
// Modify yellow wool to drop *either* yellow wool or emeralds by adding
// emeralds to the same loot pool.
if (Blocks.YELLOW_WOOL.getLootTableId().equals(id)) {
if (Blocks.YELLOW_WOOL.getLootTableId() == key) {
tableBuilder.modifyPools(poolBuilder -> poolBuilder.with(ItemEntry.builder(Items.EMERALD)));
}
});
LootTableEvents.ALL_LOADED.register((resourceManager, lootManager) -> {
LootTable blackWoolTable = lootManager.getLootTable(Blocks.BLACK_WOOL.getLootTableId());
LootTableEvents.ALL_LOADED.register((resourceManager, lootRegistry) -> {
LootTable blackWoolTable = lootRegistry.get(Blocks.BLACK_WOOL.getLootTableId());
if (blackWoolTable == LootTable.EMPTY) {
throw new AssertionError("black wool loot table should not be empty");

View file

@ -1,11 +0,0 @@
version = getSubprojectVersion(project)
moduleDependencies(project, [
'fabric-api-base',
'fabric-lifecycle-events-v1',
'fabric-resource-loader-v0'
])
testDependencies(project, [
':fabric-lifecycle-events-v1'
])

View file

@ -1,51 +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.api.mininglevel.v1;
import net.minecraft.block.Block;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
/**
* Defines additional {@code mineable} tags for vanilla tools not covered by vanilla.
*
* <p>{@code mineable} tags specify which tools are able to break a block effectively and drop it.
* Fabric API defines two additional {@code mineable} tags: {@link #SWORD_MINEABLE #fabric:mineable/sword}
* and {@link #SHEARS_MINEABLE #fabric:mineable/shears}.
*/
public final class FabricMineableTags {
/**
* Blocks in this tag ({@code #fabric:mineable/sword}) can be effectively mined with swords.
*
* <p>As swords have materials and mining levels, the mining level tags described in
* {@link MiningLevelManager} also apply.
*/
public static final TagKey<Block> SWORD_MINEABLE = register("mineable/sword");
/**
* Blocks in this tag ({@code #fabric:mineable/shears}) can be effectively mined with shears.
*/
public static final TagKey<Block> SHEARS_MINEABLE = register("mineable/shears");
private FabricMineableTags() {
}
private static TagKey<Block> register(String id) {
return TagKey.of(RegistryKeys.BLOCK, new Identifier("fabric", id));
}
}

View file

@ -1,84 +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.api.mininglevel.v1;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.impl.mininglevel.MiningLevelManagerImpl;
/**
* Provides access to block mining levels.
*
* <h2>Mining level tags</h2>
* {@code MiningLevelManager} supports the vanilla minimum mining level tags:
* {@link net.minecraft.registry.tag.BlockTags#NEEDS_STONE_TOOL #needs_stone_tool},
* {@link net.minecraft.registry.tag.BlockTags#NEEDS_IRON_TOOL #needs_iron_tool} and
* {@link net.minecraft.registry.tag.BlockTags#NEEDS_DIAMOND_TOOL #needs_diamond_tool}.
* In addition to them, you can use dynamic mining level tags for any mining level (such as wood, netherite
* or a custom one). The dynamic tags are checked automatically.
*
* <p>Dynamic mining level tags are in the format {@code #fabric:needs_tool_level_N}, where {@code N}
* is the wanted tool level as an integer. For example, a mining level tag for netherite (mining level 4) would be
* {@code #fabric:needs_tool_level_4}.
*/
public final class MiningLevelManager {
private MiningLevelManager() {
}
/**
* Gets the tool mining level required to effectively mine and drop a block state.
*
* <p>Note: this method does not take into account tool-specific mining levels declared
* with the tool attribute API.
*
* <p>The default mining level of blocks not modified with mining level tags
* is -1 (the hand mining level).
*
* @param state the block state
* @return the mining level of the block state
*/
public static int getRequiredMiningLevel(BlockState state) {
return MiningLevelManagerImpl.getRequiredMiningLevel(state);
}
/**
* Gets the mining level block tag corresponding to a given integer mining level.
* More precisely, return the corresponding vanilla tag ({@code #minecraft:needs_x_tool}) for levels 1 to 3,
* and the Fabric tag ({@code #fabric:needs_tool_level_N}) for levels above 3.
*
* @param miningLevel the integer mining level
* @return the corresponding mining level block tag
* @throws IllegalArgumentException if a negative or zero mining level is passed
*/
public static TagKey<Block> getBlockTag(int miningLevel) {
if (miningLevel <= 0) {
throw new IllegalArgumentException("Mining level tags only exist for mining levels > 0, received: " + miningLevel);
}
return switch (miningLevel) {
case 1 -> BlockTags.NEEDS_STONE_TOOL;
case 2 -> BlockTags.NEEDS_IRON_TOOL;
case 3 -> BlockTags.NEEDS_DIAMOND_TOOL;
default -> TagKey.of(RegistryKeys.BLOCK, new Identifier("fabric", "needs_tool_level_" + miningLevel));
};
}
}

View file

@ -1,81 +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.impl.mininglevel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.registry.tag.TagKey;
import net.fabricmc.fabric.api.event.lifecycle.v1.CommonLifecycleEvents;
import net.fabricmc.yarn.constants.MiningLevels;
public final class MiningLevelManagerImpl {
private static final Logger LOGGER = LoggerFactory.getLogger("fabric-mining-level-api-v1/MiningLevelManagerImpl");
private static final String TOOL_TAG_NAMESPACE = "fabric";
private static final Pattern TOOL_TAG_PATTERN = Pattern.compile("^needs_tool_level_([0-9]+)$");
// A cache of block state mining levels. Cleared when tags are updated.
private static final ThreadLocal<Reference2IntMap<BlockState>> CACHE = ThreadLocal.withInitial(Reference2IntOpenHashMap::new);
static {
CommonLifecycleEvents.TAGS_LOADED.register((registries, client) -> CACHE.get().clear());
}
public static int getRequiredMiningLevel(BlockState state) {
return CACHE.get().computeIfAbsent(state, s -> {
int miningLevel = MiningLevels.HAND;
// Handle #fabric:needs_tool_level_N
for (TagKey<Block> tagId : state.streamTags().toList()) {
if (!tagId.id().getNamespace().equals(TOOL_TAG_NAMESPACE)) {
continue;
}
Matcher matcher = TOOL_TAG_PATTERN.matcher(tagId.id().getPath());
if (matcher.matches()) {
try {
int tagMiningLevel = Integer.parseInt(matcher.group(1));
miningLevel = Math.max(miningLevel, tagMiningLevel);
} catch (NumberFormatException e) {
LOGGER.error("Could not read mining level from tag #{}", tagId, e);
}
}
}
// Handle vanilla tags
if (state.isIn(BlockTags.NEEDS_DIAMOND_TOOL)) {
miningLevel = Math.max(miningLevel, MiningLevels.DIAMOND);
} else if (state.isIn(BlockTags.NEEDS_IRON_TOOL)) {
miningLevel = Math.max(miningLevel, MiningLevels.IRON);
} else if (state.isIn(BlockTags.NEEDS_STONE_TOOL)) {
miningLevel = Math.max(miningLevel, MiningLevels.STONE);
}
return miningLevel;
});
}
}

View file

@ -1,38 +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.mininglevel;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.block.BlockState;
import net.minecraft.item.MiningToolItem;
import net.fabricmc.fabric.api.mininglevel.v1.MiningLevelManager;
@Mixin(MiningToolItem.class)
abstract class MiningToolItemMixin {
@Inject(method = "isSuitableFor", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/item/ToolMaterial;getMiningLevel()I"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD)
private void fabric$onIsSuitableFor(BlockState state, CallbackInfoReturnable<Boolean> info, int toolMiningLevel) {
if (toolMiningLevel < MiningLevelManager.getRequiredMiningLevel(state)) {
info.setReturnValue(false);
}
}
}

View file

@ -1,56 +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.mininglevel;
import org.spongepowered.asm.mixin.Mixin;
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.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ShearsItem;
import net.fabricmc.fabric.api.mininglevel.v1.FabricMineableTags;
/**
* Adds support for {@link FabricMineableTags#SHEARS_MINEABLE}.
*/
@Mixin(ShearsItem.class)
abstract class ShearsItemMixin {
@Inject(method = "isSuitableFor", at = @At("HEAD"), cancellable = true)
private void fabric$onIsSuitableFor(BlockState state, CallbackInfoReturnable<Boolean> info) {
if (state.isIn(FabricMineableTags.SHEARS_MINEABLE)) {
info.setReturnValue(true);
}
}
@Inject(method = "getMiningSpeedMultiplier", at = @At("RETURN"), cancellable = true)
private void fabric$onGetMiningSpeedMultiplier(ItemStack stack, BlockState state, CallbackInfoReturnable<Float> info) {
if (info.getReturnValueF() == 1.0f) { // if not caught by vanilla checks
if (state.isIn(FabricMineableTags.SHEARS_MINEABLE)) { // mimics MiningToolItem.getMiningSpeedMultiplier
// In vanilla 1.17, shears have three special mining speed multiplier values:
// - cobweb and leaves return 15.0
// - wool returns 5.0
// - vines and glow lichen return 2.0
// As the most "neutral" option out of these three,
// we'll use 5.0 as it's not extremely fast nor extremely slow.
info.setReturnValue(5.0f);
}
}
}
}

View file

@ -1,61 +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.mininglevel;
import org.spongepowered.asm.mixin.Mixin;
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.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.SwordItem;
import net.minecraft.item.ToolItem;
import net.minecraft.item.ToolMaterial;
import net.fabricmc.fabric.api.mininglevel.v1.FabricMineableTags;
import net.fabricmc.fabric.api.mininglevel.v1.MiningLevelManager;
/**
* Adds support for {@link FabricMineableTags#SWORD_MINEABLE}.
*/
@Mixin(SwordItem.class)
abstract class SwordItemMixin extends ToolItem {
private SwordItemMixin(ToolMaterial material, Settings settings) {
super(material, settings);
}
@Inject(method = "isSuitableFor", at = @At("HEAD"), cancellable = true)
private void fabric$onIsSuitableFor(BlockState state, CallbackInfoReturnable<Boolean> info) {
if (state.isIn(FabricMineableTags.SWORD_MINEABLE)) {
int miningLevel = getMaterial().getMiningLevel();
if (miningLevel >= MiningLevelManager.getRequiredMiningLevel(state)) {
info.setReturnValue(true);
}
}
}
@Inject(method = "getMiningSpeedMultiplier", at = @At("RETURN"), cancellable = true)
private void fabric$onGetMiningSpeedMultiplier(ItemStack stack, BlockState state, CallbackInfoReturnable<Float> info) {
if (info.getReturnValueF() == 1.0f) { // if not caught by vanilla checks
if (state.isIn(FabricMineableTags.SWORD_MINEABLE)) { // mimics MiningToolItem.getMiningSpeedMultiplier
info.setReturnValue(getMaterial().getMiningSpeedMultiplier());
}
}
}
}

View file

@ -1,13 +0,0 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.mininglevel",
"compatibilityLevel": "JAVA_17",
"mixins": [
"MiningToolItemMixin",
"ShearsItemMixin",
"SwordItemMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -1,31 +0,0 @@
{
"schemaVersion": 1,
"id": "fabric-mining-level-api-v1",
"name": "Fabric Mining Level API (v1)",
"version": "${version}",
"environment": "*",
"license": "Apache-2.0",
"icon": "assets/fabric-mining-level-api-v1/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.15.6",
"fabric-api-base": "*",
"fabric-lifecycle-events-v1": "*",
"fabric-resource-loader-v0": "*"
},
"description": "Adds support for custom mining levels.",
"mixins": [
"fabric-mining-level-api-v1.mixins.json"
],
"custom": {
"fabric-api:module-lifecycle": "stable"
}
}

View file

@ -1,135 +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.test.mininglevel;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
// This test must pass without the tool attribute API present.
// It has its own handlers for mining levels, which might "hide" this module
// not working on its own.
public final class MiningLevelTest implements ModInitializer {
private static final String ID = "fabric-mining-level-api-v1-testmod";
private static final Logger LOGGER = LoggerFactory.getLogger(MiningLevelTest.class);
/// Tagged blocks
// sword + dynamic mining level tag
public static final Block NEEDS_NETHERITE_SWORD = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
// sword + vanilla mining level tag
public static final Block NEEDS_STONE_SWORD = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
// any sword
public static final Block NEEDS_ANY_SWORD = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
// shears
public static final Block NEEDS_SHEARS = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
// vanilla mineable tag + dynamic mining level tag
public static final Block NEEDS_NETHERITE_PICKAXE = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
// vanilla mineable tag, requires tool (this type of block doesn't exist in vanilla)
public static final Block NEEDS_AXE = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
// vanilla mineable tag, requires tool (this type of block doesn't exist in vanilla)
public static final Block NEEDS_HOE = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
// vanilla mineable tag, requires tool (this type of block doesn't exist in vanilla)
public static final Block NEEDS_SHOVEL = new Block(AbstractBlock.Settings.create().strength(2, 3).requiresTool());
@Override
public void onInitialize() {
register("needs_netherite_sword", NEEDS_NETHERITE_SWORD);
register("needs_stone_sword", NEEDS_STONE_SWORD);
register("needs_any_sword", NEEDS_ANY_SWORD);
register("needs_shears", NEEDS_SHEARS);
register("needs_netherite_pickaxe", NEEDS_NETHERITE_PICKAXE);
register("needs_axe", NEEDS_AXE);
register("needs_hoe", NEEDS_HOE);
register("needs_shovel", NEEDS_SHOVEL);
ServerLifecycleEvents.SERVER_STARTED.register(server -> test());
}
private static void register(String id, Block block) {
Identifier identifier = new Identifier(ID, id);
Registry.register(Registries.BLOCK, identifier, block);
Registry.register(Registries.ITEM, identifier, new BlockItem(block, new Item.Settings()));
}
private static void test() {
List<AssertionError> errors = new ArrayList<>();
test(errors, () -> checkMiningLevel(NEEDS_NETHERITE_SWORD, List.of(Items.NETHERITE_SWORD), List.of(Items.NETHERITE_PICKAXE, Items.STONE_SWORD)));
test(errors, () -> checkMiningLevel(NEEDS_STONE_SWORD, List.of(Items.STONE_SWORD, Items.IRON_SWORD), List.of(Items.STONE_PICKAXE, Items.WOODEN_SWORD)));
test(errors, () -> checkMiningLevel(NEEDS_ANY_SWORD, List.of(Items.WOODEN_SWORD), List.of()));
test(errors, () -> checkMiningLevel(NEEDS_SHEARS, List.of(Items.SHEARS), List.of()));
test(errors, () -> checkMiningLevel(NEEDS_NETHERITE_PICKAXE, List.of(Items.NETHERITE_PICKAXE), List.of(Items.DIAMOND_PICKAXE, Items.NETHERITE_AXE)));
test(errors, () -> checkMiningLevel(Blocks.STONE, List.of(Items.WOODEN_PICKAXE), List.of(Items.STICK)));
test(errors, () -> checkMiningLevel(NEEDS_AXE, List.of(Items.WOODEN_AXE), List.of(Items.STICK)));
test(errors, () -> checkMiningLevel(NEEDS_HOE, List.of(Items.WOODEN_HOE), List.of(Items.STICK)));
test(errors, () -> checkMiningLevel(NEEDS_SHOVEL, List.of(Items.WOODEN_SHOVEL), List.of(Items.STICK)));
if (errors.isEmpty()) {
LOGGER.info("Mining level tests passed!");
} else {
AssertionError error = new AssertionError("Mining level tests failed!");
errors.forEach(error::addSuppressed);
throw error;
}
}
private static void test(List<AssertionError> errors, Runnable runnable) {
try {
runnable.run();
} catch (AssertionError e) {
errors.add(e);
}
}
private static void checkMiningLevel(Block block, List<Item> successfulItems, List<Item> failingItems) {
BlockState state = block.getDefaultState();
for (Item success : successfulItems) {
ItemStack successStack = new ItemStack(success);
if (!successStack.isSuitableFor(state)) {
throw new AssertionError(success + " is not suitable for " + block);
}
if (successStack.getMiningSpeedMultiplier(state) == 1f) {
throw new AssertionError(success + " returns default mining speed for " + block);
}
}
for (Item failing : failingItems) {
if (new ItemStack(failing).isSuitableFor(state)) {
throw new AssertionError(failing + " is suitable for " + block);
}
}
}
}

View file

@ -1,6 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_shears"
]
}

View file

@ -1,8 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_netherite_sword",
"fabric-mining-level-api-v1-testmod:needs_stone_sword",
"fabric-mining-level-api-v1-testmod:needs_any_sword"
]
}

View file

@ -1,7 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_netherite_pickaxe",
"fabric-mining-level-api-v1-testmod:needs_netherite_sword"
]
}

View file

@ -1,6 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_axe"
]
}

View file

@ -1,6 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_hoe"
]
}

View file

@ -1,6 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_netherite_pickaxe"
]
}

View file

@ -1,6 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_shovel"
]
}

View file

@ -1,6 +0,0 @@
{
"replace": false,
"values": [
"fabric-mining-level-api-v1-testmod:needs_stone_sword"
]
}

View file

@ -1,17 +0,0 @@
{
"schemaVersion": 1,
"id": "fabric-mining-level-api-v1-testmod",
"name": "Fabric Mining Level API (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-mining-level-api-v1": "*",
"fabric-lifecycle-events-v1": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.mininglevel.MiningLevelTest"
]
}
}

View file

@ -27,7 +27,7 @@ import net.fabricmc.fabric.mixin.object.builder.client.ModelPredicateProviderReg
* Allows registering model predicate providers for item models.
*
* <p>A registered model predicate providers for an item can be retrieved through
* {@link net.minecraft.client.item.ModelPredicateProviderRegistry#get(Item, Identifier)}.</p>
* {@link net.minecraft.client.item.ModelPredicateProviderRegistry#get(net.minecraft.item.ItemStack, Identifier)}.</p>
*
* @see net.minecraft.client.item.ModelPredicateProviderRegistry
* @deprecated Replaced by access wideners for {@link net.minecraft.client.item.ModelPredicateProviderRegistry}

View file

@ -208,5 +208,9 @@ public class FabricRegistryInit implements ModInitializer {
// Synced by rawID.
RegistryAttributeHolder.get(Registries.DATA_COMPONENT_TYPE)
.addAttribute(RegistryAttribute.SYNCED);
// Synced by rawID.
RegistryAttributeHolder.get(Registries.MAP_DECORATION_TYPE)
.addAttribute(RegistryAttribute.SYNCED);
}
}

View file

@ -4,7 +4,7 @@ fabric.loom.multiProjectOptimisation=true
version=0.96.12
minecraft_version=24w12a
yarn_version=+build.2
yarn_version=+build.5
loader_version=0.15.6
installer_version=0.11.1
@ -38,7 +38,6 @@ fabric-keybindings-v0-version=0.2.41
fabric-lifecycle-events-v1-version=2.3.1
fabric-loot-api-v2-version=2.1.13
fabric-message-api-v1-version=6.0.8
fabric-mining-level-api-v1-version=2.1.69
fabric-model-loading-api-v1-version=1.0.10
fabric-models-v0-version=0.4.9
fabric-networking-api-v1-version=4.0.5

View file

@ -37,9 +37,8 @@ include 'fabric-item-api-v1'
include 'fabric-item-group-api-v1'
include 'fabric-key-binding-api-v1'
include 'fabric-lifecycle-events-v1'
//include 'fabric-loot-api-v2'
include 'fabric-loot-api-v2'
include 'fabric-message-api-v1'
//include 'fabric-mining-level-api-v1'
include 'fabric-model-loading-api-v1'
include 'fabric-networking-api-v1'
include 'fabric-object-builder-api-v1'