diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch index 772e836788a4..1a3af0c6e700 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/ProjectileDispenseBehavior.java.patch @@ -40,7 +40,8 @@ + + // SPIGOT-7923: Avoid create projectiles with empty item + if (!singleItemStack.isEmpty()) { -+ Projectile projectile = Projectile.spawnProjectileUsingShoot(this.projectileItem.asProjectile(level, position, org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()), direction), level, singleItemStack, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), this.dispenseConfig.uncertainty()); // Paper - track changed items in the dispense event; unwrap is safe here because all uses of the stack make their own copies ++ Projectile projectileEntity = this.projectileItem.asProjectile(level, position, org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()), direction); ++ Projectile projectile = Projectile.spawnProjectileUsingShoot(projectileEntity, level, singleItemStack, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.dispenseConfig.power(), io.papermc.paper.configuration.ProjectileUncertainty.resolve(projectileEntity.getType(), this.dispenseConfig.uncertainty())); // Paper - track changed items in the dispense event; configurable projectile uncertainty + projectile.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource(source.blockEntity()); + } + if (shrink) dispensed.shrink(1); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch index 5fc22c0ceaf4..0661a19d5f5d 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/OminousItemSpawner.java.patch @@ -9,20 +9,24 @@ } level.levelEvent(LevelEvent.PARTICLES_TRIAL_SPAWNER_SPAWN_ITEM, this.blockPosition(), 1); -@@ -92,7 +_,7 @@ +@@ -92,16 +_,17 @@ ProjectileItem.DispenseConfig dispenseConfig = projectileItem.createDispenseConfig(); dispenseConfig.overrideDispenseEvent().ifPresent(event -> level.levelEvent(event, this.blockPosition(), 0)); Direction direction = Direction.DOWN; - Projectile projectile = Projectile.spawnProjectileUsingShoot( +- projectileItem.asProjectile(level, this.position(), item, direction), ++ Projectile spawnedProjectile = projectileItem.asProjectile(level, this.position(), item, direction); + Projectile projectile = Projectile.spawnProjectileUsingShootDelayed( // Paper - fixes and addition to spawn reason API - projectileItem.asProjectile(level, this.position(), item, direction), ++ spawnedProjectile, level, item, -@@ -101,7 +_,7 @@ + direction.getStepX(), + direction.getStepY(), direction.getStepZ(), dispenseConfig.power(), - dispenseConfig.uncertainty() +- dispenseConfig.uncertainty() - ); ++ io.papermc.paper.configuration.ProjectileUncertainty.resolve(spawnedProjectile.getType(), dispenseConfig.uncertainty()) // Paper - configurable projectile uncertainty + ).spawn(org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OMINOUS_ITEM_SPAWNER); // Paper - fixes and addition to spawn reason API projectile.setOwner(this); return projectile; diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch index e0f8cef5ef29..3ce399312c11 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/equine/Llama.java.patch @@ -45,4 +45,9 @@ + return super.getMaxTemper(); // Paper - Missing entity API; delegate to parent } ++@@ -348,7 +_,7 @@ ++ if (this.level() instanceof ServerLevel serverLevel) { ++ Projectile.spawnProjectileUsingShoot(spit, serverLevel, ItemStack.EMPTY, xd, yd + yo, zd, 1.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(spit.getType(), 10.0F)); // Paper - configurable projectile uncertainty ++ } ++ @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch index 5f42e0859876..4065d6b8758c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/golem/SnowGolem.java.patch @@ -35,9 +35,22 @@ this.gameEvent(GameEvent.SHEAR, player); itemStack.hurtAndBreak(1, player, hand.asEquipmentSlot()); } -@@ -151,9 +_,29 @@ +@@ -124,7 +_,7 @@ + Projectile.spawnProjectile( + new Snowball(serverLevel, this, itemStack), + serverLevel, + itemStack, +- projectile -> projectile.shoot(xd, yd + yo - projectile.getY(), zd, 1.6F, 12.0F) ++ projectile -> projectile.shoot(xd, yd + yo - projectile.getY(), zd, 1.6F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(projectile.getType(), 12.0F)) // Paper - configurable projectile uncertainty + ); + } + + this.playSound(SoundEvents.SNOW_GOLEM_SHOOT, 1.0F, 0.4F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); + } @Override + protected InteractionResult mobInteract(final Player player, final InteractionHand hand) { +@@ -151,9 +_,29 @@ public void shear(final ServerLevel level, final SoundSource soundSource, final ItemStack tool) { + // Paper start - custom shear drops + this.shear(level, soundSource, tool, this.generateDefaultDrops(level, tool)); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch index e34cc86ae3e0..18bf6a039fc6 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/Witch.java.patch @@ -80,5 +80,5 @@ + itemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getPotion()); + // Paper end - WitchThrowPotionEven Projectile.spawnProjectileUsingShoot( - ThrownSplashPotion::new, serverLevel, itemStack, this, xd, yd + dist * 0.2, zd, dist <= 2.0 ? 0.45F : 0.75F, 8.0F + ThrownSplashPotion::new, serverLevel, itemStack, this, xd, yd + dist * 0.2, zd, dist <= 2.0 ? 0.45F : 0.75F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.SPLASH_POTION, 8.0F) // Paper - configurable projectile uncertainty ); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/breeze/Shoot.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/breeze/Shoot.java.patch new file mode 100644 index 000000000000..0827134ec522 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/breeze/Shoot.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/monster/breeze/Shoot.java ++++ b/net/minecraft/world/entity/monster/breeze/Shoot.java +@@ -93,7 +_,7 @@ + double yd = target.getY(target.isPassenger() ? 0.8 : 0.3) - breeze.getFiringYPosition(); + double zd = target.getZ() - breeze.getZ(); + Projectile.spawnProjectileUsingShoot( +- new BreezeWindCharge(breeze, level), level, ItemStack.EMPTY, xd, yd, zd, 0.7F, 5 - level.getDifficulty().getId() * 4 ++ new BreezeWindCharge(breeze, level), level, ItemStack.EMPTY, xd, yd, zd, 0.7F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.BREEZE_WIND_CHARGE, 5 - level.getDifficulty().getId() * 4) // Paper - configurable projectile uncertainty + ); + breeze.playSound(SoundEvents.BREEZE_SHOOT, 1.5F, 1.0F); + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch index 110a69882b7e..68fa21afac04 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/illager/Illusioner.java.patch @@ -16,7 +16,7 @@ if (this.level() instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileUsingShoot( + Projectile.Delayed delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed - arrow, serverLevel, projectile, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4 + arrow, serverLevel, projectile, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(arrow.getType(), 14 - serverLevel.getDifficulty().getId() * 4) // Paper - configurable projectile uncertainty ); + + // Paper start - call EntityShootBowEvent diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch index e74512076626..671b9e0d36f1 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/skeleton/AbstractSkeleton.java.patch @@ -55,7 +55,7 @@ if (this.level() instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileUsingShoot( + Projectile.Delayed delayedEntity = Projectile.spawnProjectileUsingShootDelayed( // Paper - delayed - arrow, serverLevel, projectile, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, 14 - serverLevel.getDifficulty().getId() * 4 + arrow, serverLevel, projectile, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(arrow.getType(), 14 - serverLevel.getDifficulty().getId() * 4) // Paper - configurable projectile uncertainty ); + + // Paper start - call EntityShootBowEvent diff --git a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch index 925cfb08bed9..39f96c2a2950 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/monster/zombie/Drowned.java.patch @@ -9,3 +9,10 @@ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Axolotl.class, true, false)); this.targetSelector.addGoal(5, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR)); +@@ -288,7 +_,7 @@ + if (this.level() instanceof ServerLevel serverLevel) { + Projectile.spawnProjectileUsingShoot( +- trident, serverLevel, tridentItemStack, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, 14 - this.level().getDifficulty().getId() * 4 ++ trident, serverLevel, tridentItemStack, xd, yd + distanceToTarget * 0.2F, zd, 1.6F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(trident.getType(), 14 - this.level().getDifficulty().getId() * 4) // Paper - configurable projectile uncertainty + ); + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch index 8a324ab85f8d..f34eb2b436b6 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BowItem.java.patch @@ -5,7 +5,15 @@ List firedProjectiles = draw(itemStack, projectile, player); if (level instanceof ServerLevel serverLevel && !firedProjectiles.isEmpty()) { - this.shoot(serverLevel, player, player.getUsedItemHand(), itemStack, firedProjectiles, pow * 3.0F, 1.0F, pow == 1.0F, null); -+ this.shoot(serverLevel, player, player.getUsedItemHand(), itemStack, firedProjectiles, pow * 3.0F, 1.0F, pow == 1.0F, null, pow); // Paper - Pass draw strength ++ this.shoot(serverLevel, player, player.getUsedItemHand(), itemStack, firedProjectiles, pow * 3.0F, 1.0F, pow == 1.0F, null, pow); // Paper - Pass draw strength } level.playSound( +@@ -68,7 +_,7 @@ + final @Nullable LivingEntity targetOverrride + ) { +- projectileEntity.shootFromRotation(shooter, shooter.getXRot(), shooter.getYRot() + angle, 0.0F, power, uncertainty); ++ projectileEntity.shootFromRotation(shooter, shooter.getXRot(), shooter.getYRot() + angle, 0.0F, power, io.papermc.paper.configuration.ProjectileUncertainty.resolve(projectileEntity.getType(), uncertainty)); // Paper - configurable projectile uncertainty + } + + public static float getPowerForTime(final int timeHeld) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch index 6bcc58cd3d4c..99295198c9d2 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch @@ -30,6 +30,14 @@ } Projectile projectileEntity = super.createProjectile(level, shooter, heldItem, projectile, isCrit); +@@ -130,7 +_,7 @@ + } + +- projectileEntity.shoot(shotVector.x(), shotVector.y(), shotVector.z(), power, uncertainty); ++ projectileEntity.shoot(shotVector.x(), shotVector.y(), shotVector.z(), power, io.papermc.paper.configuration.ProjectileUncertainty.resolve(projectileEntity.getType(), uncertainty)); // Paper - configurable projectile uncertainty + float soundPitch = getShotPitch(livingEntity.getRandom(), index); + livingEntity.level() + .playSound( @@ -187,7 +_,7 @@ if (level instanceof ServerLevel serverLevel) { ChargedProjectiles charged = weapon.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.EMPTY); diff --git a/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch index c04d7fd6a803..86de7d441cb9 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch @@ -18,7 +18,7 @@ - Projectile.spawnProjectileFromRotation(ThrownEgg::new, serverLevel, itemStack, player, 0.0F, 1.5F, 1.0F); - } + // Paper start -+ final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemStack, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed thrownEgg = Projectile.spawnProjectileFromRotationDelayed(ThrownEgg::new, (ServerLevel) level, itemStack, player, 0.0F, EggItem.PROJECTILE_SHOOT_POWER, io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.EGG, 1.0F)); // Paper - configurable projectile uncertainty + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownEgg.projectile().getBukkitEntity()); + if (event.callEvent() && thrownEgg.attemptSpawn()) { + if (event.shouldConsume()) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch index 233d4a56fe41..48f500184ee3 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch @@ -18,7 +18,7 @@ - Projectile.spawnProjectileFromRotation(ThrownEnderpearl::new, serverLevel, itemStack, player, 0.0F, 1.5F, 1.0F); + // CraftBukkit start + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemStack, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed thrownEnderpearl = Projectile.spawnProjectileFromRotationDelayed(ThrownEnderpearl::new, serverLevel, itemStack, player, 0.0F, EnderpearlItem.PROJECTILE_SHOOT_POWER, io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.ENDER_PEARL, 1.0F)); // Paper - configurable projectile uncertainty + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownEnderpearl.projectile().getBukkitEntity()); + if (event.callEvent() && thrownEnderpearl.attemptSpawn()) { + if (event.shouldConsume()) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch index 7ca72e919df4..abcedb91d19d 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch @@ -18,7 +18,7 @@ if (level instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileFromRotation(ThrownExperienceBottle::new, serverLevel, itemStack, player, -20.0F, 0.7F, 1.0F); + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed thrownExperienceBottle = Projectile.spawnProjectileFromRotationDelayed(ThrownExperienceBottle::new, serverLevel, itemStack, player, -20.0F, 0.7F, 1.0F); ++ final Projectile.Delayed thrownExperienceBottle = Projectile.spawnProjectileFromRotationDelayed(ThrownExperienceBottle::new, serverLevel, itemStack, player, -20.0F, 0.7F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.EXPERIENCE_BOTTLE, 1.0F)); // Paper - configurable projectile uncertainty + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownExperienceBottle.projectile().getBukkitEntity()); + if (event.callEvent() && thrownExperienceBottle.attemptSpawn()) { + if (event.shouldConsume()) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch index 0f4722e438a9..58e6da085044 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch @@ -18,7 +18,7 @@ if (level instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileFromRotation(Snowball::new, serverLevel, itemStack, player, 0.0F, 1.5F, 1.0F); + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemStack, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, 1.0F); ++ final Projectile.Delayed snowball = Projectile.spawnProjectileFromRotationDelayed(Snowball::new, serverLevel, itemStack, player, 0.0F, SnowballItem.PROJECTILE_SHOOT_POWER, io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.SNOWBALL, 1.0F)); // Paper - configurable projectile uncertainty + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) snowball.projectile().getBukkitEntity()); + if (event.callEvent() && snowball.attemptSpawn()) { + player.awardStat(Stats.ITEM_USED.get(this)); diff --git a/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch index 5d405d26804c..5faf9bea9bd2 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch @@ -6,7 +6,7 @@ if (level instanceof ServerLevel serverLevel) { - Projectile.spawnProjectileFromRotation(this::createPotion, serverLevel, itemStack, player, -20.0F, 0.5F, 1.0F); + // Paper start - PlayerLaunchProjectileEvent -+ final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemStack, player, -20.0F, 0.5F, 1.0F); ++ final Projectile.Delayed thrownPotion = Projectile.spawnProjectileFromRotationDelayed(this::createPotion, serverLevel, itemStack, player, -20.0F, 0.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(itemStack.is(net.minecraft.world.item.Items.LINGERING_POTION) ? net.minecraft.world.entity.EntityTypes.LINGERING_POTION : net.minecraft.world.entity.EntityTypes.SPLASH_POTION, 1.0F)); // Paper - configurable projectile uncertainty + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) thrownPotion.projectile().getBukkitEntity()); + if (event.callEvent() && thrownPotion.attemptSpawn()) { + if (event.shouldConsume()) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch index 603f4f408ce2..9ca95c89bde4 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch @@ -11,7 +11,7 @@ - ThrownTrident trident = Projectile.spawnProjectileFromRotation( + ItemStack thrownItemStack = itemStack.copyWithCount(1); // Paper + Projectile.Delayed tridentDelayed = Projectile.spawnProjectileFromRotationDelayed( // Paper - PlayerLaunchProjectileEvent( - ThrownTrident::new, serverLevel, thrownItemStack, player, 0.0F, 2.5F, 1.0F + ThrownTrident::new, serverLevel, thrownItemStack, player, 0.0F, 2.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.TRIDENT, 1.0F) // Paper - configurable projectile uncertainty ); + // Paper start - PlayerLaunchProjectileEvent + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), (org.bukkit.entity.Projectile) tridentDelayed.projectile().getBukkitEntity()); diff --git a/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch index a68f6c793efe..7775e044d6d6 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch @@ -11,7 +11,8 @@ stack, @@ -37,6 +_,22 @@ 1.5F, - 1.0F +- 1.0F ++ io.papermc.paper.configuration.ProjectileUncertainty.resolve(net.minecraft.world.entity.EntityTypes.WIND_CHARGE, 1.0F) // Paper - configurable projectile uncertainty ); + // Paper start - PlayerLaunchProjectileEvent + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), (org.bukkit.entity.Projectile) windCharge.projectile().getBukkitEntity()); diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java index 232bc5e8b7cb..7adf6883277f 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java @@ -2,6 +2,7 @@ import com.mojang.logging.LogUtils; import io.papermc.paper.configuration.constraint.Constraints; +import io.papermc.paper.configuration.serializer.collection.map.ThrowExceptions; import io.papermc.paper.configuration.serializer.collection.map.WriteKeyBack; import io.papermc.paper.configuration.type.number.DoubleOr; import io.papermc.paper.configuration.type.number.IntOr; @@ -12,7 +13,10 @@ import net.minecraft.core.component.DataComponents; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ServerboundPlaceRecipePacket; +import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import net.minecraft.resources.Identifier; +import net.minecraft.world.entity.EntityType; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.spongepowered.configurate.objectmapping.ConfigSerializable; @@ -29,7 +33,7 @@ @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"}) public class GlobalConfiguration extends ConfigurationPart { private static final Logger LOGGER = LogUtils.getLogger(); - static final int CURRENT_VERSION = 31; // (when you change the version, change the comment, so it conflicts on rebases): allow-nether property to config + static final int CURRENT_VERSION = 32; // (when you change the version, change the comment, so it conflicts on rebases): configurable projectile uncertainty private static GlobalConfiguration instance; public static boolean isFirstStart = false; public static GlobalConfiguration get() { @@ -394,4 +398,11 @@ public void bindDataSanitizer() { public class UpdateChecker extends ConfigurationPart { public boolean enabled = true; } + + public Projectiles projectiles; + + public class Projectiles extends ConfigurationPart { + @Comment("Per-projectile uncertainty overrides for Projectile#shoot. Set to 0 for no spread. Use 'default' per entry to keep vanilla behavior. Keys are minecraft entity type ids (e.g. minecraft:arrow).") + public @ThrowExceptions Reference2ObjectMap, DoubleOr.Default> uncertainty = new Reference2ObjectOpenHashMap<>(); + } } diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/ProjectileUncertainty.java b/paper-server/src/main/java/io/papermc/paper/configuration/ProjectileUncertainty.java new file mode 100644 index 000000000000..e8353de90055 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/configuration/ProjectileUncertainty.java @@ -0,0 +1,18 @@ +package io.papermc.paper.configuration; + +import io.papermc.paper.configuration.type.number.DoubleOr; +import net.minecraft.world.entity.EntityType; + +public final class ProjectileUncertainty { + + private ProjectileUncertainty() { + } + + public static float resolve(final EntityType type, final float vanillaUncertainty) { + final DoubleOr.Default configured = GlobalConfiguration.get().projectiles.uncertainty.get(type); + if (configured == null) { + return vanillaUncertainty; + } + return (float) configured.or(vanillaUncertainty); + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 335de83ba3e4..505e71d35262 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -689,7 +689,7 @@ public T spawnArrow(Location location, Vector directio } arrow.snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); - arrow.shoot(direction.getX(), direction.getY(), direction.getZ(), speed, spread); + arrow.shoot(direction.getX(), direction.getY(), direction.getZ(), speed, io.papermc.paper.configuration.ProjectileUncertainty.resolve(arrow.getType(), spread)); // Paper - configurable projectile uncertainty this.world.addFreshEntity(arrow); return (T) arrow.getBukkitEntity(); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java index fdde7295df23..9bcf2be5c2a9 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -558,13 +558,13 @@ public T launchProjectile(Class projectile, if (Snowball.class.isAssignableFrom(projectile)) { launch = new net.minecraft.world.entity.projectile.throwableitemprojectile.Snowball(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.SNOWBALL)); - ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, 1.0F); // ItemSnowball + ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // ItemSnowball // Paper - configurable projectile uncertainty } else if (Egg.class.isAssignableFrom(projectile)) { launch = new ThrownEgg(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.EGG)); - ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, 1.0F); // ItemEgg + ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // ItemEgg // Paper - configurable projectile uncertainty } else if (EnderPearl.class.isAssignableFrom(projectile)) { launch = new ThrownEnderpearl(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.ENDER_PEARL)); - ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, 1.0F); // ItemEnderPearl + ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // ItemEnderPearl // Paper - configurable projectile uncertainty } else if (AbstractArrow.class.isAssignableFrom(projectile)) { if (TippedArrow.class.isAssignableFrom(projectile)) { launch = new net.minecraft.world.entity.projectile.arrow.Arrow(world, this.getHandle(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.ARROW), null); @@ -576,17 +576,17 @@ public T launchProjectile(Class projectile, } else { launch = new net.minecraft.world.entity.projectile.arrow.Arrow(world, this.getHandle(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.ARROW), null); } - ((net.minecraft.world.entity.projectile.arrow.AbstractArrow) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, Trident.class.isAssignableFrom(projectile) ? net.minecraft.world.item.TridentItem.PROJECTILE_SHOOT_POWER : 3.0F, 1.0F); // ItemBow // Paper - see TridentItem + ((net.minecraft.world.entity.projectile.arrow.AbstractArrow) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, Trident.class.isAssignableFrom(projectile) ? net.minecraft.world.item.TridentItem.PROJECTILE_SHOOT_POWER : 3.0F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // ItemBow // Paper - see TridentItem; configurable projectile uncertainty } else if (ThrownPotion.class.isAssignableFrom(projectile)) { if (LingeringPotion.class.isAssignableFrom(projectile)) { launch = new net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownLingeringPotion(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.LINGERING_POTION)); } else { launch = new net.minecraft.world.entity.projectile.throwableitemprojectile.ThrownSplashPotion(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.SPLASH_POTION)); } - ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), -20.0F, 0.5F, 1.0F); // ItemSplashPotion + ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), -20.0F, 0.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // ItemSplashPotion // Paper - configurable projectile uncertainty } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { launch = new ThrownExperienceBottle(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.EXPERIENCE_BOTTLE)); - ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), -20.0F, 0.7F, 1.0F); // ItemExpBottle + ((ThrowableProjectile) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), -20.0F, 0.7F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // ItemExpBottle // Paper - configurable projectile uncertainty } else if (FishHook.class.isAssignableFrom(projectile) && this.getHandle() instanceof net.minecraft.world.entity.player.Player) { launch = new FishingHook((net.minecraft.world.entity.player.Player) this.getHandle(), world, 0, 0); } else if (Fireball.class.isAssignableFrom(projectile)) { @@ -608,7 +608,7 @@ public T launchProjectile(Class projectile, } ((net.minecraft.world.entity.projectile.hurtingprojectile.windcharge.AbstractWindCharge) launch).setOwner(this.getHandle()); - ((net.minecraft.world.entity.projectile.hurtingprojectile.windcharge.AbstractWindCharge) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, 1.0F); // WindChargeItem + ((net.minecraft.world.entity.projectile.hurtingprojectile.windcharge.AbstractWindCharge) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 1.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // WindChargeItem // Paper - configurable projectile uncertainty } else { launch = new LargeFireball(world, this.getHandle(), vec, 1); } @@ -622,7 +622,7 @@ public T launchProjectile(Class projectile, launch = EntityTypes.LLAMA_SPIT.create(world, EntitySpawnReason.TRIGGERED); ((net.minecraft.world.entity.projectile.LlamaSpit) launch).setOwner(this.getHandle()); - ((net.minecraft.world.entity.projectile.LlamaSpit) launch).shoot(direction.getX(), direction.getY(), direction.getZ(), 1.5F, 10.0F); // EntityLlama + ((net.minecraft.world.entity.projectile.LlamaSpit) launch).shoot(direction.getX(), direction.getY(), direction.getZ(), 1.5F, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 10.0F)); // EntityLlama // Paper - configurable projectile uncertainty launch.snapTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); } else if (ShulkerBullet.class.isAssignableFrom(projectile)) { Location location = this.getEyeLocation(); @@ -650,7 +650,7 @@ public T launchProjectile(Class projectile, org.joml.Quaternionf upQuaternion = new org.joml.Quaternionf().setAngleAxis((double)(angle * (float) (Math.PI / 180.0)), upVector.x, upVector.y, upVector.z); Vec3 viewVec = this.getHandle().getViewVector(1.0F); org.joml.Vector3f shotVector = viewVec.toVector3f().rotate(upQuaternion); - ((FireworkRocketEntity) launch).shoot(shotVector.x(), shotVector.y(), shotVector.z(), net.minecraft.world.item.CrossbowItem.FIREWORK_POWER, 1.0F); + ((FireworkRocketEntity) launch).shoot(shotVector.x(), shotVector.y(), shotVector.z(), net.minecraft.world.item.CrossbowItem.FIREWORK_POWER, io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), 1.0F)); // Paper - configurable projectile uncertainty // Paper end } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java b/paper-server/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java index 4d7457349078..a3b66f0196d7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java @@ -90,7 +90,7 @@ public T launchProjectile(Class projectile, Position position = dispenseConfig.positionFunction().getDispensePosition(source, direction); net.minecraft.world.entity.projectile.Projectile launch = projectileItem.asProjectile(level, position, itemstack, direction); launch.projectileSource = this; - projectileItem.shoot(launch, direction.getStepX(), direction.getStepY(), direction.getStepZ(), dispenseConfig.power(), dispenseConfig.uncertainty()); + projectileItem.shoot(launch, direction.getStepX(), direction.getStepY(), direction.getStepZ(), dispenseConfig.power(), io.papermc.paper.configuration.ProjectileUncertainty.resolve(launch.getType(), dispenseConfig.uncertainty())); // Paper - configurable projectile uncertainty if (velocity != null) { launch.getBukkitEntity().setVelocity(velocity);