games647 / FastLogin

Checks if a minecraft player has a valid paid account. If so, they can skip offline authentication automatically. (premium auto login)
https://www.spigotmc.org/resources/fastlogin.14153
MIT License
494 stars 121 forks source link

Mixin implementation #869

Open 66Leo66 opened 2 years ago

66Leo66 commented 2 years ago

Can we implement the function like how the fabric mod EasyAuth does?

That is, keep server in online-mode, and do some mixin injection to allow offline players to join.

games647 commented 2 years ago

Really good idea, because it would also improve the compatibility with other projects as it hooks directly in the Spigot code. So we could also fire normal join events.

However I don't know how good we could integrate a Mixin solution to the supported platforms: Spigot, Bungee and Velocity. Fabric includes the Mixin support from the start (and also Sponge has support for it). These do integrate a custom loader, but our supported platforms do not. Years ago, we also thought about depending only on ProtocolSupport. They re-implement the vanilla code and replace the login classes during runtime. There they could also fire vanilla events, but server software specific optimizations in forks would be replaced too.

66Leo66 commented 2 years ago

Can we use ProtocolLib to do with protocol or packet? This (allow non-premium users with online-mode=true) seems only some login-protocol stuff and ProtocolLib should be able to deal with it (I haven't checked their doc yet). In this way we don't need to modify the game on our own.

games647 commented 2 years ago

This (allow non-premium users with online-mode=true) seems only some login-protocol stuff and ProtocolLib should be able to deal with it

Setting the server in onlinemode means that all client requests will be answered with authentication requests. Reverting back to offline mode is difficult, because we would need to cancel these outgoing requests and also answer them correctly to the server with fake data. Especially the last part is difficult, because the server would verify the data. Therefore requires to disabling/intercepting the Mojang check and furthermore disable the network encryption.

Although difficult, it could be possible. This then fires the normal vanilla events, but there also the topic about hybrid onlinemode servers. What led to requiring offlinemode is that there are plugins and server features that check for the onlinemode. If enabled, they perform a Mojang API lookup. This is not valid for the offline players, playing on it. Offline mode seems to be better choice in our opinion.

Nevertheless, I'm interested in what you think.

games647 commented 2 years ago

So what might work is an simulating Bukkit.getOnlineMode() -> true only for the LoginListener of Spigot. Although this seems to very difficult and potentially breaking.

66Leo66 commented 2 years ago

Could we modify PlayerLoginEvent and re-implement the Premium auth which is just some sort of HTTPs request, and cancel the original event to prevent offline players from being kicked?

games647 commented 2 years ago

Spigot fires no event before sending the Encryption_Begin packet. So there nothing to cancel. AsyncPlayerPreLoginEvent runs for example after it has been sent. Re-implement Mojang endpoint is possible although difficult, because you would need to match request to the fake endpoint. Furthermore as explained, Spigot then enables encryption for onlinemode connections, which also needs to be disabled.

Then we also have the mixed onlinemode issue explained above.

66Leo66 commented 2 years ago

Spigot fires no event before sending the Encryption_Begin packet. So there nothing to cancel. AsyncPlayerPreLoginEvent runs for example after it has been sent. Re-implement Mojang endpoint is possible although difficult, because you would need to match request to the fake endpoint. Furthermore as explained, Spigot then enables encryption for onlinemode connections, which also needs to be disabled.

Then we also have the mixed onlinemode issue explained above.

ProtocolLib seemed to support handling login start packets. And I didn't see EasyAuth do with encryptions explicitly so maybe we don't need to as well. Their technical details are at here.

I couldn't understand why we need to make a "fake endpoint". My meaning was to talk to real auth server to confirm premium users and have them logged in through the normal procedure while skip online auth for offline players and log them in directly (by canceling some packets and sending PACKET_SUCCESS or something). All these should be able to be done after we got LOGIN_START packet.

If enabled, they perform a Mojang API lookup.

As long as we can handle player profile stuff correctly at Bukkit side, nothing should go wrong, for plugins seldom communicate with Mojang servers directly (I guess).

games647 commented 2 years ago

BTW to prevent any conflicts I always meant cancelling server-side (server -> client) packets to this point.

ProtocolLib seemed to support handling login start packets.

Yes they do, but we need to handle the logic behind it. This means cancelling the packets and somehow answer it with a valid reply. It's similar to the current behavior, but onlinemode=true in the server.properties makes it more complicated. In offline-mode authentication, some login steps are just skipped, which makes a re-implementation of those easier.

The big issue is how can we bypass the verification in the server implementation if it expects onlinemode without support of Mixins? Once that's solved, we also need a way to revert the changes made for onlinemode like the fact that in onlinemode all following packets are encrypted (not the case in offlinemode).

The current behavior is:

All these steps involve our own implementation or existing separate methods. So we only need to call methods (enableEncryption(...)) and change fields (spoofUUID). This all possible from an external view.

And I didn't see EasyAuth do with encryptions explicitly so maybe we don't need to as well.

Yes, because they could use Mixins. Therefore we could access internal data and intercept the incoming method call. How can we do that without Mixin?

Onlinemode: https://github.com/NikitaCartes/EasyAuth/blob/a44984a708e4779ab835bff64bb99e557511bb29/src/main/java/xyz/nikitacartes/easyauth/mixin/ServerLoginNetworkHandlerMixin.java#L71

Offline: https://github.com/NikitaCartes/EasyAuth/blob/a44984a708e4779ab835bff64bb99e557511bb29/src/main/java/xyz/nikitacartes/easyauth/mixin/ServerLoginNetworkHandlerMixin.java#L94

I couldn't understand why we need to make a "fake endpoint".

Yes, but how would skip it without Mixins? Cancelling the packets won't work, because the server still expects a valid answer to its request.

As long as we can handle player profile stuff correctly at Bukkit side, nothing should go wrong

How do you expect we do that? Intercepting the API requests, because the server has implementations like these: https://github.com/PaperMC/Paper/blob/7044a9c538edb16239a0579722968d395842cdbe/patches/server/0102-Add-setting-for-proxy-online-mode-status.patch#L52

We would need to change/replace them too.

66Leo66 commented 2 years ago

I'm sorry that my knowledge of plugin development is very shallow, so it's difficult to discuss more in-depth about specific implementations and compatibility. Thank you for taking the time to discuss this with me, and I look forward to this feature being implemented or partially supported in the future.

SzczurekYT commented 1 year ago

Maybe this could be used: https://github.com/vectrix-space/ignite It's a wrapper around the server jar, to let you use mixins. The plugin could have two versions: the normal one, that we have today, and another one using this lib for these user that are willing to setup their server to use mixins.