Skip to content

Commit

Permalink
1.20.3 / 1.20.4 support (#1275)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Feb 26, 2024
1 parent f97a236 commit 1d9a382
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 35 deletions.
2 changes: 1 addition & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ The client's protocol version

### client.version

The client's version
The client's version, as a string

### `packet` event

Expand Down
48 changes: 36 additions & 12 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Parse and serialize minecraft packets, plus authentication and encryption.

* Supports Minecraft PC version 1.7.10, 1.8.8, 1.9 (15w40b, 1.9, 1.9.1-pre2, 1.9.2, 1.9.4),
1.10 (16w20a, 1.10-pre1, 1.10, 1.10.1, 1.10.2), 1.11 (16w35a, 1.11, 1.11.2), 1.12 (17w15a, 17w18b, 1.12-pre4, 1.12, 1.12.1, 1.12.2), and 1.13 (17w50a, 1.13, 1.13.1, 1.13.2-pre1, 1.13.2-pre2, 1.13.2), 1.14 (1.14, 1.14.1, 1.14.3, 1.14.4)
, 1.15 (1.15, 1.15.1, 1.15.2) and 1.16 (20w13b, 20w14a, 1.16-rc1, 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4, 1.16.5), 1.17 (21w07a, 1.17, 1.17.1), 1.18 (1.18, 1.18.1 and 1.18.2), 1.19 (1.19, 1.19.1, 1.19.2, 1.19.3, 1.19.4), 1.20 (1.20, 1.20.1, 1.20.2)
, 1.15 (1.15, 1.15.1, 1.15.2) and 1.16 (20w13b, 20w14a, 1.16-rc1, 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4, 1.16.5), 1.17 (21w07a, 1.17, 1.17.1), 1.18 (1.18, 1.18.1 and 1.18.2), 1.19 (1.19, 1.19.1, 1.19.2, 1.19.3, 1.19.4), 1.20 (1.20, 1.20.1, 1.20.2, 1.20.3 and 1.20.4)
* Parses all packets and emits events with packet fields as JavaScript
objects.
* Send a packet by supplying fields as a JavaScript object.
Expand Down Expand Up @@ -118,16 +118,23 @@ const client = mc.createClient({
For a more up to date example, see examples/server/server.js.

```js
const mc = require('minecraft-protocol');
const mc = require('minecraft-protocol')
const nbt = require('prismarine-nbt')
const server = mc.createServer({
'online-mode': true, // optional
encryption: true, // optional
host: '0.0.0.0', // optional
port: 25565, // optional
version: '1.16.3'
});
version: '1.20.4'
})
const mcData = require('minecraft-data')(server.version)

function chatText (text) {
return mcData.supportFeature('chatPacketsUseNbtComponents')
? nbt.comp({ text: nbt.string(text) })
: JSON.stringify({ text })
}

server.on('playerJoin', function(client) {
const loginPacket = mcData.loginPacket

Expand All @@ -141,7 +148,7 @@ server.on('playerJoin', function(client) {
enableRespawnScreen: true,
isDebug: false,
isFlat: false
});
})

client.write('position', {
x: 0,
Expand All @@ -150,18 +157,35 @@ server.on('playerJoin', function(client) {
yaw: 0,
pitch: 0,
flags: 0x00
});
})

const msg = {
const message = {
translate: 'chat.type.announcement',
"with": [
with: [
'Server',
'Hello, world!'
]
};

client.write("chat", { message: JSON.stringify(msg), position: 0, sender: '0' });
});
}
if (mcData.supportFeature('signedChat')) {
client.write('player_chat', {
plainMessage: message,
signedChatContent: '',
unsignedChatContent: chatText(message),
type: 0,
senderUuid: 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43', // random
senderName: JSON.stringify({ text: 'me' }),
senderTeam: undefined,
timestamp: Date.now(),
salt: 0n,
signature: mcData.supportFeature('useChatSessions') ? undefined : Buffer.alloc(0),
previousMessages: [],
filterType: 0,
networkName: JSON.stringify({ text: 'me' })
})
} else {
client.write('chat', { message: JSON.stringify({ text: message }), position: 0, sender: 'me' })
}
})
```

## Testing
Expand Down
8 changes: 7 additions & 1 deletion examples/server/server.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const mc = require('minecraft-protocol')
const nbt = require('prismarine-nbt')

const options = {
motd: 'Vox Industries',
Expand All @@ -10,6 +11,11 @@ const options = {
const server = mc.createServer(options)
const mcData = require('minecraft-data')(server.version)
const loginPacket = mcData.loginPacket
function chatText (text) {
return mcData.supportFeature('chatPacketsUseNbtComponents')
? nbt.comp({ text: nbt.string(text) })
: JSON.stringify({ text })
}

server.on('playerJoin', function (client) {
broadcast(client.username + ' joined the game.')
Expand Down Expand Up @@ -67,7 +73,7 @@ function sendBroadcastMessage (server, clients, message, sender) {
server.writeToClients(clients, 'player_chat', {
plainMessage: message,
signedChatContent: '',
unsignedChatContent: JSON.stringify({ text: message }),
unsignedChatContent: chatText(message),
type: 0,
senderUuid: 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43', // random
senderName: JSON.stringify({ text: sender }),
Expand Down
29 changes: 27 additions & 2 deletions examples/server_helloworld/server_helloworld.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ const options = {
const server = mc.createServer(options)
const mcData = require('minecraft-data')(server.version)
const loginPacket = mcData.loginPacket
const nbt = require('prismarine-nbt')

function chatText (text) {
return mcData.supportFeature('chatPacketsUseNbtComponents')
? nbt.comp({ text: nbt.string(text) })
: JSON.stringify({ text })
}

server.on('playerJoin', function (client) {
const addr = client.socket.remoteAddress
Expand Down Expand Up @@ -49,14 +56,32 @@ server.on('playerJoin', function (client) {
flags: 0x00
})

const msg = {
const message = {
translate: 'chat.type.announcement',
with: [
'Server',
'Hello, world!'
]
}
client.write('chat', { message: JSON.stringify(msg), position: 0, sender: '0' })
if (mcData.supportFeature('signedChat')) {
client.write('player_chat', {
plainMessage: message,
signedChatContent: '',
unsignedChatContent: chatText(message),
type: 0,
senderUuid: 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43', // random
senderName: JSON.stringify({ text: 'me' }),
senderTeam: undefined,
timestamp: Date.now(),
salt: 0n,
signature: mcData.supportFeature('useChatSessions') ? undefined : Buffer.alloc(0),
previousMessages: [],
filterType: 0,
networkName: JSON.stringify({ text: 'me' })
})
} else {
client.write('chat', { message: JSON.stringify({ text: message }), position: 0, sender: 'me' })
}
})

server.on('error', function (error) {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@
"endian-toggle": "^0.0.0",
"lodash.get": "^4.1.2",
"lodash.merge": "^4.3.0",
"minecraft-data": "^3.53.0",
"minecraft-data": "^3.55.0",
"minecraft-folder-path": "^1.2.0",
"node-fetch": "^2.6.1",
"node-rsa": "^0.4.2",
"prismarine-auth": "^2.2.0",
"prismarine-chat": "^1.10.0",
"prismarine-nbt": "^2.5.0",
"prismarine-realms": "^1.2.0",
"protodef": "^1.8.0",
Expand Down
20 changes: 12 additions & 8 deletions src/client/chat.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const crypto = require('crypto')
const concat = require('../transforms/binaryStream').concat
const { processNbtMessage } = require('prismarine-chat')
const messageExpireTime = 420000 // 7 minutes (ms)

function isFormatted (message) {
Expand All @@ -25,6 +26,10 @@ module.exports = function (client, options) {
// This stores the last n (5 or 20) messages that the player has seen, from unique players
if (mcData.supportFeature('chainedChatWithHashing')) client._lastSeenMessages = new LastSeenMessages()
else client._lastSeenMessages = new LastSeenMessagesWithInvalidation()
// 1.20.3+ serializes chat components in either NBT or JSON. If the chat is sent as NBT, then the structure read will differ
// from the normal JSON structure, so it needs to be normalized. prismarine-chat processNbtMessage will do that by default
// on a fromNotch call. Since we don't call fromNotch here (done in mineflayer), we manually call processNbtMessage
const processMessage = (msg) => mcData.supportFeature('chatPacketsUseNbtComponents') ? processNbtMessage(msg) : msg

// This stores the last 128 inbound (signed) messages for 1.19.3 chat validation
client._signatureCache = new SignatureCache()
Expand Down Expand Up @@ -139,12 +144,11 @@ module.exports = function (client, options) {

client.on('profileless_chat', (packet) => {
// Profileless chat is parsed as an unsigned player chat message but logged as a system message

client.emit('playerChat', {
formattedMessage: packet.message,
formattedMessage: processMessage(packet.message),
type: packet.type,
senderName: packet.name,
targetName: packet.target,
senderName: processMessage(packet.name),
targetName: processMessage(packet.target),
verified: false
})

Expand All @@ -160,7 +164,7 @@ module.exports = function (client, options) {
client.on('system_chat', (packet) => {
client.emit('systemChat', {
positionId: packet.isActionBar ? 2 : 1,
formattedMessage: packet.content
formattedMessage: processMessage(packet.content)
})

client._lastChatHistory.push({
Expand Down Expand Up @@ -198,11 +202,11 @@ module.exports = function (client, options) {
if (verified) client._signatureCache.push(packet.signature)
client.emit('playerChat', {
plainMessage: packet.plainMessage,
unsignedContent: packet.unsignedChatContent,
unsignedContent: processMessage(packet.unsignedChatContent),
type: packet.type,
sender: packet.senderUuid,
senderName: packet.networkName,
targetName: packet.networkTargetName,
senderName: processMessage(packet.networkName),
targetName: processMessage(packet.networkTargetName),
verified
})

Expand Down
8 changes: 7 additions & 1 deletion src/datatypes/uuid.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ function nameToMcOfflineUUID (name) {
return (new UUID(javaUUID('OfflinePlayer:' + name))).toString()
}

module.exports = { nameToMcOfflineUUID }
function fromIntArray (arr) {
const buf = Buffer.alloc(16)
arr.forEach((num, index) => { buf.writeInt32BE(num, index * 4) })
return buf.toString('hex')
}

module.exports = { nameToMcOfflineUUID, fromIntArray }
4 changes: 2 additions & 2 deletions src/version.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

module.exports = {
defaultVersion: '1.20.2',
supportedVersions: ['1.7', '1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20', '1.20.1', '1.20.2']
defaultVersion: '1.20.4',
supportedVersions: ['1.7', '1.8.8', '1.9.4', '1.10.2', '1.11.2', '1.12.2', '1.13.2', '1.14.4', '1.15.2', '1.16.5', '1.17.1', '1.18.2', '1.19', '1.19.2', '1.19.3', '1.19.4', '1.20', '1.20.1', '1.20.2', '1.20.4']
}
16 changes: 9 additions & 7 deletions test/clientTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ for (const supportedVersion of mc.supportedVersions) {
'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)
Expand Down Expand Up @@ -165,21 +166,22 @@ for (const supportedVersion of mc.supportedVersions) {
}
} else {
// 1.19+

const message = JSON.parse(data.formattedMessage || JSON.stringify({ text: data.plainMessage }))
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(message.text, 'hello everyone; I have logged in.')
const sender = JSON.parse(data.senderName)
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) {
assert.strictEqual(message.text, 'hello')
const sender = JSON.parse(data.senderName)
assert.strictEqual(sender.text, 'Server')
const plainSender = client.parseMessage(sender).toString()
assert.strictEqual(plainMessage, 'hello')
assert.strictEqual(plainSender, 'Server')
wrap.removeListener('line', lineListener)
client.end()
done()
Expand Down
19 changes: 19 additions & 0 deletions test/common/clientHelpers.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Registry = require('prismarine-registry')
module.exports = client => {
client.nextMessage = (containing) => {
return new Promise((resolve) => {
Expand All @@ -20,5 +21,23 @@ module.exports = client => {
})
}

client.on('login', (packet) => {
client.registry ??= Registry(client.version)
if (packet.dimensionCodec) {
client.registry.loadDimensionCodec(packet.dimensionCodec)
}
})
client.on('registry_data', (data) => {
client.registry ??= Registry(client.version)
client.registry.loadDimensionCodec(data.codec)
})

client.on('playerJoin', () => {
const ChatMessage = require('prismarine-chat')(client.registry || client.version)
client.parseMessage = (comp) => {
return new ChatMessage(comp)
}
})

return client
}
4 changes: 4 additions & 0 deletions test/packetTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ const values = {
packedChunkPos: {
x: 10,
z: 12
},
particle: {
particleId: 0,
data: null
}
}

Expand Down

0 comments on commit 1d9a382

Please sign in to comment.