mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-30 04:02:50 +01:00
241 lines
16 KiB
Diff
241 lines
16 KiB
Diff
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 bcdda70cb55113c37f5f1fbd511dc1ce973361e3..0eb3384df396508c3d26d1e155cd0e6d64251346 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -944,22 +944,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, false))); // Paper - drop function taken from Inventory#dropAll (don't fire drop event)
|
|
}
|
|
}
|
|
}
|
|
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
|
|
|
|
@@ -2441,8 +2439,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 - override method with most params
|
|
+ ItemEntity entityitem = super.drop(stack, throwRandomly, retainOwnership, callDropEvent); // Paper - 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 1f2e74969f9d459cf3bc123c96309fc0a6165ea0..0c46a4aeafd03fbbfd590b0362d41bf2b1d5ca74 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -2697,6 +2697,25 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
|
|
|
|
@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) {
|
|
@@ -2704,14 +2723,21 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
|
|
} 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 bc45bd5816b1b62cdd6011f2372702451b83f22b..96885946be3b8e129984353f3dfe4330e73ad84a 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -254,7 +254,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 ad6df234b618235b4e08fb45952d4f0f0078fded..d65a6de8a0fc4859b7313c903201a41a7da830fa 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
|
|
@@ -534,10 +534,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 ab708b256183fc54fe8e13f341d8a38acf611739..1e86e86b0a100a5d14aee10b60e70c32d2733660 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
@@ -610,7 +610,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
|
|
}
|
|
|
|
@@ -624,7 +624,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);
|
|
}
|
|
}
|
|
@@ -632,7 +632,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 a7da88af0a41f16b3a54b28023a5afb6d9396262..f67ec3f5f4b7e2f678609f2387cc8afa2adce161 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -939,17 +939,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);
|
|
@@ -963,19 +967,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
|
|
+ for (Entity.DefaultDrop drop : drops) {
|
|
+ if (drop == null) continue;;
|
|
+ final org.bukkit.inventory.ItemStack stack = drop.stack();
|
|
+ // Paper end
|
|
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
|
|
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
|
|
@@ -994,10 +1002,14 @@ public class CraftEventFactory {
|
|
victim.expToDrop = event.getDroppedExp();
|
|
victim.newExp = event.getNewExp();
|
|
|
|
- for (org.bukkit.inventory.ItemStack stack : event.getDrops()) {
|
|
+ // Paper start
|
|
+ for (Entity.DefaultDrop drop : drops) {
|
|
+ if (drop == null) continue;
|
|
+ final org.bukkit.inventory.ItemStack stack = drop.stack();
|
|
+ // Paper end
|
|
if (stack == null || stack.getType() == Material.AIR) continue;
|
|
|
|
- world.dropItem(entity.getLocation(), stack);
|
|
+ drop.runConsumer(world, entity.getLocation()); // Paper
|
|
}
|
|
|
|
return event;
|