Tis

Minecraft 26.2 snapshot 7

Original Changelog

Minecraft now has a friends list and peer-to-peer!

Privacy Considerations

Adding a friend in Minecraft also adds them as a friend on Xbox, allowing them to see your Xbox profile. Make sure your Xbox profile does not contain any personal information before adding players you don’t trust as friends.

Since peer-to-peer connections attempt to use the most direct network path possible, Joining a friend’s world or letting a friend join your world may expose your IP address to them, even if normal traffic is routed through a VPN or proxy.

Potent Sulfur

Eruption and cooldown times of Geysers are now random based on the world seed and the position of the block.

To launch entities, geysers no longer require an entity’s eye position to have visual line of sight with the center of the geyser’s topmost water block.

To apply Nausea to a player in range, the player’s eyes must be in an air block directly above a water block, and the center of that water block must have collision line of sight (used to be visual line of sight) with the point one block below the player’s eyes.

Block Data

potent_sulfur_state can now be continuous.

Block Entity Data

Removed dormant_time and eruption_time.

Music Disc

“Bounce” plays for 3:54.

Bounce appears in chest minecarts that are opened in a Sulfur Cave biome, not just minecarts that generate in a Sulfur Cave biome. It is possible to move a minecart into a Sulfur Cave, then open it for a chance to get the music disc.

Chest minecarts opened in a Sulfur Cave biome contain their usual loot, with the Bounce music disc added to the pool containing rails and torches (3 rolls):

Item Weight Chance
Rails 20/60 70.370%
Torch 15/60 57.812%
Music Disc (Bounce) 10/60 42.130%
Activator Rail 5/60 22.975%
Detector Rail 5/60 22.975%
Powered Rail 5/60 22.975%

Friends List

If the Xbox account associated with your Microsoft account has friends, those friends will appear in the friends list. Adding a friend in Minecraft also adds them as a friend on Xbox.

Players that have their chat settings set to “Friends Only” in either Minecraft or Xbox settings must opt-in to the friends list before they can talk to existing friends, otherwise they will not be able to see anyone’s messages.

Invites to join your world expire after one minute.

Outgoing invites and presence (“Online”, “In a world”, etc…) are sent every 60 seconds, or every 10 seconds if the friends menu is open. Your presence is sent 10 seconds after the last update when:

  • You open/close a world
  • You publish/unpublish a server
  • You disconnect from a server
  • You send an invite to join your world
  • You accept or decline an incoming invite
  • An outgoing invite expires

Peer-to-Peer

Peer-to-peer uses the WebRTC protocol to connect two players through as direct a network path as possible. If two players cannot connect directly, all traffic is routed through Mojang’s relay servers.

Peer-to-peer traffic is encrypted. Even if traffic is routed through Mojang’s relay servers, Mojang cannot read its content.

Like a LAN server, worlds shared to friends are in online-mode. Unlike LAN servers, worlds shared to friends will not let players in if their username cannot be verified or if authentication servers cannot be reached.

Up to 8 players can play on the same published singleplayer world, whether they join through LAN or peer-to-peer. Mods can increase this limit.

Friends who attempt to join you world when the world has not been published online will be rejected. Non-friends will be ignored instead.

Splash Text

Added a new splash text: “Music by fingerspit!”

Data

The last_explosion_impact_pos entity NBT field was moved from all living entities to just players.

Debug Properties

Added two debug properties:

  • -DMC_DEBUG_DEBUG_CHAT_FRIENDS_ONLY [sic] - Acts as if chat is restricted to friends only.
  • -DMC_DEBUG_NATIVE_WEBRTC_LOGS - Prints logs at the INFO level or above from the webrtc-java library.

Session API

