--- a/net/minecraft/world/entity/Leashable.java
+++ b/net/minecraft/world/entity/Leashable.java
@@ -16,6 +16,11 @@
 import net.minecraft.world.level.IMaterial;
 import net.minecraft.world.level.World;
 
+// CraftBukkit start
+import org.bukkit.event.entity.EntityUnleashEvent;
+import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason;
+// CraftBukkit end
+
 public interface Leashable {
 
     String LEASH_TAG = "leash";
@@ -45,7 +50,7 @@
 
     default void setDelayedLeashHolderId(int i) {
         this.setLeashData(new Leashable.a(i));
-        dropLeash((Entity) this, false, false);
+        dropLeash((Entity & Leashable) this, false, false); // CraftBukkit - decompile error
     }
 
     default void readLeashData(NBTTagCompound nbttagcompound) {
@@ -64,7 +69,7 @@
             return new Leashable.a(Either.left(nbttagcompound.getCompound("leash").getUUID("UUID")));
         } else {
             if (nbttagcompound.contains("leash", 11)) {
-                Either<UUID, BlockPosition> either = (Either) GameProfileSerializer.readBlockPos(nbttagcompound, "leash").map(Either::right).orElse((Object) null);
+                Either<UUID, BlockPosition> either = (Either) GameProfileSerializer.readBlockPos(nbttagcompound, "leash").map(Either::right).orElse(null); // CraftBukkit - decompile error
 
                 if (either != null) {
                     return new Leashable.a(either);
@@ -79,6 +84,11 @@
         if (leashable_a != null) {
             Either<UUID, BlockPosition> either = leashable_a.delayedLeashInfo;
             Entity entity = leashable_a.leashHolder;
+            // CraftBukkit start - SPIGOT-7487: Don't save (and possible drop) leash, when the holder was removed by a plugin
+            if (entity != null && entity.pluginRemoved) {
+                return;
+            }
+            // CraftBukkit end
 
             if (entity instanceof EntityLeash) {
                 EntityLeash entityleash = (EntityLeash) entity;
@@ -121,7 +131,9 @@
                 }
 
                 if (e0.tickCount > 100) {
+                    e0.forceDrops = true; // CraftBukkit
                     e0.spawnAtLocation(worldserver, (IMaterial) Items.LEAD);
+                    e0.forceDrops = false; // CraftBukkit
                     ((Leashable) e0).setLeashData((Leashable.a) null);
                 }
             }
@@ -130,11 +142,11 @@
     }
 
     default void dropLeash() {
-        dropLeash((Entity) this, true, true);
+        dropLeash((Entity & Leashable) this, true, true); // CraftBukkit - decompile error
     }
 
     default void removeLeash() {
-        dropLeash((Entity) this, true, false);
+        dropLeash((Entity & Leashable) this, true, false); // CraftBukkit - decompile error
     }
 
     default void onLeashRemoved() {}
@@ -151,7 +163,9 @@
                 WorldServer worldserver = (WorldServer) world;
 
                 if (flag1) {
+                    e0.forceDrops = true; // CraftBukkit
                     e0.spawnAtLocation(worldserver, (IMaterial) Items.LEAD);
+                    e0.forceDrops = false; // CraftBukkit
                 }
 
                 if (flag) {
@@ -171,7 +185,8 @@
 
         if (leashable_a != null && leashable_a.leashHolder != null) {
             if (!e0.isAlive() || !leashable_a.leashHolder.isAlive()) {
-                if (worldserver.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
+                worldserver.getCraftServer().getPluginManager().callEvent(new EntityUnleashEvent(e0.getBukkitEntity(), (!e0.isAlive()) ? UnleashReason.PLAYER_UNLEASH : UnleashReason.HOLDER_GONE)); // CraftBukkit
+                if (worldserver.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS) && !e0.pluginRemoved) { // CraftBukkit - SPIGOT-7487: Don't drop leash, when the holder was removed by a plugin
                     ((Leashable) e0).dropLeash();
                 } else {
                     ((Leashable) e0).removeLeash();
@@ -205,13 +220,18 @@
     }
 
     default void leashTooFarBehaviour() {
+        // CraftBukkit start
+        if (this instanceof Entity entity) {
+            entity.level().getCraftServer().getPluginManager().callEvent(new EntityUnleashEvent(entity.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE));
+        }
+        // CraftBukkit end
         this.dropLeash();
     }
 
     default void closeRangeLeashBehaviour(Entity entity) {}
 
     default void elasticRangeLeashBehaviour(Entity entity, float f) {
-        legacyElasticRangeLeashBehaviour((Entity) this, entity, f);
+        legacyElasticRangeLeashBehaviour((Entity & Leashable) this, entity, f); // CraftBukkit - decompile error
     }
 
     private static <E extends Entity & Leashable> void legacyElasticRangeLeashBehaviour(E e0, Entity entity, float f) {
@@ -223,7 +243,7 @@
     }
 
     default void setLeashedTo(Entity entity, boolean flag) {
-        setLeashedTo((Entity) this, entity, flag);
+        setLeashedTo((Entity & Leashable) this, entity, flag); // CraftBukkit - decompile error
     }
 
     private static <E extends Entity & Leashable> void setLeashedTo(E e0, Entity entity, boolean flag) {
@@ -254,7 +274,7 @@
 
     @Nullable
     default Entity getLeashHolder() {
-        return getLeashHolder((Entity) this);
+        return getLeashHolder((Entity & Leashable) this); // CraftBukkit - decompile error
     }
 
     @Nullable
