unmojang / drasl

Yggdrasil-compatible API server for Minecraft
GNU General Public License v3.0
115 stars 18 forks source link

Classic 0.0.17a up to 1.6.4 support #52

Open catfromplan9 opened 8 months ago

catfromplan9 commented 8 months ago

I have only checked for beta 1.7.3, since most beta players prefer this version, but to implement support for it should be feasible. Authlib did not exist at the time, instead URLs are hardcoded to be used, but they're all http so using jvm proxy argument you can reroute these API endpoints to any drasl instance.

For skins, http://s3.amazonaws.com/MinecraftSkins/PlayerName.png is checked, and the skin returned should be a 64x32 image (old skin format) with any layers merged into 1 layer.

For authentication, http://www.minecraft.net/game/joinserver.jsp is checked by the client and http://www.minecraft.net/game/checkserver.jsp is checked by the server. I do not know how these APIs work.

The skins endpoint is offline, but the joinserver.jsp and checkserver.jsp endpoints are online however they've been moved to https://session.minecraft.net (https://session.minecraft.net/game/joinserver.jsp and https://session.minecraft.net/game/checkserver.jsp respectively)

Here is the source code from beta 1.7.3 referencing these URLs that I hope can help with implementing this.

Authentication: https://github.com/jacobo-mc/mc_b1.7.3_release/blob/main/1.7.3-LTS/src/minecraft/net/minecraft/src/NetClientHandler.java - Client https://github.com/jacobo-mc/mc_b1.7.3_release/blob/main/1.7.3-LTS/src/minecraft_server/net/minecraft/src/ThreadLoginVerifier.java - Server

Skins: https://github.com/jacobo-mc/mc_b1.7.3_release/blob/main/1.7.3-LTS/src/minecraft/net/minecraft/src/EntityPlayerSP.java - Self https://github.com/jacobo-mc/mc_b1.7.3_release/blob/main/1.7.3-LTS/src/minecraft/net/minecraft/src/EntityOtherPlayerMP.java - Other players

catfromplan9 commented 8 months ago

Add alternate endpoints for http://www.minecraft.net/game/joinserver.jsp and http://www.minecraft.net/game/checkserver.jsp at http://session.minecraft.net/game/joinserver.jsp and http://session.minecraft.net/game/checkserver.jsp since modded clients and servers tend to use these

catfromplan9 commented 8 months ago

You can see a craftbukkit fork's authentication API implementation here https://github.com/RhysB/Project-Poseidon/blob/master/src/main/java/com/legacyminecraft/poseidon/util/SessionAPI.java

catfromplan9 commented 8 months ago

For checkserver.jsp, it works as follows

http://(www|session).minecraft.net/game/checkserver.jsp?user=USERNAME&serverId=IDENTIFIER

USERNAME is the player's username, for example Steve

IDENTIFIER is a bit more complex. It is a random hexadecimal value given to the client by the server, which the client will set by calling the joinserver.jsp endpoint

The content-type is application/json for these endpoints

For a request to checkserver.jsp, it will always respond with HTTP 405 and content of "NO" if any parameters are missing or if that user account has not made a request to the joinserver.jsp endpoint with the given parameter. If the user account has sent to the joinserver.jsp endpoint that parameter, then it replies with a HTTP 200 and content of "YES"

This should be all you need to get checkserver.jsp implemented

catfromplan9 commented 8 months ago

Alright, for joinserver.jsp, heres how it works

http://(www|session).minecraft.net/game/joinserver.jsp?user=USERNAME&sessionId=SESSION&serverId=IDENTIFIER

USERNAME and IDENTIFIER works as before, except that here IDENTIFIER is being set so that when the endpoint is queried by the server with checkserver.jsp it will return "YES" instead of "NO". SESSION is, I believe, the session token.

Just like before, content-type is application/json on all responses

I am not sure what is returned when it is successful, but if the username and session token do not correspond to the same account or if the session token is invalid/not specified, the server will always respond HTTP 405 with content "Bad login"

catfromplan9 commented 7 months ago

To fix skins on classic up until beta 1.2_02, add alternative endpoint of skins for http://www.minecraft.net/skin/playername.png and http://www.minecraft.net/skins/playername.png

catfromplan9 commented 7 months ago

As for Cloaks, http://www.minecraft.net/cloak/get.jsp?user=username is used in beta 1.0, and in beta 1.3 until release 1.2, http://s3.amazonaws.com/MinecraftCloaks/username.png is used

