From e17eb6be0c14ca133f3b036d2f6729fd36e1a8d5 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 28 Sep 2024 11:05:45 -0700 Subject: [PATCH] Improve entity effect API (#11444) --- .../api/0491-Improve-entity-effect-API.patch | 140 ++++++++++++++++ .../server/0854-Bandaid-fix-for-Effect.patch | 5 +- .../1060-Improve-entity-effect-API.patch | 152 ++++++++++++++++++ 3 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 patches/api/0491-Improve-entity-effect-API.patch create mode 100644 patches/server/1060-Improve-entity-effect-API.patch diff --git a/patches/api/0491-Improve-entity-effect-API.patch b/patches/api/0491-Improve-entity-effect-API.patch new file mode 100644 index 0000000000..912d051f49 --- /dev/null +++ b/patches/api/0491-Improve-entity-effect-API.patch @@ -0,0 +1,140 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Fri, 27 Sep 2024 17:12:50 -0700 +Subject: [PATCH] Improve entity effect API + + +diff --git a/src/main/java/org/bukkit/EntityEffect.java b/src/main/java/org/bukkit/EntityEffect.java +index 5341957b10cccd7bce5a7595699b1d90412a01d0..83d623cc824ddeeae7350c9f9a757fcf8e97c9ea 100644 +--- a/src/main/java/org/bukkit/EntityEffect.java ++++ b/src/main/java/org/bukkit/EntityEffect.java +@@ -112,11 +112,25 @@ public enum EntityEffect { + // 5 - unused + /** + * The smoke when taming an entity fails. ++ * @deprecated use {@link EntityEffect#TAMING_FAILED} + */ ++ @Deprecated(since = "1.21") // Paper + WOLF_SMOKE(6, Tameable.class), ++ // Paper start - rename "wolf" effects ++ /** ++ * The smoke when taming an entity fails. ++ */ ++ TAMING_FAILED(6, Tameable.class), ++ /** ++ * The hearts when taming an entity succeeds. ++ */ ++ TAMING_SUCCEEDED(7, Tameable.class), ++ // Paper end - rename "wolf" effects + /** + * The hearts when taming an entity succeeds. ++ * @deprecated use {@link EntityEffect#TAMING_SUCCEEDED} + */ ++ @Deprecated(since = "1.21") // Paper + WOLF_HEARTS(7, Tameable.class), + /** + * When a wolf shakes (after being wet). +@@ -204,7 +218,9 @@ public enum EntityEffect { + ARMOR_STAND_HIT(32, ArmorStand.class), + /** + * Entity hurt by thorns attack. ++ * @deprecated in favor of {@link LivingEntity#playHurtAnimation(float)} or {@link Entity#broadcastHurtAnimation(java.util.Collection)} + */ ++ @Deprecated(since = "1.19.4", forRemoval = true) // Paper + THORNS_HURT(33, LivingEntity.class), + /** + * Iron golem puts away rose. +@@ -216,11 +232,15 @@ public enum EntityEffect { + TOTEM_RESURRECT(35, LivingEntity.class), + /** + * Entity hurt due to drowning damage. ++ * @deprecated in favor of {@link LivingEntity#playHurtAnimation(float)} or {@link Entity#broadcastHurtAnimation(java.util.Collection)} + */ ++ @Deprecated(since = "1.19.4", forRemoval = true) + HURT_DROWN(36, LivingEntity.class), + /** + * Entity hurt due to explosion damage. ++ * @deprecated in favor of {@link LivingEntity#playHurtAnimation(float)} or {@link Entity#broadcastHurtAnimation(java.util.Collection)} + */ ++ @Deprecated(since = "1.19.4", forRemoval = true) + HURT_EXPLOSION(37, LivingEntity.class), + /** + * Dolphin has been fed and is locating a structure. +@@ -244,11 +264,15 @@ public enum EntityEffect { + VILLAGER_SPLASH(42, Villager.class), + /** + * Player's bad omen effect removed to start or increase raid difficult. ++ * @deprecated raid system was overhauled in 1.20.5 + */ ++ @Deprecated(since = "1.20.5", forRemoval = true) + PLAYER_BAD_OMEN_RAID(43, Player.class), + /** + * Entity hurt due to berry bush. Prickly! ++ * @deprecated in favor of {@link LivingEntity#playHurtAnimation(float)} or {@link Entity#broadcastHurtAnimation(java.util.Collection)} + */ ++ @Deprecated(since = "1.19.4", forRemoval = true) + HURT_BERRY_BUSH(44, LivingEntity.class), + /** + * Fox chews the food in its mouth +@@ -331,7 +355,17 @@ public enum EntityEffect { + * Sniffer must have a target and be in {@link Sniffer.State#SEARCHING} or + * {@link Sniffer.State#DIGGING} + */ +- SNIFFER_DIG(63, Sniffer.class); ++ SNIFFER_DIG(63, Sniffer.class), ++ // Paper start - add missing EntityEffect ++ /** ++ * Armadillo peeks out of its shell ++ */ ++ ARMADILLO_PEEK(64, org.bukkit.entity.Armadillo.class), ++ /** ++ * {@link org.bukkit.inventory.EquipmentSlot#BODY} armor piece breaks ++ */ ++ BODY_BREAK(65, LivingEntity.class); ++ // Paper end - add missing EntityEffect + + private final byte data; + private final Class applicable; +diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java +index 725ef320f929d5e3d141c1ed3246d73a7d741f31..d0ae8a94db20281d3664d74718c65234eb2e5f83 100644 +--- a/src/main/java/org/bukkit/entity/Entity.java ++++ b/src/main/java/org/bukkit/entity/Entity.java +@@ -1159,4 +1159,17 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent + */ + @NotNull String getScoreboardEntryName(); + // Paper end - entity scoreboard name ++ ++ // Paper start - broadcast hurt animation ++ /** ++ * Broadcasts a hurt animation. This fakes incoming damage towards the target entity. ++ *

++ * The target players cannot include {@code this} player. For self-damage, use ++ * {@link Player#sendHurtAnimation(float)}. ++ * ++ * @param players the players to broadcast to (cannot include {@code this} ++ * @throws IllegalArgumentException if {@code this} is contained in {@code players} ++ */ ++ void broadcastHurtAnimation(@NotNull java.util.Collection players); ++ // Paper end - broadcast hurt animation + } +diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java +index 7c56182acaf827f4b1a986a61cea8e9960604c98..8086acceacbceb2c5a7228fff005e41a86d37008 100644 +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -3859,4 +3859,16 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM + @Override + Spigot spigot(); + // Spigot end ++ ++ // Paper start - entity effect API ++ /** ++ * Plays an entity effect to this player for the target entity ++ *

++ * If the effect is not applicable to this class of entity, it will not play. ++ * ++ * @param effect the entity effect ++ * @param target the target entity ++ */ ++ void sendEntityEffect(org.bukkit.@NotNull EntityEffect effect, @NotNull Entity target); ++ // Paper end - entity effect API + } diff --git a/patches/server/0854-Bandaid-fix-for-Effect.patch b/patches/server/0854-Bandaid-fix-for-Effect.patch index d7a85ce5c8..c99dd754f1 100644 --- a/patches/server/0854-Bandaid-fix-for-Effect.patch +++ b/patches/server/0854-Bandaid-fix-for-Effect.patch @@ -95,10 +95,10 @@ index 62914bde6f6b045e6b3682581899d756d1b272f1..ccd81bc7279a92f78bb882c9443cf9ca Preconditions.checkArgument(effect.getData() == null || effect == Effect.ELECTRIC_SPARK, "Wrong kind of data for the %s effect", effect); diff --git a/src/test/java/org/bukkit/EffectTest.java b/src/test/java/org/bukkit/EffectTest.java new file mode 100644 -index 0000000000000000000000000000000000000000..eaccc9a154ff2f498d765a2932b6f3edb449f4f7 +index 0000000000000000000000000000000000000000..3129212415c151aa028995b6e698f81b638e4c35 --- /dev/null +++ b/src/test/java/org/bukkit/EffectTest.java -@@ -0,0 +1,79 @@ +@@ -0,0 +1,78 @@ +package org.bukkit; + +import com.google.common.base.Joiner; @@ -134,7 +134,6 @@ index 0000000000000000000000000000000000000000..eaccc9a154ff2f498d765a2932b6f3ed + return !Effect.class.getDeclaredField(effect.name()).isAnnotationPresent(Deprecated.class); + } + -+ @SuppressWarnings("deprecation") + @Test + public void checkAllApiExists() throws ReflectiveOperationException { + Map toId = new HashMap<>(); diff --git a/patches/server/1060-Improve-entity-effect-API.patch b/patches/server/1060-Improve-entity-effect-API.patch new file mode 100644 index 0000000000..5aefeb8279 --- /dev/null +++ b/patches/server/1060-Improve-entity-effect-API.patch @@ -0,0 +1,152 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Fri, 27 Sep 2024 17:13:16 -0700 +Subject: [PATCH] Improve entity effect API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index cd789c235acf740ec29c30b180e7fbe1a140caa9..89c8713d2c2206d1b0d8c0a392c9d13b3e736f0c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1299,4 +1299,15 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + return this.getHandle().getScoreboardName(); + } + // Paper end - entity scoreboard name ++ ++ // Paper start - broadcast hurt animation ++ @Override ++ public void broadcastHurtAnimation(java.util.Collection players) { ++ //noinspection SuspiciousMethodCalls ++ Preconditions.checkArgument(!players.contains(this), "Cannot broadcast hurt animation to self without a yaw"); ++ for (final org.bukkit.entity.Player player : players) { ++ ((CraftPlayer) player).sendHurtAnimation(0, this); ++ } ++ } ++ // Paper end - broadcast hurt animation + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index ad740739437be632fc7fedec488a7d0c49534688..42d7660efe5baa6f796f2a7606686c765b6f2478 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1277,6 +1277,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + @Override + public void sendHurtAnimation(float yaw) { ++ // Paper start - Add target entity to sendHurtAnimation ++ this.sendHurtAnimation(yaw, this); ++ } ++ public void sendHurtAnimation(float yaw, org.bukkit.entity.Entity target) { ++ // Paper end - Add target entity to sendHurtAnimation + if (this.getHandle().connection == null) { + return; + } +@@ -1286,7 +1291,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + * This makes no sense. We'll add 90 to it so that 0 = front, clockwise from there. + */ + float actualYaw = yaw + 90; +- this.getHandle().connection.send(new ClientboundHurtAnimationPacket(this.getEntityId(), actualYaw)); ++ this.getHandle().connection.send(new ClientboundHurtAnimationPacket(target.getEntityId(), actualYaw)); // Paper - Add target entity to sendHurtAnimation + } + + @Override +@@ -3553,4 +3558,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer)this.getHandle()) + .moonrise$getViewDistanceHolder().setSendViewDistance(viewDistance); + } ++ ++ // Paper start - entity effect API ++ @Override ++ public void sendEntityEffect(final org.bukkit.EntityEffect effect, final org.bukkit.entity.Entity target) { ++ if (this.getHandle().connection == null || !effect.isApplicableTo(target)) { ++ return; ++ } ++ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData())); ++ } ++ // Paper end - entity effect API + } +diff --git a/src/test/java/org/bukkit/EntityEffectTest.java b/src/test/java/org/bukkit/EntityEffectTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..fdd3b443ae0ae16a09134d4a8b3e35905e287154 +--- /dev/null ++++ b/src/test/java/org/bukkit/EntityEffectTest.java +@@ -0,0 +1,80 @@ ++package org.bukkit; ++ ++import com.google.common.base.Joiner; ++import java.lang.reflect.Field; ++import java.lang.reflect.Modifier; ++import java.util.ArrayList; ++import java.util.HashMap; ++import java.util.HashSet; ++import java.util.List; ++import java.util.Map; ++import java.util.Set; ++import net.minecraft.world.entity.EntityEvent; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.fail; ++ ++public class EntityEffectTest { ++ ++ private static List collectNmsLevelEvents() throws ReflectiveOperationException { ++ final List events = new ArrayList<>(); ++ for (final Field field : EntityEvent.class.getFields()) { ++ if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.getType() == byte.class) { ++ events.add((byte) field.get(null)); ++ } ++ } ++ for (int i = 22; i <= 28; i++) { ++ events.remove(Byte.valueOf((byte) i)); // all have existing API (debug info and op level) ++ } ++ events.remove(Byte.valueOf(EntityEvent.STOP_ATTACKING)); // not used on client anywhere ++ events.remove(Byte.valueOf(EntityEvent.USE_ITEM_COMPLETE)); // not suitable for API (complete using item on Player) ++ events.remove(Byte.valueOf(EntityEvent.FISHING_ROD_REEL_IN)); // not suitable for API (fishing rod reel in on FishingHook) ++ events.add((byte) 0); // handled on Arrow (for some reason it's not in the EntityEvent nms file as a constant) ++ return events; ++ } ++ ++ private static boolean isNotDeprecated(EntityEffect effect) throws ReflectiveOperationException { ++ return !EntityEffect.class.getDeclaredField(effect.name()).isAnnotationPresent(Deprecated.class); ++ } ++ ++ @Test ++ public void checkAllApiExists() throws ReflectiveOperationException { ++ Map toId = new HashMap<>(); ++ for (final EntityEffect effect : EntityEffect.values()) { ++ if (isNotDeprecated(effect)) { ++ toId.put(effect.getData(), effect); ++ } ++ } ++ ++ final Set missingEvents = new HashSet<>(); ++ for (final Byte event : collectNmsLevelEvents()) { ++ if (toId.get(event) == null) { ++ missingEvents.add(event); ++ } ++ } ++ if (!missingEvents.isEmpty()) { ++ fail("Missing API EntityEffects:\n" + Joiner.on("\n").join(missingEvents)); ++ } ++ } ++ ++ @Test ++ public void checkNoExtraApi() throws ReflectiveOperationException { ++ Map toId = new HashMap<>(); ++ for (final EntityEffect effect : EntityEffect.values()) { ++ if (isNotDeprecated(effect)) { ++ toId.put(effect.getData(), effect); ++ } ++ } ++ ++ final List nmsEvents = collectNmsLevelEvents(); ++ final Set extraApiEffects = new HashSet<>(); ++ for (final Map.Entry entry : toId.entrySet()) { ++ if (!nmsEvents.contains(entry.getKey())) { ++ extraApiEffects.add(entry.getValue()); ++ } ++ } ++ if (!extraApiEffects.isEmpty()) { ++ fail("Extra API EntityEffects:\n" + Joiner.on("\n").join(extraApiEffects)); ++ } ++ } ++}