Bukkit/CraftBukkit — pull request #1386 by mcmonkey4eva (2014-06-03 05:26:54+00:00)
Title: [B+C] Add and implement an EntityDespawnEvent. adds BUKKIT-5645

## The Issue:

There was not any event fired for when an entity despawns for reasons other than death, EG when a non-persistent entity is removed because no player is nearby (RemoveWhenFarAway).

## Justification for this PR:

An event to listen for entity despawns is incredibly useful for plugins.
One example of when this might be used is a plugin that needs to store information on the entity (and save it to file), but does not wish this data to persist if the entity no longer exists.
With this PR, a plugin could record the entity's UUID and map some information to it, then simply delete it if the EntityDespawnEvent is called for an entity with a matching UUID.

## PR Breakdown:

#### Bukkit-side:
Simply adds an EntityDespawnEvent with base functionality for plugins to listen to.

#### CraftBukkit-side:
Fire the EntityDespawnEvent added in Bukkit whenever an entity is removed from the world. Additionally, as there was a fair bit of code exactly duplicating the functionality in the `removeEntity()` function, change all other entity remove calls to fire the function rather than duplicate all related code.

## Testing Results and Materials:

Any default plugin or pre-existing plugin can listen to EntityDespawnEvent.
For my initial testing, I had:
```
@EventHandler
public void onEntityDespawn(EntityDespawnEvent event) {
getLogger().info("Despawn: " + event.getEntity().getType().name() + " is " + event.getEntity().getUniqueId());
}
```
This showed a fair number of regular despawns, that appeared to roughly match up to EntitySpawnEvent's (eyeballed only - obviously many entities were spawned and didn't despawn, or exist in the world prior to testing and despawned during the time)

I also integrated it into an entity-data storage system described in the justification, however, that is part of a larger system that isn't so easily copy-pasted. The stored data was confirmed as removed after the entity despawned (Entity despawn was induced by leaving a zombie in a box with a roof, then running far away, then running back)

... Also, I noticed the removeEntity() function was called fairly often with invalid / unspawned entities... I've placed the event firing inside an if block that ensures it only fires the event if the entity was spawned.

## Relevant PR(s):

[TO EDIT: Add link to other PR]

## JIRA Ticket:

BUKKIT-5645 - https://bukkit.atlassian.net/browse/BUKKIT-5645