Allow canceling the NBT update animation and block break reset ()

* Allow canceling the NBT update animation

* Remove the event and introduce FabricItem instead

* Remove FabricItem and add an item setting instead

* Revert "Remove FabricItem and add an item setting instead"

This reverts commit a93205c927.

After some discussion, it was agreed upon internally that an interface is better than item settings for behavior extensions.

* Add allowContinuingBlockBreaking

* Update ClientPlayerInteractionManagerMixin comment and inject name
This commit is contained in:
Technici4n 2021-12-03 14:34:34 +01:00 committed by modmuss50
parent 3b82842e3d
commit 691a79b5ca
8 changed files with 292 additions and 5 deletions
fabric-item-api-v1/src

View file

@ -0,0 +1,64 @@
/*
* 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.item.v1;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
/**
* General-purpose Fabric-provided extensions for {@link Item} subclasses.
*
* <p>Note: This interface is automatically implemented on all items via Mixin,
* however it has to be implemented explicitly by modders to be able to override functions.
* In the future, it is planned that {@code public class Item implements FabricItem} will be visible in a development environment.
*
* <p>Note to maintainers: Functions should only be added to this interface if they are general-purpose enough,
* to be evaluated on a case-by-case basis. Otherwise they are better suited for more specialized APIs.
*/
public interface FabricItem {
/**
* When the NBT of an item stack in the main hand or off hand changes, vanilla runs an "update animation".
* This function is called on the client side when the NBT or count of the stack has changed, but not the item,
* and returning false cancels this animation.
*
* @param player the current player; this may be safely cast to {@link ClientPlayerEntity} in client-only code
* @param hand the hand; this function applies both to the main hand and the off hand
* @param oldStack the previous stack, of this item
* @param newStack the new stack, also of this item
* @return true to run the vanilla animation, false to cancel it.
*/
default boolean allowNbtUpdateAnimation(PlayerEntity player, Hand hand, ItemStack oldStack, ItemStack newStack) {
return true;
}
/**
* When the NBT of the selected stack changes, block breaking progress is reset.
* This function is called when the NBT of the selected stack has changed,
* and returning true allows the block breaking progress to continue.
*
* @param player the player breaking the block
* @param oldStack the previous stack, of this item
* @param newStack the new stack, also of this item
* @return true to allow continuing block breaking, false to reset the progress.
*/
default boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
return false;
}
}

View file

