Add tag-based mining level API, fix and deprecate FabricBlockSettings.breakByTool (#1629)

* Mining Level API, part 1

* Fix incorrect netherite mining level in MiningLevelManager docs

* Add some format checks in dynamic mining level tag checking

* Add mining level API test mod

* Grammar

* Add cursed basic support for the tool attribute api

* Fix tool attribute JD

* Add proper compat for tool attribute api

Also fixes the remaining bugs in it!

* Update license headers

* stuff

* better comments

* more test mod work

* more

* Fix test mod

* i'm done with this module

* Address some code review things

* Use Reference2IntMap (basically IdentityHashMap for int values)

* Prefix mixin handlers with fabric$

* Add missing license header

* Update fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricBlockSettings.java

* Resolve liach's comment

* Update to use TagFactory
This commit is contained in:
Juuxel 2021-11-05 17:24:32 +02:00 committed by modmuss50
parent e14f9c8f05
commit d2f76b0fdf
40 changed files with 977 additions and 8 deletions

View file

@ -0,0 +1,12 @@
archivesBaseName = "fabric-mining-level-api-v1"
version = getSubprojectVersion(project, "1.0.0")
moduleDependencies(project, [
'fabric-api-base',
'fabric-resource-loader-v0',
'fabric-tag-extensions-v0'
])
dependencies {
testmodImplementation project(path: ':fabric-lifecycle-events-v1', configuration: 'dev')
}

View file

@ -0,0 +1,52 @@
/*
* 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.tag.Tag;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.tag.TagFactory;
/**
* 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 Tag.Identified<Block> SWORD_MINEABLE = register("mineable/sword");
/**
* Blocks in this tag ({@code #fabric:mineable/shears}) can be effectively mined with shears.
*/
public static final Tag.Identified<Block> SHEARS_MINEABLE = register("mineable/shears");
private FabricMineableTags() {
}
private static Tag.Identified<Block> register(String id) {
return TagFactory.BLOCK.create(new Identifier("fabric", id));
}
}

View file

@ -0,0 +1,54 @@
/*
* 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.BlockState;
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.tag.BlockTags#NEEDS_STONE_TOOL #needs_stone_tool},
* {@link net.minecraft.tag.BlockTags#NEEDS_IRON_TOOL #needs_iron_tool} and
* {@link net.minecraft.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.
*
* @param state the block state
* @return the mining level of the block state
*/
public static int getRequiredMiningLevel(BlockState state) {
return MiningLevelManagerImpl.getRequiredMiningLevel(state);
}
}

View file

@ -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.mininglevel;
import net.minecraft.resource.ResourceType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
public final class FabricMiningLevelInit implements ModInitializer {
@Override
public void onInitialize() {
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new MiningLevelCacheInvalidator());
}
}

View file

