WIP: ECS / Network System #1

Draft
simponic wants to merge 13 commits from websockets into main
5 changed files with 57 additions and 23 deletions
Showing only changes of commit 9f5e81327b - Show all commits

View File

@ -101,7 +101,7 @@ class Publishable:
class EventProcessor: class EventProcessor:
@abstractmethod @abstractmethod
def accept(self, entity_manager: EntityManager, event: Event) -> None: def accept(self, entity_manager: EntityManager, event: Event | tuple[Event, str]) -> None:
pass pass

View File

@ -23,7 +23,7 @@ system_manager = SystemManager()
class KennelEventProcessor(EventProcessor): class KennelEventProcessor(EventProcessor):
def accept( def accept(
self, entity_manager: EntityManager, event: type[Event | tuple[Event, str]] self, entity_manager: EntityManager, event: Event | tuple[Event, str]
) -> None: ) -> None:
if isinstance(event, tuple): if isinstance(event, tuple):
client_event, client_id = event client_event, client_id = event

View File

@ -1,16 +1,13 @@
import $ from "jquery"; import $ from "jquery";
import { Vec2 } from "./vector"; import { Vec2 } from "./vector";
import {
EventType,
type SetControllableEvent,
type Event,
type EventPublisher,
} from "./event";
import { MouseController } from "./mouse_controller"; import { MouseController } from "./mouse_controller";
import { import {
EntityPositionUpdateEvent, EntityPositionUpdateEvent,
EventProcessor, EventPublisher,
EventQueue, EventQueue,
EventType,
SetControllableEvent,
WebsocketEventPublisher,
WebSocketEventQueue, WebSocketEventQueue,
} from "./network"; } from "./network";
@ -18,10 +15,11 @@ class KennelClient {
private running: boolean; private running: boolean;
private last_update: number; private last_update: number;
private controllable_entities: Set<string>; private controllable_entities: Set<string> = new Set();
private mouse_controller: MouseController; private mouse_controller: MouseController;
constructor( constructor(
private readonly client_id: string,
private readonly event_queue: EventQueue, private readonly event_queue: EventQueue,
private readonly event_publisher: EventPublisher, private readonly event_publisher: EventPublisher,
) { ) {
@ -39,21 +37,45 @@ class KennelClient {
this.mouse_controller.start(); this.mouse_controller.start();
const loop = (timestamp: number) => { const loop = (timestamp: number) => {
if (!this.running) return;
const dt = timestamp - this.last_update; const dt = timestamp - this.last_update;
this.propogate_state_after(dt); this.propogate_state_after(dt);
requestAnimationFrame(loop); // tail call recursion! /s requestAnimationFrame(loop); // tail call recursion! /s
}; };
requestAnimationFrame(loop); requestAnimationFrame(loop);
$(document).on("mousemove", (event) => {
this.mouse_controller.move(event.clientX, event.clientY);
});
} }
public close() { public close() {
this.running = false; this.running = false;
this.mouse_controller.stop(); this.mouse_controller.stop();
this.controllable_entities.clear(); this.controllable_entities.clear();
$(document).off("mousemove");
} }
private propogate_state_after(dt: number) { private propogate_state_after(dt: number) {
// TODO: interpolate cats and lasers and stuff const events = this.event_queue.peek();
for (const event of events) {
switch (event.event_type) {
case EventType.SET_CONTROLLABLE:
this.process_set_controllable_event(event as SetControllableEvent);
break;
}
}
console.log(events, dt);
this.event_queue.clear();
}
private process_set_controllable_event(event: SetControllableEvent) {
if (event.data.client_id !== this.client_id) {
console.warn("got controllable event for client that is not us");
return;
}
this.controllable_entities.add(event.data.id);
} }
private on_mouse_move(position: Vec2) { private on_mouse_move(position: Vec2) {
@ -62,13 +84,13 @@ class KennelClient {
event_type: EventType.ENTITY_POSITION_UPDATE, event_type: EventType.ENTITY_POSITION_UPDATE,
data: { id, position: { x: position.x, y: position.y } }, data: { id, position: { x: position.x, y: position.y } },
}; };
this.event_publisher.send(event); this.event_publisher.add(event);
} }
} }
} }
$(async () => { $(async () => {
const session_id = await fetch("/assign", { const client_id = await fetch("/assign", {
credentials: "include", credentials: "include",
}) })
.then((res) => res.json()) .then((res) => res.json())
@ -80,14 +102,9 @@ $(async () => {
}); });
const queue: EventQueue = new WebSocketEventQueue(ws); const queue: EventQueue = new WebSocketEventQueue(ws);
const publisher: EventPublisher = new WebsocketEventPublisher(ws);
const kennel_client = new KennelClient(session_id, null); const kennel_client = new KennelClient(client_id, queue, publisher);
ws.onclose = () => kennel_client.close(); ws.onclose = () => kennel_client.close();
const mouse_controller = new MouseController(on_mouse_move); kennel_client.start();
$(document).on("mousemove", (event) => {
mouse_controller.move(event.clientX, event.clientY);
});
mouse_controller.start();
}); });

View File

@ -71,3 +71,20 @@ export class WebSocketEventQueue implements EventQueue {
}; };
} }
} }
export class WebsocketEventPublisher implements EventPublisher {
private queue: Event[];
constructor(private readonly websocket: WebSocket) {
this.queue = [];
}
public add(event: Event) {
this.queue.push(event);
}
public publish() {
this.websocket.send(JSON.stringify(this.queue));
this.queue = [];
}
}

View File

@ -1,7 +1,7 @@
export class Vec2 { export class Vec2 {
constructor( constructor(
private readonly x: number, public readonly x: number,
private readonly y: number, public readonly y: number,
) {} ) {}
public distance_to(that: Vec2): number { public distance_to(that: Vec2): number {