Server actually ticks
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Component } from "$/common/ecs/mod.ts";
|
||||
import { chunk_key } from "$/common/utils.ts";
|
||||
import { block_value, chunk_key, default_block_value } from "$/common/utils.ts";
|
||||
import { AIR_ID, BlockChange } from "$/common/protocol.ts";
|
||||
import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.ts";
|
||||
import { AIR, CHUNK_AREA, CHUNK_HEIGHT, CHUNK_SIZE, Faces, ID_MASK, VOID } from "../../common/constants.ts";
|
||||
@@ -101,7 +101,8 @@ export class Dimension extends Component {
|
||||
return this.chunks.get(chunk_key(x, z));
|
||||
}
|
||||
|
||||
add_block(block: Block) {
|
||||
// state is the block's state bits, its defaults when not given
|
||||
add_block(block: Block, state?: number) {
|
||||
const block_chunk_x = Math.floor(block.x / CHUNK_SIZE);
|
||||
const block_chunk_z = Math.floor(block.z / CHUNK_SIZE);
|
||||
let chunk = this.get_chunk(block_chunk_x, block_chunk_z);
|
||||
@@ -109,7 +110,7 @@ export class Dimension extends Component {
|
||||
chunk = this.add_chunk(block_chunk_x, block_chunk_z);
|
||||
}
|
||||
|
||||
const nid = EverythingRegistry.get_id("blocks", block.id)!;
|
||||
const [nid, info] = EverythingRegistry.get_full<BlockRegistry>("blocks", block.id)!;
|
||||
|
||||
const lx = block.x - block_chunk_x * CHUNK_SIZE;
|
||||
const lz = block.z - block_chunk_z * CHUNK_SIZE;
|
||||
@@ -117,11 +118,23 @@ export class Dimension extends Component {
|
||||
|
||||
const index = ly * CHUNK_SIZE * CHUNK_SIZE + lz * CHUNK_SIZE + lx;
|
||||
|
||||
chunk.blocks[index] = nid;
|
||||
chunk.blocks[index] = state === undefined ? default_block_value(nid, info) : block_value(nid, state);
|
||||
chunk.dirty = true;
|
||||
this.#mark_border_neighbors_dirty(block_chunk_x, block_chunk_z, lx, lz);
|
||||
}
|
||||
|
||||
// the id with its state bits, VOID outside loaded chunks
|
||||
get_block_value(x: number, y: number, z: number) {
|
||||
const chunk_x = Math.floor(x / CHUNK_SIZE);
|
||||
const chunk_z = Math.floor(z / CHUNK_SIZE);
|
||||
const chunk = this.get_chunk(chunk_x, chunk_z);
|
||||
if (!chunk) {
|
||||
return VOID;
|
||||
}
|
||||
return chunk.blocks[y * CHUNK_AREA + (z - chunk_z * CHUNK_SIZE) * CHUNK_SIZE + (x - chunk_x * CHUNK_SIZE)] ??
|
||||
AIR;
|
||||
}
|
||||
|
||||
get_block(x: number, y: number, z: number) {
|
||||
const chunk_x = Math.floor(x / CHUNK_SIZE);
|
||||
const chunk_z = Math.floor(z / CHUNK_SIZE);
|
||||
@@ -196,18 +209,18 @@ export class Dimension extends Component {
|
||||
return [x, y, z];
|
||||
}
|
||||
|
||||
record_change(x: number, y: number, z: number, id: string) {
|
||||
record_change(x: number, y: number, z: number, id: string, state = 0) {
|
||||
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]);
|
||||
chunk_changes.set(`${x},${y},${z}`, [x, y, z, id, state]);
|
||||
}
|
||||
|
||||
// set a block the server told us about
|
||||
apply_change(x: number, y: number, z: number, id: string) {
|
||||
apply_change(x: number, y: number, z: number, id: string, state = 0) {
|
||||
const chunk = this.get_chunk(Math.floor(x / CHUNK_SIZE), Math.floor(z / CHUNK_SIZE));
|
||||
if (!chunk || !chunk.generated) {
|
||||
return;
|
||||
@@ -222,15 +235,15 @@ export class Dimension extends Component {
|
||||
}
|
||||
|
||||
const nid = EverythingRegistry.get_id("blocks", id);
|
||||
if (nid === undefined || nid === current) {
|
||||
if (nid === undefined || block_value(nid, state) === this.get_block_value(x, y, z)) {
|
||||
return;
|
||||
}
|
||||
this.add_block({ x, y, z, id });
|
||||
this.add_block({ x, y, z, id }, state);
|
||||
}
|
||||
|
||||
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);
|
||||
for (const [x, y, z, id, state] of this.changes.get(`${cx},${cz}`)?.values() ?? []) {
|
||||
this.apply_change(x, y, z, id, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ export function start_game(world: ClientWorld) {
|
||||
const dimension = new Entity("dimension");
|
||||
world.dimension?.dispose();
|
||||
world.dimension = new Dimension(world, world.connection.seed);
|
||||
for (const [x, y, z, id] of world.connection.initial_changes) {
|
||||
world.dimension.record_change(x, y, z, id);
|
||||
for (const [x, y, z, id, state] of world.connection.initial_changes) {
|
||||
world.dimension.record_change(x, y, z, id, state);
|
||||
}
|
||||
dimension.add(world.dimension);
|
||||
world.add_entity(dimension);
|
||||
|
||||
@@ -39,8 +39,8 @@ export class NetworkSystem extends System {
|
||||
break;
|
||||
}
|
||||
case "set_block":
|
||||
world.dimension.record_change(message.x, message.y, message.z, message.id);
|
||||
world.dimension.apply_change(message.x, message.y, message.z, message.id);
|
||||
world.dimension.record_change(message.x, message.y, message.z, message.id, message.state);
|
||||
world.dimension.apply_change(message.x, message.y, message.z, message.id, message.state);
|
||||
break;
|
||||
case "chat":
|
||||
world.add_chat(message.from ? `<${message.from}> ${message.text}` : message.text);
|
||||
|
||||
@@ -128,6 +128,10 @@ export class PlayerControlsSystem extends System {
|
||||
|
||||
if (block && player_component.screens.length === 0) {
|
||||
const block_info = EverythingRegistry.get_by_id<BlockRegistry>("blocks", block.block)!;
|
||||
if (InputManager.is_mouse_pressed(0)) {
|
||||
// for mods' on_click, breaking itself is timed here and sent when done
|
||||
send({ type: "hit_block", x: block.x, y: block.y, z: block.z });
|
||||
}
|
||||
if (player_component.breaking_block) {
|
||||
player_component.breaking_block = { x: block.x, y: block.y, z: block.z };
|
||||
player_component.break_progress_max = block_info.toughness ?? 9999;
|
||||
@@ -151,9 +155,11 @@ export class PlayerControlsSystem extends System {
|
||||
const target = { x: block.x + offset.x, y: block.y + offset.y, z: block.z + offset.z };
|
||||
const target_id = world.dimension.get_block(target.x, target.y, target.z);
|
||||
const replaceable = target_id === AIR ||
|
||||
EverythingRegistry.get_by_id<BlockRegistry>("blocks", target_id)?.id === "bworld:water";
|
||||
if (!block_info.interactive && holding_item_info?.block_id && replaceable) {
|
||||
world.dimension.add_block({ ...target, id: holding_item_info.block_id });
|
||||
EverythingRegistry.get_by_id<BlockRegistry>("blocks", target_id)?.replaceable;
|
||||
// items with components might do something else on the server, like on_use
|
||||
const place_id = holding_item_info?.components ? undefined : holding_item_info?.block_id;
|
||||
if (!block_info.interactive && place_id && replaceable) {
|
||||
world.dimension.add_block({ ...target, id: place_id });
|
||||
hotbar_slot.amount = hotbar_slot.amount! - 1;
|
||||
}
|
||||
}
|
||||
@@ -161,6 +167,9 @@ export class PlayerControlsSystem extends System {
|
||||
player_component.breaking_block = undefined;
|
||||
player_component.break_progress_max = 0;
|
||||
player_component.break_progress = 0;
|
||||
if (!block && player_component.screens.length === 0 && InputManager.is_mouse_pressed(2)) {
|
||||
send({ type: "use_item" });
|
||||
}
|
||||
}
|
||||
|
||||
if (player_component.screens.length === 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { Texture } from "../renderer/types.ts";
|
||||
import type { FromChunkWorker, ToChunkWorker } from "./chunk_messages.ts";
|
||||
import { generate_raw_chunk, WorldgenSetup } from "$/common/generation.ts";
|
||||
import { load_worldgen } from "$/common/worldgen_loader.ts";
|
||||
import { default_block_value } from "$/common/utils.ts";
|
||||
|
||||
const pad = 0.5;
|
||||
|
||||
@@ -268,6 +269,8 @@ let block_ids: Record<string, number> = {};
|
||||
let textures_info: TexturesInfo = {};
|
||||
let image: Texture;
|
||||
let worldgen: WorldgenSetup | undefined;
|
||||
// numeric id to what a generated block stores, with its default states. matches the server
|
||||
let default_values: number[] = [];
|
||||
// generating has to wait for mods' worldgen scripts, or this worker's terrain wouldn't match the server's
|
||||
let worldgen_ready: Promise<void> = Promise.resolve();
|
||||
|
||||
@@ -279,6 +282,7 @@ self.onmessage = async (event: MessageEvent<ToChunkWorker>) => {
|
||||
block_ids = message.block_ids;
|
||||
textures_info = message.textures_info;
|
||||
image = message.image as Texture;
|
||||
default_values = blocks_registry.map((block, nid) => default_block_value(nid, block));
|
||||
worldgen_ready = load_worldgen(message.worldgen_scripts, message.ores).then((setup) => {
|
||||
worldgen = setup;
|
||||
});
|
||||
@@ -319,7 +323,7 @@ function post(message: FromChunkWorker, transfer: Transferable[]) {
|
||||
}
|
||||
|
||||
function generate(chunk_x: number, chunk_z: number, seed: string) {
|
||||
const { blocks, spills } = generate_raw_chunk(chunk_x, chunk_z, seed, block_ids, worldgen);
|
||||
const { blocks, spills } = generate_raw_chunk(chunk_x, chunk_z, seed, block_ids, worldgen, default_values);
|
||||
post({ type: "generated", chunk_x, chunk_z, blocks, spills }, [blocks.buffer, spills.buffer]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user