aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0794-More-Projectile-API.patch
diff options
context:
space:
mode:
authorJake Potrebic <[email protected]>2023-06-20 09:40:14 -0700
committerJake Potrebic <[email protected]>2023-06-20 09:50:05 -0700
commit3fa4ea66682488002ec01ff1638ffc3951183d20 (patch)
treec583f1df959c5faaebecfa4629385f1522928e07 /patches/server/0794-More-Projectile-API.patch
parent873533b1297480d6157ee5bbdbe51720c790e575 (diff)
downloadPaper-3fa4ea66682488002ec01ff1638ffc3951183d20.tar.gz
Paper-3fa4ea66682488002ec01ff1638ffc3951183d20.zip
Updated Upstream (Bukkit/CraftBukkit/Spigot)
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 6680169e [#660/Enum] Merge remote-tracking branch 'origin/pr/660' into experimental 8b97f215 Add missing AbstractTestingBase extension 9f21f42b [#660/Enum] Merge remote-tracking branch 'origin/pr/660' into experimental fb59a4a0 Create experimental version a7c1393b Merge branch 'master' into enums-to-registers 1af01165 Merge branch 'master' into enums-to-registers 4ee82e4e Implement feedback af8ffd60 Merge branch 'master' into enums-to-registers 6a8ea63f Updated to 1.20 22ae9ebc Merge branch 'master' into enums-to-registers b1d669be Some clean up 685d812e Merge branch 'master' into enums-to-registers 00d778c7 Convert MusicInstrument bba2eb5f Convert GameEvent ffbf67a1 Convert PotionType eacaa45d Convert Particle d08d21d1 Convert PatternType c6b51f7c Convert Cat type af6c2987 Make missing Frog variant abstract a67a5f5c Add missing Annotation 6ab21c50 Change how converting from / to BlockType and ItemType work e3e84e69 Add Objects.requireNonNull so that there are not marked as nullable fceddab5 Add missing deprecation e91906f5 Don't convert legacy in register instead, only in required method 2200b334 Use static constructors for ItemStack b5f483b0 Deprecated Material a995df2a Fix typo getItemTyp 9cedb664 import ItemType 27e282b2 getSteerItemType -> getSteerItem d8d0e43b Better Piglin method names 3a2ab399 BLOCK_TYPE -> BLOCK, ITEM_TYPE -> ITEM a0eb63ac Interface it is 4bb0b646 Split Material into BlockType and ItemType b6bfcff5 Merge branch 'master' into enums-to-registers 1f86c847 Updated to Mockito 5.3.1 280ee1f7 Fix merge, updated to 1.19.4 9e0c7ad5 Merge branch 'master' into enums-to-registers fdbed698 Updated to 1.19.3 85c3e2d3 Merge branch 'master' into enums-to-registers b2c390af Fix merge 4e405647 Merge branch 'master' into enums-to-registers d01b4c90 Fix copy/paste ba2c8cb1 Update to 1.19 7e4f2db2 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers a1a974f0 Fix merge 7d3a91d3 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers 499e22d9 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers a0cf419f Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers d5bd36a2 Fix / Implement merge changes fe643952 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers cf1d2005 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers f18dce93 Make Statistic abstract bbe3f791 Fix 1.18 merge Handle comment out test cases 0988647e Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers 6e4f2c50 Populated BlockType and ItemType 7a58144d Convert Material enum, midpoint push, it compiles and runs 4771132c Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/bukkit into enums-to-registers e6b179ff Convert Material enum, midpoint push to merge BlockState changes f33b85a0 Change other enums / classes. 8b0d5418 Create seperat OldEnum class, which holds common enum methods. 45544426 Change Enums to classes to easier handle none standart minecraft values CraftBukkit Changes: 8969b32d0 [#931/Enum] Merge remote-tracking branch 'origin/pr/931' into experimental 222257a67 Add missing AbstractTestingBase extension 0480af399 [#931/Enum] Merge remote-tracking branch 'origin/pr/931' into experimental 1afa1ddc2 Create experimental version 00780ea51 Ignore FactoryItemMaterialTest test 676969d01 SPIGOT-7389: Handle setting null items in ChiseledBookshelf Inventory 84f10cc36 Fix merge 9e114e13e Merge branch 'master' into enums-to-registers 941787e24 Add missing Commodore for 1.20 updated 6dac9a12d Updated tests 737426398 Only allow reference holder aaaa5fa88 Merge branch 'master' into enums-to-registers 74957eb99 Merge branch 'master' into enums-to-registers a1ca4e870 Merge branch 'master' into enums-to-registers f293f4a61 Updated to 1.20 b434b3d15 Merge branch 'master' into enums-to-registers e99dcbda7 Some clean up fcead8aed Use correct primitive class e955d9c50 Fix some Commodore errors af5526ebb Allow Material to support older plugins b83afd643 Add rewrite for Google enum set methods 067323765 Add missing method replacement in dynamic invocation 63e17e631 Merge branch 'master' into enums-to-registers 26dfcacf8 Bad copy and past d50c9bd6a Convert MusicInstrument c0c5312db Convert GameEvent 39daffe2c Convert PotionType 9b974f832 Convert Particle f528fca63 Convert PatternType 525c65006 Convert Cat type 6832b8fbb More consistent to / from bukkit / minecraft methods d31e38e16 Make missing Frog variant abstract e4f0e7d8e This shouldn't be committed 6fee81baa Add Commodore for EnumSet 82a668683 Fix hasItemType / getItemType f70162d66 Change how converting from / to BlockType and ItemType work c3f7c7886 Don't convert legacy in register instead, only in required method 2039e05fa Use static constructors for ItemStack fe221578b More Commodore 2b70bd171 More Commodore 70f4a89f5 Fix some Commodore 06544ed4b Fix typo getItemTyp 6269d2e42 getSteerItemType -> getSteerItem a19ac46c0 Better Piglin method names eef5f52c6 BLOCK_TYPE -> BLOCK, ITEM_TYPE -> ITEM bbaff1348 Interface it is c39e1316c Finish Commodore action for Material split dd8552105 Work on Commodore 1d4ef8bf2 Split Material into BlockType and ItemType 6c5a98220 Merge branch 'master' into enums-to-registers 869658a96 Handle Material calls in lambda expression 523ac4ac0 Add reroute for Class#getEnumConstants 0a4463279 Use extra method for getting registry 602d9b404 Updated to Mockito 5.3.1 8ff87b77d Fix merge, updated to 1.19.4 9d739d313 Merge branch 'master' into enums-to-registers eb6f702ff Reduce usage of BuiltInRegistries b6f667cac Some more asm compatibility changes, add config option 87c931d38 Handle enum maps a2c6699db Updated to 1.19.3 f7c27584f Merge branch 'master' into enums-to-registers 2f95b9951 Fix merge 184b05740 Merge branch 'master' into enums-to-registers 12bd8de26 Updated to 1.19 9c57831b7 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers 4ed8eb402 Fix merge a9faac8e4 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers 0d2988603 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers 3f8f9557d Fix merge, updated to 1.18.2 1560490c6 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers a0e4eb12c Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers 8b59f682d Move getType method to CraftEntity class b849c0147 Add missing patches 4644ba79f Fix / Implement merge changes cf9ee732e Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers 0c9125b67 Use Tag where possible cc05153d9 Cache interactable call ab5cc36de Use getHandle 83ebf4114 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers bc20aea0c Make Statistic abstract 3faa7e135 Add Tests for Material BlockType and ItemType e10f74365 Fix 1.18 merge Handle comment out test cases f72f70ec4 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers dbf4f5b7e Populated BlockType and ItemType 015afc1bc Convert Material enum, midpoint push, it compiles and runs cc0112866 Merge branch 'master' of https://hub.spigotmc.org/stash/scm/~derfrzocker/craftbukkit into enums-to-registers e26742c59 Convert Material enum, midpoint push to merge BlockState changes 796ad9295 Fix bug in legacy naming converting. 199c8278c Change other enums / classes. fd513652a Seperated custom biome value handling. Fix compareTo call. 60c71ce07 Change Enums to classes to easier handle none standart minecraft values Spigot Changes: addcf45f [Enum] Rebuild patches
Diffstat (limited to 'patches/server/0794-More-Projectile-API.patch')
-rw-r--r--patches/server/0794-More-Projectile-API.patch520
1 files changed, 520 insertions, 0 deletions
diff --git a/patches/server/0794-More-Projectile-API.patch b/patches/server/0794-More-Projectile-API.patch
new file mode 100644
index 0000000000..cf262dae57
--- /dev/null
+++ b/patches/server/0794-More-Projectile-API.patch
@@ -0,0 +1,520 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Owen1212055 <[email protected]>
+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.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.ThrownTrident dealtDamage
+public net.minecraft.world.entity.projectile.Projectile hasBeenShot
+public net.minecraft.world.entity.projectile.Projectile leftOwner
+public net.minecraft.world.entity.projectile.Projectile preOnHit(Lnet/minecraft/world/phys/HitResult;)V
+public net.minecraft.world.entity.projectile.Projectile canHitEntity(Lnet/minecraft/world/entity/Entity;)Z
+
+Co-authored-by: Nassim Jahnke <[email protected]>
+
+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 bc8cc9ced3fd32ff916c42e8ae95a95414dd1f25..6b3153de653a72720537795ff96f0c64fb6932d0 100644
+--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java
++++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java
+@@ -99,6 +99,11 @@ public class ThrownPotion extends ThrowableItemProjectile implements ItemSupplie
+ @Override
+ protected void onHit(HitResult hitResult) {
+ super.onHit(hitResult);
++ // Paper start - More projectile API
++ this.splash(hitResult);
++ }
++ public void splash(@org.jetbrains.annotations.Nullable HitResult hitResult) {
++ // Paper end - More projectile API
+ if (!this.level().isClientSide) {
+ ItemStack itemstack = this.getItem();
+ Potion potionregistry = PotionUtils.getPotion(itemstack);
+@@ -112,7 +117,7 @@ public class ThrownPotion extends ThrowableItemProjectile implements ItemSupplie
+ if (this.isLingering()) {
+ showParticles = this.makeAreaOfEffectCloud(itemstack, potionregistry); // Paper
+ } else {
+- showParticles = this.applySplash(list, hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null); // Paper
++ showParticles = this.applySplash(list, hitResult != null && hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null); // Paper - nullable hitResult
+ }
+ }
+
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java b/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java
+index 40e5b19bc8fa3de3b3d54da0762aee5bd7bb8d7b..b3814bd6c6d6aae090fe417696535ed1376d84d5 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java
+@@ -21,5 +21,66 @@ public abstract class AbstractProjectile extends CraftEntity implements Projecti
+ public void setBounce(boolean doesBounce) {
+ this.doesBounce = doesBounce;
+ }
++ // Paper start
++ @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().preOnHit(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().preOnHit(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
+
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
+index 5056ec1ad51be9209591d34d32d256c350feed63..96a20efc60efef4485eca9ebffed92dc195ed357 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
+@@ -58,20 +58,7 @@ public class CraftArrow extends AbstractProjectile implements AbstractArrow {
+ 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() {
+@@ -105,6 +92,27 @@ public class CraftArrow extends AbstractProjectile implements AbstractArrow {
+ return org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(getHandle().getPickupItem());
+ }
+
++ @Override
++ public void setLifetimeTicks(int ticks) {
++ this.getHandle().life = ticks;
++ }
++
++ @Override
++ public int getLifetimeTicks() {
++ return this.getHandle().life;
++ }
++
++ @org.jetbrains.annotations.NotNull
++ @Override
++ public org.bukkit.Sound getHitSound() {
++ return org.bukkit.craftbukkit.CraftSound.minecraftToBukkit(this.getHandle().soundEvent);
++ }
++
++ @Override
++ public void setHitSound(@org.jetbrains.annotations.NotNull org.bukkit.Sound sound) {
++ this.getHandle().setSoundEvent(org.bukkit.craftbukkit.CraftSound.bukkitToMinecraft(sound));
++ }
++
+ @Override
+ public void setNoPhysics(boolean noPhysics) {
+ this.getHandle().setNoPhysics(noPhysics);
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java
+index 013de344fc9a4b21952fc6177243905982d88486..e04500dcdc5b72cca7ac81b5d12e76822db9c8c5 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java
+@@ -32,20 +32,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 a624a2a6cf31fbfed86930a462cbf1e2d93ca002..2fdaed196a563303bc3ce0c4cdcbc3ce8d8d0d40 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() != ItemType.FIREWORK_ROCKET) {
+- this.item.setType(ItemType.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() != ItemType.FIREWORK_ROCKET) {
++ // this.item.setType(ItemType.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), 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 ? ItemStack.EMPTY : 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 b32a8d0a942d1a3c4e4b047cda4aa32d03b09875..9de72665c947bb00547cdd7c41a890711f5b2afc 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java
+@@ -196,4 +196,15 @@ 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;
++ }
++ // Paper end
+ }
+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 9a68f4ef68870d0baab5b6464d6c0a82a8fd105d..fd5beb956f643532e08613366ebd380d7999e79f 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 929a429428493093837c8a7a065896873d533f2d..807a498c2a80a5bd5eedf5322c699adebf25872f 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java
++++ b/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,40 @@ 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() {
++ return org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(this.getHandle().currentMoveDirection);
++ }
++
++ @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/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
+index 196755521ad7cc68ba87e91592565b2e2c520b43..0d9c96fa877660e756bcf99baef1787b8893eb00 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
+@@ -35,11 +35,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() == ItemType.LINGERING_POTION || item.getType() == ItemType.SPLASH_POTION, "ItemStack type must be ItemType.LINGERING_POTION or ItemType.SPLASH_POTION but was ItemType.%s", item.getType());
++ // Preconditions.checkArgument(item.getType() == ItemType.LINGERING_POTION || item.getType() == ItemType.SPLASH_POTION, "ItemStack type must be ItemType.LINGERING_POTION or ItemType.SPLASH_POTION but was ItemType.%s", item.getType()); // Paper - Projectile API
++ org.bukkit.inventory.meta.PotionMeta meta = (item.getType() == ItemType.LINGERING_POTION || item.getType() == ItemType.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().getItemRaw(), 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) entity;
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java
+index c628594b981f276acae7b9337100d811f919631b..c8b65210d2416b5a293cb4bcc1b71f56ed365cd7 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 CraftArrow 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/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+index a30ccb1ff3a02ce482ce2b0d40c76dd07d3b0dc1..37819199655d03810f9f4c48b242798a43cbc06f 100644
+--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+@@ -279,13 +279,20 @@ 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 meta) {
++ ((org.bukkit.craftbukkit.inventory.CraftMetaItem) meta).applyToItem(itemStack.getOrCreateTag());
++ }
+
+ public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item) {
++ return getItemMeta(item, CraftItemStack.getType(item));
++ }
++ public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, ItemType type) {
++ // Paper end
+ if (!CraftItemStack.hasItemMeta(item)) {
+- return CraftItemFactory.instance().getItemMeta(CraftItemStack.getType(item));
++ return CraftItemFactory.instance().getItemMeta(type); // Paper
+ }
+
+- ItemType type = CraftItemStack.getType(item);
+ if (type == ItemType.WRITTEN_BOOK) {
+ return new CraftMetaBookSigned(item.getTag());
+ }