From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sat, 21 Jul 2018 03:11:03 -0500
Subject: [PATCH] PlayerLaunchProjectileEvent


diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
@@ -0,0 +0,0 @@ public abstract class Projectile extends Entity implements TraceableEntity {
     }
 
     public static <T extends Projectile> T spawnProjectileFromRotation(Projectile.ProjectileFactory<T> creator, ServerLevel world, ItemStack projectileStack, LivingEntity shooter, float roll, float power, float divergence) {
-        return Projectile.spawnProjectile(creator.create(world, shooter, projectileStack), world, projectileStack, (iprojectile) -> {
+    // Paper start - PlayerLaunchProjectileEvent
+        return spawnProjectileFromRotationDelayed(creator, world, projectileStack, shooter, roll, power, divergence).spawn();
+    }
+    public static <T extends Projectile> Delayed<T> spawnProjectileFromRotationDelayed(Projectile.ProjectileFactory<T> creator, ServerLevel world, ItemStack projectileStack, LivingEntity shooter, float roll, float power, float divergence) {
+        return Projectile.spawnProjectileDelayed(creator.create(world, shooter, projectileStack), world, projectileStack, (iprojectile) -> {
+    // Paper end - PlayerLaunchProjectileEvent
             iprojectile.shootFromRotation(shooter, shooter.getXRot(), shooter.getYRot(), roll, power, divergence);
         });
     }
diff --git a/src/main/java/net/minecraft/world/item/EggItem.java b/src/main/java/net/minecraft/world/item/EggItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/EggItem.java
+++ b/src/main/java/net/minecraft/world/item/EggItem.java
@@ -0,0 +0,0 @@ public class EggItem extends Item implements ProjectileItem {
         // world.playSound((EntityHuman) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEffects.EGG_THROW, SoundCategory.PLAYERS, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)); // CraftBukkit - moved down
         if (world instanceof ServerLevel worldserver) {
             // CraftBukkit start
-            if (Projectile.spawnProjectileFromRotation(ThrownEgg::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F).isRemoved()) {
+            // Paper start - PlayerLaunchProjectileEvent
+            final Projectile.Delayed<ThrownEgg> thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F);
+            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) thrownEgg.projectile().getBukkitEntity());
+            if (event.callEvent() && thrownEgg.attemptSpawn()) {
+                if (event.shouldConsume()) {
+                    itemstack.consume(1, user);
+                } else if (user instanceof net.minecraft.server.level.ServerPlayer) {
+                    ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
+                }
+
+                world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.EGG_THROW, SoundSource.PLAYERS, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
+                user.awardStat(Stats.ITEM_USED.get(this));
+            } else {
                 if (user instanceof net.minecraft.server.level.ServerPlayer) {
                     ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
                 }
@@ -0,0 +0,0 @@ public class EggItem extends Item implements ProjectileItem {
             // CraftBukkit end
         }
         world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.EGG_THROW, SoundSource.PLAYERS, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
-
-        user.awardStat(Stats.ITEM_USED.get(this));
-        itemstack.consume(1, user);
+        // Paper - PlayerLaunchProjectileEvent - moved up
         return InteractionResult.SUCCESS;
     }
 
diff --git a/src/main/java/net/minecraft/world/item/EnderpearlItem.java b/src/main/java/net/minecraft/world/item/EnderpearlItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/EnderpearlItem.java
+++ b/src/main/java/net/minecraft/world/item/EnderpearlItem.java
@@ -0,0 +0,0 @@ public class EnderpearlItem extends Item {
 
         if (world instanceof ServerLevel worldserver) {
             // CraftBukkit start
-            if (Projectile.spawnProjectileFromRotation(ThrownEnderpearl::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F).isRemoved()) {
+            // Paper start - PlayerLaunchProjectileEvent
+            final Projectile.Delayed<ThrownEnderpearl> thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F);
+            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) thrownEnderpearl.projectile().getBukkitEntity());
+            if (event.callEvent() && thrownEnderpearl.attemptSpawn()) {
+                if (event.shouldConsume()) {
+                    itemstack.consume(1, user);
+                } else if (user instanceof net.minecraft.server.level.ServerPlayer) {
+                    ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
+                }
+
+                world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENDER_PEARL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
+                user.awardStat(Stats.ITEM_USED.get(this));
+            } else {
+            // Paper end - PlayerLaunchProjectileEvent
                 if (user instanceof net.minecraft.server.level.ServerPlayer) {
                     ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
                 }
@@ -0,0 +0,0 @@ public class EnderpearlItem extends Item {
         world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENDER_PEARL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
         // CraftBukkit end
 
-        user.awardStat(Stats.ITEM_USED.get(this));
-        itemstack.consume(1, user);
+        // Paper - PlayerLaunchProjectileEvent - moved up
         return InteractionResult.SUCCESS;
     }
 }
diff --git a/src/main/java/net/minecraft/world/item/ExperienceBottleItem.java b/src/main/java/net/minecraft/world/item/ExperienceBottleItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/ExperienceBottleItem.java
+++ b/src/main/java/net/minecraft/world/item/ExperienceBottleItem.java
@@ -0,0 +0,0 @@ public class ExperienceBottleItem extends Item implements ProjectileItem {
     @Override
     public InteractionResult use(Level world, Player user, InteractionHand hand) {
         ItemStack itemStack = user.getItemInHand(hand);
-        world.playSound(
-            null,
-            user.getX(),
-            user.getY(),
-            user.getZ(),
-            SoundEvents.EXPERIENCE_BOTTLE_THROW,
-            SoundSource.NEUTRAL,
-            0.5F,
-            0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)
-        );
+        // Paper - PlayerLaunchProjectileEvent - moved down
         if (world instanceof ServerLevel serverLevel) {
-            Projectile.spawnProjectileFromRotation(ThrownExperienceBottle::new, serverLevel, itemStack, user, -20.0F, 0.7F, 1.0F);
+            // Paper start - PlayerLaunchProjectileEvent
+            final Projectile.Delayed<ThrownExperienceBottle> thrownExperienceBottle = Projectile.spawnProjectileFromRotationDelayed(ThrownExperienceBottle::new, serverLevel, itemStack, user, -20.0F, 0.7F, 1.0F);
+            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownExperienceBottle.projectile().getBukkitEntity());
+            if (event.callEvent() && thrownExperienceBottle.attemptSpawn()) {
+                if (event.shouldConsume()) {
+                    itemStack.consume(1, user);
+                } else if (user instanceof net.minecraft.server.level.ServerPlayer) {
+                    ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
+                }
+
+                world.playSound(
+                    null,
+                    user.getX(),
+                    user.getY(),
+                    user.getZ(),
+                    SoundEvents.EXPERIENCE_BOTTLE_THROW,
+                    SoundSource.NEUTRAL,
+                    0.5F,
+                    0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)
+                );
+            } else {
+                if (user instanceof net.minecraft.server.level.ServerPlayer) {
+                    ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
+                }
+                return InteractionResult.FAIL;
+            }
+            // Paper end - PlayerLaunchProjectileEvent
         }
 
-        user.awardStat(Stats.ITEM_USED.get(this));
-        itemStack.consume(1, user);
+        // Paper - PlayerLaunchProjectileEvent - moved up
         return InteractionResult.SUCCESS;
     }
 
