Skip to content

Commit e218297

Browse files
authored
add addTypesToCompiler and addTypesToInterperter method for ProtoDef (#81)
* add addTypesToCompiler and addTypesToInterperter method for ProtoDef * add nbtTagName alias to shortString for interperter * add to types * add optionalNbt type from node-minecraft-protocol * fix naming * add anonOptionalNbt and optionalNbt types from nmp protocol.json, remove broken tagType defaults * fix optional compiler code * run benchmark script on test to test addTypesToCompiler/addTypesToInterpreter * Update zigzag.js * Update zigzag.js lint * update nbt.json
1 parent 3d02c78 commit e218297

File tree

8 files changed

+163
-35
lines changed

8 files changed

+163
-35
lines changed

README.md

+11-6
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,16 @@ Prismarine-NBT is a JavaScript parser and serializer for [NBT](http://wiki.vg/NB
1212
#### as a async promise
1313

1414
```js
15-
const fs = require('fs/promises')
15+
const fs = require('fs')
1616
const nbt = require('prismarine-nbt')
1717

1818
async function main(file) {
19-
const buffer = await fs.readFile(file)
19+
const buffer = fs.readFileSync(file)
2020
const { parsed, type } = await nbt.parse(buffer)
21-
console.log('JSON serialized', JSON.stringify(result, null, 2))
22-
fs.createWriteStream('file.nbt').write(nbt.writeUncompressed(result, type)) // Write it back
21+
console.log('JSON serialized', JSON.stringify(parsed, null, 2))
22+
fs.createWriteStream('bigtest.nbt').write(nbt.writeUncompressed(parsed, type)) // Write it back
2323
}
24-
25-
main('file.nbt')
24+
main('bigtest.nbt')
2625
```
2726

2827
#### as a callback
@@ -85,6 +84,12 @@ Provide the big-endian protodef instance used to parse and serialize nbt.
8584

8685
Provide the little-endian protodef instance used to parse and serialize little endian nbt.
8786

87+
### addTypesToCompiler (type, compiler)
88+
Adds prismarine-nbt types to an ProtoDef compiler instance
89+
90+
### addTypesToInterpreter (type, interperter)
91+
Adds prismarine-nbt types to a ProtoDef interpreter instance
92+
8893
### builder
8994

9095
Provides a way to build complex nbt structures simply:

bench/compiled_nbt.js

+13-14
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,19 @@ const { performance } = require('perf_hooks')
33
const assert = require('assert')
44
const { ProtoDefCompiler } = require('protodef').Compiler
55
const fs = require('fs')
6+
const nbt = require('../nbt')
67

78
const mainType = 'nbt'
89

9-
fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
10-
if (error) {
11-
throw error
12-
}
13-
14-
const proto = new ProtoDef()
15-
proto.addTypes(require('../compound'))
16-
proto.addTypes(require('../nbt.json'))
10+
function main (nbTests = 10000) {
11+
const buffer = fs.readFileSync(__dirname + '/../sample/bigtest.nbt') // eslint-disable-line n/no-path-concat
12+
const validate = true
13+
const proto = new ProtoDef(validate)
14+
nbt.addTypesToInterpreter('big', proto)
1715

1816
const compiler = new ProtoDefCompiler()
19-
compiler.addTypes(require('../compiler-compound'))
20-
compiler.addTypesToCompile(require('../nbt.json'))
21-
const compiledProto = await compiler.compileProtoDef()
17+
nbt.addTypesToCompiler('big', compiler)
18+
const compiledProto = compiler.compileProtoDefSync()
2219

2320
const result = compiledProto.parsePacketBuffer(mainType, buffer).data
2421
const result2 = proto.parsePacketBuffer(mainType, buffer).data
@@ -30,7 +27,6 @@ fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
3027
assert.deepStrictEqual(result2, result3)
3128
assert.strictEqual(buffer.length, buffer2.length)
3229

33-
const nbTests = 10000
3430
console.log('Running ' + nbTests + ' tests')
3531

3632
let start, time, ps
@@ -54,7 +50,7 @@ fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
5450
console.log('read / write parser: ' + time.toFixed(2) + ' ms (' + ps.toFixed(2) + 'k packet/s)')
5551

5652
// Closure optimized:
57-
const optimizedProto = await compiler.compileProtoDef({ optimize: true })
53+
const optimizedProto = compiler.compileProtoDefSync({ optimize: true })
5854
start = performance.now()
5955
for (let i = 0; i < nbTests; i++) {
6056
const result = optimizedProto.parsePacketBuffer(mainType, buffer).data
@@ -63,4 +59,7 @@ fs.readFile('../sample/bigtest.nbt', async (error, buffer) => {
6359
time = performance.now() - start
6460
ps = nbTests / time
6561
console.log('read / write compiled (+closure): ' + time.toFixed(2) + ' ms (' + ps.toFixed(2) + 'k packet/s)')
66-
})
62+
}
63+
64+
module.exports = main
65+
if (!module.parent) main()

nbt.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,37 @@ const beNbtJson = JSON.stringify(require('./nbt.json'))
66
const leNbtJson = beNbtJson.replace(/([iuf][0-7]+)/g, 'l$1')
77
const varintJson = JSON.stringify(require('./nbt-varint.json')).replace(/([if][0-7]+)/g, 'l$1')
88

9-
function createProto (type) {
10-
const compiler = new ProtoDefCompiler()
9+
function addTypesToCompiler (type, compiler) {
1110
compiler.addTypes(require('./compiler-compound'))
1211
compiler.addTypes(require('./compiler-tagname'))
12+
compiler.addTypes(require('./optional').compiler)
13+
compiler.addTypes(require('./zigzag').compiler)
1314
let proto = beNbtJson
1415
if (type === 'littleVarint') {
15-
compiler.addTypes(require('./compiler-zigzag'))
1616
proto = varintJson
1717
} else if (type === 'little') {
1818
proto = leNbtJson
1919
}
2020
compiler.addTypesToCompile(JSON.parse(proto))
21+
}
22+
23+
function addTypesToInterpreter (type, compiler) {
24+
compiler.addTypes(require('./compound'))
25+
compiler.addTypes(require('./optional').interpret)
26+
compiler.addTypes(require('./zigzag').interpret)
27+
let proto = beNbtJson
28+
if (type === 'littleVarint') {
29+
proto = varintJson
30+
} else if (type === 'little') {
31+
proto = leNbtJson
32+
}
33+
compiler.addTypes(JSON.parse(proto))
34+
compiler.types.nbtTagName = compiler.types.shortString
35+
}
36+
37+
function createProto (type) {
38+
const compiler = new ProtoDefCompiler()
39+
addTypesToCompiler(type, compiler)
2140
return compiler.compileProtoDefSync()
2241
}
2342

@@ -228,6 +247,8 @@ const builder = {
228247
}
229248

230249
module.exports = {
250+
addTypesToCompiler,
251+
addTypesToInterpreter,
231252
writeUncompressed,
232253
parseUncompressed,
233254
simplify,

nbt.json

+41-9
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@
44
"i8": "native",
55
"switch": "native",
66
"compound": "native",
7+
"nbtTagName": "native",
78
"i16": "native",
89
"u16": "native",
910
"i32": "native",
1011
"i64": "native",
1112
"f32": "native",
1213
"f64": "native",
1314
"pstring": "native",
14-
"shortString": ["pstring",{
15-
"countType":"u16"
16-
}],
15+
"shortString": [
16+
"pstring",
17+
{
18+
"countType": "u16"
19+
}
20+
],
1721
"byteArray": [
1822
"array",
1923
{
@@ -34,7 +38,12 @@
3438
"array",
3539
{
3640
"countType": "i32",
37-
"type": ["nbtSwitch",{"type":"type"}]
41+
"type": [
42+
"nbtSwitch",
43+
{
44+
"type": "type"
45+
}
46+
]
3847
}
3948
]
4049
}
@@ -54,7 +63,8 @@
5463
"type": "i64"
5564
}
5665
],
57-
"nbtMapper":["mapper",
66+
"nbtMapper": [
67+
"mapper",
5868
{
5969
"type": "i8",
6070
"mappings": {
@@ -74,7 +84,7 @@
7484
}
7585
}
7686
],
77-
"nbtSwitch":[
87+
"nbtSwitch": [
7888
"switch",
7989
{
8090
"compareTo": "$type",
@@ -108,7 +118,12 @@
108118
},
109119
{
110120
"name": "value",
111-
"type": ["nbtSwitch",{"type":"type"}]
121+
"type": [
122+
"nbtSwitch",
123+
{
124+
"type": "type"
125+
}
126+
]
112127
}
113128
]
114129
],
@@ -121,8 +136,25 @@
121136
},
122137
{
123138
"name": "value",
124-
"type": ["nbtSwitch",{"type":"type"}]
139+
"type": [
140+
"nbtSwitch",
141+
{
142+
"type": "type"
143+
}
144+
]
125145
}
126146
]
147+
],
148+
"anonOptionalNbt": [
149+
"optionalNbtType",
150+
{
151+
"tagType": "anonymousNbt"
152+
}
153+
],
154+
"optionalNbt": [
155+
"optionalNbtType",
156+
{
157+
"tagType": "nbt"
158+
}
127159
]
128-
}
160+
}