@ -26,11 +26,12 @@ import net.minecraft.item.Item;
import net.fabricmc.fabric.api.item.v1.CustomDamageHandler;
import net.fabricmc.fabric.api.item.v1.EquipmentSlotProvider;
import net.fabricmc.fabric.api.item.v1.FabricItem;
import net.fabricmc.fabric.impl.item.FabricItemInternals;
import net.fabricmc.fabric.impl.item.ItemExtensions;
@Mixin(Item.class)
abstract class ItemMixin implements ItemExtensions {
abstract class ItemMixin implements ItemExtensions, FabricItem {
@Unique
private EquipmentSlotProvider equipmentSlotProvider;

View file

@ -0,0 +1,68 @@
/*
* 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.item.client;
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.ModifyVariable;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.fabricmc.fabric.api.item.v1.FabricItem;
@Mixin(ClientPlayerInteractionManager.class)
public class ClientPlayerInteractionManagerMixin {
@Shadow
@Final
private MinecraftClient client;
@Shadow
private BlockPos currentBreakingPos;
@Shadow
private ItemStack selectedStack;
/**
* Allows a FabricItem to continue block breaking progress even if the count or nbt changed.
* For this, we inject after vanilla decided that the stack was "not unchanged", and we set if back to "unchanged"
* if the item wishes to continue mining.
*/
@ModifyVariable(
at = @At(
value = "INVOKE",
target = "net/minecraft/util/math/BlockPos.equals(Ljava/lang/Object;)Z"
),
method = "isCurrentlyBreaking",
index = 3
)
private boolean fabricItemContinueBlockBreakingInject(boolean stackUnchanged) {
if (!stackUnchanged) {
// The stack changed and vanilla is about to cancel block breaking progress. Check if the item wants to continue block breaking instead.
ItemStack oldStack = this.selectedStack;
ItemStack newStack = this.client.player.getMainHandStack();
if (oldStack.isOf(newStack.getItem()) && ((FabricItem) oldStack.getItem()).allowContinuingBlockBreaking(this.client.player, oldStack, newStack)) {
stackUnchanged = true;
}
}
return stackUnchanged;
}
}

View file

@ -0,0 +1,68 @@
/*
* 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.item.client;
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.CallbackInfo;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.item.HeldItemRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.fabricmc.fabric.api.item.v1.FabricItem;
/**
* Allow canceling the held item update animation if {@link FabricItem#allowNbtUpdateAnimation} returns false.
*/
@Mixin(HeldItemRenderer.class)
public class HeldItemRendererMixin {
@Shadow
private ItemStack mainHand;
@Shadow
private ItemStack offHand;
@Shadow
@Final
private MinecraftClient client;
@Inject(method = "updateHeldItems", at = @At("HEAD"))
private void modifyProgressAnimation(CallbackInfo ci) {
// Modify main hand
ItemStack newMainStack = client.player.getMainHandStack();
if (mainHand.getItem() == newMainStack.getItem()) {
if (!((FabricItem) mainHand.getItem()).allowNbtUpdateAnimation(client.player, Hand.MAIN_HAND, mainHand, newMainStack)) {
mainHand = newMainStack;
}
}
// Modify off hand
ItemStack newOffStack = client.player.getOffHandStack();
if (offHand.getItem() == newOffStack.getItem()) {
if (!((FabricItem) offHand.getItem()).allowNbtUpdateAnimation(client.player, Hand.OFF_HAND, offHand, newOffStack)) {
offHand = newOffStack;
}
}
}
}

View file

@ -8,6 +8,8 @@
"LivingEntityMixin"
],
"client": [
"client.ClientPlayerInteractionManagerMixin",
"client.HeldItemRendererMixin",
"client.ItemStackMixin"
],
"injectors": {

View file

@ -0,0 +1,30 @@
/*
* 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.item;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ModInitializer;
public class ItemUpdateAnimationTest implements ModInitializer {
@Override
public void onInitialize() {
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "updating_allowed"), new UpdatingItem(true));
Registry.register(Registry.ITEM, new Identifier("fabric-item-api-v1-testmod", "updating_disallowed"), new UpdatingItem(false));
}
}

View file

@ -0,0 +1,55 @@
/*
* 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.item;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import net.fabricmc.fabric.api.item.v1.FabricItem;
public class UpdatingItem extends Item implements FabricItem {
private final boolean allowUpdateAnimation;
public UpdatingItem(boolean allowUpdateAnimation) {
super(new Settings().group(ItemGroup.MISC));
this.allowUpdateAnimation = allowUpdateAnimation;
}
@Override
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
if (!world.isClient) {
NbtCompound tag = stack.getOrCreateNbt();
tag.putLong("ticks", tag.getLong("ticks")+1);
}
}
@Override
public boolean allowNbtUpdateAnimation(PlayerEntity player, Hand hand, ItemStack originalStack, ItemStack updatedStack) {
return allowUpdateAnimation;
}
@Override
public boolean allowContinuingBlockBreaking(PlayerEntity player, ItemStack oldStack, ItemStack newStack) {
return true; // set to false and you won't be able to break a block in survival with this item
}
}

View file

@ -10,13 +10,12 @@
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.item.FabricItemSettingsTests"
"net.fabricmc.fabric.test.item.CustomDamageTest",
"net.fabricmc.fabric.test.item.FabricItemSettingsTests",
"net.fabricmc.fabric.test.item.ItemUpdateAnimationTest"
],
"client": [
"net.fabricmc.fabric.test.item.client.TooltipTests"
],
"main": [
"net.fabricmc.fabric.test.item.CustomDamageTest"
]
}
}