Several fixes and new api for experience merging/stacking (#9242)

This commit is contained in:
Jake Potrebic 2024-05-29 13:58:57 -07:00
parent 6496275397
commit 3991e67f19
7 changed files with 148 additions and 70 deletions

View File

@ -1,12 +1,15 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 19 Dec 2017 22:56:24 -0500
Subject: [PATCH] ExperienceOrbMergeEvent
Subject: [PATCH] ExperienceOrb merging/stacking API
Adds ExperienceOrbMergeEvent
Fired when the server is about to merge 2 experience orbs
Plugins can cancel this if they want to ensure experience orbs do not lose important
metadata such as spawn reason, or conditionally move data from source to target.
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/com/destroystokyo/paper/event/entity/ExperienceOrbMergeEvent.java b/src/main/java/com/destroystokyo/paper/event/entity/ExperienceOrbMergeEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
@ -104,3 +107,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/org/bukkit/entity/ExperienceOrb.java b/src/main/java/org/bukkit/entity/ExperienceOrb.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/entity/ExperienceOrb.java
+++ b/src/main/java/org/bukkit/entity/ExperienceOrb.java
@@ -0,0 +0,0 @@ public interface ExperienceOrb extends Entity {
* @param value Amount of experience
*/
public void setExperience(int value);
+
+ // Paper start - expose count
+ /**
+ * Get the stacked count for this experience orb.
+ *
+ * @return the count
+ */
+ int getCount();
+
+ /**
+ * Sets the stacked count for this experience orb.
+ *
+ * @param count the new count
+ */
+ void setCount(int count);
+ // Paper end
// Paper start
/**

View File

@ -23,7 +23,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
itemstack.setDamageValue(itemstack.getDamageValue() - j);
- int k = amount - this.durabilityToXp(j);
+ int k = amount - event.getDurabilityToXpOperation().applyAsInt(j); // Paper - Expand PlayerItemMendEvent
this.value = k; // CraftBukkit - update exp value of orb for PlayerItemMendEvent calls
// this.value = k; // 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
+ // Paper start - Expand PlayerItemMendEvent
+ if (j == 0 && amount == k) { // if repair amount is 0 and no xp was removed, don't do recursion; treat as cancelled
+ return k;

View File

@ -0,0 +1,113 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Fri, 10 Nov 2017 23:03:12 -0500
Subject: [PATCH] ExperienceOrb merging/stacking API and fixes
Adds an option for maximum exp value when merging orbs
Adds ExperienceOrbMergeEvent
Fired when the server is about to merge 2 experience orbs
as entities. Plugins can cancel it if they want to ensure experience orbs do not lose important
metadata such as spawn reason, or conditionally move data from source to target.
Fixes an issue where the stacked count was not taking into account
for mending repairs and when merging with spigot's merge-on-spawn
logic
== AT ==
public net.minecraft.world.entity.ExperienceOrb count
Co-authored-by: Aikar <aikar@aikar.co>
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
@@ -0,0 +0,0 @@ public class ExperienceOrb extends Entity {
}
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<ExperienceOrb> list = world.getEntities(EntityTypeTest.forClass(ExperienceOrb.class), axisalignedbb, (entityexperienceorb) -> {
@@ -0,0 +0,0 @@ public class ExperienceOrb extends Entity {
}
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(EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause
@@ -0,0 +0,0 @@ public class ExperienceOrb extends Entity {
itemstack.setDamageValue(itemstack.getDamageValue() - j);
int k = amount - this.durabilityToXp(j);
- this.value = k; // CraftBukkit - update exp value of orb for PlayerItemMendEvent calls
+ // this.value = k; // 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 k > 0 ? this.repairPlayerItems(player, k) : 0;
} else {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
@@ -0,0 +0,0 @@ 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/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 {
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<Entity> 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

View File

@ -16,5 +16,5 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ event = CraftEventFactory.callEntitySpawnEvent(entity); // Call spawn event for ExperienceOrb entities
+ if (radius > 0 && !event.isCancelled() && !entity.isRemoved()) {
// Paper start - Maximum exp value when merging; Whole section has been tweaked, see comments for specifics
final int maxValue = world.paperConfig().entities.behavior.experienceMergeMaxValue;
final boolean mergeUnconditionally = world.paperConfig().entities.behavior.experienceMergeMaxValue <= 0;
final long maxValue = world.paperConfig().entities.behavior.experienceMergeMaxValue;
final boolean mergeUnconditionally = maxValue <= 0;

View File

@ -1,23 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 19 Dec 2017 22:57:26 -0500
Subject: [PATCH] ExperienceOrbMergeEvent
Has to be reimplemented at one point maybe
Fired when the server is about to merge 2 experience orbs
Plugins can cancel this if they want to ensure experience orbs do not lose important
metadata such as spawn reason, or conditionally move data from source to target.
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 {
for (Entity e : entities) {
if (e instanceof net.minecraft.world.entity.ExperienceOrb loopItem) {
// Paper start
- if (!loopItem.isRemoved() && !(maxValue > 0 && loopItem.value >= maxValue)) {
+ if (!loopItem.isRemoved() && !(maxValue > 0 && 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 (maxValue > 0 && newTotal > (long)maxValue) {

View File

@ -26,7 +26,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
@@ -0,0 +0,0 @@ public class ExperienceOrb extends Entity {
public int value;
private int count;
public int count;
private Player followingPlayer;
+ // Paper start
+ @javax.annotation.Nullable

View File

@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Fri, 10 Nov 2017 23:03:12 -0500
Subject: [PATCH] Option for maximum exp value when merging orbs
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 {
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 int maxValue = world.paperConfig().entities.behavior.experienceMergeMaxValue;
+ final boolean mergeUnconditionally = world.paperConfig().entities.behavior.experienceMergeMaxValue <= 0;
+ if (mergeUnconditionally || xp.value < maxValue) { // Paper - Skip iteration if unnecessary
+
List<Entity> 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() && !(maxValue > 0 && loopItem.value >= maxValue)) {
+ long newTotal = (long)xp.value + (long)loopItem.value;
+ if ((int) newTotal < 0) continue; // Overflow
+ if (maxValue > 0 && newTotal > (long)maxValue) {
+ loopItem.value = (int) (newTotal - maxValue);
+ xp.value = 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