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 b0e49ad831f1ebc6b126bf82c5fddaebffb91312..179886dcbda29c5cdb7dbd43e44951ae38d9df96 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -1300,4 +1300,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 e1e73e877dfef5bcf30a99b275ececbf42f59c95..4eb2cd3f75e8e727199193dc8b1c01d4c855223c 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1294,6 +1294,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; } @@ -1303,7 +1308,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 @@ -3542,4 +3547,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player { public void setSendViewDistance(final int viewDistance) { throw new UnsupportedOperationException("Not implemented yet"); } + + // 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..5e64dfaeba167374dc45c5becfb2e7114657dff6 --- /dev/null +++ b/src/test/java/org/bukkit/EntityEffectTest.java @@ -0,0 +1,82 @@ +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.bukkit.support.environment.Normal; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.fail; + +@Normal +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)); + } + } +}