bedrock-protocol

repository·master·Indexed 19 days ago

https://github.com/prismarinejs/bedrock-protocol

A Minecraft Bedrock Edition (MCPE) protocol library supporting authentication, encryption, and packet serialization/deserialization. It enables the development of clients (bots), servers, and MITM relay proxies. The library uses ProtoDef for packet handling and supports Xbox Live authentication and Minecraft Realms connectivity.

Tokens
9.6K
Snippets
34
Records
48
Agent score
65%

What's inside bedrock-protocol

  1. Understand Packet Serialization with ProtoDef and YAML

    master

    bedrock-protocol uses ProtoDef to handle the serialization and deserialization of Minecraft packets. While the final JavaScript code is generated from JSON, the project maintains human-readable YAML files for easier maintenance.

    • proto.yml: Contains packet definitions (fields starting with packet_).
    • types.yml: Contains custom data type definitions.

    YAML Syntax Rules

    • Data Types: Uses standard types like li32 (little-endian 32-bit integer), lu32 (little-endian 32-bit unsigned integer), lf32 (little-endian 32-bit float), and bool.
    • Packet Metadata: Fields starting with ! are ignored by the parser but used for documentation or mapping.
      • !id: Used by the parser to generate the packet map.
      • !bound: Used for documentation (e.g., client or server).
    • Mapping/Enums: Use the => syntax to map integers to strings (e.g., u8 => 0: value).
    • Switch Statements: Use the ? operator to create conditional logic based on previously read values.
    • Arrays: Use square brackets []. The syntax is [Type][LengthPrefixType].
      • Example: Position[]varint reads an array of Position objects where the length is a VarInt.
    • Anonymous Structures: Use _ as a field name to inline a data structure.
    # Example of a custom type and a packet definition
    Position:
        x: li32
        z: lu32
        y: lf32
    
    packet_player_position:
        !id: 0x29
        !bound: client
        on_ground: bool
        position: Position
        movement_reason: u8 =>
            0: player_jump
            1: player_autojump
        _: movement_reason ?
            if player_jump or player_autojump:
                original_position: Position
                jump_tick: li64
  2. Create a MITM connection using Relay

    master

    A Relay acts as a proxy (Machine-in-the-Middle) between a client and a destination server. It functions as a combined server and client, handling authentication and encryption for the upstream server.

    Key Features

    • Packet Interception: Listen to clientbound (server to client) and serverbound (client to server) events to observe or modify packets.
    • Packet Modification: The data emitted in these events can be modified before being forwarded.
    • Upstream Access: Use player.upstream.queue() to send packets to the backend server.
    • Client Access: Use player.queue() to send packets to the connected client.

    Configuration

    When instantiating Relay, you must specify:

    • version: The Minecraft version.
    • host and port: The address/port the proxy listens on for clients.
    • destination: An object containing the host and port of the actual backend server.
    const { Relay } = require('bedrock-protocol')
    const relay = new Relay({
      version: '1.16.220',
      host: '0.0.0.0',
      port: 19132,
      destination: {
        host: '127.0.0.1',
        port: 19131
      }
    })
    relay.listen()
    
    relay.on('connect', player => {
      // Intercept server-to-client packets
      player.on('clientbound', ({ name, params }, des) => {
        if (name === 'disconnect') {
          params.message = 'Intercepted'
        }
      })
    
      // Intercept client-to-server packets
      player.on('serverbound', ({ name, params }, des) => {
        if (name === 'text') {
          params.message += `, on ${new Date().toLocaleString()}`
        }
        
        if (name === 'command_request') {
          if (params.command == "/test") {
            des.canceled = true
          }
        }
      })
    })
  3. Develop and test protocol changes locally

    master

    You can test protocol changes without waiting for a remote minecraft-data release by modifying the local node_modules version.

    Local development workflow:

    1. Clone the repository and run npm install.
    2. Modify the .YML files directly in node_modules/minecraft-data/minecraft-data/data/bedrock/latest/ (ensure you update !version if changing the version).
    3. Run npm run build in the root of the bedrock-protocol repository.
    4. Run npm test to verify that the protocol changes are applied correctly.
    npm install
    npm run build
    npm test
  4. Update the bedrock-protocol for a new Minecraft version

    master

    When a new Minecraft version is released, you must update both minecraft-data (the protocol schema) and bedrock-protocol (the library implementation).

    Steps to perform a full update:

    1. Update minecraft-data:
      • Find relevant protocol changes.
      • Update the .YML files in minecraft-data.
      • Build the .YML files into JSON.
      • Release the updated minecraft-data package.
    2. Update bedrock-protocol:
      • Add the new version to src/options.js.
      • Run npm run build to regenerate code from the updated schema.
      • Run npm test to verify the changes.
  5. Manage connection lifecycle with ClientStatus

    master

    The Connection class tracks the current state of the network session using the ClientStatus enum. You can monitor state changes by listening to the status event.

    Status Values:

    • ClientStatus.Disconnected (0)
    • ClientStatus.Connecting (1)
    • ClientStatus.Authenticating (2) (Handshaking)
    • ClientStatus.Initializing (3) (Authed, need to spawn)
    • ClientStatus.Initialized (4) (play_status spawn sent by server, client responded with SetPlayerInit packet)

    Note: Setting the status property automatically emits a 'status' event.

    const { ClientStatus } = require('bedrock-protocol/src/connection');
    
    connection.on('status', (newStatus) => {
      if (newStatus === ClientStatus.Initialized) {
        console.log('Connection is ready for gameplay!');
      }
    });
  6. Handle client events and lifecycle

    master

    The Client class emits several events that you can listen to for managing the connection lifecycle:

    • connect_allowed: Emitted after initialization is complete and the client is ready to call .connect().
    • loggingIn: Emitted when the client starts the login process.
    • join: Emitted when the client transitions to the Initializing status (often after play_status is received).
    • spawn: Emitted when the player has spawned in the world.
    • packet: Emitted whenever a packet is successfully deserialized.
    • kick: Emitted when the server sends a disconnect request.
    • error: Emitted on connection or protocol errors.
    • close: Emitted when the client connection is closed.
  7. Fix localhost connection issues on Windows 10 (Minecraft Win10 Edition)

    master

    If you cannot connect to a local server using Minecraft Windows 10 Edition, it is likely due to loopback restrictions on Windows 10 UWP apps. You can lift this restriction by running PowerShell as an administrator and using the CheckNetIsolation command.

    To unlock the standard version:

    CheckNetIsolation LoopbackExempt -a -n="Microsoft.MinecraftUWP_8wekyb3d8bbwe"

    To unlock the Preview or Beta release:

    CheckNetIsolation LoopbackExempt -a -n="Microsoft.MinecraftWindowsBeta"

    If the commands above do not work, find your specific package name by running:

    Get-AppxPackage -AllUsers | Where Name -Match ".*Minecraft.*" | Select Name,InstallLocation,PackageFullName

    Then, use the value from the PackageFullName field in the CheckNetIsolation command instead of the default strings.

  8. Enable debug logging for connection errors

    master

    If you are being kicked during login without receiving explicit error messages, the error details are likely being logged in debug mode. To see these logs, enable the minecraft-protocol debug namespace by setting the DEBUG environment variable at the very top of your entry file.

    process.env.DEBUG = 'minecraft-protocol';
    // ... rest of your code
  9. Join a Realm with bedrock.createClient()

    master

    To connect to a Realm that you own or have been invited to, provide a realms configuration object to createClient. The pickRealm function receives an array of available Realms and must return a single Realm object. This function can be asynchronous.

    const bedrock = require('bedrock-protocol')
    const client = bedrock.createClient({
      realms: {
        pickRealm: (realms) => realms[0] // Function which receives an array of joined/owned Realms and must return a single Realm. Can be async
      }
    })