@ -0,0 +1,47 @@
/*
* 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.Collection;
import java.util.Collections;
import java.util.Set;
import net.minecraft.resource.ResourceManager;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.resource.ResourceReloadListenerKeys;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
final class MiningLevelCacheInvalidator implements SimpleSynchronousResourceReloadListener {
private static final Identifier ID = new Identifier("fabric-mining-level-api-v1", "cache_invalidator");
private static final Set<Identifier> DEPENDENCIES = Collections.singleton(ResourceReloadListenerKeys.TAGS);
@Override
public Identifier getFabricId() {
return ID;
}
@Override
public Collection<Identifier> getFabricDependencies() {
return DEPENDENCIES;
}
@Override
public void reload(ResourceManager manager) {
MiningLevelManagerImpl.clearCache();
}
}

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.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.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tag.BlockTags;
import net.minecraft.tag.TagGroup;
import net.minecraft.util.Identifier;
import net.fabricmc.yarn.constants.MiningLevels;
public final class MiningLevelManagerImpl {
private static final Logger LOGGER = LogManager.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 by
// - MiningLevelCacheInvalidator when tags are reloaded
// - ClientPlayNetworkHandlerMixin when tags are synced
private static final ThreadLocal<Reference2IntMap<BlockState>> CACHE = ThreadLocal.withInitial(Reference2IntOpenHashMap::new);
public static int getRequiredMiningLevel(BlockState state) {
return CACHE.get().computeIntIfAbsent(state, s -> {
TagGroup<Block> blockTags = BlockTags.getTagGroup();
int miningLevel = MiningLevels.HAND;
// Handle #fabric:needs_tool_level_N
for (Identifier tagId : blockTags.getTagsFor(state.getBlock())) {
if (!tagId.getNamespace().equals(TOOL_TAG_NAMESPACE)) {
continue;
}
Matcher matcher = TOOL_TAG_PATTERN.matcher(tagId.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;
});
}
public static void clearCache() {
CACHE.get().clear();
}
}

View file

@ -0,0 +1,38 @@
/*
* 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

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

@ -0,0 +1,61 @@
/*
* 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

@ -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.mininglevel.client;
import org.objectweb.asm.Opcodes;
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.Slice;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.s2c.play.SynchronizeTagsS2CPacket;
import net.fabricmc.fabric.impl.mininglevel.MiningLevelManagerImpl;
@Mixin(ClientPlayNetworkHandler.class)
abstract class ClientPlayNetworkHandlerMixin {
@Inject(method = "onSynchronizeTags", at = @At("RETURN"), slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;tagManager:Lnet/minecraft/tag/TagManager;", opcode = Opcodes.PUTFIELD)))
private void fabric$clearMiningLevelCache(SynchronizeTagsS2CPacket packet, CallbackInfo info) {
MiningLevelManagerImpl.clearCache();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

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

View file

@ -0,0 +1,36 @@
{
"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"
],
"entrypoints": {
"main": [
"net.fabricmc.fabric.impl.mininglevel.FabricMiningLevelInit"
]
},
"depends": {
"fabricloader": ">=0.4.0",
"fabric-api-base": "*",
"fabric-resource-loader-v0": "*",
"fabric-tag-extensions-v0": "*"
},
"description": "Adds support for custom mining levels.",
"mixins": [
"fabric-mining-level-api-v1.mixins.json"
],
"custom": {
"fabric-api:module-lifecycle": "stable"
}
}

View file

@ -0,0 +1,136 @@
/*
* 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.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.Material;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
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 = LogManager.getLogger();
/// Tagged blocks
// sword + dynamic mining level tag
public static final Block NEEDS_NETHERITE_SWORD = new Block(AbstractBlock.Settings.of(Material.STONE).strength(2, 3).requiresTool());
// sword + vanilla mining level tag
public static final Block NEEDS_STONE_SWORD = new Block(AbstractBlock.Settings.of(Material.STONE).strength(2, 3).requiresTool());
// any sword
public static final Block NEEDS_ANY_SWORD = new Block(AbstractBlock.Settings.of(Material.STONE).strength(2, 3).requiresTool());
// shears
public static final Block NEEDS_SHEARS = new Block(AbstractBlock.Settings.of(Material.STONE).strength(2, 3).requiresTool());
// vanilla mineable tag + dynamic mining level tag
public static final Block NEEDS_NETHERITE_PICKAXE = new Block(AbstractBlock.Settings.of(Material.STONE).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.of(Material.STONE).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.of(Material.STONE).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.of(Material.STONE).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(Registry.BLOCK, identifier, block);
Registry.register(Registry.ITEM, identifier, new BlockItem(block, new Item.Settings().group(ItemGroup.MISC)));
}
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

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

View file

@ -0,0 +1,8 @@
{
"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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,17 @@
{
"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

@ -315,7 +315,10 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
/**
* Please make the block require a tool if you plan to disable drops and slow the breaking down using the
* incorrect tool by using {@link FabricBlockSettings#requiresTool()}.
*
* @deprecated Replaced by {@code mineable} tags. See fabric-mining-level-api-v1 for further details.
*/
@Deprecated(forRemoval = true)
public FabricBlockSettings breakByTool(Tag<Item> tag, int miningLevel) {
FabricBlockInternals.computeExtraData(this).addMiningLevel(tag, miningLevel);
return this;
@ -324,7 +327,10 @@ public class FabricBlockSettings extends AbstractBlock.Settings {
/**
* Please make the block require a tool if you plan to disable drops and slow the breaking down using the
* incorrect tool by using {@link FabricBlockSettings#requiresTool()}.
*
* @deprecated Replaced by {@code mineable} tags. See fabric-mining-level-api-v1 for further details.S
*/
@Deprecated(forRemoval = true)
public FabricBlockSettings breakByTool(Tag<Item> tag) {
return this.breakByTool(tag, 0);
}

View file

@ -9,5 +9,6 @@ dependencies {
moduleDependencies(project, [
'fabric-api-base',
'fabric-mining-level-api-v1',
'fabric-tag-extensions-v0'
])

View file

@ -19,12 +19,16 @@ package net.fabricmc.fabric.impl.tool.attribute;
import java.util.Arrays;
import net.minecraft.item.Items;
import net.minecraft.tag.BlockTags;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.mininglevel.v1.FabricMineableTags;
import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags;
import net.fabricmc.fabric.impl.tool.attribute.handlers.ModdedToolsVanillaBlocksToolHandler;
import net.fabricmc.fabric.impl.tool.attribute.handlers.ModdedToolsModdedBlocksToolHandler;
import net.fabricmc.fabric.impl.tool.attribute.handlers.ModdedToolsVanillaBlocksToolHandler;
import net.fabricmc.fabric.impl.tool.attribute.handlers.ShearsVanillaBlocksToolHandler;
import net.fabricmc.fabric.impl.tool.attribute.handlers.TaggedToolsModdedBlocksToolHandler;
import net.fabricmc.fabric.impl.tool.attribute.handlers.TaggedToolsTaggedBlocksToolHandler;
import net.fabricmc.fabric.impl.tool.attribute.handlers.VanillaToolsModdedBlocksToolHandler;
/**
@ -35,6 +39,7 @@ public class ToolHandlers implements ModInitializer {
public void onInitialize() {
ToolManagerImpl.general().register(new ModdedToolsModdedBlocksToolHandler());
ToolManagerImpl.general().register(new VanillaToolsModdedBlocksToolHandler());
ToolManagerImpl.general().register(new TaggedToolsModdedBlocksToolHandler());
ToolManagerImpl.tag(FabricToolTags.PICKAXES).register(new ModdedToolsVanillaBlocksToolHandler(
Arrays.asList(
Items.WOODEN_PICKAXE,
@ -81,5 +86,11 @@ public class ToolHandlers implements ModInitializer {
)
));
ToolManagerImpl.tag(FabricToolTags.SHEARS).register(new ShearsVanillaBlocksToolHandler());
ToolManagerImpl.tag(FabricToolTags.AXES).register(new TaggedToolsTaggedBlocksToolHandler(BlockTags.AXE_MINEABLE));
ToolManagerImpl.tag(FabricToolTags.HOES).register(new TaggedToolsTaggedBlocksToolHandler(BlockTags.HOE_MINEABLE));
ToolManagerImpl.tag(FabricToolTags.PICKAXES).register(new TaggedToolsTaggedBlocksToolHandler(BlockTags.PICKAXE_MINEABLE));
ToolManagerImpl.tag(FabricToolTags.SHEARS).register(new TaggedToolsTaggedBlocksToolHandler(FabricMineableTags.SHEARS_MINEABLE));
ToolManagerImpl.tag(FabricToolTags.SHOVELS).register(new TaggedToolsTaggedBlocksToolHandler(BlockTags.SHOVEL_MINEABLE));
ToolManagerImpl.tag(FabricToolTags.SWORDS).register(new TaggedToolsTaggedBlocksToolHandler(FabricMineableTags.SWORD_MINEABLE));
}
}

View file

@ -22,6 +22,7 @@ import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Objects;
import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -30,16 +31,31 @@ import net.minecraft.block.BlockState;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tag.BlockTags;
import net.minecraft.tag.Tag;
import net.minecraft.util.ActionResult;
import net.minecraft.util.TypedActionResult;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.mininglevel.v1.FabricMineableTags;
import net.fabricmc.fabric.api.mininglevel.v1.MiningLevelManager;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags;
import net.fabricmc.fabric.api.util.TriState;
public final class ToolManagerImpl {
private static final Map<Tag<Item>, Tag<Block>> MINEABLE_TAG_BY_TOOL = ImmutableMap.<Tag<Item>, Tag<Block>>builder()
// Vanilla mineable tags
.put(FabricToolTags.AXES, BlockTags.AXE_MINEABLE)
.put(FabricToolTags.HOES, BlockTags.HOE_MINEABLE)
.put(FabricToolTags.PICKAXES, BlockTags.PICKAXE_MINEABLE)
.put(FabricToolTags.SHOVELS, BlockTags.SHOVEL_MINEABLE)
// Fabric mineable tags
.put(FabricToolTags.SHEARS, FabricMineableTags.SHEARS_MINEABLE)
.put(FabricToolTags.SWORDS, FabricMineableTags.SWORD_MINEABLE)
.build();
public interface Entry {
void setBreakByHand(boolean value);
@ -49,10 +65,15 @@ public final class ToolManagerImpl {
}
private static class EntryImpl implements Entry {
private final Block block;
private Tag<Item>[] tags = new Tag[0];
private int[] tagLevels = new int[0];
private TriState defaultValue = TriState.DEFAULT;
private EntryImpl(Block block) {
this.block = block;
}
@Override
public void setBreakByHand(boolean value) {
this.defaultValue = TriState.of(value);
@ -78,13 +99,24 @@ public final class ToolManagerImpl {
@Override
public int getMiningLevel(Tag<Item> tag) {
// Implementation detail: the actual logic does not check the state.
// TODO: This should be changed some day to respect the block state,
// but the entry code is quite coupled in blocks instead of states.
int miningLevel = MiningLevelManager.getRequiredMiningLevel(block.getDefaultState());
for (int i = 0; i < tags.length; i++) {
if (tags[i] == tag) {
return tagLevels[i];
miningLevel = Math.max(miningLevel, tagLevels[i]);
}
}
return -1;
for (Tag<Item> key : MINEABLE_TAG_BY_TOOL.keySet()) {
if (tag == key && MINEABLE_TAG_BY_TOOL.get(key).contains(block)) {
miningLevel = Math.max(miningLevel, 0);
}
}
return miningLevel;
}
}
@ -152,7 +184,7 @@ public final class ToolManagerImpl {
}
public static Entry entry(Block block) {
return ENTRIES.computeIfAbsent(block, (bb) -> new EntryImpl());
return ENTRIES.computeIfAbsent(block, (bb) -> new EntryImpl(block));
}
@Nullable

View file

@ -34,7 +34,7 @@ import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl;
/**
* This handler handles items that are a subclass of {@link DynamicAttributeTool} by using the
* vanilla {@link Item#isEffectiveOn(BlockState)} with a custom fake tool material to use the mining level
* vanilla {@link Item#isSuitableFor(BlockState)} with a custom fake tool material to use the mining level
* from {@link DynamicAttributeTool#getMiningLevel(Tag, BlockState, ItemStack, LivingEntity)}.
*
* <p>Only applicable to blocks that are vanilla or share the material that is handled by their vanilla tool.</p>

View file

@ -34,7 +34,7 @@ import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl;
/**
* This handler handles items that are registered in the {@link FabricToolTags#SHEARS} by using the
* vanilla {@link Item#isEffectiveOn(BlockState)} using the vanilla shears or the item itself if the item
* vanilla {@link Item#isSuitableFor(BlockState)} using the vanilla shears or the item itself if the item
* is a subclass of {@link ShearsItem}.
*
* <p>Only applicable to items that are not a subclass of {@link DynamicAttributeTool}</p>

View file

@ -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.tool.attribute.handlers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ToolItem;
import net.minecraft.tag.Tag;
import net.minecraft.util.ActionResult;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl;
/**
* This handler handles items that are registered in a tool tag,
* but aren't any known tool items in code. For that reason, we use a few callback values:
* The mining level of this kind of item is always 0, and the mining speed multiplier is always 1.
*
* <p>Only applicable to items that are not a subclass of {@link DynamicAttributeTool} or {@link ToolItem}</p>
*/
public class TaggedToolsModdedBlocksToolHandler implements ToolManagerImpl.ToolHandler {
@NotNull
@Override
public ActionResult isEffectiveOn(Tag<Item> tag, BlockState state, ItemStack stack, @Nullable LivingEntity user) {
if (!(stack.getItem() instanceof DynamicAttributeTool) && !(stack.getItem() instanceof ToolItem)) {
@Nullable ToolManagerImpl.Entry entry = ToolManagerImpl.entryNullable(state.getBlock());
if (entry != null) {
int miningLevel = 0; // minimum mining level: the tool is tagged but nothing else is said about it
int requiredMiningLevel = entry.getMiningLevel(tag);
return requiredMiningLevel >= miningLevel ? ActionResult.SUCCESS : ActionResult.PASS;
}
}
return ActionResult.PASS;
}
}

View file

@ -0,0 +1,59 @@
/*
* 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.tool.attribute.handlers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ToolItem;
import net.minecraft.tag.Tag;
import net.minecraft.util.ActionResult;
import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool;
import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl;
/**
* This handler handles items that are registered in a tool tag,
* but aren't any known tool items in code. For that reason, we use a few callback values:
* The mining level of this kind of item is always 0, and the mining speed multiplier is always 1.
*
* <p>Only applicable to items that are not a subclass of {@link DynamicAttributeTool} or {@link ToolItem}</p>
*/
public class TaggedToolsTaggedBlocksToolHandler implements ToolManagerImpl.ToolHandler {
private final Tag<Block> mineableTag;
public TaggedToolsTaggedBlocksToolHandler(Tag<Block> mineableTag) {
this.mineableTag = mineableTag;
}
@NotNull
@Override
public ActionResult isEffectiveOn(Tag<Item> tag, BlockState state, ItemStack stack, @Nullable LivingEntity user) {
if (!(stack.getItem() instanceof DynamicAttributeTool) && !(stack.getItem() instanceof ToolItem)) {
if (state.isIn(mineableTag)) {
return ActionResult.SUCCESS;
}
}
return ActionResult.PASS;
}
}

View file

@ -18,6 +18,7 @@
"depends": {
"fabricloader": ">=0.4.0",
"fabric-api-base": "*",
"fabric-mining-level-api-v1": "*",
"fabric-tag-extensions-v0": "*"
},
"entrypoints": {

View file

@ -58,6 +58,7 @@ public class ToolAttributeTest implements ModInitializer {
Block stoneBlock;
Item testShovel;
Item testPickaxe;
Item testSword;
Item testStoneLevelTater;
Item testStoneDynamicLevelTater;
@ -65,12 +66,30 @@ public class ToolAttributeTest implements ModInitializer {
Item testDiamondDynamicLevelTater;
Block taterEffectiveBlock;
// Simple blocks that only need a tool without a specific mining level (legacy technique using block settings)
Block needsShears;
Block needsSword;
Block needsPickaxe;
Block needsAxe;
Block needsHoe;
Block needsShovel;
// These items are only tagged, but are not actual ToolItems or DynamicAttributeTools.
Item fakeShears;
Item fakeSword;
Item fakePickaxe;
Item fakeAxe;
Item fakeHoe;
Item fakeShovel;
@Override
public void onInitialize() {
// Register a custom shovel that has a mining level of 2 (iron) dynamically.
testShovel = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "test_shovel"), new TestTool(new Item.Settings(), FabricToolTags.SHOVELS, 2));
//Register a custom pickaxe that has a mining level of 2 (iron) dynamically.
testPickaxe = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "test_pickaxe"), new TestTool(new Item.Settings(), FabricToolTags.PICKAXES, 2));
//Register a custom sword that has a mining level of 2 (iron) dynamically.
testSword = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "test_sword"), new TestTool(new Item.Settings(), FabricToolTags.SWORDS, 2));
// Register a block that requires a shovel that is as strong or stronger than an iron one.
gravelBlock = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "hardened_gravel_block"),
new Block(FabricBlockSettings.of(new FabricMaterialBuilder(MapColor.PALE_YELLOW).build(), MapColor.STONE_GRAY)
@ -115,6 +134,21 @@ public class ToolAttributeTest implements ModInitializer {
// Test parameter nullability
Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "null_test"), new TestNullableItem(new Item.Settings()));
needsShears = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "needs_shears"), new Block(FabricBlockSettings.of(Material.STONE).requiresTool().strength(1, 1).breakByTool(FabricToolTags.SHEARS)));
needsSword = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "needs_sword"), new Block(FabricBlockSettings.of(Material.STONE).requiresTool().strength(1, 1).breakByTool(FabricToolTags.SWORDS)));
needsPickaxe = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "needs_pickaxe"), new Block(FabricBlockSettings.of(Material.STONE).requiresTool().strength(1, 1).breakByTool(FabricToolTags.PICKAXES)));
needsAxe = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "needs_axe"), new Block(FabricBlockSettings.of(Material.STONE).requiresTool().strength(1, 1).breakByTool(FabricToolTags.AXES)));
needsHoe = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "needs_hoe"), new Block(FabricBlockSettings.of(Material.STONE).requiresTool().strength(1, 1).breakByTool(FabricToolTags.HOES)));
needsShovel = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "needs_shovel"), new Block(FabricBlockSettings.of(Material.STONE).requiresTool().strength(1, 1).breakByTool(FabricToolTags.SHOVELS)));
// "Fake" tools, see explanation above
fakeShears = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "fake_shears"), new Item(new Item.Settings()));
fakeSword = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "fake_sword"), new Item(new Item.Settings()));
fakePickaxe = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "fake_pickaxe"), new Item(new Item.Settings()));
fakeAxe = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "fake_axe"), new Item(new Item.Settings()));
fakeHoe = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "fake_hoe"), new Item(new Item.Settings()));
fakeShovel = Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "fake_shovel"), new Item(new Item.Settings()));
ServerTickEvents.START_SERVER_TICK.register(this::validate);
}
@ -170,6 +204,22 @@ public class ToolAttributeTest implements ModInitializer {
testToolOnBlock(new ItemStack(testShovel), taterEffectiveBlock, false, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(Items.IRON_PICKAXE), taterEffectiveBlock, false, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(Items.IRON_SHOVEL), taterEffectiveBlock, false, DEFAULT_BREAK_SPEED);
//Test vanilla tools on blocks
testToolOnBlock(new ItemStack(Items.SHEARS), needsShears, true, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(Items.IRON_SWORD), needsSword, true, ToolMaterials.IRON.getMiningSpeedMultiplier());
testToolOnBlock(new ItemStack(Items.IRON_AXE), needsAxe, true, ToolMaterials.IRON.getMiningSpeedMultiplier());
testToolOnBlock(new ItemStack(Items.IRON_PICKAXE), needsPickaxe, true, ToolMaterials.IRON.getMiningSpeedMultiplier());
testToolOnBlock(new ItemStack(Items.IRON_HOE), needsHoe, true, ToolMaterials.IRON.getMiningSpeedMultiplier());
testToolOnBlock(new ItemStack(Items.IRON_SHOVEL), needsShovel, true, ToolMaterials.IRON.getMiningSpeedMultiplier());
//Test fake tools on corresponding blocks
testToolOnBlock(new ItemStack(fakeShears), needsShears, true, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(fakeSword), needsSword, true, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(fakeAxe), needsAxe, true, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(fakePickaxe), needsPickaxe, true, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(fakeHoe), needsHoe, true, DEFAULT_BREAK_SPEED);
testToolOnBlock(new ItemStack(fakeShovel), needsShovel, true, DEFAULT_BREAK_SPEED);
}
private void testToolOnBlock(ItemStack item, Block block, boolean inEffective, float inSpeed) {

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"fabric-tool-attribute-api-v1-testmod:fake_axe"
]
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"fabric-tool-attribute-api-v1-testmod:fake_hoe"
]
}

View file

@ -1,6 +1,7 @@
{
"replace": false,
"values": [
"fabric-tool-attribute-api-v1-testmod:fake_pickaxe",
"fabric-tool-attribute-api-v1-testmod:test_pickaxe"
]
}
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"fabric-tool-attribute-api-v1-testmod:fake_shears"
]
}

View file

@ -1,6 +1,7 @@
{
"replace": false,
"values": [
"fabric-tool-attribute-api-v1-testmod:fake_shovel",
"fabric-tool-attribute-api-v1-testmod:test_shovel"
]
}
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"fabric-tool-attribute-api-v1-testmod:fake_sword"
]
}

View file

@ -33,6 +33,7 @@ include 'fabric-keybindings-v0'
include 'fabric-key-binding-api-v1'
include 'fabric-lifecycle-events-v1'
include 'fabric-loot-tables-v1'
include 'fabric-mining-level-api-v1'
include 'fabric-mining-levels-v0'
include 'fabric-models-v0'
include 'fabric-networking-v0'