diff --git a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
+++ b/src/main/java/net/minecraft/world/item/FireworkRocketItem.java
@@ -0,0 +0,0 @@ public class FireworkRocketItem extends Item implements ProjectileItem {
             ItemStack itemStack = context.getItemInHand();
             Vec3 vec3 = context.getClickLocation();
             Direction direction = context.getClickedFace();
-            Projectile.spawnProjectile(
+            final Projectile.Delayed<FireworkRocketEntity> fireworkRocketEntity = Projectile.spawnProjectileDelayed( // Paper - PlayerLaunchProjectileEvent
                 new FireworkRocketEntity(
                     level,
                     context.getPlayer(),
@@ -0,0 +0,0 @@ public class FireworkRocketItem extends Item implements ProjectileItem {
                 serverLevel,
                 itemStack, f -> f.spawningEntity = context.getPlayer() == null ? null : context.getPlayer().getUUID()  // Paper - firework api - assign spawning entity uuid
             );
-            itemStack.shrink(1);
+            // Paper start - PlayerLaunchProjectileEvent
+            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Firework) fireworkRocketEntity.projectile().getBukkitEntity());
+            if (!event.callEvent() || fireworkRocketEntity.attemptSpawn()) return InteractionResult.PASS;
+            if (event.shouldConsume() && !context.getPlayer().hasInfiniteMaterials()) itemStack.shrink(1);
+            else if (context.getPlayer() instanceof net.minecraft.server.level.ServerPlayer) ((net.minecraft.server.level.ServerPlayer) context.getPlayer()).getBukkitEntity().updateInventory();
+            // Paper end - PlayerLaunchProjectileEvent
         }
 
         return InteractionResult.SUCCESS;
diff --git a/src/main/java/net/minecraft/world/item/LingeringPotionItem.java b/src/main/java/net/minecraft/world/item/LingeringPotionItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/LingeringPotionItem.java
+++ b/src/main/java/net/minecraft/world/item/LingeringPotionItem.java
@@ -0,0 +0,0 @@ public class LingeringPotionItem extends ThrowablePotionItem {
 
     @Override
     public InteractionResult use(Level world, Player user, InteractionHand hand) {
+        // Paper start - PlayerLaunchProjectileEvent
+        final InteractionResult wrapper = super.use(world, user, hand);
+        if (wrapper instanceof InteractionResult.Fail) return wrapper;
+        // Paper end - PlayerLaunchProjectileEvent
         world.playSound(
             null,
             user.getX(),
@@ -0,0 +0,0 @@ public class LingeringPotionItem extends ThrowablePotionItem {
             0.5F,
             0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)
         );
-        return super.use(world, user, hand);
+        return wrapper; // Paper - PlayerLaunchProjectileEvent
     }
 }
diff --git a/src/main/java/net/minecraft/world/item/SnowballItem.java b/src/main/java/net/minecraft/world/item/SnowballItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/SnowballItem.java
+++ b/src/main/java/net/minecraft/world/item/SnowballItem.java
@@ -0,0 +0,0 @@ public class SnowballItem extends Item implements ProjectileItem {
         // CraftBukkit start - moved down
         // world.playSound((EntityHuman) null, entityhuman.getX(), entityhuman.getY(), entityhuman.getZ(), SoundEffects.SNOWBALL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
         if (world instanceof ServerLevel worldserver) {
-            if (Projectile.spawnProjectileFromRotation(Snowball::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F).isAlive()) {
-                itemstack.consume(1, user);
+            // Paper start - PlayerLaunchProjectileEvent
+            final Projectile.Delayed<Snowball> snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, worldserver, itemstack, user, 0.0F, 1.5F, 1.0F);
+            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) snowball.projectile().getBukkitEntity());
+            if (event.callEvent() && snowball.attemptSpawn()) {
+                user.awardStat(Stats.ITEM_USED.get(this));
+                if (event.shouldConsume()) {
+                    itemstack.consume(1, user);
+                } else if (user instanceof net.minecraft.server.level.ServerPlayer) {
+                    ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
+                }
+            // Paper end - PlayerLaunchProjectileEvent
 
                 world.playSound((Player) null, user.getX(), user.getY(), user.getZ(), SoundEvents.SNOWBALL_THROW, SoundSource.NEUTRAL, 0.5F, 0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F));
-            } else if (user instanceof net.minecraft.server.level.ServerPlayer) {
+            } else { if (user instanceof net.minecraft.server.level.ServerPlayer) { // Paper - PlayerLaunchProjectileEvent - return fail
                 ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
-            }
+            } return InteractionResult.FAIL; } // Paper - PlayerLaunchProjectileEvent - return fail
             // CraftBukkit end
         }
 