optional.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function readOptionalNbt (buffer, offset, { tagType }, rootNode) {
2+
if (offset + 1 > buffer.length) { throw new Error('Read out of bounds') }
3+
if (buffer.readInt8(offset) === 0) return { size: 1 }
4+
return this.read(buffer, offset, tagType, rootNode)
5+
}
6+
7+
function writeOptionalNbt (value, buffer, offset, { tagType }, rootNode) {
8+
if (value === undefined) {
9+
buffer.writeInt8(0, offset)
10+
return offset + 1
11+
}
12+
return this.write(value, buffer, offset, tagType, rootNode)
13+
}
14+
15+
function sizeOfOptionalNbt (value, { tagType }, rootNode) {
16+
if (value === undefined) { return 1 }
17+
return this.sizeOf(value, tagType, tagType, rootNode)
18+
}
19+
20+
const compiler = {
21+
Read: {
22+
optionalNbtType: ['parametrizable', (compiler, { tagType }) => {
23+
return compiler.wrapCode(`
24+
if (offset + 1 > buffer.length) { throw new PartialReadError() }
25+
if (buffer.readInt8(offset) === 0) return { size: 1 }
26+
return ${compiler.callType(tagType)}
27+
`)
28+
}]
29+
},
30+
Write: {
31+
optionalNbtType: ['parametrizable', (compiler, { tagType }) => {
32+
return compiler.wrapCode(`
33+
if (value === undefined) {
34+
buffer.writeInt8(0, offset)
35+
return offset + 1
36+
}
37+
return ${compiler.callType('value', tagType)}
38+
`)
39+
}]
40+
},
41+
SizeOf: {
42+
optionalNbtType: ['parametrizable', (compiler, { tagType }) => {
43+
return compiler.wrapCode(`
44+
if (value === undefined) { return 1 }
45+
return ${compiler.callType('value', tagType)}
46+
`)
47+
}]
48+
}
49+
}
50+
51+
module.exports = {
52+
compiler,
53+
interpret: { optionalNbtType: [readOptionalNbt, writeOptionalNbt, sizeOfOptionalNbt] }
54+
}

