Skip to content

Commit

Permalink
Add support for Bedrock edition block hashes (#97)
Browse files Browse the repository at this point in the history
* Added getHashValue

* Added getHash to Block

* simplify and add test

* Fixed hashing

* Added other collided blocks

* prismarine-block to global

* Update index.js

* Update index.d.ts

* added 4 byte mask

* Added sorting

* Added getHashValue

* Added getHash to Block

* simplify and add test

* Fixed hashing

* Added other collided blocks

* prismarine-block to global

* Update index.js

* Update index.d.ts

* added 4 byte mask

* Added sorting

* Add back EOL for lint

* Description corrections

* Updated docs

* Updated docs #2

---------

Co-authored-by: extremeheat <extreme@protonmail.ch>
  • Loading branch information
FreezeEngine and extremeheat authored Jan 4, 2025
1 parent 251e0b0 commit bc8a7af
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 9 deletions.
35 changes: 29 additions & 6 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
* `stateString` is the string representation of a block
* `biomeId` is the biome numerical id

#### Block(type,biomeId,metadata)
#### Block.fromProperties(typeId, properties, biomeId)

* `typeId` - The block type ID (numerical or string)
* `properties` - A dictionary of block state properties to build from
* `biomeId` - The biome numerical id

#### Block(type, biomeId, metadata, stateId = null)

Constructor of a block
* `type` is the block numerical id
* `biomeId` is the biome numerical id
* `metadata` is the metadata numerical value
* `stateId` (optional) represents the state of the block (same as metadata in newer versions)

#### block.canHarvest(heldItemType)

Expand All @@ -24,6 +31,13 @@ Tells you if `heldItemType` is one of the right tool to harvest the block.

Parse the block state and return its properties.

#### getHash(prefixedName, states)

**(Bedrock Edition)** Returns an integer hash to represent the block state.

* `prefixedName` - The name of the block, including the `minecraft:` prefix (string).
* `states` - A record of block state properties.

#### block.digTime(heldItemType, creative, inWater, notOnGround, enchantments = [], effects = {})

Tells you how long it will take to dig the block, in milliseconds.
Expand All @@ -45,15 +59,19 @@ Numerical id.

#### block.name

Minecraft ID (string) of the block.

#### block.displayName

Display name of the block.

#### block.shapes

Array of bounding boxes representing the block shape. Each bounding box is an array of the form `[xmin, ymin, zmin, xmax, ymax, zmax]`. Depends on the type and state of the block.

#### block.entity

If this block is a block entity, this contains the NBT data for the entity
If this block is a block entity, this contains the NBT data for the entity.

#### block.blockEntity

Expand All @@ -64,6 +82,10 @@ Simplified block entity data using prismarine-nbt's simplify() function. Only fo
Number which represents different things depending on the block.
See http://www.minecraftwiki.net/wiki/Data_values#Data

#### block.hash

**(Bedrock Edition)** A hash uniquely representing the block name and its properties (number).

#### block.light

#### block.skyLight
Expand Down Expand Up @@ -93,7 +115,7 @@ Boolean, whether the block is considered diggable.

#### block.boundingBox

The shape of the block according to the physics engine's collision decection. Currently one of:
The shape of the block according to the physics engine's collision detection. Currently one of:

* `block` - currently, partially solid blocks, such as half-slabs and ladders, are considered entirely solid.
* `empty` - such as flowers and lava.
Expand Down Expand Up @@ -131,11 +153,12 @@ Sets the text for the sign, can be plaintext, or array of JSON or prismarine-cha

#### getSignText (): [string, string?]

Gets the plain text content of the sign, the first item of the array returned and the second is the back and will be undefined for versions that don't support writing on the back of signs.
Gets the plain text content of the sign. The first item of the array returned and the second is the back, which will be undefined for versions that don't support writing on the back of signs.

#### get .signText

Deprecated, returns a plaintext string containing the sign's text
Deprecated, returns a plaintext string containing the sign's text.

#### set .signText
Deprecated, sets the text for a sign's text, can be plaintext, or array of JSON or prismarine-chat instances

Deprecated, sets the text for a sign's text. Can be plaintext, or array of JSON or prismarine-chat instances.
13 changes: 12 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface Effect {
duration: number;
}

type States = { [key: string]: string | number }

export type Shape = [number, number, number, number, number, number];

Expand Down Expand Up @@ -42,6 +43,9 @@ export declare class Block {
// Contains a full NBT, unserialized object
entity: NBT | null;

// (Bedrock Edition) A hash to uniquely represent block name and its properties
hash?: number

/**
* A biome instance.
* @see https://github.com/prismarinejs/prismarine-biome#api.
Expand Down Expand Up @@ -170,14 +174,21 @@ export declare class Block {
* @param properties - A dictionary of block states to build from.
* @param biomeId - The biome this block is in.
*/
static fromProperties(typeId: number | string, properties: { [key: string]: string | number }, biomeId: number): Block;
static fromProperties(typeId: number | string, properties: States, biomeId: number): Block;

/**
* Create a block from a given string.
* @param stateString - the string representation of a block
* @param biomeId - the biome numerical id
*/
static fromString(stateString: string, biomeId: number): Block;

/**
* (Bedrock Edition) Returns an integer hash to represent the block state
* @param prefixedName name of the block, with a minecraft: prefix
* @param states a record of block state properties
*/
static getHash(prefixedName: string, states: States): number | undefined
}

/** @deprecated */
Expand Down
29 changes: 29 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ function provider (registry, { Biome, version }) {
if (this.name.includes('sign')) {
mergeObject(this, blockMethods.sign)
}

if (blockEnum && registry.supportFeature('blockHashes')) {
this.hash = Block.getHash(this.name, this._properties)
}
}

static fromStateId (stateId, biomeId) {
Expand Down Expand Up @@ -271,6 +275,21 @@ function provider (registry, { Biome, version }) {
return Object.assign(this._properties, this.computedStates)
}

static getHash (name, states) {
if (registry.supportFeature('blockHashes')) {
const sortedStates = {}
for (const key of Object.keys(states).sort()) {
sortedStates[key] = states[key]
}
const tag = nbt.comp({
name: { type: 'string', value: name.includes(':') ? name : `minecraft:${name}` },
states: nbt.comp(sortedStates)
})
const buf = nbt.writeUncompressed(tag, 'little')
return computeFnv1a32Hash(buf)
}
}

canHarvest (heldItemType) {
if (!this.harvestTools) { return true }; // for blocks harvestable by hand
return heldItemType && this.harvestTools && this.harvestTools[heldItemType]
Expand Down Expand Up @@ -393,3 +412,13 @@ function provider (registry, { Biome, version }) {
function mergeObject (to, from) {
Object.defineProperties(to, Object.getOwnPropertyDescriptors(from))
}

function computeFnv1a32Hash (buf) {
const FNV1_OFFSET_32 = 0x811c9dc5
let h = FNV1_OFFSET_32
for (let i = 0; i < buf.length; i++) {
h ^= buf[i] & 0xff
h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)
}
return h & 0xffffffff
}
26 changes: 24 additions & 2 deletions test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ describe('Dig time', () => {
it('using iron_shovel', () => {
const tool = registry.itemsByName[toolName]
const block = Block.fromStateId(registry.blocksByName[blockName].defaultState)
console.log('Block', block)
const time = block.digTime(tool.id, false, false, false, [], {})
expect(time).toBe(750)
})
Expand Down Expand Up @@ -171,8 +172,29 @@ describe('fromString', () => {
'1.20': 'minecraft:candle[lit=true]'
}
for (const [version, str] of Object.entries(versions)) {
it(version, () => {
const Block = require('prismarine-block')(version)
const block = Block.fromString(str, 0)
// console.log(block)
expect(block.getProperties().lit).toBeTruthy()
})
}
})

describe('Block hash computation', () => {
for (const version of ['bedrock_1.20.0']) {
const Block = require('prismarine-block')(version)
const block = Block.fromString(str, 0)
expect(block.getProperties().lit).toBeTruthy()
it('minecraft:soul_soil', function () {
const block = Block.fromString('minecraft:soul_soil', 0)
expect(block.hash).toBe(601701031)
})
it('minecraft:planks', function () {
const block = Block.fromString('minecraft:planks', 0)
expect(block.hash).toBe(1835335165)
})
it('minecraft:stone', function () {
const block = Block.fromString('minecraft:stone', 0)
expect(block.hash).toBe(-1177000405)
})
}
})

0 comments on commit bc8a7af

Please sign in to comment.