Fix advancement triggers on entity death

Fixes PaperMC/Paper#3729
Fixes PaperMC/Paper#4252
This commit is contained in:
BillyGalbreath
2021-05-29 06:50:37 -05:00
parent 49a0fb907c
commit 4c2d7e5651
2 changed files with 209 additions and 77 deletions

View File

@@ -0,0 +1,209 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <blake.galbreath@gmail.com>
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<ItemStack> bp;
+ private final NonNullList<ItemStack> bp; public List<ItemStack> getHandEquipment() { return this.bp; } // Purpur - OBFHELPER
public final float[] dropChanceHand;
- private final NonNullList<ItemStack> bq;
+ private final NonNullList<ItemStack> bq; public List<ItemStack> 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<List<ItemStack>> cloneEquipment() {
+ List<List<ItemStack>> list = new java.util.ArrayList<>();
+ List<ItemStack> handItems = new java.util.ArrayList<>();
+ for (ItemStack item : this.getHandEquipment()) {
+ handItems.add(item.cloneItemStack());
+ }
+ list.add(handItems);
+ List<ItemStack> armorItems = new java.util.ArrayList<>();
+ for (ItemStack item : this.getArmorEquipment()) {
+ armorItems.add(item.cloneItemStack());
+ }
+ list.add(armorItems);
+ return list;
+ }
+
+ public void restoreEquipment(List<List<ItemStack>> list) {
+ this.getHandEquipment().clear();
+ List<ItemStack> handItems = list.get(0);
+ for (int i = 0; i < handItems.size(); i++) {
+ this.getHandEquipment().set(i, handItems.get(1));
+ }
+ this.getArmorEquipment().clear();
+ List<ItemStack> 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<List<ItemStack>> 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<List<ItemStack>> cloneEquipment();
+ public abstract void restoreEquipment(List<List<ItemStack>> 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<List<ItemStack>> cloneEquipment() {
+ List<List<ItemStack>> list = new java.util.ArrayList<>();
+ List<ItemStack> handItems = new java.util.ArrayList<>();
+ for (ItemStack item : this.handItems) {
+ handItems.add(item.cloneItemStack());
+ }
+ list.add(handItems);
+ List<ItemStack> armorItems = new java.util.ArrayList<>();
+ for (ItemStack item : this.armorItems) {
+ armorItems.add(item.cloneItemStack());
+ }
+ list.add(armorItems);
+ return list;
+ }
+
+ public void restoreEquipment(List<List<ItemStack>> list) {
+ this.handItems.clear();
+ List<ItemStack> handItems = list.get(0);
+ for (int i = 0; i < handItems.size(); i++) {
+ this.handItems.set(i, handItems.get(1));
+ }
+ this.armorItems.clear();
+ List<ItemStack> 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<List<ItemStack>> cloneEquipment() {
+ List<List<ItemStack>> list = new java.util.ArrayList<>();
+ List<ItemStack> invItems = new java.util.ArrayList<>();
+ for (ItemStack item : this.inventory.items) {
+ invItems.add(item.cloneItemStack());
+ }
+ list.add(invItems);
+ List<ItemStack> armorItems = new java.util.ArrayList<>();
+ for (ItemStack item : this.inventory.armor) {
+ armorItems.add(item.cloneItemStack());
+ }
+ list.add(armorItems);
+ List<ItemStack> 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<ItemStack>> list) {
+ this.inventory.items.clear();
+ List<ItemStack> 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<ItemStack> 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<ItemStack> 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

View File

@@ -1,77 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <blake.galbreath@gmail.com>
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
}