minecraft-protocol
Version:
Parse and serialize minecraft packets, plus authentication and encryption.
110 lines (97 loc) • 3.58 kB
JavaScript
const ProtoDef = require('protodef').ProtoDef
const minecraft = require('../datatypes/minecraft')
const debug = require('debug')('minecraft-protocol')
const nbt = require('prismarine-nbt')
module.exports = function (client, options) {
const mcdata = require('minecraft-data')(options.version || require('../version').defaultVersion)
const channels = []
const proto = new ProtoDef(options.validateChannelProtocol ?? true)
nbt.addTypesToInterpreter('big', proto)
proto.addTypes(mcdata.protocol.types)
proto.addTypes(minecraft)
proto.addType('registerarr', [readDumbArr, writeDumbArr, sizeOfDumbArr])
client.registerChannel = registerChannel
client.unregisterChannel = unregisterChannel
client.writeChannel = writeChannel
const above385 = mcdata.version.version >= 385
if (above385) { // 1.13-pre3 (385) added Added Login Plugin Message (https://wiki.vg/Protocol_History#1.13-pre3)
client.on('login_plugin_request', onLoginPluginRequest)
}
const channelNames = above385 ? ['minecraft:register', 'minecraft:unregister'] : ['REGISTER', 'UNREGISTER']
client.registerChannel(channelNames[0], ['registerarr', []])
client.registerChannel(channelNames[1], ['registerarr', []])
function registerChannel (name, parser, custom) {
if (custom) {
client.writeChannel(channelNames[0], [name])
}
if (parser) proto.addType(name, parser)
channels.push(name)
if (channels.length === 1) { client.on('custom_payload', onCustomPayload) }
}
function unregisterChannel (channel, custom) {
if (custom) {
client.writeChannel(channelNames[1], [channel])
}
const index = channels.find(function (name) {
return channel === name
})
if (index) {
proto.types[channel] = undefined
channels.splice(index, 1)
if (channels.length === 0) { client.removeListener('custom_payload', onCustomPayload) }
}
}
function onCustomPayload (packet) {
const channel = channels.find(function (channel) {
return channel === packet.channel
})
if (channel) {
if (proto.types[channel]) {
try {
packet.data = proto.parsePacketBuffer(channel, packet.data).data
} catch (error) {
client.emit('error', error)
return
}
}
debug('read custom payload ' + channel + ' ' + packet.data)
client.emit(channel, packet.data)
}
}
function onLoginPluginRequest (packet) {
client.write('login_plugin_response', { // write that login plugin request is not understood, just like the Notchian client
messageId: packet.messageId
})
}
function writeChannel (channel, params) {
debug('write custom payload ' + channel + ' ' + params)
client.write('custom_payload', {
channel,
data: proto.createPacketBuffer(channel, params)
})
}
function readDumbArr (buf, offset) {
const ret = {
value: [],
size: 0
}
let results
while (offset < buf.length) {
if (buf.indexOf(0x0, offset) === -1) { results = this.read(buf, offset, 'restBuffer', {}) } else { results = this.read(buf, offset, 'cstring', {}) }
ret.size += results.size
ret.value.push(results.value.toString())
offset += results.size
}
return ret
}
function writeDumbArr (value, buf, offset) {
// TODO: Remove trailing \0
value.forEach(function (v) {
offset += proto.write(v, buf, offset, 'cstring')
})
return offset
}
function sizeOfDumbArr (value) {
return value.reduce((acc, v) => acc + this.sizeOf(v, 'cstring', {}), 0)
}
}