aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0353-Fix-item-duplication-and-teleport-issues.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0353-Fix-item-duplication-and-teleport-issues.patch')
-rw-r--r--patches/server/0353-Fix-item-duplication-and-teleport-issues.patch167
1 files changed, 167 insertions, 0 deletions
diff --git a/patches/server/0353-Fix-item-duplication-and-teleport-issues.patch b/patches/server/0353-Fix-item-duplication-and-teleport-issues.patch
new file mode 100644
index 0000000000..b59b420d07
--- /dev/null
+++ b/patches/server/0353-Fix-item-duplication-and-teleport-issues.patch
@@ -0,0 +1,167 @@
+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 511208e323b26df24263b87eeb7d2645572d9ff8..c6877df96229eb922fa0d4029f31d9698eab78ab 100644
+--- a/src/main/java/net/minecraft/world/entity/Entity.java
++++ b/src/main/java/net/minecraft/world/entity/Entity.java
+@@ -2447,11 +2447,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
+@@ -3238,6 +3239,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ @Nullable
+ public Entity teleportTo(ServerLevel worldserver, Vec3 location) {
+ // CraftBukkit end
++ // Paper start - Fix item duplication and teleport issues
++ if (!this.isAlive() || !this.valid) {
++ LOGGER.warn("Illegal Entity Teleport " + this + " to " + worldserver + ":" + location, new Throwable());
++ return null;
++ }
++ // Paper end - Fix item duplication and teleport issues
+ if (this.level() instanceof ServerLevel && !this.isRemoved()) {
+ this.level().getProfiler().push("changeDimension");
+ // CraftBukkit start
+@@ -3264,6 +3271,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ // CraftBukkit end
+
+ this.level().getProfiler().popPush("reloading");
++ // 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
+ Entity entity = this.getType().create(worldserver);
+
+ if (entity != null) {
+@@ -3281,10 +3293,6 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ // CraftBukkit start - Forward the CraftEntity to the new entity
+ this.getBukkitEntity().setHandle(entity);
+ entity.bukkitEntity = this.getBukkitEntity();
+-
+- if (this instanceof Mob) {
+- ((Mob) this).dropLeash(true, false); // Unleash to prevent duping of leads.
+- }
+ // CraftBukkit end
+ }
+
+@@ -3405,7 +3413,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+ }
+
+ public boolean canChangeDimensions() {
+- return !this.isPassenger() && !this.isVehicle();
++ return !this.isPassenger() && !this.isVehicle() && isAlive() && 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 2f3615cb8e4206ed2eb87c1341de4258879d3907..c640d68c35b4454bfa0ae1764dee341041d9c31e 100644
+--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+@@ -1740,9 +1740,9 @@ public abstract class LivingEntity extends Entity implements Attackable {
+ // Paper start
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(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) {
+@@ -1843,8 +1843,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
+ this.dropCustomDeathLoot(source, i, flag);
+ this.clearEquipmentSlots = prev; // Paper
+ }
+- // CraftBukkit start - Call death event
+- org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, source, 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, source, this.drops, () -> {
++ final LivingEntity entityliving = this.getKillCredit();
++ if (this.deathScore >= 0 && entityliving != null) {
++ entityliving.awardKillScore(this, this.deathScore, source);
++ }
++ }); // 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 029d5756f424dba57b4a974b09453c2f0cf0e8e2..e1f6853c450a2e6a0d7607f1faec7bc5c82d9621 100644
+--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
++++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+@@ -628,7 +628,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);
+ }
+ }
+@@ -636,7 +636,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 74fc73549b504eb2b7fcaa141aa125f84a077ed9..ac4a8c9d4f727f3caa16f6dc5497d69f9db52aab 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -894,6 +894,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());
+@@ -908,11 +913,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;