Multiplayer utilities¶
The following components are used for various multiplayer interacions (generally needed during the server joining process).
mcproto.multiplayer.JoinAcknowledgeData
¶
Bases: TypedDict
Response from :func:join_check
(hasJoined minecraft API endpoint).
This response contains information on the user has submitted the :func:join_request
.
(uuid, name, and player skin properties)
mcproto.multiplayer.JoinAcknowledgeProperty
¶
mcproto.multiplayer.UserJoinCheckFailedError
¶
Bases: Exception
Exception raised when :func:join_check
fails.
This signifies that the Minecraft session API server didn't contain a join request for the
server_hash
and client_username
, and it therefore didn't acknowledge the join.
This means the user didn't confirm this join with Minecraft API (didn't call
:func:join_request
), hence the validity of this account can't be verified. The server
should kick the user and end the join flow.
mcproto.multiplayer.UserJoinRequestErrorKind
¶
Enum for various different kinds of exceptions that can occur during :func:join_request
.
mcproto.multiplayer.UserJoinRequestErrorKind.from_status_error
classmethod
¶
from_status_error(code: int, err_msg: str | None) -> UserJoinRequestErrorKind
Determine the error kind based on the status code and error message.
mcproto.multiplayer.UserJoinRequestFailedError
¶
Bases: Exception
Exception raised when :func:join_request
fails.
This can be caused by various reasons. See: :class:UserJoinRequestErrorKind
enum class.
The most likely case for this error is invalid authentication token, or the user being
banned from multiplayer.
mcproto.multiplayer.compute_server_hash
¶
compute_server_hash(server_id: str, shared_secret: bytes, server_public_key: RSAPublicKey) -> str
Compute a hash to be sent as 'serverId' field to Mojang session server.
This function is used for :func:join_request
and :func:join_check
functions, which require
this hash value.
This SHA1 hash is computed based on the server_id
, server_public_key
and shared_secret
.
Together, these values ensure that there can't be any middle-man listening in after encryption is
established.
This is because a middle man/proxy who would want to listed into the encrypted communication would
need to know the encryption key (shared_secret
). A proxy can capture this key, as the client
sends it over to the server in :class:~mcproto.packets.login.login.LoginEncryptionResponse
packet,
however it is sent encrypted. The client performs this encryption with a public key, which it got
from the server, in :class:mcproto.packets.login.login.LoginEncryptionRequest
packet.
That mans that for a proxy to be able to actually obtain this shared secret value, it would need to be able to capture the encryption response, and decrypt the shared secret value. That means it would need to send a spoofed version of the encryption request packet, with the server's public key replaced by one that the proxy owns a private key for. This will work, and the proxy could indeed decrypt the sent shared secret now. All it would need to do now is send this shared secret to the server. That's easy, just re-encrypt it with the server's original public key, and send it in a custom encryption request!
So then it seems that it's possible to intercept the client-server communication and spy in, and indeed, this will work with offline mode (warez) servers, however with online mode servers, that's where this function comes in!
Online mode servers rely on an API server from Mojang's (session server), which the client informs of the join. The server then queries this server for an acknowledgement of this join, and only if this session server confirms that the client did indeed inform it of this join will the server allow this client to join.
The trick is, this request to inform the session server of the join can only be performed by the client directly, a proxy can't simulate it, because this request requires a token for the minecraft account, which only the launcher has. The client never sends this token to the server, only to the Mojang's session server, so a proxy wouldn't have it.
This join request then contains those 3 variables, one of which being the public key itself, so if the proxy sent a different key, the server would no longer arrive at the same server hash, and the check would fail, so the server wouldn't allow this client to join.
mcproto.multiplayer.join_check
async
¶
join_check(client: AsyncClient, client_username: str, server_hash: str, client_ip: str | None = None) -> JoinAcknowledgeData
Check with the Mojang session server if a join request was made.
This function is called by the server in online mode (non-warez), to verify that the joining client
really does have an official minecraft account. The client will first inform the server about this
join request (:func:join_request
), server then runs this check confirming the client is who they
say they are.
This request should be performed after receiving the after receiving the
:class:~mcproto.packets.login.login.LoginEncryptionResponse
packet.
This request uses a server_hash
, this is the value under which the client has submitted their
join request, and we'll now be checking for that submission with that same value. This is a hash
composed of various values, which together serve as a way to prevent any MITMA (man in the middle
attacks). To obtain this hash, see :func:compute_server_hash
. This function's docstring also
includes description for why and how this prevents a MITMA.
:param client: HTTPX async client to make the HTTP request with. :param client_username: Must match joining the username of the joining client (case sensitive).
Note: This is the in-game nickname of the selected profile, not Mojang account name
(which is never sent to the server). Servers should use the name in "name" field which was
received in the :class:`~mcproto.packets.login.login.LoginStart` packet.
:param server_hash: SHA1 hash of the server (see :func:compute_server_hash
)
:param client_ip:
IP address of the connecting player (optional)
Servers only include this when 'prevent-proxy-connections' is set to true in server.properties
mcproto.multiplayer.join_request
async
¶
Inform the Mojang session server about this new user join.
This function is called by the client, when joining an online mode (non-warez) server. This is
required and the server will check that this request was indeed made (:func:join_check
).
This request should be performed after receiving the
:class:~mcproto.packets.login.login.LoginEncryptionRequest
packet, but before sending the
:class:~mcproto.packets.login.login.LoginEncryptionResponse
.
Performing this request requires an :class:~mcproto.auth.account.Account
instance, as this request
is here to ensure that only original Minceraft accounts (officially bought accounts) can join.
This request uses a server_hash
to identify which server is the client attempting to join. This
hash is composed of various values, which together serve as a way to prevent any MITMA (man in the
middle attacks). To obtain this hash, see :func:compute_server_hash
. This function's docstring
also includes description for why and how this prevents a MITMA.
:param client: HTTPX async client to make the HTTP request with.
:param account: Instance of an account containing the minecraft token necessary for this request.
:param server_hash: SHA1 hash of the server (see :func:compute_server_hash
)