The fix for MC-257875 which was to add a check to see if the itemstack had a damageable item failed to take into account the "Unbreakable" nbt tag. Currently, if you give yourself an unbreakable flint and steel via
/give @s flint_and_steel[unbreakable={}]
and ignite a creeper with it in survival mode, the itemstack is completely removed due to the stack size being shrunk by one in Creeper#mobInteract.
The proper fix I believe is shown here in the Paper repository.
Linked issues
is duplicated by 4
relates to 1
Comments 10
The following code analysis below is inefficient for any future updates, please regard to the paper fix.
Code Analysis:
The following is based on a decompiled version of Minecraft 1.20.1 using MCP-Reborn.
net.minecraft.world.entity.monster.Creeper.java // Original
public class Creeper extends Monster implements PowerableMob {
...
protected InteractionResult mobInteract(Player p_32301_, InteractionHand p_32302_) {
ItemStack itemstack = p_32301_.getItemInHand(p_32302_);
if (itemstack.is(ItemTags.CREEPER_IGNITERS)) {
SoundEvent soundevent = itemstack.is(Items.FIRE_CHARGE) ? SoundEvents.FIRECHARGE_USE : SoundEvents.FLINTANDSTEEL_USE;
this.level().playSound(p_32301_, this.getX(), this.getY(), this.getZ(), soundevent, this.getSoundSource(), 1.0 F, this.random.nextFloat() * 0.4 F + 0.8 F);
if (!this.level().isClientSide) {
this.ignite();
if (!itemstack.isDamageableItem()) {
itemstack.shrink(1);
} else {
itemstack.hurtAndBreak(1, p_32301_, (p_32290_) - > {
p_32290_.broadcastBreakEvent(p_32302_);
});
}
} return InteractionResult.sidedSuccess(this.level().isClientSide);
} else {
return super.mobInteract(p_32301_, p_32302_);
}
}
...
}
As reported by the OP, the ItemStack is being shrunken because ItemStack#isDamageableItem determines if it can be damaged (durability) or it is unbreakable. But, it never checks if the max itemstack is greater than one. Thus consuming the unbreakable flint and steel.
Fix:
net.minecraft.world.entity.monster.Creeper.java // Updated
public class Creeper extends Monster implements PowerableMob {
...
protected InteractionResult mobInteract(Player p_32301_, InteractionHand p_32302_) {
ItemStack itemstack = p_32301_.getItemInHand(p_32302_);
if (itemstack.is(ItemTags.CREEPER_IGNITERS)) {
SoundEvent soundevent = itemstack.is(Items.FIRE_CHARGE) ? SoundEvents.FIRECHARGE_USE : SoundEvents.FLINTANDSTEEL_USE;
this.level().playSound(p_32301_, this.getX(), this.getY(), this.getZ(), soundevent, this.getSoundSource(), 1.0 F, this.random.nextFloat() * 0.4 F + 0.8 F);
if (!this.level().isClientSide) {
this.ignite();
if (!itemstack.isDamageableItem() && itemstack.getMaxStackSize() > 1) {
itemstack.shrink(1);
} else {
itemstack.hurtAndBreak(1, p_32301_, (p_32290_) - > {
p_32290_.broadcastBreakEvent(p_32302_);
});
}
} return InteractionResult.sidedSuccess(this.level().isClientSide);
} else {
return super.mobInteract(p_32301_, p_32302_);
}
}
...
}
This simple fix, checks if the itemstack used has a great stack size than 1, if so, then we should consume the itemstack used.
That is not a good fix. There are items that should be consumed and only have a stack size of 1, but are also not damageable. The correct fix is to change that conditional to instead check if them stack's item type has durability. If it does not have durability, consume the item, if it does, run the hurtAndBreak logic which has its own check for "Unbreakable". You can see how we fixed this issue on Paper.
You're right, I completely forgot about optimizing the condition for any future ignitable items, the paper commit does fix the issue at hand. I've updated my comment to divert to the proper fix for the minecraft bug.
@unknown, the latest version is 1.21.3. Despite there being minimal differences, please confirm for the latest version. 🙂
Can confirm this in 1.20.1