client close handling, add spawn event (#48)
This commit is contained in:
parent
99cce3b371
commit
bd97a8e1b7
10 changed files with 102 additions and 49 deletions
|
|
@ -1048,23 +1048,24 @@
|
|||
}
|
||||
]
|
||||
],
|
||||
"ItemStack": [
|
||||
"container",
|
||||
[
|
||||
{
|
||||
"name": "runtime_id",
|
||||
"type": "zigzag32"
|
||||
},
|
||||
{
|
||||
"name": "item",
|
||||
"type": "Item"
|
||||
}
|
||||
]
|
||||
],
|
||||
"ItemStacks": [
|
||||
"array",
|
||||
{
|
||||
"countType": "varint",
|
||||
"type": [
|
||||
"container",
|
||||
[
|
||||
{
|
||||
"name": "runtime_id",
|
||||
"type": "zigzag32"
|
||||
},
|
||||
{
|
||||
"name": "item",
|
||||
"type": "Item"
|
||||
}
|
||||
]
|
||||
]
|
||||
"type": "ItemStack"
|
||||
}
|
||||
],
|
||||
"RecipeIngredient": [
|
||||
|
|
@ -4600,13 +4601,9 @@
|
|||
"name": "slot",
|
||||
"type": "varint"
|
||||
},
|
||||
{
|
||||
"name": "uniqueid",
|
||||
"type": "zigzag32"
|
||||
},
|
||||
{
|
||||
"name": "item",
|
||||
"type": "Item"
|
||||
"type": "ItemStack"
|
||||
}
|
||||
]
|
||||
],
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Parse and serialize Minecraft Bedrock Edition packets",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "cd data/new && node compile.js",
|
||||
"build": "cd tools && node compileProtocol.js",
|
||||
"prepare": "npm run build",
|
||||
"test": "mocha",
|
||||
"pretest": "npm run lint",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
const { Ber } = require('asn1')
|
||||
const { ClientStatus } = require('../connection')
|
||||
const JWT = require('jsonwebtoken')
|
||||
const crypto = require('crypto')
|
||||
const ecPem = require('ec-pem')
|
||||
|
|
@ -82,6 +83,7 @@ function Encrypt (client, server, options) {
|
|||
// It works! First encrypted packet :)
|
||||
client.write('client_to_server_handshake', {})
|
||||
this.emit('join')
|
||||
client.status = ClientStatus.Initializing
|
||||
}
|
||||
|
||||
client.on('server.client_handshake', startClientboundEncryption)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const { Connection } = require('./connection')
|
||||
const { ClientStatus, Connection } = require('./connection')
|
||||
const { createDeserializer, createSerializer } = require('./transforms/serializer')
|
||||
const { RakClient } = require('./Rak')
|
||||
const { serialize } = require('./datatypes/util')
|
||||
|
|
@ -32,10 +32,12 @@ class Client extends Connection {
|
|||
auth.authenticateDeviceCode(this, options)
|
||||
}
|
||||
|
||||
this.startGameData = {}
|
||||
|
||||
this.on('session', this.connect)
|
||||
this.startQueue()
|
||||
this.inLog = (...args) => console.info('C ->', ...args)
|
||||
this.outLog = (...args) => console.info('C <-', ...args)
|
||||
this.inLog = (...args) => debug('C ->', ...args)
|
||||
this.outLog = (...args) => debug('C <-', ...args)
|
||||
}
|
||||
|
||||
validateOptions () {
|
||||
|
|
@ -62,11 +64,13 @@ class Client extends Connection {
|
|||
|
||||
this.connection = new RakClient({ useWorkers: true, hostname, port })
|
||||
this.connection.onConnected = () => this.sendLogin()
|
||||
this.connection.onCloseConnection = () => this._close()
|
||||
this.connection.onEncapsulated = this.onEncapsulated
|
||||
this.connection.connect()
|
||||
}
|
||||
|
||||
sendLogin () {
|
||||
this.status = ClientStatus.Authenticating
|
||||
this.createClientChain()
|
||||
|
||||
const chain = [
|
||||
|
|
@ -96,8 +100,25 @@ class Client extends Connection {
|
|||
process.exit(1) // TODO: handle
|
||||
}
|
||||
|
||||
onPlayStatus(statusPacket) {
|
||||
if (this.status == ClientStatus.Initializing && this.options.autoInitPlayer === true) {
|
||||
if (statusPacket.status === 'player_spawn') {
|
||||
this.status = ClientStatus.Initialized
|
||||
this.write('set_local_player_as_initialized', { runtime_entity_id: this.startGameData.runtime_entity_id })
|
||||
this.emit('spawn')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_close() {
|
||||
this.q = []
|
||||
this.q2 = []
|
||||
}
|
||||
|
||||
close () {
|
||||
console.warn('Close not implemented!!')
|
||||
this._close()
|
||||
this.connection.close()
|
||||
console.log('Closed!')
|
||||
}
|
||||
|
||||
tryRencode (name, params, actual) {
|
||||
|
|
@ -147,15 +168,12 @@ class Client extends Connection {
|
|||
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))
|
||||
// break
|
||||
// case 'level_chunk':
|
||||
// // fs.writeFileSync(`./chunks/chunk-${chunks++}.txt`, packet.toString('hex'))
|
||||
// break
|
||||
case 'start_game':
|
||||
this.startGameData = pakData.params
|
||||
break
|
||||
case 'play_status':
|
||||
this.onPlayStatus(pakData.params)
|
||||
break
|
||||
default:
|
||||
// console.log('Sending to listeners')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,16 @@ const debug = require('debug')('minecraft-protocol')
|
|||
|
||||
const SKIP_BATCH = ['level_chunk', 'client_cache_blob_status', 'client_cache_miss_response']
|
||||
|
||||
const ClientStatus = {
|
||||
Disconnected: 0,
|
||||
Authenticating: 1, // Handshaking
|
||||
Initializing: 2, // Authed, need to spawn
|
||||
Initialized: 3 // play_status spawn sent by server, client responded with SetPlayerInit packet
|
||||
}
|
||||
|
||||
class Connection extends EventEmitter {
|
||||
state = ClientStatus.Disconnected
|
||||
|
||||
versionLessThan (version) {
|
||||
if (typeof version === 'string') {
|
||||
return Versions[version] < this.options.version
|
||||
|
|
@ -112,6 +121,7 @@ class Connection extends EventEmitter {
|
|||
|
||||
// TODO: Rename this to sendEncapsulated
|
||||
sendMCPE (buffer, immediate) {
|
||||
if (this.connection.connected === false) return
|
||||
this.connection.sendReliable(buffer, immediate)
|
||||
}
|
||||
|
||||
|
|
@ -151,4 +161,4 @@ class Connection extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = { Connection }
|
||||
module.exports = { ClientStatus, Connection }
|
||||
|
|
|
|||
|
|
@ -5,11 +5,16 @@ const CURRENT_VERSION = '1.16.201'
|
|||
|
||||
const defaultOptions = {
|
||||
// https://minecraft.gamepedia.com/Protocol_version#Bedrock_Edition_2
|
||||
version: CURRENT_VERSION
|
||||
version: CURRENT_VERSION,
|
||||
// client: If we should send SetPlayerInitialized to the server after getting play_status spawn.
|
||||
// if this is disabled, no 'spawn' event will be emitted, you should manually set
|
||||
// client.status to ClientStatus.Initialized after sending the init packet.
|
||||
autoInitPlayer: true
|
||||
}
|
||||
|
||||
const Versions = {
|
||||
'1.16.210': 428,
|
||||
// TODO
|
||||
// '1.16.210': 428,
|
||||
'1.16.201': 422
|
||||
}
|
||||
|
||||
|
|
|
|||
19
src/rak.js
19
src/rak.js
|
|
@ -14,6 +14,7 @@ try {
|
|||
class RakNativeClient extends EventEmitter {
|
||||
constructor (options) {
|
||||
super()
|
||||
this.connected = false
|
||||
this.onConnected = () => { }
|
||||
this.onCloseConnection = () => { }
|
||||
this.onEncapsulated = () => { }
|
||||
|
|
@ -23,8 +24,14 @@ class RakNativeClient extends EventEmitter {
|
|||
this.onEncapsulated(buffer, address)
|
||||
})
|
||||
this.raknet.on('connected', () => {
|
||||
this.connected = true
|
||||
this.onConnected()
|
||||
})
|
||||
|
||||
this.raknet.on('disconnected', ({ reason }) => {
|
||||
this.connected = false
|
||||
this.onCloseConnection(reason)
|
||||
})
|
||||
}
|
||||
|
||||
async ping () {
|
||||
|
|
@ -42,7 +49,15 @@ class RakNativeClient extends EventEmitter {
|
|||
this.raknet.connect()
|
||||
}
|
||||
|
||||
close() {
|
||||
this.connected = false
|
||||
setTimeout(() => {
|
||||
this.raknet.close()
|
||||
}, 40)
|
||||
}
|
||||
|
||||
sendReliable (buffer, immediate) {
|
||||
if (!this.connected) return
|
||||
const priority = immediate ? PacketPriority.IMMEDIATE_PRIORITY : PacketPriority.MEDIUM_PRIORITY
|
||||
return this.raknet.send(buffer, priority, PacketReliability.RELIABLE_ORDERED, 0)
|
||||
}
|
||||
|
|
@ -83,6 +98,10 @@ class RakNativeServer extends EventEmitter {
|
|||
listen () {
|
||||
this.raknet.listen()
|
||||
}
|
||||
|
||||
close() {
|
||||
this.raknet.close()
|
||||
}
|
||||
}
|
||||
|
||||
class RakJsClient extends EventEmitter {
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ class RelayPlayer extends Player {
|
|||
}
|
||||
this.flushUpQueue() // Send queued packets
|
||||
this.downInLog('Recv packet', packet)
|
||||
// TODO: If we fail to parse a packet, proxy it raw and log an error
|
||||
const des = this.server.deserializer.parsePacketBuffer(packet)
|
||||
|
||||
if (debugging) { // some packet encode/decode testing stuff
|
||||
|
|
@ -150,7 +151,8 @@ class Relay extends Server {
|
|||
const client = new Client({
|
||||
hostname: this.options.destination.hostname,
|
||||
port: this.options.destination.port,
|
||||
encrypt: this.options.encrypt
|
||||
encrypt: this.options.encrypt,
|
||||
autoInitPlayer: false
|
||||
})
|
||||
client.outLog = ds.upOutLog
|
||||
client.inLog = ds.upInLog
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const { Connection } = require('./connection')
|
||||
const { ClientStatus, Connection } = require('./connection')
|
||||
const fs = require('fs')
|
||||
const Options = require('./options')
|
||||
|
||||
|
|
@ -6,12 +6,6 @@ const { Encrypt } = require('./auth/encryption')
|
|||
const Login = require('./auth/login')
|
||||
const LoginVerify = require('./auth/loginVerify')
|
||||
|
||||
const ClientStatus = {
|
||||
Authenticating: 0,
|
||||
Initializing: 1,
|
||||
Initialized: 2
|
||||
}
|
||||
|
||||
class Player extends Connection {
|
||||
constructor (server, connection) {
|
||||
super()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
*/
|
||||
const fs = require('fs')
|
||||
const { ProtoDefCompiler } = require('protodef').Compiler
|
||||
const { join } = require('path')
|
||||
|
||||
function getJSON (path) {
|
||||
return JSON.parse(fs.readFileSync(path, 'utf-8'))
|
||||
}
|
||||
|
||||
// Parse the YML files and turn to JSON
|
||||
function genProtoSchema () {
|
||||
|
|
@ -37,15 +42,15 @@ function genProtoSchema () {
|
|||
const t = `#Auto-generated from proto.yml, do not modify\n!import: types.yaml\nmcpe_packet:\n name: varint =>\n${l1}\n params: name ?\n${l2}`
|
||||
fs.writeFileSync('./packet_map.yml', t)
|
||||
|
||||
compile('./proto.yml', 'protocol.json')
|
||||
compile('./proto.yml', 'proto.json')
|
||||
return version
|
||||
}
|
||||
|
||||
// Compile the ProtoDef JSON into JS
|
||||
function createProtocol (version) {
|
||||
const compiler = new ProtoDefCompiler()
|
||||
const protocol = require(`../${version}/protocol.json`).types
|
||||
compiler.addTypes(require('../../src/datatypes/compiler-minecraft'))
|
||||
const protocol = getJSON(`../${version}/protocol.json`).types
|
||||
compiler.addTypes(require('../src/datatypes/compiler-minecraft'))
|
||||
compiler.addTypes(require('prismarine-nbt/compiler-zigzag'))
|
||||
compiler.addTypesToCompile(protocol)
|
||||
|
||||
|
|
@ -57,11 +62,12 @@ function createProtocol (version) {
|
|||
return compiledProto
|
||||
}
|
||||
|
||||
function main () {
|
||||
function main (ver = 'latest') {
|
||||
process.chdir(join(__dirname, '/../data/', ver))
|
||||
const version = genProtoSchema()
|
||||
|
||||
fs.writeFileSync(`../${version}/protocol.json`, JSON.stringify({ types: require('./protocol.json') }, null, 2))
|
||||
fs.unlinkSync('./protocol.json') // remove temp file
|
||||
fs.writeFileSync(`../${version}/protocol.json`, JSON.stringify({ types: getJSON('./proto.json') }, null, 2))
|
||||
fs.unlinkSync('./proto.json') // remove temp file
|
||||
fs.unlinkSync('./packet_map.yml') // remove temp file
|
||||
|
||||
console.log('Generating JS...')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue