Don't use the world's getPlayers-method, as it's enumerating a
collection that's not thread safe. This is important as
getEntityModifier may be called by a client packet listener (which is
async).
If the NetServerHandler injector fails and we revert to the network
field injector instead, any map chunk listener will crash the server
due to complications with Bukkit's ChunkCompressionThread.
We avoid this by disabling map chunk listening entirely.
The different injectors using CGLib was using a custom CallbackFilter
to optimize the resulting generated class. Unfortunately, the custom
filter didn't properly implement equals() and hashCode(), and so every
time a player logged, a new injector class had to be generated.
This was fixed by making the injectors share a single CallbackFilter.
In addition, I've begun adding cleanup code that will reset all static
fields once the plugin has unloaded.
This occurs whenever a listener is added or removed. A listener can
now specify whether or not it's listening for packets sent BEFORE
a player has logged in (every packet upto Packet1Login and a few more),
or AFTER. By default, listeners only receive notifcation of packets
sent and received after.
ProtocolLib will now only hook NetLoginHandler if there's a login
listener, and vice versa. Thus, the new login feature will only
tax the server if another plugin is using it. In addition, ProtocolLib
will not consume any resources when it's not serving any listeners.
This is achieved by injecting the NetLoginHandler when it's added
to the DedicatedServerConnectionThread's list of current login handlers.
PacketEvents during this phase uses "fake" Player objects that only
support a subset of methods. Consumers can expect the following methods
to be functional:
* getPlayer()
* getAddress()
* getServer()
* chat(String)
* sendMessage(String)
* sendMessage(String[])
* kickPlayer(String)
A "fake" Player object can be converted to its real counterpart by
calling getPlayer().