-        user.awardStat(Stats.ITEM_USED.get(this));
+        // Paper - PlayerLaunchProjectileEvent - moved up
         // itemstack.consume(1, entityhuman); // CraftBukkit - moved up
         return InteractionResult.SUCCESS;
     }
diff --git a/src/main/java/net/minecraft/world/item/SplashPotionItem.java b/src/main/java/net/minecraft/world/item/SplashPotionItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/SplashPotionItem.java
+++ b/src/main/java/net/minecraft/world/item/SplashPotionItem.java
@@ -0,0 +0,0 @@ public class SplashPotionItem extends ThrowablePotionItem {
 
     @Override
     public InteractionResult use(Level world, Player user, InteractionHand hand) {
+        // Paper start - PlayerLaunchProjectileEvent
+        final InteractionResult wrapper = super.use(world, user, hand);
+        if (wrapper instanceof InteractionResult.Fail) return wrapper;
+        // Paper end - PlayerLaunchProjectileEvent
         world.playSound(
             null,
             user.getX(),
@@ -0,0 +0,0 @@ public class SplashPotionItem extends ThrowablePotionItem {
             0.5F,
             0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)
         );
-        return super.use(world, user, hand);
+        return wrapper; // Paper - PlayerLaunchProjectileEvent
     }
 }
