mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-14 19:25:23 -05:00
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:
parent
e14f9c8f05
commit
d2f76b0fdf
40 changed files with 977 additions and 8 deletions
12
fabric-mining-level-api-v1/build.gradle
Normal file
12
fabric-mining-level-api-v1/build.gradle
Normal 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')
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 |
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-mining-level-api-v1-testmod:needs_shears"
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-mining-level-api-v1-testmod:needs_axe"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-mining-level-api-v1-testmod:needs_hoe"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-mining-level-api-v1-testmod:needs_netherite_pickaxe"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-mining-level-api-v1-testmod:needs_shovel"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-mining-level-api-v1-testmod:needs_stone_sword"
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -9,5 +9,6 @@ dependencies {
|
|||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base',
|
||||
'fabric-mining-level-api-v1',
|
||||
'fabric-tag-extensions-v0'
|
||||
])
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
"depends": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"fabric-api-base": "*",
|
||||
"fabric-mining-level-api-v1": "*",
|
||||
"fabric-tag-extensions-v0": "*"
|
||||
},
|
||||
"entrypoints": {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-tool-attribute-api-v1-testmod:fake_axe"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-tool-attribute-api-v1-testmod:fake_hoe"
|
||||
]
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-tool-attribute-api-v1-testmod:fake_pickaxe",
|
||||
"fabric-tool-attribute-api-v1-testmod:test_pickaxe"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-tool-attribute-api-v1-testmod:fake_shears"
|
||||
]
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-tool-attribute-api-v1-testmod:fake_shovel",
|
||||
"fabric-tool-attribute-api-v1-testmod:test_shovel"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"fabric-tool-attribute-api-v1-testmod:fake_sword"
|
||||
]
|
||||
}
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue