Paper/patches/server/0668-More-Projectile-API.patch
Spottedleaf 8c5b837e05 Rework async chunk api implementation
Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.

Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.

Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.

Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.

Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
2024-11-18 23:00:59 -08:00

933 lines
50 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Tue, 22 Jun 2021 23:41:11 -0400
Subject: [PATCH] More Projectile API
== AT ==
public net.minecraft.world.entity.projectile.FishingHook timeUntilLured
public net.minecraft.world.entity.projectile.FishingHook fishAngle
public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaX
public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaY
public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaZ
public net.minecraft.world.entity.projectile.ShulkerBullet currentMoveDirection
public net.minecraft.world.entity.projectile.ShulkerBullet flightSteps
public net.minecraft.world.entity.projectile.AbstractArrow soundEvent
public net.minecraft.world.entity.projectile.AbstractArrow setPickupItemStack(Lnet/minecraft/world/item/ItemStack;)V
public net.minecraft.world.entity.projectile.ThrownTrident dealtDamage
public net.minecraft.world.entity.projectile.Arrow NO_EFFECT_COLOR
public net.minecraft.world.entity.projectile.Projectile hasBeenShot
public net.minecraft.world.entity.projectile.Projectile leftOwner
public net.minecraft.world.entity.projectile.Projectile ownerUUID
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 <nassim@njahnke.dev>
Co-authored-by: SoSeDiK <mrsosedik@gmail.com>
Co-authored-by: MelnCat <melncatuwu@gmail.com>
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 536196a740f607adda2a5ae7f644981ac26bef98..1f95234c0a1457050574aa0f6c4b2a8c91b1f272 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
@@ -419,13 +419,18 @@ public class FishingHook extends Projectile {
}
} else {
// 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
}
}
}
+ // 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;
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
index 0e4ba92d97998937ccb83ebd60aaad3fb68f7546..01684c903930b14a0df3a146176e2b476a374efb 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
@@ -288,7 +288,7 @@ public abstract class Projectile extends Entity implements TraceableEntity {
}
// 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/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java
index d6ac07d9d5ee0430a1d91b7084b378aac1d047e5..a486466040a646b8a5a5ff2430cdd25b95b7e20f 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java
@@ -103,8 +103,12 @@ public class ThrownPotion extends ThrowableItemProjectile {
@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);
@@ -116,7 +120,7 @@ public class ThrownPotion extends ThrowableItemProjectile {
if (this.isLingering()) {
showParticles = this.makeAreaOfEffectCloud(potioncontents, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper
} else {
- 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
}
}
@@ -178,7 +182,7 @@ public class ThrownPotion extends ThrowableItemProjectile {
}
- private boolean applySplash(ServerLevel worldserver, Iterable<MobEffectInstance> iterable, @Nullable Entity entity, HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - Fix potions splash events
+ private boolean applySplash(ServerLevel worldserver, Iterable<MobEffectInstance> 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<net.minecraft.world.entity.LivingEntity> list = worldserver.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb);
Map<LivingEntity, Double> affected = new HashMap<LivingEntity, Double>(); // CraftBukkit
@@ -256,7 +260,7 @@ public class ThrownPotion extends ThrowableItemProjectile {
}
- 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();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java b/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java
index 91c2d0b40d3fca86938cd454e1415a4eea3df7c7..de4fb2654c7895cfd83ad694455ee56cb708c2f2 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java
index 0f85c1f991469b277bba8b40b087f7224b4b3a85..1f30109abd86b76af343eb5eb75ec3db83ef9417 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java
index 6591513bb62226b6f85fd2ef9f6ebe376f0f7362..f9c113dc018702159345240d6d0de85767afa0c3 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
index 199d5836dc787cca54c6b653a4e67573f2f758a2..15d50a284cafc2eb59239ca00926836526f09e06 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java
index 0bafd3b1a55154c8e9eb37c96df9f5985640a675..0b9c4f9b61651660e821735f18b4f3cc7cfd164c 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java
@@ -442,7 +442,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/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java
index 1b084d63bdbb24dad45d28eed1693eb6e26e24dc..43d7bea201a52cfeacf60c75caa28dfd2c4ff164 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java
index c9e15a9d82dee935293b2e7e233f5b9b2d822448..2d54cf6f3d9696c55335f0a2057025e2034d4e13 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
index 6e2f91423371ead9890095cf4b1e2299c4dcba28..9d8f4b7176e60180565e3134a14ecf19060f2621 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index e148239d4930e5cbb000beed4de386f992f28d88..14b4c3835388d957653ba34444968bb718ce7f68 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/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 extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity) {
+ // Paper start - launchProjectile consumer
+ return this.launchProjectile(projectile, velocity, null);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity, java.util.function.Consumer<? super T> 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.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/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java
index 70cbc6c668c60e9d608ca7013b72f9b916c05c2d..47633f05b4fab1dcabc2117e7645fe6d6949622a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java
index 696fdfa723aa896a67946f862d7c6890f7f7ab17..4f1fa7dec78970bdfc184d3c1f1632dc9d75a574 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java
index d685d09cae5f862c0004f148298c800736d2139e..b3797a43eeee11cb7ae0774d61bd5f195d0aa3ad 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java
@@ -12,31 +12,56 @@ public class CraftShulkerBullet extends AbstractProjectile implements ShulkerBul
super(server, entity);
}
+ // Paper - moved to AbstractProjectile
+
+ @Override
+ public org.bukkit.entity.Entity getTarget() {
+ return this.getHandle().getTarget() != null ? this.getHandle().getTarget().getBukkitEntity() : null;
+ }
+
@Override
- public ProjectileSource getShooter() {
- return this.getHandle().projectileSource;
+ public void setTarget(org.bukkit.entity.Entity target) {
+ Preconditions.checkState(!this.getHandle().generation, "Cannot set target during world generation");
+
+ this.getHandle().setTarget(target == null ? null : ((CraftEntity) target).getHandle());
}
@Override
- public void setShooter(ProjectileSource shooter) {
- if (shooter instanceof Entity) {
- this.getHandle().setOwner(((CraftEntity) shooter).getHandle());
- } else {
- this.getHandle().setOwner(null);
+ 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
}
- this.getHandle().projectileSource = shooter;
+ return org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(dir);
}
@Override
- public org.bukkit.entity.Entity getTarget() {
- return this.getHandle().getTarget() != null ? this.getHandle().getTarget().getBukkitEntity() : null;
+ public void setCurrentMovementDirection(org.bukkit.block.BlockFace movementDirection) {
+ this.getHandle().currentMoveDirection = org.bukkit.craftbukkit.block.CraftBlock.blockFaceToNotch(movementDirection);
}
@Override
- public void setTarget(org.bukkit.entity.Entity target) {
- Preconditions.checkState(!this.getHandle().generation, "Cannot set target during world generation");
+ public int getFlightSteps() {
+ return this.getHandle().flightSteps;
+ }
- this.getHandle().setTarget(target == null ? null : ((CraftEntity) target).getHandle());
+ @Override
+ public void setFlightSteps(int steps) {
+ this.getHandle().flightSteps = steps;
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
index d67a80161b3e7c1fe02a6ed9d341c00dc7c2847a..f6fa6f1ac50b757dd3bc9a8dee9f6085446182c8 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java
index e374b9f40eddca13b30855d25a2030f8df98138f..4fc893378fb0568ddcffc7593d66df6bfe23f659 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index a5285a8952b2d99bfbb928b1bb31d3ddcf8ed57b..ca094e393de32b64db59c9fe906433761d70d29b 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/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<LivingEntity, Double> affectedEntities) {
+ public static PotionSplashEvent callPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, @Nullable HitResult position, Map<LivingEntity, Double> 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/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index 60062ea5b18b95a14c459f2f3a0743c1e1ac0f12..502be683e8b04a9966043c9bee9d9fe793b12ef5 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/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/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java
index 642f5bf75661eb485558bc227f668e84416f3b5f..76fd4d27730d9139caa67099a6757ea33d681be9 100644
--- a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java
+++ b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java
@@ -56,7 +56,15 @@ public class CraftBlockProjectileSource implements BlockProjectileSource {
@Override
public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity) {
+ // Paper start - launchProjectile consumer
+ return this.launchProjectile(projectile, velocity, null);
+ }
+
+ @Override
+ public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity, java.util.function.Consumer<? super T> 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();