diff --git a/Spigot-API-Patches/Add-fromBottle-flag-to-Experience-Orbs.patch b/Spigot-API-Patches/Add-fromBottle-flag-to-Experience-Orbs.patch
deleted file mode 100644
index 29a18cb2e5..0000000000
--- a/Spigot-API-Patches/Add-fromBottle-flag-to-Experience-Orbs.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath <Blake.Galbreath@GMail.com>
-Date: Sat, 14 Jan 2017 16:15:20 -0600
-Subject: [PATCH] Add fromBottle flag to Experience Orbs
-
-
-diff --git a/src/main/java/org/bukkit/entity/ExperienceOrb.java b/src/main/java/org/bukkit/entity/ExperienceOrb.java
-index c286edfd..e8a83c06 100644
---- a/src/main/java/org/bukkit/entity/ExperienceOrb.java
-+++ b/src/main/java/org/bukkit/entity/ExperienceOrb.java
-@@ -0,0 +0,0 @@ public interface ExperienceOrb extends Entity {
-      * @param value Amount of experience
-      */
-     public void setExperience(int value);
-+
-+    /**
-+     * Check if this orb was spawned from a {@link ThrownExpBottle}
-+     *
-+     * @return if orb was spawned from a bottle
-+     */
-+    public boolean isFromBottle();
- }
---
\ No newline at end of file
diff --git a/Spigot-API-Patches/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch b/Spigot-API-Patches/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
new file mode 100644
index 0000000000..c4f10130ce
--- /dev/null
+++ b/Spigot-API-Patches/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
@@ -0,0 +1,117 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <aikar@aikar.co>
+Date: Tue, 19 Dec 2017 16:28:32 -0500
+Subject: [PATCH] ExperienceOrbs API for Reason/Source/Triggering player
+
+Adds lots of information about why this orb exists.
+
+Replaces isFromBottle() with logic that persists entity reloads too.
+
+diff --git a/src/main/java/org/bukkit/entity/ExperienceOrb.java b/src/main/java/org/bukkit/entity/ExperienceOrb.java
+index c286edfd..f847543b 100644
+--- a/src/main/java/org/bukkit/entity/ExperienceOrb.java
++++ b/src/main/java/org/bukkit/entity/ExperienceOrb.java
+@@ -0,0 +0,0 @@
+ package org.bukkit.entity;
+ 
++import javax.annotation.Nullable;
++
+ /**
+  * Represents an Experience Orb.
+  */
+@@ -0,0 +0,0 @@ public interface ExperienceOrb extends Entity {
+      * @param value Amount of experience
+      */
+     public void setExperience(int value);
++
++    // Paper start
++    /**
++     * Check if this orb was spawned from a {@link ThrownExpBottle}
++     *
++     * @return if orb was spawned from a bottle
++     * @deprecated Use getSpawnReason() == EXP_BOTTLE
++     */
++    @Deprecated
++    default boolean isFromBottle() {
++        return getSpawnReason() == SpawnReason.EXP_BOTTLE;
++    }
++
++    /**
++     * Reasons for why this Experience Orb was spawned
++     */
++    enum SpawnReason {
++        /**
++         * Spawned by a player dying
++         */
++        PLAYER_DEATH,
++        /**
++         * Spawned by an entity dying after being damaged by a player
++         */
++        ENTITY_DEATH,
++        /**
++         * Spawned by player using a furnace
++         */
++        FURNACE,
++        /**
++         * Spawned by player breeding animals
++         */
++        BREED,
++        /**
++         * Spawned by player trading with a villager
++         */
++        VILLAGER_TRADE,
++        /**
++         * Spawned by player fishing
++         */
++        FISHING,
++        /**
++         * Spawned by player breaking a block that gives experience points such as Diamond Ore
++         */
++        BLOCK_BREAK,
++        /**
++         * Spawned by Bukkit API
++         */
++        CUSTOM,
++        /**
++         * Spawned by a player throwing an experience points bottle
++         */
++        EXP_BOTTLE,
++        /**
++         * We do not know why it was spawned
++         */
++        UNKNOWN
++    }
++
++    /**
++     * If this experience orb was triggered to be spawned by
++     * an entity such as a player, due to events such as killing entity,
++     * breaking blocks, smelting in a furnace, etc, this will return the UUID
++     * of the entity that triggered this orb to drop.
++     *
++     * In the case of an entity being killed, this will be the killers UUID.
++     *
++     * @return UUID of the player that triggered this orb to drop, or null if unknown/no triggering entity
++     */
++    @Nullable java.util.UUID getTriggerEntityId();
++
++    /**
++     * If this experience orb was spawned in relation to another
++     * entity, such as a player or other living entity death, or breeding,
++     * return the source entity UUID.
++     *
++     * In the case of breeding, this will be the new baby entities UUID.
++     * In the case of an entity being killed, this will be the dead entities UUID.
++     *
++     * @return The UUID of the entity that sourced this experience orb
++     */
++    @Nullable java.util.UUID getSourceEntityId();
++
++    /**
++     * Gets the reason that this experience orb was spawned. For any case that we
++     * do not know, such as orbs spawned before this API was added, UNKNOWN is returned.
++     * @return The reason for this orb being spawned.
++     */
++    SpawnReason getSpawnReason();
++    // Paper end
+ }
+--
\ No newline at end of file
diff --git a/Spigot-Server-Patches/Add-PlayerArmorChangeEvent.patch b/Spigot-Server-Patches/Add-PlayerArmorChangeEvent.patch
index 2a5bc37d42..cf4b0d5a20 100644
--- a/Spigot-Server-Patches/Add-PlayerArmorChangeEvent.patch
+++ b/Spigot-Server-Patches/Add-PlayerArmorChangeEvent.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add PlayerArmorChangeEvent
 
 
 diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
