aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0794-More-Projectile-API.patch
diff options
context:
space:
mode:
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());
+ }