Server actually ticks
This commit is contained in:
+29
-14
@@ -3,7 +3,7 @@ import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.
|
||||
import { generate_raw_chunk, RawChunk, WorldgenSetup } from "$/common/generation.ts";
|
||||
import { Container } from "$/common/inventory.ts";
|
||||
import { AIR_ID, BlockChange } from "$/common/protocol.ts";
|
||||
import { chunk_key } from "$/common/utils.ts";
|
||||
import { block_value, chunk_key, default_block_value } from "$/common/utils.ts";
|
||||
import { LruMap } from "./lru.ts";
|
||||
|
||||
// generation only, rebuilt when needed. 128 KB each
|
||||
@@ -31,6 +31,8 @@ export class ServerWorld {
|
||||
readonly seed: string;
|
||||
readonly block_ids: Record<string, number> = {};
|
||||
readonly worldgen: WorldgenSetup | undefined;
|
||||
// what a freshly placed or generated block stores, by numeric id: the id with its default states
|
||||
readonly default_values: number[] = [];
|
||||
|
||||
// what generation made, and the final blocks with neighbors' leaves and player changes applied
|
||||
#raw = new LruMap<number, RawChunk>(RAW_CACHE_SIZE);
|
||||
@@ -43,13 +45,14 @@ export class ServerWorld {
|
||||
// set when anything that gets saved changes
|
||||
dirty = false;
|
||||
|
||||
on_block_change?: (x: number, y: number, z: number, id: string) => void;
|
||||
on_block_change?: (x: number, y: number, z: number, id: string, state: number) => void;
|
||||
|
||||
constructor(seed: string, worldgen?: WorldgenSetup) {
|
||||
this.seed = seed;
|
||||
this.worldgen = worldgen;
|
||||
EverythingRegistry.get_registry<BlockRegistry>("blocks").forEach((block, nid) => {
|
||||
this.block_ids[block.id] = nid;
|
||||
this.default_values[nid] = default_block_value(nid, block);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,13 +71,23 @@ export class ServerWorld {
|
||||
return blocks[index_in_chunk(x, y, z, chunk_x, chunk_z)] & ID_MASK;
|
||||
}
|
||||
|
||||
// the id with its state bits
|
||||
get_block_value(x: number, y: number, z: number): number {
|
||||
if (y < 0 || y >= CHUNK_HEIGHT) {
|
||||
return AIR;
|
||||
}
|
||||
const chunk_x = Math.floor(x / CHUNK_SIZE);
|
||||
const chunk_z = Math.floor(z / CHUNK_SIZE);
|
||||
return this.#get_chunk(chunk_x, chunk_z)[index_in_chunk(x, y, z, chunk_x, chunk_z)];
|
||||
}
|
||||
|
||||
get_block_info(x: number, y: number, z: number): BlockRegistry | undefined {
|
||||
const nid = this.get_block_nid(x, y, z);
|
||||
return nid === AIR ? undefined : EverythingRegistry.get_by_id<BlockRegistry>("blocks", nid);
|
||||
}
|
||||
|
||||
// only changes the block, the game server runs behaviors and tiles
|
||||
set_block(x: number, y: number, z: number, id: string) {
|
||||
// only changes the block, the game server runs behaviors and tiles. state bits default to the block's defaults
|
||||
set_block(x: number, y: number, z: number, id: string, state?: number) {
|
||||
if (y < 0 || y >= CHUNK_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
@@ -82,15 +95,17 @@ export class ServerWorld {
|
||||
if (nid === undefined) {
|
||||
throw new Error(`Unknown block ${id}`);
|
||||
}
|
||||
const value = state === undefined ? this.default_values[nid] ?? nid : block_value(nid, state);
|
||||
|
||||
const chunk_x = Math.floor(x / CHUNK_SIZE);
|
||||
const chunk_z = Math.floor(z / CHUNK_SIZE);
|
||||
const blocks = this.#get_chunk(chunk_x, chunk_z);
|
||||
blocks[index_in_chunk(x, y, z, chunk_x, chunk_z)] = nid;
|
||||
blocks[index_in_chunk(x, y, z, chunk_x, chunk_z)] = value;
|
||||
|
||||
this.#record_change(x, y, z, id);
|
||||
const state_bits = value >>> 16;
|
||||
this.#record_change(x, y, z, id, state_bits);
|
||||
this.dirty = true;
|
||||
this.on_block_change?.(x, y, z, id);
|
||||
this.on_block_change?.(x, y, z, id, state_bits);
|
||||
}
|
||||
|
||||
all_changes(): BlockChange[] {
|
||||
@@ -102,8 +117,8 @@ export class ServerWorld {
|
||||
}
|
||||
|
||||
load_changes(changes: BlockChange[]) {
|
||||
for (const [x, y, z, id] of changes) {
|
||||
this.#record_change(x, y, z, id);
|
||||
for (const [x, y, z, id, state] of changes) {
|
||||
this.#record_change(x, y, z, id, state ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,21 +136,21 @@ export class ServerWorld {
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
#record_change(x: number, y: number, z: number, id: string) {
|
||||
#record_change(x: number, y: number, z: number, id: string, state: number) {
|
||||
const key = chunk_key(Math.floor(x / CHUNK_SIZE), Math.floor(z / CHUNK_SIZE));
|
||||
let chunk_changes = this.#changes.get(key);
|
||||
if (!chunk_changes) {
|
||||
chunk_changes = new Map();
|
||||
this.#changes.set(key, chunk_changes);
|
||||
}
|
||||
chunk_changes.set(position_key(x, y, z), [x, y, z, id]);
|
||||
chunk_changes.set(position_key(x, y, z), state ? [x, y, z, id, state] : [x, y, z, id]);
|
||||
}
|
||||
|
||||
#get_raw(chunk_x: number, chunk_z: number) {
|
||||
const key = chunk_key(chunk_x, chunk_z);
|
||||
let raw = this.#raw.get(key);
|
||||
if (!raw) {
|
||||
raw = generate_raw_chunk(chunk_x, chunk_z, this.seed, this.block_ids, this.worldgen);
|
||||
raw = generate_raw_chunk(chunk_x, chunk_z, this.seed, this.block_ids, this.worldgen, this.default_values);
|
||||
this.#raw.set(key, raw);
|
||||
}
|
||||
return raw;
|
||||
@@ -174,10 +189,10 @@ export class ServerWorld {
|
||||
}
|
||||
}
|
||||
|
||||
for (const [x, y, z, id] of this.#changes.get(chunk_key(chunk_x, chunk_z))?.values() ?? []) {
|
||||
for (const [x, y, z, id, state] of this.#changes.get(chunk_key(chunk_x, chunk_z))?.values() ?? []) {
|
||||
const nid = id === AIR_ID ? AIR : this.block_ids[id];
|
||||
if (nid !== undefined) {
|
||||
blocks[index_in_chunk(x, y, z, chunk_x, chunk_z)] = nid;
|
||||
blocks[index_in_chunk(x, y, z, chunk_x, chunk_z)] = block_value(nid, state ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user