From a6ac47e502d4cadef0c2c289956971739076c49c Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 25 Apr 2020 06:52:30 -0400 Subject: [PATCH] Fix numerous item duplication issues and teleport issues This notably fixes the newest "Donkey Dupe", but also fixes a lot of dupe bugs in general around nether portals and entity world transfer We also fix item duplication generically by anytime we clone an item to drop it on the ground, destroy the source item. This avoid an itemstack ever existing twice in the world state pre clean up stage. So even if something NEW comes up, it would be impossible to drop the same item twice because the source was destroyed. This should make us more forward proof on preventing dupes. These dupes have been in for years at this point, they aren't new... Everyone knows about them and are mitigating with plugins atm breaking gameplay. so better to make it clear its fixed in the messaging. I am submitting this to Mojang. --- ...m-duplication-issues-and-teleport-is.patch | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Spigot-Server-Patches/0493-Fix-numerous-item-duplication-issues-and-teleport-is.patch diff --git a/Spigot-Server-Patches/0493-Fix-numerous-item-duplication-issues-and-teleport-is.patch b/Spigot-Server-Patches/0493-Fix-numerous-item-duplication-issues-and-teleport-is.patch new file mode 100644 index 0000000000..843fb1237e --- /dev/null +++ b/Spigot-Server-Patches/0493-Fix-numerous-item-duplication-issues-and-teleport-is.patch @@ -0,0 +1,97 @@ +From a8fee247c04a539c268716e2907eae22405c9a41 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sat, 25 Apr 2020 06:46:35 -0400 +Subject: [PATCH] Fix numerous item duplication issues and teleport issues + +This notably fixes the newest "Donkey Dupe", but also fixes a lot +of dupe bugs in general around nether portals and entity world transfer + +We also fix item duplication generically by anytime we clone an item +to drop it on the ground, destroy the source item. + +This avoid an itemstack ever existing twice in the world state pre +clean up stage. + +So even if something NEW comes up, it would be impossible to drop the +same item twice because the source was destroyed. + +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index f973466ba5..a69969f46b 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -1966,11 +1966,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + } else { + // CraftBukkit start - Capture drops for death event + if (this instanceof EntityLiving && !((EntityLiving) this).forceDrops) { +- ((EntityLiving) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); ++ ((EntityLiving) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)); // Paper - mirror so we can destroy it later + return null; + } + // CraftBukkit end +- EntityItem entityitem = new EntityItem(this.world, this.locX(), this.locY() + (double) f, this.locZ(), itemstack); ++ EntityItem entityitem = new EntityItem(this.world, this.locX(), this.locY() + (double) f, this.locZ(), itemstack.cloneItemStack()); // Paper - clone so we can destroy original ++ itemstack.setCount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe + + entityitem.defaultPickupDelay(); + // CraftBukkit start +@@ -2632,6 +2633,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + @Nullable + public Entity teleportTo(DimensionManager dimensionmanager, BlockPosition location) { + // CraftBukkit end ++ // Paper start - fix bad state entities causing dupes ++ if (!isAlive() || !valid) { ++ LOGGER.warn("Illegal Entity Teleport " + this + " to " + dimensionmanager + ":" + location, new Throwable()); ++ return null; ++ } ++ // Paper end + if (!this.world.isClientSide && !this.dead) { + this.world.getMethodProfiler().enter("changeDimension"); + MinecraftServer minecraftserver = this.getMinecraftServer(); +@@ -2755,7 +2762,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke + } + + public boolean canPortal() { +- return true; ++ return isAlive() && valid; // Paper + } + + public float a(Explosion explosion, IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid, float f) { +diff --git a/src/main/java/net/minecraft/server/EntityArmorStand.java b/src/main/java/net/minecraft/server/EntityArmorStand.java +index 8ad131e4fc..d35a0b2d94 100644 +--- a/src/main/java/net/minecraft/server/EntityArmorStand.java ++++ b/src/main/java/net/minecraft/server/EntityArmorStand.java +@@ -557,7 +557,7 @@ public class EntityArmorStand extends EntityLiving { + for (i = 0; i < this.handItems.size(); ++i) { + itemstack = (ItemStack) this.handItems.get(i); + if (!itemstack.isEmpty()) { +- drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops ++ drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)); // CraftBukkit - add to drops // Paper - mirror so we can destroy it later - though this call site was safe + this.handItems.set(i, ItemStack.a); + } + } +@@ -565,7 +565,7 @@ public class EntityArmorStand extends EntityLiving { + for (i = 0; i < this.armorItems.size(); ++i) { + itemstack = (ItemStack) this.armorItems.get(i); + if (!itemstack.isEmpty()) { +- drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops ++ drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)); // CraftBukkit - add to drops // Paper - mirror so we can destroy it later - though this call site was safe + this.armorItems.set(i, ItemStack.a); + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 1f5d15bb49..75d38bc5d1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -769,7 +769,8 @@ public class CraftEventFactory { + for (org.bukkit.inventory.ItemStack stack : event.getDrops()) { + if (stack == null || stack.getType() == Material.AIR || stack.getAmount() == 0) continue; + +- world.dropItem(entity.getLocation(), stack); ++ world.dropItem(entity.getLocation(), stack); // Paper - note: dropItem already clones due to this being bukkit -> NMS ++ stack.setAmount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe + } + + return event; +-- +2.25.1 +