mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-12 09:51:12 +01:00
c3749a2358
CraftBukkit (and Minecraft as well in some cases, such as when getting an item from the creative inventory menu) will omit the Damage tag when it is zero. However, minecraft will add the tag in some situations nevertheless, such as when loading the ItemStack, or when explictly setting the item undamaged. These items (with and without the Damage tag for undamaged items) will be considered as different by minecraft and CraftBukkit in various situations, even though they should not. In CraftBukkit these items will actually only be considered unsimilar if the items' metadata is not 'empty' (if it contains other additional metadata, such as enchantments, etc.). If the item's tag is empty after removing the Damage tag, it gets completely removed. The setRepairCost function was adapted to behave in the same way (removal of the tag if it becomes empty).
342 lines
16 KiB
Diff
342 lines
16 KiB
Diff
--- a/net/minecraft/server/ItemStack.java
|
|
+++ b/net/minecraft/server/ItemStack.java
|
|
@@ -15,6 +15,24 @@
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
+// CraftBukkit start
|
|
+import com.mojang.datafixers.Dynamic;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.TreeType;
|
|
+import org.bukkit.block.BlockState;
|
|
+import org.bukkit.craftbukkit.block.CraftBlock;
|
|
+import org.bukkit.craftbukkit.block.CraftBlockState;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
|
+import org.bukkit.entity.Player;
|
|
+import org.bukkit.event.block.BlockFertilizeEvent;
|
|
+import org.bukkit.event.player.PlayerItemDamageEvent;
|
|
+import org.bukkit.event.world.StructureGrowEvent;
|
|
+// CraftBukkit end
|
|
+
|
|
public final class ItemStack {
|
|
|
|
private static final Logger c = LogManager.getLogger();
|
|
@@ -49,25 +67,49 @@
|
|
this.E();
|
|
}
|
|
|
|
+ // Called to run this stack through the data converter to handle older storage methods and serialized items
|
|
+ public void convertStack() {
|
|
+ if (false && MinecraftServer.getServer() != null) { // Skip for now, causes more issues than it solves
|
|
+ // Don't convert some things - both the old and new data values are valid
|
|
+ // Conversion would make getting then impossible
|
|
+ if (this.item == Blocks.PUMPKIN.getItem() || this.item == Blocks.GRASS.getItem() || this.item == Blocks.SNOW.getItem()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ NBTTagCompound savedStack = new NBTTagCompound();
|
|
+ this.save(savedStack);
|
|
+ savedStack = (NBTTagCompound) MinecraftServer.getServer().dataConverterManager.update(DataConverterTypes.ITEM_STACK, new Dynamic(DynamicOpsNBT.a, savedStack), -1, CraftMagicNumbers.INSTANCE.getDataVersion()).getValue();
|
|
+ this.load(savedStack);
|
|
+ }
|
|
+ }
|
|
+
|
|
private void E() {
|
|
+ if (this.h && this == ItemStack.a) throw new AssertionError("TRAP"); // CraftBukkit
|
|
this.h = false;
|
|
this.h = this.isEmpty();
|
|
}
|
|
|
|
- private ItemStack(NBTTagCompound nbttagcompound) {
|
|
+ // CraftBukkit - break into own method
|
|
+ private void load(NBTTagCompound nbttagcompound) {
|
|
Item item = (Item) IRegistry.ITEM.get(new MinecraftKey(nbttagcompound.getString("id")));
|
|
|
|
this.item = item == null ? Items.AIR : item;
|
|
this.count = nbttagcompound.getByte("Count");
|
|
if (nbttagcompound.hasKeyOfType("tag", 10)) {
|
|
- this.tag = nbttagcompound.getCompound("tag");
|
|
- this.getItem().a(nbttagcompound);
|
|
+ // CraftBukkit start - make defensive copy as this data may be coming from the save thread
|
|
+ this.tag = (NBTTagCompound) nbttagcompound.getCompound("tag").clone();
|
|
+ this.getItem().a(this.tag);
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
if (this.getItem().usesDurability()) {
|
|
this.setDamage(this.getDamage());
|
|
}
|
|
+ }
|
|
|
|
+ private ItemStack(NBTTagCompound nbttagcompound) {
|
|
+ this.load(nbttagcompound);
|
|
+ // CraftBukkit end
|
|
this.E();
|
|
}
|
|
|
|
@@ -97,7 +139,7 @@
|
|
return this.h ? Items.AIR : this.item;
|
|
}
|
|
|
|
- public EnumInteractionResult placeItem(ItemActionContext itemactioncontext) {
|
|
+ public EnumInteractionResult placeItem(ItemActionContext itemactioncontext, EnumHand enumhand) { // CraftBukkit - add hand
|
|
EntityHuman entityhuman = itemactioncontext.getEntity();
|
|
BlockPosition blockposition = itemactioncontext.getClickPosition();
|
|
ShapeDetectorBlock shapedetectorblock = new ShapeDetectorBlock(itemactioncontext.getWorld(), blockposition, false);
|
|
@@ -105,12 +147,150 @@
|
|
if (entityhuman != null && !entityhuman.abilities.mayBuild && !this.b(itemactioncontext.getWorld().F(), shapedetectorblock)) {
|
|
return EnumInteractionResult.PASS;
|
|
} else {
|
|
+ // CraftBukkit start - handle all block place event logic here
|
|
+ NBTTagCompound oldData = this.getTagClone();
|
|
+ int oldCount = this.getCount();
|
|
+ World world = itemactioncontext.getWorld();
|
|
+
|
|
+ if (!(this.getItem() instanceof ItemBucket)) { // if not bucket
|
|
+ world.captureBlockStates = true;
|
|
+ // special case bonemeal
|
|
+ if (this.getItem() == Items.BONE_MEAL) {
|
|
+ world.captureTreeGeneration = true;
|
|
+ }
|
|
+ }
|
|
Item item = this.getItem();
|
|
EnumInteractionResult enuminteractionresult = item.a(itemactioncontext);
|
|
+ NBTTagCompound newData = this.getTagClone();
|
|
+ int newCount = this.getCount();
|
|
+ this.setCount(oldCount);
|
|
+ this.setTagClone(oldData);
|
|
+ world.captureBlockStates = false;
|
|
+ if (enuminteractionresult == EnumInteractionResult.SUCCESS && world.captureTreeGeneration && world.capturedBlockStates.size() > 0) {
|
|
+ world.captureTreeGeneration = false;
|
|
+ Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
|
+ TreeType treeType = BlockSapling.treeType;
|
|
+ BlockSapling.treeType = null;
|
|
+ List<BlockState> blocks = (List<BlockState>) world.capturedBlockStates.clone();
|
|
+ world.capturedBlockStates.clear();
|
|
+ StructureGrowEvent structureEvent = null;
|
|
+ if (treeType != null) {
|
|
+ boolean isBonemeal = getItem() == Items.BONE_MEAL;
|
|
+ structureEvent = new StructureGrowEvent(location, treeType, isBonemeal, (Player) entityhuman.getBukkitEntity(), blocks);
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
|
|
+ }
|
|
+
|
|
+ BlockFertilizeEvent fertilizeEvent = new BlockFertilizeEvent(CraftBlock.at(world, blockposition), (Player) entityhuman.getBukkitEntity(), blocks);
|
|
+ fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled());
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent);
|
|
+
|
|
+ if (!fertilizeEvent.isCancelled()) {
|
|
+ // Change the stack to its new contents if it hasn't been tampered with.
|
|
+ if (this.getCount() == oldCount && Objects.equals(this.tag, oldData)) {
|
|
+ this.setTag(newData);
|
|
+ this.setCount(newCount);
|
|
+ }
|
|
+ for (BlockState blockstate : blocks) {
|
|
+ blockstate.update(true);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return enuminteractionresult;
|
|
+ }
|
|
+ world.captureTreeGeneration = false;
|
|
|
|
if (entityhuman != null && enuminteractionresult == EnumInteractionResult.SUCCESS) {
|
|
- entityhuman.b(StatisticList.ITEM_USED.b(item));
|
|
+ org.bukkit.event.block.BlockPlaceEvent placeEvent = null;
|
|
+ List<BlockState> blocks = (List<BlockState>) world.capturedBlockStates.clone();
|
|
+ world.capturedBlockStates.clear();
|
|
+ if (blocks.size() > 1) {
|
|
+ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(world, entityhuman, enumhand, blocks, blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
|
+ } else if (blocks.size() == 1) {
|
|
+ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, enumhand, blocks.get(0), blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
|
+ }
|
|
+
|
|
+ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) {
|
|
+ enuminteractionresult = EnumInteractionResult.FAIL; // cancel placement
|
|
+ // PAIL: Remove this when MC-99075 fixed
|
|
+ placeEvent.getPlayer().updateInventory();
|
|
+ // revert back all captured blocks
|
|
+ for (BlockState blockstate : blocks) {
|
|
+ blockstate.update(true, false);
|
|
+ }
|
|
+
|
|
+ // Brute force all possible updates
|
|
+ BlockPosition placedPos = ((CraftBlock) placeEvent.getBlock()).getPosition();
|
|
+ for (EnumDirection dir : EnumDirection.values()) {
|
|
+ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, placedPos.shift(dir)));
|
|
+ }
|
|
+ } else {
|
|
+ // Change the stack to its new contents if it hasn't been tampered with.
|
|
+ if (this.getCount() == oldCount && Objects.equals(this.tag, oldData)) {
|
|
+ this.setTag(newData);
|
|
+ this.setCount(newCount);
|
|
+ }
|
|
+
|
|
+ for (Map.Entry<BlockPosition, TileEntity> e : world.capturedTileEntities.entrySet()) {
|
|
+ world.setTileEntity(e.getKey(), e.getValue());
|
|
+ }
|
|
+
|
|
+ for (BlockState blockstate : blocks) {
|
|
+ int updateFlag = ((CraftBlockState) blockstate).getFlag();
|
|
+ IBlockData oldBlock = ((CraftBlockState) blockstate).getHandle();
|
|
+ BlockPosition newblockposition = ((CraftBlockState) blockstate).getPosition();
|
|
+ IBlockData block = world.getType(newblockposition);
|
|
+
|
|
+ if (!(block.getBlock() instanceof BlockTileEntity)) { // Containers get placed automatically
|
|
+ block.getBlock().onPlace(block, world, newblockposition, oldBlock);
|
|
+ }
|
|
+
|
|
+ world.notifyAndUpdatePhysics(newblockposition, null, oldBlock, block, world.getType(newblockposition), updateFlag); // send null chunk as chunk.k() returns false by this point
|
|
+ }
|
|
+
|
|
+ // Special case juke boxes as they update their tile entity. Copied from ItemRecord.
|
|
+ // PAIL: checkme on updates.
|
|
+ if (this.item instanceof ItemRecord) {
|
|
+ ((BlockJukeBox) Blocks.JUKEBOX).a(world, blockposition, world.getType(blockposition), this);
|
|
+ world.a((EntityHuman) null, 1010, blockposition, Item.getId(this.item));
|
|
+ this.subtract(1);
|
|
+ entityhuman.a(StatisticList.PLAY_RECORD);
|
|
+ }
|
|
+
|
|
+ if (this.item == Items.WITHER_SKELETON_SKULL) { // Special case skulls to allow wither spawns to be cancelled
|
|
+ BlockPosition bp = blockposition;
|
|
+ if (!world.getType(blockposition).getMaterial().isReplaceable()) {
|
|
+ if (!world.getType(blockposition).getMaterial().isBuildable()) {
|
|
+ bp = null;
|
|
+ } else {
|
|
+ bp = bp.shift(itemactioncontext.getClickedFace());
|
|
+ }
|
|
+ }
|
|
+ if (bp != null) {
|
|
+ TileEntity te = world.getTileEntity(bp);
|
|
+ if (te instanceof TileEntitySkull) {
|
|
+ BlockWitherSkull.a(world, bp, (TileEntitySkull) te);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // SPIGOT-4678
|
|
+ if (this.item instanceof ItemSign && ItemSign.openSign) {
|
|
+ ItemSign.openSign = false;
|
|
+ entityhuman.openSign((TileEntitySign) world.getTileEntity(new BlockActionContext(itemactioncontext).getClickPosition()));
|
|
+ }
|
|
+
|
|
+ // SPIGOT-1288 - play sound stripped from ItemBlock
|
|
+ if (this.item instanceof ItemBlock) {
|
|
+ SoundEffectType soundeffecttype = ((ItemBlock) this.item).getBlock().getStepSound();
|
|
+ world.a(entityhuman, blockposition, soundeffecttype.e(), SoundCategory.BLOCKS, (soundeffecttype.a() + 1.0F) / 2.0F, soundeffecttype.b() * 0.8F);
|
|
+ }
|
|
+
|
|
+ entityhuman.b(StatisticList.ITEM_USED.b(item));
|
|
+ }
|
|
}
|
|
+ world.capturedTileEntities.clear();
|
|
+ world.capturedBlockStates.clear();
|
|
+ // CraftBukkit end
|
|
|
|
return enuminteractionresult;
|
|
}
|
|
@@ -134,7 +314,7 @@
|
|
nbttagcompound.setString("id", minecraftkey == null ? "minecraft:air" : minecraftkey.toString());
|
|
nbttagcompound.setByte("Count", (byte) this.count);
|
|
if (this.tag != null) {
|
|
- nbttagcompound.set("tag", this.tag);
|
|
+ nbttagcompound.set("tag", this.tag.clone()); // CraftBukkit - make defensive copy, data is going to another thread
|
|
}
|
|
|
|
return nbttagcompound;
|
|
@@ -167,6 +347,12 @@
|
|
}
|
|
|
|
public void setDamage(int i) {
|
|
+ // CraftBukkit start - remove Damage tag if 0
|
|
+ if (i <= 0) {
|
|
+ this.c("Damage"); // PAIL removeTag
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.getOrCreateTag().setInt("Damage", Math.max(0, i));
|
|
}
|
|
|
|
@@ -191,6 +377,21 @@
|
|
}
|
|
|
|
i -= k;
|
|
+ // CraftBukkit start
|
|
+ if (entityplayer != null) {
|
|
+ PlayerItemDamageEvent event = new PlayerItemDamageEvent(entityplayer.getBukkitEntity(), CraftItemStack.asCraftMirror(this), i);
|
|
+ event.getPlayer().getServer().getPluginManager().callEvent(event);
|
|
+
|
|
+ if (i != event.getDamage() || event.isCancelled()) {
|
|
+ event.getPlayer().updateInventory();
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ i = event.getDamage();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (i <= 0) {
|
|
return false;
|
|
}
|
|
@@ -212,6 +413,11 @@
|
|
if (this.isDamaged(i, entityliving.getRandom(), entityliving instanceof EntityPlayer ? (EntityPlayer) entityliving : null)) {
|
|
entityliving.c(this);
|
|
Item item = this.getItem();
|
|
+ // CraftBukkit start - Check for item breaking
|
|
+ if (this.count == 1 && entityliving instanceof EntityHuman) {
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((EntityHuman) entityliving, this);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
|
|
this.subtract(1);
|
|
if (entityliving instanceof EntityHuman) {
|
|
@@ -335,6 +541,17 @@
|
|
return this.tag;
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ @Nullable
|
|
+ private NBTTagCompound getTagClone() {
|
|
+ return this.tag == null ? null : this.tag.clone();
|
|
+ }
|
|
+
|
|
+ private void setTagClone(@Nullable NBTTagCompound nbtttagcompound) {
|
|
+ this.setTag(nbtttagcompound == null ? null : nbtttagcompound.clone());
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public NBTTagCompound getOrCreateTag() {
|
|
if (this.tag == null) {
|
|
this.setTag(new NBTTagCompound());
|
|
@@ -479,6 +696,12 @@
|
|
}
|
|
|
|
public void setRepairCost(int i) {
|
|
+ // CraftBukkit start - remove RepairCost tag when 0 (SPIGOT-3945)
|
|
+ if (i == 0) {
|
|
+ this.c("RepairCost"); // PAIL removeTag
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.getOrCreateTag().setInt("RepairCost", i);
|
|
}
|
|
|
|
@@ -521,6 +744,13 @@
|
|
nbttaglist.add((NBTBase) nbttagcompound);
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ @Deprecated
|
|
+ public void setItem(Item item) {
|
|
+ this.item = item;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public IChatBaseComponent A() {
|
|
IChatBaseComponent ichatbasecomponent = (new ChatComponentText("")).addSibling(this.getName());
|
|
|