diff options
| author | Elizabeth Hunt <elizabeth@simponic.xyz> | 2024-09-02 20:10:35 -0700 |
|---|---|---|
| committer | Elizabeth Hunt <elizabeth@simponic.xyz> | 2024-09-02 20:10:35 -0700 |
| commit | d35ab6cc851d3fc85f55cfb5676f8dc3552fab24 (patch) | |
| tree | bef9784e90e2e8f8f5b9e05300238747b775e618 /static/src | |
| parent | a0a2068b66204d7d06eb3c3b4aa61b28469121f6 (diff) | |
| download | kennel.hatecomputers.club-d35ab6cc851d3fc85f55cfb5676f8dc3552fab24.tar.gz kennel.hatecomputers.club-d35ab6cc851d3fc85f55cfb5676f8dc3552fab24.zip | |
a bit more progress
Diffstat (limited to 'static/src')
| -rw-r--r-- | static/src/event.ts | 28 | ||||
| -rw-r--r-- | static/src/main.ts | 114 | ||||
| -rw-r--r-- | static/src/mouse_controller.ts | 42 | ||||
| -rw-r--r-- | static/src/network.ts | 73 | ||||
| -rw-r--r-- | static/src/vector.ts | 4 |
5 files changed, 176 insertions, 85 deletions
diff --git a/static/src/event.ts b/static/src/event.ts deleted file mode 100644 index cd50122..0000000 --- a/static/src/event.ts +++ /dev/null @@ -1,28 +0,0 @@ -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 fc04b72..eb98ba8 100644 --- a/static/src/main.ts +++ b/static/src/main.ts @@ -1,51 +1,93 @@ import $ from "jquery"; import { Vec2 } from "./vector"; -import { EventType, type SetControllableEvent, type Event } from "./event"; +import { + EventType, + type SetControllableEvent, + type Event, + type EventPublisher, +} from "./event"; import { MouseController } from "./mouse_controller"; +import { + EntityPositionUpdateEvent, + EventProcessor, + EventQueue, + WebSocketEventQueue, +} from "./network"; -$(document).ready(async () => { +class KennelClient { + private running: boolean; + private last_update: number; + + private controllable_entities: Set<string>; + private mouse_controller: MouseController; + + constructor( + private readonly event_queue: EventQueue, + private readonly event_publisher: EventPublisher, + ) { + this.last_update = 0; + this.running = false; + + this.mouse_controller = new MouseController((position: Vec2) => + this.on_mouse_move(position), + ); + } + + public start() { + this.running = true; + this.last_update = performance.now(); + this.mouse_controller.start(); + + const loop = (timestamp: number) => { + const dt = timestamp - this.last_update; + this.propogate_state_after(dt); + requestAnimationFrame(loop); // tail call recursion! /s + }; + requestAnimationFrame(loop); + } + + public close() { + this.running = false; + this.mouse_controller.stop(); + this.controllable_entities.clear(); + } + + private propogate_state_after(dt: number) { + // TODO: interpolate cats and lasers and stuff + } + + private on_mouse_move(position: Vec2) { + for (const id of this.controllable_entities) { + const event: EntityPositionUpdateEvent = { + event_type: EventType.ENTITY_POSITION_UPDATE, + data: { id, position: { x: position.x, y: position.y } }, + }; + this.event_publisher.send(event); + } + } +} + +$(async () => { const session_id = await fetch("/assign", { credentials: "include", }) .then((res) => res.json()) .then(({ session }) => session); - const controllable_entities = new Set<string>(); - 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"); await new Promise<void>((resolve) => { - ws.onopen = () => { - console.log("connected"); - resolve(); - }; + ws.onopen = () => resolve(); }); - ws.onmessage = ({ 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 = () => { - controllable_entities.clear(); - }; + const queue: EventQueue = new WebSocketEventQueue(ws); + + const kennel_client = new KennelClient(session_id, null); + + ws.onclose = () => kennel_client.close(); + + const mouse_controller = new MouseController(on_mouse_move); + $(document).on("mousemove", (event) => { + mouse_controller.move(event.clientX, event.clientY); + }); + mouse_controller.start(); }); diff --git a/static/src/mouse_controller.ts b/static/src/mouse_controller.ts index b8849b4..c7ae304 100644 --- a/static/src/mouse_controller.ts +++ b/static/src/mouse_controller.ts @@ -1,21 +1,23 @@ 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; + private last_movement: Vec2 | undefined; + private interval_id: number | undefined; - constructor(private readonly callback: (new_movement: Vec2) => void) {} + constructor( + private readonly publisher: (new_movement: Vec2) => void | Promise<void>, + private readonly debounce_ms = 200, + private readonly l2_norm_threshold = 40, + ) {} public start() { - if (this.interval_id !== null) { + if (typeof this.interval_id !== "undefined") { return; } - this.interval_id = setInterval(() => { - this.publish_movement(); - }, this.debounce_ms); + this.interval_id = setInterval( + () => this.publish_movement(), + this.debounce_ms, + ); } public stop() { @@ -23,32 +25,30 @@ export class MouseController { return; } clearInterval(this.interval_id); - this.interval_id = null; + delete this.interval_id; } 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 + typeof this.last_movement !== "undefined" && + new_movement.distance_to(this.last_movement) >= this.l2_norm_threshold ) { - return; + this.publish_movement(); } - this.publish_movement(); + this.last_movement = new_movement; } private publish_movement() { if ( - Date.now() - this.last_event_time < this.debounce_ms || - this.movement_queue.length === 0 + typeof this.last_movement === "undefined" || + Date.now() - this.last_event_time < this.debounce_ms ) { return; } this.last_event_time = Date.now(); - this.callback(this.movement_queue.at(-1)!); - this.movement_queue = []; + this.publisher(this.last_movement.copy()); + delete this.last_movement; } } diff --git a/static/src/network.ts b/static/src/network.ts new file mode 100644 index 0000000..0d4e216 --- /dev/null +++ b/static/src/network.ts @@ -0,0 +1,73 @@ +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 EntityPositionUpdateEvent extends Event { + event_type: EventType.ENTITY_POSITION_UPDATE; + data: { + id: string; + position: { + x: number; + y: number; + }; + }; +} + +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; + }; +} + +export interface EventQueue { + peek(): Event[]; + clear(): void; +} + +export interface EventPublisher { + add(event: Event): void; + publish(): void; +} + +export class WebSocketEventQueue implements EventQueue { + private queue: Event[]; + + constructor(websocket: WebSocket) { + this.queue = []; + this.listen_to(websocket); + } + + public peek() { + return this.queue; + } + + public clear() { + this.queue = []; + } + + private listen_to(websocket: WebSocket) { + websocket.onmessage = ({ data }) => { + const [event_type, event_data] = JSON.parse(data); + this.queue.push({ event_type, data: event_data } as Event); + }; + } +} diff --git a/static/src/vector.ts b/static/src/vector.ts index e7be9fa..96bd721 100644 --- a/static/src/vector.ts +++ b/static/src/vector.ts @@ -7,4 +7,8 @@ export class Vec2 { public distance_to(that: Vec2): number { return Math.sqrt((this.x - that.x) ** 2 + (this.y - that.y) ** 2); } + + public copy(): Vec2 { + return new Vec2(this.x, this.y); + } } |
