The snapshot version contains a release date, so we'll simply compare
that against a known release date of a Minecraft version. It it's
later, we know it is at least a minor version above, and vice versa.
ProtocolLib can now keep track of the amount of time spent by each
listener (for each packet), generated as a report file. This is
done in the new "protocol timings" command.
As it happens, there are two very similar methods in IntHashMap:
Object get(int key);
Object remove(int key);
I called the latter by mistake. Now, I distinguish between the two
by performing a removal test.
This causes wierd artifacts such as double placing of half-slabs and so
on, as Minecraft will process the packets again (except chat packets).
We correct this by uncancelling the asynchronous packet, and then
cancelling it again.
This contains a fully-fledged API for reading and modifying Attribute-
Snapshot and AttributeModifier. Keep in mind that these objects are
immutable, so modification must be made through object builders.
The packets are also shared, so packet cloning might be necessary if
attributes should differ per player.
Most people use it to retrieve the entity referenced in a certain
packet, which will always be in the world of the receiving player. This
method makes the same assumption.
We can't use a traditional builder pattern though, as it's commonly
used for constructing inner anonymous classes. But we can do the next
best thing and create a builder for the parameters themselves.
Here's what the new syntax looks like:
new PacketAdapter(params(this, 1, 2, 3).clientSide()) {
@Override
public void onPacketReceiving(PacketEvent event) {
// Your code here
}
};
Where "this" referes to your plugin.
We can't override the getID method in Packet, as it is marked as final.
Instead, we'll just generate a seperate class for each packet ID that
needs to be intercepted.
We can't inherit from the packet ID of any particular class, as it
may get recognized and modified by the sendPacket() method. This could
be a problem if this recogition is necessary, but we'll come back to
this later.
This is done by constructing a proxy around the class after every
event handler has been invoked, intercepting the write method. Each
PacketOutputHandler registered by the packet event listeners is
invoked in turn, modifying a byte array of the data that will be
written to the network stream.
The byte array is initially filled with the serialized version of the
packet in the packet event.
A lot of methods changed from accepting DataInputStream to DataInput,
which messed with the part that dynamically finds the "readPacket"
method. Changed to accepting any method whose signature contains a
parameter derived from DataInput.
In NetworkFieldInjector, we use a "inverted" packet class to undo the
data received counter when a packet has been cancelled.
Previously, we would generate a proxy class that inherits from the
class of the packet (Packet3Chat, etc.) along with its size() method
(which is called and added to the counter, cancelling the other packet),
but this doesn't work for packets that are dynamically sized such as
Packet255KickDisconnect. Instead, we now pass the actual instance to
the proxy class through a weak hash map.
All warnings and error messages will now be identified using fields in
the sender classes, to avoid depending on the format of the error or
warning messages directly. This decoupling will make it possible to
filter out certain irrelevant messages.
Earlier versions of ProtocolLib would in fact load at startup, but due
to an experimental change (ffd920e5b2) -
where ProtocolLib injected code into DedicatedServerConnectionThread in
order to intercept pending connections - the load order had to be
set to world.
This injection was later removed, but the load order was never
reinstated. This causes problems with plugins that load on startup,
but also depend on ProtocolLib as load order trumpts dependency.
This change reintroduces startup load order.
The original intent of catching throwable is to "sandbox" arbitrary
plugin logic and prevent it from ever accidentally killing threads on
the server. A LinkageError due to a missing or old dependency shouldn't
bring down the server, so we secure it by catching all exceptions around
plugin event handlers.
Trouble is, this also catches exceptions such as OutOfMemoryError or
ThreadDead, which assuredly should NOT be caught. The latter case has
even occured in the wild as seen by ticket 45 of TagAPI on BukkitDev.
Minecraft may terminate the reader and writer thread by calling stop(),
and this could occur within the event handler in a plugin. So we should
let ThreadDead go and propagate it to the appropriate handler in
Minecraft.
The original code attempted to parse the JavaScript as it went along,
counting open and close brackets. Unfortunately, this doesn't
take comments and string literals into consideration, so it would very
likely have failed with more complicated filters.
Instead, we'll let the JavaScript compiler handle all the complexity
and simply see if the code compiles. If it doesn't, but the error
occured in the last line, we assume it can be recovered by adding a
new line.
The filter command allows users with sufficient permission (or OPs) to
execute arbitrary JavaScript (no sandboxing). This is fine for a
debug and testing, but could potentially be exploited in a
production environment.
Instead, we disable this command by default and force users to enable it
specifically in the configuration file (not through commands). If
someone has access to the config.yml file, they probably also have
access to the plugins/ folder and thus the ability to install plugins
with arbitrary code execution as well.
Should ensure that packet listeners recieve the most up-to-date player
instance, regardless of whether or not the main thread is blocked in the
player listener. No more temporary players.
This is because multiple plugins depend on us, and are not properly
notified after ProtocolLib has been reloaded.
The only possible solution is to reload every dependent plugin after
ProtocolLib has been reloaded, but unfortunately, I ran into
LinkageErrors when I tried it. So it's probably not possible with the
current architecture to support reloaders.
Instead, we'll simply print a BIG BOLD warning telling any users of
these plugins that ProtocolLib cannot be reloaded except through the
built in "/reload" command.
Previously, we have used a BlockingHashMap to simply lock the packet
read thread until we have had a chance to intercept the
NetLoginHandler/PendingConnection and store InputStream ->
PlayerInjector -> TemporaryPlayer.
Problem is, this could potentially cause problems if, for some reason, a
packet is intercepted after the player has logged out and the player
injector has been removed from the lookup map. In that case, the read
thread would wait until it reaches the default timeout of 2 seconds.
Locking threads is fairly inefficient in general, and waiting for the
server connection thread to update the NetLoginHandler list could take a
while.
Instead, ProtocolLib will now intercept any Socket accepted in the
server's main ServerSocket, and record any calls to getInputStream().
That way, we can get a InputStream -> Socket mapping before the server
thread ever creates the read and write threads in NetLoginHandler ->
NetworkManager.
Unfortunately, it's not trivial to swap out the ServerSocket in the
DedicatedServerConnectionThread - we actually have to trigger the
accept() thread and move through a cycle of the loop before our custom
ServerSocket is used. To do this, we will actually connect to the server
and read its MOTD manually, hopefully getting to it before any other
players.
This creates a slight overhead of a couple of threads per server start,
but it's probably much better than locking the read thread. More testing
is needed though before this can be confirmed.
Perhaps NbtBase shouldn't have implemented getValue() after all - it
would have been better to have a shared base interface with getName()
and getType(), and only let the primitive elements implement getValue().
Too late to change it now though.
Added a missing remove method in NbtCompound. In addition, the
getValue() method in NbtCompount has been depreciated. It is far better
to use the put and get methods in NbtCompound instead.
This required extensive reworking of the inner packet injection system
in ProtocolLib.
I've also fixed a minior bug in the fuzzy member contract class.