Restore vanilla entity drops behavior (#7650)

This commit is contained in:
Jake Potrebic 2023-12-02 17:35:10 -08:00 committed by GitHub
parent ffa4115239
commit 37bee09e3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -0,0 +1,230 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
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 08980763020a13ab49dc7d637625a4fba56da8c9..907c8f15f5247f9972c6677ff0f9e1aa22764a04 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -937,22 +937,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
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))); // Paper - drop function taken from Inventory#dropAll
}
}
}
if (this.shouldDropLoot() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { // Paper - preserve this check from vanilla
// 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
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index c655c6fee393c62ba79301f76baa72f9b1154a9a..fece91254b10b59474056aa730fd420f90cd7bec 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -2673,6 +2673,25 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@Nullable
public ItemEntity spawnAtLocation(ItemStack stack, float yOffset) {
+ // Paper start
+ 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.util.CraftMagicNumbers.getItem(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
if (stack.isEmpty()) {
return null;
} else if (this.level().isClientSide) {
@@ -2680,14 +2699,21 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
} 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
+ ((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
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
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 87134e57a57df0fceda903e35d22f3f2de31adf3..15e1d8c09fad181406a6acb8b3f177cd5e6c0f52 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -255,7 +255,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
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 1e07febcf7a3dfb281728cc5e3e4f15dd776d7e0..c9a4feb4a52c0eb621b120e5b8c18d0a74dae0cd 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
@@ -533,10 +533,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 - 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 9dcf28bdcb5770a191e62353a60c953731671283..523f14916073fb137578da777a23ba8265fd8af6 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -623,7 +623,7 @@ public class ArmorStand extends LivingEntity {
itemstack.setHoverName(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 - spawn drops correctly
return this.brokenByAnything(damageSource); // Paper
}
@@ -637,7 +637,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 - mirror so we can destroy it later - though this call site was safe & spawn drops correctly
this.handItems.set(i, ItemStack.EMPTY);
}
}
@@ -645,7 +645,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 - 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 5dc160b743534665c6b3efb10b10f7c36e2da5ab..64ae7cfe765ebe697a2cce1b71751e628d6f1662 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -942,17 +942,21 @@ 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
}
- 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
// 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 java.util.function.Function<org.bukkit.inventory.ItemStack, Entity.DefaultDrop> FROM_FUNCTION = stack -> {
+ if (stack == null) return null;
+ return new Entity.DefaultDrop(CraftMagicNumbers.getItem(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
populateFields(victim, event); // Paper - make cancellable
CraftWorld world = (CraftWorld) entity.getWorld();
Bukkit.getServer().getPluginManager().callEvent(event);
@@ -966,19 +970,22 @@ public class CraftEventFactory {
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;
+ // Paper start
+ for (Entity.DefaultDrop drop : drops) {
+ final org.bukkit.inventory.ItemStack stack = drop.stack();
+ if (drop == null || stack == null || stack.getType() == Material.AIR || stack.getAmount() == 0) continue;
+ // Paper end
- world.dropItem(entity.getLocation(), stack); // Paper - note: dropItem already clones due to this being bukkit -> NMS
+ drop.runConsumer(world, entity.getLocation()); // Paper
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, String stringDeathMessage, boolean keepInventory) { // Paper - Adventure
+ public static PlayerDeathEvent callPlayerDeathEvent(ServerPlayer victim, List<Entity.DefaultDrop> drops, net.kyori.adventure.text.Component deathMessage, String stringDeathMessage, boolean keepInventory) { // Paper - Adventure & improve drops
CraftPlayer entity = victim.getBukkitEntity();
- PlayerDeathEvent event = new PlayerDeathEvent(entity, drops, victim.getExpReward(), 0, deathMessage, stringDeathMessage); // Paper - Adventure
+ PlayerDeathEvent event = new PlayerDeathEvent(entity, new io.papermc.paper.util.TransformingRandomAccessList<>(drops, Entity.DefaultDrop::stack, FROM_FUNCTION), victim.getExpReward(), 0, deathMessage, stringDeathMessage); // Paper - Adventure & improve drops
event.setKeepInventory(keepInventory);
event.setKeepLevel(victim.keepLevel); // SPIGOT-2222: pre-set keepLevel
populateFields(victim, event); // Paper - make cancellable
@@ -997,10 +1004,13 @@ public class CraftEventFactory {
victim.expToDrop = event.getDroppedExp();
victim.newExp = event.getNewExp();
- for (org.bukkit.inventory.ItemStack stack : event.getDrops()) {
- if (stack == null || stack.getType() == Material.AIR) continue;
+ // Paper start
+ for (Entity.DefaultDrop drop : drops) {
+ final org.bukkit.inventory.ItemStack stack = drop.stack();
+ if (drop == null || stack == null || stack.getType() == Material.AIR) continue;
+ // Paper end
- world.dropItem(entity.getLocation(), stack);
+ drop.runConsumer(world, entity.getLocation()); // Paper
}
return event;