Server authority
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Component } from "$/common/ecs/mod.ts";
|
||||
import { chunk_key } 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";
|
||||
@@ -6,19 +7,9 @@ import { AssetManager } from "../assets.ts";
|
||||
import { ClientWorld } from "../client_world.ts";
|
||||
import { ChunkWorkerPool } from "../chunk_workers.ts";
|
||||
import type { FromChunkWorker } from "../workers/chunk_messages.ts";
|
||||
import { ItemStack } from "../inventory.ts";
|
||||
import { PlayerComponent } from "../player.ts";
|
||||
import { create_vertex_buffer, destroy_vertex_buffer, Texture } from "../renderer/mod.ts";
|
||||
import { Camera } from "./camera.ts";
|
||||
|
||||
export interface BlockData<T = unknown> {
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface Block {
|
||||
id: string;
|
||||
x: number;
|
||||
@@ -32,7 +23,6 @@ export interface Chunk {
|
||||
x: number;
|
||||
z: number;
|
||||
blocks: Uint32Array;
|
||||
blocks_data: BlockData[];
|
||||
generated: boolean;
|
||||
dirty: boolean;
|
||||
// bumped on every mesh request so late results from older requests get ignored
|
||||
@@ -43,10 +33,7 @@ export interface Chunk {
|
||||
transparent_vertex_count?: number;
|
||||
}
|
||||
|
||||
// numeric so looking chunks up doesnt allocate a string every time, fine for |x|, |z| < 32768
|
||||
export function chunk_key(x: number, z: number) {
|
||||
return (x + 32768) * 65536 + (z + 32768);
|
||||
}
|
||||
export { chunk_key };
|
||||
|
||||
const NEIGHBOR_OFFSETS = [[-1, 0], [1, 0], [0, -1], [0, 1]] as const;
|
||||
|
||||
@@ -99,7 +86,6 @@ export class Dimension extends Component {
|
||||
x,
|
||||
z,
|
||||
blocks,
|
||||
blocks_data: [],
|
||||
dirty: true,
|
||||
generated: false,
|
||||
mesh_version: 0,
|
||||
@@ -120,7 +106,7 @@ export class Dimension extends Component {
|
||||
chunk = this.add_chunk(block_chunk_x, block_chunk_z);
|
||||
}
|
||||
|
||||
const [nid, block_info] = EverythingRegistry.get_full<BlockRegistry>("blocks", block.id)!;
|
||||
const nid = EverythingRegistry.get_id("blocks", block.id)!;
|
||||
|
||||
const lx = block.x - block_chunk_x * CHUNK_SIZE;
|
||||
const lz = block.z - block_chunk_z * CHUNK_SIZE;
|
||||
@@ -130,9 +116,7 @@ export class Dimension extends Component {
|
||||
|
||||
chunk.blocks[index] = nid;
|
||||
chunk.dirty = true;
|
||||
if (block_info?.on_create) {
|
||||
block_info?.on_create(this, block);
|
||||
}
|
||||
this.#mark_border_neighbors_dirty(block_chunk_x, block_chunk_z, lx, lz);
|
||||
}
|
||||
|
||||
get_block(x: number, y: number, z: number) {
|
||||
@@ -154,33 +138,8 @@ export class Dimension extends Component {
|
||||
return chunk.blocks[index] & ID_MASK;
|
||||
}
|
||||
|
||||
add_block_data(block_data: BlockData) {
|
||||
const block_chunk_x = Math.floor(block_data.x / CHUNK_SIZE);
|
||||
const block_chunk_z = Math.floor(block_data.z / CHUNK_SIZE);
|
||||
let chunk = this.get_chunk(block_chunk_x, block_chunk_z);
|
||||
if (!chunk) {
|
||||
chunk = this.add_chunk(block_chunk_x, block_chunk_z);
|
||||
}
|
||||
|
||||
chunk.blocks_data.push(block_data);
|
||||
}
|
||||
|
||||
get_block_data<T>(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 undefined;
|
||||
}
|
||||
|
||||
return chunk.blocks_data.find((block_data) =>
|
||||
block_data.x === x && block_data.y === y && block_data.z === z
|
||||
) as BlockData<T>;
|
||||
}
|
||||
|
||||
break_block(x: number, y: number, z: number, drop_item: boolean = true) {
|
||||
// only changes what this client shows, drops and everything else happen on the server
|
||||
break_block(x: number, y: number, z: number) {
|
||||
const block_chunk_x = Math.floor(x / CHUNK_SIZE);
|
||||
const block_chunk_z = Math.floor(z / CHUNK_SIZE);
|
||||
const chunk = this.get_chunk(block_chunk_x, block_chunk_z);
|
||||
@@ -188,14 +147,6 @@ export class Dimension extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const block_info = EverythingRegistry.get_by_id<BlockRegistry>("blocks", this.get_block(x, y, z))!;
|
||||
|
||||
if (drop_item && block_info.drop_table) {
|
||||
const [player] = this.world.get_tag("player")!;
|
||||
const player_component = player.get(PlayerComponent)!;
|
||||
player_component.player_inventory.container.add_item(new ItemStack(block_info.drop_table));
|
||||
}
|
||||
|
||||
const lx = x - block_chunk_x * CHUNK_SIZE;
|
||||
const lz = z - block_chunk_z * CHUNK_SIZE;
|
||||
const ly = y;
|
||||
@@ -203,7 +154,11 @@ export class Dimension extends Component {
|
||||
const index = ly * CHUNK_SIZE * CHUNK_SIZE + lz * CHUNK_SIZE + lx;
|
||||
chunk.blocks[index] = AIR;
|
||||
chunk.dirty = true;
|
||||
this.#mark_border_neighbors_dirty(block_chunk_x, block_chunk_z, lx, lz);
|
||||
}
|
||||
|
||||
// a block on a chunk's edge changes which faces its neighbor shows
|
||||
#mark_border_neighbors_dirty(block_chunk_x: number, block_chunk_z: number, lx: number, lz: number) {
|
||||
if (lx === 0) {
|
||||
const n = this.get_chunk(block_chunk_x - 1, block_chunk_z);
|
||||
if (n) {
|
||||
@@ -226,10 +181,6 @@ export class Dimension extends Component {
|
||||
n.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (block_info?.on_break) {
|
||||
block_info.on_break(this, { x, y, z, id: block_info.id });
|
||||
}
|
||||
}
|
||||
|
||||
index_to_xyz(index: number) {
|
||||
@@ -252,18 +203,7 @@ export class Dimension extends Component {
|
||||
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
|
||||
// set a block the server told us about
|
||||
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) {
|
||||
@@ -273,7 +213,7 @@ export class Dimension extends Component {
|
||||
const current = this.get_block(x, y, z);
|
||||
if (id === AIR_ID) {
|
||||
if (current !== AIR) {
|
||||
this.break_block(x, y, z, false);
|
||||
this.break_block(x, y, z);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -423,7 +363,8 @@ export class Dimension extends Component {
|
||||
chunk.transparent_vertex_count = message.transparent_count / 9;
|
||||
}
|
||||
|
||||
// sets a block from generation, no hooks, makes a placeholder chunk like add_block does
|
||||
// a neighbor's leaves, only fill air so the result doesn't depend on which chunk loaded first.
|
||||
// the server builds chunks the same way (server/game/world.ts)
|
||||
#set_block_raw(x: number, y: number, z: number, nid: number) {
|
||||
if (y < 0 || y >= CHUNK_HEIGHT) {
|
||||
return;
|
||||
@@ -433,8 +374,11 @@ export class Dimension extends Component {
|
||||
const chunk = this.get_chunk(chunk_x, chunk_z) ?? this.add_chunk(chunk_x, chunk_z);
|
||||
const lx = x - chunk_x * CHUNK_SIZE;
|
||||
const lz = z - chunk_z * CHUNK_SIZE;
|
||||
chunk.blocks[y * CHUNK_AREA + lz * CHUNK_SIZE + lx] = nid;
|
||||
chunk.dirty = true;
|
||||
const index = y * CHUNK_AREA + lz * CHUNK_SIZE + lx;
|
||||
if (chunk.blocks[index] === AIR) {
|
||||
chunk.blocks[index] = nid;
|
||||
chunk.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
delete_chunk_mesh(chunk: Chunk) {
|
||||
|
||||
Reference in New Issue
Block a user