aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/unapplied/server/0343-Fix-item-duplication-and-teleport-issues.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/unapplied/server/0343-Fix-item-duplication-and-teleport-issues.patch')
-rw-r--r--patches/unapplied/server/0343-Fix-item-duplication-and-teleport-issues.patch155
1 files changed, 155 insertions, 0 deletions
diff --git a/patches/unapplied/server/0343-Fix-item-duplication-and-teleport-issues.patch b/patches/unapplied/server/0343-Fix-item-duplication-and-teleport-issues.patch
new file mode 100644
index 0000000000..8b468ae1a2
--- /dev/null
+++ b/patches/unapplied/server/0343-Fix-item-duplication-and-teleport-issues.patch
@@ -0,0 +1,155 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Sat, 25 Apr 2020 06:46:35 -0400
+Subject: [PATCH] Fix item duplication 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/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
+index dce9bdf371ebbfd4407ac7ca940df29777384232..5ed8a86b6525992a3e8ffa63feef82e29180f0e9 100644
+--- a/src/main/java/net/minecraft/world/entity/Entity.java
++++ b/src/main/java/net/minecraft/world/entity/Entity.java
+@@ -2631,11 +2631,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ } else {
+ // CraftBukkit start - Capture drops for death event
+ if (this instanceof net.minecraft.world.entity.LivingEntity && !((net.minecraft.world.entity.LivingEntity) this).forceDrops) {
+- ((net.minecraft.world.entity.LivingEntity) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack));
++ ((net.minecraft.world.entity.LivingEntity) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack)); // Paper - mirror so we can destroy it later
+ return null;
+ }
+ // CraftBukkit end
+- ItemEntity entityitem = new ItemEntity(world, this.getX(), this.getY() + (double) yOffset, this.getZ(), stack);
++ ItemEntity entityitem = new ItemEntity(world, this.getX(), this.getY() + (double) yOffset, this.getZ(), stack.copy()); // Paper - copy so we can destroy original
++ stack.setCount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe
+
+ entityitem.setDefaultPickUpDelay();
+ // CraftBukkit start
+@@ -3463,6 +3464,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ public Entity teleport(TeleportTransition teleportTarget) {
+ Level world = this.level();
+
++ // Paper start - Fix item duplication and teleport issues
++ if ((!this.isAlive() || !this.valid) && (teleportTarget.newLevel() != world)) {
++ LOGGER.warn("Illegal Entity Teleport " + this + " to " + teleportTarget.newLevel() + ":" + teleportTarget.position(), new Throwable());
++ return null;
++ }
++ // Paper end - Fix item duplication and teleport issues
+ if (world instanceof ServerLevel worldserver) {
+ if (!this.isRemoved()) {
+ // CraftBukkit start
+@@ -3551,6 +3558,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ gameprofilerfiller.pop();
+ return null;
+ } else {
++ // Paper start - Fix item duplication and teleport issues
++ if (this instanceof Leashable leashable) {
++ leashable.dropLeash(true, true); // Paper drop lead
++ }
++ // Paper end - Fix item duplication and teleport issues
+ entity.restoreFrom(this);
+ this.removeAfterChangingDimensions();
+ // CraftBukkit start - Forward the CraftEntity to the new entity
+@@ -3663,6 +3675,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ }
+
+ public boolean canTeleport(Level from, Level to) {
++ if (!this.isAlive() || !this.valid) return false; // Paper - Fix item duplication and teleport issues
+ if (from.dimension() == Level.END && to.dimension() == Level.OVERWORLD) {
+ Iterator iterator = this.getPassengers().iterator();
+
+diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+index 06cac1b4c9e97cade7dd6e7971ca06e934b495ac..911dcfb9b34d94e38db71c17022b8a8c1e707eb7 100644
+--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+@@ -1765,9 +1765,9 @@ public abstract class LivingEntity extends Entity implements Attackable {
+ // Paper start
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(worldserver, damageSource);
+ if (deathEvent == null || !deathEvent.isCancelled()) {
+- if (this.deathScore >= 0 && entityliving != null) {
+- entityliving.awardKillScore(this, this.deathScore, damageSource);
+- }
++ // if (this.deathScore >= 0 && entityliving != null) { // Paper - Fix item duplication and teleport issues; moved to be run earlier in #dropAllDeathLoot before destroying the drop items in CraftEventFactory#callEntityDeathEvent
++ // entityliving.awardKillScore(this, this.deathScore, damageSource);
++ // }
+ // Paper start - clear equipment if event is not cancelled
+ if (this instanceof Mob) {
+ for (EquipmentSlot slot : this.clearedEquipmentSlots) {
+@@ -1861,8 +1861,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
+ this.dropCustomDeathLoot(world, damageSource, flag);
+ this.clearEquipmentSlots = prev; // Paper
+ }
+- // CraftBukkit start - Call death event
+- org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, damageSource, this.drops); // Paper
++ // CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment
++ org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, damageSource, this.drops, () -> {
++ final LivingEntity entityliving = this.getKillCredit();
++ if (this.deathScore >= 0 && entityliving != null) {
++ entityliving.awardKillScore(this, this.deathScore, damageSource);
++ }
++ }); // Paper end
+ this.postDeathDropItems(deathEvent); // Paper
+ this.drops = new ArrayList<>();
+ // CraftBukkit end
+diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+index e20565cf256aacd012a1722c5ebbf9016bc82e42..59fbfe8de2dc5ec020dd61a5e446b0b6f67d76e4 100644
+--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
++++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+@@ -633,7 +633,7 @@ public class ArmorStand extends LivingEntity {
+ for (i = 0; i < this.handItems.size(); ++i) {
+ itemstack = (ItemStack) this.handItems.get(i);
+ if (!itemstack.isEmpty()) {
+- this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops
++ this.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.EMPTY);
+ }
+ }
+@@ -641,7 +641,7 @@ public class ArmorStand extends LivingEntity {
+ for (i = 0; i < this.armorItems.size(); ++i) {
+ itemstack = (ItemStack) this.armorItems.get(i);
+ if (!itemstack.isEmpty()) {
+- this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops
++ this.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.EMPTY);
+ }
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+index 74f33aeb6193080446ebbcc5242d95c3b1c97823..f482fc14e171129f9fa60113b1223b2db79ec6ec 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -903,6 +903,11 @@ public class CraftEventFactory {
+ }
+
+ public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, DamageSource damageSource, List<org.bukkit.inventory.ItemStack> drops) {
++ // Paper start
++ return CraftEventFactory.callEntityDeathEvent(victim, damageSource, drops, com.google.common.util.concurrent.Runnables.doNothing());
++ }
++ public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, DamageSource damageSource, List<org.bukkit.inventory.ItemStack> drops, Runnable lootCheck) {
++ // Paper end
+ CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity();
+ CraftDamageSource bukkitDamageSource = new CraftDamageSource(damageSource);
+ CraftWorld world = (CraftWorld) entity.getWorld();
+@@ -917,11 +922,13 @@ public class CraftEventFactory {
+ playDeathSound(victim, event);
+ // Paper end
+ victim.expToDrop = event.getDroppedExp();
++ lootCheck.run(); // Paper - advancement triggers before destroying items
+
+ 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
++ if (stack instanceof CraftItemStack) stack.setAmount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe, but don't nuke bukkit stacks of manually added items
+ }
+
+ return event;