--- ../src-base/minecraft/net/minecraft/entity/item/EntityItem.java
+++ ../src-work/minecraft/net/minecraft/entity/item/EntityItem.java
@@ -22,6 +22,11 @@
 import cpw.mods.fml.common.FMLCommonHandler;
 import cpw.mods.fml.common.eventhandler.Event.Result;
 
+// CraftBukkit start
+import org.bukkit.event.player.PlayerPickupItemEvent;
+import net.minecraft.server.MinecraftServer;
+// CraftBukkit end
+
 public class EntityItem extends Entity
 {
     private static final Logger logger = LogManager.getLogger();
@@ -31,6 +36,7 @@
     private String field_145801_f;
     private String field_145802_g;
     public float hoverStart;
+    private int lastTick = MinecraftServer.currentTick; // CraftBukkit
     private static final String __OBFID = "CL_00001669";
 
     /**
@@ -55,6 +61,15 @@
     public EntityItem(World p_i1710_1_, double p_i1710_2_, double p_i1710_4_, double p_i1710_6_, ItemStack p_i1710_8_)
     {
         this(p_i1710_1_, p_i1710_2_, p_i1710_4_, p_i1710_6_);        
+
+        // CraftBukkit start - Can't set null items in the datawatcher
+        if (p_i1710_8_ == null || p_i1710_8_.getItem() == null)
+        {
+            return;
+        }
+
+        // CraftBukkit end
+
         this.setEntityItemStack(p_i1710_8_);
         this.lifespan = (p_i1710_8_.getItem() == null ? 6000 : p_i1710_8_.getItem().getEntityLifespan(p_i1710_8_, p_i1710_1_));
     }
@@ -81,6 +96,10 @@
     public void onUpdate()
     {
         ItemStack stack = this.getDataWatcher().getWatchableObjectItemStack(10);
+        if (stack == null || stack.stackSize == 0) {
+            setDead();
+            return;
+        }
         if (stack != null && stack.getItem() != null)
         {
             if (stack.getItem().onEntityItemUpdate(this))
@@ -89,93 +108,103 @@
             }
         }
 
-        if (this.getEntityItem() == null)
-        {
-            this.setDead();
+        super.onUpdate();
+        // CraftBukkit start - Use wall time for pickup and despawn timers
+        int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
+        this.delayBeforeCanPickup -= elapsedTicks;
+        if (this.delayBeforeCanPickup < 0) this.delayBeforeCanPickup = 0; // Cauldron
+        this.age += elapsedTicks;
+        this.lastTick = MinecraftServer.currentTick;
+        // CraftBukkit end
+
+        boolean forceUpdate = this.ticksExisted > 0 && this.ticksExisted % 25 == 0; // Cauldron - optimize item tick updates
+        this.prevPosX = this.posX;
+        this.prevPosY = this.posY;
+        this.prevPosZ = this.posZ;
+        this.motionY -= 0.03999999910593033D;
+        // Cauldron start - if forced
+        if (forceUpdate || noClip) {
+            this.noClip = this.func_145771_j(this.posX, (this.boundingBox.minY + this.boundingBox.maxY) / 2.0D, this.posZ);
         }
-        else
-        {
-            super.onUpdate();
+        // Cauldron end
+        this.moveEntity(this.motionX, this.motionY, this.motionZ);
+        boolean flag = (int)this.prevPosX != (int)this.posX || (int)this.prevPosY != (int)this.posY || (int)this.prevPosZ != (int)this.posZ;
 
-            if (this.delayBeforeCanPickup > 0)
+        if ((flag && this.ticksExisted % 5 == 0) || forceUpdate) // Cauldron - if forced
+        {
+            if (this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)).getMaterial() == Material.lava)
             {
-                --this.delayBeforeCanPickup;
+                this.motionY = 0.20000000298023224D;
+                this.motionX = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F);
+                this.motionZ = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F);
+                this.playSound("random.fizz", 0.4F, 2.0F + this.rand.nextFloat() * 0.4F);
             }
 
-            this.prevPosX = this.posX;
-            this.prevPosY = this.posY;
-            this.prevPosZ = this.posZ;
-            this.motionY -= 0.03999999910593033D;
-            this.noClip = this.func_145771_j(this.posX, (this.boundingBox.minY + this.boundingBox.maxY) / 2.0D, this.posZ);
-            this.moveEntity(this.motionX, this.motionY, this.motionZ);
-            boolean flag = (int)this.prevPosX != (int)this.posX || (int)this.prevPosY != (int)this.posY || (int)this.prevPosZ != (int)this.posZ;
-
-            if (flag || this.ticksExisted % 25 == 0)
+            if (forceUpdate && !this.worldObj.isRemote) // Cauldron - if forced
             {
-                if (this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)).getMaterial() == Material.lava)
-                {
-                    this.motionY = 0.20000000298023224D;
-                    this.motionX = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F);
-                    this.motionZ = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F);
-                    this.playSound("random.fizz", 0.4F, 2.0F + this.rand.nextFloat() * 0.4F);
-                }
-
-                if (!this.worldObj.isRemote)
-                {
-                    this.searchForOtherItemsNearby();
-                }
+                this.searchForOtherItemsNearby();
             }
+        }
 
-            float f = 0.98F;
+        float f = 0.98F;
 
-            if (this.onGround)
-            {
-                f = this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)).slipperiness * 0.98F;
-            }
+        if (this.onGround)
+        {
+            f = this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)).slipperiness * 0.98F;
+        }
 
-            this.motionX *= (double)f;
-            this.motionY *= 0.9800000190734863D;
-            this.motionZ *= (double)f;
+        this.motionX *= (double)f;
+        this.motionY *= 0.9800000190734863D;
+        this.motionZ *= (double)f;
 
-            if (this.onGround)
-            {
-                this.motionY *= -0.5D;
-            }
+        if (this.onGround)
+        {
+            this.motionY *= -0.5D;
+        }
 
-            ++this.age;
+        // ++this.age; // CraftBukkit - Moved up (base age on wall time)
 
-            ItemStack item = getDataWatcher().getWatchableObjectItemStack(10);
-    
-            if (!this.worldObj.isRemote && this.age >= lifespan)
+        ItemStack item = getDataWatcher().getWatchableObjectItemStack(10);
+        
+        if (!this.worldObj.isRemote && this.age >= lifespan - 1) // Cauldron adjust for age being off by one when it is first dropped
+        {
+            // CraftBukkit start
+            if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled())
             {
-                if (item != null)
-                {   
-                    ItemExpireEvent event = new ItemExpireEvent(this, (item.getItem() == null ? 6000 : item.getItem().getEntityLifespan(item, worldObj)));
-                    if (MinecraftForge.EVENT_BUS.post(event))
-                    {
-                        lifespan += event.extraLife;
-                    }
-                    else
-                    {
-                        this.setDead();
-                    }
+                this.age = 0;
+                return;
+            }
+            // CraftBukkit end
+            if (item != null)
+            {   
+                ItemExpireEvent event = new ItemExpireEvent(this, (item.getItem() == null ? this.worldObj.getSpigotConfig().itemDespawnRate : item.getItem().getEntityLifespan(item, worldObj))); // Spigot // Cauldron
+                if (MinecraftForge.EVENT_BUS.post(event))
+                {
+                    lifespan += event.extraLife;
                 }
                 else
                 {
                     this.setDead();
                 }
             }
-    
-            if (item != null && item.stackSize <= 0)
+            else
             {
                 this.setDead();
             }
         }
+
+        if (item != null && item.stackSize <= 0)
+        {
+            this.setDead();
+        }
     }
 
     private void searchForOtherItemsNearby()
     {
-        Iterator iterator = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.boundingBox.expand(0.5D, 0.0D, 0.5D)).iterator();
+        // Spigot start
+        double radius = worldObj.getSpigotConfig().itemMerge; // Cauldron
+        Iterator iterator = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.boundingBox.expand(radius, radius, radius)).iterator();
+        // Spigot end
 
         while (iterator.hasNext())
         {
@@ -225,11 +254,13 @@
             }
             else
             {
-                itemstack1.stackSize += itemstack.stackSize;
-                p_70289_1_.delayBeforeCanPickup = Math.max(p_70289_1_.delayBeforeCanPickup, this.delayBeforeCanPickup);
-                p_70289_1_.age = Math.min(p_70289_1_.age, this.age);
-                p_70289_1_.setEntityItemStack(itemstack1);
-                this.setDead();
+                // Spigot start
+                itemstack.stackSize += itemstack1.stackSize;
+                this.delayBeforeCanPickup = Math.max(p_70289_1_.delayBeforeCanPickup, this.delayBeforeCanPickup);
+                this.age = Math.min(p_70289_1_.age, this.age);
+                this.setEntityItemStack(itemstack);
+                p_70289_1_.setDead();
+                // Spigot end
                 return true;
             }
         }
@@ -316,8 +347,27 @@
         }
 
         NBTTagCompound nbttagcompound1 = p_70037_1_.getCompoundTag("Item");
-        this.setEntityItemStack(ItemStack.loadItemStackFromNBT(nbttagcompound1));
 
+        // CraftBukkit start
+        if (nbttagcompound1 != null)
+        {
+            ItemStack itemstack = ItemStack.loadItemStackFromNBT(nbttagcompound1);
+
+            if (itemstack != null)
+            {
+                this.setEntityItemStack(itemstack);
+            }
+            else
+            {
+                this.setDead();
+            }
+        }
+        else
+        {
+            this.setDead();
+        }
+
+        // CraftBukkit end
         ItemStack item = getDataWatcher().getWatchableObjectItemStack(10);
 
         if (item == null || item.stackSize <= 0)
@@ -350,6 +400,31 @@
             ItemStack itemstack = this.getEntityItem();
             int i = itemstack.stackSize;
 
+            // CraftBukkit start
+            int canHold = p_70100_1_.inventory.canHold(itemstack);
+            int remaining = itemstack.stackSize - canHold;
+
+            if (this.delayBeforeCanPickup <= 0 && canHold > 0)
+            {
+                itemstack.stackSize = canHold;
+                // Cauldron start - rename to cbEvent to fix naming collision
+                PlayerPickupItemEvent cbEvent = new PlayerPickupItemEvent((org.bukkit.entity.Player) p_70100_1_.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+                //cbEvent.setCancelled(!par1EntityPlayer.canPickUpLoot); TODO
+                this.worldObj.getServer().getPluginManager().callEvent(cbEvent);
+                itemstack.stackSize = canHold + remaining;
+
+                if (cbEvent.isCancelled())
+                {
+                    return;
+                }
+                // Cauldron end
+
+                // Possibly < 0; fix here so we do not have to modify code below
+                this.delayBeforeCanPickup = 0;
+            }
+
+            // CraftBukkit end
+
             if (this.delayBeforeCanPickup <= 0 && (this.field_145802_g == null || lifespan - this.age <= 200 || this.field_145802_g.equals(p_70100_1_.getCommandSenderName())) && (event.getResult() == Result.ALLOW || i <= 0 || p_70100_1_.inventory.addItemStackToInventory(itemstack)))
             {
                 if (itemstack.getItem() == Item.getItemFromBlock(Blocks.log))
@@ -423,7 +498,7 @@
     public ItemStack getEntityItem()
     {
         ItemStack itemstack = this.getDataWatcher().getWatchableObjectItemStack(10);
-        return itemstack == null ? new ItemStack(Blocks.stone) : itemstack;
+        return itemstack == null ? new ItemStack(Blocks.air, 0) : itemstack;
     }
 
     public void setEntityItemStack(ItemStack p_92058_1_)
@@ -451,4 +526,10 @@
     {
         this.field_145801_f = p_145799_1_;
     }
+    
+    @Override
+    public boolean entityAllowedToSpawn() {
+        ItemStack stack = getDataWatcher().getWatchableObjectItemStack(10);
+        return stack != null && stack.getItem() != null && stack.stackSize > 0;
+    }
 }
