diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch index 6af717c0b..da00e3e03 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ExperienceOrb.java.patch @@ -119,7 +119,7 @@ } } -@@ -150,12 +227,20 @@ +@@ -150,18 +227,27 @@ } public static void award(ServerLevel world, Vec3 pos, int amount) { @@ -141,8 +141,22 @@ } } -@@ -190,7 +275,7 @@ + } + + private static boolean tryMergeToExisting(ServerLevel world, Vec3 pos, int amount) { ++ // Paper - TODO some other event for this kind of merge + AABB axisalignedbb = AABB.ofSize(pos, 1.0D, 1.0D, 1.0D); + int j = world.getRandom().nextInt(40); + List list = world.getEntities(EntityTypeTest.forClass(ExperienceOrb.class), axisalignedbb, (entityexperienceorb) -> { +@@ -188,9 +274,14 @@ + } + private void merge(ExperienceOrb other) { ++ // Paper start - call orb merge event ++ if (!new com.destroystokyo.paper.event.entity.ExperienceOrbMergeEvent((org.bukkit.entity.ExperienceOrb) this.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) other.getBukkitEntity()).callEvent()) { ++ return; ++ } ++ // Paper end - call orb merge event this.count += other.count; this.age = Math.min(this.age, other.age); - other.discard(); @@ -150,7 +164,7 @@ } private void setUnderwaterMovement() { -@@ -215,7 +300,7 @@ +@@ -215,7 +306,7 @@ this.markHurt(); this.health = (int) ((float) this.health - amount); if (this.health <= 0) { @@ -159,7 +173,7 @@ } return true; -@@ -226,33 +311,35 @@ +@@ -226,33 +317,35 @@ public void addAdditionalSaveData(CompoundTag nbt) { nbt.putShort("Health", (short) this.health); nbt.putShort("Age", (short) this.age); @@ -201,7 +215,7 @@ } } -@@ -266,12 +353,20 @@ +@@ -266,12 +359,20 @@ ItemStack itemstack = ((EnchantedItemInUse) optional.get()).itemStack(); int j = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.serverLevel(), itemstack, amount); int k = Math.min(j, itemstack.getDamageValue()); @@ -218,11 +232,11 @@ int l = amount - k * amount / j; if (l > 0) { -+ this.value = l; // CraftBukkit - update exp value of orb for PlayerItemMendEvent calls ++ // this.value = l; // CraftBukkit - update exp value of orb for PlayerItemMendEvent calls // Paper - the value field should not be mutated here because it doesn't take "count" into account return this.repairPlayerItems(player, l); } } -@@ -291,6 +386,24 @@ +@@ -291,6 +392,24 @@ } public static int getExperienceValue(int value) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java index 5a7d314ec..650e4a01c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java @@ -18,6 +18,18 @@ public class CraftExperienceOrb extends CraftEntity implements ExperienceOrb { this.getHandle().value = value; } + // Paper start - expose count + @Override + public int getCount() { + return this.getHandle().count; + } + + @Override + public void setCount(final int count) { + this.getHandle().count = count; + } + // Paper end + // Paper start public java.util.UUID getTriggerEntityId() { return getHandle().triggerEntityId; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index f482fc14e..b8e1a7251 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -712,15 +712,29 @@ public class CraftEventFactory { if (entity instanceof net.minecraft.world.entity.ExperienceOrb xp) { double radius = world.spigotConfig.expMerge; if (radius > 0) { + // Paper start - Maximum exp value when merging; Whole section has been tweaked, see comments for specifics + final long maxValue = world.paperConfig().entities.behavior.experienceMergeMaxValue; + final boolean mergeUnconditionally = maxValue <= 0; + if (mergeUnconditionally || xp.value < maxValue) { // Paper - Skip iteration if unnecessary + List entities = world.getEntities(entity, entity.getBoundingBox().inflate(radius, radius, radius)); for (Entity e : entities) { if (e instanceof net.minecraft.world.entity.ExperienceOrb loopItem) { - if (!loopItem.isRemoved()) { + // Paper start + if (!loopItem.isRemoved() && xp.count == loopItem.count && (mergeUnconditionally || loopItem.value < maxValue) && new com.destroystokyo.paper.event.entity.ExperienceOrbMergeEvent((org.bukkit.entity.ExperienceOrb) entity.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) loopItem.getBukkitEntity()).callEvent()) { // Paper - ExperienceOrbMergeEvent + long newTotal = (long)xp.value + (long)loopItem.value; + if ((int) newTotal < 0) continue; // Overflow + if (!mergeUnconditionally && newTotal > maxValue) { + loopItem.value = (int) (newTotal - maxValue); + xp.value = (int) maxValue; + } else { xp.value += loopItem.value; loopItem.discard(null); // Add Bukkit remove cause + } // Paper end - Maximum exp value when merging } } } + } // Paper end - End iteration skip check - All tweaking ends here } } // Spigot end