From b54562400c4e0f8a413715349e01b5cb26358748 Mon Sep 17 00:00:00 2001 From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:19:02 +0200 Subject: [PATCH] Update launchProjectile API (#11300) --- ...arameter-to-ProjectileSource-launchP.patch | 34 --- patches/api/Fix-upstream-javadocs.patch | 26 +++ patches/api/More-Projectile-API.patch | 32 +++ ...arameter-to-ProjectileSource-launchP.patch | 69 ------- ...etty-printing-for-advancement-saving.patch | 2 +- patches/server/More-Projectile-API.patch | 193 +++++++++++++++++- 6 files changed, 250 insertions(+), 106 deletions(-) delete mode 100644 patches/api/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch delete mode 100644 patches/server/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch diff --git a/patches/api/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch b/patches/api/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch deleted file mode 100644 index c4ba37c2a8..0000000000 --- a/patches/api/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Mon, 19 Sep 2022 14:04:13 -0700 -Subject: [PATCH] Add a consumer parameter to ProjectileSource#launchProjectile - - -diff --git a/src/main/java/org/bukkit/projectiles/ProjectileSource.java b/src/main/java/org/bukkit/projectiles/ProjectileSource.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/projectiles/ProjectileSource.java -+++ b/src/main/java/org/bukkit/projectiles/ProjectileSource.java -@@ -0,0 +0,0 @@ public interface ProjectileSource { - */ - @NotNull - public T launchProjectile(@NotNull Class projectile, @Nullable Vector velocity); -+ -+ // Paper start - add consumer to launchProjectile -+ /** -+ * Launches a {@link Projectile} from the ProjectileSource with an -+ * initial velocity, with the supplied function run before the -+ * entity is added to the world. -+ *
-+ * Note that when the function is run, the entity will not be actually in -+ * the world. Any operation involving such as teleporting the entity is undefined -+ * until after this function returns. -+ * -+ * @param a projectile subclass -+ * @param projectile class of the projectile to launch -+ * @param velocity the velocity with which to launch -+ * @param function the function to be run before the entity is spawned -+ * @return the launched projectile -+ */ -+ @NotNull T launchProjectile(@NotNull Class projectile, @Nullable Vector velocity, java.util.function.@Nullable Consumer function); -+ // Paper end - add consumer to launchProjectile - } diff --git a/patches/api/Fix-upstream-javadocs.patch b/patches/api/Fix-upstream-javadocs.patch index 63f4bbc3c0..81acc243f9 100644 --- a/patches/api/Fix-upstream-javadocs.patch +++ b/patches/api/Fix-upstream-javadocs.patch @@ -1719,6 +1719,32 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 * * @return Set containing all the channels that this client may accept. */ +diff --git a/src/main/java/org/bukkit/projectiles/ProjectileSource.java b/src/main/java/org/bukkit/projectiles/ProjectileSource.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/projectiles/ProjectileSource.java ++++ b/src/main/java/org/bukkit/projectiles/ProjectileSource.java +@@ -0,0 +0,0 @@ public interface ProjectileSource { + + /** + * Launches a {@link Projectile} from the ProjectileSource. ++ *

++ * The family of launchProjectile methods only promise the ability to launch projectile types ++ * that the {@link ProjectileSource} is capable of firing in vanilla. ++ * Any other types of projectiles *may* be implemented but are not part of the method contract. + * + * @param a projectile subclass + * @param projectile class of the projectile to launch +@@ -0,0 +0,0 @@ public interface ProjectileSource { + /** + * Launches a {@link Projectile} from the ProjectileSource with an + * initial velocity. ++ *

++ * The family of launchProjectile methods only promise the ability to launch projectile types ++ * that the {@link ProjectileSource} is capable of firing in vanilla. ++ * Any other types of projectiles *may* be implemented but are not part of the method contract. + * + * @param a projectile subclass + * @param projectile class of the projectile to launch diff --git a/src/main/java/org/bukkit/scoreboard/Objective.java b/src/main/java/org/bukkit/scoreboard/Objective.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/scoreboard/Objective.java diff --git a/patches/api/More-Projectile-API.patch b/patches/api/More-Projectile-API.patch index b46ed7ac11..0b1de7e3e4 100644 --- a/patches/api/More-Projectile-API.patch +++ b/patches/api/More-Projectile-API.patch @@ -5,6 +5,7 @@ Subject: [PATCH] More Projectile API Co-authored-by: Nassim Jahnke Co-authored-by: SoSeDiK +Co-authored-by: MelnCat diff --git a/src/main/java/org/bukkit/entity/AbstractArrow.java b/src/main/java/org/bukkit/entity/AbstractArrow.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 @@ -486,3 +487,34 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + void setHasDealtDamage(boolean hasDealtDamage); } // Paper end +diff --git a/src/main/java/org/bukkit/projectiles/ProjectileSource.java b/src/main/java/org/bukkit/projectiles/ProjectileSource.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/projectiles/ProjectileSource.java ++++ b/src/main/java/org/bukkit/projectiles/ProjectileSource.java +@@ -0,0 +0,0 @@ public interface ProjectileSource { + */ + @NotNull + public T launchProjectile(@NotNull Class projectile, @Nullable Vector velocity); ++ ++ // Paper start - add consumer to launchProjectile ++ /** ++ * Launches a {@link Projectile} from the ProjectileSource with an ++ * initial velocity, with the supplied function run before the ++ * entity is added to the world. ++ *
++ * Note that when the function is run, the entity will not be actually in ++ * the world. Any operation involving such as teleporting the entity is undefined ++ * until after this function returns. ++ *

++ * The family of launchProjectile methods only promise the ability to launch projectile types ++ * that the {@link ProjectileSource} is capable of firing in vanilla. ++ * Any other types of projectiles *may* be implemented but are not part of the method contract. ++ * @param a projectile subclass ++ * @param projectile class of the projectile to launch ++ * @param velocity the velocity with which to launch ++ * @param function the function to be run before the entity is spawned ++ * @return the launched projectile ++ */ ++ @NotNull T launchProjectile(@NotNull Class projectile, @Nullable Vector velocity, java.util.function.@Nullable Consumer function); ++ // Paper end - add consumer to launchProjectile + } diff --git a/patches/server/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch b/patches/server/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch deleted file mode 100644 index fb64a29595..0000000000 --- a/patches/server/Add-a-consumer-parameter-to-ProjectileSource-launchP.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MelnCat -Date: Mon, 19 Sep 2022 14:16:10 -0700 -Subject: [PATCH] Add a consumer parameter to ProjectileSource#launchProjectile - - -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - } - - @Override -- @SuppressWarnings("unchecked") - public T launchProjectile(Class projectile, Vector velocity) { -+ // Paper start - launchProjectile consumer -+ return this.launchProjectile(projectile, velocity, null); -+ } -+ -+ @Override -+ @SuppressWarnings("unchecked") -+ public T launchProjectile(Class projectile, Vector velocity, java.util.function.Consumer function) { -+ // Paper end - launchProjectile consumer - Preconditions.checkState(!this.getHandle().generation, "Cannot launch projectile during world generation"); - - net.minecraft.world.level.Level world = ((CraftWorld) this.getWorld()).getHandle(); -@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - if (velocity != null) { - ((T) launch.getBukkitEntity()).setVelocity(velocity); - } -+ // Paper start - launchProjectile consumer -+ if (function != null) { -+ function.accept((T) launch.getBukkitEntity()); -+ } -+ // Paper end - launchProjectile consumer - - world.addFreshEntity(launch); - return (T) launch.getBukkitEntity(); -diff --git a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java -+++ b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java -@@ -0,0 +0,0 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { - - @Override - public T launchProjectile(Class projectile, Vector velocity) { -+ // Paper start - launchProjectile consumer -+ return this.launchProjectile(projectile, velocity, null); -+ } -+ -+ @Override -+ public T launchProjectile(Class projectile, Vector velocity, java.util.function.Consumer function) { -+ // Paper end - launchProjectile consumer - Preconditions.checkArgument(this.getBlock().getType() == Material.DISPENSER, "Block is no longer dispenser"); - // Copied from BlockDispenser.dispense() - BlockSource sourceblock = new BlockSource((ServerLevel) this.dispenserBlock.getLevel(), this.dispenserBlock.getBlockPos(), this.dispenserBlock.getBlockState(), this.dispenserBlock); -@@ -0,0 +0,0 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { - if (velocity != null) { - ((T) launch.getBukkitEntity()).setVelocity(velocity); - } -+ // Paper start -+ if (function != null) { -+ function.accept((T) launch.getBukkitEntity()); -+ } -+ // Paper end - - world.addFreshEntity(launch); - return (T) launch.getBukkitEntity(); diff --git a/patches/server/Disable-pretty-printing-for-advancement-saving.patch b/patches/server/Disable-pretty-printing-for-advancement-saving.patch index 8efcf4a516..64b2f601f0 100644 --- a/patches/server/Disable-pretty-printing-for-advancement-saving.patch +++ b/patches/server/Disable-pretty-printing-for-advancement-saving.patch @@ -8,7 +8,7 @@ Not sure why advancements even had pretty printing enabled. My best guess was by accident on mojang's part, especially since stats json files don't have pretty printing. diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java -index 9fabf9322..862a4bf00 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/PlayerAdvancements.java +++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java @@ -0,0 +0,0 @@ import org.slf4j.Logger; diff --git a/patches/server/More-Projectile-API.patch b/patches/server/More-Projectile-API.patch index 34eee0a11b..bbe97a147d 100644 --- a/patches/server/More-Projectile-API.patch +++ b/patches/server/More-Projectile-API.patch @@ -20,9 +20,12 @@ public net.minecraft.world.entity.projectile.Projectile leftOwner public net.minecraft.world.entity.projectile.Projectile preOnHit(Lnet/minecraft/world/phys/HitResult;)V public net.minecraft.world.entity.projectile.Projectile canHitEntity(Lnet/minecraft/world/entity/Entity;)Z public net.minecraft.world.entity.projectile.FireworkRocketEntity getDefaultItem()Lnet/minecraft/world/item/ItemStack; +public net.minecraft.world.item.CrossbowItem FIREWORK_POWER Co-authored-by: Nassim Jahnke Co-authored-by: SoSeDiK +Co-authored-by: MelnCat +Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 @@ -518,6 +521,23 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + } + + @Override +- @SuppressWarnings("unchecked") + public T launchProjectile(Class projectile, Vector velocity) { ++ // Paper start - launchProjectile consumer ++ return this.launchProjectile(projectile, velocity, null); ++ } ++ ++ @Override ++ @SuppressWarnings("unchecked") ++ public T launchProjectile(Class projectile, Vector velocity, java.util.function.Consumer function) { ++ // Paper end - launchProjectile consumer + Preconditions.checkState(!this.getHandle().generation, "Cannot launch projectile during world generation"); + + net.minecraft.world.level.Level world = ((CraftWorld) this.getWorld()).getHandle(); @@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { } else { launch = new net.minecraft.world.entity.projectile.Arrow(world, this.getHandle(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.ARROW), null); @@ -553,10 +573,42 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 Location location = this.getEyeLocation(); - launch = new FireworkRocketEntity(world, net.minecraft.world.item.ItemStack.EMPTY, this.getHandle()); -+ launch = new FireworkRocketEntity(world, FireworkRocketEntity.getDefaultItem(), this.getHandle()); // Paper - pass correct default to rocket for data storage - launch.moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); +- launch.moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); ++ // Paper start - see CrossbowItem ++ launch = new FireworkRocketEntity(world, FireworkRocketEntity.getDefaultItem(), this.getHandle(), location.getX(), location.getY() - 0.15F, location.getZ(), true); // Paper - pass correct default to rocket for data storage & see CrossbowItem for regular launch without elytra boost ++ ++ // Lifted from net.minecraft.world.item.ProjectileWeaponItem.shoot ++ float f2 = /* net.minecraft.world.item.enchantment.EnchantmentHelper.processProjectileSpread((ServerLevel) world, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.CROSSBOW), this.getHandle(), 0.0F); */ 0; // Just shortcut this to 0, no need to do any calculations on a non existing stack ++ int projectileSize = 1; ++ int i = 0; ++ ++ float f3 = projectileSize == 1 ? 0.0F : 2.0F * f2 / (float) (projectileSize - 1); ++ float f4 = (float) ((projectileSize - 1) % 2) * f3 / 2.0F; ++ float f5 = 1.0F; ++ float yaw = f4 + f5 * (float) ((i + 1) / 2) * f3; ++ ++ // Lifted from net.minecraft.world.item.CrossbowItem.shootProjectile ++ Vec3 vec3 = this.getHandle().getUpVector(1.0F); ++ org.joml.Quaternionf quaternionf = new org.joml.Quaternionf().setAngleAxis((double)(yaw * (float) (Math.PI / 180.0)), vec3.x, vec3.y, vec3.z); ++ Vec3 vec32 = this.getHandle().getViewVector(1.0F); ++ org.joml.Vector3f vector3f = vec32.toVector3f().rotate(quaternionf); ++ ((FireworkRocketEntity) launch).shoot((double)vector3f.x(), (double)vector3f.y(), (double)vector3f.z(), net.minecraft.world.item.CrossbowItem.FIREWORK_POWER, 1.0F); ++ // Paper end } + Preconditions.checkArgument(launch != null, "Projectile (%s) not supported", projectile.getName()); +@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } ++ // Paper start - launchProjectile consumer ++ if (function != null) { ++ function.accept((T) launch.getBukkitEntity()); ++ } ++ // Paper end - launchProjectile consumer + + world.addFreshEntity(launch); + return (T) launch.getBukkitEntity(); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java @@ -812,3 +864,140 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 return ((CraftItemType) CraftItemType.minecraftToBukkitNew(item.getItem())).getItemMeta(item); } +diff --git a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java ++++ b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java +@@ -0,0 +0,0 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { + + @Override + public T launchProjectile(Class projectile, Vector velocity) { ++ // Paper start - launchProjectile consumer ++ return this.launchProjectile(projectile, velocity, null); ++ } ++ ++ @Override ++ public T launchProjectile(Class projectile, Vector velocity, java.util.function.Consumer function) { ++ // Paper end - launchProjectile consumer + Preconditions.checkArgument(this.getBlock().getType() == Material.DISPENSER, "Block is no longer dispenser"); +- // Copied from BlockDispenser.dispense() +- BlockSource sourceblock = new BlockSource((ServerLevel) this.dispenserBlock.getLevel(), this.dispenserBlock.getBlockPos(), this.dispenserBlock.getBlockState(), this.dispenserBlock); +- // Copied from DispenseBehaviorProjectile +- Position iposition = DispenserBlock.getDispensePosition(sourceblock); +- Direction enumdirection = (Direction) sourceblock.state().getValue(DispenserBlock.FACING); +- net.minecraft.world.level.Level world = this.dispenserBlock.getLevel(); +- net.minecraft.world.entity.Entity launch = null; + ++ // Paper start - rewrite whole method to match ProjectileDispenseBehavior ++ net.minecraft.world.item.Item item = null; + if (Snowball.class.isAssignableFrom(projectile)) { +- launch = new net.minecraft.world.entity.projectile.Snowball(world, iposition.x(), iposition.y(), iposition.z()); ++ item = net.minecraft.world.item.Items.SNOWBALL; + } else if (Egg.class.isAssignableFrom(projectile)) { +- launch = new ThrownEgg(world, iposition.x(), iposition.y(), iposition.z()); +- } else if (EnderPearl.class.isAssignableFrom(projectile)) { +- launch = new ThrownEnderpearl(world, null); +- launch.setPos(iposition.x(), iposition.y(), iposition.z()); ++ item = net.minecraft.world.item.Items.EGG; + } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { +- launch = new ThrownExperienceBottle(world, iposition.x(), iposition.y(), iposition.z()); ++ item = net.minecraft.world.item.Items.EXPERIENCE_BOTTLE; + } else if (ThrownPotion.class.isAssignableFrom(projectile)) { + if (LingeringPotion.class.isAssignableFrom(projectile)) { +- launch = new net.minecraft.world.entity.projectile.ThrownPotion(world, iposition.x(), iposition.y(), iposition.z()); +- ((net.minecraft.world.entity.projectile.ThrownPotion) launch).setItem(CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.LINGERING_POTION, 1))); ++ item = net.minecraft.world.item.Items.LINGERING_POTION; + } else { +- launch = new net.minecraft.world.entity.projectile.ThrownPotion(world, iposition.x(), iposition.y(), iposition.z()); +- ((net.minecraft.world.entity.projectile.ThrownPotion) launch).setItem(CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.SPLASH_POTION, 1))); ++ item = net.minecraft.world.item.Items.SPLASH_POTION; + } + } else if (AbstractArrow.class.isAssignableFrom(projectile)) { +- if (TippedArrow.class.isAssignableFrom(projectile)) { +- launch = new net.minecraft.world.entity.projectile.Arrow(world, iposition.x(), iposition.y(), iposition.z(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.ARROW), null); +- ((Arrow) launch.getBukkitEntity()).setBasePotionType(PotionType.WATER); +- } else if (SpectralArrow.class.isAssignableFrom(projectile)) { +- launch = new net.minecraft.world.entity.projectile.SpectralArrow(world, iposition.x(), iposition.y(), iposition.z(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.SPECTRAL_ARROW), null); ++ if (SpectralArrow.class.isAssignableFrom(projectile)) { ++ item = net.minecraft.world.item.Items.SPECTRAL_ARROW; + } else { +- launch = new net.minecraft.world.entity.projectile.Arrow(world, iposition.x(), iposition.y(), iposition.z(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.ARROW), null); +- } +- ((net.minecraft.world.entity.projectile.AbstractArrow) launch).pickup = net.minecraft.world.entity.projectile.AbstractArrow.Pickup.ALLOWED; +- ((net.minecraft.world.entity.projectile.AbstractArrow) launch).projectileSource = this; +- } else if (Fireball.class.isAssignableFrom(projectile)) { +- double d0 = iposition.x() + (double) ((float) enumdirection.getStepX() * 0.3F); +- double d1 = iposition.y() + (double) ((float) enumdirection.getStepY() * 0.3F); +- double d2 = iposition.z() + (double) ((float) enumdirection.getStepZ() * 0.3F); +- RandomSource random = world.random; +- double d3 = random.nextGaussian() * 0.05D + (double) enumdirection.getStepX(); +- double d4 = random.nextGaussian() * 0.05D + (double) enumdirection.getStepY(); +- double d5 = random.nextGaussian() * 0.05D + (double) enumdirection.getStepZ(); +- +- if (SmallFireball.class.isAssignableFrom(projectile)) { +- launch = new net.minecraft.world.entity.projectile.SmallFireball(world, null, new Vec3(d0, d1, d2)); +- } else if (WitherSkull.class.isAssignableFrom(projectile)) { +- launch = EntityType.WITHER_SKULL.create(world); +- launch.setPos(d0, d1, d2); +- +- ((AbstractHurtingProjectile) launch).assignDirectionalMovement(new Vec3(d3, d4, d5), 0.1D); +- } else { +- launch = EntityType.FIREBALL.create(world); +- launch.setPos(d0, d1, d2); +- +- ((AbstractHurtingProjectile) launch).assignDirectionalMovement(new Vec3(d3, d4, d5), 0.1D); ++ item = net.minecraft.world.item.Items.ARROW; + } ++ } else if (org.bukkit.entity.WindCharge.class.isAssignableFrom(projectile)) { ++ item = net.minecraft.world.item.Items.WIND_CHARGE; ++ } else if (org.bukkit.entity.Firework.class.isAssignableFrom(projectile)) { ++ item = net.minecraft.world.item.Items.FIREWORK_ROCKET; ++ } else if (SmallFireball.class.isAssignableFrom(projectile)) { ++ item = net.minecraft.world.item.Items.FIRE_CHARGE; ++ } + +- ((AbstractHurtingProjectile) launch).projectileSource = this; ++ if (!(item instanceof net.minecraft.world.item.ProjectileItem projectileItem)) { ++ throw new IllegalArgumentException("Projectile '%s' is not supported".formatted(projectile.getSimpleName())); + } + +- Preconditions.checkArgument(launch != null, "Projectile not supported"); ++ net.minecraft.world.item.ProjectileItem.DispenseConfig config = projectileItem.createDispenseConfig(); ++ net.minecraft.world.level.block.state.BlockState state = this.dispenserBlock.getBlockState(); ++ net.minecraft.world.level.Level world = this.dispenserBlock.getLevel(); ++ BlockSource pointer = new BlockSource((ServerLevel) world, this.dispenserBlock.getBlockPos(), state, this.dispenserBlock); // copied from DispenseBlock#dispenseFrom ++ Direction facing = state.getValue(DispenserBlock.FACING); ++ Position pos = config.positionFunction().getDispensePosition(pointer, facing); + +- if (launch instanceof net.minecraft.world.entity.projectile.Projectile) { +- if (launch instanceof ThrowableProjectile) { +- ((ThrowableProjectile) launch).projectileSource = this; +- } +- // Values from DispenseBehaviorProjectile +- float a = 6.0F; +- float b = 1.1F; +- if (launch instanceof net.minecraft.world.entity.projectile.ThrownPotion || launch instanceof ThrownExpBottle) { +- // Values from respective DispenseBehavior classes +- a *= 0.5F; +- b *= 1.25F; +- } +- // Copied from DispenseBehaviorProjectile +- ((net.minecraft.world.entity.projectile.Projectile) launch).shoot((double) enumdirection.getStepX(), (double) ((float) enumdirection.getStepY() + 0.1F), (double) enumdirection.getStepZ(), b, a); +- } ++ net.minecraft.world.entity.projectile.Projectile launch = projectileItem.asProjectile(world, pos, new net.minecraft.world.item.ItemStack(item), facing); ++ // some projectile are not shoot and doesn't rely on the config for power/uncertainty ++ projectileItem.shoot(launch, facing.getStepX(), facing.getStepY(), facing.getStepZ(), config.power(), config.uncertainty()); ++ launch.projectileSource = this; ++ // Paper end + + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } ++ // Paper start ++ if (function != null) { ++ function.accept((T) launch.getBukkitEntity()); ++ } ++ // Paper end + + world.addFreshEntity(launch); + return (T) launch.getBukkitEntity();