Files
bworld/client/systems/network_system.ts
T
2026-09-25 13:25:25 -03:00

123 lines
4.0 KiB
TypeScript

import { System } from "$/common/ecs/mod.ts";
import { Position } from "$/common/components/position.ts";
import { ClientWorld } from "../client_world.ts";
import { Camera } from "../components/camera.ts";
import { Container, ItemStack } from "$/common/inventory.ts";
import { PlayerComponent } from "../player.ts";
import { GuiContainer } from "../gui/gui_container.ts";
import { show_fatal_error } from "../fatal.ts";
const MOVE_SEND_INTERVAL = 1 / 10;
const REMOTE_PLAYER_SMOOTHING = 12;
export class NetworkSystem extends System {
move_timer = 0;
update(world: ClientWorld, delta: number): void {
const connection = world.connection;
const [local_player] = world.get_tag("player")!;
const player_component = local_player.get(PlayerComponent)!;
const inventories = player_component.inventories;
for (const message of connection.incoming) {
switch (message.type) {
case "player_join":
connection.add_player(message.player);
break;
case "player_leave":
connection.players.delete(message.id);
break;
case "player_move": {
const player = connection.players.get(message.id);
if (player) {
player.x = message.x;
player.y = message.y;
player.z = message.z;
player.yaw = message.yaw;
player.pitch = message.pitch;
}
break;
}
case "set_block":
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);
break;
case "container": {
let container = message.container === "screen"
? inventories.screen
: inventories[message.container];
if (message.container === "screen" && container?.size !== message.items.length) {
container = inventories.screen = new Container(message.items.length);
}
container?.load(message.items);
break;
}
case "cursor":
inventories.cursor.item = message.item ? ItemStack.from_data(message.item) : undefined;
break;
case "open_screen": {
// replace anything open locally without telling the server, it just opened this one
player_component.screens.length = 0;
const size = Math.max(0, ...message.layout.slots.map((slot) => slot.index + 1));
inventories.screen = new Container(size);
player_component.screens.push(
new GuiContainer(inventories, (m) => connection.send(m), message.layout, message.properties),
);
break;
}
case "screen_properties": {
const screen = player_component.screens.at(-1);
if (screen instanceof GuiContainer) {
screen.properties = message.properties;
}
break;
}
case "teleport": {
const position = local_player.get(Position)!;
position.x = message.x;
position.y = message.y;
position.z = message.z;
break;
}
case "close_screen":
// closed by the server (the block broke), it already put everything back
player_component.screens = player_component.screens.filter((s) => !(s instanceof GuiContainer));
inventories.screen = undefined;
break;
}
}
connection.incoming.length = 0;
if (connection.closed) {
show_fatal_error("Lost connection to the server");
return;
}
const t = Math.min(1, delta * REMOTE_PLAYER_SMOOTHING);
for (const player of connection.players.values()) {
player.display_x += (player.x - player.display_x) * t;
player.display_y += (player.y - player.display_y) * t;
player.display_z += (player.z - player.display_z) * t;
}
this.move_timer += delta;
if (this.move_timer >= MOVE_SEND_INTERVAL) {
this.move_timer = 0;
const [player] = world.get_tag("player")!;
const position = player.get(Position)!;
const camera = player.get(Camera)!;
connection.send({
type: "move",
x: position.x,
y: position.y,
z: position.z,
yaw: camera.yaw,
pitch: camera.pitch,
});
}
}
}