Skip to content

Nethernet support for joining worlds#533

Draft
LucienHH wants to merge 32 commits into
PrismarineJS:masterfrom
LucienHH:Nethernet
Draft

Nethernet support for joining worlds#533
LucienHH wants to merge 32 commits into
PrismarineJS:masterfrom
LucienHH:Nethernet

Conversation

@LucienHH
Copy link
Copy Markdown
Contributor

@LucienHH LucienHH commented Oct 4, 2024

Majority work for Nethernet support. Supports listening and joining over LAN, signalling via the franchise WebSocket and creating/joining an Xbox session.

To do

Closes #473

@extremeheat
Copy link
Copy Markdown
Member

Thanks for working on this, please advise when it's ready for review

@LucienHH
Copy link
Copy Markdown
Contributor Author

LucienHH commented Oct 7, 2024

Most the work is done apart from some bits but would probably be good to start moving parts of this out of here into other areas. Mainly moving the contents of ./nethernet, I'd think it'd make sense to have this in a prismarine-nethernet repo maybe?

Same with the Xbox Session API, I think we could get away with putting this in pauth unless you think we should separate an Xbox API to another repo?

Comment thread src/createServer.js Outdated
Comment thread src/nethernet/client.js Outdated
Comment thread src/createClient.js

client.conLog?.(`Connecting to ${client.options.host}:${client.options.port} ${ad.motd} (${ad.levelName}), version ${ad.version} ${client.options.version !== ad.version ? ` (as ${client.options.version})` : ''}`)
} else if (client.options.transport === 'nethernet') {
client.conLog?.(`Connecting to ${client.options.networkId} ${ad.motd} (${ad.levelName})`)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we know what version to connect as here if the user does not specify/can't get via ping?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user doesn't specify it defaults to the current version and the version isn't included in the Nethernet ping response

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, what is advertisement.version then? We may have to figure out the version after connecting to the server then like nmp can do. It should be sent in login/network_settings packet otherwise. The current code should also be matching version by protocol version num over strings but that should be fixed outside this PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure but I assume it's the version of NetherNet protocol being used as it's just a uint8. From my testing it's always been set to 3 over multiple version updates.

Comment thread src/createClient.js Outdated
Copy link
Copy Markdown
Member

@extremeheat extremeheat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node-fetch can be removed in favor of native fetch now that it's also removed from prismarine-auth

Comment thread src/createClient.js Outdated
Comment thread src/createClient.js Outdated
@socket-security
Copy link
Copy Markdown

socket-security Bot commented Sep 15, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedxbox-rta@​2.1.0731009980100
Addedjson-bigint@​1.0.010010010075100
Addedws@​8.20.09810010088100

View full report

@yntha
Copy link
Copy Markdown

yntha commented May 9, 2026

Hi, great work so far on this PR. I've developed a similar nethernet implementation that is fully working as of the latest update to minecraft. The new bedrock signalling servers use the JSON-RPC format, not the old Type/From/Message format.

On Realm connections (and some newer Xbox session flows), the signalling websocket sends messages in this format:

{
  "jsonrpc": "2.0",
  "method": "NetherNetMessage",
  "params": {
    "fromNetherNetId": "12312312312312312",
    "innerMessage": "<SignalStructure b64 payload>"
  }
}

And expects signals from the client in this format:

{
  "jsonrpc": "2.0",
  "method": "NetherNetMessage",
  "params": {
    "netherNetId": "12312312312312312",
    "message": "<SignalStructure b64 payload>"
  }
}

I've patched src/websocket/signal.js with the following, that adds legacy protocol support and jsonrpc support. Callers can switch to the new protocol via signallingProtocol: 'jsonrpc' when calling createClient:

const client = bedrock.createClient({
  transport: 'nethernet',
  useSignalling: true,
  signallingProtocol: 'jsonrpc',
  //...
})

Internal patch (to src/websocket/signal.js):

// new parameter 'options'
constructor(networkId, authflow, version, options = {}) {
  //...
  this.protocol = options.protocol || 'legacy'
}

// send new or legacy format
write(signal) {
  const message = this.protocol === 'jsonrpc'
    ? stringify({
        jsonrpc: '2.0',
        method: 'NetherNetMessage',
        params: {
          netherNetId: String(signal.networkId),
          message: signal.toString()
        }
      })
    : stringify({ Type: MessageType.Signal, To: signal.networkId, Message: signal.toString() })
  this.ws.send(message)
}

onJsonRpcMessage(message) {
  const signal = parseJsonRpcSignal(message)
  if (!signal) return debug('Unhandled jsonrpc message', message)
  this.emit('signal', signal)
}

I also modified the onMessage function to catch inbound jsonrpc messages by default when all other cases fail:

default: {
  if (message.jsonrpc) this.onJsonRpcMessage(message)
}

Helpers:

function parseSignalMessage(message) {
  if (typeof message !== 'string') return null
  try {
    const parsed = JSON.parse(message)
    const signal = parseJsonRpcSignal(parsed)
    if (signal) return signal
  } catch {}
  return SignalStructure.fromString(message)
}

function parseJsonRpcSignal(message) {
  const params = message?.params || message?.Params || message?.result || message?.Result
  if (!params || typeof params !== 'object') return null
  const signalText = params.message || params.Message || params.innerMessage || params.InnerMessage
  if (!signalText) return null
  const signal = SignalStructure.fromString(signalText)
  const networkId = params.netherNetId || params.NetherNetId || params.fromNetherNetId || params.FromNetherNetId || params.fromPlayerId || params.FromPlayerId
  if (networkId) signal.networkId = String(networkId)
  return signal
}

And in src/client.js, pass the protocol through:

this.nethernet.signalling = new NethernetSignal(
  this.connection.nethernet.networkId,
  this.options.authflow,
  this.options.version,
  { protocol: this.options.signallingProtocol }
)

The parseSignalMessage replacement for SignalStructure.fromString on inbound signals can handle both protocols, so the same code path works regardless of what the server sends.

I'd be happy to open a follow up pr against this branch if that'd be easier. Let me know! 🙂

@LucienHH
Copy link
Copy Markdown
Contributor Author

LucienHH commented May 9, 2026

Hi @yntha, appreciate the write up! Yeah please do that'd be very helpful as I haven't had time recently to work on this.

@yntha
Copy link
Copy Markdown

yntha commented May 10, 2026

#735

@extremeheat
Copy link
Copy Markdown
Member

Ready for merge into a dev branch?

@extremeheat
Copy link
Copy Markdown
Member

Looks like CI failing is that related to PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow bots to join friend worlds.

4 participants