All endpoints are located at `https://sessionserver.mojang.com.

GET /session/minecraft/profile/<uuid>

Response

action in profileActions was renamed to updateType.

Example:

{
  ...,
  "profileActions": [
    {
      "updateType": "USING_BANNED_SKIN"
    }
  ]
}

Minecraft Services API

All endpoints are located at https://api.minecraftservices.com and require an Authorization bearer token.

All endpoints may respond with the following HTTP codes when an error occurs:

  • 400: Name or UUID does not exist
  • 403: Access token is invalid
  • 429: Too many requests
  • 500: Service not available Other errors are treated as generic errors.

GET /player/attributes

Response

Added new fields:

  • friendsPreferences: Optional
    • friends: Whether the friends list is enabled, either ENABLED or DISABLED
    • acceptInvites: Whether friend requests are allowed, either ENABLED or DISABLED
  • chatPreferences: Optional
    • textCommunication: Whether chat is enabled, either ENABLED, FRIENDS_ONLY, or DISABLED

Example:

{
  ...,
  "friendsPreferences": {
    "friends": "ENABLED",
    "acceptInvites": "ENABLED"
  },
  "chatPreferences": {
    "textCommunication": "ENABLED"
  },
  ...
}

POST /player/attributes

Payload

Added new fields:

  • friendsPreferences: Optional
    • friends: Whether the friends list is enabled, either ENABLED or DISABLED
    • acceptInvites: Whether friend requests are allowed, either ENABLED or DISABLED

Example:

{
  ...,
  "friendsPreferences": {
    "friends": "ENABLED",
    "acceptInvites": "ENABLED"
  }
}

Response

The POST /player/attributes response is the same as GET /player/attributes.

GET /friends

New endpoint added.

Response

  • friends: Current friend list
    • A friend request
      • profileId: The player’s in-game UUID
      • name: The player’s in-game username
  • incomingRequests: Incoming friend requests
    • A friend request
      • profileId: The player’s in-game UUID
      • name: The player’s in-game username
  • outgoingRequests: Outgoing friend requests
    • A friend request
      • profileId: The player’s in-game UUID
      • name: The player’s in-game username
  • empty: Optional, whether the friends list is empty

The response has an ETag header which can be used for caching.

Example:

{
  "friends": [
    {
      "profileId": "f6489b79-7a9f-49e2-980e-265a05dbc3af",
      "name": "Tis_awesomeness"
    }
  ],
  "incomingRequests": [],
  "outgoingRequests": [],
  "empty": false
}

PUT /friends

New endpoint added.

Payload

  • name: Optional, the player’s in-game username
  • profileId: Optional, the player’s in-game UUID
  • updateType: Either ADD to send an outgoing friend request or accept an incoming friend request, or REMOVE to remove a friend, decline an incoming friend request, or revoke an outgoing friend request

Either name or profileId must be provided. If both are provided, profileId is used and name is ignored.

The response has an ETag header which can be used for caching.

Example:

{
  "name": "jeb_",
  "updateType": "ADD"
}

Response

The PUT /friends response is the same as GET /friends.

PUT /presence

New endpoint added.

Payload

  • status: One of ONLINE, PLAYING_OFFLINE, PLAYING_REALMS, PLAYING_SERVER, PLAYING_HOSTED_SERVER, or OFFLINE
  • joinInfo: Optional
    • value: String with unknown purpose, client always sends null
    • invites: Optional, list of invited players
      • In-game UUID of an invited player

Example:

{
  "status": "PLAYING_SERVER",
  "joinInfo": {
    "value": null,
    "invites": [
      "f6489b79-7a9f-49e2-980e-265a05dbc3af"
    ]
  }
}

Response

  • presence: List of presence statuses for each player in the friends list
    • A presence status
      • profileId: The player’s in-game UUID
      • pmid: The player’s PMID, a UUID with dashes used for peer-to-peer multiplayer
      • status: One of ONLINE, PLAYING_OFFLINE, PLAYING_REALMS, PLAYING_SERVER, PLAYING_HOSTED_SERVER, or OFFLINE
      • joinInfo: Optional
        • value: Unused string with unknown purpose
        • invited: Whether the player has invited you to their world
      • lastUpdated: When presence was last updated
{
  "presence": [
    {
      "profileId": "f6489b79-7a9f-49e2-980e-265a05dbc3af",
      "pmid": "13280211-851c-4543-9448-89777f191702",
      "status": "PLAYING_SERVER",
      "joinInfo": {
        "value": "13280211-851c-4543-9448-89777f191702",
        "invited": true
      },
      "lastUpdated": "2026-05-20T04:42:00.139151764Z"
    }
  ]
}

Signaling API

All endpoints are located at https://signaling-afd.franchise.minecraft-services.net.

GET /

Request

  • x-mojangauth: The Minecraft access token
  • Session-Id: A random UUID generated once on startup
  • Request-Id: A random UUID generated for each request

Example:

x-mojangauth: <token>
Session-Id: 87c025a5-8ab5-4c07-8188-2057dabf7ed1
Request-Id: e4a43120-687a-4b13-97e7-d175b0f4aae2

Response

  • result
    • signalingUri: A base URI to a Minecraft signaling server
    • pingFrequency: A time in hh:mm:ss format, currently unused

Example:

{
  "result": {
    "signalingUri": "wss://signal-eastus2.franchise.minecraft-services.net"
  }
}

Peer-to-Peer Protocol

Peer-to-peer uses WebRTC to connect two players to each other.

First, the client requests the signalingUri from the Signaling API. Then, it sends a GET request with the same headers to <signalingUri>/ws/v1.0/messaging/connect/java to open a websocket connection.

Signaling messages such as join requests are sent through the websocket using the JSON-RPC 2.0 protocol. Both clients use the signaling server to negotiate how best to connect to each other.

Ping

The client sends the websocket a ping every 50 seconds:

  • jsonrpc: 2.0
  • id: The numeric transaction ID as a string, incremented with each request
  • method: System_Ping_v1_0
  • params: Empty list

Example:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "System_Ping_v1_0",
  "params": []
}

The server will send back a pong, which the client ignores:

  • jsonrpc: 2.0
  • id: The numeric transaction ID as a string, incremented with each request
  • method: System_Pong_v1_0
  • params: Empty list

Example:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "System_Pong_v1_0",
  "params": []
}

Signaling Messages

Sending

Clients can send signaling messages to other players over the signaling websocket:

  • jsonrpc: 2.0
  • id: The numeric transaction ID as a string, incremented with each request
  • method: Signaling_SendClientMessage_v1_0
  • params: A list with three parameters
    • null
    • The PMID of the player to send the message to
    • The signal message JSON encoded as a string

Example:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "Signaling_SendClientMessage_v1_0",
  "params": [
    null,
    "13280211-851c-4543-9448-89777f191702",
    "{\"type\":\"JOIN_REQUEST\",\"sessionId\":\"87c025a5-8ab5-4c07-8188-2057dabf7ed1\"}"
  ]
}

If an error occurs, the websocket will return the following:

  • jsonrpc: 2.0
  • id: The same transaction ID
  • error
    • code: The JSON-RPC error code
    • message: The error message

Example:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32600,
    "message": "Invalid Request"
  }
}

Receiving

Clients also receive messages on the same websocket:

  • jsonrpc: 2.0
  • id: The numeric transaction ID as a string, incremented with each request
  • method: Signaling_ReceiveMessage_v1_0
  • params: A list with one parameter
    • A message
      • From: The PMID of the player the message was sent from
      • Message: The signal message JSON encoded as a string
      • Id: Optional, UUID with unknown purpose, currently unused

Example:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "Signaling_ReceiveMessage_v1_0",
  "params": [
    {
      "From": "13280211-851c-4543-9448-89777f191702",
      "Message": "{\"type\":\"JOIN_REQUEST\",\"sessionId\":\"87c025a5-8ab5-4c07-8188-2057dabf7ed1\"}",
      "Id": "e4a43120-687a-4b13-97e7-d175b0f4aae2"
    }
  ]
}

Join Requests

Join requests and responses (and declined invites) are sent as signaling messages:

  • type: One of JOIN_REQUEST, JOIN_ACCEPTED, JOIN_REJECTED, or INVITE_DECLINED
  • sessionId: The same Session-Id sent to the Signaling API

Example:

{
  "type": "JOIN_REQUEST",
  "sessionId": "87c025a5-8ab5-4c07-8188-2057dabf7ed1"
}

When a host publishes a world online, either:

  • The host sends an invite to a guest, who can send either a JOIN_REQUEST to accept or an INVITE_DECLINED to decline.
  • A guest sends a JOIN_REQUEST to the host without receiving an invite.

When the host receives a JOIN_REQUEST:

  • If the join request came from a friend and the host is hosting an online world, the host responds with JOIN_ACCEPTED
  • If the join request came from a friend, but the host is not hosting an online world, the host responds with JOIN_REJECTED
  • If the join request did not come from a friend, it is silently ignored

Request TURN Server

Once the host sends JOIN_ACCEPTED and the guest receives JOIN_ACCEPTED, both request TURN/STUN servers from Mojang over the signaling websocket. The TURN/STUN servers are later used for Interactive Connectivity Establishment.

  • jsonrpc: 2.0
  • id: The numeric transaction ID as a string, incremented with each request
  • method: Signaling_TurnAuth_v1_0
  • params: Empty list

Example:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "Signaling_TurnAuth_v1_0",
  "params": []
}

The websocket returns the following result:

  • jsonrpc: 2.0
  • id: The same transaction ID
  • result
    • ExpirationInSeconds: Time until the username/passwords expires
    • TurnAuthServers: A list of TURN auth servers
      • A TURN auth server
        • Username: Username used to authenticate to the TURN server
        • Password: Password used to authenticate to the TURN server
        • Urls: List of TURN/STUN URLs
          • A TURN/STUN URL

Example:

{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "ExpirationInSeconds": 604799,
    "TurnAuthServers": [
      {
        "Username": "...",
        "Password": "...",
        "Urls": [
          "stun:relay.communication.microsoft.com:3478",
          "turn:relay.communication.microsoft.com:3478"
        ]
      }
    ]
  }
}

Handshake

Once each client obtains the TURN servers, the WebRTC handshake begins. Both SDP offer/answer and ICE candidate messages are exchanged in parallel.

SDP Offer/Answer

The guest and host negotiates and agrees upon network parameters (such as for SCTP and DTLS) that allow a connection to be established and allow the guest and host to understand each other.

First, the guest sends the host an OFFER signaling message, containing network parameters in SDP format. Then, the host sends the guest an ANSWER in SDP format, which contains the network parameters that will be used for the connection.

  • type: One of OFFER or ANSWER
  • sessionId: The same Session-Id sent to the Signaling API
  • sdp: The SDP offer or answer

Example:

{
  "type": "OFFER",
  "sessionId": "87c025a5-8ab5-4c07-8188-2057dabf7ed1",
  "sdp": "v=0\r\no=- ...\r\n"
}

ICE

Interactive Connectivity Establishment (ICE) is used to find ways the guest and host can communicate with each other as directly as possible. ICE is negotiated over exclusively UDP, over IPv4 or IPv6.

Both clients continually generate a set of ICE candidates: possible IP addresses and ports the clients can use to connect to each other:

  • If both clients are on the same network, connecting directly is preferred.
  • Each client reaches out to a STUN server received earlier, which replies with the client’s public address and whether or not the client is accessible behind its router’s NAT, if one exists.
  • As a last resort, the TURN relay server allocates an IP and port the client can use to communicate with the other client. All traffic will be routed through the relay server.

Both clients send ICE candidate signaling messages to each other as they are discovered:

  • type: ICE_CANDIDATE
  • sessionId: The same Session-Id sent to the Signaling API
  • iceCandidate
    • candidate: The ICE candidate in SDP format
    • sdpMid: Optional, the SDP stream media ID, defaults to “0”
    • sdpMLineIndex: The index (starting at 0) of the SDP media description the candidate is associated with

Example:

{
  "type": "ICE_CANDIDATE",
  "sessionId": "87c025a5-8ab5-4c07-8188-2057dabf7ed1",
  "iceCandidate": {
    "candidate": "candidate:842163049 1 udp 2122260223 192.0.2.1 49152 typ host",
    "sdpMid": "0",
    "sdpMLineIndex": 0
  }
}

Each client pairs up sent and received ICE candidates, performing connectivity checks to ensure the other client can be reached. Finally, the guest nominates an ICE candidate pair, and both the guest and host begin the Minecraft protocol using those IP addresses and ports.

Handoff to Minecraft Protocol

Both the guest and the host perform the usual client-server handshake for an online mode server. However, since the connection is already encrypted with DTLS (as required by the WebRTC protocol), the symmetric key negotiated during the client-server handshake is not used to double-encrypt the connection.