diff --git a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
+++ b/src/main/java/net/minecraft/world/item/ThrowablePotionItem.java
@@ -0,0 +0,0 @@ public class ThrowablePotionItem extends PotionItem implements ProjectileItem {
     public InteractionResult use(Level world, Player user, InteractionHand hand) {
         ItemStack itemStack = user.getItemInHand(hand);
         if (world instanceof ServerLevel serverLevel) {
-            Projectile.spawnProjectileFromRotation(ThrownPotion::new, serverLevel, itemStack, user, -20.0F, 0.5F, 1.0F);
+            // Paper start - PlayerLaunchProjectileEvent
+            final Projectile.Delayed<ThrownPotion> thrownPotion = Projectile.spawnProjectileFromRotationDelayed(ThrownPotion::new, serverLevel, itemStack, user, -20.0F, 0.5F, 1.0F);
+            // Paper start - PlayerLaunchProjectileEvent
+            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownPotion.projectile().getBukkitEntity());
+            if (event.callEvent() && thrownPotion.attemptSpawn()) {
+                if (event.shouldConsume()) {
+                    itemStack.consume(1, user);
+                } else if (user instanceof net.minecraft.server.level.ServerPlayer) {
+                    ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
+                }
+
+                user.awardStat(Stats.ITEM_USED.get(this));
+            } else {
+                if (user instanceof net.minecraft.server.level.ServerPlayer) {
+                    ((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity().updateInventory();
+                }
+                return InteractionResult.FAIL;
+            }
+            // Paper end - PlayerLaunchProjectileEvent
         }
 
-        user.awardStat(Stats.ITEM_USED.get(this));
-        itemStack.consume(1, user);
+        // Paper - PlayerLaunchProjectileEvent - move up
         return InteractionResult.SUCCESS;
     }
 
diff --git a/src/main/java/net/minecraft/world/item/TridentItem.java b/src/main/java/net/minecraft/world/item/TridentItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/TridentItem.java
+++ b/src/main/java/net/minecraft/world/item/TridentItem.java
@@ -0,0 +0,0 @@ public class TridentItem extends Item implements ProjectileItem {
 
                         // itemstack.hurtWithoutBreaking(1, entityhuman); // CraftBukkit - moved down
                         if (f == 0.0F) {
-                            ThrownTrident entitythrowntrident = (ThrownTrident) Projectile.spawnProjectileFromRotation(ThrownTrident::new, worldserver, stack, entityhuman, 0.0F, 2.5F, 1.0F);
-                            // CraftBukkit start
-                            if (entitythrowntrident.isRemoved()) {
+                            // Paper start - PlayerLaunchProjectileEvent
+                            Projectile.Delayed<ThrownTrident> tridentDelayed = Projectile.spawnProjectileFromRotationDelayed(ThrownTrident::new, worldserver, stack, entityhuman, 0.0F, 2.5F, 1.0F);
+                            // Paper start - PlayerLaunchProjectileEvent
+                            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) tridentDelayed.projectile().getBukkitEntity());
+                            if (!event.callEvent() || !tridentDelayed.attemptSpawn()) {
+                                // CraftBukkit start
+                            // Paper end - PlayerLaunchProjectileEvent
                                 if (entityhuman instanceof net.minecraft.server.level.ServerPlayer) {
                                     ((net.minecraft.server.level.ServerPlayer) entityhuman).getBukkitEntity().updateInventory();
                                 }
                                 return false;
                             }
-                            stack.hurtWithoutBreaking(1, entityhuman);
+                            ThrownTrident entitythrowntrident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent
+                            if (event.shouldConsume()) stack.hurtWithoutBreaking(1, entityhuman); // Paper - PlayerLaunchProjectileEvent
                             entitythrowntrident.pickupItemStack = stack.copy(); // SPIGOT-4511 update since damage call moved
                             // CraftBukkit end
 
                             if (entityhuman.hasInfiniteMaterials()) {
                                 entitythrowntrident.pickup = AbstractArrow.Pickup.CREATIVE_ONLY;
-                            } else {
+                            } else if (event.shouldConsume()) { // Paper - PlayerLaunchProjectileEvent
                                 entityhuman.getInventory().removeItem(stack);
                             }
 
diff --git a/src/main/java/net/minecraft/world/item/WindChargeItem.java b/src/main/java/net/minecraft/world/item/WindChargeItem.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/WindChargeItem.java
+++ b/src/main/java/net/minecraft/world/item/WindChargeItem.java
@@ -0,0 +0,0 @@ public class WindChargeItem extends Item implements ProjectileItem {
     public InteractionResult use(Level world, Player user, InteractionHand hand) {
         ItemStack itemStack = user.getItemInHand(hand);
         if (world instanceof ServerLevel serverLevel) {
-            Projectile.spawnProjectileFromRotation(
+            final Projectile.Delayed<WindCharge> windCharge = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent
                 (world2, shooter, stack) -> new WindCharge(user, world, user.position().x(), user.getEyePosition().y(), user.position().z()),
                 serverLevel,
                 itemStack,
@@ -0,0 +0,0 @@ public class WindChargeItem extends Item implements ProjectileItem {
                 1.5F,
                 1.0F
             );
+            com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) user.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) windCharge.projectile().getBukkitEntity());
+            if (!event.callEvent() || !windCharge.attemptSpawn()) {
+                user.containerMenu.sendAllDataToRemote();
+                if (user instanceof net.minecraft.server.level.ServerPlayer player) {
+                    player.connection.send(new net.minecraft.network.protocol.game.ClientboundCooldownPacket(user.getCooldowns().getCooldownGroup(itemStack), 0)); // prevent visual desync of cooldown on the slot
+                }
+                return InteractionResult.FAIL;
+            }
+
+            user.awardStat(Stats.ITEM_USED.get(this));
+            if (event.shouldConsume()) itemStack.consume(1, user);
+            else if (!user.hasInfiniteMaterials()) {
+                user.containerMenu.sendAllDataToRemote();
+            }
+            // Paper end - PlayerLaunchProjectileEvent
         }
 
         world.playSound(
@@ -0,0 +0,0 @@ public class WindChargeItem extends Item implements ProjectileItem {
             0.5F,
             0.4F / (world.getRandom().nextFloat() * 0.4F + 0.8F)
         );
-        user.awardStat(Stats.ITEM_USED.get(this));
-        itemStack.consume(1, user);
         return InteractionResult.SUCCESS;
     }
 
