Day night cycle

This commit is contained in:
2026-09-26 11:03:07 -03:00
parent 8f32a59bd6
commit 8b549162c4
16 changed files with 244 additions and 6 deletions
+1
View File
@@ -53,6 +53,7 @@ export class Client {
this.#on_disconnect = on_disconnect;
this.level = new ClientLevel(connection.seed);
this.level.time = connection.time;
for (const [x, y, z, id, state] of connection.initial_changes) {
this.level.record_change(x, y, z, id, state);
}
+3
View File
@@ -1,10 +1,13 @@
import type { Client } from "$/client/client.ts";
import { DebugUI } from "$/client/debug_ui.ts";
import { format_clock } from "$/common/time.ts";
// f3: every entity's fields, editable
export class DebugOverlay {
render(client: Client) {
DebugUI.begin("Entities", 10, 10, 300);
DebugUI.text(`${format_clock(client.level.time)} (tick ${client.level.time})`);
DebugUI.separator();
for (const entity of client.level.entities.values()) {
if (DebugUI.collapsing_header(`${entity.constructor.name} - ${entity.id}`)) {
+3
View File
@@ -87,6 +87,8 @@ export class ClientLevel {
second_timer = 0;
tick_timer = 0;
seed: string;
// world time in ticks, counted here between the server's updates. see common/time.ts
time = 0;
// blocks players changed from the generated terrain, per chunk, so they survive reloading chunks
changes = new Map<string, Map<string, BlockChange>>();
@@ -129,6 +131,7 @@ export class ClientLevel {
}
tick() {
this.time += 1;
for (const entity of this.entities.values()) {
entity.save_previous_position();
entity.tick();
+3
View File
@@ -100,6 +100,9 @@ function client_context(listing: ModListing): ClientContext {
if (nid === AIR) return AIR_ID;
return EverythingRegistry.get_by_id<BlockRegistry>("blocks", nid)?.id;
},
get time() {
return need_client().level.time;
},
},
log: (...args) => console.log(`[${mod}]`, ...args),
};
+2
View File
@@ -11,6 +11,7 @@ export class Connection {
initial_changes: BlockChange[];
spawn: { x: number; y: number; z: number; yaw: number; pitch: number };
selected_slot: number;
time: number;
// who was already there when we joined, they become entities in the level
initial_players: PlayerInfo[];
initial_entities: EntityInfo[];
@@ -24,6 +25,7 @@ export class Connection {
this.initial_changes = join.changes;
this.spawn = join.spawn;
this.selected_slot = join.selected_slot;
this.time = join.time;
this.initial_players = join.players;
this.initial_entities = join.entities;
}
+3
View File
@@ -31,6 +31,9 @@ export class ClientPacketListener {
case "player_join":
level.add_entity(new RemotePlayer(level, message.player));
break;
case "time":
level.time = message.time;
break;
case "player_leave":
level.remove_entity(message.id);
break;
+23 -1
View File
@@ -1,19 +1,41 @@
import type { Client } from "$/client/client.ts";
import { begin_mode_3d, end_mode_3d } from "$/client/renderer/mod.ts";
import { begin_mode_3d, clear_background, end_mode_3d, update_lightmap } from "$/client/renderer/mod.ts";
import { daylight } from "$/common/time.ts";
import { Hud } from "$/client/gui/hud.ts";
import { DebugOverlay } from "$/client/gui/debug_overlay.ts";
import { LevelRenderer } from "./level_renderer.ts";
// the sky at noon and in the middle of the night
const DAY_SKY = [0.69, 0.8, 1];
const NIGHT_SKY = [0.02, 0.03, 0.08];
// minecraft's darkest sky: moonlight still lights things a little
const NIGHT_SKY_LIGHT = 0.2;
// draws a frame, like minecraft's GameRenderer: the level from the camera, then the hud and screens on top
export class GameRenderer {
level_renderer = new LevelRenderer();
hud = new Hud();
debug_overlay = new DebugOverlay();
// what the lightmap was last made for, so it's only rebuilt when the light changes
#lightmap_daylight = -1;
// sky color and sky light for the time of day
#setup_sky(client: Client, partial_tick: number) {
const light = daylight(client.level.time + partial_tick);
const [r, g, b] = DAY_SKY.map((day, i) => NIGHT_SKY[i] + (day - NIGHT_SKY[i]) * light);
clear_background(r, g, b, 1);
if (Math.abs(light - this.#lightmap_daylight) > 0.001) {
this.#lightmap_daylight = light;
update_lightmap(NIGHT_SKY_LIGHT + (1 - NIGHT_SKY_LIGHT) * light);
}
}
// partial_tick is how far this frame is between the last tick and the next, entities are drawn in between
render(client: Client, partial_tick: number) {
const camera = client.camera;
camera.setup(client.player, partial_tick);
this.#setup_sky(client, partial_tick);
begin_mode_3d(camera);
this.level_renderer.render_opaque(client.level, camera);