diff --git a/README.md b/README.md index eac859a..4f6433d 100644 --- a/README.md +++ b/README.md @@ -42,3 +42,29 @@ chat type should be formatted and what the relevant parameters are. #### .dimensionsById, dimensionsByName (1.19+) Mapping to dimension data object containing dimension `name`, `minY` and `height`. + +### mcpe + +#### loadItemStates / writeItemStates + +* loads/writes data from an item states array inside the bedrock start game packet. + +```js +// In a client +const { createClient } = require('bedrock-protocol'); +const registry = require('prismarine-registry')('bedrock_1.19.50'); + +const client = createClient({ + 'host': '127.0.0.1' +}) + +client.on('start_game', ({ itemstates }) => { + registry.loadItemStates(itemstates); +}) + +// In a server +server.on('connect', (client) => { + const itemstates = registry.writeItemStates() + client.write('start_game', { ...startGamePacket, itemstates }) +}) +``` \ No newline at end of file diff --git a/lib/bedrock/index.js b/lib/bedrock/index.js index 7cca41f..cf893e8 100644 --- a/lib/bedrock/index.js +++ b/lib/bedrock/index.js @@ -1 +1,32 @@ -module.exports = (data) => ({}) +const buildIndexFromArray = require('../indexer') + +module.exports = (data) => { + return { + loadItemStates (itemStates) { + const items = [] + for (const item of itemStates) { + const name = item.name.replace('minecraft:', '') + items.push({ ...data.itemsByName[name], name, id: item.runtime_id }) + } + data.itemsArray = items + data.items = buildIndexFromArray(data.itemsArray, 'id') + data.itemsByName = buildIndexFromArray(data.itemsArray, 'name') + }, + + writeItemStates () { + const itemstates = [] + for (const item of data.itemsArray) { + // Custom items with different namespaces can also be in the palette + let [ns, name] = item.name.split(':') + if (!name) { + name = ns + ns = 'minecraft' + } + + itemstates.push({ name: `${ns}:${name}`, runtime_id: item.id, component_based: ns !== 'minecraft' }) + } + + return itemstates + } + } +} diff --git a/lib/index.d.ts b/lib/index.d.ts index 6e76fad..3c7c0f6 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,13 +1,24 @@ -import {IndexedData} from 'minecraft-data' -import {NBT} from 'prismarine-nbt' +import { IndexedData } from 'minecraft-data' +import { NBT } from 'prismarine-nbt' -interface PCRegistry extends IndexedData { - loadDimensionCodec(codec: NBT): void - writeDimensionCodec(): NBT +declare function loader(mcVersion: string): loader.Registry +declare namespace loader { + export interface RegistryPc extends IndexedData { + loadDimensionCodec(codec: NBT): void; + writeDimensionCodec(): NBT; + } + + export interface RegistryBedrock extends IndexedData { + loadItemStates(itemStates: ItemState[]): void; + writeItemStates(): ItemState[]; + } + + export type Registry = RegistryBedrock | RegistryPc + export type ItemState = { + name: string + runtime_id: number + component_based: boolean + } } -interface BedrockRegistry extends IndexedData { - -} -export type Registry = PCRegistry & BedrockRegistry -export default function loader(mcVersion: string): Registry +export = loader \ No newline at end of file diff --git a/lib/indexer.js b/lib/indexer.js new file mode 100644 index 0000000..922d7e2 --- /dev/null +++ b/lib/indexer.js @@ -0,0 +1,7 @@ +module.exports = function buildIndexFromArray (array, fieldToIndex) { + if (array === undefined) { return undefined } + return array.reduce(function (index, element) { + index[element[fieldToIndex]] = element + return index + }, {}) +} diff --git a/package.json b/package.json index ded779b..b68c96c 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ }, "homepage": "https://github.com/PrismarineJS/prismarine-registry#readme", "devDependencies": { + "bedrock-protocol": "^3.22.0", "debug": "^4.3.3", + "minecraft-bedrock-server": "^1.1.2", "minecraft-protocol": "^1.30.0", "minecraft-wrap": "^1.4.0", "mocha": "^10.0.0", diff --git a/test/mcbedrock.js b/test/mcbedrock.js new file mode 100644 index 0000000..f4ca665 --- /dev/null +++ b/test/mcbedrock.js @@ -0,0 +1,31 @@ +const Registry = require('prismarine-registry') +const collectPackets = require('./util/collectBedrockPackets') +const assert = require('assert') + +async function main (version = '1.19.63') { + const registry = Registry(`bedrock_${version}`) + let loggedIn = false + const handlers = { + start_game (version, params) { + registry.loadItemStates(params.itemstates) + console.log('Loaded item palette', registry.items) + + const reEncoded = registry.writeItemStates() + assert.deepEqual( + reEncoded.sort((a, b) => a.runtime_id - b.runtime_id), + params.itemstates.sort((a, b) => a.runtime_id - b.runtime_id) + ) + console.log('Re-encoded item palette') + + loggedIn = true + } + } + + await collectPackets(version, Object.keys(handlers), (name, params) => handlers[name](version, params)) + await new Promise((resolve) => setTimeout(resolve, 6000)) + if (!loggedIn) { + throw new Error('Did not login') + } +} + +module.exports = main diff --git a/test/mcbedrock.test.js b/test/mcbedrock.test.js new file mode 100644 index 0000000..eee688c --- /dev/null +++ b/test/mcbedrock.test.js @@ -0,0 +1,12 @@ +/* eslint-env mocha */ + +const SUPPORTED_VERSIONS = ['1.17.10', '1.18.0', '1.18.11', '1.18.30', '1.19.1', '1.19.10'] +const test = require('./mcbedrock') + +describe('mcbedrock', function () { + this.timeout(9000 * 10) + + for (const version of SUPPORTED_VERSIONS) { + it('works on ' + version, () => test(version)) + } +}) diff --git a/test/util/collectBedrockPackets.js b/test/util/collectBedrockPackets.js new file mode 100644 index 0000000..9b8b3e4 --- /dev/null +++ b/test/util/collectBedrockPackets.js @@ -0,0 +1,52 @@ +const bedrock = require('bedrock-protocol') +const { startServer } = require('minecraft-bedrock-server') +const debug = require('debug')('prismarine-registry') +const path = require('path') + +async function collectPackets (version, names = ['start_game'], cb) { + const collected = [] + const server = await new Promise((resolve) => { + const server = startServer(version, () => resolve(server), { + 'online-mode': false, + 'server-port': 19130, + path: path.join(__dirname, `server_bedrock_${version}`) + }) + }) + + console.log('Started server', version) + + const client = bedrock.createClient({ + version, + host: '127.0.0.1', + port: 19130, + username: 'test', + offline: true + }) + + let clientConnected = false + + client.on('join', () => { + console.log('[client] Client connected') + clientConnected = true + }) + + for (const name of names) { + client.on(name, (packet) => { + cb(name, packet) + collected.push(packet) + }) + } + + client.on('packet', ({ name }) => debug('[client] -> ', name)) + + setTimeout(() => { + console.log('Stopping server', version) + server.kill() + client.close() + if (!clientConnected) { + throw new Error('Client never connected') + } + }, 9000) +} + +module.exports = collectPackets