diff --git a/client/blocks/hoed_dirt.ts b/client/blocks/hoed_dirt.ts index 38a0af3..0e1963d 100644 --- a/client/blocks/hoed_dirt.ts +++ b/client/blocks/hoed_dirt.ts @@ -10,6 +10,9 @@ const block = EverythingRegistry.register("blocks", "bworld:hoed_ toughness: 5, requires_tool: false, tool_to_break: "shovel", + states: [ + { name: "watered", bits: 1, default: 0 }, + ], }); register_block_item(block); diff --git a/client/components/dimension.ts b/client/components/dimension.ts index c2a6f9f..e339cf9 100644 --- a/client/components/dimension.ts +++ b/client/components/dimension.ts @@ -1,6 +1,6 @@ import { Component } from "$/common/ecs/mod.ts"; import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.ts"; -import { Faces } from "../../common/constants.ts"; +import { AIR, Faces, ID_MASK, VOID } from "../../common/constants.ts"; import { AssetManager } from "../assets.ts"; import { ClientWorld } from "../client_world.ts"; import { generate_chunk } from "../generation.ts"; @@ -31,7 +31,7 @@ export const CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE; export interface Chunk { x: number; z: number; - blocks: Int16Array; + blocks: Uint32Array; blocks_data: BlockData[]; generated: boolean; dirty: boolean; @@ -57,7 +57,7 @@ export class Dimension extends Component { const chunk = { x, z, - blocks: new Int16Array(CHUNK_AREA * CHUNK_HEIGHT), + blocks: new Uint32Array(CHUNK_AREA * CHUNK_HEIGHT), blocks_data: [], dirty: true, generated: false, @@ -100,7 +100,7 @@ export class Dimension extends Component { const chunk = this.get_chunk(chunk_x, chunk_z); if (!chunk) { - return -1; + return VOID; } const lx = x - chunk_x * CHUNK_SIZE; @@ -109,7 +109,7 @@ export class Dimension extends Component { const index = ly * CHUNK_SIZE * CHUNK_SIZE + lz * CHUNK_SIZE + lx; - return chunk.blocks[index]; + return chunk.blocks[index] & ID_MASK; } add_block_data(block_data: BlockData) { @@ -159,7 +159,7 @@ export class Dimension extends Component { const ly = y; const index = ly * CHUNK_SIZE * CHUNK_SIZE + lz * CHUNK_SIZE + lx; - chunk.blocks[index] = 0; + chunk.blocks[index] = AIR; chunk.dirty = true; if (lx === 0) { @@ -283,7 +283,7 @@ export class Dimension extends Component { const block = dimension.get_block(bx, by, bz); - if (block && block !== 0 && block !== -1) { + if (block && block !== AIR && block !== VOID) { let face: Faces; if (bx > prev_bx) { @@ -316,7 +316,7 @@ export class Dimension extends Component { const cz = chunk.z; const size = CHUNK_SIZE + 2; - const padded = new Int16Array(size * size * CHUNK_HEIGHT); + const padded = new Uint32Array(size * size * CHUNK_HEIGHT); for (let y = 0; y < CHUNK_HEIGHT; y++) { for (let z = -1; z <= CHUNK_SIZE; z++) { @@ -325,11 +325,11 @@ export class Dimension extends Component { if (x >= 0 && x < CHUNK_SIZE && z >= 0 && z < CHUNK_SIZE) { const index = y * CHUNK_SIZE * CHUNK_SIZE + z * CHUNK_SIZE + x; - block = chunk.blocks[index]; + block = chunk.blocks[index] & ID_MASK; } else { const wx = cx * CHUNK_SIZE + x; const wz = cz * CHUNK_SIZE + z; - block = this.get_block(wx, y, wz) ?? 0; + block = this.get_block(wx, y, wz) ?? AIR; } const px = x + 1; diff --git a/client/workers/chunk_mesh_worker.ts b/client/workers/chunk_mesh_worker.ts index f935844..0a598d5 100644 --- a/client/workers/chunk_mesh_worker.ts +++ b/client/workers/chunk_mesh_worker.ts @@ -279,7 +279,7 @@ self.onmessage = (event) => { function make_chunk_mesh( chunk_x: number, chunk_z: number, - padded_chunk: Int16Array, + padded_chunk: Uint32Array, blocks_registry: BlockRegistry[], textures_info: TexturesInfo, image: Texture, diff --git a/common/constants.ts b/common/constants.ts index 1103709..d3732e4 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -12,3 +12,9 @@ export interface SpriteRegion { export const faces = ["west", "east", "bottom", "top", "north", "south"] as const; export type Faces = typeof faces[number]; + +export const AIR = 0; +export const VOID = 0xFFFFFFFF; + +export const ID_MASK = 0xFFFF; +export const STATE_SHIFT = 16; diff --git a/common/everything_registry.ts b/common/everything_registry.ts index e3c84b0..1df32a2 100644 --- a/common/everything_registry.ts +++ b/common/everything_registry.ts @@ -62,6 +62,23 @@ interface TextureFront { side: string; } +export interface BlockStateDefinition { + name: string; + bits: number; + default: number; +} + +interface CompiledStateDefinition { + name: string; + mask: number; + shift: number; +} + +interface BlockStateVariant { + model: string; + y: number; +} + export interface BlockRegistry { id: string; textures: string | TextureSideTopBottom | TextureFront; @@ -74,12 +91,17 @@ export interface BlockRegistry { tool_to_break?: string; drop_table?: string; + states?: BlockStateDefinition[]; + variants?: Record; + on_create?(dimension: Dimension, block: Block): void; on_break?(dimension: Dimension, block: Block): void; on_click?(dimension: Dimension, block: Block): void; on_interact?(dimension: Dimension, block: Block): boolean; on_tick?(dimension: Dimension, block: Block, tick_delta: number): void; on_second?(dimension: Dimension, block: Block, second_delta: number): void; + + compiled_states?: CompiledStateDefinition[]; } export interface ItemRegistry { diff --git a/common/utils.ts b/common/utils.ts index 1bddf62..9c6d7bb 100644 --- a/common/utils.ts +++ b/common/utils.ts @@ -1,5 +1,5 @@ import { AssetManager } from "../client/assets.ts"; -import { SpriteRegion } from "./constants.ts"; +import { ID_MASK, SpriteRegion, STATE_SHIFT } from "./constants.ts"; import { BlockRegistry, EverythingRegistry, ItemRegistry } from "./everything_registry.ts"; export function point_inside_rec( @@ -56,3 +56,53 @@ export function register_block_item(block: BlockRegistry) { block_id: block.id, }); } + +export function get_state_value(value: number, block_info: BlockRegistry, name: string) { + if (!block_info.states) { + return undefined; + } + + if (!block_info.compiled_states) { + compile_block_states(block_info); + } + + const state_bits = value >>> STATE_SHIFT; + + const s = block_info.compiled_states!.find((s) => s.name === name)!; + return (state_bits & s.mask) >>> s.shift; +} + +export function set_state_value(value: number, block_info: BlockRegistry, name: string, new_value: number) { + if (!block_info.states) { + return undefined; + } + + if (!block_info.compiled_states) { + compile_block_states(block_info); + } + const id = value & ID_MASK; + let state_bits = value >>> 16; + + const s = block_info.compiled_states!.find((s) => s.name === name)!; + + state_bits = (state_bits & ~s.mask) | (new_value << s.shift); + + return (state_bits << 16) | id; +} + +function compile_block_states(block_info: BlockRegistry) { + if (!block_info.states) { + return; + } + + let offset = 0; + + block_info.compiled_states = block_info.states.map((s) => { + const shift = offset; + const mask = ((1 << s.bits) - 1) << shift; + + offset += s.bits; + + return { name: s.name, mask, shift }; + }); +}