test/protodef.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* eslint-env mocha */
2+
const bench = require('../bench/compiled_nbt')
3+
describe('protodef', function () {
4+
it('benchmark', () => {
5+
bench(1000)
6+
})
7+
})

typings/index.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ declare module 'prismarine-nbt'{
5757
export const proto: any;
5858
// Little Endian protocol
5959
export const protoLE: any;
60+
// Adds prismarine-nbt types to an ProtoDef compiler instance
61+
export function addTypesToCompiler(type: NBTFormat, compiler)
62+
// Adds prismarine-nbt types to a ProtoDef interpreter instance
63+
export function addTypesToInterpreter(type: NBTFormat, protodef)
6064

6165
/** @deprecated */
6266
export function writeUncompressed(value: NBT, little?: boolean): Buffer;

compiler-zigzag.js renamed to zigzag.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,13 @@ function writeSignedVarInt (value, buffer, offset) {
119119
}
120120

121121
module.exports = {
122-
Read: { zigzag64: ['native', readSignedVarLong], zigzag32: ['native', readSignedVarInt] },
123-
Write: { zigzag64: ['native', writeSignedVarLong], zigzag32: ['native', writeSignedVarInt] },
124-
SizeOf: { zigzag64: ['native', sizeOfVarLong], zigzag32: ['native', sizeOfVarInt] }
122+
compiler: {
123+
Read: { zigzag64: ['native', readSignedVarLong], zigzag32: ['native', readSignedVarInt] },
124+
Write: { zigzag64: ['native', writeSignedVarLong], zigzag32: ['native', writeSignedVarInt] },
125+
SizeOf: { zigzag64: ['native', sizeOfVarLong], zigzag32: ['native', sizeOfVarInt] }
126+
},
127+
interpret: {
128+
zigzag64: [readSignedVarLong, writeSignedVarLong, sizeOfVarLong],
129+
zigzag32: [readSignedVarInt, writeSignedVarInt, sizeOfVarInt]
130+
}
125131
}

0 commit comments

Comments
 (0)