protocol updates, use ProtoDef compiler

This commit is contained in:
extremeheat 2021-02-10 01:32:54 -05:00
commit 60e57fad44
10 changed files with 298 additions and 121 deletions

View file

@ -1,4 +1,5 @@
const fs = require('fs')
const { ProtoDefCompiler } = require('protodef').Compiler
let compile
try {
@ -11,5 +12,23 @@ if (compile) {
compile('./proto.yml', 'protocol.json')
}
fs.writeFileSync( '../newproto.json', JSON.stringify({ types: require('./protocol.json') }, null, 2) )
fs.unlinkSync('./protocol.json') //remove temp file
fs.writeFileSync('../newproto.json', JSON.stringify({ types: require('./protocol.json') }, null, 2))
fs.unlinkSync('./protocol.json') //remove temp file
function createProtocol() {
const compiler = new ProtoDefCompiler()
const protocol = require('../newproto.json').types
compiler.addTypesToCompile(protocol)
compiler.addTypes(require('../../src/datatypes/compiler-minecraft'))
compiler.addTypes(require('prismarine-nbt/compiler-zigzag'))
fs.writeFileSync('../read.js', 'module.exports = ' + compiler.readCompiler.generate())
fs.writeFileSync('../write.js', 'module.exports = ' + compiler.writeCompiler.generate())
fs.writeFileSync('../size.js', 'module.exports = ' + compiler.sizeOfCompiler.generate())
const compiledProto = compiler.compileProtoDefSync()
return compiledProto
}
console.log('Generating JS...')
createProtocol()

View file

@ -1,9 +1,9 @@
!StartDocs: Types
# !StartDocs: Types
BehaviourPackInfos: []li16
uuid: string
version: string
size: lu64
length: lu64
content_key: string
sub_pack_name: string
content_identity: string
@ -12,7 +12,7 @@ BehaviourPackInfos: []li16
TexturePackInfos: []li16
uuid: string
version: string
size: lu64
length: lu64
content_key: string
sub_pack_name: string
content_identity: string
@ -71,11 +71,11 @@ Item:
default:
auxiliary_value: zigzag32
has_nbt: lu16 =>
0xffff: '1'
0x0000: '0'
_: has_nbt?
if 1:
nbt_version: u8
0xffff: 'true'
0x0000: 'false'
nbt: has_nbt?
if true:
version: u8
nbt: nbt
default: void
can_place_on: string[]zigzag32
@ -150,10 +150,10 @@ BlockCoordinates: # mojang...
z: zigzag32
PlayerAttributes: []varint
min: lf32
max: lf32
current: lf32
default: lf32
min_value: lf32
max_value: lf32
current_value: lf32
default_value: lf32
name: string
Transaction:
@ -360,11 +360,10 @@ ScoreEntries:
1: player
2: entity
3: fake_player
_: entry_type?
if player or entity:
entity_unique_id: zigzag64
if fake_player:
custom_name: string
entity_unique_id: entry_type?
if player or entity: zigzag64
custom_name: entry_type?
if fake_player: string
ScoreboardIdentityEntries:
type: i8 =>

View file

@ -25,7 +25,7 @@
"type": "string"
},
{
"name": "size",
"name": "length",
"type": "lu64"
},
{
@ -64,7 +64,7 @@
"type": "string"
},
{
"name": "size",
"name": "length",
"type": "lu64"
},
{
@ -281,24 +281,24 @@
{
"type": "lu16",
"mappings": {
"0": "0",
"65535": "1"
"0": "false",
"65535": "true"
}
}
]
},
{
"anon": true,
"name": "nbt",
"type": [
"switch",
{
"compareTo": "has_nbt",
"fields": {
"1": [
"true": [
"container",
[
{
"name": "nbt_version",
"name": "version",
"type": "u8"
},
{
@ -431,6 +431,10 @@
"type": [
"container",
[
{
"name": "key",
"type": "varint"
},
{
"name": "type",
"type": [
@ -576,19 +580,19 @@
"container",
[
{
"name": "min",
"name": "min_value",
"type": "lf32"
},
{
"name": "max",
"name": "max_value",
"type": "lf32"
},
{
"name": "current",
"name": "current_value",
"type": "lf32"
},
{
"name": "default",
"name": "default_value",
"type": "lf32"
},
{
@ -771,7 +775,7 @@
"type": [
"switch",
{
"compareTo": "../has_network_ids",
"compareTo": "has_network_ids",
"fields": {
"true": "zigzag32"
},
@ -1386,7 +1390,7 @@
"type": [
"switch",
{
"compareTo": "../type",
"compareTo": "type",
"fields": {
"add": [
"container",
@ -1512,7 +1516,7 @@
"type": [
"switch",
{
"compareTo": "../type",
"compareTo": "type",
"fields": {
"remove": [
"container",
@ -1532,39 +1536,27 @@
]
},
{
"anon": true,
"name": "entity_unique_id",
"type": [
"switch",
{
"compareTo": "entry_type",
"fields": {
"player": [
"container",
[
{
"name": "entity_unique_id",
"type": "zigzag64"
}
]
],
"entity": [
"container",
[
{
"name": "entity_unique_id",
"type": "zigzag64"
}
]
],
"fake_player": [
"container",
[
{
"name": "custom_name",
"type": "string"
}
]
]
"player": "zigzag64",
"entity": "zigzag64"
},
"default": "void"
}
]
},
{
"name": "custom_name",
"type": [
"switch",
{
"compareTo": "entry_type",
"fields": {
"fake_player": "string"
},
"default": "void"
}
@ -1618,7 +1610,7 @@
"type": [
"switch",
{
"compareTo": "../type",
"compareTo": "type",
"fields": {
"TYPE_REGISTER_IDENTITY": "zigzag64"
},

View file

@ -28,7 +28,7 @@
"jwt-simple": "^0.5.6",
"lodash.merge": "^4.4.0",
"prismarine-nbt": "github:extremeheat/prismarine-nbt#le",
"protodef": "github:extremeheat/node-protodef#big",
"protodef": "github:extremeheat/node-protodef#compiler",
"raknet": "git+https://github.com/mhsjlw/node-raknet.git#master",
"uuid-1345": "^0.99.7"
},

View file

@ -0,0 +1,39 @@
const UUID = require('uuid-1345')
const minecraft = require('./minecraft')
module.exports = {
Read: {
UUID: ['native', (buffer, offset) => {
return {
value: UUID.stringify(buffer.slice(offset, 16 + offset)),
size: 16
}
}],
restBuffer: ['native', (buffer, offset) => {
return {
value: buffer.slice(offset),
size: buffer.length - offset
}
}],
nbt: ['native', minecraft.nbt[0]]
},
Write: {
UUID: ['native', (value, buffer, offset) => {
const buf = UUID.parse(value)
buf.copy(buffer, offset)
return offset + 16
}],
restBuffer: ['native', (value, buffer, offset) => {
value.copy(buffer, offset)
return offset + value.length
}],
nbt: ['native', minecraft.nbt[1]]
},
SizeOf: {
UUID: ['native', 16],
restBuffer: ['native', (value) => {
return value.length
}],
nbt: ['native', minecraft.nbt[2]]
}
}

12
src/options.js Normal file
View file

@ -0,0 +1,12 @@
// Minimum supported version (< will be kicked)
const MIN_VERSION = 422
// Currently supported verson
const CURRENT_VERSION = 422
const defaultOptions = {
// https://minecraft.gamepedia.com/Protocol_version#Bedrock_Edition_2
version: CURRENT_VERSION
}
module.exports = { defaultOptions, MIN_VERSION, CURRENT_VERSION }

View file

@ -1,29 +1,12 @@
const Listener = require('@jsprismarine/raknet/listener')
const { ProtoDef, Parser, Serializer } = require('protodef')
const { EventEmitter } = require('events')
const { createDeserializer, createSerializer } = require('./transforms/serializer')
const { Encrypt } = require('./auth/encryption')
const { decodeLoginJWT } = require('./auth/chains')
const { Connection } = require('./connection')
const Options = require('./options')
var protocol = require('../data/newproto.json').types;
function createProtocol() {
var proto = new ProtoDef();
proto.addTypes(require('./datatypes/minecraft'));
proto.addTypes(protocol);
return proto;
}
function createSerializer() {
var proto = createProtocol()
return new Serializer(proto, 'mcpe_packet');
}
function createDeserializer() {
var proto = createProtocol()
return new Parser(proto, 'mcpe_packet');
}
const log = (...args) => console.log(...args)
class Player extends Connection {
constructor(server, connection, options) {
@ -102,20 +85,10 @@ class Player extends Connection {
}
}
// Minimum supported version (< will be kicked)
const MIN_VERSION = 422
// Currently supported verson
const CURRENT_VERSION = 422
const defaultServerOptions = {
// https://minecraft.gamepedia.com/Protocol_version#Bedrock_Edition_2
version: CURRENT_VERSION,
}
class Server extends EventEmitter {
constructor(options) {
super()
this.options = { ...defaultServerOptions, options }
this.options = { ...Options.defaultOptions, options }
this.serializer = createSerializer()
this.deserializer = createDeserializer()
this.clients = {}
@ -123,8 +96,8 @@ class Server extends EventEmitter {
}
validateOptions() {
if (this.options.version < defaultServerOptions.version) {
throw new Error(`Unsupported protocol version < ${defaultServerOptions.version}: ${this.options.version}`)
if (this.options.version < Options.MIN_VERSION) {
throw new Error(`Unsupported protocol version < ${Options.MIN_VERSION} : ${this.options.version}`)
}
}
@ -133,7 +106,7 @@ class Server extends EventEmitter {
}
onOpenConnection = (conn) => {
console.log('Got connection', conn)
log('new connection', conn)
const player = new Player(this, conn)
this.clients[this.getAddrHash(conn.address)] = player
@ -141,12 +114,12 @@ class Server extends EventEmitter {
}
onCloseConnection = (inetAddr, reason) => {
console.log('Close connection', inetAddr, reason)
log('close connection', inetAddr, reason)
delete this.clients[this.getAddrHash(inetAddr)]
}
onEncapsulated = (encapsulated, inetAddr) => {
console.log('Encapsulated', encapsulated, inetAddr)
log(inetAddr.address, ': Encapsulated', encapsulated)
const buffer = encapsulated.buffer
const client = this.clients[this.getAddrHash(inetAddr)]
if (!client) {
@ -158,7 +131,7 @@ class Server extends EventEmitter {
async create(serverIp, port) {
this.listener = new Listener(this)
this.raknet = await this.listener.listen(serverIp, port)
console.log('Listening on', serverIp, port)
log('Listening on', serverIp, port)
this.raknet.on('openConnection', this.onOpenConnection)
this.raknet.on('closeConnection', this.onCloseConnection)

View file

@ -42,21 +42,23 @@ server.on('connect', ({ client }) => {
let ids = 0
for (var item of CreativeItems) {
let creativeitem = { runtime_id: items.length }
const has_nbt = !!item.nbt_b64
if (item.id != 0) {
const hasNbt = !!item.nbt_b64
creativeitem.item = {
network_id: item.id,
auxiliary_value: item.damage || 0,
has_nbt: hasNbt|0,
nbt_version: 1,
has_nbt,
nbt: {
version: 1,
},
blocking_tick: 0,
can_destroy: [],
can_place_on: []
}
if (hasNbt) {
if (has_nbt) {
let nbtBuf = Buffer.from(item.nbt_b64, 'base64')
let { result } = await NBT.parse(nbtBuf, 'little')
creativeitem.item.nbt = result
let { parsed } = await NBT.parse(nbtBuf, 'little')
creativeitem.item.nbt.nbt = parsed
}
}
items.push(creativeitem)

View file

@ -1,30 +1,52 @@
'use strict';
var ProtoDef = require('protodef').ProtoDef;
var Serializer = require('protodef').Serializer;
var Parser = require('protodef').Parser;
var protocol = require('../../data/protocol.json').types;
const { ProtoDefCompiler, CompiledProtodef } = require('protodef').Compiler
const { FullPacketParser, Serializer } = require('protodef')
function createProtocol() {
var proto = new ProtoDef();
proto.addTypes(require('../datatypes/minecraft'));
proto.addTypes(protocol);
const protocol = require('../../data/newproto.json').types
console.log('Proto', protocol)
var compiler = new ProtoDefCompiler()
compiler.addTypesToCompile(protocol)
compiler.addTypes(require('../datatypes/compiler-minecraft'))
compiler.addTypes(require('prismarine-nbt/compiler-zigzag'))
return proto;
const compiledProto = compiler.compileProtoDefSync()
return compiledProto
}
function getProtocol() {
const compiler = new ProtoDefCompiler()
compiler.addTypes(require('../datatypes/compiler-minecraft'))
compiler.addTypes(require('prismarine-nbt/compiler-zigzag'))
const compile = (compiler, file) => {
global.native = compiler.native // eslint-disable-line
const { PartialReadError } = require('protodef/src/utils') // eslint-disable-line
return require(file)() // eslint-disable-line
}
return new CompiledProtodef(
compile(compiler.sizeOfCompiler, '../../data/size.js'),
compile(compiler.writeCompiler, '../../data/write.js'),
compile(compiler.readCompiler, '../../data/read.js')
// compiler.sizeOfCompiler.compile(fs.readFileSync(__dirname + '/../../data/size.js', 'utf-8')),
// compiler.writeCompiler.compile(fs.readFileSync(__dirname + '/../../data/write.js', 'utf-8')),
// compiler.readCompiler.compile(fs.readFileSync(__dirname + '/../../data/read.js', 'utf-8'))
)
}
function createSerializer() {
var proto = createProtocol();
return new Serializer(proto, 'packet');
var proto = getProtocol()
return new Serializer(proto, 'mcpe_packet');
}
function createDeserializer() {
var proto = createProtocol();
return new Parser(proto, 'packet');
var proto = getProtocol()
return new FullPacketParser(proto, 'mcpe_packet');
}
module.exports = {
createDeserializer: createDeserializer,
createSerializer: createSerializer,
createProtocol: createProtocol
};
}

119
test/serialization.js Normal file
View file

@ -0,0 +1,119 @@
const { createDeserializer, createSerializer } = require('../src/transforms/serializer')
function test() {
const serializer = createSerializer()
const deserializer = createDeserializer()
function write(name, params) {
const packet = serializer.createPacketBuffer({ name, params })
console.log('Encoded', packet)
return packet
}
function read(packet) {
const des = deserializer.parsePacketBuffer(packet)
return des
}
async function creativeTest() {
let CreativeItems = require('../../data/creativeitems.json')
let items = []
let ids = 0
for (var item of CreativeItems) {
let creativeitem = { runtime_id: items.length }
if (item.id != 0) {
const hasNbt = !!item.nbt_b64
creativeitem.item = {
network_id: item.id,
auxiliary_value: item.damage || 0,
has_nbt: hasNbt,
nbt: { version: 1 },
blocking_tick: 0,
can_destroy: [],
can_place_on: []
}
if (hasNbt) {
let nbtBuf = Buffer.from(item.nbt_b64, 'base64')
let { result } = await NBT.parse(nbtBuf, 'little')
const buf = NBT.writeUncompressed(result, 'littleVarint')
console.log(nbtBuf, buf, JSON.stringify(result))
console.log('\n')
let res2 = await NBT.parse(buf, 'littleVarint')
console.log(JSON.stringify(result), JSON.stringify(res2.result))
console.assert(JSON.stringify(result) == JSON.stringify(res2.result), JSON.stringify(result), JSON.stringify(res2.result))
console.log('\n')
creativeitem.item.nbt.nbt = result
}
}
items.push(creativeitem)
console.log(JSON.stringify(creativeitem))
// console.log(JSON.stringify(creativeitem))
var s = write('creative_content', { items: [creativeitem] })
var d = read(s).data.params
// console.log(JSON.stringify(d), JSON.stringify(s))
// if (JSON.stringify(d) != JSON.stringify(creative_content)) throw 'mismatch'
}
}
async function creativeTst() {
var creativeitem = {
"runtime_id": 1166,
"item": {
"network_id": 403,
"auxiliary_value": 0,
"has_nbt": true,
"nbt": {
"version": 1,
"nbt": {
"type": "compound",
"name": "",
"value": {
"ench": {
"type": "list",
"value": {
"type": "compound",
"value": [
{
"id": {
"type": "short",
"value": 0
},
"lvl": {
"type": "short",
"value": 1
}
}
]
}
}
}
}
},
"blocking_tick": 0,
"can_destroy": [],
"can_place_on": []
}
}
var s = write('creative_content', { items: [creativeitem] })
var d = read(s).data.params
console.log(JSON.stringify(d))
}
creativeTst()
}
if (!module.parent) {
test()
}