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
+5 -1
View File
@@ -11,7 +11,11 @@ export interface ClientContext {
net: ClientNet;
player: { readonly name: string; readonly position: Readonly<Position> };
// read only, what this client sees
world: { get_block(x: number, y: number, z: number): Id | undefined };
world: {
get_block(x: number, y: number, z: number): Id | undefined;
// ticks since 06:00 of day 1, see ServerWorld.time
readonly time: number;
};
log(...args: unknown[]): void;
}
+3
View File
@@ -128,6 +128,9 @@ export interface ServerWorld {
drop_item(x: number, y: number, z: number, item: ItemStack): void;
is_loaded(x: number, z: number): boolean;
readonly seed: string;
// ticks since 06:00 of day 1. a day is 36000 ticks (06:00 to 18:00), a night 12000
readonly time: number;
set_time(time: number): void;
}
export interface Player {
+6 -2
View File
@@ -7,7 +7,7 @@ export const AIR_ID = "bworld:air";
// bump when a client and server of different versions can't play together.
// the server rejects a different version before the client downloads anything
export const PROTOCOL_VERSION = 2;
export const PROTOCOL_VERSION = 3;
export interface PlayerInfo {
id: string;
@@ -101,6 +101,8 @@ export type ServerMessage =
changes: BlockChange[];
spawn: { x: number; y: number; z: number; yaw: number; pitch: number };
selected_slot: number;
// world time in ticks, see common/time.ts
time: number;
}
| { type: "player_join"; player: PlayerInfo }
| { type: "player_leave"; id: string }
@@ -120,7 +122,9 @@ export type ServerMessage =
| { type: "cursor"; item: ItemData | null }
| { type: "open_screen"; layout: ScreenLayout; properties: Record<string, number> }
| { type: "screen_properties"; properties: Record<string, number> }
| { type: "close_screen" };
| { type: "close_screen" }
// the world time, every second and when it's changed. clients count ticks in between
| { type: "time"; time: number };
export const MAX_NAME_LENGTH = 16;
export const MAX_CHAT_LENGTH = 256;
+67
View File
@@ -0,0 +1,67 @@
// the day and night cycle. world time counts ticks since 06:00 of day 1, it never wraps around
import { TICKS_PER_SECOND } from "./constants.ts";
// 30 minutes of day, 06:00 to 18:00
export const DAY_TICKS = 30 * 60 * TICKS_PER_SECOND;
// 10 minutes of night, 18:00 to 06:00
export const NIGHT_TICKS = 10 * 60 * TICKS_PER_SECOND;
export const CYCLE_TICKS = DAY_TICKS + NIGHT_TICKS;
// dusk right after sunset and dawn right before sunrise, so the whole day is bright
export const TWILIGHT_TICKS = 60 * TICKS_PER_SECOND;
// in game hours are longer during the day than at night, since both halves are 12 hours
const DAY_TICKS_PER_HOUR = DAY_TICKS / 12;
const NIGHT_TICKS_PER_HOUR = NIGHT_TICKS / 12;
// from midnight to 06:00, to count days from midnight
const TICKS_BEFORE_SIX = 6 * NIGHT_TICKS_PER_HOUR;
// named times of day, as ticks into the cycle
export const TIMES_OF_DAY: Record<string, number> = {
day: 0,
noon: 6 * DAY_TICKS_PER_HOUR,
sunset: DAY_TICKS,
night: DAY_TICKS + TWILIGHT_TICKS,
midnight: DAY_TICKS + 6 * NIGHT_TICKS_PER_HOUR,
};
// ticks into the current cycle, 0 is 06:00
export function time_of_day(time: number): number {
return ((time % CYCLE_TICKS) + CYCLE_TICKS) % CYCLE_TICKS;
}
export function is_day(time: number): boolean {
return time_of_day(time) < DAY_TICKS;
}
// the day number, starting at 1 and going up at midnight, and the time on a 24 hour clock
export function clock(time: number): { day: number; hour: number; minute: number } {
const t = time_of_day(time);
const hours = t < DAY_TICKS ? 6 + t / DAY_TICKS_PER_HOUR : 18 + (t - DAY_TICKS) / NIGHT_TICKS_PER_HOUR;
const total_minutes = Math.floor(hours * 60) % (24 * 60);
return {
day: Math.floor((time + TICKS_BEFORE_SIX) / CYCLE_TICKS) + 1,
hour: Math.floor(total_minutes / 60),
minute: total_minutes % 60,
};
}
// like "Day 3, 14:05"
export function format_clock(time: number): string {
const { day, hour, minute } = clock(time);
return `Day ${day}, ${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
}
// how much daylight there is, 1 all day and 0 in the middle of the night, fading through dusk and dawn.
// time can be fractional, for drawing between ticks
export function daylight(time: number): number {
const t = time_of_day(time);
if (t < DAY_TICKS) return 1;
const into_night = t - DAY_TICKS;
const until_day = CYCLE_TICKS - t;
const fade = Math.min(1, into_night / TWILIGHT_TICKS, until_day / TWILIGHT_TICKS);
return 1 - smoothstep(fade);
}
function smoothstep(x: number) {
return x * x * (3 - 2 * x);
}