From c8964fcb8d9e04e80c0a9365f26c6823c7e44154 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 11 May 2020 22:12:43 +0100 Subject: [PATCH] tool attribute 1.1 (#589) * Actual support * Remove this useless mixin * Resolve some issues * Remove duplicate entries * maybe? * fix max breaking speed * choose an item that is the closest * Set version to 1.1 * better docs remove stuff * deprecation doc and no todo comments * Check if block has valid mining level * javadocs * Most of the issues resolved * Some more javadoc and resolve some issues * Forgot to change order for these * Adds post process method to DynamicAttributeTool Fixed going to vanilla again to query Fixed tool mining level < 0 Fixed vanilla items on modded blocks not working Renamed taggedToolHandlerInvoker to toolHandlerInvoker Renamed entries to ENTRIES * New comparision method for modded tools to vanilla blocks * Remove useless mixins and use getMaterial().getMiningSpeed to support more tools. * Simply mixin * Some useful javadocs * Fix license * Bump fabric-object-builders-v0 because we are good * Bump version of fabric-object-builder-api-v1 because we are nice * Resolve some issues * a * remove vanilla tools vanilla blocks thing ok * descriptive * Get the faster speed instead * Rename ModdedToolsVanillaBlocksToolHandler * Remove tri state * Fix compile errors * testmod * fix formatting * fix testmod javadoc * forgot to license format testmod * Bump to 1.1.0 # Conflicts: # fabric-object-builder-api-v1/build.gradle # fabric-tool-attribute-api-v1/build.gradle # fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/DynamicAttributeTool.java # fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinItemStack.java # fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinPlayerInventory.java --- fabric-object-builder-api-v1/build.gradle | 2 +- .../builder/v1/block/FabricBlockSettings.java | 11 + .../v1/block/FabricMaterialBuilder.java | 3 + .../object/builder/FabricBlockInternals.java | 6 +- .../fabric/api/block/FabricBlockSettings.java | 11 + fabric-tool-attribute-api-v1/build.gradle | 3 +- .../attribute/v1/DynamicAttributeTool.java | 77 ++++- .../api/tool/attribute/v1/ToolManager.java | 80 ++++++ .../impl/tool/attribute/ToolHandlers.java | 80 ++++++ .../impl/tool/attribute/ToolManager.java | 119 -------- .../impl/tool/attribute/ToolManagerImpl.java | 267 ++++++++++++++++++ .../ModdedToolsModdedBlocksToolHandler.java | 62 ++++ .../ModdedToolsVanillaBlocksToolHandler.java | 100 +++++++ .../ShearsVanillaBlocksToolHandler.java | 76 +++++ .../VanillaToolsModdedBlocksToolHandler.java | 63 +++++ .../mixin/tool/attribute/MixinItemStack.java | 29 +- .../tool/attribute/MixinPlayerInventory.java | 73 ----- .../fabric-tool-attribute-api-v1.mixins.json | 3 +- .../src/main/resources/fabric.mod.json | 5 + .../tool/attribute/ToolAttributeTest.java | 74 +++++ .../blockstates/hardened_block.json | 5 + .../models/item/hardened_block.json | 3 + .../models/item/test_shovel.json | 6 + .../loot_tables/blocks/hardened_block.json | 19 ++ .../data/fabric/tags/items/shovels.json | 6 + .../src/testmod/resources/fabric.mod.json | 16 ++ 26 files changed, 968 insertions(+), 231 deletions(-) create mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/ToolManager.java create mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolHandlers.java delete mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManager.java create mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManagerImpl.java create mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsModdedBlocksToolHandler.java create mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsVanillaBlocksToolHandler.java create mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ShearsVanillaBlocksToolHandler.java create mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/VanillaToolsModdedBlocksToolHandler.java delete mode 100644 fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinPlayerInventory.java create mode 100644 fabric-tool-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/tool/attribute/ToolAttributeTest.java create mode 100644 fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/blockstates/hardened_block.json create mode 100644 fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/hardened_block.json create mode 100644 fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/test_shovel.json create mode 100644 fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric-tool-attribute-api-v1-testmod/loot_tables/blocks/hardened_block.json create mode 100644 fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric/tags/items/shovels.json create mode 100644 fabric-tool-attribute-api-v1/src/testmod/resources/fabric.mod.json diff --git a/fabric-object-builder-api-v1/build.gradle b/fabric-object-builder-api-v1/build.gradle index ef75a1839..865045c7c 100644 --- a/fabric-object-builder-api-v1/build.gradle +++ b/fabric-object-builder-api-v1/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "fabric-object-builder-api-v1" -version = getSubprojectVersion(project, "1.2.0") +version = getSubprojectVersion(project, "1.3.0") dependencies { compile project(path: ':fabric-api-base', configuration: 'dev') diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricBlockSettings.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricBlockSettings.java index 36ec6b24e..6d422ae4b 100644 --- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricBlockSettings.java +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricBlockSettings.java @@ -261,16 +261,27 @@ public class FabricBlockSettings extends AbstractBlock.Settings { /* FABRIC HELPERS */ + /** + * Makes the block breakable by any tool if {@code breakByHand} is set to true. + */ public FabricBlockSettings breakByHand(boolean breakByHand) { FabricBlockInternals.computeExtraData(this).breakByHand(breakByHand); return this; } + /** + * Please make the material require a tool if you plan to disable drops and slow the breaking down using the + * incorrect tool by using {@link FabricMaterialBuilder#requiresTool()}. + */ public FabricBlockSettings breakByTool(Tag tag, int miningLevel) { FabricBlockInternals.computeExtraData(this).addMiningLevel(tag, miningLevel); return this; } + /** + * Please make the material require a tool if you plan to disable drops and slow the breaking down using the + * incorrect tool by using {@link FabricMaterialBuilder#requiresTool()}. + */ public FabricBlockSettings breakByTool(Tag tag) { return this.breakByTool(tag, 0); } diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricMaterialBuilder.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricMaterialBuilder.java index 3f04e799e..b98c5fd8e 100644 --- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricMaterialBuilder.java +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/block/FabricMaterialBuilder.java @@ -84,6 +84,9 @@ public class FabricMaterialBuilder extends Material.Builder { return this; } + /** + * Make the material require tool to drop and slows down mining speed if the incorrect tool is used. + */ @Override public FabricMaterialBuilder requiresTool() { super.requiresTool(); diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricBlockInternals.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricBlockInternals.java index 1bfa92dfe..d72ef29e5 100644 --- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricBlockInternals.java +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/FabricBlockInternals.java @@ -23,7 +23,7 @@ import net.minecraft.block.Block; import net.minecraft.item.Item; import net.minecraft.tag.Tag; -import net.fabricmc.fabric.impl.tool.attribute.ToolManager; +import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl; public final class FabricBlockInternals { private FabricBlockInternals() { @@ -45,11 +45,11 @@ public final class FabricBlockInternals { if (data != null) { if (data.breakByHand != null) { - ToolManager.entry(block).setBreakByHand(data.breakByHand); + ToolManagerImpl.entry(block).setBreakByHand(data.breakByHand); } for (MiningLevel tml : data.miningLevels) { - ToolManager.entry(block).putBreakByTool(tml.tag, tml.level); + ToolManagerImpl.entry(block).putBreakByTool(tml.tag, tml.level); } } } diff --git a/fabric-object-builders-v0/src/main/java/net/fabricmc/fabric/api/block/FabricBlockSettings.java b/fabric-object-builders-v0/src/main/java/net/fabricmc/fabric/api/block/FabricBlockSettings.java index 5e52a4df1..ef10724ba 100644 --- a/fabric-object-builders-v0/src/main/java/net/fabricmc/fabric/api/block/FabricBlockSettings.java +++ b/fabric-object-builders-v0/src/main/java/net/fabricmc/fabric/api/block/FabricBlockSettings.java @@ -68,16 +68,27 @@ public class FabricBlockSettings { /* FABRIC HELPERS */ + /** + * Makes the block breakable by any tool if {@code breakByHand} is set to true. + */ public FabricBlockSettings breakByHand(boolean breakByHand) { this.delegate.breakByHand(breakByHand); return this; } + /** + * Please make the material require a tool if you plan to disable drops and slow the breaking down using the + * incorrect tool by using {@link net.fabricmc.fabric.api.object.builder.v1.block.FabricMaterialBuilder#requiresTool()}. + */ public FabricBlockSettings breakByTool(Tag tag, int miningLevel) { this.delegate.breakByTool(tag, miningLevel); return this; } + /** + * Please make the material require a tool if you plan to disable drops and slow the breaking down using the + * incorrect tool by using {@link net.fabricmc.fabric.api.object.builder.v1.block.FabricMaterialBuilder#requiresTool()}. + */ public FabricBlockSettings breakByTool(Tag tag) { this.delegate.breakByTool(tag); return this; diff --git a/fabric-tool-attribute-api-v1/build.gradle b/fabric-tool-attribute-api-v1/build.gradle index 247b04fcc..ccfbdc5e7 100644 --- a/fabric-tool-attribute-api-v1/build.gradle +++ b/fabric-tool-attribute-api-v1/build.gradle @@ -1,7 +1,8 @@ archivesBaseName = "fabric-tool-attribute-api-v1" -version = getSubprojectVersion(project, "1.0.4") +version = getSubprojectVersion(project, "1.1.0") dependencies { compile project(path: ':fabric-api-base', configuration: 'dev') compile project(path: ':fabric-tag-extensions-v0', configuration: 'dev') + testmodCompile project(path: ':fabric-object-builder-api-v1', configuration: 'dev') } diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/DynamicAttributeTool.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/DynamicAttributeTool.java index f8dd796f7..7592ac206 100644 --- a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/DynamicAttributeTool.java +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/DynamicAttributeTool.java @@ -19,11 +19,14 @@ package net.fabricmc.fabric.api.tool.attribute.v1; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multimap; +import net.minecraft.block.BlockState; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.EntityAttribute; import net.minecraft.entity.attribute.EntityAttributeModifier; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.tag.Tag; /** * Interface for adding various tool attributes to items. @@ -34,22 +37,74 @@ public interface DynamicAttributeTool { /** * Determines the mining level of the passed stack, which is used for calculating what blocks this tool is allowed to break. * - * @param stack The stack to check on. - * @param user The current user of the tool, or null if there isn't any. + * @param stack The item stack being used to mine the block + * @param user The current user of the tool, or null if there isn't any + * @return The mining level of the item. 3 is equal to a diamond pick. + * @deprecated Use {@link #getMiningLevel(Tag, BlockState, ItemStack, LivingEntity)} to detect tag and block. + */ + // nullable on user once we have an official @Nullable annotation in + @Deprecated + default int getMiningLevel(ItemStack stack, /* @Nullable */ LivingEntity user) { + return 0; + } + + /** + * Determines the mining level of the passed stack, which is used for calculating what blocks this tool is allowed to break. + * + * @param tag The tool tag the item stack is being compared to + * @param state The block to mine + * @param stack The item stack being used to mine the block + * @param user The current user of the tool, or null if there isn't any * @return The mining level of the item. 3 is equal to a diamond pick. */ - //TODO: nullable on user once we have an official @Nullable annotation in - int getMiningLevel(ItemStack stack, LivingEntity user); + // nullable on user once we have an official @Nullable annotation in + default int getMiningLevel(Tag tag, BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + return getMiningLevel(stack, user); + } /** * Determines the mining speed multiplier of the passed stack, which is one factor in overall mining speed. * - * @param stack The stack to check on. - * @param user The current user of the tool, or null if there isn't any. + * @param stack The item stack being used to mine the block + * @param user The current user of the tool, or null if there isn't any + * @return The mining speed multiplier of the item. 8.0 is equal to a diamond pick. + * @deprecated Use {@link #getMiningLevel(Tag, BlockState, ItemStack, LivingEntity)} to detect tag and block. + */ + // nullable on user once we have an official @Nullable annotation in + @Deprecated + default float getMiningSpeedMultiplier(ItemStack stack, /* @Nullable */ LivingEntity user) { + return 1.0F; + } + + /** + * Determines the mining speed multiplier of the passed stack, which is one factor in overall mining speed. + * + * @param tag The tool tag the item stack is being compared to + * @param state The block to mine + * @param stack The item stack being used to mine the block + * @param user The current user of the tool, or null if there isn't any * @return The mining speed multiplier of the item. 8.0 is equal to a diamond pick. */ - //TODO: nullable on user once we have an official @Nullable annotation in - float getMiningSpeedMultiplier(ItemStack stack, LivingEntity user); + // nullable on user once we have an official @Nullable annotation in + default float getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + return getMiningSpeedMultiplier(stack, user); + } + + /** + * Post process the mining speed, can be used to change tool speed regardless if the tool is effective. + * Useful if you want to change the mining speed even if it is not effective. + * + * @param tag The tool tag the item stack is handled by + * @param state The block to mine + * @param stack The item stack being used to mine the block + * @param user The current user of the tool, or null if there isn't any + * @param currentSpeed The mining speed before post process + * @param isEffective whether the tool has been handled + * @return the speed after post processing + */ + default float postProcessMiningSpeed(Tag tag, BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user, float currentSpeed, boolean isEffective) { + return currentSpeed; + } /** * Add modifiers for any {@link net.minecraft.entity.attribute.EntityAttributes} your item should give when equipped, based on the stack. @@ -58,11 +113,11 @@ public interface DynamicAttributeTool { * * @param slot The equipment slot this item is equipped in. * @param stack The stack that's equipped. - * @param user The current user of the tool, or none if there isn't any + * @param user The current user of the tool, or none if there isn't any * @return The dynamic modifiers to add on top of other modifiers on this stack. If none, return {@link #EMPTY}. */ - //TODO: nullable on user once we have an official @Nullable annotation in - default Multimap getDynamicModifiers(EquipmentSlot slot, ItemStack stack, LivingEntity user) { + // nullable on user once we have an official @Nullable annotation in + default Multimap getDynamicModifiers(EquipmentSlot slot, ItemStack stack, /* @Nullable */ LivingEntity user) { return EMPTY; } } diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/ToolManager.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/ToolManager.java new file mode 100644 index 000000000..9d0b03b17 --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/api/tool/attribute/v1/ToolManager.java @@ -0,0 +1,80 @@ +/* + * 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.tool.attribute.v1; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; + +import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl; + +/** + * API facing part to register tool handlers and get information about how tools are handled. + * Implement {@link DynamicAttributeTool} to change the mining level or speed of your tool depending on the {@link ItemStack}. + */ +public final class ToolManager { + /** + * Handles if the tool is effective on a block. + * + * @param state the block state to break + * @param stack the item stack involved with breaking the block + * @param user the user involved in breaking the block, null if not applicable. + * @return whether the tool is effective + */ + public static boolean handleIsEffectiveOn(BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + return stack.isEffectiveOn(state) || handleIsEffectiveOnIgnoresVanilla(state, stack, user); + } + + /** + * Handles if the tool is effective on a block, ignores vanilla tools on vanilla blocks. + * + * @param state the block state to break + * @param stack the item stack involved with breaking the block + * @param user the user involved in breaking the block, null if not applicable. + * @return whether the tool is effective + */ + public static boolean handleIsEffectiveOnIgnoresVanilla(BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + return ToolManagerImpl.handleIsEffectiveOnIgnoresVanilla(state, stack, user); + } + + /** + * Handles the breaking speed breaking a block. + * + * @param state the block state to break + * @param stack the item stack involved with breaking the block + * @param user the user involved in breaking the block, null if not applicable. + * @return the speed multiplier in breaking the block, 1.0 if no change. + */ + public static float handleBreakingSpeed(BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + return Math.max(stack.getMiningSpeedMultiplier(state), handleBreakingSpeedIgnoresVanilla(state, stack, user)); + } + + /** + * Handles the breaking speed breaking a block, ignores vanilla tools on vanilla blocks. + * + * @param state the block state to break + * @param stack the item stack involved with breaking the block + * @param user the user involved in breaking the block, null if not applicable. + * @return the speed multiplier in breaking the block, 1.0 if no change. + */ + public static float handleBreakingSpeedIgnoresVanilla(BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + return ToolManagerImpl.handleBreakingSpeedIgnoresVanilla(state, stack, user); + } + + private ToolManager() { + } +} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolHandlers.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolHandlers.java new file mode 100644 index 000000000..bc0e4decd --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolHandlers.java @@ -0,0 +1,80 @@ +/* + * 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; + +import java.util.Arrays; + +import net.minecraft.item.Items; + +import net.fabricmc.api.ModInitializer; +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.ShearsVanillaBlocksToolHandler; +import net.fabricmc.fabric.impl.tool.attribute.handlers.VanillaToolsModdedBlocksToolHandler; + +/** + * Entrypoint to register the default tool handlers. + */ +public class ToolHandlers implements ModInitializer { + @Override + public void onInitialize() { + ToolManagerImpl.general().register(new ModdedToolsModdedBlocksToolHandler()); + ToolManagerImpl.general().register(new VanillaToolsModdedBlocksToolHandler()); + ToolManagerImpl.tag(FabricToolTags.PICKAXES).register(new ModdedToolsVanillaBlocksToolHandler( + Arrays.asList( + Items.WOODEN_PICKAXE, + Items.STONE_PICKAXE, + Items.IRON_PICKAXE, + Items.DIAMOND_PICKAXE + ) + )); + ToolManagerImpl.tag(FabricToolTags.AXES).register(new ModdedToolsVanillaBlocksToolHandler( + Arrays.asList( + Items.WOODEN_AXE, + Items.STONE_AXE, + Items.IRON_AXE, + Items.DIAMOND_AXE + ) + )); + ToolManagerImpl.tag(FabricToolTags.SHOVELS).register(new ModdedToolsVanillaBlocksToolHandler( + Arrays.asList( + Items.WOODEN_SHOVEL, + Items.STONE_SHOVEL, + Items.IRON_SHOVEL, + Items.DIAMOND_SHOVEL + ) + )); + ToolManagerImpl.tag(FabricToolTags.HOES).register(new ModdedToolsVanillaBlocksToolHandler( + Arrays.asList( + Items.WOODEN_HOE, + Items.STONE_HOE, + Items.IRON_HOE, + Items.DIAMOND_HOE + ) + )); + ToolManagerImpl.tag(FabricToolTags.SWORDS).register(new ModdedToolsVanillaBlocksToolHandler( + Arrays.asList( + Items.WOODEN_SWORD, + Items.STONE_SWORD, + Items.IRON_SWORD, + Items.DIAMOND_SWORD + ) + )); + ToolManagerImpl.tag(FabricToolTags.SHEARS).register(new ShearsVanillaBlocksToolHandler()); + } +} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManager.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManager.java deleted file mode 100644 index 5ee834c2c..000000000 --- a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManager.java +++ /dev/null @@ -1,119 +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.tool.attribute; - -import java.util.HashMap; -import java.util.Map; - -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.tag.Tag; - -import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; -import net.fabricmc.fabric.api.util.TriState; - -public final class ToolManager { - public interface Entry { - void setBreakByHand(boolean value); - - void putBreakByTool(Tag tag, int miningLevel); - } - - private static class EntryImpl implements Entry { - @SuppressWarnings("unchecked") - private Tag[] tags = new Tag[0]; - private int[] tagLevels = new int[0]; - private TriState defaultValue = TriState.DEFAULT; - - @Override - public void setBreakByHand(boolean value) { - this.defaultValue = TriState.of(value); - } - - @Override - public void putBreakByTool(Tag tag, int miningLevel) { - for (int i = 0; i < tags.length; i++) { - if (tags[i] == tag) { - tagLevels[i] = miningLevel; - return; - } - } - - //noinspection unchecked - Tag[] newTags = new Tag[tags.length + 1]; - int[] newTagLevels = new int[tagLevels.length + 1]; - System.arraycopy(tags, 0, newTags, 0, tags.length); - System.arraycopy(tagLevels, 0, newTagLevels, 0, tagLevels.length); - newTags[tags.length] = tag; - newTagLevels[tagLevels.length] = miningLevel; - tags = newTags; - tagLevels = newTagLevels; - } - } - - private static final Map entries = new HashMap<>(); - - private ToolManager() { } - - public static Entry entry(Block block) { - return entries.computeIfAbsent(block, (bb) -> new EntryImpl()); - } - - @Deprecated - public static void registerBreakByHand(Block block, boolean value) { - entry(block).setBreakByHand(value); - } - - @Deprecated - public static void registerBreakByTool(Block block, Tag tag, int miningLevel) { - entry(block).putBreakByTool(tag, miningLevel); - } - - //TODO: nullable on user once we have an official @Nullable annotation in - private static int getMiningLevel(ItemStack stack, LivingEntity user) { - if (stack.getItem() instanceof DynamicAttributeTool) { - return ((DynamicAttributeTool) stack.getItem()).getMiningLevel(stack, user); - } else { - return 0; - } - } - - /** - * Hook for ItemStack.isEffectiveOn and similar methods. - */ - //TODO: nullable on user once we have an official @Nullable annotation in - public static TriState handleIsEffectiveOn(ItemStack stack, BlockState state, LivingEntity user) { - EntryImpl entry = entries.get(state.getBlock()); - - if (entry != null) { - Item item = stack.getItem(); - - for (int i = 0; i < entry.tags.length; i++) { - if (item.isIn(entry.tags[i])) { - return TriState.of(getMiningLevel(stack, user) >= entry.tagLevels[i]); - } - } - - return entry.defaultValue; - } else { - return TriState.DEFAULT; - } - } -} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManagerImpl.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManagerImpl.java new file mode 100644 index 000000000..89bf90a9a --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/ToolManagerImpl.java @@ -0,0 +1,267 @@ +/* + * 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; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Objects; + +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.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.tool.attribute.v1.DynamicAttributeTool; +import net.fabricmc.fabric.api.util.TriState; + +public final class ToolManagerImpl { + public interface Entry { + void setBreakByHand(boolean value); + + void putBreakByTool(Tag tag, int miningLevel); + + int getMiningLevel(Tag tag); + } + + private static class EntryImpl implements Entry { + private Tag[] tags = new Tag[0]; + private int[] tagLevels = new int[0]; + private TriState defaultValue = TriState.DEFAULT; + + @Override + public void setBreakByHand(boolean value) { + this.defaultValue = TriState.of(value); + } + + @Override + public void putBreakByTool(Tag tag, int miningLevel) { + tag(tag); // Generate tag entry + + for (int i = 0; i < tags.length; i++) { + if (tags[i] == tag) { + tagLevels[i] = miningLevel; + return; + } + } + + tags = Arrays.copyOf(tags, tags.length + 1); + tags[tags.length - 1] = tag; + + tagLevels = Arrays.copyOf(tagLevels, tagLevels.length + 1); + tagLevels[tagLevels.length - 1] = miningLevel; + } + + @Override + public int getMiningLevel(Tag tag) { + for (int i = 0; i < tags.length; i++) { + if (tags[i] == tag) { + return tagLevels[i]; + } + } + + return -1; + } + } + + private static final Map, Event> HANDLER_MAP = new HashMap<>(); + private static final Event GENERAL_TOOLS_HANDLER = EventFactory.createArrayBacked(ToolHandler.class, ToolManagerImpl::toolHandlerInvoker); + + private static final Map ENTRIES = new IdentityHashMap<>(); + + /** + * Returns a event for the tag provided, creates a new event if it does not exist. + * + * @param tag the tag provided for the tool + * @return the event callback. + */ + public static Event tag(Tag tag) { + for (Map.Entry, Event> entry : HANDLER_MAP.entrySet()) { + if ((tag instanceof Tag.Identified ? (((Tag.Identified) entry.getKey()).getId().equals(((Tag.Identified) tag).getId())) : entry.getKey().equals(tag))) { + return entry.getValue(); + } + } + + HANDLER_MAP.put(tag, EventFactory.createArrayBacked(ToolHandler.class, ToolManagerImpl::toolHandlerInvoker)); + return HANDLER_MAP.get(tag); + } + + /** + * Returns a event used for every tag registered. + * + * @return the event callback. + */ + public static Event general() { + return GENERAL_TOOLS_HANDLER; + } + + private static ToolHandler toolHandlerInvoker(ToolHandler[] toolHandlers) { + return new ToolHandler() { + @Override + public ActionResult isEffectiveOn(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + for (ToolHandler toolHandler : toolHandlers) { + ActionResult effectiveOn = Objects.requireNonNull(toolHandler.isEffectiveOn(tag, state, stack, user)); + + if (effectiveOn != ActionResult.PASS) { + return effectiveOn; + } + } + + return ActionResult.PASS; + } + + @Override + public TypedActionResult getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + for (ToolHandler toolHandler : toolHandlers) { + TypedActionResult miningSpeedMultiplier = Objects.requireNonNull(toolHandler.getMiningSpeedMultiplier(tag, state, stack, user)); + + if (miningSpeedMultiplier.getResult() != ActionResult.PASS) { + return miningSpeedMultiplier; + } + } + + return TypedActionResult.pass(1f); + } + }; + } + + public static Entry entry(Block block) { + return ENTRIES.computeIfAbsent(block, (bb) -> new EntryImpl()); + } + + public static Entry entryNullable(Block block) { + return ENTRIES.get(block); + } + + @Deprecated + public static void registerBreakByHand(Block block, boolean value) { + entry(block).setBreakByHand(value); + } + + @Deprecated + public static void registerBreakByTool(Block block, Tag tag, int miningLevel) { + entry(block).putBreakByTool(tag, miningLevel); + } + + /** + * Hook for ItemStack.isEffectiveOn and similar methods. + */ + //TODO: nullable on user once we have an official @Nullable annotation in + public static boolean handleIsEffectiveOnIgnoresVanilla(BlockState state, ItemStack stack, LivingEntity user) { + for (Map.Entry, Event> eventEntry : HANDLER_MAP.entrySet()) { + if (stack.getItem().isIn(eventEntry.getKey())) { + ActionResult effective = eventEntry.getValue().invoker().isEffectiveOn(eventEntry.getKey(), state, stack, user); + if (effective.isAccepted()) return true; + effective = general().invoker().isEffectiveOn(eventEntry.getKey(), state, stack, user); + if (effective.isAccepted()) return true; + } + } + + EntryImpl entry = (EntryImpl) entryNullable(state.getBlock()); + + return entry != null && entry.defaultValue.get(); + } + + public static float handleBreakingSpeedIgnoresVanilla(BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + float breakingSpeed = 0f; + Tag handledTag = null; + boolean handled = false; + + for (Map.Entry, Event> eventEntry : HANDLER_MAP.entrySet()) { + if (stack.getItem().isIn(eventEntry.getKey())) { + ActionResult effective = eventEntry.getValue().invoker().isEffectiveOn(eventEntry.getKey(), state, stack, user); + + if (effective.isAccepted()) { + TypedActionResult speedMultiplier = Objects.requireNonNull(eventEntry.getValue().invoker().getMiningSpeedMultiplier(eventEntry.getKey(), state, stack, user)); + + if (speedMultiplier.getResult().isAccepted()) { + handled = true; + + if (speedMultiplier.getValue() > breakingSpeed) { + breakingSpeed = speedMultiplier.getValue(); + handledTag = eventEntry.getKey(); + } + } + } + + effective = general().invoker().isEffectiveOn(eventEntry.getKey(), state, stack, user); + + if (effective.isAccepted()) { + TypedActionResult speedMultiplier = Objects.requireNonNull(general().invoker().getMiningSpeedMultiplier(eventEntry.getKey(), state, stack, user)); + + if (speedMultiplier.getResult().isAccepted()) { + handled = true; + + if (speedMultiplier.getValue() > breakingSpeed) { + breakingSpeed = speedMultiplier.getValue(); + handledTag = eventEntry.getKey(); + } + } + } + } + } + + // Give it a default speed if it is not handled. + breakingSpeed = handled ? breakingSpeed : 1f; + + if (stack.getItem() instanceof DynamicAttributeTool) { + breakingSpeed = ((DynamicAttributeTool) stack.getItem()).postProcessMiningSpeed(handledTag, state, stack, user, breakingSpeed, handled); + } + + return breakingSpeed; + } + + /** + * The handler to handle tool speed and effectiveness. + * + * @see net.fabricmc.fabric.impl.tool.attribute.ToolHandlers for default handlers. + */ + public interface ToolHandler { + /** + * Determines whether this handler is active and effective of the tools. + * + * @param tag the tag involved + * @param state the block state to break + * @param stack the item stack breaking the block + * @param user the user involved in breaking the block, null if not applicable. + * @return the result of effectiveness + */ + default ActionResult isEffectiveOn(Tag tag, BlockState state, ItemStack stack, /* @Nullable */ LivingEntity user) { + return ActionResult.PASS; + } + + /** + * Determines the mining speed multiplier of the tools. + * + * @param tag the tag involved + * @param state the block state to break + * @param stack the item stack breaking the block + * @param user the user involved in breaking the block, null if not applicable. + * @return the result of mining speed. + */ + default TypedActionResult getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + return null; + } + } +} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsModdedBlocksToolHandler.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsModdedBlocksToolHandler.java new file mode 100644 index 000000000..766c9deb7 --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsModdedBlocksToolHandler.java @@ -0,0 +1,62 @@ +/* + * 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 net.minecraft.block.BlockState; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.tag.Tag; +import net.minecraft.util.ActionResult; +import net.minecraft.util.TypedActionResult; + +import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; +import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl; + +/** + * This handler handles items that are an subclass of {@link DynamicAttributeTool} by comparing their mining level + * using {@link DynamicAttributeTool#getMiningLevel(Tag, BlockState, ItemStack, LivingEntity)} and the block mining level. + * + *

Only applicable to modded blocks that are registered, as only they have the registered required mining level.

+ */ +public class ModdedToolsModdedBlocksToolHandler implements ToolManagerImpl.ToolHandler { + @Override + public ActionResult isEffectiveOn(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (stack.getItem() instanceof DynamicAttributeTool) { + ToolManagerImpl.Entry entry = ToolManagerImpl.entryNullable(state.getBlock()); + + if (entry != null) { + int miningLevel = ((DynamicAttributeTool) stack.getItem()).getMiningLevel(tag, state, stack, user); + int requiredMiningLevel = entry.getMiningLevel(tag); + + return requiredMiningLevel >= 0 && miningLevel >= 0 && miningLevel >= requiredMiningLevel ? ActionResult.SUCCESS : ActionResult.PASS; + } + } + + return ActionResult.PASS; + } + + @Override + public TypedActionResult getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (stack.getItem() instanceof DynamicAttributeTool) { + float multiplier = ((DynamicAttributeTool) stack.getItem()).getMiningSpeedMultiplier(tag, state, stack, user); + if (multiplier != 1.0F) return TypedActionResult.success(multiplier); + } + + return TypedActionResult.pass(1.0F); + } +} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsVanillaBlocksToolHandler.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsVanillaBlocksToolHandler.java new file mode 100644 index 000000000..b80cd9d56 --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ModdedToolsVanillaBlocksToolHandler.java @@ -0,0 +1,100 @@ +/* + * 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 java.util.List; + +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.minecraft.util.TypedActionResult; + +import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; +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 + * from {@link DynamicAttributeTool#getMiningLevel(Tag, BlockState, ItemStack, LivingEntity)}. + * + *

Only applicable to blocks that are vanilla or share the material that is handled by their vanilla tool.

+ */ +public class ModdedToolsVanillaBlocksToolHandler implements ToolManagerImpl.ToolHandler { + private final List vanillaItems; + + public ModdedToolsVanillaBlocksToolHandler(List vanillaItems) { + this.vanillaItems = vanillaItems; + } + + private ToolItem getVanillaItem(int miningLevel) { + if (miningLevel < 0) return (ToolItem) vanillaItems.get(0); + if (miningLevel >= vanillaItems.size()) return (ToolItem) vanillaItems.get(vanillaItems.size() - 1); + return (ToolItem) vanillaItems.get(miningLevel); + } + + @Override + public ActionResult isEffectiveOn(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (stack.getItem() instanceof DynamicAttributeTool) { + if (ToolManagerImpl.entryNullable(state.getBlock()) != null) { + // Block is a modded block, and we should ignore it + return ActionResult.PASS; + } + + // Gets the mining level from our modded tool + int miningLevel = ((DynamicAttributeTool) stack.getItem()).getMiningLevel(tag, state, stack, user); + if (miningLevel < 0) return ActionResult.PASS; + + ToolItem vanillaItem = getVanillaItem(miningLevel); + boolean effective = vanillaItem.isEffectiveOn(state) || vanillaItem.getMiningSpeedMultiplier(new ItemStack(vanillaItem), state) != 1.0F; + return effective ? ActionResult.SUCCESS : ActionResult.PASS; + } + + return ActionResult.PASS; + } + + @Override + public TypedActionResult getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (stack.getItem() instanceof DynamicAttributeTool) { + // Gets the mining level from our modded tool + int miningLevel = ((DynamicAttributeTool) stack.getItem()).getMiningLevel(tag, state, stack, user); + if (miningLevel < 0) return null; + + float moddedToolSpeed = ((DynamicAttributeTool) stack.getItem()).getMiningSpeedMultiplier(tag, state, stack, user); + ToolItem firstVanillaItem = getVanillaItem(miningLevel); + ToolItem secondVanillaItem = getVanillaItem(miningLevel + 1 >= vanillaItems.size() ? vanillaItems.size() - 2 : miningLevel + 1); + + float firstSpeed = firstVanillaItem.getMiningSpeedMultiplier(new ItemStack(firstVanillaItem, 1), state); + float secondSpeed = secondVanillaItem.getMiningSpeedMultiplier(new ItemStack(secondVanillaItem, 1), state); + boolean hasForcedSpeed = firstSpeed == secondSpeed && firstSpeed >= 1.0F; + + // Has forced speed, which as actions like swords breaking cobwebs. + if (hasForcedSpeed) { + return secondSpeed != 1.0F ? TypedActionResult.success(secondSpeed) : TypedActionResult.pass(1.0F); + } + + // We adjust the mining speed according to the ratio for the closest tool. + float adjustedMiningSpeed = firstSpeed / firstVanillaItem.getMaterial().getMiningSpeedMultiplier() * moddedToolSpeed; + return adjustedMiningSpeed != 1.0F ? TypedActionResult.success(adjustedMiningSpeed) : TypedActionResult.pass(1.0F); + } + + return TypedActionResult.pass(1.0F); + } +} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ShearsVanillaBlocksToolHandler.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ShearsVanillaBlocksToolHandler.java new file mode 100644 index 000000000..461fd0f65 --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/ShearsVanillaBlocksToolHandler.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.tool.attribute.handlers; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.ShearsItem; +import net.minecraft.tag.Tag; +import net.minecraft.util.ActionResult; +import net.minecraft.util.TypedActionResult; + +import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +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 + * is a subclass of {@link ShearsItem}. + * + *

Only applicable to items that are not a subclass of {@link DynamicAttributeTool}

+ *

Only applicable to blocks that are vanilla or share the material that is handled by their vanilla tool.

+ */ +public class ShearsVanillaBlocksToolHandler implements ToolManagerImpl.ToolHandler { + private final Item vanillaItem = Items.SHEARS; + + @Override + public ActionResult isEffectiveOn(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (ToolManagerImpl.entryNullable(state.getBlock()) != null) { + // Block is a modded block, and we should ignore it + return ActionResult.PASS; + } + + if (!(stack.getItem() instanceof DynamicAttributeTool)) { + if (!(stack.getItem() instanceof ShearsItem)) { + return vanillaItem.isEffectiveOn(state) || vanillaItem.getMiningSpeedMultiplier(new ItemStack(vanillaItem), state) != 1.0F ? ActionResult.SUCCESS : ActionResult.PASS; + } else { + return stack.getItem().isEffectiveOn(state) || stack.getItem().getMiningSpeedMultiplier(stack, state) != 1.0F ? ActionResult.SUCCESS : ActionResult.PASS; + } + } + + return ActionResult.PASS; + } + + @Override + public TypedActionResult getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + float speed = 1.0F; + + if (!(stack.getItem() instanceof DynamicAttributeTool)) { + if (!(stack.getItem() instanceof ShearsItem)) { + speed = vanillaItem.getMiningSpeedMultiplier(new ItemStack(vanillaItem), state); + } else { + speed = stack.getItem().getMiningSpeedMultiplier(stack, state); + } + } + + return speed != 1.0F ? TypedActionResult.success(speed) : TypedActionResult.pass(1.0F); + } +} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/VanillaToolsModdedBlocksToolHandler.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/VanillaToolsModdedBlocksToolHandler.java new file mode 100644 index 000000000..b47cb151a --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/impl/tool/attribute/handlers/VanillaToolsModdedBlocksToolHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.tool.attribute.handlers; + +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.item.ToolMaterial; +import net.minecraft.tag.Tag; +import net.minecraft.util.ActionResult; +import net.minecraft.util.TypedActionResult; + +import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; +import net.fabricmc.fabric.impl.tool.attribute.ToolManagerImpl; + +/** + * This handler handles items that are not a subclass of {@link DynamicAttributeTool} by + * comparing their mining level using {@link ToolMaterial#getMiningLevel()} and the block mining level. + * + *

Only applicable to modded blocks that are registered, as only they have the registered required mining level.

+ */ +public class VanillaToolsModdedBlocksToolHandler implements ToolManagerImpl.ToolHandler { + @Override + public ActionResult isEffectiveOn(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (!(stack.getItem() instanceof DynamicAttributeTool)) { + ToolManagerImpl.Entry entry = ToolManagerImpl.entryNullable(state.getBlock()); + + if (entry != null) { + int miningLevel = stack.getItem() instanceof ToolItem ? ((ToolItem) stack.getItem()).getMaterial().getMiningLevel() : -1; + int requiredMiningLevel = entry.getMiningLevel(tag); + return requiredMiningLevel >= 0 && miningLevel >= 0 && miningLevel >= requiredMiningLevel ? ActionResult.SUCCESS : ActionResult.PASS; + } + } + + return ActionResult.PASS; + } + + @Override + public TypedActionResult getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (!(stack.getItem() instanceof DynamicAttributeTool)) { + float multiplier = stack.getItem() instanceof ToolItem ? ((ToolItem) stack.getItem()).getMaterial().getMiningSpeedMultiplier() : stack.getItem().getMiningSpeedMultiplier(stack, state); + if (multiplier != 1.0F) return TypedActionResult.success(multiplier); + } + + return TypedActionResult.pass(1.0F); + } +} diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinItemStack.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinItemStack.java index 37b65d9dd..ba93e3c1c 100644 --- a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinItemStack.java +++ b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinItemStack.java @@ -32,40 +32,27 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; -import net.fabricmc.fabric.api.util.TriState; +import net.fabricmc.fabric.api.tool.attribute.v1.ToolManager; import net.fabricmc.fabric.impl.tool.attribute.AttributeManager; -import net.fabricmc.fabric.impl.tool.attribute.ToolManager; @Mixin(ItemStack.class) public abstract class MixinItemStack { @Shadow public abstract Item getItem(); - @Inject(at = @At("HEAD"), method = "isEffectiveOn", cancellable = true) + @Inject(at = @At("RETURN"), method = "isEffectiveOn", cancellable = true) public void isEffectiveOn(BlockState state, CallbackInfoReturnable info) { - TriState triState = ToolManager.handleIsEffectiveOn((ItemStack) (Object) this, state, null); - - if (triState != TriState.DEFAULT) { - info.setReturnValue(triState.get()); - info.cancel(); + if (!info.getReturnValueZ()) { + info.setReturnValue(ToolManager.handleIsEffectiveOnIgnoresVanilla(state, (ItemStack) (Object) this, null)); } } - @Inject(at = @At("HEAD"), method = "getMiningSpeedMultiplier", cancellable = true) + @Inject(at = @At("RETURN"), method = "getMiningSpeedMultiplier", cancellable = true) public void getMiningSpeedMultiplier(BlockState state, CallbackInfoReturnable info) { - TriState triState = ToolManager.handleIsEffectiveOn((ItemStack) (Object) this, state, null); + float customSpeed = ToolManager.handleBreakingSpeedIgnoresVanilla(state, (ItemStack) (Object) this, null); - if (triState != TriState.DEFAULT) { - Item item = this.getItem(); - float miningSpeed; - - if (item instanceof DynamicAttributeTool) { - miningSpeed = ((DynamicAttributeTool) this.getItem()).getMiningSpeedMultiplier((ItemStack) (Object) this, null); - } else { - return; - } - - info.setReturnValue(triState.get() ? miningSpeed : 1.0F); + if (info.getReturnValueF() < customSpeed) { + info.setReturnValue(customSpeed); } } diff --git a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinPlayerInventory.java b/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinPlayerInventory.java deleted file mode 100644 index 169a6249c..000000000 --- a/fabric-tool-attribute-api-v1/src/main/java/net/fabricmc/fabric/mixin/tool/attribute/MixinPlayerInventory.java +++ /dev/null @@ -1,73 +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.tool.attribute; - -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.item.ItemStack; -import net.minecraft.util.collection.DefaultedList; - -import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; -import net.fabricmc.fabric.api.util.TriState; -import net.fabricmc.fabric.impl.tool.attribute.ToolManager; - -@Mixin(PlayerInventory.class) -public abstract class MixinPlayerInventory { - @Shadow - @Final - public DefaultedList main; - - @Shadow - public int selectedSlot; - - @Shadow - @Final - public PlayerEntity player; - - @Shadow - public abstract ItemStack getStack(int int_1); - - @Inject(method = "isUsingEffectiveTool", at = @At("HEAD"), cancellable = true) - public void actMiningLevel(BlockState state, CallbackInfoReturnable info) { - ItemStack stack = this.getStack(this.selectedSlot); - - if (stack.getItem() instanceof DynamicAttributeTool) { - TriState ret = ToolManager.handleIsEffectiveOn(stack, state, player); - - if (ret != TriState.DEFAULT) { - info.setReturnValue(ret.get()); - } - } - } - - @Inject(method = "getBlockBreakingSpeed", at = @At("HEAD"), cancellable = true) - public void actMiningSleed(BlockState state, CallbackInfoReturnable info) { - ItemStack stack = this.main.get(this.selectedSlot); - - if (stack.getItem() instanceof DynamicAttributeTool) { - info.setReturnValue(((DynamicAttributeTool) stack.getItem()).getMiningSpeedMultiplier(stack, player)); - } - } -} diff --git a/fabric-tool-attribute-api-v1/src/main/resources/fabric-tool-attribute-api-v1.mixins.json b/fabric-tool-attribute-api-v1/src/main/resources/fabric-tool-attribute-api-v1.mixins.json index b2b1ad5d9..70792f2f3 100644 --- a/fabric-tool-attribute-api-v1/src/main/resources/fabric-tool-attribute-api-v1.mixins.json +++ b/fabric-tool-attribute-api-v1/src/main/resources/fabric-tool-attribute-api-v1.mixins.json @@ -4,8 +4,7 @@ "compatibilityLevel": "JAVA_8", "mixins": [ "MixinItemStack", - "MixinLivingEntity", - "MixinPlayerInventory" + "MixinLivingEntity" ], "injectors": { "defaultRequire": 1 diff --git a/fabric-tool-attribute-api-v1/src/main/resources/fabric.mod.json b/fabric-tool-attribute-api-v1/src/main/resources/fabric.mod.json index 2a9700d87..5211c0d89 100644 --- a/fabric-tool-attribute-api-v1/src/main/resources/fabric.mod.json +++ b/fabric-tool-attribute-api-v1/src/main/resources/fabric.mod.json @@ -20,6 +20,11 @@ "fabric-api-base": "*", "fabric-tag-extensions-v0": "*" }, + "entrypoints": { + "main": [ + "net.fabricmc.fabric.impl.tool.attribute.ToolHandlers" + ] + }, "description": "Dynamic atttributes for tools.", "mixins": [ "fabric-tool-attribute-api-v1.mixins.json" diff --git a/fabric-tool-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/tool/attribute/ToolAttributeTest.java b/fabric-tool-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/tool/attribute/ToolAttributeTest.java new file mode 100644 index 000000000..2ee4441d9 --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/testmod/java/net/fabricmc/fabric/test/tool/attribute/ToolAttributeTest.java @@ -0,0 +1,74 @@ +/* + * 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.tool.attribute; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.MaterialColor; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.sound.BlockSoundGroup; +import net.minecraft.tag.Tag; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricMaterialBuilder; +import net.fabricmc.fabric.api.tool.attribute.v1.DynamicAttributeTool; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; + +public class ToolAttributeTest implements ModInitializer { + @Override + public void onInitialize() { + // Register a custom shovel that has a mining level of 2 (iron) dynamically. + Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "test_shovel"), new TestShovel(new Item.Settings())); + // Register a block that requires a shovel that is as strong or stronger than an iron one. + Block block = Registry.register(Registry.BLOCK, new Identifier("fabric-tool-attribute-api-v1-testmod", "hardened_block"), + new Block(FabricBlockSettings.of(new FabricMaterialBuilder(MaterialColor.SAND).requiresTool().build(), MaterialColor.STONE) + .breakByTool(FabricToolTags.SHOVELS, 2) + .strength(0.6F) + .sounds(BlockSoundGroup.GRAVEL))); + Registry.register(Registry.ITEM, new Identifier("fabric-tool-attribute-api-v1-testmod", "hardened_block"), new BlockItem(block, new Item.Settings())); + } + + private static class TestShovel extends Item implements DynamicAttributeTool { + private TestShovel(Settings settings) { + super(settings); + } + + @Override + public int getMiningLevel(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (tag.equals(FabricToolTags.SHOVELS)) { + return 2; + } + + return 0; + } + + @Override + public float getMiningSpeedMultiplier(Tag tag, BlockState state, ItemStack stack, LivingEntity user) { + if (tag.equals(FabricToolTags.SHOVELS)) { + return 10.0F; + } + + return 1.0F; + } + } +} diff --git a/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/blockstates/hardened_block.json b/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/blockstates/hardened_block.json new file mode 100644 index 000000000..fab9f0a06 --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/blockstates/hardened_block.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "block/gravel" } + } +} diff --git a/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/hardened_block.json b/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/hardened_block.json new file mode 100644 index 000000000..2a38b147b --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/hardened_block.json @@ -0,0 +1,3 @@ +{ + "parent": "block/gravel" +} diff --git a/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/test_shovel.json b/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/test_shovel.json new file mode 100644 index 000000000..f0f76884b --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/testmod/resources/assets/fabric-tool-attribute-api-v1-testmod/models/item/test_shovel.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "item/apple" + } +} diff --git a/fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric-tool-attribute-api-v1-testmod/loot_tables/blocks/hardened_block.json b/fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric-tool-attribute-api-v1-testmod/loot_tables/blocks/hardened_block.json new file mode 100644 index 000000000..44936390f --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric-tool-attribute-api-v1-testmod/loot_tables/blocks/hardened_block.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "fabric-tool-attribute-api-v1-testmod:hardened_block" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} diff --git a/fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric/tags/items/shovels.json b/fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric/tags/items/shovels.json new file mode 100644 index 000000000..e58d9239c --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/testmod/resources/data/fabric/tags/items/shovels.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "fabric-tool-attribute-api-v1-testmod:test_shovel" + ] +} diff --git a/fabric-tool-attribute-api-v1/src/testmod/resources/fabric.mod.json b/fabric-tool-attribute-api-v1/src/testmod/resources/fabric.mod.json new file mode 100644 index 000000000..f77a1cecc --- /dev/null +++ b/fabric-tool-attribute-api-v1/src/testmod/resources/fabric.mod.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": 1, + "id": "fabric-tool-attribute-api-v1-testmod", + "name": "Fabric Tool Attribute API (v1) Test Mod", + "version": "1.0.0", + "environment": "*", + "license": "Apache-2.0", + "depends": { + "fabric-tool-attribute-api-v1": "*" + }, + "entrypoints": { + "main": [ + "net.fabricmc.fabric.test.tool.attribute.ToolAttributeTest" + ] + } +}