/** * ChunkStore — the SDK-managed chunk/voxel cache: bulk loading, typed * per-voxel and per-chunk state, realtime merge of voxel notifications, * optimistic local edits, and the deterministic-worldgen write-back pattern. * Replaces the WorldStreamer + WorldState + codec plumbing every voxel game * hand-writes (~860 LOC in Blocks with Friends). */ import { type StateCodec } from './codec.js'; import { type ChunkCoord } from './keys.js'; import type { WorldSessionContext } from './session.js'; /** Load lifecycle of a cached chunk. */ export type ChunkLoadState = 'loading' | 'loaded' | 'missing' | 'seeded' | 'failed'; /** * One cached chunk. Object identity is stable; check `revision` for cheap * change detection from a render loop. */ export interface CachedChunk { readonly key: string; readonly coord: ChunkCoord; /** * Dense voxel-type grid (4096 bytes), null when unknown. Indexed * `x + y*16 + z*256` by default; see {@link ChunkStoreConfig.voxelIndex}. */ voxels: Uint8Array | null; /** Sparse typed per-voxel state by voxel index. */ voxelStates: Map; /** Typed chunk-level state (null when absent/undecoded). */ chunkState: TChunkState | null; loadState: ChunkLoadState; /** Bumped on every change to this chunk. */ revision: number; /** Local time of the last change. */ updatedAt: number; /** Whether sparse voxel states were hydrated (bulk loads omit them). */ hydrated: boolean; /** Whether local edits are queued for write-back. */ dirty: boolean; } /** Options for {@link attachChunkStore}. */ export interface ChunkStoreConfig { /** Codec for per-voxel state blobs. Defaults to raw base64 strings. */ voxelStateCodec?: StateCodec; /** Codec for the chunk-level state blob. Defaults to raw base64 strings. */ chunkStateCodec?: StateCodec; /** * After a bulk load, fetch each chunk individually to hydrate its sparse * `voxelStates` (`getChunksByDistance` does NOT return them — a platform * trap this store encapsulates). Defaults to true when a * `voxelStateCodec` is configured, else false. */ hydrateVoxelStates?: boolean; /** * Called for chunks the server has never stored. Return a 4096-byte dense * grid to seed it locally (deterministic client-side worldgen) — seeded * chunks are queued for write-back so the world persists and stays * identical for everyone. Return `{ voxels, writeBack: false }` to seed * locally WITHOUT persisting (e.g. outside your world's write-back radius * or budget). */ onMissing?: (coord: ChunkCoord) => Uint8Array | { voxels: Uint8Array; writeBack?: boolean; } | undefined | void; /** * Write-back cadence: one dirty chunk persists per tick (throttled, like * the proven BWF pattern). Defaults to 700 ms; `false` disables the timer * (call {@link ChunkStore.flush} yourself). Runs on the session ticker. */ writeBackIntervalMs?: number | false; /** Replication radius for outbound voxel updates (0-8). */ distance?: number; /** Decay algorithm for outbound voxel updates (0-5). */ decayRate?: number; /** * The actor uuid stamped on outbound voxel updates. Wired from the * session's local actor automatically; a random uuid otherwise. */ actorUuid?: string | (() => string | null); /** * Within-chunk (x,y,z in 0-15) → dense-grid byte offset. Defaults to the * platform-documented layout `x + y*16 + z*256`. Override for worlds whose * existing dense blobs were written with a different convention (e.g. * Blocks with Friends uses `y*256 + z*16 + x`) — the layout is opaque to * the platform, so all clients of a world just have to agree. */ voxelIndex?: (x: number, y: number, z: number) => number; /** Clock override for tests. Defaults to `Date.now`. */ now?: () => number; } /** A voxel edit for {@link ChunkStore.setVoxel}. */ export interface SetVoxelInput { chunk: ChunkCoord; /** Within-chunk voxel coordinates (0-15 each). */ x: number; y: number; z: number; voxelType: number; /** Typed per-voxel state (encoded with the store's codec). */ state?: TVoxelState; /** Apply locally before the send resolves. Defaults to true. */ optimistic?: boolean; } /** * The SDK-managed **chunk/voxel cache** — the client-side source of truth * for terrain: * * - `ensureAround(center, radius)` bulk-loads via `chunks.byDistance` * (in-flight deduped), hydrates sparse voxel states, marks chunks the * server never stored as `missing`, and hands them to your `onMissing` * worldgen hook. * - Realtime `voxelUpdate` notifications merge into the cache automatically * (dense grid write + typed state decode + revision bump + change event). * - `setVoxel` applies locally (optimistic) and replicates via the UDP path. * - `seed`/`flush` implement deterministic-worldgen write-back through * `chunks.update`, one throttled chunk at a time. * * All reads are synchronous; writes land on WebSocket events, so render * loops and background tabs behave (see the module docs). */ export declare class ChunkStore { private readonly ctx; private readonly config; private readonly chunks; private readonly inFlight; private readonly writeBackQueue; private readonly changeListeners; private readonly voxelStateCodec; private readonly chunkStateCodec; private readonly hydrateStates; private readonly now; private readonly fallbackUuid; private readonly voxelIndex; private revisionValue; private sequence; constructor(ctx: WorldSessionContext, config?: ChunkStoreConfig); /** Bumped on every cache change — poll it cheaply from a render loop. */ get revision(): number; /** The cached chunk at a coordinate (any load state), if tracked. */ get(coord: ChunkCoord): CachedChunk | undefined; /** Every tracked chunk (any load state). */ list(): Array>; /** The dense voxel type at a within-chunk coordinate (0 when unknown). */ voxelTypeAt(coord: ChunkCoord, x: number, y: number, z: number): number; /** The typed per-voxel state at a within-chunk coordinate, if any. */ voxelStateAt(coord: ChunkCoord, x: number, y: number, z: number): TVoxelState | undefined; /** Subscribe to per-chunk changes (loads, merges, edits). @returns off. */ onChunkChanged(listener: (chunk: CachedChunk) => void): () => void; /** * Ensure every chunk within `radius` (Chebyshev, 1-8) of `center` is * tracked: bulk-loads untracked ones, hydrates sparse voxel states when * configured, marks server-unknown chunks `missing`, and seeds them via * `onMissing`. In-flight requests are deduped; safe to call every time the * player crosses a chunk boundary. */ ensureAround(center: ChunkCoord, radius: number): Promise; /** * Hydrate one chunk's sparse voxel states (and chunk state) via a * single-chunk fetch — bulk loads omit them. */ hydrate(coord: ChunkCoord): Promise; /** * Edit one voxel: applies to the cache immediately (optimistic) and * replicates via the realtime voxel path. Resolves with the send * acceptance. */ setVoxel(input: SetVoxelInput): Promise; /** * Seed a locally generated chunk (deterministic worldgen) and queue it for * write-back so the server copy exists for everyone. */ seed(coord: ChunkCoord, voxels: Uint8Array, options?: { writeBack?: boolean; }): void; /** Queue a tracked chunk's dense grid for (throttled) write-back. */ markDirty(coord: ChunkCoord): void; /** Chunks currently queued for write-back. */ get pendingWriteBacks(): number; /** Persist every queued chunk now (awaits all writes). */ flush(): Promise; /** Drop tracked chunks farther than `radius` from `center` (dirty ones kept). */ pruneBeyond(center: ChunkCoord, radius: number): void; private persistNext; private applyServerChunk; private markMissing; private applyVoxel; private ensureEntry; private decodeChunkState; private touch; private senderUuid; private nextSequence; } /** * Attach a {@link ChunkStore} to a world session context. Prefer the * `chunks` key of `createWorldSession`'s config. */ export declare function attachChunkStore(ctx: WorldSessionContext, config?: ChunkStoreConfig): ChunkStore; //# sourceMappingURL=chunks.d.ts.map