mirror of
https://github.com/PrismarineJS/node-minecraft-protocol.git
synced 2024-11-27 17:55:45 -05:00
Pc1.20.2 (#1265)
* Initial changes for 1.20.2 * add NBT serialize tag type handling * update tests * Update pnbt and mcdata for nbt change * lint * fix wrong param to sizeOfNbt * fix dupe NBT types * move nbt logic to prismarine-nbt * update tests * update tests * disable protodef validator in pluginChannel * Fix state desync * dump loginPacket.json in test output * enable validation * remove testing line in ci.yml * update pnbt to 2.5.0 * update doc for `playerJoin` * Update serializer.js * update examples * lint * disable client bundle handling if bundle becomes too big * Update client.js * bump mcdata * add soundSource and packedChunkPos example test values --------- Co-authored-by: Romain Beaumont <romain.rom1@gmail.com>
This commit is contained in:
parent
1740124c47
commit
112926da0c
25 changed files with 212 additions and 178 deletions
|
@ -89,6 +89,11 @@ Called when a client connects, but before any login has happened. Takes a
|
||||||
|
|
||||||
Called when a client is logged in against server. Takes a `Client` parameter.
|
Called when a client is logged in against server. Takes a `Client` parameter.
|
||||||
|
|
||||||
|
### `playerJoin` event
|
||||||
|
|
||||||
|
Emitted after a player joins and enters the PLAY protocol state and can send and recieve game packets. This is emitted after the `login` event. On 1.20.2 and above after we emit the `login` event, the player will enter a CONFIG state, as opposed to the PLAY state (where game packets can be sent), so you must instead now wait for `playerJoin`.
|
||||||
|
|
||||||
|
|
||||||
### `listening` event
|
### `listening` event
|
||||||
|
|
||||||
Called when the server is listening for connections. This means that the server is ready to accept incoming connections.
|
Called when the server is listening for connections. This means that the server is ready to accept incoming connections.
|
||||||
|
@ -261,6 +266,10 @@ Called when user authentication is resolved. Takes session data as parameter.
|
||||||
Called when the protocol changes state. Takes the new state and old state as
|
Called when the protocol changes state. Takes the new state and old state as
|
||||||
parameters.
|
parameters.
|
||||||
|
|
||||||
|
### `playerJoin` event
|
||||||
|
|
||||||
|
Emitted after the player enters the PLAY protocol state and can send and recieve game packets
|
||||||
|
|
||||||
### `error` event
|
### `error` event
|
||||||
|
|
||||||
Called when an error occurs within the client. Takes an Error as parameter.
|
Called when an error occurs within the client. Takes an Error as parameter.
|
||||||
|
|
|
@ -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),
|
* 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.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.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.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.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)
|
||||||
* Parses all packets and emits events with packet fields as JavaScript
|
* Parses all packets and emits events with packet fields as JavaScript
|
||||||
objects.
|
objects.
|
||||||
* Send a packet by supplying fields as a JavaScript object.
|
* Send a packet by supplying fields as a JavaScript object.
|
||||||
|
@ -115,6 +115,8 @@ const client = mc.createClient({
|
||||||
|
|
||||||
### Hello World server example
|
### Hello World server example
|
||||||
|
|
||||||
|
For a more up to date example, see examples/server/server.js.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const mc = require('minecraft-protocol');
|
const mc = require('minecraft-protocol');
|
||||||
const server = mc.createServer({
|
const server = mc.createServer({
|
||||||
|
@ -126,18 +128,12 @@ const server = mc.createServer({
|
||||||
});
|
});
|
||||||
const mcData = require('minecraft-data')(server.version)
|
const mcData = require('minecraft-data')(server.version)
|
||||||
|
|
||||||
server.on('login', function(client) {
|
server.on('playerJoin', function(client) {
|
||||||
const loginPacket = mcData.loginPacket
|
const loginPacket = mcData.loginPacket
|
||||||
|
|
||||||
client.write('login', {
|
client.write('login', {
|
||||||
|
...loginPacket,
|
||||||
entityId: client.id,
|
entityId: client.id,
|
||||||
isHardcore: false,
|
|
||||||
gameMode: 0,
|
|
||||||
previousGameMode: 255,
|
|
||||||
worldNames: loginPacket.worldNames,
|
|
||||||
dimensionCodec: loginPacket.dimensionCodec,
|
|
||||||
dimension: loginPacket.dimension,
|
|
||||||
worldName: 'minecraft:overworld',
|
|
||||||
hashedSeed: [0, 0],
|
hashedSeed: [0, 0],
|
||||||
maxPlayers: server.maxPlayers,
|
maxPlayers: server.maxPlayers,
|
||||||
viewDistance: 10,
|
viewDistance: 10,
|
||||||
|
|
|
@ -11,7 +11,7 @@ const server = mc.createServer(options)
|
||||||
const mcData = require('minecraft-data')(server.version)
|
const mcData = require('minecraft-data')(server.version)
|
||||||
const loginPacket = mcData.loginPacket
|
const loginPacket = mcData.loginPacket
|
||||||
|
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
broadcast(client.username + ' joined the game.')
|
broadcast(client.username + ' joined the game.')
|
||||||
const addr = client.socket.remoteAddress + ':' + client.socket.remotePort
|
const addr = client.socket.remoteAddress + ':' + client.socket.remotePort
|
||||||
console.log(client.username + ' connected', '(' + addr + ')')
|
console.log(client.username + ' connected', '(' + addr + ')')
|
||||||
|
@ -23,14 +23,11 @@ server.on('login', function (client) {
|
||||||
|
|
||||||
// send init data so client will start rendering world
|
// send init data so client will start rendering world
|
||||||
client.write('login', {
|
client.write('login', {
|
||||||
|
...loginPacket,
|
||||||
entityId: client.id,
|
entityId: client.id,
|
||||||
isHardcore: false,
|
isHardcore: false,
|
||||||
gameMode: 0,
|
gameMode: 0,
|
||||||
previousGameMode: 1,
|
previousGameMode: 1,
|
||||||
worldNames: loginPacket.worldNames,
|
|
||||||
dimensionCodec: loginPacket.dimensionCodec,
|
|
||||||
dimension: loginPacket.dimension,
|
|
||||||
worldName: 'minecraft:overworld',
|
|
||||||
hashedSeed: [0, 0],
|
hashedSeed: [0, 0],
|
||||||
maxPlayers: server.maxPlayers,
|
maxPlayers: server.maxPlayers,
|
||||||
viewDistance: 10,
|
viewDistance: 10,
|
||||||
|
@ -48,11 +45,13 @@ server.on('login', function (client) {
|
||||||
flags: 0x00
|
flags: 0x00
|
||||||
})
|
})
|
||||||
|
|
||||||
client.on('chat', function (data) {
|
function handleChat (data) {
|
||||||
const message = '<' + client.username + '>' + ' ' + data.message
|
const message = '<' + client.username + '>' + ' ' + data.message
|
||||||
broadcast(message, null, client.username)
|
broadcast(message, null, client.username)
|
||||||
console.log(message)
|
console.log(message)
|
||||||
})
|
}
|
||||||
|
client.on('chat', handleChat) // pre-1.19
|
||||||
|
client.on('chat_message', handleChat) // post 1.19
|
||||||
})
|
})
|
||||||
|
|
||||||
server.on('error', function (error) {
|
server.on('error', function (error) {
|
||||||
|
@ -63,27 +62,28 @@ server.on('listening', function () {
|
||||||
console.log('Server listening on port', server.socketServer.address().port)
|
console.log('Server listening on port', server.socketServer.address().port)
|
||||||
})
|
})
|
||||||
|
|
||||||
function broadcast (message, exclude, username) {
|
function sendBroadcastMessage (server, clients, message, sender) {
|
||||||
let client
|
if (mcData.supportFeature('signedChat')) {
|
||||||
const translate = username ? 'chat.type.announcement' : 'chat.type.text'
|
server.writeToClients(clients, 'player_chat', {
|
||||||
username = username || 'Server'
|
plainMessage: message,
|
||||||
for (const clientId in server.clients) {
|
signedChatContent: '',
|
||||||
if (server.clients[clientId] === undefined) continue
|
unsignedChatContent: JSON.stringify({ text: message }),
|
||||||
|
type: 0,
|
||||||
client = server.clients[clientId]
|
senderUuid: 'd3527a0b-bc03-45d5-a878-2aafdd8c8a43', // random
|
||||||
if (client !== exclude) {
|
senderName: JSON.stringify({ text: sender }),
|
||||||
const msg = {
|
senderTeam: undefined,
|
||||||
translate,
|
timestamp: Date.now(),
|
||||||
with: [
|
salt: 0n,
|
||||||
username,
|
signature: mcData.supportFeature('useChatSessions') ? undefined : Buffer.alloc(0),
|
||||||
message
|
previousMessages: [],
|
||||||
]
|
filterType: 0,
|
||||||
}
|
networkName: JSON.stringify({ text: sender })
|
||||||
client.write('chat', {
|
})
|
||||||
message: JSON.stringify(msg),
|
} else {
|
||||||
position: 0,
|
server.writeToClients(clients, 'chat', { message: JSON.stringify({ text: message }), position: 0, sender: sender || '0' })
|
||||||
sender: '0'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function broadcast (message, exclude, username) {
|
||||||
|
sendBroadcastMessage(server, Object.values(server.clients).filter(client => client !== exclude), message)
|
||||||
|
}
|
||||||
|
|
|
@ -8,18 +8,16 @@ const server = mc.createServer({
|
||||||
const mcData = require('minecraft-data')(server.version)
|
const mcData = require('minecraft-data')(server.version)
|
||||||
const loginPacket = mcData.loginPacket
|
const loginPacket = mcData.loginPacket
|
||||||
|
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
client.registerChannel('minecraft:brand', ['string', []])
|
client.registerChannel('minecraft:brand', ['string', []])
|
||||||
client.on('minecraft:brand', console.log)
|
client.on('minecraft:brand', console.log)
|
||||||
|
|
||||||
client.write('login', {
|
client.write('login', {
|
||||||
|
...loginPacket,
|
||||||
entityId: client.id,
|
entityId: client.id,
|
||||||
isHardcore: false,
|
isHardcore: false,
|
||||||
gameMode: 0,
|
gameMode: 0,
|
||||||
previousGameMode: 1,
|
previousGameMode: 1,
|
||||||
worldNames: loginPacket.worldNames,
|
|
||||||
dimensionCodec: loginPacket.dimensionCodec,
|
|
||||||
dimension: loginPacket.dimension,
|
|
||||||
worldName: 'minecraft:overworld',
|
worldName: 'minecraft:overworld',
|
||||||
hashedSeed: [0, 0],
|
hashedSeed: [0, 0],
|
||||||
maxPlayers: server.maxPlayers,
|
maxPlayers: server.maxPlayers,
|
||||||
|
|
|
@ -8,15 +8,13 @@ const server = mc.createServer({
|
||||||
const mcData = require('minecraft-data')(server.version)
|
const mcData = require('minecraft-data')(server.version)
|
||||||
const loginPacket = mcData.loginPacket
|
const loginPacket = mcData.loginPacket
|
||||||
|
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
client.write('login', {
|
client.write('login', {
|
||||||
|
...loginPacket,
|
||||||
entityId: client.id,
|
entityId: client.id,
|
||||||
isHardcore: false,
|
isHardcore: false,
|
||||||
gameMode: 0,
|
gameMode: 0,
|
||||||
previousGameMode: 1,
|
previousGameMode: 1,
|
||||||
worldNames: loginPacket.worldNames,
|
|
||||||
dimensionCodec: loginPacket.dimensionCodec,
|
|
||||||
dimension: loginPacket.dimension,
|
|
||||||
worldName: 'minecraft:overworld',
|
worldName: 'minecraft:overworld',
|
||||||
hashedSeed: [0, 0],
|
hashedSeed: [0, 0],
|
||||||
maxPlayers: server.maxPlayers,
|
maxPlayers: server.maxPlayers,
|
||||||
|
|
|
@ -9,7 +9,7 @@ const server = mc.createServer(options)
|
||||||
const mcData = require('minecraft-data')(server.version)
|
const mcData = require('minecraft-data')(server.version)
|
||||||
const loginPacket = mcData.loginPacket
|
const loginPacket = mcData.loginPacket
|
||||||
|
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
const addr = client.socket.remoteAddress
|
const addr = client.socket.remoteAddress
|
||||||
console.log('Incoming connection', '(' + addr + ')')
|
console.log('Incoming connection', '(' + addr + ')')
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,13 @@ for (let x = 0; x < 16; x++) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
client.write('login', {
|
client.write('login', {
|
||||||
|
...loginPacket,
|
||||||
entityId: client.id,
|
entityId: client.id,
|
||||||
isHardcore: false,
|
isHardcore: false,
|
||||||
gameMode: 0,
|
gameMode: 0,
|
||||||
previousGameMode: 1,
|
previousGameMode: 1,
|
||||||
worldNames: loginPacket.worldNames,
|
|
||||||
dimensionCodec: loginPacket.dimensionCodec,
|
|
||||||
dimension: loginPacket.dimension,
|
|
||||||
worldName: 'minecraft:overworld',
|
worldName: 'minecraft:overworld',
|
||||||
hashedSeed: [0, 0],
|
hashedSeed: [0, 0],
|
||||||
maxPlayers: server.maxPlayers,
|
maxPlayers: server.maxPlayers,
|
||||||
|
|
|
@ -51,12 +51,12 @@
|
||||||
"endian-toggle": "^0.0.0",
|
"endian-toggle": "^0.0.0",
|
||||||
"lodash.get": "^4.1.2",
|
"lodash.get": "^4.1.2",
|
||||||
"lodash.merge": "^4.3.0",
|
"lodash.merge": "^4.3.0",
|
||||||
"minecraft-data": "^3.37.0",
|
"minecraft-data": "^3.53.0",
|
||||||
"minecraft-folder-path": "^1.2.0",
|
"minecraft-folder-path": "^1.2.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"node-rsa": "^0.4.2",
|
"node-rsa": "^0.4.2",
|
||||||
"prismarine-auth": "^2.2.0",
|
"prismarine-auth": "^2.2.0",
|
||||||
"prismarine-nbt": "^2.0.0",
|
"prismarine-nbt": "^2.5.0",
|
||||||
"prismarine-realms": "^1.2.0",
|
"prismarine-realms": "^1.2.0",
|
||||||
"protodef": "^1.8.0",
|
"protodef": "^1.8.0",
|
||||||
"readable-stream": "^4.1.0",
|
"readable-stream": "^4.1.0",
|
||||||
|
|
|
@ -93,7 +93,7 @@ class Client extends EventEmitter {
|
||||||
const s = JSON.stringify(parsed.data, null, 2)
|
const s = JSON.stringify(parsed.data, null, 2)
|
||||||
debug(s && s.length > 10000 ? parsed.data : s)
|
debug(s && s.length > 10000 ? parsed.data : s)
|
||||||
}
|
}
|
||||||
if (parsed.metadata.name === 'bundle_delimiter') {
|
if (this._hasBundlePacket && parsed.metadata.name === 'bundle_delimiter') {
|
||||||
if (this._mcBundle.length) { // End bundle
|
if (this._mcBundle.length) { // End bundle
|
||||||
this._mcBundle.forEach(emitPacket)
|
this._mcBundle.forEach(emitPacket)
|
||||||
emitPacket(parsed)
|
emitPacket(parsed)
|
||||||
|
@ -103,6 +103,11 @@ class Client extends EventEmitter {
|
||||||
}
|
}
|
||||||
} else if (this._mcBundle.length) {
|
} else if (this._mcBundle.length) {
|
||||||
this._mcBundle.push(parsed)
|
this._mcBundle.push(parsed)
|
||||||
|
if (this._mcBundle.length > 32) {
|
||||||
|
this._mcBundle.forEach(emitPacket)
|
||||||
|
this._mcBundle = []
|
||||||
|
this._hasBundlePacket = false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
emitPacket(parsed)
|
emitPacket(parsed)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,30 +32,54 @@ module.exports = function (client, options) {
|
||||||
|
|
||||||
function onLogin (packet) {
|
function onLogin (packet) {
|
||||||
const mcData = require('minecraft-data')(client.version)
|
const mcData = require('minecraft-data')(client.version)
|
||||||
client.state = states.PLAY
|
|
||||||
client.uuid = packet.uuid
|
client.uuid = packet.uuid
|
||||||
client.username = packet.username
|
client.username = packet.username
|
||||||
|
|
||||||
if (mcData.supportFeature('signedChat')) {
|
if (mcData.supportFeature('hasConfigurationState')) {
|
||||||
if (options.disableChatSigning && client.serverFeatures.enforcesSecureChat) {
|
client.write('login_acknowledged', {})
|
||||||
throw new Error('"disableChatSigning" was enabled in client options, but server is enforcing secure chat')
|
enterConfigState()
|
||||||
}
|
// Server can tell client to re-enter config state
|
||||||
signedChatPlugin(client, options)
|
client.on('start_configuration', enterConfigState)
|
||||||
} else {
|
} else {
|
||||||
client.on('chat', (packet) => {
|
client.state = states.PLAY
|
||||||
client.emit(packet.position === 0 ? 'playerChat' : 'systemChat', {
|
onReady()
|
||||||
formattedMessage: packet.message,
|
}
|
||||||
sender: packet.sender,
|
|
||||||
positionId: packet.position,
|
function enterConfigState () {
|
||||||
verified: false
|
if (client.state === states.CONFIGURATION) return
|
||||||
})
|
client.state = states.CONFIGURATION
|
||||||
|
// Server should send finish_configuration on its own right after sending the client a dimension codec
|
||||||
|
// for login (that has data about world height, world gen, etc) after getting a login success from client
|
||||||
|
client.once('finish_configuration', () => {
|
||||||
|
client.write('finish_configuration', {})
|
||||||
|
client.state = states.PLAY
|
||||||
|
onReady()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function unsignedChat (message) {
|
function onReady () {
|
||||||
client.write('chat', { message })
|
client.emit('playerJoin')
|
||||||
}
|
if (mcData.supportFeature('signedChat')) {
|
||||||
|
if (options.disableChatSigning && client.serverFeatures.enforcesSecureChat) {
|
||||||
|
throw new Error('"disableChatSigning" was enabled in client options, but server is enforcing secure chat')
|
||||||
|
}
|
||||||
|
signedChatPlugin(client, options)
|
||||||
|
} else {
|
||||||
|
client.on('chat', (packet) => {
|
||||||
|
client.emit(packet.position === 0 ? 'playerChat' : 'systemChat', {
|
||||||
|
formattedMessage: packet.message,
|
||||||
|
sender: packet.sender,
|
||||||
|
positionId: packet.position,
|
||||||
|
verified: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
client.chat = client._signedChat || unsignedChat
|
function unsignedChat (message) {
|
||||||
|
client.write('chat', { message })
|
||||||
|
}
|
||||||
|
|
||||||
|
client.chat = client._signedChat || unsignedChat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
const ProtoDef = require('protodef').ProtoDef
|
const ProtoDef = require('protodef').ProtoDef
|
||||||
const minecraft = require('../datatypes/minecraft')
|
const minecraft = require('../datatypes/minecraft')
|
||||||
const debug = require('debug')('minecraft-protocol')
|
const debug = require('debug')('minecraft-protocol')
|
||||||
|
const nbt = require('prismarine-nbt')
|
||||||
|
|
||||||
module.exports = function (client, options) {
|
module.exports = function (client, options) {
|
||||||
const mcdata = require('minecraft-data')(options.version || require('../version').defaultVersion)
|
const mcdata = require('minecraft-data')(options.version || require('../version').defaultVersion)
|
||||||
const channels = []
|
const channels = []
|
||||||
const proto = new ProtoDef(options.validateChannelProtocol ?? true)
|
const proto = new ProtoDef(options.validateChannelProtocol ?? true)
|
||||||
|
nbt.addTypesToInterpreter('big', proto)
|
||||||
proto.addTypes(mcdata.protocol.types)
|
proto.addTypes(mcdata.protocol.types)
|
||||||
proto.addTypes(minecraft)
|
proto.addTypes(minecraft)
|
||||||
proto.addType('registerarr', [readDumbArr, writeDumbArr, sizeOfDumbArr])
|
proto.addType('registerarr', [readDumbArr, writeDumbArr, sizeOfDumbArr])
|
||||||
|
|
|
@ -37,7 +37,7 @@ module.exports = function (client, options) {
|
||||||
: client.profileKeys.signature
|
: client.profileKeys.signature
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
playerUUID: client.session?.selectedProfile?.id
|
playerUUID: client.session?.selectedProfile?.id ?? client.uuid
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ const tcpDns = require('./client/tcp_dns')
|
||||||
const autoVersion = require('./client/autoVersion')
|
const autoVersion = require('./client/autoVersion')
|
||||||
const pluginChannels = require('./client/pluginChannels')
|
const pluginChannels = require('./client/pluginChannels')
|
||||||
const versionChecking = require('./client/versionChecking')
|
const versionChecking = require('./client/versionChecking')
|
||||||
|
const uuid = require('./datatypes/uuid')
|
||||||
|
|
||||||
module.exports = createClient
|
module.exports = createClient
|
||||||
|
|
||||||
|
@ -54,6 +55,8 @@ function createClient (options) {
|
||||||
case 'offline':
|
case 'offline':
|
||||||
default:
|
default:
|
||||||
client.username = options.username
|
client.username = options.username
|
||||||
|
client.uuid = uuid.nameToMcOfflineUUID(client.username)
|
||||||
|
options.auth = 'offline'
|
||||||
options.connect(client)
|
options.connect(client)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ function createServer (options = {}) {
|
||||||
server.onlineModeExceptions = Object.create(null)
|
server.onlineModeExceptions = Object.create(null)
|
||||||
server.favicon = favicon
|
server.favicon = favicon
|
||||||
server.options = options
|
server.options = options
|
||||||
|
options.registryCodec = options.registryCodec || mcData.registryCodec || mcData.loginPacket?.dimensionCodec
|
||||||
|
|
||||||
// The RSA keypair can take some time to generate
|
// The RSA keypair can take some time to generate
|
||||||
// and is only needed for online-mode
|
// and is only needed for online-mode
|
||||||
|
|
|
@ -16,8 +16,6 @@ module.exports = {
|
||||||
size: buffer.length - offset
|
size: buffer.length - offset
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
nbt: ['native', minecraft.nbt[0]],
|
|
||||||
optionalNbt: ['native', minecraft.optionalNbt[0]],
|
|
||||||
compressedNbt: ['native', minecraft.compressedNbt[0]],
|
compressedNbt: ['native', minecraft.compressedNbt[0]],
|
||||||
entityMetadataLoop: ['parametrizable', (compiler, { type, endVal }) => {
|
entityMetadataLoop: ['parametrizable', (compiler, { type, endVal }) => {
|
||||||
let code = 'let cursor = offset\n'
|
let code = 'let cursor = offset\n'
|
||||||
|
@ -55,8 +53,6 @@ module.exports = {
|
||||||
value.copy(buffer, offset)
|
value.copy(buffer, offset)
|
||||||
return offset + value.length
|
return offset + value.length
|
||||||
}],
|
}],
|
||||||
nbt: ['native', minecraft.nbt[1]],
|
|
||||||
optionalNbt: ['native', minecraft.optionalNbt[1]],
|
|
||||||
compressedNbt: ['native', minecraft.compressedNbt[1]],
|
compressedNbt: ['native', minecraft.compressedNbt[1]],
|
||||||
entityMetadataLoop: ['parametrizable', (compiler, { type, endVal }) => {
|
entityMetadataLoop: ['parametrizable', (compiler, { type, endVal }) => {
|
||||||
let code = 'for (const i in value) {\n'
|
let code = 'for (const i in value) {\n'
|
||||||
|
@ -84,8 +80,6 @@ module.exports = {
|
||||||
restBuffer: ['native', (value) => {
|
restBuffer: ['native', (value) => {
|
||||||
return value.length
|
return value.length
|
||||||
}],
|
}],
|
||||||
nbt: ['native', minecraft.nbt[2]],
|
|
||||||
optionalNbt: ['native', minecraft.optionalNbt[2]],
|
|
||||||
compressedNbt: ['native', minecraft.compressedNbt[2]],
|
compressedNbt: ['native', minecraft.compressedNbt[2]],
|
||||||
entityMetadataLoop: ['parametrizable', (compiler, { type }) => {
|
entityMetadataLoop: ['parametrizable', (compiler, { type }) => {
|
||||||
let code = 'let size = 1\n'
|
let code = 'let size = 1\n'
|
||||||
|
|
|
@ -8,8 +8,6 @@ const [readVarInt, writeVarInt, sizeOfVarInt] = require('protodef').types.varint
|
||||||
module.exports = {
|
module.exports = {
|
||||||
varlong: [readVarLong, writeVarLong, sizeOfVarLong],
|
varlong: [readVarLong, writeVarLong, sizeOfVarLong],
|
||||||
UUID: [readUUID, writeUUID, 16],
|
UUID: [readUUID, writeUUID, 16],
|
||||||
nbt: [readNbt, writeNbt, sizeOfNbt],
|
|
||||||
optionalNbt: [readOptionalNbt, writeOptionalNbt, sizeOfOptionalNbt],
|
|
||||||
compressedNbt: [readCompressedNbt, writeCompressedNbt, sizeOfCompressedNbt],
|
compressedNbt: [readCompressedNbt, writeCompressedNbt, sizeOfCompressedNbt],
|
||||||
restBuffer: [readRestBuffer, writeRestBuffer, sizeOfRestBuffer],
|
restBuffer: [readRestBuffer, writeRestBuffer, sizeOfRestBuffer],
|
||||||
entityMetadataLoop: [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata],
|
entityMetadataLoop: [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata],
|
||||||
|
@ -43,35 +41,8 @@ function writeUUID (value, buffer, offset) {
|
||||||
return offset + 16
|
return offset + 16
|
||||||
}
|
}
|
||||||
|
|
||||||
function readNbt (buffer, offset) {
|
function sizeOfNbt (value, { tagType } = { tagType: 'nbt' }) {
|
||||||
return nbt.proto.read(buffer, offset, 'nbt')
|
return nbt.proto.sizeOf(value, tagType)
|
||||||
}
|
|
||||||
|
|
||||||
function writeNbt (value, buffer, offset) {
|
|
||||||
return nbt.proto.write(value, buffer, offset, 'nbt')
|
|
||||||
}
|
|
||||||
|
|
||||||
function sizeOfNbt (value) {
|
|
||||||
return nbt.proto.sizeOf(value, 'nbt')
|
|
||||||
}
|
|
||||||
|
|
||||||
function readOptionalNbt (buffer, offset) {
|
|
||||||
if (offset + 1 > buffer.length) { throw new PartialReadError() }
|
|
||||||
if (buffer.readInt8(offset) === 0) return { size: 1 }
|
|
||||||
return nbt.proto.read(buffer, offset, 'nbt')
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeOptionalNbt (value, buffer, offset) {
|
|
||||||
if (value === undefined) {
|
|
||||||
buffer.writeInt8(0, offset)
|
|
||||||
return offset + 1
|
|
||||||
}
|
|
||||||
return nbt.proto.write(value, buffer, offset, 'nbt')
|
|
||||||
}
|
|
||||||
|
|
||||||
function sizeOfOptionalNbt (value) {
|
|
||||||
if (value === undefined) { return 1 }
|
|
||||||
return nbt.proto.sizeOf(value, 'nbt')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length-prefixed compressed NBT, see differences: http://wiki.vg/index.php?title=Slot_Data&diff=6056&oldid=4753
|
// Length-prefixed compressed NBT, see differences: http://wiki.vg/index.php?title=Slot_Data&diff=6056&oldid=4753
|
||||||
|
@ -111,7 +82,7 @@ function writeCompressedNbt (value, buffer, offset) {
|
||||||
function sizeOfCompressedNbt (value) {
|
function sizeOfCompressedNbt (value) {
|
||||||
if (value === undefined) { return 2 }
|
if (value === undefined) { return 2 }
|
||||||
|
|
||||||
const nbtBuffer = Buffer.alloc(sizeOfNbt(value, 'nbt'))
|
const nbtBuffer = Buffer.alloc(sizeOfNbt(value, { tagType: 'nbt' }))
|
||||||
nbt.proto.write(value, nbtBuffer, 0, 'nbt')
|
nbt.proto.write(value, nbtBuffer, 0, 'nbt')
|
||||||
|
|
||||||
const compressedNbt = zlib.gzipSync(nbtBuffer) // TODO: async
|
const compressedNbt = zlib.gzipSync(nbtBuffer) // TODO: async
|
||||||
|
|
18
src/datatypes/uuid.js
Normal file
18
src/datatypes/uuid.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const UUID = require('uuid-1345')
|
||||||
|
|
||||||
|
// https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/UUID.java#L163
|
||||||
|
function javaUUID (s) {
|
||||||
|
const hash = crypto.createHash('md5')
|
||||||
|
hash.update(s, 'utf8')
|
||||||
|
const buffer = hash.digest()
|
||||||
|
buffer[6] = (buffer[6] & 0x0f) | 0x30
|
||||||
|
buffer[8] = (buffer[8] & 0x3f) | 0x80
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
function nameToMcOfflineUUID (name) {
|
||||||
|
return (new UUID(javaUUID('OfflinePlayer:' + name))).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { nameToMcOfflineUUID }
|
4
src/index.d.ts
vendored
4
src/index.d.ts
vendored
|
@ -51,6 +51,8 @@ declare module 'minecraft-protocol' {
|
||||||
on(event: `raw.${string}`, handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this
|
on(event: `raw.${string}`, handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this
|
||||||
on(event: 'playerChat', handler: (data: { formattedMessage: string, message: string, type: string, sender: string, senderName: string, senderTeam: string, verified?: boolean }) => PromiseLike): this
|
on(event: 'playerChat', handler: (data: { formattedMessage: string, message: string, type: string, sender: string, senderName: string, senderTeam: string, verified?: boolean }) => PromiseLike): this
|
||||||
on(event: 'systemChat', handler: (data: { positionId: number, formattedMessage: string }) => PromiseLike): this
|
on(event: 'systemChat', handler: (data: { positionId: number, formattedMessage: string }) => PromiseLike): this
|
||||||
|
// Emitted after the player enters the PLAY state and can send and recieve game packets
|
||||||
|
on(event: 'playerJoin', handler: () => void): this
|
||||||
once(event: 'error', listener: (error: Error) => PromiseLike): this
|
once(event: 'error', listener: (error: Error) => PromiseLike): this
|
||||||
once(event: 'packet', handler: (data: any, packetMeta: PacketMeta, buffer: Buffer, fullBuffer: Buffer) => PromiseLike): this
|
once(event: 'packet', handler: (data: any, packetMeta: PacketMeta, buffer: Buffer, fullBuffer: Buffer) => PromiseLike): this
|
||||||
once(event: 'raw', handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this
|
once(event: 'raw', handler: (buffer: Buffer, packetMeta: PacketMeta) => PromiseLike): this
|
||||||
|
@ -156,6 +158,8 @@ declare module 'minecraft-protocol' {
|
||||||
on(event: 'error', listener: (error: Error) => PromiseLike): this
|
on(event: 'error', listener: (error: Error) => PromiseLike): this
|
||||||
on(event: 'login', handler: (client: ServerClient) => PromiseLike): this
|
on(event: 'login', handler: (client: ServerClient) => PromiseLike): this
|
||||||
on(event: 'listening', listener: () => PromiseLike): this
|
on(event: 'listening', listener: () => PromiseLike): this
|
||||||
|
// Emitted after the player enters the PLAY state and can send and recieve game packets
|
||||||
|
on(event: 'playerJoin', handler: (client: ServerClient) => void): this
|
||||||
once(event: 'connection', handler: (client: ServerClient) => PromiseLike): this
|
once(event: 'connection', handler: (client: ServerClient) => PromiseLike): this
|
||||||
once(event: 'error', listener: (error: Error) => PromiseLike): this
|
once(event: 'error', listener: (error: Error) => PromiseLike): this
|
||||||
once(event: 'login', handler: (client: ServerClient) => PromiseLike): this
|
once(event: 'login', handler: (client: ServerClient) => PromiseLike): this
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const UUID = require('uuid-1345')
|
const uuid = require('../datatypes/uuid')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const pluginChannels = require('../client/pluginChannels')
|
const pluginChannels = require('../client/pluginChannels')
|
||||||
const states = require('../states')
|
const states = require('../states')
|
||||||
|
@ -166,37 +166,28 @@ module.exports = function (client, server, options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/UUID.java#L163
|
|
||||||
function javaUUID (s) {
|
|
||||||
const hash = crypto.createHash('md5')
|
|
||||||
hash.update(s, 'utf8')
|
|
||||||
const buffer = hash.digest()
|
|
||||||
buffer[6] = (buffer[6] & 0x0f) | 0x30
|
|
||||||
buffer[8] = (buffer[8] & 0x3f) | 0x80
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
function nameToMcOfflineUUID (name) {
|
|
||||||
return (new UUID(javaUUID('OfflinePlayer:' + name))).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
function loginClient () {
|
function loginClient () {
|
||||||
const isException = !!server.onlineModeExceptions[client.username.toLowerCase()]
|
const isException = !!server.onlineModeExceptions[client.username.toLowerCase()]
|
||||||
if (onlineMode === false || isException) {
|
if (onlineMode === false || isException) {
|
||||||
client.uuid = nameToMcOfflineUUID(client.username)
|
client.uuid = uuid.nameToMcOfflineUUID(client.username)
|
||||||
}
|
}
|
||||||
options.beforeLogin?.(client)
|
options.beforeLogin?.(client)
|
||||||
if (client.protocolVersion >= 27) { // 14w28a (27) added whole-protocol compression (http://wiki.vg/Protocol_History#14w28a), earlier versions per-packet compressed TODO: refactor into minecraft-data
|
if (client.protocolVersion >= 27) { // 14w28a (27) added whole-protocol compression (http://wiki.vg/Protocol_History#14w28a), earlier versions per-packet compressed TODO: refactor into minecraft-data
|
||||||
client.write('compress', { threshold: 256 }) // Default threshold is 256
|
client.write('compress', { threshold: 256 }) // Default threshold is 256
|
||||||
client.compressionThreshold = 256
|
client.compressionThreshold = 256
|
||||||
}
|
}
|
||||||
|
// TODO: find out what properties are on 'success' packet
|
||||||
client.write('success', {
|
client.write('success', {
|
||||||
uuid: client.uuid,
|
uuid: client.uuid,
|
||||||
username: client.username,
|
username: client.username,
|
||||||
properties: []
|
properties: []
|
||||||
})
|
})
|
||||||
// TODO: find out what properties are on 'success' packet
|
if (client.supportFeature('hasConfigurationState')) {
|
||||||
client.state = states.PLAY
|
client.once('login_acknowledged', onClientLoginAck)
|
||||||
|
} else {
|
||||||
|
client.state = states.PLAY
|
||||||
|
server.emit('playerJoin', client)
|
||||||
|
}
|
||||||
client.settings = {}
|
client.settings = {}
|
||||||
|
|
||||||
if (client.supportFeature('chainedChatWithHashing')) { // 1.19.1+
|
if (client.supportFeature('chainedChatWithHashing')) { // 1.19.1+
|
||||||
|
@ -217,6 +208,16 @@ module.exports = function (client, server, options) {
|
||||||
if (client.supportFeature('signedChat')) chatPlugin(client, server, options)
|
if (client.supportFeature('signedChat')) chatPlugin(client, server, options)
|
||||||
server.emit('login', client)
|
server.emit('login', client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onClientLoginAck () {
|
||||||
|
client.state = states.CONFIGURATION
|
||||||
|
client.write('registry_data', { codec: options.registryCodec || {} })
|
||||||
|
client.once('finish_configuration', () => {
|
||||||
|
client.state = states.PLAY
|
||||||
|
server.emit('playerJoin', client)
|
||||||
|
})
|
||||||
|
client.write('finish_configuration', {})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mcPubKeyToPem (mcPubKeyBuffer) {
|
function mcPubKeyToPem (mcPubKeyBuffer) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ const states = {
|
||||||
HANDSHAKING: 'handshaking',
|
HANDSHAKING: 'handshaking',
|
||||||
STATUS: 'status',
|
STATUS: 'status',
|
||||||
LOGIN: 'login',
|
LOGIN: 'login',
|
||||||
|
CONFIGURATION: 'configuration',
|
||||||
PLAY: 'play'
|
PLAY: 'play'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ const Serializer = require('protodef').Serializer
|
||||||
const Parser = require('protodef').FullPacketParser
|
const Parser = require('protodef').FullPacketParser
|
||||||
const { ProtoDefCompiler } = require('protodef').Compiler
|
const { ProtoDefCompiler } = require('protodef').Compiler
|
||||||
|
|
||||||
|
const nbt = require('prismarine-nbt')
|
||||||
const minecraft = require('../datatypes/minecraft')
|
const minecraft = require('../datatypes/minecraft')
|
||||||
const states = require('../states')
|
const states = require('../states')
|
||||||
const merge = require('lodash.merge')
|
const merge = require('lodash.merge')
|
||||||
|
@ -30,6 +31,7 @@ function createProtocol (state, direction, version, customPackets, compiled = tr
|
||||||
const compiler = new ProtoDefCompiler()
|
const compiler = new ProtoDefCompiler()
|
||||||
compiler.addTypes(require('../datatypes/compiler-minecraft'))
|
compiler.addTypes(require('../datatypes/compiler-minecraft'))
|
||||||
compiler.addProtocol(merge(mcData.protocol, get(customPackets, [mcData.version.majorVersion])), [state, direction])
|
compiler.addProtocol(merge(mcData.protocol, get(customPackets, [mcData.version.majorVersion])), [state, direction])
|
||||||
|
nbt.addTypesToCompiler('big', compiler)
|
||||||
const proto = compiler.compileProtoDefSync()
|
const proto = compiler.compileProtoDefSync()
|
||||||
protocols[key] = proto
|
protocols[key] = proto
|
||||||
return proto
|
return proto
|
||||||
|
@ -38,6 +40,7 @@ function createProtocol (state, direction, version, customPackets, compiled = tr
|
||||||
const proto = new ProtoDef(false)
|
const proto = new ProtoDef(false)
|
||||||
proto.addTypes(minecraft)
|
proto.addTypes(minecraft)
|
||||||
proto.addProtocol(merge(mcData.protocol, get(customPackets, [mcData.version.majorVersion])), [state, direction])
|
proto.addProtocol(merge(mcData.protocol, get(customPackets, [mcData.version.majorVersion])), [state, direction])
|
||||||
|
nbt.addTypesToInterperter('big', proto)
|
||||||
protocols[key] = proto
|
protocols[key] = proto
|
||||||
return proto
|
return proto
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
defaultVersion: '1.20.1',
|
defaultVersion: '1.20.2',
|
||||||
supportedVersions: ['1.7', '1.8', '1.9', '1.10', '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']
|
supportedVersions: ['1.7', '1.8', '1.9', '1.10', '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']
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
const mc = require('../')
|
const mc = require('../')
|
||||||
const os = require('os')
|
const os = require('os')
|
||||||
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const assert = require('power-assert')
|
const assert = require('power-assert')
|
||||||
const util = require('util')
|
const util = require('util')
|
||||||
|
@ -20,7 +21,8 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
const version = mcData.version
|
const version = mcData.version
|
||||||
const MC_SERVER_JAR_DIR = process.env.MC_SERVER_JAR_DIR || os.tmpdir()
|
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_JAR = MC_SERVER_JAR_DIR + '/minecraft_server.' + version.minecraftVersion + '.jar'
|
||||||
const wrap = new Wrap(MC_SERVER_JAR, MC_SERVER_PATH + '_' + supportedVersion, {
|
const MC_SERVER_DIR = MC_SERVER_PATH + '_' + supportedVersion
|
||||||
|
const wrap = new Wrap(MC_SERVER_JAR, MC_SERVER_DIR, {
|
||||||
minMem: 1024,
|
minMem: 1024,
|
||||||
maxMem: 1024
|
maxMem: 1024
|
||||||
})
|
})
|
||||||
|
@ -118,7 +120,23 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
assert.strictEqual(packet.gameMode, 0)
|
assert.strictEqual(packet.gameMode, 0)
|
||||||
client.chat('hello everyone; I have logged in.')
|
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) {
|
client.on('playerChat', function (data) {
|
||||||
chatCount += 1
|
chatCount += 1
|
||||||
assert.ok(chatCount <= 2)
|
assert.ok(chatCount <= 2)
|
||||||
|
|
|
@ -35,6 +35,20 @@ const slotValue = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nbtValue = {
|
||||||
|
type: 'compound',
|
||||||
|
name: 'test',
|
||||||
|
value: {
|
||||||
|
test1: { type: 'int', value: 4 },
|
||||||
|
test2: { type: 'long', value: [12, 42] },
|
||||||
|
test3: { type: 'byteArray', value: [32] },
|
||||||
|
test4: { type: 'string', value: 'ohi' },
|
||||||
|
test5: { type: 'list', value: { type: 'int', value: [4] } },
|
||||||
|
test6: { type: 'compound', value: { test: { type: 'int', value: 4 } } },
|
||||||
|
test7: { type: 'intArray', value: [12, 42] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const values = {
|
const values = {
|
||||||
i32: 123456,
|
i32: 123456,
|
||||||
i16: -123,
|
i16: -123,
|
||||||
|
@ -110,46 +124,12 @@ const values = {
|
||||||
f64: 99999.2222,
|
f64: 99999.2222,
|
||||||
f32: -333.444,
|
f32: -333.444,
|
||||||
slot: slotValue,
|
slot: slotValue,
|
||||||
nbt: {
|
nbt: nbtValue,
|
||||||
type: 'compound',
|
optionalNbt: nbtValue,
|
||||||
name: 'test',
|
compressedNbt: nbtValue,
|
||||||
value: {
|
anonymousNbt: nbtValue,
|
||||||
test1: { type: 'int', value: 4 },
|
anonOptionalNbt: nbtValue,
|
||||||
test2: { type: 'long', value: [12, 42] },
|
|
||||||
test3: { type: 'byteArray', value: [32] },
|
|
||||||
test4: { type: 'string', value: 'ohi' },
|
|
||||||
test5: { type: 'list', value: { type: 'int', value: [4] } },
|
|
||||||
test6: { type: 'compound', value: { test: { type: 'int', value: 4 } } },
|
|
||||||
test7: { type: 'intArray', value: [12, 42] }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
optionalNbt: {
|
|
||||||
type: 'compound',
|
|
||||||
name: 'test',
|
|
||||||
value: {
|
|
||||||
test1: { type: 'int', value: 4 },
|
|
||||||
test2: { type: 'long', value: [12, 42] },
|
|
||||||
test3: { type: 'byteArray', value: [32] },
|
|
||||||
test4: { type: 'string', value: 'ohi' },
|
|
||||||
test5: { type: 'list', value: { type: 'int', value: [4] } },
|
|
||||||
test6: { type: 'compound', value: { test: { type: 'int', value: 4 } } },
|
|
||||||
test7: { type: 'intArray', value: [12, 42] }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
previousMessages: [],
|
previousMessages: [],
|
||||||
compressedNbt: {
|
|
||||||
type: 'compound',
|
|
||||||
name: 'test',
|
|
||||||
value: {
|
|
||||||
test1: { type: 'int', value: 4 },
|
|
||||||
test2: { type: 'long', value: [12, 42] },
|
|
||||||
test3: { type: 'byteArray', value: [32] },
|
|
||||||
test4: { type: 'string', value: 'ohi' },
|
|
||||||
test5: { type: 'list', value: { type: 'int', value: [4] } },
|
|
||||||
test6: { type: 'compound', value: { test: { type: 'int', value: 4 } } },
|
|
||||||
test7: { type: 'intArray', value: [12, 42] }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
i64: [0, 1],
|
i64: [0, 1],
|
||||||
u64: [0, 1],
|
u64: [0, 1],
|
||||||
entityMetadata: [
|
entityMetadata: [
|
||||||
|
@ -223,6 +203,11 @@ const values = {
|
||||||
},
|
},
|
||||||
suggestionType: 'minecraft:summonable_entities'
|
suggestionType: 'minecraft:summonable_entities'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
soundSource: 'master',
|
||||||
|
packedChunkPos: {
|
||||||
|
x: 10,
|
||||||
|
z: 12
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
|
|
||||||
describe('mc-server ' + supportedVersion + 'v', function () {
|
describe('mc-server ' + supportedVersion + 'v', function () {
|
||||||
this.timeout(5000)
|
this.timeout(5000)
|
||||||
this.beforeAll(async function () {
|
this.beforeEach(async function () {
|
||||||
PORT = await getPort()
|
PORT = await getPort()
|
||||||
console.log(`Using port for tests: ${PORT}`)
|
console.log(`Using port for tests: ${PORT}`)
|
||||||
})
|
})
|
||||||
|
@ -299,7 +299,8 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
|
|
||||||
const username = ['player1', 'player2']
|
const username = ['player1', 'player2']
|
||||||
let index = 0
|
let index = 0
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
|
console.log('ChatTest: Player has joined')
|
||||||
assert.notEqual(client.id, null)
|
assert.notEqual(client.id, null)
|
||||||
assert.strictEqual(client.username, username[index++])
|
assert.strictEqual(client.username, username[index++])
|
||||||
broadcast(client.username + ' joined the game.')
|
broadcast(client.username + ' joined the game.')
|
||||||
|
@ -322,8 +323,10 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
version: version.minecraftVersion,
|
version: version.minecraftVersion,
|
||||||
port: PORT
|
port: PORT
|
||||||
}))
|
}))
|
||||||
|
console.log('ChatTest: Player1 is joining...')
|
||||||
|
|
||||||
player1.on('login', async function (packet) {
|
player1.on('login', async function (packet) {
|
||||||
|
console.log('ChatTest: Player 1 has joined')
|
||||||
assert.strictEqual(packet.gameMode, 1)
|
assert.strictEqual(packet.gameMode, 1)
|
||||||
const player2 = applyClientHelpers(mc.createClient({
|
const player2 = applyClientHelpers(mc.createClient({
|
||||||
username: 'player2',
|
username: 'player2',
|
||||||
|
@ -332,14 +335,16 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
port: PORT
|
port: PORT
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
console.log('ChatTest: waiting for next message from P2')
|
||||||
const p1Join = await player1.nextMessage('player2')
|
const p1Join = await player1.nextMessage('player2')
|
||||||
|
|
||||||
assert.strictEqual(p1Join, '{"text":"player2 joined the game."}')
|
assert.strictEqual(p1Join, '{"text":"player2 joined the game."}')
|
||||||
|
console.log('ChatTest: Got message from P2')
|
||||||
player2.chat('hi')
|
player2.chat('hi')
|
||||||
const p2hi = await player1.nextMessage('player2')
|
const p2hi = await player1.nextMessage('player2')
|
||||||
assert.strictEqual(p2hi, '{"text":"<player2> hi"}')
|
assert.strictEqual(p2hi, '{"text":"<player2> hi"}')
|
||||||
|
|
||||||
|
console.log('ChatTest: Waiting again for next message from P2')
|
||||||
player1.chat('hello')
|
player1.chat('hello')
|
||||||
const p1hello = await player2.nextMessage('player1')
|
const p1hello = await player2.nextMessage('player1')
|
||||||
assert.strictEqual(p1hello, '{"text":"<player1> hello"}')
|
assert.strictEqual(p1hello, '{"text":"<player1> hello"}')
|
||||||
|
@ -389,7 +394,7 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
port: PORT
|
port: PORT
|
||||||
})
|
})
|
||||||
let count = 2
|
let count = 2
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
client.on('end', function (reason) {
|
client.on('end', function (reason) {
|
||||||
assert.strictEqual(reason, 'ServerShutdown')
|
assert.strictEqual(reason, 'ServerShutdown')
|
||||||
resolve()
|
resolve()
|
||||||
|
@ -404,7 +409,7 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
version: version.minecraftVersion,
|
version: version.minecraftVersion,
|
||||||
port: PORT
|
port: PORT
|
||||||
})
|
})
|
||||||
client.on('login', function () {
|
client.on('playerJoin', function () {
|
||||||
server.close()
|
server.close()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -420,7 +425,7 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
version: version.minecraftVersion,
|
version: version.minecraftVersion,
|
||||||
port: PORT
|
port: PORT
|
||||||
})
|
})
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
client.write('login', loginPacket(client, server))
|
client.write('login', loginPacket(client, server))
|
||||||
})
|
})
|
||||||
server.on('close', done)
|
server.on('close', done)
|
||||||
|
@ -459,7 +464,7 @@ for (const supportedVersion of mc.supportedVersions) {
|
||||||
version: version.minecraftVersion,
|
version: version.minecraftVersion,
|
||||||
port: PORT
|
port: PORT
|
||||||
})
|
})
|
||||||
server.on('login', function (client) {
|
server.on('playerJoin', function (client) {
|
||||||
client.on('end', function (reason) {
|
client.on('end', function (reason) {
|
||||||
assert.strictEqual(reason, 'ServerShutdown')
|
assert.strictEqual(reason, 'ServerShutdown')
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue