mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-08 21:14:41 -04:00
Allow canceling the NBT update animation and block break reset (#1790)
* 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:
parent
3b82842e3d
commit
691a79b5ca
8 changed files with 292 additions and 5 deletions
fabric-item-api-v1/src
main
java/net/fabricmc/fabric
api/item/v1
mixin/item
resources
testmod
java/net/fabricmc/fabric/test/item
resources
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
"LivingEntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"client.ClientPlayerInteractionManagerMixin",
|
||||
"client.HeldItemRendererMixin",
|
||||
"client.ItemStackMixin"
|
||||
],
|
||||
"injectors": {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue