Main menu
This commit is contained in:
+114
-59
@@ -1,26 +1,36 @@
|
||||
import { AssetManager } from "./assets.ts";
|
||||
import { Client } from "./client.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 { Connection } from "./network.ts";
|
||||
import { connect, HandshakeError, is_trusted, join, load_atlas, remember_trust, ServerAddress } 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 {
|
||||
begin_drawing,
|
||||
canvas,
|
||||
clear_background,
|
||||
end_drawing,
|
||||
init_font,
|
||||
init_window,
|
||||
load_texture,
|
||||
resize_canvas,
|
||||
} from "./renderer/mod.ts";
|
||||
import { is_stopped, show_fatal_error } from "./fatal.ts";
|
||||
import { load_client_mods, set_mods_client } from "./mods.ts";
|
||||
import type { GuiScreen } from "./gui/gui_screen.ts";
|
||||
import { TitleScreen } from "./gui/title_screen.ts";
|
||||
import { DisconnectedScreen } from "./gui/disconnected_screen.ts";
|
||||
|
||||
// runs whatever is showing every frame: a menu screen before joining (and after leaving), or the game
|
||||
export class ClientLoop {
|
||||
running = false;
|
||||
last_time = 0;
|
||||
client: Client;
|
||||
client: Client | undefined;
|
||||
screen: GuiScreen | undefined;
|
||||
|
||||
frame_count = 0;
|
||||
last_fps_time = 0;
|
||||
|
||||
constructor(client: Client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
start() {
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (!document.hidden) {
|
||||
@@ -40,6 +50,21 @@ export class ClientLoop {
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
show_screen(screen: GuiScreen) {
|
||||
this.client = undefined;
|
||||
this.screen = screen;
|
||||
InputManager.set_mouse_grabbed(false);
|
||||
}
|
||||
|
||||
play(connection: Connection) {
|
||||
const client = new Client(connection, (message) => this.show_screen(new DisconnectedScreen(message)));
|
||||
set_mods_client(client);
|
||||
client.chat.add("Connected to the server");
|
||||
this.screen = undefined;
|
||||
this.client = client;
|
||||
console.log("Game started");
|
||||
}
|
||||
|
||||
loop(time: number) {
|
||||
if (!this.running || is_stopped()) {
|
||||
return;
|
||||
@@ -49,7 +74,12 @@ export class ClientLoop {
|
||||
|
||||
begin_drawing();
|
||||
clear_background(0.69, 0.8, 1, 1.0);
|
||||
this.client.run_frame(delta);
|
||||
if (this.client) {
|
||||
this.client.run_frame(delta);
|
||||
} else if (this.screen) {
|
||||
this.screen.on_tick(delta);
|
||||
this.screen.on_render();
|
||||
}
|
||||
end_drawing();
|
||||
|
||||
this.frame_count += 1;
|
||||
@@ -68,14 +98,70 @@ export class ClientLoop {
|
||||
}
|
||||
}
|
||||
|
||||
const canvas = document.getElementById("game") as HTMLCanvasElement;
|
||||
if (!canvas) {
|
||||
// a server's blocks, items and scripts can't be taken back out once loaded, so a failure after that point
|
||||
// can't go back to the title screen to try again, the page has to start over
|
||||
class FailedAfterLoadingMods extends Error {}
|
||||
|
||||
// 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(address: ServerAddress, name: string, status: (text: string) => void): Promise<Connection> {
|
||||
const { socket, welcome } = await connect(address, name);
|
||||
console.log(`Connected to ${address.ws_url}`);
|
||||
|
||||
let loaded_mods = false;
|
||||
try {
|
||||
if (address.cross_origin && !is_trusted(address, welcome)) {
|
||||
status("Waiting for you to accept the server's mods...");
|
||||
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
|
||||
status("Downloading the server's mods...");
|
||||
const atlas = await load_atlas(address, welcome.atlas);
|
||||
loaded_mods = true;
|
||||
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"}`);
|
||||
|
||||
status("Joining...");
|
||||
const joined = await join(socket);
|
||||
return new Connection(socket, welcome, joined);
|
||||
} catch (e) {
|
||||
socket.close();
|
||||
const message = error_message(e);
|
||||
throw loaded_mods ? new FailedAfterLoadingMods(message) : new HandshakeError(message);
|
||||
}
|
||||
}
|
||||
|
||||
function error_message(e: unknown) {
|
||||
if (e instanceof HandshakeError) {
|
||||
return e.message;
|
||||
}
|
||||
if (e instanceof ModLoadError) {
|
||||
return `Couldn't load the server's mods: ${e.message}`;
|
||||
}
|
||||
return `Something went wrong joining the server: ${e instanceof Error ? e.message : e}`;
|
||||
}
|
||||
|
||||
const game_canvas = document.getElementById("game") as HTMLCanvasElement;
|
||||
if (!game_canvas) {
|
||||
throw Error("Canvas was not found");
|
||||
}
|
||||
|
||||
await init_window(canvas);
|
||||
try {
|
||||
await init_window(game_canvas);
|
||||
} catch (e) {
|
||||
show_fatal_error(e instanceof Error ? e.message : String(e));
|
||||
throw e;
|
||||
}
|
||||
|
||||
InputManager.initialize(canvas);
|
||||
InputManager.initialize(game_canvas);
|
||||
self.addEventListener("resize", resize_canvas);
|
||||
resize_canvas();
|
||||
canvas.addEventListener("contextmenu", (event) => event.preventDefault());
|
||||
|
||||
AssetManager.instance.load("bworld:assets_text", "/assets/ASSETS.md");
|
||||
|
||||
@@ -90,51 +176,20 @@ 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}`);
|
||||
const loop = new ClientLoop();
|
||||
loop.show_screen(
|
||||
new TitleScreen(async (address, name, status) => {
|
||||
let connection: Connection;
|
||||
try {
|
||||
connection = await join_server(address, name, status);
|
||||
} catch (e) {
|
||||
if (e instanceof FailedAfterLoadingMods) {
|
||||
loop.show_screen(new DisconnectedScreen(e.message));
|
||||
return;
|
||||
}
|
||||
remember_trust(address, welcome);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// 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 = new Client(connection);
|
||||
set_mods_client(client);
|
||||
client.chat.add("Connected to the server");
|
||||
|
||||
const loop = new ClientLoop(client);
|
||||
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);
|
||||
}
|
||||
loop.play(connection);
|
||||
}),
|
||||
);
|
||||
loop.start();
|
||||
|
||||
Reference in New Issue
Block a user