ProtocolLib

3M Downloads

Players cannot join the server when running on Java 15

Justsnoopy30 opened this issue ยท 7 comments

commented

Describe the bug
Players cannot join the server when I use Java 15. It was working properly when I used Java 14. Instead, an error appears in console and the player is disconnected with an internal server error has occured.
Error: https://paste.gg/p/anonymous/ee703bf5ac15457c99afe7141e474e1b

To Reproduce
Steps to reproduce the behavior:

  1. Use Java 15 on a server with protocollib.
  2. Try to join the server and be disconnected due to an internal server error.

Expected behavior
Be able to join.

Screenshots
If applicable, add screenshots to help explain your problem.

Version Info
Provide your ProtocolLib install info with /protocol dump through pastebin.
https://paste.gg/p/anonymous/2b7711fc2ed34d30b7823003f1675b7d

Additional context
Add any other context about the problem here.

commented

I ran some more tests and the issue occurs on 1.13.2 and all versions after it (using Spigot jars modified with Recaf to skip the Java version check).
The issue does not occur when only ProtocolLib (PL) is installed; It only happens when I have LibsDisguises (LD) installed as well (which is only one of two plugins I downloaded to test PL, the other being NoPlugins, which had no effect). Because LD didn't work on 1.13.0/1, I was unable to test if this issue also happens on those versions.

commented

After taking a closer look, I think the issue is that starting in Minecraft 1.13.0, the NetworkManager doesn't send an explicitly defined Runnable to the eventLoop's execute method, but it uses a lambda instead. This difference causes the resulting object's class to be a hidden one in Java 15, which is why ProtocolLib's DefaultFieldAccessor's set method cannot access the field inside it (which holds the packet) anymore.
I haven't really read up on the specifics hidden classes yet, but I did read somewhere that you should still be able to access them using reflection, but I guess not like this or this way?

commented

After taking a closer look, I think the issue is that starting in Minecraft 1.13.0, the NetworkManager doesn't send an explicitly defined Runnable to the eventLoop's execute method, but it uses a lambda instead. This difference causes the resulting object's class to be a hidden one in Java 15, which is why ProtocolLib's DefaultFieldAccessor's set method cannot access the field inside it (which holds the packet) anymore.

This assessment seems to be correct. Removing the lambda there makes it work again.

As for accessing hidden classes: Isn't the goal of that feature to explicitly prevent that from happening?

commented

I have the same issue, for now I've moved back to using Java 14 but it would be really useful if this bug was fixed as it is the only thing preventing me from being able to use Java 15 on my server.

commented

I've implemented a proof of concept fix for ProtocolLib here.

As also explained in the commit message, the reason the current method no longer works is that the lambda is a hidden class and final fields in hidden classes are not modifiable (not even using reflection).

The proof of concept fix creates a new object of the runnable/callable with the updated packet and uses that instead (thereby bypassing the need to modifying anything). It's still a proof of concept instead of a PR because it's not implemented particularly efficiently (doing almost all the reflection stuff every time it runs), but I'd like to know what @dmulloy2 thinks of this approach and how best to cache the reflection.

Regarding testing, it passed all unit tests and doing manual tests on J14 and J15 on Minecraft 1.16.3 and 1.12.2 using LibsDisguises and a simple test plugin everything worked as intended.

I think this is a better approach than patching all servers between 1.13 and 1.16.4, as I don't see that happening any time soon.

commented

Any updates on this issue?

commented

https://github.com/ZentriaMC/ProtocolLib/commit/c9903b55f81d75f63ab318b78de504f3c0c19c8e makes the PoC more future proof by determining fields to copy on runtime, instead of hardcoding the number of fields