bedrock-protocol/src/client.js
2021-02-16 23:31:19 -05:00

130 lines
No EOL
4 KiB
JavaScript

const RakClient = require('@jsprismarine/raknet/client')
const { Connection } = require('./connection')
const { createDeserializer, createSerializer } = require('./transforms/serializer')
const { Encrypt } = require('./auth/encryption')
const auth = require('./client/auth')
const Options = require('./options')
const fs = require('fs')
const log = console.log
class Client extends Connection {
constructor(options) {
super()
this.options = { ...Options.defaultOptions, options }
this.serializer = createSerializer()
this.deserializer = createDeserializer()
this.validateOptions()
Encrypt(this, null, options)
if (options.password) {
auth.authenticatePassword(this, options)
} else {
auth.authenticateDeviceCode(this, options)
}
this.on('session', this.connect)
}
validateOptions() {
if (this.options.version < Options.MIN_VERSION) {
throw new Error(`Unsupported protocol version < ${Options.MIN_VERSION} : ${this.options.version}`)
}
}
onEncapsulated = (encapsulated, inetAddr) => {
// log(inetAddr.address, ': Encapsulated', encapsulated)
const buffer = encapsulated.buffer
this.handle(buffer)
}
connect = async (sessionData) => {
console.log('Got session data', sessionData)
if (this.raknet) return
this.raknet = new RakClient('127.0.0.1', 19132)
await this.raknet.connect()
this.raknet.on('connecting', () => {
// console.log(`[client] connecting to ${hostname}/${port}`)
})
this.raknet.on('connected', (connection) => {
console.log(`[client] connected!`)
this.connection = connection
this.sendLogin()
})
this.raknet.on('encapsulated', this.onEncapsulated)
this.raknet.on('raw', (buffer, inetAddr) => {
console.log('Raw packet', buffer, inetAddr)
})
}
sendLogin() {
this.createClientChain()
const chain = [
this.clientIdentityChain, // JWT we generated for auth
...this.accessToken // Mojang + Xbox JWT from auth
]
const encodedChain = JSON.stringify({ chain })
const skinChain = JSON.stringify({})
const bodyLength = this.clientUserChain.length + encodedChain.length + 8
console.log('Auth chain', chain)
this.write('login', {
protocol_version: this.options.version,
payload_size: bodyLength,
chain: encodedChain,
client_data: this.clientUserChain
})
}
// After sending Server to Client Handshake, this handles the client's
// Client to Server handshake response. This indicates successful encryption
onHandshake() {
// https://wiki.vg/Bedrock_Protocol#Play_Status
this.write('play_status', { status: PLAY_STATUS.LoginSuccess })
this.emit('join')
}
onDisconnectRequest(packet) {
// We're talking over UDP, so there is no connection to close, instead
// we stop communicating with the server
console.warn(`Server requested ${packet.hide_disconnect_reason ? 'silent disconnect' : 'disconnect'}: ${packet.message}`)
process.exit(1)
}
readPacket(packet) {
// console.log('packet', packet)
const des = this.deserializer.parsePacketBuffer(packet)
console.log('->',des)
const pakData = { name: des.data.name, params: des.data.params }
// console.info('->', JSON.stringify(pakData, (k,v) => typeof v == 'bigint' ? v.toString() : v))
switch (des.data.name) {
case 'server_to_client_handshake':
this.emit('client.server_handshake', des.data.params)
break
case 'disconnect': // Client kicked
this.onDisconnectRequest(des.data.params)
break
case 'crafting_data':
fs.writeFileSync('crafting.json', JSON.stringify(des.data.params, (k,v) => typeof v == 'bigint' ? v.toString() : v))
break
case 'start_game':
fs.writeFileSync('start_game.json', JSON.stringify(des.data.params, (k,v) => typeof v == 'bigint' ? v.toString() : v))
default:
// console.log('Sending to listeners')
}
this.emit(des.data.name, des.data.params)
}
}
module.exports = { Client }