aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/unapplied/server/1025-Improve-entity-effect-API.patch
blob: 8214467d023d8fb44732a615cd9d644d8e689a8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
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 d1d9916db7bd4886d30355e6a0ecb69cd21c2364..ddabaed899c755925ad8618b78c33dacaf2126ac 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -1295,4 +1295,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<Player> 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 f694e79c21a8f84e48c8f46de8aebb0889b7c9f8..7e3552390c7dd11a79fd95d3543707cc5d652c66 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1284,6 +1284,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;
         }
@@ -1293,7 +1298,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
@@ -3530,4 +3535,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<Byte> collectNmsLevelEvents() throws ReflectiveOperationException {
+        final List<Byte> 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<Byte, EntityEffect> toId = new HashMap<>();
+        for (final EntityEffect effect : EntityEffect.values()) {
+            if (isNotDeprecated(effect)) {
+                toId.put(effect.getData(), effect);
+            }
+        }
+
+        final Set<Byte> 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<Byte, EntityEffect> toId = new HashMap<>();
+        for (final EntityEffect effect : EntityEffect.values()) {
+            if (isNotDeprecated(effect)) {
+                toId.put(effect.getData(), effect);
+            }
+        }
+
+        final List<Byte> nmsEvents = collectNmsLevelEvents();
+        final Set<EntityEffect> extraApiEffects = new HashSet<>();
+        for (final Map.Entry<Byte, EntityEffect> 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));
+        }
+    }
+}