Move protocol to node-nethernet

This commit is contained in:
LucienHH 2025-04-11 02:09:52 +01:00
commit bd11068a17
17 changed files with 5 additions and 911 deletions

View file

@ -21,7 +21,6 @@
],
"license": "MIT",
"dependencies": {
"@jsprismarine/jsbinaryutils": "^5.5.3",
"debug": "^4.3.1",
"json-bigint": "^1.0.0",
"jsonwebtoken": "^9.0.0",
@ -29,14 +28,13 @@
"minecraft-data": "^3.0.0",
"minecraft-folder-path": "^1.2.0",
"node-fetch": "^2.6.1",
"node-nethernet": "github:LucienHH/node-nethernet#protocol",
"prismarine-auth": "github:LucienHH/prismarine-auth#playfab",
"prismarine-nbt": "^2.0.0",
"prismarine-realms": "^1.1.0",
"protodef": "^1.14.0",
"raknet-native": "^1.0.3",
"sdp-transform": "^2.14.2",
"uuid-1345": "^1.0.2",
"werift": "^0.19.9",
"ws": "^8.18.0",
"xbox-rta": "^2.1.0"
},

View file

@ -4,7 +4,7 @@ const assert = require('assert')
const { getRandomUint64 } = require('./datatypes/util')
const { serverAuthenticate } = require('./client/auth')
const { SignalType } = require('./nethernet/signalling')
const { SignalType } = require('node-nethernet')
/** @param {{ port?: number, version?: number, networkId?: string, transport?: string, delayedInit?: boolean }} options */
function createServer (options) {

View file

@ -1,6 +1,5 @@
const { waitFor } = require('./datatypes/util')
const { Client } = require('./nethernet/client')
const { Server } = require('./nethernet/server')
const { Client, Server } = require('node-nethernet')
class NethernetClient {
constructor (options = {}) {

View file

@ -1,307 +0,0 @@
const dgram = require('node:dgram')
const { write } = require('sdp-transform')
const { EventEmitter } = require('node:events')
const { RTCIceCandidate, RTCPeerConnection } = require('werift')
const { Connection } = require('./connection')
const { SignalType, SignalStructure } = require('./signalling')
const { getBroadcastAddress } = require('./net')
const { PACKET_TYPE } = require('./discovery/packets/Packet')
const { RequestPacket } = require('./discovery/packets/RequestPacket')
const { MessagePacket } = require('./discovery/packets/MessagePacket')
const { ResponsePacket } = require('./discovery/packets/ResponsePacket')
const { decrypt, encrypt, calculateChecksum } = require('./discovery/crypto')
const { getRandomUint64 } = require('./util')
const debug = require('debug')('minecraft-protocol')
const PORT = 7551
const BROADCAST_ADDRESS = getBroadcastAddress()
class Client extends EventEmitter {
constructor (networkId) {
super()
this.serverNetworkId = networkId
this.networkId = getRandomUint64()
debug('C: Generated networkId:', this.networkId)
this.connectionId = getRandomUint64()
debug('C: Generated connectionId:', this.connectionId)
this.socket = dgram.createSocket('udp4')
this.socket.on('message', (buffer, rinfo) => {
debug('C: Received message from', rinfo.address, ':', rinfo.port)
this.processPacket(buffer, rinfo)
})
this.responses = new Map()
this.addresses = new Map()
this.credentials = []
this.signalHandler = this.sendDiscoveryMessage
this.sendDiscoveryRequest()
debug('C: Sent initial discovery request')
this.pingInterval = setInterval(() => {
debug('C: Sending periodic discovery request')
this.sendDiscoveryRequest()
}, 2000)
}
async handleCandidate (signal) {
debug('C: Handling ICE candidate signal:', signal)
await this.rtcConnection.addIceCandidate(new RTCIceCandidate({ candidate: signal.data }))
}
async handleAnswer (signal) {
debug('C: Handling answer signal:', signal)
await this.rtcConnection.setRemoteDescription({ type: 'answer', sdp: signal.data })
}
async createOffer () {
debug('C: Creating RTC offer')
this.rtcConnection = new RTCPeerConnection({
iceServers: this.credentials
})
this.connection = new Connection(this, this.connectionId, this.rtcConnection)
const candidates = []
this.rtcConnection.onicecandidate = (e) => {
if (e.candidate) {
debug('C: Collected ICE candidate:', e.candidate.candidate)
candidates.push(e.candidate.candidate)
}
}
this.connection.setChannels(
this.rtcConnection.createDataChannel('ReliableDataChannel'),
this.rtcConnection.createDataChannel('UnreliableDataChannel')
)
this.connection.reliable.onopen = () => { this.emit('connected', this.connection) }
this.rtcConnection.onconnectionstatechange = () => {
const state = this.rtcConnection.connectionState
debug('C: Connection state changed:', state)
if (state === 'disconnected') this.emit('disconnect', this.connectionId, 'disconnected')
}
await this.rtcConnection.createOffer()
const ice = this.rtcConnection.iceTransports[0]
const dtls = this.rtcConnection.dtlsTransports[0]
if (!ice || !dtls) {
debug('C: Failed to create ICE or DTLS transports')
throw new Error('Failed to create transports')
}
const iceParams = ice.iceGather.localParameters
const dtlsParams = dtls.localParameters
if (dtlsParams.fingerprints.length === 0) {
debug('C: No DTLS fingerprints available')
throw new Error('local DTLS parameters has no fingerprints')
}
const desc = write({
version: 0,
origin: {
username: '-',
sessionId: getRandomUint64().toString(),
sessionVersion: 2,
netType: 'IN',
ipVer: 4,
address: '127.0.0.1'
},
name: '-',
timing: { start: 0, stop: 0 },
groups: [{ type: 'BUNDLE', mids: '0' }],
extmapAllowMixed: 'extmap-allow-mixed',
msidSemantic: { semantic: '', token: 'WMS' },
media: [
{
rtp: [],
fmtp: [],
type: 'application',
port: 9,
protocol: 'UDP/DTLS/SCTP',
payloads: 'webrtc-datachannel',
connection: { ip: '0.0.0.0', version: 4 },
iceUfrag: iceParams.usernameFragment,
icePwd: iceParams.password,
iceOptions: 'trickle',
fingerprint: { type: dtlsParams.fingerprints[0].algorithm, hash: dtlsParams.fingerprints[0].value },
setup: 'active',
mid: '0',
sctpPort: 5000,
maxMessageSize: 65536
}
]
})
await this.rtcConnection.setLocalDescription({ type: 'offer', sdp: desc })
debug('C: Local SDP set:', desc)
this.signalHandler(
new SignalStructure(SignalType.ConnectRequest, this.connectionId, desc, this.serverNetworkId)
)
for (const candidate of candidates) {
debug('C: Sending ICE candidate signal:', candidate)
this.signalHandler(
new SignalStructure(SignalType.CandidateAdd, this.connectionId, candidate, this.serverNetworkId)
)
}
}
processPacket (buffer, rinfo) {
debug('C: Processing packet from', rinfo.address, ':', rinfo.port)
if (buffer.length < 32) {
debug('C: Received packet is too short')
throw new Error('Packet is too short')
}
const decryptedData = decrypt(buffer.slice(32))
const checksum = calculateChecksum(decryptedData)
if (Buffer.compare(buffer.slice(0, 32), checksum) !== 0) {
debug('C: Checksum mismatch for packet from', rinfo.address)
throw new Error('Checksum mismatch')
}
const packetType = decryptedData.readUInt16LE(2)
debug('C: Packet type:', packetType)
switch (packetType) {
case PACKET_TYPE.DISCOVERY_REQUEST:
debug('C: Received DISCOVERY_REQUEST packet')
break
case PACKET_TYPE.DISCOVERY_RESPONSE:
debug('C: Received DISCOVERY_RESPONSE packet')
this.handleResponse(new ResponsePacket(decryptedData).decode(), rinfo)
break
case PACKET_TYPE.DISCOVERY_MESSAGE:
debug('C: Received DISCOVERY_MESSAGE packet')
this.handleMessage(new MessagePacket(decryptedData).decode())
break
default:
debug('C: Unknown packet type:', packetType)
throw new Error('Unknown packet type')
}
}
handleResponse (packet, rinfo) {
debug('C: Handling discovery response from', rinfo.address, 'with data:', packet)
this.addresses.set(packet.senderId, rinfo)
this.responses.set(packet.senderId, packet.data)
this.emit('pong', packet)
}
handleMessage (packet) {
debug('C: Handling discovery message:', packet)
if (packet.data === 'Ping') {
debug('C: Ignoring ping message')
return
}
const signal = SignalStructure.fromString(packet.data)
signal.networkId = packet.senderId
debug('C: Processing signal:', signal)
this.handleSignal(signal)
}
handleSignal (signal) {
debug('C: Handling signal of type:', signal.type)
switch (signal.type) {
case SignalType.ConnectResponse:
debug('C: Handling ConnectResponse signal')
this.handleAnswer(signal)
break
case SignalType.CandidateAdd:
debug('C: Handling CandidateAdd signal')
this.handleCandidate(signal)
break
}
}
sendDiscoveryRequest () {
debug('C: Sending discovery request')
const requestPacket = new RequestPacket()
requestPacket.senderId = this.networkId
requestPacket.encode()
const buf = requestPacket.getBuffer()
const packetToSend = Buffer.concat([calculateChecksum(buf), encrypt(buf)])
this.socket.send(packetToSend, PORT, BROADCAST_ADDRESS)
}
sendDiscoveryMessage (signal) {
debug('C: Sending discovery message for signal:', signal)
const rinfo = this.addresses.get(signal.networkId)
if (!rinfo) {
debug('C: No address found for networkId:', signal.networkId)
return
}
const messagePacket = new MessagePacket()
messagePacket.senderId = this.networkId
messagePacket.recipientId = BigInt(signal.networkId)
messagePacket.data = signal.toString()
messagePacket.encode()
const buf = messagePacket.getBuffer()
const packetToSend = Buffer.concat([calculateChecksum(buf), encrypt(buf)])
this.socket.send(packetToSend, rinfo.port, rinfo.address)
}
async connect () {
debug('C: Initiating connection')
this.running = true
await this.createOffer()
}
send (buffer) {
this.connection.send(buffer)
}
ping () {
debug('C: Sending ping')
this.sendDiscoveryRequest()
}
close (reason) {
debug('C: Closing client with reason:', reason)
if (!this.running) return
clearInterval(this.pingInterval)
this.connection?.close()
setTimeout(() => this.socket.close(), 100)
this.connection = null
this.running = false
this.removeAllListeners()
}
}
module.exports = { Client }

View file

@ -1,113 +0,0 @@
const debug = require('debug')('minecraft-protocol')
const MAX_MESSAGE_SIZE = 10_000
class Connection {
constructor (nethernet, address, rtcConnection) {
this.nethernet = nethernet
this.address = address
this.rtcConnection = rtcConnection
this.reliable = null
this.unreliable = null
this.promisedSegments = 0
this.buf = Buffer.alloc(0)
}
setChannels (reliable, unreliable) {
if (reliable) {
this.reliable = reliable
this.reliable.onmessage = (msg) => this.handleMessage(msg.data)
}
if (unreliable) {
this.unreliable = unreliable
}
}
handleMessage (data) {
if (typeof data === 'string') {
data = Buffer.from(data)
}
if (data.length < 2) {
throw new Error('Unexpected EOF')
}
const segments = data[0]
debug(`handleMessage segments: ${segments}`)
data = data.subarray(1)
if (this.promisedSegments > 0 && this.promisedSegments - 1 !== segments) {
throw new Error(`Invalid promised segments: expected ${this.promisedSegments - 1}, got ${segments}`)
}
this.promisedSegments = segments
this.buf = this.buf ? Buffer.concat([this.buf, data]) : data
if (this.promisedSegments > 0) {
return
}
this.nethernet.emit('encapsulated', this.buf, this.address)
this.buf = null
}
send (data) {
if (!this.reliable || this.reliable.readyState !== 'open') {
throw new Error('Reliable data channel is not available')
}
let n = 0
if (typeof data === 'string') {
data = Buffer.from(data)
}
let segments = Math.ceil(data.length / MAX_MESSAGE_SIZE)
for (let i = 0; i < data.length; i += MAX_MESSAGE_SIZE) {
segments--
let end = i + MAX_MESSAGE_SIZE
if (end > data.length) end = data.length
const frag = data.subarray(i, end)
const message = Buffer.concat([Buffer.from([segments]), frag])
debug('Sending fragment', segments, 'header', message[0])
this.reliable.send(message)
n += frag.length
}
if (segments !== 0) {
throw new Error('Segments count did not reach 0 after sending all fragments')
}
return n
}
close () {
if (this.reliable) {
this.reliable.close()
}
if (this.unreliable) {
this.unreliable.close()
}
if (this.rtcConnection) {
this.rtcConnection.close()
}
}
}
module.exports = { Connection }

View file

@ -1,45 +0,0 @@
const BinaryStream = require('@jsprismarine/jsbinaryutils').default
class ServerData extends BinaryStream {
encode () {
this.writeByte(this.version)
this.writeString(this.motd)
this.writeString(this.levelName)
this.writeIntLE(this.gamemodeId)
this.writeIntLE(this.playerCount)
this.writeIntLE(this.playersMax)
this.writeBoolean(this.isEditorWorld)
this.writeBoolean(this.hardcore)
this.writeIntLE(this.transportLayer)
}
decode () {
this.version = this.readByte()
this.motd = this.readString()
this.levelName = this.readString()
this.gamemodeId = this.readIntLE()
this.playerCount = this.readIntLE()
this.playersMax = this.readIntLE()
this.isEditorWorld = this.readBoolean()
this.hardcore = this.readBoolean()
this.transportLayer = this.readIntLE()
}
readString () {
return this.read(this.readByte()).toString()
}
writeString (v) {
this.writeByte(Buffer.byteLength(v))
this.write(Buffer.from(v, 'utf-8'))
}
prependLength () {
const buf = Buffer.alloc(2)
buf.writeUInt16LE(this.binary.length, 0)
this.binary = [...buf, ...this.binary]
this.writeIndex += 2
}
}
module.exports = { ServerData }

View file

@ -1,30 +0,0 @@
const crypto = require('node:crypto')
const appIdBuffer = Buffer.allocUnsafe(8)
appIdBuffer.writeBigUInt64LE(BigInt(0xdeadbeef))
const AES_KEY = crypto.createHash('sha256')
.update(appIdBuffer)
.digest()
function encrypt (data) {
const cipher = crypto.createCipheriv('aes-256-ecb', AES_KEY, null)
return Buffer.concat([cipher.update(data), cipher.final()])
}
function decrypt (data) {
const decipher = crypto.createDecipheriv('aes-256-ecb', AES_KEY, null)
return Buffer.concat([decipher.update(data), decipher.final()])
}
function calculateChecksum (data) {
const hmac = crypto.createHmac('sha256', AES_KEY)
hmac.update(data)
return hmac.digest()
}
module.exports = {
encrypt,
decrypt,
calculateChecksum
}

View file

@ -1,31 +0,0 @@
const { PACKET_TYPE, Packet } = require('./Packet')
class MessagePacket extends Packet {
constructor (data) {
super(PACKET_TYPE.DISCOVERY_MESSAGE, data)
}
encode () {
super.encode()
this.writeUnsignedLongLE(this.recipientId)
this.writeUnsignedIntLE(this.data.length)
this.write(Buffer.from(this.data, 'utf-8'))
this.prependLength()
return this
}
decode () {
super.decode()
this.recipientId = this.readUnsignedLongLE()
const length = this.readUnsignedIntLE()
this.data = this.read(length).toString()
return this
}
}
module.exports = { MessagePacket }

View file

@ -1,37 +0,0 @@
const PACKET_TYPE = {
DISCOVERY_REQUEST: 0,
DISCOVERY_RESPONSE: 1,
DISCOVERY_MESSAGE: 2
}
const BinaryStream = require('@jsprismarine/jsbinaryutils').default
class Packet extends BinaryStream {
constructor (id, buffer) {
super(buffer)
this.id = id
}
encode () {
this.writeUnsignedShortLE(this.id)
this.writeUnsignedLongLE(this.senderId)
this.write(Buffer.alloc(8))
}
decode () {
this.packetLength = this.readUnsignedShortLE()
this.id = this.readUnsignedShortLE()
this.senderId = this.readUnsignedLongLE()
this.read(8)
}
prependLength () {
const buf = Buffer.alloc(2)
buf.writeUInt16LE(this.binary.length, 0)
this.binary = [...buf, ...this.binary]
this.writeIndex += 2
}
}
module.exports = { PACKET_TYPE, Packet }

View file

@ -1,23 +0,0 @@
const { PACKET_TYPE, Packet } = require('./Packet')
class RequestPacket extends Packet {
constructor (data) {
super(PACKET_TYPE.DISCOVERY_REQUEST, data)
}
encode () {
super.encode()
this.prependLength()
return this
}
decode () {
super.decode()
return this
}
}
module.exports = { RequestPacket }

View file

@ -1,29 +0,0 @@
const { PACKET_TYPE, Packet } = require('./Packet')
class ResponsePacket extends Packet {
constructor (data) {
super(PACKET_TYPE.DISCOVERY_RESPONSE, data)
}
encode () {
super.encode()
const hex = this.data.toString('hex')
this.writeUnsignedIntLE(hex.length)
this.write(Buffer.from(hex, 'utf-8'))
this.prependLength()
return this
}
decode () {
super.decode()
const length = this.readUnsignedIntLE()
this.data = Buffer.from(this.read(length).toString('utf-8'), 'hex')
return this
}
}
module.exports = { ResponsePacket }

View file

@ -1,22 +0,0 @@
const os = require('node:os')
function getBroadcastAddress () {
const interfaces = os.networkInterfaces()
for (const interfaceName in interfaces) {
for (const iface of interfaces[interfaceName]) {
// Only consider IPv4, non-internal (non-loopback) addresses
if (iface.family === 'IPv4' && !iface.internal) {
const ip = iface.address.split('.').map(Number)
const netmask = iface.netmask.split('.').map(Number)
const broadcast = ip.map((octet, i) => (octet | (~netmask[i] & 255)))
return broadcast.join('.') // Return the broadcast address
}
}
}
}
module.exports = {
getBroadcastAddress
}

View file

@ -1,229 +0,0 @@
const dgram = require('node:dgram')
const { EventEmitter } = require('node:events')
const { RTCIceCandidate, RTCPeerConnection } = require('werift')
const { Connection } = require('./connection')
const { SignalStructure, SignalType } = require('./signalling')
const { PACKET_TYPE } = require('./discovery/packets/Packet')
const { MessagePacket } = require('./discovery/packets/MessagePacket')
const { ResponsePacket } = require('./discovery/packets/ResponsePacket')
const { decrypt, encrypt, calculateChecksum } = require('./discovery/crypto')
const { getRandomUint64 } = require('./util')
const debug = require('debug')('minecraft-protocol')
class Server extends EventEmitter {
constructor (options = {}) {
super()
this.options = options
this.networkId = options.networkId ?? getRandomUint64()
this.connections = new Map()
debug('S: Server initialised with networkId: %s', this.networkId)
}
async handleCandidate (signal) {
const conn = this.connections.get(signal.connectionId)
if (conn) {
debug('S: Adding ICE candidate for connectionId: %s', signal.connectionId)
await conn.rtcConnection.addIceCandidate(new RTCIceCandidate({ candidate: signal.data }))
} else {
debug('S: Received candidate for unknown connection', signal)
}
}
async handleOffer (signal, respond, credentials = []) {
debug('S: Handling offer for connectionId: %s', signal.connectionId)
const rtcConnection = new RTCPeerConnection({
iceServers: credentials
})
const connection = new Connection(this, signal.connectionId, rtcConnection)
this.connections.set(signal.connectionId, connection)
rtcConnection.onicecandidate = (e) => {
if (e.candidate) {
debug('S: ICE candidate generated for connectionId: %s', signal.connectionId)
respond(
new SignalStructure(SignalType.CandidateAdd, signal.connectionId, e.candidate.candidate, signal.networkId)
)
}
}
rtcConnection.ondatachannel = ({ channel }) => {
debug('S: Data channel established with label: %s', channel.label)
if (channel.label === 'ReliableDataChannel') connection.setChannels(channel)
if (channel.label === 'UnreliableDataChannel') connection.setChannels(null, channel)
}
rtcConnection.onconnectionstatechange = () => {
const state = rtcConnection.connectionState
debug('S: Connection state changed for connectionId: %s, state: %s', signal.connectionId, state)
if (state === 'connected') this.emit('openConnection', connection)
if (state === 'disconnected') this.emit('closeConnection', signal.connectionId, 'disconnected')
}
await rtcConnection.setRemoteDescription({ type: 'offer', sdp: signal.data })
debug('S: Remote description set for connectionId: %s', signal.connectionId)
const answer = await rtcConnection.createAnswer()
await rtcConnection.setLocalDescription(answer)
debug('S: Local description set (answer) for connectionId: %s', signal.connectionId)
respond(
new SignalStructure(SignalType.ConnectResponse, signal.connectionId, answer.sdp, signal.networkId)
)
}
processPacket (buffer, rinfo) {
debug('S: Processing packet from %s:%s', rinfo.address, rinfo.port)
if (buffer.length < 32) {
debug('S: Packet is too short')
throw new Error('Packet is too short')
}
const decryptedData = decrypt(buffer.slice(32))
const checksum = calculateChecksum(decryptedData)
if (Buffer.compare(buffer.slice(0, 32), checksum) !== 0) {
debug('S: Checksum mismatch')
throw new Error('Checksum mismatch')
}
const packetType = decryptedData.readUInt16LE(2)
debug('S: Packet type: %s', packetType)
switch (packetType) {
case PACKET_TYPE.DISCOVERY_REQUEST:
debug('S: Handling discovery request')
this.handleRequest(rinfo)
break
case PACKET_TYPE.DISCOVERY_RESPONSE:
debug('S: Discovery response received (ignored)')
break
case PACKET_TYPE.DISCOVERY_MESSAGE:
debug('S: Handling discovery message')
this.handleMessage(new MessagePacket(decryptedData).decode(), rinfo)
break
default:
debug('S: Unknown packet type: %s', packetType)
throw new Error('Unknown packet type')
}
}
setAdvertisement (buffer) {
debug('S: Setting advertisement data')
this.advertisement = buffer
}
handleRequest (rinfo) {
debug('S: Handling request from %s:%s', rinfo.address, rinfo.port)
const data = this.advertisement
if (!data) {
debug('S: Advertisement data not set')
return new Error('Advertisement data not set yet')
}
const responsePacket = new ResponsePacket()
responsePacket.senderId = this.networkId
responsePacket.data = data
responsePacket.encode()
const buf = responsePacket.getBuffer()
const packetToSend = Buffer.concat([calculateChecksum(buf), encrypt(buf)])
this.socket.send(packetToSend, rinfo.port, rinfo.address)
debug('S: Response sent to %s:%s', rinfo.address, rinfo.port)
}
handleMessage (packet, rinfo) {
debug('S: Handling message from %s:%s', rinfo.address, rinfo.port)
if (packet.data === 'Ping') {
debug('S: Ping message received')
return
}
const respond = (signal) => {
debug('S: Responding with signal: %o', signal)
const messagePacket = new MessagePacket()
messagePacket.senderId = this.networkId
messagePacket.recipientId = signal.networkId
messagePacket.data = signal.toString()
messagePacket.encode()
const buf = messagePacket.getBuffer()
const packetToSend = Buffer.concat([calculateChecksum(buf), encrypt(buf)])
this.socket.send(packetToSend, rinfo.port, rinfo.address)
debug('S: Signal response sent to %s:%s', rinfo.address, rinfo.port)
}
const signal = SignalStructure.fromString(packet.data)
signal.networkId = packet.senderId
switch (signal.type) {
case SignalType.ConnectRequest:
debug('S: Handling ConnectRequest signal')
this.handleOffer(signal, respond)
break
case SignalType.CandidateAdd:
debug('S: Handling CandidateAdd signal')
this.handleCandidate(signal)
break
}
}
async listen () {
debug('S: Starting server')
this.socket = dgram.createSocket('udp4')
this.socket.on('message', (buffer, rinfo) => {
debug('S: Message received from %s:%s', rinfo.address, rinfo.port)
this.processPacket(buffer, rinfo)
})
await new Promise((resolve, reject) => {
const failFn = e => reject(e)
this.socket.once('error', failFn)
this.socket.bind(7551, () => {
debug('S: Server is listening on port 7551')
this.socket.removeListener('error', failFn)
resolve(true)
})
})
}
send (buffer) {
this.connection.send(buffer)
}
close (reason) {
debug('S: Closing server: %s', reason)
for (const conn of this.connections.values()) {
conn.close()
}
this.socket.close(() => {
debug('S: Server closed')
this.emit('close', reason)
this.removeAllListeners()
})
}
}
module.exports = { Server }

View file

@ -1,27 +0,0 @@
const SignalType = {
ConnectRequest: 'CONNECTREQUEST',
ConnectResponse: 'CONNECTRESPONSE',
CandidateAdd: 'CANDIDATEADD',
ConnectError: 'CONNECTERROR'
}
class SignalStructure {
constructor (type, connectionId, data, networkId) {
this.type = type
this.connectionId = connectionId
this.data = data
this.networkId = networkId
}
toString () {
return `${this.type} ${this.connectionId} ${this.data}`
}
static fromString (message) {
const [type, connectionId, ...data] = message.split(' ')
return new this(type, BigInt(connectionId), data.join(' '))
}
}
module.exports = { SignalStructure, SignalType }

View file

@ -1,10 +0,0 @@
const getRandomUint64 = () => {
const high = Math.floor(Math.random() * 0xFFFFFFFF)
const low = Math.floor(Math.random() * 0xFFFFFFFF)
return (BigInt(high) << 32n) | BigInt(low)
}
module.exports = {
getRandomUint64
}

View file

@ -1,6 +1,6 @@
const { Versions, CURRENT_VERSION } = require('../options')
const { ServerData } = require('../nethernet/discovery/ServerData')
const { ServerData } = require('node-nethernet')
class NethernetServerAdvertisement {
version = 3

View file

@ -1,7 +1,7 @@
const { WebSocket } = require('ws')
const { stringify } = require('json-bigint')
const { once, EventEmitter } = require('node:events')
const { SignalStructure } = require('../nethernet/signalling')
const { SignalStructure } = require('node-nethernet')
const debug = require('debug')('minecraft-protocol')