aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0935-Restore-vanilla-entity-drops-behavior.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0935-Restore-vanilla-entity-drops-behavior.patch')
-rw-r--r--patches/server/0935-Restore-vanilla-entity-drops-behavior.patch243
1 files changed, 243 insertions, 0 deletions
diff --git a/patches/server/0935-Restore-vanilla-entity-drops-behavior.patch b/patches/server/0935-Restore-vanilla-entity-drops-behavior.patch
new file mode 100644
index 0000000000..2a317f2be1
--- /dev/null
+++ b/patches/server/0935-Restore-vanilla-entity-drops-behavior.patch
@@ -0,0 +1,243 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+Date: Tue, 22 Mar 2022 09:34:41 -0700
+Subject: [PATCH] Restore vanilla entity drops behavior
+
+Instead of just tracking the itemstacks, this tracks with it, the
+action to take with that itemstack to apply the correct logic
+on dropping the item instead of generalizing it for all dropped
+items like CB does.
+
+diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+index e447aa85b51c7e28fcd89bf8aa7602c88d599239..0d4224c56bcdff54bbf6cc2d9d25e4d7b0ba187b 100644
+--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+@@ -968,22 +968,20 @@ public class ServerPlayer extends Player {
+ if (this.isRemoved()) {
+ return;
+ }
+- java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(this.getInventory().getContainerSize());
++ List<DefaultDrop> loot = new java.util.ArrayList<>(this.getInventory().getContainerSize()); // Paper - Restore vanilla drops behavior
+ boolean keepInventory = this.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || this.isSpectator();
+
+ if (!keepInventory) {
+ for (ItemStack item : this.getInventory().getContents()) {
+ if (!item.isEmpty() && !EnchantmentHelper.hasVanishingCurse(item)) {
+- loot.add(CraftItemStack.asCraftMirror(item));
++ loot.add(new DefaultDrop(item, stack -> this.drop(stack, true, false, false))); // Paper - Restore vanilla drops behavior; drop function taken from Inventory#dropAll (don't fire drop event)
+ }
+ }
+ }
+ if (this.shouldDropLoot() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { // Paper - fix player loottables running when mob loot gamerule is false
+ // SPIGOT-5071: manually add player loot tables (SPIGOT-5195 - ignores keepInventory rule)
+ this.dropFromLootTable(damageSource, this.lastHurtByPlayerTime > 0);
+- for (org.bukkit.inventory.ItemStack item : this.drops) {
+- loot.add(item);
+- }
++ loot.addAll(this.drops); // Paper
+ this.drops.clear(); // SPIGOT-5188: make sure to clear
+ } // Paper - fix player loottables running when mob loot gamerule is false
+
+@@ -2481,8 +2479,8 @@ public class ServerPlayer extends Player {
+ }
+
+ @Override
+- public ItemEntity drop(ItemStack stack, boolean throwRandomly, boolean retainOwnership) {
+- ItemEntity entityitem = super.drop(stack, throwRandomly, retainOwnership);
++ public ItemEntity drop(ItemStack stack, boolean throwRandomly, boolean retainOwnership, boolean callDropEvent) { // Paper - Restore vanilla drops behavior; override method with most params
++ ItemEntity entityitem = super.drop(stack, throwRandomly, retainOwnership, callDropEvent); // Paper - Restore vanilla drops behavior; override method with most params
+
+ if (entityitem == null) {
+ return null;
+diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
+index ee38f76e46c20e9fadfb5d4d602b7b82a8a22e51..4e96b6c05bf8b8235e91bdd26e5615c5299fd9aa 100644
+--- a/src/main/java/net/minecraft/world/entity/Entity.java
++++ b/src/main/java/net/minecraft/world/entity/Entity.java
+@@ -2524,6 +2524,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
+
+ @Nullable
+ public ItemEntity spawnAtLocation(ItemStack stack, float yOffset) {
++ // Paper start - Restore vanilla drops behavior
++ return this.spawnAtLocation(stack, yOffset, null);
++ }
++ public record DefaultDrop(Item item, org.bukkit.inventory.ItemStack stack, @Nullable java.util.function.Consumer<ItemStack> dropConsumer) {
++ public DefaultDrop(final ItemStack stack, final java.util.function.Consumer<ItemStack> dropConsumer) {
++ this(stack.getItem(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), dropConsumer);
++ }
++
++ public void runConsumer(final org.bukkit.World fallbackWorld, final Location fallbackLoc) {
++ if (this.dropConsumer == null || org.bukkit.craftbukkit.inventory.CraftItemType.bukkitToMinecraft(this.stack.getType()) != this.item) {
++ fallbackWorld.dropItem(fallbackLoc, this.stack);
++ } else {
++ this.dropConsumer.accept(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(this.stack));
++ }
++ }
++ }
++ @Nullable
++ public ItemEntity spawnAtLocation(ItemStack stack, float yOffset, @Nullable java.util.function.Consumer<? super ItemEntity> delayedAddConsumer) {
++ // Paper end - Restore vanilla drops behavior
+ if (stack.isEmpty()) {
+ return null;
+ } else if (this.level().isClientSide) {
+@@ -2531,14 +2550,21 @@ 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.asCraftMirror(stack)); // Paper - mirror so we can destroy it later
++ // Paper start - Restore vanilla drops behavior
++ ((net.minecraft.world.entity.LivingEntity) this).drops.add(new net.minecraft.world.entity.Entity.DefaultDrop(stack, itemStack -> {
++ ItemEntity itemEntity = new ItemEntity(this.level, this.getX(), this.getY() + (double) yOffset, this.getZ(), itemStack); // stack is copied before consumer
++ itemEntity.setDefaultPickUpDelay();
++ this.level.addFreshEntity(itemEntity);
++ if (delayedAddConsumer != null) delayedAddConsumer.accept(itemEntity);
++ }));
++ // Paper end - Restore vanilla drops behavior
+ return null;
+ }
+ // CraftBukkit end
+ 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();
++ entityitem.setDefaultPickUpDelay(); // Paper - diff on change (in dropConsumer)
+ // Paper start - Call EntityDropItemEvent
+ return this.spawnAtLocation(entityitem);
+ }
+diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+index 6b156049b06b38afbbd7cf3155b06601e334a805..f024038d473e424aa08256040c1591d23871c20c 100644
+--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
+@@ -266,7 +266,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
+ // CraftBukkit start
+ public int expToDrop;
+ public boolean forceDrops;
+- public ArrayList<org.bukkit.inventory.ItemStack> drops = new ArrayList<org.bukkit.inventory.ItemStack>();
++ public ArrayList<DefaultDrop> drops = new ArrayList<>(); // Paper - Restore vanilla drops behavior
+ public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
+ public boolean collides = true;
+ public Set<UUID> collidableExemptions = new HashSet<>();
+diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
+index aa22829c3d41118664a872540fdc8f716120c407..c23d4ee0a16d1ae7168b2496d97189a14256bdcc 100644
+--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
++++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
+@@ -537,10 +537,10 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
+ @Override
+ protected void dropCustomDeathLoot(DamageSource source, int lootingMultiplier, boolean allowDrops) {
+ super.dropCustomDeathLoot(source, lootingMultiplier, allowDrops);
+- ItemEntity entityitem = this.spawnAtLocation((ItemLike) Items.NETHER_STAR);
++ ItemEntity entityitem = this.spawnAtLocation(new net.minecraft.world.item.ItemStack(Items.NETHER_STAR), 0, ItemEntity::setExtendedLifetime); // Paper - Restore vanilla drops behavior; spawnAtLocation returns null so modify the item entity with a consumer
+
+ if (entityitem != null) {
+- entityitem.setExtendedLifetime();
++ entityitem.setExtendedLifetime(); // Paper - diff on change
+ }
+
+ }
+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 b1b0b0554e0ad035463fd53aa440d9c079b7cfb4..2ed6845f16fab175e2e9e96e76391e63ab4a43e2 100644
+--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
++++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+@@ -614,7 +614,7 @@ public class ArmorStand extends LivingEntity {
+ ItemStack itemstack = new ItemStack(Items.ARMOR_STAND);
+
+ itemstack.set(DataComponents.CUSTOM_NAME, this.getCustomName());
+- this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops
++ this.drops.add(new DefaultDrop(itemstack, stack -> Block.popResource(this.level(), this.blockPosition(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior
+ return this.brokenByAnything(damageSource); // Paper
+ }
+
+@@ -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.asCraftMirror(itemstack)); // CraftBukkit - add to drops // Paper - mirror so we can destroy it later - though this call site was safe
++ this.drops.add(new DefaultDrop(itemstack, stack -> Block.popResource(this.level(), this.blockPosition().above(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior; mirror so we can destroy it later - though this call site was safe & spawn drops correctly
+ 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.asCraftMirror(itemstack)); // CraftBukkit - add to drops // Paper - mirror so we can destroy it later - though this call site was safe
++ this.drops.add(new DefaultDrop(itemstack, stack -> Block.popResource(this.level(), this.blockPosition().above(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior; mirror so we can destroy it later - though this call site was safe & spawn drops correctly
+ 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 1366e0e03277ebf12057d12b251abc1a6c5ef10e..2c127f1aa22815168b07cf226c0fb56e8358bc8b 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -964,17 +964,23 @@ public class CraftEventFactory {
+ }
+
+ public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim) {
+- return CraftEventFactory.callEntityDeathEvent(victim, new ArrayList<org.bukkit.inventory.ItemStack>(0));
++ return CraftEventFactory.callEntityDeathEvent(victim, new ArrayList<>(0)); // Paper - Restore vanilla drops behavior
+ }
+
+- public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List<org.bukkit.inventory.ItemStack> drops) {
++ public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List<Entity.DefaultDrop> drops) { // Paper - Restore vanilla drops behavior
+ // Paper start
+ return CraftEventFactory.callEntityDeathEvent(victim, drops, com.google.common.util.concurrent.Runnables.doNothing());
+ }
+- public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List<org.bukkit.inventory.ItemStack> drops, Runnable lootCheck) {
++
++ private static final java.util.function.Function<org.bukkit.inventory.ItemStack, Entity.DefaultDrop> FROM_FUNCTION = stack -> {
++ if (stack == null) return null;
++ return new Entity.DefaultDrop(CraftItemType.bukkitToMinecraft(stack.getType()), stack, null);
++ };
++
++ public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List<Entity.DefaultDrop> drops, Runnable lootCheck) { // Paper
+ // Paper end
+ CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity();
+- EntityDeathEvent event = new EntityDeathEvent(entity, drops, victim.getExpReward());
++ EntityDeathEvent event = new EntityDeathEvent(entity, new io.papermc.paper.util.TransformingRandomAccessList<>(drops, Entity.DefaultDrop::stack, FROM_FUNCTION), victim.getExpReward()); // Paper - Restore vanilla drops behavior
+ populateFields(victim, event); // Paper - make cancellable
+ CraftWorld world = (CraftWorld) entity.getWorld();
+ Bukkit.getServer().getPluginManager().callEvent(event);
+@@ -988,19 +994,23 @@ public class CraftEventFactory {
+ victim.expToDrop = event.getDroppedExp();
+ lootCheck.run(); // Paper - advancement triggers before destroying items
+
+- for (org.bukkit.inventory.ItemStack stack : event.getDrops()) {
++ // Paper start - Restore vanilla drops behavior
++ for (Entity.DefaultDrop drop : drops) {
++ if (drop == null) continue;
++ final org.bukkit.inventory.ItemStack stack = drop.stack();
++ // Paper end - Restore vanilla drops behavior
+ if (stack == null || stack.getType() == Material.AIR || stack.getAmount() == 0) continue;
+
+- world.dropItem(entity.getLocation(), stack); // Paper - note: dropItem already clones due to this being bukkit -> NMS
++ drop.runConsumer(world, entity.getLocation()); // Paper - Restore vanilla drops behavior
+ 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;
+ }
+
+- public static PlayerDeathEvent callPlayerDeathEvent(ServerPlayer victim, List<org.bukkit.inventory.ItemStack> drops, net.kyori.adventure.text.Component deathMessage, boolean keepInventory) { // Paper - Adventure
++ public static PlayerDeathEvent callPlayerDeathEvent(ServerPlayer victim, List<Entity.DefaultDrop> drops, net.kyori.adventure.text.Component deathMessage, boolean keepInventory) { // Paper - Adventure & Restore vanilla drops behavior
+ CraftPlayer entity = victim.getBukkitEntity();
+- PlayerDeathEvent event = new PlayerDeathEvent(entity, drops, victim.getExpReward(), 0, deathMessage);
++ PlayerDeathEvent event = new PlayerDeathEvent(entity, new io.papermc.paper.util.TransformingRandomAccessList<>(drops, Entity.DefaultDrop::stack, FROM_FUNCTION), victim.getExpReward(), 0, deathMessage); // Paper - Restore vanilla drops behavior
+ event.setKeepInventory(keepInventory);
+ event.setKeepLevel(victim.keepLevel); // SPIGOT-2222: pre-set keepLevel
+ populateFields(victim, event); // Paper - make cancellable
+@@ -1019,10 +1029,14 @@ public class CraftEventFactory {
+ victim.expToDrop = event.getDroppedExp();
+ victim.newExp = event.getNewExp();
+
+- for (org.bukkit.inventory.ItemStack stack : event.getDrops()) {
++ // Paper start - Restore vanilla drops behavior
++ for (Entity.DefaultDrop drop : drops) {
++ if (drop == null) continue;
++ final org.bukkit.inventory.ItemStack stack = drop.stack();
++ // Paper end - Restore vanilla drops behavior
+ if (stack == null || stack.getType() == Material.AIR) continue;
+
+- world.dropItem(entity.getLocation(), stack);
++ drop.runConsumer(world, entity.getLocation()); // Paper - Restore vanilla drops behavior
+ }
+
+ return event;