mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-30 16:19:03 +01:00
30285e7121
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 3a3bea52 SPIGOT-7829: Increase maximum outgoing plugin message size to match Vanilla intention 5cd1c8cb SPIGOT-7831: Add CreatureSpawnEvent.SpawnReason#POTION_EFFECT a8e278f0 SPIGOT-7827: Sync EntityPortalEvent with PlayerPortalEvent since non-players can now create portals 53729d12 Remove spurious ApiStatus.Internal annotation b9f57486 SPIGOT-7799, PR-1039: Expose explosion world interaction in EntityExplodeEvent and BlockExplodeEvent 7983b966 PR-1029: Trial changing a small number of inner enums to classes/interfaces to better support custom values CraftBukkit Changes: 403accd56 SPIGOT-7831: Add CreatureSpawnEvent.SpawnReason#POTION_EFFECT 812761660 Increase outdated build delay bed1e3ff6 SPIGOT-7827: Sync EntityPortalEvent with PlayerPortalEvent since non-players can now create portals 2444c8b23 SPIGOT-7823: Suspicious sand and gravel material are not marked as having gravity correctly aceddcd0b SPIGOT-7820: Enum changes - duplicate method name a0d2d6a84 SPIGOT-7813: Material#isInteractable() always returns false 8fd64b091 SPIGOT-7806: Handle both loot and inventory item drop behaviour in PlayerDeathEvent a4ee40b74 SPIGOT-7799, PR-1436: Expose explosion world interaction in EntityExplodeEvent and BlockExplodeEvent 082aa51c5 PR-1424: Trial changing a small number of inner enums to classes/interfaces to better support custom values 66e78a96b SPIGOT-7815: Consider EntityDamageEvent status for Wolf armor damage Spigot Changes: 5bbef5ad SPIGOT-7834: Modify max value for generic.max_absorption
270 lines
18 KiB
Diff
270 lines
18 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -0,0 +0,0 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
|
|
if (this.isRemoved()) {
|
|
return;
|
|
}
|
|
- java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<>(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.has(item, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) {
|
|
- loot.add(CraftItemStack.asCraftMirror(item).markForInventoryDrop());
|
|
+ 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);
|
|
- this.dropCustomDeathLoot(this.serverLevel(), damageSource, flag);
|
|
+ // Paper - Restore vanilla drops behaviour; custom death loot is a noop on server player, remove.
|
|
|
|
loot.addAll(this.drops);
|
|
this.drops.clear(); // SPIGOT-5188: make sure to clear
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -0,0 +0,0 @@ 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) {
|
|
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
|
protected float appliedScale;
|
|
// CraftBukkit start
|
|
public int expToDrop;
|
|
- 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
|
|
@@ -0,0 +0,0 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
|
|
@Override
|
|
protected void dropCustomDeathLoot(ServerLevel world, DamageSource source, boolean causedByPlayer) {
|
|
super.dropCustomDeathLoot(world, source, causedByPlayer);
|
|
- 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
@@ -0,0 +0,0 @@ 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(world, damageSource); // Paper
|
|
}
|
|
|
|
@@ -0,0 +0,0 @@ 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);
|
|
}
|
|
}
|
|
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -0,0 +0,0 @@ public class CraftEventFactory {
|
|
}
|
|
|
|
public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, DamageSource damageSource) {
|
|
- return CraftEventFactory.callEntityDeathEvent(victim, damageSource, new ArrayList<org.bukkit.inventory.ItemStack>(0));
|
|
+ return CraftEventFactory.callEntityDeathEvent(victim, damageSource, new ArrayList<>(0)); // Paper - Restore vanilla drops behavior
|
|
}
|
|
|
|
- public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, DamageSource damageSource, List<org.bukkit.inventory.ItemStack> drops) {
|
|
+ public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, DamageSource damageSource, List<Entity.DefaultDrop> drops) { // Paper - Restore vanilla drops behavior
|
|
// 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) {
|
|
+
|
|
+ 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, DamageSource damageSource, List<Entity.DefaultDrop> drops, Runnable lootCheck) { // Paper
|
|
// Paper end
|
|
CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity();
|
|
CraftDamageSource bukkitDamageSource = new CraftDamageSource(damageSource);
|
|
- EntityDeathEvent event = new EntityDeathEvent(entity, bukkitDamageSource, drops, victim.getExpReward(damageSource.getEntity()));
|
|
+ EntityDeathEvent event = new EntityDeathEvent(entity, bukkitDamageSource, new io.papermc.paper.util.TransformingRandomAccessList<>(drops, Entity.DefaultDrop::stack, FROM_FUNCTION), victim.getExpReward(damageSource.getEntity())); // Paper - Restore vanilla drops behavior
|
|
populateFields(victim, event); // Paper - make cancellable
|
|
CraftWorld world = (CraftWorld) entity.getWorld();
|
|
Bukkit.getServer().getPluginManager().callEvent(event);
|
|
@@ -0,0 +0,0 @@ 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, DamageSource damageSource, List<org.bukkit.inventory.ItemStack> drops, net.kyori.adventure.text.Component deathMessage, boolean keepInventory) { // Paper - Adventure
|
|
+ public static PlayerDeathEvent callPlayerDeathEvent(ServerPlayer victim, DamageSource damageSource, List<Entity.DefaultDrop> drops, net.kyori.adventure.text.Component deathMessage, boolean keepInventory) { // Paper - Adventure & Restore vanilla drops behavior
|
|
CraftPlayer entity = victim.getBukkitEntity();
|
|
CraftDamageSource bukkitDamageSource = new CraftDamageSource(damageSource);
|
|
- PlayerDeathEvent event = new PlayerDeathEvent(entity, bukkitDamageSource, drops, victim.getExpReward(damageSource.getEntity()), 0, deathMessage);
|
|
+ PlayerDeathEvent event = new PlayerDeathEvent(entity, bukkitDamageSource, new io.papermc.paper.util.TransformingRandomAccessList<>(drops, Entity.DefaultDrop::stack, FROM_FUNCTION), victim.getExpReward(damageSource.getEntity()), 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
|
|
@@ -0,0 +0,0 @@ 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;
|
|
|
|
- if (stack instanceof CraftItemStack craftItemStack && craftItemStack.isForInventoryDrop()) {
|
|
- victim.drop(CraftItemStack.asNMSCopy(stack), true, false, false); // SPIGOT-7800, SPIGOT-7801: Vanilla Behaviour for Player Inventory dropped items
|
|
- } else {
|
|
- victim.forceDrops = true;
|
|
- victim.spawnAtLocation(CraftItemStack.asNMSCopy(stack)); // SPIGOT-7806: Vanilla Behaviour for items not related to Player Inventory dropped items
|
|
- victim.forceDrops = false;
|
|
- }
|
|
+ drop.runConsumer(entity.getWorld(), entity.getLocation()); // Paper - Restore vanilla drops behavior
|
|
}
|
|
|
|
return event;
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
@@ -0,0 +0,0 @@ public final class CraftItemStack extends ItemStack {
|
|
this.setItemMeta(itemMeta);
|
|
}
|
|
|
|
- /**
|
|
- * Gets if the item is marked as an inventory drop in death events.
|
|
- *
|
|
- * @return true if the item is marked as an inventory drop
|
|
- */
|
|
- @ApiStatus.Internal
|
|
- public boolean isForInventoryDrop() {
|
|
- return this.isForInventoryDrop;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Marks this item as an inventory drop in death events.
|
|
- *
|
|
- * @return the ItemStack marked as an inventory drop
|
|
- */
|
|
- @ApiStatus.Internal
|
|
- public ItemStack markForInventoryDrop() {
|
|
- this.isForInventoryDrop = true;
|
|
- return this;
|
|
- }
|
|
-
|
|
@Override
|
|
public MaterialData getData() {
|
|
return this.handle != null ? CraftMagicNumbers.getMaterialData(this.handle.getItem()) : super.getData();
|