Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/client/autoVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ module.exports = function (client, options) {
.filter(function (info) { return info })
.sort(function (a, b) { return b.version - a.version })
const versions = (minecraftData.postNettyVersionsByProtocolVersion.pc[protocolVersion] || []).concat(guessFromName)
.filter(info => minecraftData(info.minecraftVersion)?.version.version === protocolVersion)
if (versions.length === 0) {
client.emit('error', new Error(`Unsupported protocol version '${protocolVersion}'; try updating your packages with 'npm update'`))
return client.emit('error', new Error(`Unsupported protocol version '${protocolVersion}'; try updating your packages with 'npm update'`))
}
const minecraftVersion = versions[0].minecraftVersion

Expand Down
8 changes: 4 additions & 4 deletions src/transforms/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const protocols = {}

function createProtocol (state, direction, version, customPackets, compiled = true) {
const key = `${state};${direction};${version}${compiled ? ';c' : ''}`
if (protocols[key]) { return protocols[key] }
if (!customPackets && protocols[key]) { return protocols[key] }

const mcData = minecraftData(version)
const versionInfo = minecraftData.versionsByMinecraftVersion.pc[version]
Expand All @@ -24,23 +24,23 @@ function createProtocol (state, direction, version, customPackets, compiled = tr
throw new Error(`Unsupported protocol version '${versionInfo.version}' (attempted to use '${mcData.version.version}' data); try updating your packages with 'npm update'`)
}

const mergedProtocol = merge(mcData.protocol, customPackets?.[mcData.version.majorVersion] ?? {})
const mergedProtocol = merge({}, mcData.protocol, customPackets?.[mcData.version.majorVersion] ?? {})

if (compiled) {
const compiler = new ProtoDefCompiler()
compiler.addTypes(require('../datatypes/compiler-minecraft'))
compiler.addProtocol(mergedProtocol, [state, direction])
nbt.addTypesToCompiler('big', compiler)
const proto = compiler.compileProtoDefSync()
protocols[key] = proto
if (!customPackets) protocols[key] = proto
return proto
}

const proto = new ProtoDef(false)
proto.addTypes(minecraft)
proto.addProtocol(mergedProtocol, [state, direction])
nbt.addTypesToInterperter('big', proto)
protocols[key] = proto
if (!customPackets) protocols[key] = proto
return proto
}

Expand Down
54 changes: 54 additions & 0 deletions test/autoVersionSchemaTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-env mocha */
const assert = require('assert')
const { EventEmitter } = require('events')
const minecraftData = require('minecraft-data')
const { supportedVersions } = require('../')

describe('automatic version schema selection', () => {
const pingPath = require.resolve('../src/ping')
const autoPath = require.resolve('../src/client/autoVersion')
let originalPing
let response
beforeEach(() => {
originalPing = require.cache[pingPath]
require.cache[pingPath] = { exports: (options, callback) => callback(null, response) }
delete require.cache[autoPath]
})
afterEach(() => {
if (originalPing) require.cache[pingPath] = originalPing
else delete require.cache[pingPath]
delete require.cache[autoPath]
})

it('selects an actual protocol 5 schema instead of a snapshot alias with protocol 47 data', () => {
response = { version: { name: '1.7.10', protocol: 5 } }
const client = new EventEmitter()
const options = {}
let allowed = false
client.once('connect_allowed', () => { allowed = true })
require('../src/client/autoVersion')(client, options)
assert.equal(options.version, '1.7.10')
assert.equal(client.version, '1.7.10')
assert.equal(allowed, true)
})

it('reports unsupported schemas without proceeding to connect', () => {
response = { version: { name: 'unknown', protocol: -987654 } }
const client = new EventEmitter()
let error
client.once('error', value => { error = value })
client.once('connect_allowed', () => assert.fail('unsupported version must not connect'))
require('../src/client/autoVersion')(client, {})
assert.match(error.message, /Unsupported protocol version/)
})

for (const version of supportedVersions) {
it(`selects matching wire data for supported release ${version}`, () => {
const protocol = minecraftData(version).version.version
response = { version: { name: version, protocol } }
const client = new EventEmitter()
require('../src/client/autoVersion')(client, {})
assert.equal(minecraftData(client.version).version.version, protocol)
})
}
})
47 changes: 47 additions & 0 deletions test/customProtocolTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-env mocha */
const assert = require('assert')
const minecraftData = require('minecraft-data')

describe('custom protocol isolation', () => {
let createSerializer
const options = { version: '1.20.1', state: 'play', isServer: false }
const custom = type => ({ '1.20': { types: { custom_probe: type } } })

beforeEach(() => {
delete require.cache[require.resolve('../src/transforms/serializer')]
createSerializer = require('../src/transforms/serializer').createSerializer
})
afterEach(() => {
delete minecraftData('1.20.1').protocol.types.custom_probe
})

it('does not merge custom types into shared minecraft-data', () => {
const original = JSON.stringify(minecraftData('1.20.1').protocol)
createSerializer({ ...options, customPackets: custom('u8') })
assert.equal(JSON.stringify(minecraftData('1.20.1').protocol), original)
})

it('does not reuse a vanilla cache entry for custom packets', () => {
const vanilla = createSerializer(options)
const modified = createSerializer({ ...options, customPackets: custom('u16') })
assert.notStrictEqual(modified.proto, vanilla.proto)
assert.deepStrictEqual(modified.proto.createPacketBuffer('custom_probe', 0x1234), Buffer.from('1234', 'hex'))
})

it('isolates clients with different custom schemas', () => {
const first = createSerializer({ ...options, customPackets: custom('u8') })
const second = createSerializer({ ...options, customPackets: custom('u16') })
assert.deepStrictEqual(first.proto.createPacketBuffer('custom_probe', 0x12), Buffer.from('12', 'hex'))
assert.deepStrictEqual(second.proto.createPacketBuffer('custom_probe', 0x1234), Buffer.from('1234', 'hex'))
})

it('uses updated custom definitions while retaining the vanilla cache', () => {
const definitions = custom('u8')
const first = createSerializer({ ...options, customPackets: definitions })
definitions['1.20'].types.custom_probe = 'u16'
const second = createSerializer({ ...options, customPackets: definitions })
assert.deepStrictEqual(first.proto.createPacketBuffer('custom_probe', 1), Buffer.from('01', 'hex'))
assert.deepStrictEqual(second.proto.createPacketBuffer('custom_probe', 1), Buffer.from('0001', 'hex'))
assert.strictEqual(createSerializer(options).proto, createSerializer(options).proto)
})
})
Loading