Add alternate endpoints for http://www.minecraft.net/game/joinserver.jsp and http://www.minecraft.net/game/checkserver.jsp at http://session.minecraft.net/game/joinserver.jsp and http://session.minecraft.net/game/checkserver.jsp since modded clients and servers tend to use these

^ beta 1.8 up until release 1.6.1 uses these endpoints too

In release 1.3, skins and cloaks endpoint was moved to http://skins.minecraft.net/MinecraftCloaks/username.png and http://skins.minecraft.net/MinecraftSkins/username.png respectively. Again, more alternate endpoints are necessary

In release 1.5, snooper was added URL("http://snoop.minecraft.net/" + paramString + "?version=" + '\001');. This isnt relevant unless you decide to add telemetry to drasl, which I don't see a point with.

Finally, in release 1.6 the authentication is changed to modern logic and in 1.7 old code was removed. So, with all these API endpoints for checkserver, joinserver, and skins+cloaks, it will support classic (when skins were introduced, pre-authentication even) up until release 1.6.1.

In release 1.8, the modern logic is used for skins/capes too and authlib-injector functions as you would expect. I should note though, that the MinecraftSkins endpoint is still present (not MinecraftCloaks though) so I would have to check if it does anything in game.

catfromplan9 commented 7 months ago

And for capes, the legacy cape format will need to be use where the cape is cropped to the corner

catfromplan9 commented 7 months ago

Authentication did not exist from classic up until alpha 1.0.15 (alpha server 0.1.0). Authentication was added in alpha 1.0.16 (alpha server 0.1.1) and uses the same format specified here. In these earlier versions, drasl can be used for skins and nothing more I was wrong, the session token is still being sourced even in classic. After classic, multiplayer was not readded until alpha and when it was it was completely recoded to the implementation I discuss here. https://wiki.vg/Classic_Protocol#Heartbeats

catfromplan9 commented 7 months ago

I've forgot about login api, it is explained here

https://wiki.vg/Legacy_Minecraft_Authentication

This appeared some time before beta 1.7 and after beta 1.4, unfortunately it is in HTTPS. However, it should be noted that this appears unnecessary.

From a version of classic: com/mojang/minecraft/MinecraftApplet.java: this.minecraft.h = new a(this.getParameter("username"), this.getParameter("sessionid")); <- this exists in all versions up until release 1.5. Release 1.6 switched to yggdrasil but supports the old authentication scheme too (checkserver & joinserver endpoints present)

It appears to me that the launcher sets this, so this API endpoint does not need to be implemented

catfromplan9 commented 7 months ago

The authentication for alpha->1.6.1 is documented here at the bottom of the page: https://wiki.vg/index.php?title=Session&oldid=3107#Joining_a_Server Here is a newer revision https://wiki.vg/index.php?title=Session&direction=next&oldid=3112

Keep in mind, session.minecraft.net and www.minecraft.net need to both be implemented. session.minecraft.net is used in b1.8+ and www.minecraft.net is used prior to that

catfromplan9 commented 7 months ago

Update regarding versions:

Release 1.5.2 and below use fully the legacy authentication system, and so the proxyHost implementation works for them

Release 1.6.1-1.6.4 seem to use authlib for accessing the same legacy skins API and doing legacy authentication. With authlib-injector, skins are successfully obtained via yggdrasil (not capes though). Adding -Dhttp.proxyHost to send requests to my own skin server has no effect, but editing the hosts file works. I assume authentication is the same. It seems that the game itself no longer handles skins/capes (maybe an old version of authlib does it instead, with non-yggdrasil support?)

Release 1.7.2 and above use authlib for everything, but capes do not function. Skins work fine though. This is possibly related to some kind of change in how the API functions, so authlib-injector and drasl may need updates to support capes on 1.6.1-1.7.5. See the 1.7.6 changelog:

Skins and capes are now distributed through servers.

https://minecraft.wiki/wiki/Java_Edition_1.7.6

So, for 1.6.x support of authentication and capes (and just capes support on 1.7.2), authlib-injector would need to support the pre-1.7.6 capes API implementation and pass any http://session.minecraft.net requests authlib makes(? or some library that isn't the game itself but I assume authlib) through the proxy. Usecase unclear for playing Minecraft 1.6 though, so I consider this really low priority.

catfromplan9 commented 7 months ago

-Dhttp.nonProxyHosts can be used to specify domains to not proxy, each seperated by a pipe character ('|')

Midou36O commented 3 months ago

@evan-goode We're currently trying to figure out a solution on how to solve this but we need your input on some problems i'm encountering when providing the token from a legacy client, could by any chance check on thr drasl matrix room? (And if appropriate, continue the discussion on important research / implementation points here)