From 4c2d7e5651db181a7847698ae7b569392cba1fd6 Mon Sep 17 00:00:00 2001 From: BillyGalbreath Date: Sat, 29 May 2021 06:50:37 -0500 Subject: [PATCH] Fix advancement triggers on entity death Fixes PaperMC/Paper#3729 Fixes PaperMC/Paper#4252 --- ...advancement-triggers-on-entity-death.patch | 209 ++++++++++++++++++ ...s-not-giving-voluntary-exile-advance.patch | 77 ------- 2 files changed, 209 insertions(+), 77 deletions(-) create mode 100644 patches/server/0224-Fix-advancement-triggers-on-entity-death.patch delete mode 100644 patches/server/0224-Fix-raid-captains-not-giving-voluntary-exile-advance.patch diff --git a/patches/server/0224-Fix-advancement-triggers-on-entity-death.patch b/patches/server/0224-Fix-advancement-triggers-on-entity-death.patch new file mode 100644 index 000000000..d9998d875 --- /dev/null +++ b/patches/server/0224-Fix-advancement-triggers-on-entity-death.patch @@ -0,0 +1,209 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath +Date: Fri, 28 May 2021 12:24:45 -0500 +Subject: [PATCH] Fix advancement triggers on entity death + +This fixes PaperMC/Paper#3729 and PaperMC/Paper#4252 + +Paper changes logical order revolving around making the EntityDeathEvent +cancellable. Reordering this logic has ended up with entity equipment being +cleared _before_ advancement criteria can run, causing things like killing +raid captains not giving the voluntary exile advancement. + +This fixes the issue by storing a copy of the equipment in a new field just +before doing the death event logic where the equipment is cleared and then +restoring it back to the entity just before the criterion triggers run and +then finally clearing the equipment again right after the criterion is done. + +diff --git a/src/main/java/net/minecraft/world/entity/EntityInsentient.java b/src/main/java/net/minecraft/world/entity/EntityInsentient.java +index b895288e0aa49e5a1222f7c5897f3dc43ff946fe..e4f5a5ffd25d4d4e427938a633675be46805af66 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityInsentient.java ++++ b/src/main/java/net/minecraft/world/entity/EntityInsentient.java +@@ -106,9 +106,9 @@ public abstract class EntityInsentient extends EntityLiving { + public PathfinderGoalSelector targetSelector; + private EntityLiving goalTarget; + private final EntitySenses bo; +- private final NonNullList bp; ++ private final NonNullList bp; public List getHandEquipment() { return this.bp; } // Purpur - OBFHELPER + public final float[] dropChanceHand; +- private final NonNullList bq; ++ private final NonNullList bq; public List getArmorEquipment() { return this.bq; } // Purpur - OBFHELPER + public final float[] dropChanceArmor; + // private boolean canPickUpLoot; // CraftBukkit - moved up to EntityLiving + public boolean persistent; +@@ -1013,6 +1013,41 @@ public abstract class EntityInsentient extends EntityLiving { + } + } + ++ // Purpur start ++ public List> cloneEquipment() { ++ List> list = new java.util.ArrayList<>(); ++ List handItems = new java.util.ArrayList<>(); ++ for (ItemStack item : this.getHandEquipment()) { ++ handItems.add(item.cloneItemStack()); ++ } ++ list.add(handItems); ++ List armorItems = new java.util.ArrayList<>(); ++ for (ItemStack item : this.getArmorEquipment()) { ++ armorItems.add(item.cloneItemStack()); ++ } ++ list.add(armorItems); ++ return list; ++ } ++ ++ public void restoreEquipment(List> list) { ++ this.getHandEquipment().clear(); ++ List handItems = list.get(0); ++ for (int i = 0; i < handItems.size(); i++) { ++ this.getHandEquipment().set(i, handItems.get(1)); ++ } ++ this.getArmorEquipment().clear(); ++ List armorItems = list.get(1); ++ for (int i = 0; i < armorItems.size(); i++) { ++ this.getArmorEquipment().set(i, armorItems.get(i)); ++ } ++ } ++ ++ public void clearEquipment() { ++ this.getHandEquipment().clear(); ++ this.getArmorEquipment().clear(); ++ } ++ // Purpur end ++ + @Override + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + switch (enumitemslot.a()) { +diff --git a/src/main/java/net/minecraft/world/entity/EntityLiving.java b/src/main/java/net/minecraft/world/entity/EntityLiving.java +index 6b65b17d6b100adacd628d08a24a68164d2bb9aa..2f7352d7ae99ecdefe0f7b56cebf008906607d4d 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityLiving.java ++++ b/src/main/java/net/minecraft/world/entity/EntityLiving.java +@@ -1560,10 +1560,13 @@ public abstract class EntityLiving extends Entity { + } + + // Paper start ++ List> equipmentSnapshotBefore = this.cloneEquipment(); // Purpur + org.bukkit.event.entity.EntityDeathEvent deathEvent = this.d(damagesource); + if (deathEvent == null || !deathEvent.isCancelled()) { + if (this.getKillCount() >= 0 && entityliving != null) { ++ this.restoreEquipment(equipmentSnapshotBefore); // Purpur + entityliving.runKillTrigger(this, this.getKillCount(), damagesource); ++ this.clearEquipment(); // Purpur + } + if (this.isSleeping()) { + this.entityWakeup(); +@@ -2274,6 +2277,12 @@ public abstract class EntityLiving extends Entity { + + public abstract ItemStack getEquipment(EnumItemSlot enumitemslot); + ++ // Purpur start ++ public abstract List> cloneEquipment(); ++ public abstract void restoreEquipment(List> list); ++ public abstract void clearEquipment(); ++ // Purpur end ++ + // CraftBukkit start + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack, boolean silent) { + this.setSlot(enumitemslot, itemstack); +diff --git a/src/main/java/net/minecraft/world/entity/decoration/EntityArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/EntityArmorStand.java +index ddc1c3383cdc32fa832485f3922c74185731557a..e28a2d983ae7b83f435c9dea606a92e2fbd3391a 100644 +--- a/src/main/java/net/minecraft/world/entity/decoration/EntityArmorStand.java ++++ b/src/main/java/net/minecraft/world/entity/decoration/EntityArmorStand.java +@@ -171,6 +171,41 @@ public class EntityArmorStand extends EntityLiving { + } + } + ++ // Purpur start ++ public List> cloneEquipment() { ++ List> list = new java.util.ArrayList<>(); ++ List handItems = new java.util.ArrayList<>(); ++ for (ItemStack item : this.handItems) { ++ handItems.add(item.cloneItemStack()); ++ } ++ list.add(handItems); ++ List armorItems = new java.util.ArrayList<>(); ++ for (ItemStack item : this.armorItems) { ++ armorItems.add(item.cloneItemStack()); ++ } ++ list.add(armorItems); ++ return list; ++ } ++ ++ public void restoreEquipment(List> list) { ++ this.handItems.clear(); ++ List handItems = list.get(0); ++ for (int i = 0; i < handItems.size(); i++) { ++ this.handItems.set(i, handItems.get(1)); ++ } ++ this.armorItems.clear(); ++ List armorItems = list.get(1); ++ for (int i = 0; i < armorItems.size(); i++) { ++ this.armorItems.set(i, armorItems.get(1)); ++ } ++ } ++ ++ public void clearEquipment() { ++ this.handItems.clear(); ++ this.armorItems.clear(); ++ } ++ // Purpur end ++ + @Override + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + // CraftBukkit start +diff --git a/src/main/java/net/minecraft/world/entity/player/EntityHuman.java b/src/main/java/net/minecraft/world/entity/player/EntityHuman.java +index abac4b996c7935ac6115a9ad96191f2a57afc95f..0443aaa9fc4058cc4ce61977a89cdf6ea6452b65 100644 +--- a/src/main/java/net/minecraft/world/entity/player/EntityHuman.java ++++ b/src/main/java/net/minecraft/world/entity/player/EntityHuman.java +@@ -1892,6 +1892,52 @@ public abstract class EntityHuman extends EntityLiving { + return enumitemslot == EnumItemSlot.MAINHAND ? this.inventory.getItemInHand() : (enumitemslot == EnumItemSlot.OFFHAND ? (ItemStack) this.inventory.extraSlots.get(0) : (enumitemslot.a() == EnumItemSlot.Function.ARMOR ? (ItemStack) this.inventory.armor.get(enumitemslot.b()) : ItemStack.b)); + } + ++ // Purpur start ++ public List> cloneEquipment() { ++ List> list = new java.util.ArrayList<>(); ++ List invItems = new java.util.ArrayList<>(); ++ for (ItemStack item : this.inventory.items) { ++ invItems.add(item.cloneItemStack()); ++ } ++ list.add(invItems); ++ List armorItems = new java.util.ArrayList<>(); ++ for (ItemStack item : this.inventory.armor) { ++ armorItems.add(item.cloneItemStack()); ++ } ++ list.add(armorItems); ++ List extraItems = new java.util.ArrayList<>(); ++ for (ItemStack item : this.inventory.extraSlots) { ++ extraItems.add(item.cloneItemStack()); ++ } ++ list.add(extraItems); ++ return list; ++ } ++ ++ public void restoreEquipment(List> list) { ++ this.inventory.items.clear(); ++ List invItems = list.get(0); ++ for (int i = 0; i < invItems.size(); i++) { ++ this.inventory.items.set(i, invItems.get(1)); ++ } ++ this.inventory.armor.clear(); ++ List armorItems = list.get(1); ++ for (int i = 0; i < armorItems.size(); i++) { ++ this.inventory.armor.set(i, armorItems.get(1)); ++ } ++ this.inventory.extraSlots.clear(); ++ List extraItems = list.get(2); ++ for (int i = 0; i < extraItems.size(); i++) { ++ this.inventory.extraSlots.set(i, extraItems.get(1)); ++ } ++ } ++ ++ public void clearEquipment() { ++ this.inventory.items.clear(); ++ this.inventory.armor.clear(); ++ this.inventory.extraSlots.clear(); ++ } ++ // Purpur end ++ + @Override + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + // CraftBukkit start diff --git a/patches/server/0224-Fix-raid-captains-not-giving-voluntary-exile-advance.patch b/patches/server/0224-Fix-raid-captains-not-giving-voluntary-exile-advance.patch deleted file mode 100644 index bdcf09fe4..000000000 --- a/patches/server/0224-Fix-raid-captains-not-giving-voluntary-exile-advance.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Fri, 28 May 2021 12:24:45 -0500 -Subject: [PATCH] Fix raid captains not giving voluntary exile advancement - -This fixes PaperMC/Paper#3729 where killing a raid captain does not give the -voluntary exile advancement. The problem is 2 fold on Paper's end. - -Firstly, Paper changes logical order revolving around making the EntityDeathEvent -cancellable. Reordering this logic has ended up with raid captains unsetting the -banner on their head _before_ the criterion triggers for the advancement leaving -the criterion to believe a regular pillager has been killed, not a captain. - -Secondly, Paper sets itemstacks to count of 0 too early in an attempt to reduce -the amount of dupes caused by leaking them through various gameplay mechanics. This -means the banner on their head get converted to air before the criterion triggers -leaving the criterion to believe a regular pillager has been killed. - -This fixes both issues by storing a copy of the banner in a new field that is set -back to the head slot just before the criterion triggers and then removed again -right after the criterion is finished running. - -diff --git a/src/main/java/net/minecraft/world/entity/EntityLiving.java b/src/main/java/net/minecraft/world/entity/EntityLiving.java -index 6b65b17d6b100adacd628d08a24a68164d2bb9aa..6acef38b805b35e1f2ae9e6f4d2f387a5050d0f7 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityLiving.java -+++ b/src/main/java/net/minecraft/world/entity/EntityLiving.java -@@ -1563,7 +1563,9 @@ public abstract class EntityLiving extends Entity { - org.bukkit.event.entity.EntityDeathEvent deathEvent = this.d(damagesource); - if (deathEvent == null || !deathEvent.isCancelled()) { - if (this.getKillCount() >= 0 && entityliving != null) { -+ this.fixHead(); // Purpur - entityliving.runKillTrigger(this, this.getKillCount(), damagesource); -+ this.fixHead(); // Purpur - } - if (this.isSleeping()) { - this.entityWakeup(); -@@ -3902,4 +3904,6 @@ public abstract class EntityLiving extends Entity { - this.shieldBlockingDelay = shieldBlockingDelay; - } - // Paper end -+ -+ public void fixHead() {} // Purpur - } -diff --git a/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java b/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java -index 8eec32af12c69e1963dcd304a25ec4811b2f1f5a..d0cf1eca6f077673aa353e92ef9732f0fe5d0b54 100644 ---- a/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java -+++ b/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java -@@ -152,6 +152,10 @@ public abstract class EntityRaider extends EntityMonsterPatrolling { - } - - if (!itemstack.isEmpty() && ItemStack.matches(itemstack, Raid.s()) && entityhuman != null) { -+ // Purpur start -+ this.fixHead = itemstack.cloneItemStack(); -+ this.fixHead.setCount(1); -+ // Purpur end - MobEffect mobeffect = entityhuman.getEffect(MobEffects.BAD_OMEN); - byte b0 = 1; - int i; -@@ -564,4 +568,18 @@ public abstract class EntityRaider extends EntityMonsterPatrolling { - - } - } -+ -+ // Purpur start -+ private ItemStack fixHead = null; -+ -+ @Override -+ public void fixHead() { -+ if (this.fixHead != null) { -+ setSlot(EnumItemSlot.HEAD, this.fixHead); -+ this.fixHead = null; -+ } else { -+ setSlot(EnumItemSlot.HEAD, ItemStack.NULL_ITEM); -+ } -+ } -+ // Purpur end - }