Send skin data, protocol updates (#88)

* Add skin data

* Serialization updates
* Dynamic shield item id
* NBT reading/writing on void type uses 0 length, fix some third party servers

* Fix proxy empty chunk issue

* Fix scoreboards
compiler needs ../

* fix indentation

* Fix set_score packet

* Fix readme title auth doc

* Implement new compiler vars
This commit is contained in:
extremeheat 2021-05-24 10:17:09 -04:00 committed by GitHub
commit f0fbf4f859
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 5557 additions and 5164 deletions

View file

@ -45,6 +45,8 @@ const client = bedrock.createClient({
port: 19132, // optional, default 19132
username: 'Notch', // the username you want to join as, optional if online mode
offline: true // optional, default false. if true, do not login with Xbox Live. You will not be asked to sign-in if set to true.
// Optional for some servers which verify the title ID:
// authTitle: bedrock.title.MinecraftNintendoSwitch
})
client.on('text', (packet) => { // Listen for chat messages and echo them back.
@ -66,8 +68,6 @@ const server = new bedrock.createServer({
host: '0.0.0.0', // optional. host to bind as.
port: 19132, // optional
version: '1.16.220', // optional. The server version, latest if not specified.
// Optional for some servers which verify the title ID:
// authTitle: bedrock.title.MinecraftNintendoSwitch
})
server.on('connect', client => {

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

120
data/1.16.201/steve.json generated Normal file

File diff suppressed because one or more lines are too long

5147
data/1.16.201/steveGeometry.json generated Normal file

File diff suppressed because it is too large Load diff

BIN
data/1.16.201/steveSkin.bin Normal file

Binary file not shown.

View file

@ -422,7 +422,7 @@
{
"compareTo": "network_id",
"fields": {
"355": [
"/ShieldItemID": [
"encapsulated",
{
"lengthType": "varint",
@ -502,7 +502,7 @@
{
"compareTo": "network_id",
"fields": {
"355": [
"/ShieldItemID": [
"encapsulated",
{
"lengthType": "varint",
@ -1933,157 +1933,6 @@
}
]
],
"ScoreEntries": [
"container",
[
{
"name": "type",
"type": [
"mapper",
{
"type": "u8",
"mappings": {
"0": "change",
"1": "remove"
}
}
]
},
{
"name": "entries",
"type": [
"array",
{
"countType": "varint",
"type": [
"container",
[
{
"name": "scoreboard_id",
"type": "zigzag64"
},
{
"name": "objective_name",
"type": "string"
},
{
"name": "score",
"type": "li32"
},
{
"anon": true,
"type": [
"switch",
{
"compareTo": "type",
"fields": {
"remove": [
"container",
[
{
"name": "entry_type",
"type": [
"mapper",
{
"type": "i8",
"mappings": {
"1": "player",
"2": "entity",
"3": "fake_player"
}
}
]
},
{
"name": "entity_unique_id",
"type": [
"switch",
{
"compareTo": "entry_type",
"fields": {
"player": "zigzag64",
"entity": "zigzag64"
},
"default": "void"
}
]
},
{
"name": "custom_name",
"type": [
"switch",
{
"compareTo": "entry_type",
"fields": {
"fake_player": "string"
},
"default": "void"
}
]
}
]
]
},
"default": "void"
}
]
}
]
]
}
]
}
]
],
"ScoreboardIdentityEntries": [
"container",
[
{
"name": "type",
"type": [
"mapper",
{
"type": "i8",
"mappings": {
"0": "TYPE_REGISTER_IDENTITY",
"1": "TYPE_CLEAR_IDENTITY"
}
}
]
},
{
"name": "entries",
"type": [
"array",
{
"countType": "varint",
"type": [
"container",
[
{
"name": "scoreboard_id",
"type": "zigzag64"
},
{
"name": "entity_unique_id",
"type": [
"switch",
{
"compareTo": "type",
"fields": {
"TYPE_REGISTER_IDENTITY": "zigzag64"
},
"default": "void"
}
]
}
]
]
}
]
}
]
],
"Enchant": [
"container",
[
@ -6982,9 +6831,102 @@
"packet_set_score": [
"container",
[
{
"name": "action",
"type": [
"mapper",
{
"type": "u8",
"mappings": {
"0": "change",
"1": "remove"
}
}
]
},
{
"name": "entries",
"type": "ScoreEntries"
"type": [
"array",
{
"countType": "varint",
"type": [
"container",
[
{
"name": "scoreboard_id",
"type": "zigzag64"
},
{
"name": "objective_name",
"type": "string"
},
{
"name": "score",
"type": "li32"
},
{
"anon": true,
"type": [
"switch",
{
"compareTo": "../action",
"fields": {
"change": [
"container",
[
{
"name": "entry_type",
"type": [
"mapper",
{
"type": "i8",
"mappings": {
"1": "player",
"2": "entity",
"3": "fake_player"
}
}
]
},
{
"name": "entity_unique_id",
"type": [
"switch",
{
"compareTo": "entry_type",
"fields": {
"player": "zigzag64",
"entity": "zigzag64"
},
"default": "void"
}
]
},
{
"name": "custom_name",
"type": [
"switch",
{
"compareTo": "entry_type",
"fields": {
"fake_player": "string"
},
"default": "void"
}
]
}
]
]
},
"default": "void"
}
]
}
]
]
}
]
}
]
],
@ -7146,9 +7088,49 @@
"packet_set_scoreboard_identity": [
"container",
[
{
"name": "action",
"type": [
"mapper",
{
"type": "i8",
"mappings": {
"0": "register_identity",
"1": "clear_identity"
}
}
]
},
{
"name": "entries",
"type": "ScoreboardIdentityEntries"
"type": [
"array",
{
"countType": "varint",
"type": [
"container",
[
{
"name": "scoreboard_id",
"type": "zigzag64"
},
{
"name": "entity_unique_id",
"type": [
"switch",
{
"compareTo": "../action",
"fields": {
"register_identity": "zigzag64"
},
"default": "void"
}
]
}
]
]
}
]
}
]
],

View file

@ -1854,10 +1854,32 @@ packet_set_display_objective:
criteria_name: string
sort_order: zigzag32
# SetScore is sent by the server to send the contents of a scoreboard to the player. It may be used to either
# add, remove or edit entries on the scoreboard.
packet_set_score:
!id: 0x6c
!bound: client
entries: ScoreEntries
# ActionType is the type of the action to execute upon the scoreboard with the entries that the packet
# has. If ActionType is ScoreboardActionModify, all entries will be added to the scoreboard if not yet
# present, or modified if already present. If set to ScoreboardActionRemove, all scoreboard entries set
# will be removed from the scoreboard.
action: u8 =>
0: change
1: remove
entries: []varint
scoreboard_id: zigzag64
objective_name: string
score: li32
_: ../action ?
if change:
entry_type: i8 =>
1: player
2: entity
3: fake_player
entity_unique_id: entry_type ?
if player or entity: zigzag64
custom_name: entry_type ?
if fake_player: string
packet_lab_table:
!id: 0x6d
@ -1942,10 +1964,27 @@ DeltaMoveFlags: [ "bitflags",
}
]
# SetScoreboardIdentity is sent by the server to change the identity type of one of the entries on a
# scoreboard. This is used to change, for example, an entry pointing to a player, to a fake player when it
# leaves the server, and to change it back to a real player when it joins again.
# In non-vanilla situations, the packet is quite useless.
packet_set_scoreboard_identity:
!id: 0x70
!bound: client
entries: ScoreboardIdentityEntries
# ActionType is the type of the action to execute. The action is either ScoreboardIdentityActionRegister
# to associate an identity with the entry, or ScoreboardIdentityActionClear to remove associations with
# an entity.
action: i8 =>
0: register_identity
1: clear_identity
# Entries is a list of all entries in the packet. Each of these entries points to one of the entries on
# a scoreboard. Depending on ActionType, their identity will either be registered or cleared.
entries: []varint
scoreboard_id: zigzag64
entity_unique_id: ../action ?
if register_identity: zigzag64
default: void
# SetLocalPlayerAsInitialised is sent by the client in response to a PlayStatus packet with the status set
# to spawn. The packet marks the moment at which the client is fully initialised and can receive any packet

View file

@ -112,7 +112,8 @@ ItemLegacy:
metadata: varint
block_runtime_id: zigzag32
extra: network_id ?
if 355: '["encapsulated", { "lengthType": "varint", "type": "ItemExtraDataWithBlockingTick" }]'
# The Shield Item ID is sent in the StartGame packet. It is usually 355 in vanilla.
if /ShieldItemID: '["encapsulated", { "lengthType": "varint", "type": "ItemExtraDataWithBlockingTick" }]'
default: '["encapsulated", { "lengthType": "varint", "type": "ItemExtraDataWithoutBlockingTick" }]'
# An "ItemStack" here represents an Item instance. You can think about it like a pointer
@ -137,7 +138,9 @@ Item:
default: zigzag32
block_runtime_id: zigzag32
extra: network_id ?
if 355: '["encapsulated", { "lengthType": "varint", "type": "ItemExtraDataWithBlockingTick" }]'
# The Shield Item ID is sent in the StartGame packet. It is usually 355 in vanilla.
## Really bad compiler hack to allow us to use a global variable
if /ShieldItemID: '["encapsulated", { "lengthType": "varint", "type": "ItemExtraDataWithBlockingTick" }]'
default: '["encapsulated", { "lengthType": "varint", "type": "ItemExtraDataWithoutBlockingTick" }]'
vec3i:
@ -708,35 +711,6 @@ PlayerRecords:
verified: type ?
if add: bool[]$records_count
ScoreEntries:
type: u8 =>
0: change
1: remove
entries: []varint
scoreboard_id: zigzag64
objective_name: string
score: li32
_: type?
if remove:
entry_type: i8 =>
1: player
2: entity
3: fake_player
entity_unique_id: entry_type?
if player or entity: zigzag64
custom_name: entry_type?
if fake_player: string
ScoreboardIdentityEntries:
type: i8 =>
0: TYPE_REGISTER_IDENTITY
1: TYPE_CLEAR_IDENTITY
entries: []varint
scoreboard_id: zigzag64
entity_unique_id: type ?
if TYPE_REGISTER_IDENTITY: zigzag64
default: void
Enchant:
id: u8
level: u8

View file

@ -30,7 +30,7 @@
"minecraft-folder-path": "^1.1.0",
"node-fetch": "^2.6.1",
"prismarine-nbt": "^1.5.0",
"protodef": "extremeheat/node-protodef#patch-1",
"protodef": "github:extremeheat/node-protodef#vars",
"smart-buffer": "^4.1.0",
"uuid-1345": "^1.0.2"
},
@ -42,9 +42,9 @@
"bedrock-provider": "^1.0.0",
"babel-eslint": "^10.1.0",
"mocha": "^8.3.2",
"protodef-yaml": "^1.0.3",
"protodef-yaml": "^1.1.0",
"standard": "^16.0.3",
"leveldb-zlib": "0.0.26",
"leveldb-zlib": "^0.0.26",
"bedrock-protocol": "file:."
},
"standard": {

View file

@ -2,7 +2,6 @@ const { ClientStatus, Connection } = require('./connection')
const { createDeserializer, createSerializer } = require('./transforms/serializer')
const { RakClient } = require('./rak')
const { serialize } = require('./datatypes/util')
const fs = require('fs')
const debug = require('debug')('minecraft-protocol')
const Options = require('./options')
const auth = require('./client/auth')
@ -152,33 +151,16 @@ class Client extends Connection {
this.status = ClientStatus.Disconnected
}
tryRencode (name, params, actual) {
const packet = this.serializer.createPacketBuffer({ name, params })
console.assert(packet.equals(actual))
if (!packet.equals(actual)) {
const ours = packet.toString('hex').match(/.{1,16}/g).join('\n')
const theirs = actual.toString('hex').match(/.{1,16}/g).join('\n')
fs.writeFileSync('ours.txt', ours)
fs.writeFileSync('theirs.txt', theirs)
fs.writeFileSync('ours.json', serialize(params))
fs.writeFileSync('theirs.json', serialize(this.deserializer.parsePacketBuffer(packet).data.params))
throw new Error(name + ' Packet comparison failed!')
}
}
readPacket (packet) {
const des = this.deserializer.parsePacketBuffer(packet)
const pakData = { name: des.data.name, params: des.data.params }
this.inLog('-> C', pakData.name/*, serialize(pakData.params).slice(0, 100) */)
this.inLog('-> C', pakData.name, this.options.loggging ? serialize(pakData.params) : '')
this.emit('packet', des)
if (debugging) {
// Packet verifying (decode + re-encode + match test)
if (pakData.name) {
this.tryRencode(pakData.name, pakData.params, packet)
this.deserializer.verify(packet, this.serializer)
}
}
@ -193,6 +175,12 @@ class Client extends Connection {
break
case 'start_game':
this.startGameData = pakData.params
this.startGameData.itemstates.forEach(state => {
if (state.name === 'minecraft:shield') {
this.serializer.proto.setVariable('ShieldItemID', state.runtime_id)
this.deserializer.proto.setVariable('ShieldItemID', state.runtime_id)
}
})
break
case 'play_status':
if (this.status === ClientStatus.Authenticating) {

View file

@ -40,8 +40,25 @@ class Connection extends EventEmitter {
this.encrypt = cipher.createEncryptor(this, iv)
}
updateItemPalette (palette) {
// In the future, we can send down the whole item palette if we need
// but since it's only one item, we can just make a single variable.
let shieldItemID
for (const state of palette) {
if (state.name === 'minecraft:shield') {
shieldItemID = state.runtime_id
break
}
}
if (shieldItemID) {
this.serializer.proto.setVariable('ShieldItemID', shieldItemID)
this.deserializer.proto.setVariable('ShieldItemID', shieldItemID)
}
}
write (name, params) {
this.outLog('sending', name, params)
if (name === 'start_game') this.updateItemPalette(params.itemstates)
const batch = new Framer()
const packet = this.serializer.createPacketBuffer({ name, params })
batch.addEncodedPacket(packet)
@ -55,6 +72,7 @@ class Connection extends EventEmitter {
queue (name, params) {
this.outLog('Q <- ', name, params)
if (name === 'start_game') this.updateItemPalette(params.itemstates)
const packet = this.serializer.createPacketBuffer({ name, params })
if (name === 'level_chunk') {
// Skip queue, send ASAP
@ -113,7 +131,11 @@ class Connection extends EventEmitter {
sendMCPE (buffer, immediate) {
if (this.connection.connected === false || this.status === ClientStatus.Disconnected) return
this.connection.sendReliable(buffer, immediate)
try {
this.connection.sendReliable(buffer, immediate)
} catch (e) {
debug('while sending to', this.connection, e)
}
}
// These are callbacks called from encryption.js

View file

@ -37,10 +37,10 @@ function connect (client) {
response_status: 'completed',
resourcepackids: []
})
client.queue('request_chunk_radius', { chunk_radius: client.renderDistance || 10 })
})
client.queue('client_cache_status', { enabled: false })
client.queue('request_chunk_radius', { chunk_radius: client.renderDistance || 1 })
client.queue('tick_sync', { request_time: BigInt(Date.now()), response_time: 0n })
})

View file

@ -38,14 +38,18 @@ function sizeOfNbt (value) {
// Little Endian
function readNbtLE (buffer, offset) {
return protoLE.read(buffer, offset, 'nbt')
const r = protoLE.read(buffer, offset, 'nbt')
if (r.value.type === 'end') return { value: r.value, size: 0 }
return r
}
function writeNbtLE (value, buffer, offset) {
if (value.type === 'end') return offset
return protoLE.write(value, buffer, offset, 'nbt')
}
function sizeOfNbtLE (value) {
if (value.type === 'end') return 0
return protoLE.sizeOf(value, 'nbt')
}

View file

@ -6,7 +6,10 @@ const { PUBLIC_KEY } = require('./constants')
const algorithm = 'ES384'
module.exports = (client, server, options) => {
const skinGeom = fs.readFileSync(DataProvider(options.protocolVersion).getPath('skin_geom.txt'), 'utf-8')
const dp = DataProvider(options.protocolVersion)
const skinTex = fs.readFileSync(dp.getPath('steveSkin.bin')).toString('base64')
const skinGeom = fs.readFileSync(dp.getPath('steveGeometry.json')).toString('base64')
const skinData = JSON.parse(fs.readFileSync(dp.getPath('steve.json'), 'utf-8'))
client.createClientChain = (mojangKey, offline) => {
const privateKey = client.ecdhKeyPair.privateKey
@ -22,12 +25,12 @@ module.exports = (client, server, options) => {
certificateAuthority: true,
identityPublicKey: client.clientX509
}
token = JWT.sign(payload, privateKey, { algorithm, notBefore: 0, issuer: 'self', expiresIn: 60 * 60, header: { x5u: client.clientX509 } })
token = JWT.sign(payload, privateKey, { algorithm, notBefore: 0, issuer: 'self', expiresIn: 60 * 60, header: { x5u: client.clientX509, typ: undefined } })
} else {
token = JWT.sign({
identityPublicKey: mojangKey || PUBLIC_KEY,
certificateAuthority: true
}, privateKey, { algorithm, header: { x5u: client.clientX509 } })
}, privateKey, { algorithm, header: { x5u: client.clientX509, typ: undefined } })
}
client.clientIdentityChain = token
@ -36,49 +39,38 @@ module.exports = (client, server, options) => {
client.createClientUserChain = (privateKey) => {
let payload = {
AnimatedImageData: [],
ArmSize: 'wide',
CapeData: '',
CapeId: '',
CapeImageHeight: 0,
CapeImageWidth: 0,
CapeOnClassicSkin: false,
...skinData,
ClientRandomId: Date.now(),
CurrentInputMode: 1,
DefaultInputMode: 1,
DeviceId: nextUUID(),
DeviceModel: '',
DeviceModel: 'PrismarineJS',
DeviceOS: client.session?.deviceOS || 7,
GameVersion: options.version || '1.16.201',
GuiScale: -1,
LanguageCode: 'en_GB', // TODO locale
PersonaPieces: [],
PersonaSkin: true,
PieceTintColors: [],
PlatformOfflineId: '',
PlatformOnlineId: '', // chat
// PlayFabID is the PlayFab ID produced for the skin. PlayFab is the company that hosts the Marketplace,
// skins and other related features from the game. This ID is the ID of the skin used to store the skin
// inside of PlayFab.
PlayFabId: '5eb65f73-af11-448e-82aa-1b7b165316ad.persona-e199672a8c1a87e0-0', // 1.16.210
PremiumSkin: false,
PlayFabId: nextUUID().replace(/-/g, '').slice(0, 16), // 1.16.210
SelfSignedId: nextUUID(),
ServerAddress: `${options.host}:${options.port}`,
SkinAnimationData: '',
SkinColor: '#ffffcd96',
SkinData: 'AAAAAA==',
SkinData: skinTex,
SkinGeometryData: skinGeom,
SkinId: '5eb65f73-af11-448e-82aa-1b7b165316ad.persona-e199672a8c1a87e0-0',
SkinImageHeight: 1,
SkinImageWidth: 1,
SkinResourcePatch: '',
ThirdPartyName: client.profile.name,
ThirdPartyNameOnly: false,
UIProfile: 0
}
const customPayload = options.skinData || {}
payload = { ...payload, ...customPayload }
payload.ServerAddress = `${options.host}:${options.port}`
client.clientUserChain = JWT.sign(payload, privateKey, { algorithm, header: { x5u: client.clientX509 } })
client.clientUserChain = JWT.sign(payload, privateKey, { algorithm, header: { x5u: client.clientX509, typ: undefined }, noTimestamp: true /* pocketmine.. */ })
}
}

View file

@ -30,6 +30,7 @@ class RelayPlayer extends Player {
this.outLog = this.downOutLog
this.inLog = this.downInLog
this.chunkSendCache = []
}
// Called when we get a packet from backend server (Backend -> PROXY -> Client)
@ -45,16 +46,23 @@ class RelayPlayer extends Player {
if (name === 'play_status' && params.status === 'login_success') return // We already sent this, this needs to be sent ASAP or client will disconnect
if (debugging) { // some packet encode/decode testing stuff
const rpacket = this.server.serializer.createPacketBuffer({ name, params })
if (!rpacket.equals(packet)) {
console.warn('New', rpacket.toString('hex'))
console.warn('Old', packet.toString('hex'))
console.log('Failed to re-encode', name, params)
process.exit(1)
}
this.server.deserializer.verify(des, this.server.serializer)
}
this.emit('clientbound', des.data)
// If we're sending a chunk, but player isn't yet initialized, wait until it is.
// This is wrong and should not be an issue to send chunks before the client
// is in the world; need to investigate further, but for now it's fine.
if (name === 'level_chunk' && this.status !== 3) {
this.chunkSendCache.push([name, params])
return
} else if (this.status === 3 && this.chunkSendCache.length) {
for (const chunk of this.chunkSendCache) {
this.queue(...chunk)
}
this.chunkSendCache = []
}
this.queue(name, params)
}
@ -82,33 +90,36 @@ class RelayPlayer extends Player {
// Called when the server gets a packet from the downstream player (Client -> PROXY -> Backend)
readPacket (packet) {
if (this.startRelaying) { // The downstream client conn is established & we got a packet to send to upstream server
if (!this.upstream) { // Upstream is still connecting/handshaking
// The downstream client conn is established & we got a packet to send to upstream server
if (this.startRelaying) {
// Upstream is still connecting/handshaking
if (!this.upstream) {
this.downInLog('Got downstream connected packet but upstream is not connected yet, added to q', this.upQ.length)
this.upQ.push(packet) // Put into a queue
return
}
this.flushUpQueue() // Send queued packets
// Send queued packets
this.flushUpQueue()
this.downInLog('recv', 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
const rpacket = this.server.serializer.createPacketBuffer(des.data)
if (!rpacket.equals(packet)) {
console.warn('New', rpacket.toString('hex'))
console.warn('Old', packet.toString('hex'))
console.log('Failed to re-encode', des.data)
process.exit(1)
}
this.server.deserializer.verify(des, this.server.serializer)
}
this.emit('serverbound', des.data)
switch (des.data.name) {
case 'client_cache_status':
// Force the chunk cache off.
this.upstream.queue('client_cache_status', { enabled: false })
break
case 'set_local_player_as_initialized':
this.status = 3
break
default:
// Emit the packet as-is back to the upstream server
this.downInLog('Relaying', des.data)
@ -149,6 +160,10 @@ class Relay extends Server {
client.outLog = ds.upOutLog
client.inLog = ds.upInLog
client.once('join', () => { // Intercept once handshaking done
// Tell the server to disable chunk cache for this connection as a client.
// Wait a bit for the server to ack and process, the continue with proxying
// otherwise the player can get stuck in an empty world.
client.write('client_cache_status', { enabled: false })
ds.upstream = client
ds.flushUpQueue()
this.conLog('Connected to upstream server')

View file

@ -11,6 +11,18 @@ class Parser extends FullPacketParser {
throw e
}
}
verify (deserialized, serializer) {
const { name, params } = deserialized.data
const oldBuffer = deserialized.fullBuffer
const newBuffer = serializer.createPacketBuffer({ name, params })
if (!newBuffer.equals(oldBuffer)) {
console.warn('New', newBuffer.toString('hex'))
console.warn('Old', oldBuffer.toString('hex'))
console.log('Failed to re-encode', name, params)
process.exit(1)
}
}
}
// Compiles the ProtoDef schema at runtime
@ -31,11 +43,8 @@ function getProtocol (version) {
compiler.addTypes(require(join(__dirname, '../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
}
global.PartialReadError = require('protodef/src/utils').PartialReadError
const compile = (compiler, file) => require(file)(compiler.native)
return new CompiledProtodef(
compile(compiler.sizeOfCompiler, join(__dirname, `../../data/${version}/size.js`)),

View file

@ -55,9 +55,9 @@ function createProtocol () {
compiler.addTypes(require('prismarine-nbt/compiler-zigzag'))
compiler.addTypesToCompile(protocol)
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())
fs.writeFileSync('./read.js', 'module.exports = ' + compiler.readCompiler.generate().replace('() =>', 'native =>'))
fs.writeFileSync('./write.js', 'module.exports = ' + compiler.writeCompiler.generate().replace('() =>', 'native =>'))
fs.writeFileSync('./size.js', 'module.exports = ' + compiler.sizeOfCompiler.generate().replace('() =>', 'native =>'))
const compiledProto = compiler.compileProtoDefSync()
return compiledProto