import createDebug from "debug"; import type { DespawnPartPacket, MessagePacket, Packet, PartPositionsPacket, PlanetPositionsPacket, PlayerLeavePacket, PlayerListPacket, SpawnPartPacket, SpawnPlayerPacket, } from "./protocol.ts"; import { MessageType, PacketType } from "./protocol.ts"; import { appendPacket } from "./packet_ui.ts"; import { global } from "./globals.ts"; import { startRender } from "./rendering.ts"; import { ButtonType } from "./protocol.ts"; import type Chatbox from "./components/Chatbox.svelte"; import {part_texture_url} from "./textures.js"; import * as PIXI from "pixi.js"; const logger = createDebug("hub"); export interface ClientHub { socket: WebSocket; } export function sendPacket(client: ClientHub, packet: Packet) { client.socket.send(JSON.stringify(packet)); appendPacket(packet); } export async function hub_connect( url: string, username: string, chatbox: Chatbox, ): Promise { logger("connecting to client hub at " + url); let ws = new WebSocket(url); ws.onerror = (e) => { console.error(e); throw e; }; ws.onopen = () => { logger("connected to client hub, sending username and auth details"); let client: ClientHub = { socket: ws, }; global.client = client; let packet: Packet = { t: PacketType.ClientLogin, c: { username, jwt: null, }, }; sendPacket(client, packet); // input document.onkeydown = (e) => { // currently, input packet is sent on any key down. fix that if (e.key == "ArrowUp" || e.key == "w") { global.up = true; } if (e.key == "ArrowDown" || e.key == "s") { global.down = true; } if (e.key == "ArrowLeft" || e.key == "a") { global.left = true; } if (e.key == "ArrowRight" || e.key == "d") { global.right = true; } let input_packet: Packet = { t: PacketType.PlayerInput, c: { up: global.up, down: global.down, left: global.left, right: global.right, }, }; sendPacket(client, input_packet); }; document.onkeyup = (e) => { if (e.key == "ArrowUp" || e.key == "w") { global.up = false; } if (e.key == "ArrowDown" || e.key == "s") { global.down = false; } if (e.key == "ArrowLeft" || e.key == "a") { global.left = false; } if (e.key == "ArrowRight" || e.key == "d") { global.right = false; } let input_packet: Packet = { t: PacketType.PlayerInput, c: { up: global.up, down: global.down, left: global.left, right: global.right, }, }; sendPacket(client, input_packet); }; document.onmousedown = (e) => { if (global.me !== null) { let me_transform = global.parts_map.get(global.me?.part_id)!.transform; let x = e.clientX - window.innerWidth / 2 + me_transform.x; let y = e.clientY - window.innerHeight / 2 + me_transform.y; let button: ButtonType; if (e.button == 0) { button = ButtonType.Left; } else if (e.button == 1) { button = ButtonType.Middle; } else if (e.button == 2) { button = ButtonType.Right; } let packet: Packet = { t: PacketType.PlayerMouseInput, c: { x: x, y: y, button: button!, released: false, }, }; sendPacket(client, packet); } }; document.onmouseup = (e) => { if (global.me !== null) { let me_transform = global.parts_map.get(global.me?.part_id)!.transform; let x = e.clientX - window.innerWidth / 2 + me_transform.x; let y = e.clientY - window.innerHeight / 2 + me_transform.y; let button: ButtonType; if (e.button == 0) { button = ButtonType.Left; } else if (e.button == 1) { button = ButtonType.Middle; } else if (e.button == 2) { button = ButtonType.Right; } let packet: Packet = { t: PacketType.PlayerMouseInput, c: { x: x, y: y, button: button!, released: true, }, }; sendPacket(client, packet); } }; ws.onmessage = (e) => { let packet: Packet = JSON.parse(e.data); appendPacket(packet); if (packet.t == PacketType.SpawnPlayer) { let p = packet.c; if (p.username === username) { global.me = { username: p.username, part_id: p.id, }; logger(`client spawned (username=${p.username} part_id=${p.id})`); startRender(); } else { global.players_map.set(p.id, p.username); global.inverse_players_map.set(p.username, p.id); logger(`player joined (username=${p.username} part_id=${p.id})`); } } else if (packet.t == PacketType.PlayerList) { let p = packet.c; for (let i = 0; i < p.players.length; i++) { global.players_map.set(p.players[i][0], p.players[i][1]); global.inverse_players_map.set(p.players[i][1], p.players[i][0]); } logger(`added ${p.players.length} existing players to player list`); } else if (packet.t == PacketType.PlanetPositions) { let p = packet.c; for (let i = 0; i < p.planets.length; i++) { global.planets_map.set(p.planets[i][0], p.planets[i][1]); } } else if (packet.t == PacketType.PartPositions) { let p = packet.c; for (let i = 0; i < p.parts.length; i++) { let id = p.parts[i][0]; let new_part = p.parts[i][1]; /*if (global.parts_map.has(id)) { let old_part = global.parts_map.get(id)!; let dx = new_part.transform.x - old_part.transform.x; let dy = new_part.transform.y - old_part.transform.y; let drot = new_part.transform.rot - old_part.transform.rot; const CUTOFF_XY = 0.01; const CUTOFF_ROT = 0.01; if (dx < CUTOFF_XY && dy < CUTOFF_XY && drot < CUTOFF_ROT) { continue; // this packet is under the cutoff, we don't care about it } }*/ global.parts_map.set(id, new_part); if (id === global.me?.part_id) { document.getElementById("pos-val-x")!.innerText = Math.round( new_part.transform.x, ).toString(); document.getElementById("pos-val-y")!.innerText = Math.round( new_part.transform.y, ).toString(); } } } else if (packet.t == PacketType.SpawnPart) { let p = packet.c; let id = p.id; let part = p.part; global.parts_map.set(id, part); logger(`spawn part`); let part_sprite = PIXI.Sprite.from(part_texture_url(part.part_type)); global.rendering!.part_sprite_map.set(id, part_sprite); global.rendering!.app.stage.addChild(part_sprite); } else if (packet.t == PacketType.DespawnPart) { let p = packet.c; let id = p.id; let part = global.parts_map.get(id); let part_sprite = PIXI.Sprite.from(part_texture_url(part.part_type)); logger(`despawned part`); global.parts_map.delete(id); global.rendering!.part_sprite_map.delete(id); global.rendering!.app.stage.removeChild(part_sprite); } else if (packet.t == PacketType.PlayerLeave) { let p = packet.c; let username = global.players_map.get(p.id)!; global.inverse_players_map.delete(username); global.players_map.delete(p.id); logger(`player removed (id=${p.id})`); } else if (packet.t == PacketType.Message) { let p = packet.c; logger( `message type=${p.message_type} actor=${p.actor} content=${p.content}`, ); if (p.message_type == MessageType.Server) { chatbox.addMessage("server-message", `[SERVER] ${p.content}`); } else if (p.message_type == MessageType.Chat) { chatbox.addMessage("global-message", `${p.actor}: ${p.content}`); } else if (p.message_type == MessageType.Direct) { // actor is who sent the message. destination is not included in this packet if (p.actor === global.me!.username) { // skip (shown above) } else { chatbox.addMessage( "direct-message", `${p.actor} -> you: ${p.content}`, ); } } else { chatbox.addMessage("server-error", `${p.content}`); } } else { logger(`unrecognized packet type ${packet.t}`); } }; return client; }; return null; }