aboutsummaryrefslogtreecommitdiff
path: root/static
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth@simponic.xyz>2024-09-02 20:10:35 -0700
committerElizabeth Hunt <elizabeth@simponic.xyz>2024-09-02 20:10:35 -0700
commitd35ab6cc851d3fc85f55cfb5676f8dc3552fab24 (patch)
treebef9784e90e2e8f8f5b9e05300238747b775e618 /static
parenta0a2068b66204d7d06eb3c3b4aa61b28469121f6 (diff)
downloadkennel.hatecomputers.club-d35ab6cc851d3fc85f55cfb5676f8dc3552fab24.tar.gz
kennel.hatecomputers.club-d35ab6cc851d3fc85f55cfb5676f8dc3552fab24.zip
a bit more progress
Diffstat (limited to 'static')
-rw-r--r--static/src/event.ts28
-rw-r--r--static/src/main.ts114
-rw-r--r--static/src/mouse_controller.ts42
-rw-r--r--static/src/network.ts73
-rw-r--r--static/src/vector.ts4
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);
+ }
}