This commit is contained in:
2026-09-24 18:34:16 -03:00
parent a758037f20
commit a37ffd9127
20 changed files with 799 additions and 20 deletions
+66 -6
View File
@@ -1,6 +1,7 @@
import { Component } from "$/common/ecs/mod.ts";
import { AIR_ID, BlockChange } from "$/common/protocol.ts";
import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.ts";
import { AIR, Faces, ID_MASK, VOID } from "../../common/constants.ts";
import { AIR, CHUNK_AREA, CHUNK_HEIGHT, CHUNK_SIZE, 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";
@@ -24,9 +25,7 @@ export interface Block {
z: number;
}
export const CHUNK_SIZE = 16;
export const CHUNK_HEIGHT = 128;
export const CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE;
export { CHUNK_AREA, CHUNK_HEIGHT, CHUNK_SIZE };
export interface Chunk {
x: number;
@@ -47,10 +46,15 @@ export class Dimension extends Component {
chunks: Chunk[] = [];
second_timer = 0;
tick_timer = 0;
seed: string;
constructor(world: ClientWorld) {
// blocks players changed from the generated terrain, per chunk, so they survive reloading chunks
changes = new Map<string, Map<string, BlockChange>>();
constructor(world: ClientWorld, seed = "seed") {
super();
this.world = world;
this.seed = seed;
}
add_chunk(x: number, z: number) {
@@ -200,8 +204,57 @@ export class Dimension extends Component {
return [x, y, z];
}
record_change(x: number, y: number, z: number, id: string) {
const chunk_key = `${Math.floor(x / CHUNK_SIZE)},${Math.floor(z / CHUNK_SIZE)}`;
let chunk_changes = this.changes.get(chunk_key);
if (!chunk_changes) {
chunk_changes = new Map();
this.changes.set(chunk_key, chunk_changes);
}
chunk_changes.set(`${x},${y},${z}`, [x, y, z, id]);
}
// call after the player changes a block so it gets saved and sent to the server
sync_block(x: number, y: number, z: number) {
const numeric_id = this.get_block(x, y, z);
const id = numeric_id === AIR ? AIR_ID : EverythingRegistry.get_by_id<BlockRegistry>("blocks", numeric_id)?.id;
if (!id) {
return;
}
this.record_change(x, y, z, id);
this.world.connection?.send({ type: "set_block", x, y, z, id });
}
// set a block without drops or syncing, for changes that came from somewhere else
apply_change(x: number, y: number, z: number, id: string) {
const chunk = this.get_chunk(Math.floor(x / CHUNK_SIZE), Math.floor(z / CHUNK_SIZE));
if (!chunk || !chunk.generated) {
return;
}
const current = this.get_block(x, y, z);
if (id === AIR_ID) {
if (current !== AIR) {
this.break_block(x, y, z, false);
}
return;
}
const nid = EverythingRegistry.get_id("blocks", id);
if (nid === undefined || nid === current) {
return;
}
this.add_block({ x, y, z, id });
}
apply_chunk_changes(cx: number, cz: number) {
for (const [x, y, z, id] of this.changes.get(`${cx},${cz}`)?.values() ?? []) {
this.apply_change(x, y, z, id);
}
}
load_chunk(cx: number, cz: number) {
generate_chunk(this, cx, cz);
generate_chunk(this, cx, cz, this.seed);
const chunk = this.get_chunk(cx, cz);
if (chunk) {
chunk.generated = true;
@@ -221,6 +274,13 @@ export class Dimension extends Component {
neighbor.dirty = true;
}
}
// trees spill into neighboring chunks, so their changes need reapplying too
for (let dx = -1; dx <= 1; dx++) {
for (let dz = -1; dz <= 1; dz++) {
this.apply_chunk_changes(cx + dx, cz + dz);
}
}
}
unload_chunk(cx: number, cz: number) {