diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch index 6e4f5b900..3c4506d90 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FishingHook.java.patch @@ -22,11 +22,10 @@ public class FishingHook extends Projectile { private static final Logger LOGGER = LogUtils.getLogger(); -@@ -67,6 +73,18 @@ - public FishingHook.FishHookState currentState; +@@ -68,6 +74,18 @@ private final int luck; private final int lureSpeed; -+ + + // CraftBukkit start - Extra variables to enable modification of fishing wait time, values are minecraft defaults + public int minWaitTime = 100; + public int maxWaitTime = 600; @@ -38,9 +37,10 @@ + public boolean rainInfluenced = true; + public boolean skyInfluenced = true; + // CraftBukkit end - ++ private FishingHook(EntityType type, Level world, int luckBonus, int waitTimeReductionTicks) { super(type, world); + this.syncronizedRandom = RandomSource.create(); @@ -75,13 +93,17 @@ this.currentState = FishingHook.FishHookState.FLYING; this.luck = Math.max(0, luckBonus); @@ -145,7 +145,7 @@ this.playSound(SoundEvents.FISHING_BOBBER_SPLASH, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); double d3 = this.getY() + 0.5D; -@@ -379,12 +412,16 @@ +@@ -379,16 +412,25 @@ } if (this.timeUntilLured <= 0) { @@ -160,13 +160,22 @@ - this.timeUntilLured = Mth.nextInt(this.random, 100, 600); - this.timeUntilLured -= this.lureSpeed; + // CraftBukkit start - logic to modify fishing wait time -+ this.timeUntilLured = Mth.nextInt(this.random, this.minWaitTime, this.maxWaitTime); -+ this.timeUntilLured -= (this.applyLure) ? (this.lureSpeed >= this.maxWaitTime ? this.timeUntilLured - 1 : this.lureSpeed ) : 0; // Paper - Fix Lure infinite loop ++ this.resetTimeUntilLured(); // Paper - more projectile api - extract time until lured reset logic + // CraftBukkit end } } -@@ -446,12 +483,20 @@ + } ++ // Paper start - more projectile api - extract time until lured reset logic ++ public void resetTimeUntilLured() { ++ this.timeUntilLured = Mth.nextInt(this.random, this.minWaitTime, this.maxWaitTime); ++ this.timeUntilLured -= (this.applyLure) ? (this.lureSpeed >= this.maxWaitTime ? this.timeUntilLured - 1 : this.lureSpeed ) : 0; // Paper - Fix Lure infinite loop ++ } ++ // Paper end - more projectile api - extract time until lured reset logic + + public boolean calculateOpenWater(BlockPos pos) { + FishingHook.OpenWaterType entityfishinghook_waterposition = FishingHook.OpenWaterType.INVALID; +@@ -446,12 +488,20 @@ public void readAdditionalSaveData(CompoundTag nbt) {} public int retrieve(ItemStack usedItem) { @@ -188,7 +197,7 @@ this.pullEntity(this.hookedIn); CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer) entityhuman, usedItem, this, Collections.emptyList()); this.level().broadcastEntityEvent(this, (byte) 31); -@@ -466,15 +511,38 @@ +@@ -466,15 +516,38 @@ while (iterator.hasNext()) { ItemStack itemstack1 = (ItemStack) iterator.next(); @@ -231,7 +240,7 @@ if (itemstack1.is(ItemTags.FISHES)) { entityhuman.awardStat(Stats.FISH_CAUGHT, 1); } -@@ -484,10 +552,27 @@ +@@ -484,10 +557,27 @@ } if (this.onGround()) { @@ -260,7 +269,7 @@ return i; } else { return 0; -@@ -496,7 +581,7 @@ +@@ -496,7 +586,7 @@ @Override public void handleEntityEvent(byte status) { @@ -269,7 +278,7 @@ this.pullEntity(this.hookedIn); } -@@ -520,8 +605,15 @@ +@@ -520,8 +610,15 @@ @Override public void remove(Entity.RemovalReason reason) { @@ -286,7 +295,7 @@ } @Override -@@ -536,7 +628,7 @@ +@@ -536,7 +633,7 @@ } private void updateOwnerInfo(@Nullable FishingHook fishingBobber) { @@ -295,7 +304,7 @@ if (entityhuman != null) { entityhuman.fishing = fishingBobber; -@@ -545,10 +637,10 @@ +@@ -545,10 +642,10 @@ } @Nullable @@ -308,7 +317,7 @@ } @Nullable -@@ -575,7 +667,7 @@ +@@ -575,7 +672,7 @@ int i = packet.getData(); FishingHook.LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(i), i); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch index ce032eb23..a908a53b0 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/Projectile.java.patch @@ -122,7 +122,7 @@ } + // CraftBukkit start - call projectile hit event -+ protected ProjectileDeflection preHitTargetOrDeflectSelf(HitResult movingobjectposition) { ++ public ProjectileDeflection preHitTargetOrDeflectSelf(HitResult movingobjectposition) { // Paper - protected -> public + org.bukkit.event.entity.ProjectileHitEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); + this.hitCancelled = event != null && event.isCancelled(); + if (movingobjectposition.getType() == HitResult.Type.BLOCK || !this.hitCancelled) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownPotion.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownPotion.java.patch index 36b79fa5b..bb959fb39 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownPotion.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/ThrownPotion.java.patch @@ -55,7 +55,18 @@ super(EntityType.POTION, owner, world, stack); } -@@ -99,64 +109,86 @@ +@@ -93,70 +103,96 @@ + @Override + protected void onHit(HitResult hitResult) { + super.onHit(hitResult); ++ // Paper start - More projectile API ++ this.splash(hitResult); ++ } ++ public void splash(@Nullable HitResult hitResult) { ++ // Paper end - More projectile API + Level world = this.level(); +- + if (world instanceof ServerLevel worldserver) { ItemStack itemstack = this.getItem(); PotionContents potioncontents = (PotionContents) itemstack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); @@ -70,7 +81,7 @@ + showParticles = this.makeAreaOfEffectCloud(potioncontents, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper } else { - this.applySplash(worldserver, potioncontents.getAllEffects(), hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null); -+ showParticles = this.applySplash(worldserver, potioncontents.getAllEffects(), hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper ++ showParticles = this.applySplash(worldserver, potioncontents.getAllEffects(), hitResult != null && hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper - More projectile API } } @@ -146,7 +157,7 @@ } - private void applySplash(ServerLevel world, Iterable effects, @Nullable Entity entity) { -+ private boolean applySplash(ServerLevel worldserver, Iterable iterable, @Nullable Entity entity, HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - Fix potions splash events ++ private boolean applySplash(ServerLevel worldserver, Iterable iterable, @Nullable Entity entity, @Nullable HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - Fix potions splash events & More projectile API AABB axisalignedbb = this.getBoundingBox().inflate(4.0D, 2.0D, 4.0D); - List list = world.getEntitiesOfClass(LivingEntity.class, axisalignedbb); + List list = worldserver.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb); @@ -162,7 +173,7 @@ if (entityliving.isAffectedByPotions()) { double d0 = this.distanceToSqr((Entity) entityliving); -@@ -164,43 +196,71 @@ +@@ -164,43 +200,71 @@ if (d0 < 16.0D) { double d1; @@ -242,7 +253,7 @@ } - private void makeAreaOfEffectCloud(PotionContents potion) { -+ private boolean makeAreaOfEffectCloud(PotionContents potioncontents, HitResult position) { // CraftBukkit - Pass MovingObjectPosition ++ private boolean makeAreaOfEffectCloud(PotionContents potioncontents, @Nullable HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - More projectile API AreaEffectCloud entityareaeffectcloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); Entity entity = this.getOwner(); @@ -251,7 +262,7 @@ entityareaeffectcloud.setOwner(entityliving); } -@@ -208,8 +268,17 @@ +@@ -208,8 +272,17 @@ entityareaeffectcloud.setRadiusOnUse(-0.5F); entityareaeffectcloud.setWaitTime(10); entityareaeffectcloud.setRadiusPerTick(-entityareaeffectcloud.getRadius() / (float) entityareaeffectcloud.getDuration()); @@ -271,7 +282,7 @@ } public boolean isLingering() { -@@ -220,19 +289,31 @@ +@@ -220,19 +293,31 @@ BlockState iblockdata = this.level().getBlockState(pos); if (iblockdata.is(BlockTags.FIRE)) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java index 91c2d0b40..de4fb2654 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java @@ -17,4 +17,65 @@ public abstract class AbstractProjectile extends CraftEntity implements Projecti @Override public void setBounce(boolean doesBounce) {} + // Paper start - More projectile API + @Override + public boolean hasLeftShooter() { + return this.getHandle().leftOwner; + } + + @Override + public void setHasLeftShooter(boolean leftShooter) { + this.getHandle().leftOwner = leftShooter; + } + + @Override + public boolean hasBeenShot() { + return this.getHandle().hasBeenShot; + } + + @Override + public void setHasBeenShot(boolean beenShot) { + this.getHandle().hasBeenShot = beenShot; + } + + @Override + public boolean canHitEntity(org.bukkit.entity.Entity entity) { + return this.getHandle().canHitEntity(((CraftEntity) entity).getHandle()); + } + + @Override + public void hitEntity(org.bukkit.entity.Entity entity) { + this.getHandle().preHitTargetOrDeflectSelf(new net.minecraft.world.phys.EntityHitResult(((CraftEntity) entity).getHandle())); + } + + @Override + public void hitEntity(org.bukkit.entity.Entity entity, org.bukkit.util.Vector vector) { + this.getHandle().preHitTargetOrDeflectSelf(new net.minecraft.world.phys.EntityHitResult(((CraftEntity) entity).getHandle(), new net.minecraft.world.phys.Vec3(vector.getX(), vector.getY(), vector.getZ()))); + } + + @Override + public net.minecraft.world.entity.projectile.Projectile getHandle() { + return (net.minecraft.world.entity.projectile.Projectile) entity; + } + + @Override + public final org.bukkit.projectiles.ProjectileSource getShooter() { + return this.getHandle().projectileSource; + } + + @Override + public final void setShooter(org.bukkit.projectiles.ProjectileSource shooter) { + if (shooter instanceof CraftEntity craftEntity) { + this.getHandle().setOwner(craftEntity.getHandle()); + } else { + this.getHandle().setOwner(null); + } + this.getHandle().projectileSource = shooter; + } + + @Override + public java.util.UUID getOwnerUniqueId() { + return this.getHandle().ownerUUID; + } + // Paper end - More projectile API } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java index 0f85c1f99..1f30109ab 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java @@ -59,20 +59,7 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr this.getHandle().setCritArrow(critical); } - @Override - public ProjectileSource getShooter() { - return this.getHandle().projectileSource; - } - - @Override - public void setShooter(ProjectileSource shooter) { - if (shooter instanceof Entity) { - this.getHandle().setOwner(((CraftEntity) shooter).getHandle()); - } else { - this.getHandle().setOwner(null); - } - this.getHandle().projectileSource = shooter; - } + // Paper - moved to AbstractProjectile @Override public boolean isInBlock() { @@ -133,6 +120,7 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr @Override public ItemStack getWeapon() { + if (this.getHandle().getWeaponItem() == null) return null; // Paper - fix NPE return CraftItemStack.asBukkitCopy(this.getHandle().getWeaponItem()); } @@ -152,4 +140,37 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr public String toString() { return "CraftArrow"; } + + // Paper start + @Override + public CraftItemStack getItemStack() { + return CraftItemStack.asCraftMirror(this.getHandle().getPickupItem()); + } + + @Override + public void setItemStack(final ItemStack stack) { + Preconditions.checkArgument(stack != null, "ItemStack cannot be null"); + this.getHandle().setPickupItemStack(CraftItemStack.asNMSCopy(stack)); + } + + @Override + public void setLifetimeTicks(int ticks) { + this.getHandle().life = ticks; + } + + @Override + public int getLifetimeTicks() { + return this.getHandle().life; + } + + @Override + public org.bukkit.Sound getHitSound() { + return org.bukkit.craftbukkit.CraftSound.minecraftToBukkit(this.getHandle().soundEvent); + } + + @Override + public void setHitSound(org.bukkit.Sound sound) { + this.getHandle().setSoundEvent(org.bukkit.craftbukkit.CraftSound.bukkitToMinecraft(sound)); + } + // Paper end } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java index 6591513bb..f9c113dc0 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java @@ -125,7 +125,7 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud @Override public Color getColor() { - return Color.fromRGB(this.getHandle().potionContents.getColor()); + return Color.fromRGB(this.getHandle().potionContents.getColor() & 0x00FFFFFF); // Paper - skip alpha channel } @Override @@ -143,7 +143,7 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud this.removeCustomEffect(effect.getType()); } this.getHandle().addEffect(CraftPotionUtil.fromBukkit(effect)); - this.getHandle().updateColor(); + // this.getHandle().updateColor(); // Paper - already done above return true; } @@ -151,7 +151,7 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud public void clearCustomEffects() { PotionContents old = this.getHandle().potionContents; this.getHandle().setPotionContents(new PotionContents(old.potion(), old.customColor(), List.of(), old.customName())); - this.getHandle().updateColor(); + // this.getHandle().updateColor(); // Paper - already done above } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java index 199d5836d..15d50a284 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java @@ -43,7 +43,7 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow { this.removeCustomEffect(effect.getType()); } this.getHandle().addEffect(CraftPotionUtil.fromBukkit(effect)); - this.getHandle().updateColor(); + // this.getHandle().updateColor(); // Paper - already done above return true; } @@ -51,7 +51,7 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow { public void clearCustomEffects() { PotionContents old = this.getHandle().getPotionContents(); this.getHandle().setPotionContents(new PotionContents(old.potion(), old.customColor(), List.of(), old.customName())); - this.getHandle().updateColor(); + // this.getHandle().updateColor(); // Paper - already done above } @Override @@ -117,16 +117,17 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow { @Override public void setColor(Color color) { - int colorRGB = (color == null) ? -1 : color.asRGB(); + int colorRGB = (color == null) ? net.minecraft.world.entity.projectile.Arrow.NO_EFFECT_COLOR : color.asARGB(); // Paper PotionContents old = this.getHandle().getPotionContents(); this.getHandle().setPotionContents(new PotionContents(old.potion(), Optional.of(colorRGB), old.customEffects(), old.customName())); } @Override public Color getColor() { - if (this.getHandle().getColor() <= -1) { + int color = this.getHandle().getColor(); // Paper + if (color == net.minecraft.world.entity.projectile.Arrow.NO_EFFECT_COLOR) { // Paper return null; } - return Color.fromRGB(this.getHandle().getColor()); + return Color.fromARGB(color); // Paper } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java index f3ad9c57c..2a32f2aa3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java @@ -440,7 +440,7 @@ public final class CraftEntityTypes { BlockPos pos = BlockPos.containing(spawnData.x(), spawnData.y(), spawnData.z()); return new FallingBlockEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), spawnData.world().getBlockState(pos)); // Paper - create falling block entities correctly })); - register(new EntityTypeData<>(EntityType.FIREWORK_ROCKET, Firework.class, CraftFirework::new, spawnData -> new FireworkRocketEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), net.minecraft.world.item.ItemStack.EMPTY))); + register(new EntityTypeData<>(EntityType.FIREWORK_ROCKET, Firework.class, CraftFirework::new, spawnData -> new FireworkRocketEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), FireworkRocketEntity.getDefaultItem()))); // Paper - pass correct default to rocket for data storage register(new EntityTypeData<>(EntityType.EVOKER_FANGS, EvokerFangs.class, CraftEvokerFangs::new, spawnData -> new net.minecraft.world.entity.projectile.EvokerFangs(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), (float) Math.toRadians(spawnData.yaw()), 0, null))); register(new EntityTypeData<>(EntityType.COMMAND_BLOCK_MINECART, CommandMinecart.class, CraftMinecartCommand::new, createMinecart(net.minecraft.world.entity.EntityType.COMMAND_BLOCK_MINECART))); register(new EntityTypeData<>(EntityType.MINECART, RideableMinecart.class, CraftMinecartRideable::new, createMinecart(net.minecraft.world.entity.EntityType.MINECART))); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java index 1b084d63b..43d7bea20 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java @@ -34,20 +34,7 @@ public class CraftFireball extends AbstractProjectile implements Fireball { this.getHandle().bukkitYield = yield; } - @Override - public ProjectileSource getShooter() { - return this.getHandle().projectileSource; - } - - @Override - public void setShooter(ProjectileSource shooter) { - if (shooter instanceof CraftLivingEntity) { - this.getHandle().setOwner(((CraftLivingEntity) shooter).getHandle()); - } else { - this.getHandle().setOwner(null); - } - this.getHandle().projectileSource = shooter; - } + // Paper - moved to AbstractProjectile @Override public Vector getDirection() { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java index c9e15a9d8..2d54cf6f3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java @@ -15,24 +15,26 @@ import org.bukkit.inventory.meta.FireworkMeta; public class CraftFirework extends CraftProjectile implements Firework { private final Random random = new Random(); - private final CraftItemStack item; + //private CraftItemStack item; // Paper - Remove usage, not accurate representation of current item. public CraftFirework(CraftServer server, FireworkRocketEntity entity) { super(server, entity); - ItemStack item = this.getHandle().getEntityData().get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM); - - if (item.isEmpty()) { - item = new ItemStack(Items.FIREWORK_ROCKET); - this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, item); - } - - this.item = CraftItemStack.asCraftMirror(item); - - // Ensure the item is a firework... - if (this.item.getType() != Material.FIREWORK_ROCKET) { - this.item.setType(Material.FIREWORK_ROCKET); - } + // Paper start - Expose firework item directly +// ItemStack item = this.getHandle().getEntityData().get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM); +// +// if (item.isEmpty()) { +// item = new ItemStack(Items.FIREWORK_ROCKET); +// this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, item); +// } +// +// this.item = CraftItemStack.asCraftMirror(item); +// +// // Ensure the item is a firework... +// if (this.item.getType() != Material.FIREWORK_ROCKET) { +// this.item.setType(Material.FIREWORK_ROCKET); +// } + // Paper end - Expose firework item directly } @Override @@ -47,12 +49,12 @@ public class CraftFirework extends CraftProjectile implements Firework { @Override public FireworkMeta getFireworkMeta() { - return (FireworkMeta) this.item.getItemMeta(); + return (FireworkMeta) CraftItemStack.getItemMeta(this.getHandle().getEntityData().get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM), org.bukkit.inventory.ItemType.FIREWORK_ROCKET); // Paper - Expose firework item directly } @Override public void setFireworkMeta(FireworkMeta meta) { - this.item.setItemMeta(meta); + applyFireworkEffect(meta); // Paper - Expose firework item directly // Copied from EntityFireworks constructor, update firework lifetime/power this.getHandle().lifetime = 10 * (1 + meta.getPower()) + this.random.nextInt(6) + this.random.nextInt(7); @@ -136,4 +138,46 @@ public class CraftFirework extends CraftProjectile implements Firework { return getHandle().spawningEntity; } // Paper end + // Paper start - Expose firework item directly + manually setting flight + @Override + public org.bukkit.inventory.ItemStack getItem() { + return CraftItemStack.asBukkitCopy(this.getHandle().getItem()); + } + + @Override + public void setItem(org.bukkit.inventory.ItemStack itemStack) { + FireworkMeta meta = getFireworkMeta(); + ItemStack nmsItem = itemStack == null ? FireworkRocketEntity.getDefaultItem() : CraftItemStack.asNMSCopy(itemStack); + this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, nmsItem); + + applyFireworkEffect(meta); + } + + @Override + public int getTicksFlown() { + return this.getHandle().life; + } + + @Override + public void setTicksFlown(int ticks) { + this.getHandle().life = ticks; + } + + @Override + public int getTicksToDetonate() { + return this.getHandle().lifetime; + } + + @Override + public void setTicksToDetonate(int ticks) { + this.getHandle().lifetime = ticks; + } + + void applyFireworkEffect(FireworkMeta meta) { + ItemStack item = this.getHandle().getItem(); + CraftItemStack.applyMetaToItem(item, meta); + + this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, item); + } + // Paper end - Expose firework item directly + manually setting flight } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java index 6e2f91423..9d8f4b717 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java @@ -196,4 +196,42 @@ public class CraftFishHook extends CraftProjectile implements FishHook { public HookState getState() { return HookState.values()[this.getHandle().currentState.ordinal()]; } + // Paper start - More FishHook API + @Override + public int getWaitTime() { + return this.getHandle().timeUntilLured; + } + + @Override + public void setWaitTime(int ticks) { + this.getHandle().timeUntilLured = ticks; + } + + @Override + public int getTimeUntilBite() { + return this.getHandle().timeUntilHooked; + } + + @Override + public void setTimeUntilBite(final int ticks) { + com.google.common.base.Preconditions.checkArgument(ticks >= 1, "Cannot set time until bite to less than 1 (%s<1)", ticks); + final FishingHook hook = this.getHandle(); + + // Reset the fish angle hook only when this call "enters" the fish into the lure stage. + final boolean alreadyInLuringPhase = hook.timeUntilHooked > 0 && hook.timeUntilLured <= 0; + if (!alreadyInLuringPhase) { + hook.fishAngle = net.minecraft.util.Mth.nextFloat(hook.random, hook.minLureAngle, hook.maxLureAngle); + hook.timeUntilLured = 0; + } + + hook.timeUntilHooked = ticks; + } + + @Override + public void resetFishingState() { + final FishingHook hook = this.getHandle(); + hook.resetTimeUntilLured(); + hook.timeUntilHooked = 0; // Reset time until hooked, will be repopulated once lured time is ticked down. + } + // Paper end } 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 10a95c1a4..9399df20b 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 @@ -579,8 +579,15 @@ 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(); @@ -606,7 +613,7 @@ 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); } - ((net.minecraft.world.entity.projectile.AbstractArrow) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 3.0F, 1.0F); // ItemBow + ((net.minecraft.world.entity.projectile.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 } else if (ThrownPotion.class.isAssignableFrom(projectile)) { if (LingeringPotion.class.isAssignableFrom(projectile)) { launch = new net.minecraft.world.entity.projectile.ThrownPotion(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.LINGERING_POTION)); @@ -663,8 +670,26 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { } else if (Firework.class.isAssignableFrom(projectile)) { Location location = this.getEyeLocation(); - launch = new FireworkRocketEntity(world, net.minecraft.world.item.ItemStack.EMPTY, this.getHandle()); - 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()); @@ -672,6 +697,11 @@ 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/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java index 70cbc6c66..47633f05b 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java @@ -20,13 +20,5 @@ public class CraftLlamaSpit extends AbstractProjectile implements LlamaSpit { return "CraftLlamaSpit"; } - @Override - public ProjectileSource getShooter() { - return (this.getHandle().getOwner() != null) ? (ProjectileSource) this.getHandle().getOwner().getBukkitEntity() : null; - } - - @Override - public void setShooter(ProjectileSource source) { - this.getHandle().setOwner((source != null) ? ((CraftLivingEntity) source).getHandle() : null); - } + // Paper - moved to AbstractProjectile } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java index 696fdfa72..4f1fa7dec 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java @@ -10,20 +10,7 @@ public abstract class CraftProjectile extends AbstractProjectile implements Proj super(server, entity); } - @Override - public ProjectileSource getShooter() { - return this.getHandle().projectileSource; - } - - @Override - public void setShooter(ProjectileSource shooter) { - if (shooter instanceof CraftLivingEntity) { - this.getHandle().setOwner((LivingEntity) ((CraftLivingEntity) shooter).entity); - } else { - this.getHandle().setOwner(null); - } - this.getHandle().projectileSource = shooter; - } + // Paper - moved to AbstractProjectile @Override public net.minecraft.world.entity.projectile.Projectile getHandle() { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java index d685d09ca..b3797a43e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java @@ -12,20 +12,7 @@ public class CraftShulkerBullet extends AbstractProjectile implements ShulkerBul super(server, entity); } - @Override - public ProjectileSource getShooter() { - return this.getHandle().projectileSource; - } - - @Override - public void setShooter(ProjectileSource shooter) { - if (shooter instanceof Entity) { - this.getHandle().setOwner(((CraftEntity) shooter).getHandle()); - } else { - this.getHandle().setOwner(null); - } - this.getHandle().projectileSource = shooter; - } + // Paper - moved to AbstractProjectile @Override public org.bukkit.entity.Entity getTarget() { @@ -39,6 +26,44 @@ public class CraftShulkerBullet extends AbstractProjectile implements ShulkerBul this.getHandle().setTarget(target == null ? null : ((CraftEntity) target).getHandle()); } + @Override + public org.bukkit.util.Vector getTargetDelta() { + net.minecraft.world.entity.projectile.ShulkerBullet bullet = this.getHandle(); + return new org.bukkit.util.Vector(bullet.targetDeltaX, bullet.targetDeltaY, bullet.targetDeltaZ); + } + + @Override + public void setTargetDelta(org.bukkit.util.Vector vector) { + net.minecraft.world.entity.projectile.ShulkerBullet bullet = this.getHandle(); + bullet.targetDeltaX = vector.getX(); + bullet.targetDeltaY = vector.getY(); + bullet.targetDeltaZ = vector.getZ(); + } + + @Override + public org.bukkit.block.BlockFace getCurrentMovementDirection() { + net.minecraft.core.Direction dir = this.getHandle().currentMoveDirection; + if (dir == null) { + return null; // random dir + } + return org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(dir); + } + + @Override + public void setCurrentMovementDirection(org.bukkit.block.BlockFace movementDirection) { + this.getHandle().currentMoveDirection = org.bukkit.craftbukkit.block.CraftBlock.blockFaceToNotch(movementDirection); + } + + @Override + public int getFlightSteps() { + return this.getHandle().flightSteps; + } + + @Override + public void setFlightSteps(int steps) { + this.getHandle().flightSteps = steps; + } + @Override public String toString() { return "CraftShulkerBullet"; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java index d67a80161..f6fa6f1ac 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java @@ -36,11 +36,31 @@ public class CraftThrownPotion extends CraftThrowableProjectile implements Throw @Override public void setItem(ItemStack item) { Preconditions.checkArgument(item != null, "ItemStack cannot be null"); - Preconditions.checkArgument(item.getType() == Material.LINGERING_POTION || item.getType() == Material.SPLASH_POTION, "ItemStack material must be Material.LINGERING_POTION or Material.SPLASH_POTION but was Material.%s", item.getType()); + // Preconditions.checkArgument(item.getType() == Material.LINGERING_POTION || item.getType() == Material.SPLASH_POTION, "ItemStack material must be Material.LINGERING_POTION or Material.SPLASH_POTION but was Material.%s", item.getType()); // Paper - Projectile API + org.bukkit.inventory.meta.PotionMeta meta = (item.getType() == Material.LINGERING_POTION || item.getType() == Material.SPLASH_POTION) ? null : this.getPotionMeta(); // Paper - Projectile API this.getHandle().setItem(CraftItemStack.asNMSCopy(item)); + if (meta != null) this.setPotionMeta(meta); // Paper - Projectile API } + // Paper start - Projectile API + @Override + public org.bukkit.inventory.meta.PotionMeta getPotionMeta() { + return (org.bukkit.inventory.meta.PotionMeta) CraftItemStack.getItemMeta(this.getHandle().getItem(), org.bukkit.inventory.ItemType.SPLASH_POTION); + } + + @Override + public void setPotionMeta(org.bukkit.inventory.meta.PotionMeta meta) { + net.minecraft.world.item.ItemStack item = this.getHandle().getItem(); + CraftItemStack.applyMetaToItem(item, meta); + this.getHandle().setItem(item); // Reset item + } + + @Override + public void splash() { + this.getHandle().splash(null); + } + // Paper end @Override public net.minecraft.world.entity.projectile.ThrownPotion getHandle() { return (net.minecraft.world.entity.projectile.ThrownPotion) this.entity; diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java index e374b9f40..4fc893378 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java @@ -53,5 +53,15 @@ public class CraftTrident extends CraftAbstractArrow implements Trident { com.google.common.base.Preconditions.checkArgument(loyaltyLevel >= 0 && loyaltyLevel <= 127, "The loyalty level has to be between 0 and 127"); this.getHandle().setLoyalty((byte) loyaltyLevel); } + + @Override + public boolean hasDealtDamage() { + return this.getHandle().dealtDamage; + } + + @Override + public void setHasDealtDamage(boolean hasDealtDamage) { + this.getHandle().dealtDamage = hasDealtDamage; + } // Paper end } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index a5285a895..ca094e393 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -841,19 +841,19 @@ public class CraftEventFactory { /** * PotionSplashEvent */ - public static PotionSplashEvent callPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, HitResult position, Map affectedEntities) { + public static PotionSplashEvent callPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, @Nullable HitResult position, Map affectedEntities) { // Paper - nullable hitResult ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); Block hitBlock = null; BlockFace hitFace = null; - if (position.getType() == HitResult.Type.BLOCK) { + if (position != null && position.getType() == HitResult.Type.BLOCK) { // Paper - nullable hitResult BlockHitResult positionBlock = (BlockHitResult) position; hitBlock = CraftBlock.at(potion.level(), positionBlock.getBlockPos()); hitFace = CraftBlock.notchToBlockFace(positionBlock.getDirection()); } org.bukkit.entity.Entity hitEntity = null; - if (position.getType() == HitResult.Type.ENTITY) { + if (position != null && position.getType() == HitResult.Type.ENTITY) { // Paper - nullable hitResult hitEntity = ((EntityHitResult) position).getEntity().getBukkitEntity(); } @@ -862,20 +862,20 @@ public class CraftEventFactory { return event; } - public static LingeringPotionSplashEvent callLingeringPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, HitResult position, net.minecraft.world.entity.AreaEffectCloud cloud) { + public static LingeringPotionSplashEvent callLingeringPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, @Nullable HitResult position, net.minecraft.world.entity.AreaEffectCloud cloud) { // Paper - nullable hitResult ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); AreaEffectCloud effectCloud = (AreaEffectCloud) cloud.getBukkitEntity(); Block hitBlock = null; BlockFace hitFace = null; - if (position.getType() == HitResult.Type.BLOCK) { + if (position != null && position.getType() == HitResult.Type.BLOCK) { // Paper BlockHitResult positionBlock = (BlockHitResult) position; hitBlock = CraftBlock.at(potion.level(), positionBlock.getBlockPos()); hitFace = CraftBlock.notchToBlockFace(positionBlock.getDirection()); } org.bukkit.entity.Entity hitEntity = null; - if (position.getType() == HitResult.Type.ENTITY) { + if (position != null && position.getType() == HitResult.Type.ENTITY) { // Paper hitEntity = ((EntityHitResult) position).getEntity().getBukkitEntity(); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java index 60062ea5b..502be683e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -341,12 +341,23 @@ public final class CraftItemStack extends ItemStack { public ItemMeta getItemMeta() { return CraftItemStack.getItemMeta(this.handle); } + // Paper start + public static void applyMetaToItem(net.minecraft.world.item.ItemStack itemStack, ItemMeta itemMeta) { + final CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator(); + ((CraftMetaItem) itemMeta).applyToItem(tag); + itemStack.applyComponents(tag.build()); + } public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item) { + return getItemMeta(item, null); + } + public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, org.bukkit.inventory.ItemType metaForType) { + // Paper end if (!CraftItemStack.hasItemMeta(item)) { return CraftItemFactory.instance().getItemMeta(CraftItemStack.getType(item)); } + if (metaForType != null) { return ((CraftItemType) metaForType).getItemMeta(item); } // Paper return ((CraftItemType) CraftItemType.minecraftToBukkitNew(item.getItem())).getItemMeta(item); } 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 642f5bf75..76fd4d277 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 @@ -56,7 +56,15 @@ 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 @@ -68,7 +76,7 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { item = Items.SNOWBALL; } else if (Egg.class.isAssignableFrom(projectile)) { item = Items.EGG; - } else if (EnderPearl.class.isAssignableFrom(projectile)) { + } else if (false && EnderPearl.class.isAssignableFrom(projectile)) { // Paper - more projectile API - disallow enderpearl, it is not a projectile item item = Items.ENDER_PEARL; } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { item = Items.EXPERIENCE_BOTTLE; @@ -83,20 +91,20 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { item = Items.TIPPED_ARROW; } else if (SpectralArrow.class.isAssignableFrom(projectile)) { item = Items.SPECTRAL_ARROW; - } else { + } else if (org.bukkit.entity.Arrow.class.isAssignableFrom(projectile)) { // Paper - more projectile API - disallow trident item = Items.ARROW; } } else if (Fireball.class.isAssignableFrom(projectile)) { - if (AbstractWindCharge.class.isAssignableFrom(projectile)) { + if (org.bukkit.entity.WindCharge.class.isAssignableFrom(projectile)) { // Paper - more projectile API - only allow wind charge not breeze wind charge item = Items.WIND_CHARGE; - } else { + } else if (org.bukkit.entity.SmallFireball.class.isAssignableFrom(projectile)) { // Paper - more projectile API - only allow firing fire charges. item = Items.FIRE_CHARGE; } } else if (Firework.class.isAssignableFrom(projectile)) { item = Items.FIREWORK_ROCKET; } - Preconditions.checkArgument(item instanceof ProjectileItem, "Projectile not supported"); + Preconditions.checkArgument(item instanceof ProjectileItem, "Projectile '%s' not supported", projectile.getSimpleName()); // Paper - more projectile API - include simple name in exception ItemStack itemstack = new ItemStack(item); ProjectileItem projectileItem = (ProjectileItem) item; @@ -105,7 +113,7 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { Position iposition = dispenseConfig.positionFunction().getDispensePosition(sourceblock, enumdirection); net.minecraft.world.entity.projectile.Projectile launch = projectileItem.asProjectile(world, iposition, itemstack, enumdirection); - if (Fireball.class.isAssignableFrom(projectile)) { + if (false && Fireball.class.isAssignableFrom(projectile)) { // Paper - more project API - dispensers cannot launch anything but fire charges. AbstractHurtingProjectile customFireball = null; if (WitherSkull.class.isAssignableFrom(projectile)) { launch = customFireball = EntityType.WITHER_SKULL.create(world, EntitySpawnReason.TRIGGERED); @@ -130,7 +138,7 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { } } - if (launch instanceof net.minecraft.world.entity.projectile.AbstractArrow arrow) { + if (false && launch instanceof net.minecraft.world.entity.projectile.AbstractArrow arrow) { // Paper - more projectile API - this is set by the respective ArrowItem when constructing the projectile arrow.pickup = net.minecraft.world.entity.projectile.AbstractArrow.Pickup.ALLOWED; } launch.projectileSource = this; @@ -139,6 +147,11 @@ 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();