diff --git a/paper-api/src/main/java/io/papermc/paper/event/entity/EntityAttemptSpinAttackEvent.java b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityAttemptSpinAttackEvent.java new file mode 100644 index 000000000..927c320a0 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/event/entity/EntityAttemptSpinAttackEvent.java @@ -0,0 +1,70 @@ +package io.papermc.paper.event.entity; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.entity.EntityEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * Called when a {@link LivingEntity} attempts to perform an automatic spin attack + * against a target entity. + */ +@NullMarked +public class EntityAttemptSpinAttackEvent extends EntityEvent implements Cancellable { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final LivingEntity target; + + private boolean cancelled; + + @ApiStatus.Internal + public EntityAttemptSpinAttackEvent(final LivingEntity entity, final LivingEntity target) { + super(entity); + this.target = target; + } + + @Override + public LivingEntity getEntity() { + return (LivingEntity) this.entity; + } + + /** + * Returns the entity that is being attacked + * in the spin attack. + * + * @return the entity being attacked + */ + public LivingEntity getTarget() { + return this.target; + } + + /** + * {@inheritDoc} + *
+ * It should be noted that both the client and server independently check
+ * for a spin attack. Cancelling this on the server means the animation is not
+ * interrupted and no attack is performed, but the client will still collide
+ * and bounce away.
+ */
+ @Override
+ public void setCancelled(boolean cancelled) {
+ this.cancelled = cancelled;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/event/player/PlayerRiptideEvent.java b/paper-api/src/main/java/org/bukkit/event/player/PlayerRiptideEvent.java
index 97f8e1098..1a8d86643 100644
--- a/paper-api/src/main/java/org/bukkit/event/player/PlayerRiptideEvent.java
+++ b/paper-api/src/main/java/org/bukkit/event/player/PlayerRiptideEvent.java
@@ -1,6 +1,7 @@
package org.bukkit.event.player;
import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
@@ -14,13 +15,15 @@ import org.jetbrains.annotations.NotNull;
* N.B. the riptide action is currently performed client side, so manipulating
* the player in this event may have undesired effects.
*/
-public class PlayerRiptideEvent extends PlayerEvent {
+public class PlayerRiptideEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final ItemStack item;
private final Vector velocity;
+ private boolean cancelled;
+
@ApiStatus.Internal
public PlayerRiptideEvent(@NotNull final Player player, @NotNull final ItemStack item, @NotNull Vector velocity) {
super(player);
@@ -54,6 +57,16 @@ public class PlayerRiptideEvent extends PlayerEvent {
return this.velocity.clone();
}
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
@NotNull
@Override
public HandlerList getHandlers() {
diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch
index 1010a8143..eb69793c7 100644
--- a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch
+++ b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch
@@ -1540,6 +1540,40 @@
this.doPush(entity1);
}
}
+@@ -2954,16 +_,32 @@
+ protected void checkAutoSpinAttack(AABB boundingBoxBeforeSpin, AABB boundingBoxAfterSpin) {
+ AABB aabb = boundingBoxBeforeSpin.minmax(boundingBoxAfterSpin);
+ List