aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0350-Fix-item-duplication-and-teleport-issues.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0350-Fix-item-duplication-and-teleport-issues.patch')
-rw-r--r--patches/server/0350-Fix-item-duplication-and-teleport-issues.patch156
1 files changed, 156 insertions, 0 deletions
diff --git a/patches/server/0350-Fix-item-duplication-and-teleport-issues.patch b/patches/server/0350-Fix-item-duplication-and-teleport-issues.patch
new file mode 100644
index 0000000000..66b9f096ef
--- /dev/null
+++ b/patches/server/0350-Fix-item-duplication-and-teleport-issues.patch
@@ -0,0 +1,156 @@
+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 2ce2926f4e3a79a1a329cdd684852970f6f104f2..b1d870b9a3b414ed49b4674afc2c6088d457ebc1 100644
+--- a/src/main/java/net/minecraft/world/entity/Entity.java
++++ b/src/main/java/net/minecraft/world/entity/Entity.java
+@@ -2517,11 +2517,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(this.level(), this.getX(), this.getY() + (double) yOffset, this.getZ(), stack);
++ ItemEntity entityitem = new ItemEntity(this.level(), 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
+@@ -3337,6 +3338,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ public Entity changeDimension(DimensionTransition teleportTarget) {
+ Level world = this.level();
+
++ // Paper start - Fix item duplication and teleport issues
++ if (!this.isAlive() || !this.valid) {
++ LOGGER.warn("Illegal Entity Teleport " + this + " to " + teleportTarget.newLevel() + ":" + teleportTarget.pos(), new Throwable());
++ return null;
++ }
++ // Paper end - Fix item duplication and teleport issues
+ if (world instanceof ServerLevel worldserver) {
+ if (!this.isRemoved()) {
+ // CraftBukkit start
+@@ -3379,6 +3386,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+
+ if (entity2 != null) {
+ if (this != entity2) {
++ // Paper start - Fix item duplication and teleport issues
++ if (this instanceof Mob) {
++ ((Mob) this).dropLeash(true, true); // Paper drop lead
++ }
++ // Paper end - Fix item duplication and teleport issues
+ entity2.restoreFrom(this);
+ this.removeAfterChangingDimensions();
+ // CraftBukkit start - Forward the CraftEntity to the new entity
+@@ -3454,7 +3466,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ }
+
+ public boolean canChangeDimensions(Level from, Level to) {
+- return true;
++ return this.isAlive() && this.valid; // Paper - Fix item duplication and teleport issues
+ }
+
+ public float getBlockExplosionResistance(Explosion explosion, BlockGetter world, BlockPos pos, BlockState blockState, FluidState fluidState, float max) {
+diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+index d475a0f36a7c302fc5cf23ab2ad6d0b835710abb..96894dba3ddbf1d52266e5df0c280cc30d54d5de 100644
+--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+@@ -1717,9 +1717,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) {
+@@ -1811,8 +1811,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 92bb0c63330ad3a4cb13b2dc655020714e9b1ffd..cc1189c2d7dc57ba8f29aad4ba5d2a07362bcd5b 100644
+--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
++++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+@@ -635,7 +635,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);
+ }
+ }
+@@ -643,7 +643,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 6e9f0fdbfffe7a08185a125e2ad0f2227d25cc07..720ea811407b4288b9923cf7db0ac44ad1a086e0 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -895,6 +895,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);
+ EntityDeathEvent event = new EntityDeathEvent(entity, bukkitDamageSource, drops, victim.getExpReward(damageSource.getEntity()));
+@@ -909,11 +914,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;