node-minecraft-protocol

repository·master·Indexed 23 days ago

https://github.com/prismarinejs/node-minecraft-protocol

A Node.js implementation of the Minecraft protocol for parsing and serializing packets, handling authentication, encryption, and compression. It supports both client and server implementations across a wide range of Minecraft versions, including features for Microsoft authentication, Realm connectivity, and SOCKS5 proxy support.

Tokens
8.5K
Snippets
8
Records
50
Agent score
80%

What's inside minecraft-protocol

  1. Handle client authentication and sessions

    master

    When using mc.createClient(), authentication is handled based on the auth type:

    • Microsoft Auth: If password is omitted, it uses device code authentication. You can listen to the onMsaCode(data) callback to handle the device code flow.
    • Mojang Auth: Requires username and password. If password is omitted, it uses tokens from the profilesFolder.
    • Offline Mode: Uses the provided username without authentication.
    • Manual Session: You can provide a session object containing clientToken, accessToken, and selectedProfile (with name and id) to bypass standard auth flows.

    After successful authentication, the Client instance emits a 'session' event containing the session data.

  2. Implement chat signing support in an NMP server

    master

    Starting with Minecraft 1.19.1, chat messages use a signed chain of SHA256 hashes to ensure integrity. To maintain this chain and allow clients to verify messages, an nmp server must track messages that are broadcast to other players.

    When your server receives a message from a player and subsequently broadcasts it to other players via player_chat packets, you must call client.logSentMessageFromPeer(packet). This allows the server to store the packets necessary to verify the lastSeenMessages field in inbound chat packets from other clients, ensuring the chain integrity is maintained.

    Failure to manage this can lead to issues with chain verification or clients being kicked if the pending message acknowledgement (ACK) queue becomes too large.

  3. Handle server events

    master

    The Server instance emits several events to manage the lifecycle of connections and players:

    • listening: Emitted when the server is ready to accept incoming connections.
    • connection: Emitted when a client connects, but before login has occurred. Takes a Client object.
    • login: Emitted when a client is successfully logged in. Takes a Client object.
    • playerJoin: Emitted after a player enters the PLAY protocol state and can send/receive game packets.
      • Note for 1.20.2+: Players enter a CONFIG state after login. You must wait for playerJoin to interact with them in the PLAY state.
    • close: Emitted when the server stops listening for connections.
  4. Use the node-minecraft-protocol proxy

    master

    The node-minecraft-protocol proxy acts as an intermediary: it starts an nmp server that, when connected to by a client, automatically creates an nmp client that connects to a target Minecraft server. This is useful for inspecting traffic between a client and a server.

    Setup and Execution

    1. Start the target Minecraft server locally.
    2. Start your Minecraft client locally.
    3. Run the proxy script pointing to the target server and its version.

    Note: You can use npm install -g minecraft-wrap and the downloadMinecraft command to obtain the necessary components.

    usage: node proxy.js [<options>...] <target_srv> <version>
  5. Test your SOCKS5 proxy connection

    master

    Before using the Socks5 Proxy example, verify that your proxy is functional. You can test the connection using curl on Unix-based systems or the Windows Command Prompt.

    Run the following command, replacing <proxyAddress> and <proxyPort> with your actual proxy details:

    curl -x "socks5://<proxyAddress>:<proxyPort>" "http://ifconfig.me"

    If the command returns an IP address, the proxy is working. If it returns anything else, the proxy is not functional.

  6. Understand Minecraft Protocol States

    master

    The protocol moves through several distinct states. You can monitor the current state via client.state or listen to the 'state' event.

    Available states (via States enum):

    • handshaking: Initial connection phase.
    • login: Authentication and login phase.
    • play: The main game state where players interact with the world.
    • configuration: (1.19.3+) Configuration phase for client/server settings.
    • status: Used for server list querying.
  7. How Client state transitions work

    master

    The Client manages its internal protocol state (e.g., HANDSHAKING, STATUS, LOGIN, PLAY) via the state setter.

    When you change client.state, the following happens automatically:

    1. The current serializer and deserializer are unpiped and destroyed.
    2. A new serializer and deserializer are created for the new state.
    3. The data pipeline (compression, encryption, framing) is re-wired to accommodate the new state's requirements.
    4. A state event is emitted with the (newProperty, oldProperty).
  8. Troubleshoot SOCKS5 proxy errors

    master

    If you encounter errors while using the SOCKS5 proxy, use this guide to identify the cause:

    Error MessageMeaning
    FetchError: request to https://authserver.mojang.com/authenticate failed, reason: Socket closedThe Proxy is not working
    SocksClientError: Socket closedGeneral Socket error. The Proxy is bad or refuses the connection
    SocksClientError: connect ECONNREFUSED <some ip address>The connection to the proxy itself failed
    SocksClientError: Proxy connection timed outThe destination address is wrong/unreachable, or the Proxy is not working
    Connection Refused: Blocked by CloudFront/CloudFlareThe Proxy IP has been banned or blocked by Cloudflare
  9. Create a Minecraft server

    master

    Use mc.createServer() to instantiate a server.

    Configuration Options:

    • online-mode: Boolean. Enables/disables authentication.
    • encryption: Boolean. Enables/disables encryption.
    • host: String. The host address to bind to.
    • port: Number. The port to listen on.
    • version: String. The Minecraft version the server should run (e.g., '1.18').

    Once the server is running, you can listen for events like playerJoin and use client.write(packetName, data) to send packets to connected clients.

    const mc = require('minecraft-protocol')
    const nbt = require('prismarine-nbt')
    const server = mc.createServer({
      'online-mode': true,   // optional
      encryption: true,      // optional
      host: '0.0.0.0',       // optional
      port: 25565,           // optional
      version: '1.18'
    })
    const mcData = require('minecraft-data')(server.version)
    
    function chatText (text) {
      return mcData.supportFeature('chatPacketsUseNbtComponents')
        ? nbt.comp({ text: nbt.string(text) })
        : JSON.stringify({ text })
    }
    
    server.on('playerJoin', function(client) {
      const loginPacket = mcData.loginPacket
    
      client.write('login', {
        ...loginPacket,
        enforceSecureChat: false,
        entityId: client.id,
        hashedSeed: [0, 0],
        maxPlayers: server.maxPlayers,
        viewDistance: 10,
        reducedDebugInfo: false,
        enableRespawnScreen: true,
        isDebug: false,
        isFlat: false
      })
    
      client.write('position', {
        x: 0,
        y: 255,
        z: 0,
        yaw: 0,
        pitch: 0,
        flags: 0x00
      })
    
      const message = {
        translate: 'chat.type.announcement',
        with: [
          'Server',
          'Hello, world!'
        ]
      }
      if (mcData.supportFeature('signedChat')) {
        client.write('player_chat', {
          plainMessage: message,
          signedChatContent: '',
          unsignedChatContent: chatText(message),
          type: mcData.supportFeature('chatTypeIsHolder') ? { chatType: 1 } : 0,
          senderUuid: 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43',
          senderName: JSON.stringify({ text: 'me' }),
          senderTeam: undefined,
          timestamp: Date.now(),
          salt: 0n,
          signature: mcData.supportFeature('useChatSessions') ? undefined : Buffer.alloc(0),
          previousMessages: [],
          filterType: 0,
          networkName: JSON.stringify({ text: 'me' })
        })
      } else {
        client.write('chat', { message: JSON.stringify({ text: message }), position: 0, sender: 'me' })
      }
    })