-index 135a9c0e2..559aebf20 100644
+index 839008ad7..c0abea96e 100644
 --- a/src/main/java/net/minecraft/server/EntityLiving.java
 +++ b/src/main/java/net/minecraft/server/EntityLiving.java
 @@ -0,0 +0,0 @@
diff --git a/Spigot-Server-Patches/Add-fromBottle-flag-to-Experience-Orbs.patch b/Spigot-Server-Patches/Add-fromBottle-flag-to-Experience-Orbs.patch
deleted file mode 100644
index 843e2eccdb..0000000000
--- a/Spigot-Server-Patches/Add-fromBottle-flag-to-Experience-Orbs.patch
+++ /dev/null
@@ -1,70 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: BillyGalbreath <Blake.Galbreath@GMail.com>
-Date: Sat, 14 Jan 2017 16:15:26 -0600
-Subject: [PATCH] Add fromBottle flag to Experience Orbs
-
-
-diff --git a/src/main/java/net/minecraft/server/EntityExperienceOrb.java b/src/main/java/net/minecraft/server/EntityExperienceOrb.java
-index ec23eb98c..dab33b25a 100644
---- a/src/main/java/net/minecraft/server/EntityExperienceOrb.java
-+++ b/src/main/java/net/minecraft/server/EntityExperienceOrb.java
-@@ -0,0 +0,0 @@ public class EntityExperienceOrb extends Entity {
-     public int value;
-     private EntityHuman targetPlayer;
-     private int targetTime;
-+    // Paper start
-+    private boolean fromBottle = false;
-+
-+    public EntityExperienceOrb(World world, double d0, double d1, double d2, int i, boolean fromBottle) {
-+        this(world, d0, d1, d2, i);
-+        this.fromBottle = fromBottle;
-+    }
-+    // Paper end
- 
-     public EntityExperienceOrb(World world, double d0, double d1, double d2, int i) {
-         super(world);
-@@ -0,0 +0,0 @@ public class EntityExperienceOrb extends Entity {
-         this.value = i;
-     }
- 
-+    // Paper start
-+    public boolean isFromBottle() {
-+        return fromBottle;
-+    }
-+    // Paper end
-+
-     protected boolean playStepSound() {
-         return false;
-     }
-diff --git a/src/main/java/net/minecraft/server/EntityThrownExpBottle.java b/src/main/java/net/minecraft/server/EntityThrownExpBottle.java
-index 0255986fd..289312950 100644
---- a/src/main/java/net/minecraft/server/EntityThrownExpBottle.java
-+++ b/src/main/java/net/minecraft/server/EntityThrownExpBottle.java
-@@ -0,0 +0,0 @@ public class EntityThrownExpBottle extends EntityProjectile {
-                 int j = EntityExperienceOrb.getOrbValue(i);
- 
-                 i -= j;
--                this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j));
-+                this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j, true)); // Paper - add fromBottle flag
-             }
- 
-             this.die();
-diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
-index 3a09cab3d..61e3c6c3e 100644
---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
-+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
-@@ -0,0 +0,0 @@ public class CraftExperienceOrb extends CraftEntity implements ExperienceOrb {
-         getHandle().value = value;
-     }
- 
-+    // Paper start
-+    @Override
-+    public boolean isFromBottle() {
-+        return getHandle().isFromBottle();
-+    }
-+    // Paper end
-+
-     @Override
-     public EntityExperienceOrb getHandle() {
-         return (EntityExperienceOrb) entity;
---
\ No newline at end of file
diff --git a/Spigot-Server-Patches/Cap-Entity-Collisions.patch b/Spigot-Server-Patches/Cap-Entity-Collisions.patch
index 1c5b968182..fcce2da336 100644
--- a/Spigot-Server-Patches/Cap-Entity-Collisions.patch
+++ b/Spigot-Server-Patches/Cap-Entity-Collisions.patch
@@ -39,7 +39,7 @@ index a6ffe144f..f08f4ae56 100644
      // Spigot end
  
 diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
-index 650e549b1..341108472 100644
+index 73643bc7c..28eb13306 100644
 --- a/src/main/java/net/minecraft/server/EntityLiving.java
 +++ b/src/main/java/net/minecraft/server/EntityLiving.java
 @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity {
diff --git a/Spigot-Server-Patches/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch b/Spigot-Server-Patches/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
new file mode 100644
index 0000000000..b3bf233cdc
--- /dev/null
+++ b/Spigot-Server-Patches/ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
@@ -0,0 +1,255 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <aikar@aikar.co>
+Date: Tue, 19 Dec 2017 16:31:46 -0500
+Subject: [PATCH] ExperienceOrbs API for Reason/Source/Triggering player
+
+Adds lots of information about why this orb exists.
+
+Replaces isFromBottle() with logic that persists entity reloads too.
+
+diff --git a/src/main/java/net/minecraft/server/Block.java b/src/main/java/net/minecraft/server/Block.java
+index 352310960..3e2f52647 100644
+--- a/src/main/java/net/minecraft/server/Block.java
++++ b/src/main/java/net/minecraft/server/Block.java
+@@ -0,0 +0,0 @@ public class Block {
+         }
+     }
+ 
+-    protected void dropExperience(World world, BlockPosition blockposition, int i) {
++    protected void dropExperience(World world, BlockPosition blockposition, int i, EntityPlayer player) { // Paper
+         if (!world.isClientSide && world.getGameRules().getBoolean("doTileDrops")) {
+             while (i > 0) {
+                 int j = EntityExperienceOrb.getOrbValue(i);
+ 
+                 i -= j;
+-                world.addEntity(new EntityExperienceOrb(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, j));
++                world.addEntity(new EntityExperienceOrb(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, j, org.bukkit.entity.ExperienceOrb.SpawnReason.BLOCK_BREAK, player)); // Paper
+             }
+         }
+ 
+diff --git a/src/main/java/net/minecraft/server/EntityEnderDragon.java b/src/main/java/net/minecraft/server/EntityEnderDragon.java
+index a8cc6b61a..c925de971 100644
+--- a/src/main/java/net/minecraft/server/EntityEnderDragon.java
++++ b/src/main/java/net/minecraft/server/EntityEnderDragon.java
+@@ -0,0 +0,0 @@ public class EntityEnderDragon extends EntityInsentient implements IComplex, IMo
+             int j = EntityExperienceOrb.getOrbValue(i);
+ 
+             i -= j;
+-            this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j));
++            this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j, org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, this.killer, this)); // Paper
+         }
+ 
+     }
+diff --git a/src/main/java/net/minecraft/server/EntityExperienceOrb.java b/src/main/java/net/minecraft/server/EntityExperienceOrb.java
+index bf5f1f0e8..d567ad4a5 100644
+--- a/src/main/java/net/minecraft/server/EntityExperienceOrb.java
++++ b/src/main/java/net/minecraft/server/EntityExperienceOrb.java
+@@ -0,0 +0,0 @@ public class EntityExperienceOrb extends Entity {
+     public int value;
+     private EntityHuman targetPlayer;
+     private int targetTime;
++    // Paper start
++    public java.util.UUID sourceEntityId;
++    public java.util.UUID triggerEntityId;
++    public org.bukkit.entity.ExperienceOrb.SpawnReason spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN;
++
++    private void loadPaperNBT(NBTTagCompound nbttagcompound) {
++        if (!nbttagcompound.hasKeyOfType("Paper.ExpData", 10)) { // 10 = compound
++            return;
++        }
++        NBTTagCompound comp = nbttagcompound.getCompound("Paper.ExpData");
++        if (comp.hasUUID("source")) {
++            this.sourceEntityId = comp.getUUID("source");
++        }
++        if (comp.hasUUID("trigger")) {
++            this.triggerEntityId = comp.getUUID("trigger");
++        }
++        if (comp.hasKey("reason")) {
++            String reason = comp.getString("reason");
++            try {
++                spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.valueOf(reason);
++            } catch (Exception e) {
++                this.world.getServer().getLogger().warning("Invalid spawnReason set for experience orb: " + e.getMessage() + " - " + reason);
++            }
++        }
++    }
++    private void savePaperNBT(NBTTagCompound nbttagcompound) {
++        NBTTagCompound comp = new NBTTagCompound();
++        if (sourceEntityId != null) {
++            comp.setUUID("source", sourceEntityId);
++        }
++        if (triggerEntityId != null) {
++            comp.setUUID("trigger", triggerEntityId);
++        }
++        if (spawnReason != null && spawnReason != org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN) {
++            comp.setString("reason", spawnReason.name());
++        }
++        nbttagcompound.set("Paper.ExpData", comp);
++    }
++    public EntityExperienceOrb(World world, double d0, double d1, double d2, int i, org.bukkit.entity.ExperienceOrb.SpawnReason reason, Entity triggerId) {
++        this(world, d0, d1, d2, i, reason, triggerId, null);
++    }
+ 
+-    public EntityExperienceOrb(World world, double d0, double d1, double d2, int i) {
++    public EntityExperienceOrb(World world, double d0, double d1, double d2, int i, org.bukkit.entity.ExperienceOrb.SpawnReason reason, Entity triggerId, Entity sourceId) {
+         super(world);
++        this.sourceEntityId = sourceId != null ? sourceId.getUniqueID() : null;
++        this.triggerEntityId = triggerId != null ? triggerId.getUniqueID() : null;
++        this.spawnReason = reason != null ? reason : org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN;
++        // Paper end
+         this.setSize(0.5F, 0.5F);
+         this.setPosition(d0, d1, d2);
+         this.yaw = (float) (Math.random() * 360.0D);
+@@ -0,0 +0,0 @@ public class EntityExperienceOrb extends Entity {
+         nbttagcompound.setShort("Health", (short) this.d);
+         nbttagcompound.setShort("Age", (short) this.b);
+         nbttagcompound.setShort("Value", (short) this.value);
++        savePaperNBT(nbttagcompound); // Paper
+     }
+ 
+     public void a(NBTTagCompound nbttagcompound) {
+         this.d = nbttagcompound.getShort("Health");
+         this.b = nbttagcompound.getShort("Age");
+         this.value = nbttagcompound.getShort("Value");
++        loadPaperNBT(nbttagcompound); // Paper
+     }
+ 
+     public void d(EntityHuman entityhuman) {
+diff --git a/src/main/java/net/minecraft/server/EntityFishingHook.java b/src/main/java/net/minecraft/server/EntityFishingHook.java
+index 6ac89d1e3..177d8582f 100644
+--- a/src/main/java/net/minecraft/server/EntityFishingHook.java
++++ b/src/main/java/net/minecraft/server/EntityFishingHook.java
+@@ -0,0 +0,0 @@ public class EntityFishingHook extends Entity {
+                     this.world.addEntity(entityitem);
+                     // CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop()
+                     if (playerFishEvent.getExpToDrop() > 0) {
+-                        this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop()));
++                        this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.owner, this)); // Paper
+                     }
+                     // CraftBukkit end
+                     Item item = itemstack.getItem();
+diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
+index 650e549b1..73643bc7c 100644
+--- a/src/main/java/net/minecraft/server/EntityLiving.java
++++ b/src/main/java/net/minecraft/server/EntityLiving.java
+@@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity {
+                 int j = EntityExperienceOrb.getOrbValue(i);
+ 
+                 i -= j;
+-                this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j));
++                EntityLiving attacker = killer != null ? killer : lastDamager; // Paper
++                this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j, this instanceof Player ? org.bukkit.entity.ExperienceOrb.SpawnReason.PLAYER_DEATH : org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, attacker, this)); // Paper
+             }
+             this.expToDrop = 0;
+             // CraftBukkit end
+diff --git a/src/main/java/net/minecraft/server/EntityThrownExpBottle.java b/src/main/java/net/minecraft/server/EntityThrownExpBottle.java
+index 0255986fd..e14f614f5 100644
+--- a/src/main/java/net/minecraft/server/EntityThrownExpBottle.java
++++ b/src/main/java/net/minecraft/server/EntityThrownExpBottle.java
+@@ -0,0 +0,0 @@ public class EntityThrownExpBottle extends EntityProjectile {
+                 int j = EntityExperienceOrb.getOrbValue(i);
+ 
+                 i -= j;
+-                this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j));
++                this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j, org.bukkit.entity.ExperienceOrb.SpawnReason.EXP_BOTTLE, getShooter(), this)); // Paper
+             }
+ 
+             this.die();
+diff --git a/src/main/java/net/minecraft/server/EntityVillager.java b/src/main/java/net/minecraft/server/EntityVillager.java
+index 8ba150a45..c1f072c42 100644
+--- a/src/main/java/net/minecraft/server/EntityVillager.java
++++ b/src/main/java/net/minecraft/server/EntityVillager.java
+@@ -0,0 +0,0 @@ public class EntityVillager extends EntityAgeable implements NPC, IMerchant {
+         }
+ 
+         if (merchantrecipe.j()) {
+-            this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY + 0.5D, this.locZ, i));
++            this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY + 0.5D, this.locZ, i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, tradingPlayer, this)); // Paper
+         }
+ 
+         if (this.tradingPlayer instanceof EntityPlayer) {
+diff --git a/src/main/java/net/minecraft/server/PathfinderGoalBreed.java b/src/main/java/net/minecraft/server/PathfinderGoalBreed.java
+index 059671b54..444792ea0 100644
+--- a/src/main/java/net/minecraft/server/PathfinderGoalBreed.java
++++ b/src/main/java/net/minecraft/server/PathfinderGoalBreed.java
+@@ -0,0 +0,0 @@ public class PathfinderGoalBreed extends PathfinderGoal {
+             if (this.a.getGameRules().getBoolean("doMobLoot")) {
+                 // CraftBukkit start - use event experience
+                 if (experience > 0) {
+-                    this.a.addEntity(new EntityExperienceOrb(this.a, this.animal.locX, this.animal.locY, this.animal.locZ, experience));
++                    this.a.addEntity(new EntityExperienceOrb(this.a, this.animal.locX, this.animal.locY, this.animal.locZ, experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, entityplayer, entityageable)); // Paper
+                 }
+                 // CraftBukkit end
+             }
+diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java
+index a1689c065..a49b5c81a 100644
+--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java
++++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java
+@@ -0,0 +0,0 @@ public class PlayerInteractManager {
+ 
+                 // CraftBukkit start - Drop event experience
+                 if (flag && event != null) {
+-                    iblockdata.getBlock().dropExperience(this.world, blockposition, event.getExpToDrop());
++                    iblockdata.getBlock().dropExperience(this.world, blockposition, event.getExpToDrop(), this.player); // Paper
+                 }
+                 // CraftBukkit end
+ 
+diff --git a/src/main/java/net/minecraft/server/SlotFurnaceResult.java b/src/main/java/net/minecraft/server/SlotFurnaceResult.java
+index 1dcf967a1..ed394f7a5 100644
+--- a/src/main/java/net/minecraft/server/SlotFurnaceResult.java
++++ b/src/main/java/net/minecraft/server/SlotFurnaceResult.java
+@@ -0,0 +0,0 @@ import org.bukkit.event.inventory.FurnaceExtractEvent;
+ 
+ public class SlotFurnaceResult extends Slot {
+ 
+-    private final EntityHuman a;
++    private final EntityHuman a;public EntityHuman getPlayer() { return a; } // Paper OBFHELPER
+     private int b;
+ 
+     public SlotFurnaceResult(EntityHuman entityhuman, IInventory iinventory, int i, int j, int k) {
+@@ -0,0 +0,0 @@ public class SlotFurnaceResult extends Slot {
+             while (i > 0) {
+                 j = EntityExperienceOrb.getOrbValue(i);
+                 i -= j;
+-                this.a.world.addEntity(new EntityExperienceOrb(this.a.world, this.a.locX, this.a.locY + 0.5D, this.a.locZ + 0.5D, j));
++                this.a.world.addEntity(new EntityExperienceOrb(this.a.world, this.a.locX, this.a.locY + 0.5D, this.a.locZ + 0.5D, j, org.bukkit.entity.ExperienceOrb.SpawnReason.FURNACE, getPlayer())); // Paper
+             }
+         }
+ 
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+index 4ffe0d208..568a50ec4 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+@@ -0,0 +0,0 @@ public class CraftWorld implements World {
+         } else if (TNTPrimed.class.isAssignableFrom(clazz)) {
+             entity = new EntityTNTPrimed(world, x, y, z, null);
+         } else if (ExperienceOrb.class.isAssignableFrom(clazz)) {
+-            entity = new EntityExperienceOrb(world, x, y, z, 0);
++            entity = new EntityExperienceOrb(world, x, y, z, 0, org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM, null, null); // Paper
+         } else if (Weather.class.isAssignableFrom(clazz)) {
+             // not sure what this can do
+             if (LightningStrike.class.isAssignableFrom(clazz)) {
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
+index 3a09cab3d..3302af0e4 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java
+@@ -0,0 +0,0 @@ public class CraftExperienceOrb extends CraftEntity implements ExperienceOrb {
+         getHandle().value = value;
+     }
+ 
++    // Paper start
++    public java.util.UUID getTriggerEntityId() {
++        return getHandle().triggerEntityId;
++    }
++    public java.util.UUID getSourceEntityId() {
++        return getHandle().sourceEntityId;
++    }
++    public SpawnReason getSpawnReason() {
++        return getHandle().spawnReason;
++    }
++    // Paper end
++
+     @Override
+     public EntityExperienceOrb getHandle() {
+         return (EntityExperienceOrb) entity;
+--
\ No newline at end of file