mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-02 04:56:50 +01:00
89d51d5f29
Because this exploit has been widely known for years and has not been fixed by Mojang, we decided that it was worth allowing people to toggle it on/off due to how easy it is to make it configurable. It should be noted that this decision does not promise all future exploits will be configurable.
243 lines
17 KiB
Diff
243 lines
17 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 7272dc058c575efee5ac2643ce41b7d12e346e89..ae5a2136a0e266d4c35190f5d33552994c842786 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
|
@@ -891,22 +891,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
|
|
|
|
@@ -2389,8 +2387,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 67dfb7b6a8825a87ff69b8c33efd1c040db12bf0..198aeb71e958a3fa6f8eaa8f607120af483e597f 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -2496,6 +2496,25 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
|
|
|
|
@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) {
|
|
@@ -2503,14 +2522,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 - 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 bdae6ea5528164995d9a256be6a2f9d0cb15d8ff..211e9bb82e65bdd5811c3b4df7ac93741fc7cdc3 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 - 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 6c215470ad05d59f903cbeff15088a03b42c3f66..12440ee2dccc0a697fb403765f2e1b987ccc0283 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
|
|
@@ -535,10 +535,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 f62b5976e307a69ca40d51ae76126005c801df0c..bbe299afd361a107e3936c8ea1a62067fcac9b7e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
|
|
@@ -611,7 +611,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 - Restore vanilla drops behavior
|
|
return this.brokenByAnything(damageSource); // Paper
|
|
}
|
|
|
|
@@ -625,7 +625,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);
|
|
}
|
|
}
|
|
@@ -633,7 +633,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 7f7ec71e352e403169603dcd95b4dd50d68d37f7..f2f58ae16101293a7df53c32be015111baba1504 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;
|