node-minecraft-protocol/test/clientTest.js
William Gaylord 1d9a38253a
1.20.3 / 1.20.4 support (#1275)
* Inital 1.20.3 update, bring in minecraft-data with 1.20.3 support and add it to the versions

* Add data for packetTest for explosion packet.

* Add 1.20.4 to version list.

* Add fix for 1.20.3+ NBT isntead of strings for chat. Not happy with this but it does work.

* Fix linting

* Update version.js

Remove 1.20.3 since its the same as 1.20.4. (As suggested)

* Comment how handleNBTStrings works.

* Removed debug console.log

* Update README.md

* chat packet nbt handling fix, use feature

* big endian UUID, add back `text` wrapper

* use prismarine-chat in client test

* expose _handleNbtComponent

* use prismarine-chat exposed processNbtMessage

* fix pre-1.20.4

* Update package.json

* Update server.js

* Update server.js

add missing import

* update server hello world

---------

Co-authored-by: Romain Beaumont <romain.rom1@gmail.com>
Co-authored-by: extremeheat <extreme@protonmail.ch>
2024-02-26 01:14:49 +01:00

344 lines
13 KiB
JavaScript

/* eslint-env mocha */
const mc = require('../')
const os = require('os')
const fs = require('fs')
const path = require('path')
const assert = require('power-assert')
const util = require('util')
const applyClientHelpers = require('./common/clientHelpers')
const download = util.promisify(require('minecraft-wrap').download)
const { getPort } = require('./common/util')
const SURVIVE_TIME = 10000
const MC_SERVER_PATH = path.join(__dirname, 'server')
const Wrap = require('minecraft-wrap').Wrap
for (const supportedVersion of mc.supportedVersions) {
let PORT = null
const mcData = require('minecraft-data')(supportedVersion)
const version = mcData.version
const MC_SERVER_JAR_DIR = process.env.MC_SERVER_JAR_DIR || os.tmpdir()
const MC_SERVER_JAR = MC_SERVER_JAR_DIR + '/minecraft_server.' + version.minecraftVersion + '.jar'
const MC_SERVER_DIR = MC_SERVER_PATH + '_' + supportedVersion
const wrap = new Wrap(MC_SERVER_JAR, MC_SERVER_DIR, {
minMem: 1024,
maxMem: 1024
})
wrap.on('line', function (line) {
console.log(line)
})
describe('client ' + supportedVersion + 'v', function () {
this.timeout(10 * 60 * 1000)
before(async function () {
this.timeout(30 * 1000)
await download(version.minecraftVersion, MC_SERVER_JAR)
PORT = await getPort()
console.log(`Port chosen: ${PORT}`)
})
after(async () => {
await new Promise((resolve, reject) => {
wrap.deleteServerData(err => {
if (err) reject(err)
resolve()
})
})
})
describe('offline', function () {
this.timeout(240 * 1000)
before(async () => {
console.log(new Date() + 'starting server ' + version.minecraftVersion)
await new Promise((resolve, reject) => {
wrap.startServer({
'online-mode': 'false',
'server-port': PORT,
motd: 'test1234',
'max-players': 120,
// 'level-type': 'flat',
'use-native-transport': 'false' // java 16 throws errors without this, https://www.spigotmc.org/threads/unable-to-access-address-of-buffer.311602
}, (err) => {
if (err) reject(err)
resolve()
})
})
console.log(new Date() + 'started server ' + version.minecraftVersion)
})
after(function (done) {
console.log(new Date() + 'stopping server' + version.minecraftVersion)
wrap.stopServer(function (err) {
if (err) { console.log(err) }
console.log(new Date() + 'stopped server ' + version.minecraftVersion)
done(err)
})
})
it('pings the server', function (done) {
mc.ping({
version: version.minecraftVersion,
port: PORT
}, function (err, results) {
if (err) return done(err)
assert.ok(results.latency >= 0)
assert.ok(results.latency <= 1000)
delete results.latency
delete results.favicon // too lazy to figure it out
/* assert.deepEqual(results, {
version: {
name: '1.7.4',
protocol: 4
},
description: { text: "test1234" }
}); */
done()
})
})
it('connects successfully - offline mode', function (done) {
const client = applyClientHelpers(mc.createClient({
username: 'Player',
version: version.minecraftVersion,
port: PORT,
auth: 'offline'
}))
client.on('error', err => done(err))
const lineListener = function (line) {
const match = line.match(/\[Server thread\/INFO\]: (?:\[Not Secure\] )?<(.+?)> (.+)/)
if (!match) return
assert.strictEqual(match[1], 'Player')
assert.strictEqual(match[2], 'hello everyone; I have logged in.')
wrap.writeServer('say hello\n')
wrap.off('line', lineListener)
}
wrap.on('line', lineListener)
let chatCount = 0
client.on('login', function (packet) {
assert.strictEqual(packet.gameMode, 0)
client.chat('hello everyone; I have logged in.')
})
// Dump some data for easier debugging
client.on('raw.registry_data', (buffer) => {
fs.writeFileSync(MC_SERVER_DIR + '_registry_data.bin', buffer)
})
client.on('registry_data', (json) => {
fs.writeFileSync(MC_SERVER_DIR + '_registry_data.json', JSON.stringify(json))
})
client.on('login', (packet) => {
fs.writeFileSync(MC_SERVER_DIR + '_login.json', JSON.stringify(packet))
if (fs.existsSync(MC_SERVER_DIR + '_registry_data.json')) {
// generate a loginPacket.json for minecraft-data
fs.writeFileSync(MC_SERVER_DIR + '_loginPacket.json', JSON.stringify({
...packet,
dimensionCodec: JSON.parse(fs.readFileSync(MC_SERVER_DIR + '_registry_data.json')).codec
}, null, 2))
}
})
client.on('playerChat', function (data) {
chatCount += 1
assert.ok(chatCount <= 2)
if (!mcData.supportFeature('clientsideChatFormatting')) {
const message = JSON.parse(data.formattedMessage)
if (chatCount === 1) {
assert.strictEqual(message.translate, 'chat.type.text')
assert.deepEqual(message.with[0].clickEvent, {
action: 'suggest_command',
value: mcData.version.version > 340 ? '/tell Player ' : '/msg Player '
})
assert.deepEqual(message.with[0].text, 'Player')
assert.strictEqual(message.with[1], 'hello everyone; I have logged in.')
} else if (chatCount === 2) {
assert.strictEqual(message.translate, 'chat.type.announcement')
assert.strictEqual(message.with[0].text ? message.with[0].text : message.with[0], 'Server')
assert.deepEqual(message.with[1].extra
? (message.with[1].extra[0].text
? message.with[1].extra[0].text
: message.with[1].extra[0])
: message.with[1].text, 'hello')
wrap.removeListener('line', lineListener)
client.end()
done()
}
} else {
// 1.19+
console.log('Chat Message', data)
const sender = JSON.parse(data.senderName)
const msgPayload = data.formattedMessage ? JSON.parse(data.formattedMessage) : data.plainMessage
const plainMessage = client.parseMessage(msgPayload).toString()
if (chatCount === 1) {
assert.strictEqual(plainMessage, 'hello everyone; I have logged in.')
assert.deepEqual(sender.clickEvent, {
action: 'suggest_command',
value: '/tell Player '
})
assert.strictEqual(sender.text, 'Player')
} else if (chatCount === 2) {
const plainSender = client.parseMessage(sender).toString()
assert.strictEqual(plainMessage, 'hello')
assert.strictEqual(plainSender, 'Server')
wrap.removeListener('line', lineListener)
client.end()
done()
}
}
})
client.on('systemChat', function (data) {
// For 1.7.10
chatCount += 1
assert.ok(chatCount <= 2)
const message = JSON.parse(data.formattedMessage)
if (chatCount === 1) {
assert.strictEqual(message.translate, 'chat.type.text')
assert.deepEqual(message.with[0].clickEvent, {
action: 'suggest_command',
value: mcData.version.version > 340 ? '/tell Player ' : '/msg Player '
})
assert.deepEqual(message.with[0].text, 'Player')
assert.strictEqual(message.with[1], 'hello everyone; I have logged in.')
} else if (chatCount === 2) {
assert.strictEqual(message.translate, 'chat.type.announcement')
assert.strictEqual(message.with[0].text ? message.with[0].text : message.with[0], 'Server')
assert.deepEqual(message.with[1].extra
? (message.with[1].extra[0].text
? message.with[1].extra[0].text
: message.with[1].extra[0])
: message.with[1].text, 'hello')
wrap.removeListener('line', lineListener)
client.end()
done()
}
})
})
it('does not crash for ' + SURVIVE_TIME + 'ms', function (done) {
const client = applyClientHelpers(mc.createClient({
username: 'Player',
version: version.minecraftVersion,
port: PORT,
auth: 'offline'
}))
client.on('error', err => done(err))
client.on('login', function () {
client.chat('hello everyone; I have logged in.')
setTimeout(function () {
client.end()
done()
}, SURVIVE_TIME)
})
})
it('produce a decent error when connecting with the wrong version', function (done) {
if (process.platform === 'win32') return done()
const client = mc.createClient({
username: 'Player',
version: version.minecraftVersion === '1.8.8' ? '1.11.2' : '1.8.8',
port: PORT,
auth: 'offline'
})
client.once('error', function (err) {
if (err.message.startsWith('This server is version')) {
console.log(new Date() + 'Correctly got an error for wrong version : ' + err.message)
client.end()
done()
} else {
client.end()
done(err)
}
client.on('error', err => {
if (err.message.indexOf('ECONNRESET') === -1) {
done(err)
}
})
})
})
})
describe.skip('online', function () {
before(function (done) {
console.log(new Date() + 'starting server ' + version.minecraftVersion)
wrap.startServer({
'online-mode': 'true',
'server-port': PORT,
'use-native-transport': 'false' // java 16 throws errors without this, https://www.spigotmc.org/threads/unable-to-access-address-of-buffer.311602
}, function (err) {
if (err) { console.log(err) }
console.log(new Date() + 'started server ' + version.minecraftVersion)
done(err)
})
})
after(function (done) {
console.log(new Date() + 'stopping server ' + version.minecraftVersion)
wrap.stopServer(function (err) {
if (err) { console.log(err) }
console.log(new Date() + 'stopped server ' + version.minecraftVersion)
done(err)
})
})
it('connects successfully - online mode', function (done) {
const client = applyClientHelpers(mc.createClient({
username: process.env.MC_USERNAME,
password: process.env.MC_PASSWORD,
version: version.minecraftVersion,
port: PORT
}))
client.on('error', err => done(err))
const lineListener = function (line) {
const match = line.match(/\[Server thread\/INFO\]: <(.+?)> (.+)/)
if (!match) return
assert.strictEqual(match[1], client.username)
assert.strictEqual(match[2], 'hello everyone; I have logged in.')
wrap.writeServer('say hello\n')
}
wrap.on('line', lineListener)
client.on('login', function (packet) {
assert.strictEqual(packet.levelType, 'default')
assert.strictEqual(packet.difficulty, 1)
assert.strictEqual(packet.dimension, 0)
assert.strictEqual(packet.gameMode, 0)
client.chat('hello everyone; I have logged in.')
})
let chatCount = 0
client.on('chat', function (packet) {
chatCount += 1
assert.ok(chatCount <= 2)
if (chatCount === 2) {
client.removeAllListeners('chat')
wrap.removeListener('line', lineListener)
client.end()
done()
}
})
})
it('gets kicked when no credentials supplied in online mode', function (done) {
const client = mc.createClient({
username: 'Player',
version: version.minecraftVersion,
port: PORT,
auth: 'offline'
})
client.on('error', err => done(err))
let gotKicked = false
client.on('disconnect', function (packet) {
assert.ok(packet.reason.indexOf('"Failed to verify username!"') !== -1 || packet.reason.indexOf('multiplayer.disconnect.unverified_username') !== -1)
gotKicked = true
})
client.on('end', function () {
assert.ok(gotKicked)
client.end()
done()
})
})
})
})
}