From a0a2068b66204d7d06eb3c3b4aa61b28469121f6 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sat, 24 Aug 2024 19:11:26 -0700 Subject: improve event parsing and begin sending events from the client --- static/src/component.ts | 8 +++++++ static/src/entity.ts | 11 +++++++++ static/src/event.ts | 28 ++++++++++++++++++++++ static/src/main.ts | 47 ++++++++++++++++++++++++++++++------ static/src/mouse_controller.ts | 54 ++++++++++++++++++++++++++++++++++++++++++ static/src/vector.ts | 10 ++++++++ static/tsconfig.json | 4 ++-- 7 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 static/src/component.ts create mode 100644 static/src/entity.ts create mode 100644 static/src/event.ts create mode 100644 static/src/mouse_controller.ts create mode 100644 static/src/vector.ts (limited to 'static') diff --git a/static/src/component.ts b/static/src/component.ts new file mode 100644 index 0000000..d338cad --- /dev/null +++ b/static/src/component.ts @@ -0,0 +1,8 @@ +export enum ComponentType { + POSITION = "POSITION", + RENDERABLE = "RENDERABLE", +} + +export interface Component { + name: string; +} diff --git a/static/src/entity.ts b/static/src/entity.ts new file mode 100644 index 0000000..811d05a --- /dev/null +++ b/static/src/entity.ts @@ -0,0 +1,11 @@ +import type { Component } from "./component"; + +export enum EntityType { + LASER = "LASER", +} + +export interface Entity { + entity_type: EntityType; + id: string; + components: Record; +} diff --git a/static/src/event.ts b/static/src/event.ts new file mode 100644 index 0000000..cd50122 --- /dev/null +++ b/static/src/event.ts @@ -0,0 +1,28 @@ +export enum EventType { + INITIAL_STATE = "INITIAL_STATE", + SET_CONTROLLABLE = "SET_CONTROLLABLE", + ENTITY_BORN = "ENTITY_BORN", + ENTITY_DEATH = "ENTITY_DEATH", + ENTITY_POSITION_UPDATE = "ENTITY_POSITION_UPDATE", +} + +export interface Event { + event_type: EventType; + data: any; +} + +export interface InitialStateEvent extends Event { + event_type: EventType.INITIAL_STATE; + data: { + world: { width: number; height: number }; + entities: any[]; + }; +} + +export interface SetControllableEvent extends Event { + event_type: EventType.SET_CONTROLLABLE; + data: { + id: string; + client_id: string; + }; +} diff --git a/static/src/main.ts b/static/src/main.ts index 8d12587..fc04b72 100644 --- a/static/src/main.ts +++ b/static/src/main.ts @@ -1,18 +1,51 @@ import $ from "jquery"; +import { Vec2 } from "./vector"; +import { EventType, type SetControllableEvent, type Event } from "./event"; +import { MouseController } from "./mouse_controller"; $(document).ready(async () => { - await fetch("/assign", { + const session_id = await fetch("/assign", { credentials: "include", + }) + .then((res) => res.json()) + .then(({ session }) => session); + + const controllable_entities = new Set(); + const control_callback = (movement: Vec2) => { + for (const id of controllable_entities) { + const message = JSON.stringify({ + event_type: EventType.ENTITY_POSITION_UPDATE, + data: { id, position: movement }, + }); + ws.send(message); + } + }; + const mouse_controller = new MouseController(control_callback); + $(document).on("mousemove", (event) => { + mouse_controller.move(event.clientX, event.clientY); }); + mouse_controller.start(); const ws = new WebSocket("/ws"); - ws.onopen = () => { - console.log("connected"); - }; + await new Promise((resolve) => { + ws.onopen = () => { + console.log("connected"); + resolve(); + }; + }); ws.onmessage = ({ data }) => { - console.log(JSON.parse(data)); + const [event_type, event_data] = JSON.parse(data); + const message = { event_type, data: event_data } as Event; + + console.log("Received message", message); + if (message.event_type === EventType.SET_CONTROLLABLE) { + const event = message as SetControllableEvent; + if (event.data.client_id === session_id) { + controllable_entities.add(event.data.id); + } + } }; - ws.onclose = (e) => { - console.log("disconnected", e); + ws.onclose = () => { + controllable_entities.clear(); }; }); diff --git a/static/src/mouse_controller.ts b/static/src/mouse_controller.ts new file mode 100644 index 0000000..b8849b4 --- /dev/null +++ b/static/src/mouse_controller.ts @@ -0,0 +1,54 @@ +import { Vec2 } from "./vector"; + +export class MouseController { + private readonly debounce_ms = 400; + private readonly movement_threshold = 40; + private last_event_time = Date.now(); + private movement_queue: Vec2[] = []; + private interval_id: number | null = null; + + constructor(private readonly callback: (new_movement: Vec2) => void) {} + + public start() { + if (this.interval_id !== null) { + return; + } + this.interval_id = setInterval(() => { + this.publish_movement(); + }, this.debounce_ms); + } + + public stop() { + if (this.interval_id === null) { + return; + } + clearInterval(this.interval_id); + this.interval_id = null; + } + + public move(x: number, y: number) { + const new_movement = new Vec2(x, y); + const last_movement = this.movement_queue.at(-1); + this.movement_queue.push(new_movement); + if ( + typeof last_movement === "undefined" || + new_movement.distance_to(last_movement) < this.movement_threshold + ) { + return; + } + this.publish_movement(); + } + + private publish_movement() { + if ( + Date.now() - this.last_event_time < this.debounce_ms || + this.movement_queue.length === 0 + ) { + return; + } + + this.last_event_time = Date.now(); + this.callback(this.movement_queue.at(-1)!); + this.movement_queue = []; + } +} diff --git a/static/src/vector.ts b/static/src/vector.ts new file mode 100644 index 0000000..e7be9fa --- /dev/null +++ b/static/src/vector.ts @@ -0,0 +1,10 @@ +export class Vec2 { + constructor( + private readonly x: number, + private readonly y: number, + ) {} + + public distance_to(that: Vec2): number { + return Math.sqrt((this.x - that.x) ** 2 + (this.y - that.y) ** 2); + } +} diff --git a/static/tsconfig.json b/static/tsconfig.json index 0511b9f..8f3e483 100644 --- a/static/tsconfig.json +++ b/static/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2022", "useDefineForClassFields": true, "module": "ESNext", - "lib": ["ES2020", "DOM", "DOM.Iterable"], + "lib": ["ES2022", "DOM", "DOM.Iterable"], "skipLibCheck": true, /* Bundler mode */ -- cgit v1.3