141 lines
4.2 KiB
TypeScript
141 lines
4.2 KiB
TypeScript
import { AssetManager } from "./assets.ts";
|
|
import { ClientWorld } from "./client_world.ts";
|
|
import { InputManager } from "./input_manager.ts";
|
|
import { Connection, get_player_name } from "./network.ts";
|
|
import { connect, HandshakeError, is_trusted, join, load_atlas, remember_trust, server_address } from "./handshake.ts";
|
|
import { confirm_mods } from "./confirm_mods.ts";
|
|
import { ModLoadError } from "$/common/mod_loader.ts";
|
|
import { begin_drawing, clear_background, end_drawing, init_font, init_window, load_texture } from "./renderer/mod.ts";
|
|
import { is_stopped, show_fatal_error } from "./fatal.ts";
|
|
import { load_client_mods, set_mods_world } from "./mods.ts";
|
|
|
|
export class ClientLoop {
|
|
running = false;
|
|
last_time = 0;
|
|
world: ClientWorld;
|
|
|
|
frame_count = 0;
|
|
last_fps_time = 0;
|
|
|
|
constructor(world: ClientWorld) {
|
|
this.world = world;
|
|
}
|
|
|
|
start() {
|
|
document.addEventListener("visibilitychange", () => {
|
|
if (!document.hidden) {
|
|
this.last_time = performance.now();
|
|
}
|
|
});
|
|
|
|
this.running = true;
|
|
const now = performance.now();
|
|
this.last_time = now;
|
|
this.frame_count = 0;
|
|
this.last_fps_time = this.last_time;
|
|
requestAnimationFrame((time) => this.loop(time));
|
|
}
|
|
|
|
stop() {
|
|
this.running = false;
|
|
}
|
|
|
|
loop(time: number) {
|
|
if (!this.running || is_stopped()) {
|
|
return;
|
|
}
|
|
|
|
const delta = (time - this.last_time) / 1000;
|
|
|
|
begin_drawing();
|
|
clear_background(0.69, 0.8, 1, 1.0);
|
|
this.world.update(delta);
|
|
end_drawing();
|
|
|
|
this.frame_count += 1;
|
|
const now = performance.now();
|
|
if (now - this.last_fps_time >= 1000) {
|
|
const fps = (this.frame_count * 1000) / (now - this.last_fps_time);
|
|
console.log(`FPS: ${fps.toFixed(2)}`);
|
|
this.frame_count = 0;
|
|
this.last_fps_time = now;
|
|
}
|
|
|
|
InputManager.update();
|
|
|
|
this.last_time = time;
|
|
requestAnimationFrame((time) => this.loop(time));
|
|
}
|
|
}
|
|
|
|
const canvas = document.getElementById("game") as HTMLCanvasElement;
|
|
if (!canvas) {
|
|
throw Error("Canvas was not found");
|
|
}
|
|
|
|
await init_window(canvas);
|
|
|
|
InputManager.initialize(canvas);
|
|
|
|
AssetManager.instance.load("bworld:assets_text", "/assets/ASSETS.md");
|
|
|
|
AssetManager.instance.load("bworld:player", "/assets/sprites/player.png");
|
|
AssetManager.instance.load("bworld:roguelike", "/assets/sprites/roguelike.png");
|
|
AssetManager.instance.load("bworld:ui", "/assets/sprites/ui.png");
|
|
|
|
AssetManager.instance.load("bworld:m6x11", "/assets/fonts/m6x11.png");
|
|
AssetManager.instance.load("bworld:m6x11_fnt", "/assets/fonts/m6x11.fnt");
|
|
|
|
await AssetManager.instance.load_all();
|
|
|
|
init_font();
|
|
|
|
// the game only runs against a server, it owns the world and everything in it. see "Delivery to clients" in MODS.md
|
|
async function join_server(): Promise<Connection> {
|
|
const address = server_address();
|
|
const { socket, welcome } = await connect(address, get_player_name());
|
|
console.log(`Connected to ${address.ws_url}`);
|
|
|
|
try {
|
|
if (address.cross_origin && !is_trusted(address, welcome)) {
|
|
if (!await confirm_mods(address, welcome)) {
|
|
throw new HandshakeError(`You didn't join ${address.base.host}`);
|
|
}
|
|
remember_trust(address, welcome);
|
|
}
|
|
|
|
// textures, blocks and items all come from the server's mods, so this happens before anything else
|
|
const atlas = await load_atlas(address, welcome.atlas);
|
|
AssetManager.instance.assets["bworld:textures"] = load_texture(atlas.image);
|
|
AssetManager.instance.assets["bworld:textures_info"] = atlas.regions;
|
|
await load_client_mods(welcome.mods, address.base);
|
|
console.log(`Mods: ${welcome.mods.map((mod) => `${mod.id} ${mod.version}`).join(", ") || "none"}`);
|
|
|
|
const joined = await join(socket);
|
|
return new Connection(socket, welcome, joined);
|
|
} catch (e) {
|
|
socket.close();
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
try {
|
|
const connection = await join_server();
|
|
const client_world = new ClientWorld(connection);
|
|
set_mods_world(client_world);
|
|
client_world.add_chat("Connected to the server");
|
|
|
|
const loop = new ClientLoop(client_world);
|
|
loop.start();
|
|
|
|
console.log("Game started");
|
|
} catch (e) {
|
|
console.error(e);
|
|
const message = e instanceof HandshakeError
|
|
? e.message
|
|
: e instanceof ModLoadError
|
|
? `Couldn't load the server's mods: ${e.message}`
|
|
: `Something went wrong joining the server: ${e instanceof Error ? e.message : e}`;
|
|
show_fatal_error(message);
|
|
}
|