generate uuids for entities; scaffolding for a server

This commit is contained in:
Elizabeth Hunt 2023-08-15 18:30:19 -06:00
parent 2dc3120831
commit 732fe6f481
Signed by: simponic
GPG Key ID: 52B3774857EB24B1
8 changed files with 176 additions and 61 deletions

View File

@ -7,15 +7,63 @@ import {
Physics,
Input,
Collision,
MessageQueueProvider,
MessagePublisher,
NetworkUpdate,
} from "@engine/systems";
class ClientSocketMessageQueueProvider implements MessageQueueProvider {
private socket: WebSocket;
private messages: any[];
constructor(socket: WebSocket) {
this.socket = socket;
this.messages = [];
this.socket.addEventListener("message", (e) => {
console.log(e);
});
}
getNewMessages() {
return this.messages;
}
clearMessages() {
this.messages = [];
}
}
class ClientSocketMessagePublisher implements MessagePublisher {
private socket: WebSocket;
private messages: any[];
constructor(socket: WebSocket) {
this.socket = socket;
this.messages = [];
this.socket.addEventListener("message", (e) => {
console.log(e);
});
}
addMessage(_message: any) {}
publish() {}
}
export class JumpStorm {
private game: Game;
private socket: WebSocket;
constructor(ctx: CanvasRenderingContext2D) {
this.game = new Game();
this.socket = new WebSocket("ws://localhost:8080");
const socket = new WebSocket("ws://localhost:8080");
const clientSocketMessageQueueProvider =
new ClientSocketMessageQueueProvider(socket);
const clientSocketMessagePublisher = new ClientSocketMessagePublisher(
socket,
);
[
this.createInputSystem(),
@ -23,6 +71,10 @@ export class JumpStorm {
new Physics(),
new Collision(),
new WallBounds(ctx.canvas.width),
new NetworkUpdate(
clientSocketMessageQueueProvider,
clientSocketMessagePublisher,
),
new Render(ctx),
].forEach((system) => this.game.addSystem(system));
@ -49,6 +101,7 @@ export class JumpStorm {
inputSystem.keyPressed(e.key);
}
});
window.addEventListener("keyup", (e) => inputSystem.keyReleased(e.key));
return inputSystem;

View File

@ -7,9 +7,9 @@ export class Game {
private running: boolean;
private lastTimeStamp: number;
public entities: Map<number, Entity>;
public entities: Map<string, Entity>;
public systems: Map<string, System>;
public componentEntities: Map<string, Set<number>>;
public componentEntities: Map<string, Set<string>>;
constructor() {
this.lastTimeStamp = performance.now();
@ -29,11 +29,11 @@ export class Game {
this.entities.set(entity.id, entity);
}
public getEntity(id: number): Entity | undefined {
public getEntity(id: string): Entity | undefined {
return this.entities.get(id);
}
public removeEntity(id: number) {
public removeEntity(id: string) {
this.entities.delete(id);
}
@ -75,7 +75,7 @@ export class Game {
if (!this.componentEntities.has(component.name)) {
this.componentEntities.set(
component.name,
new Set<number>([entity.id]),
new Set<string>([entity.id]),
);
return;
}

View File

@ -1,7 +1,13 @@
import { Component, ComponentNames } from ".";
export class NetworkUpdateable extends Component {
constructor() {
public isPublish: boolean;
public isSubscribe: boolean;
constructor(isPublish: boolean, isSubscribe: boolean) {
super(ComponentNames.NetworkUpdateable);
this.isPublish = isPublish;
this.isSubscribe = isSubscribe;
}
}

View File

@ -1,13 +1,11 @@
import type { Component } from "../components";
export abstract class Entity {
private static ID = 0;
public readonly id: number;
public readonly id: string;
public readonly components: Map<string, Component>;
constructor() {
this.id = Entity.ID++;
this.id = crypto.randomUUID();
this.components = new Map();
}

View File

@ -1,7 +1,7 @@
import type { Coord2D, Dimension2D } from "../interfaces";
interface BoxedEntry {
id: number;
id: string;
dimension: Dimension2D;
center: Coord2D;
}
@ -72,8 +72,8 @@ export class QuadTree {
}
}
public getNeighborIds(boxedEntry: BoxedEntry): number[] {
const neighbors: number[] = this.objects.map(({ id }) => id);
public getNeighborIds(boxedEntry: BoxedEntry): string[] {
const neighbors: string[] = this.objects.map(({ id }) => id);
if (this.hasChildren()) {
this.getQuadrants(boxedEntry).forEach((quadrant) => {
@ -160,11 +160,7 @@ export class QuadTree {
this.objects.forEach((boxedEntry) => {
this.getQuadrants(boxedEntry).forEach((direction) => {
const quadrant = this.children.get(direction);
quadrant?.insert(
boxedEntry.id,
boxedEntry.dimension,
boxedEntry.center,
);
quadrant?.insert(boxedEntry);
});
});

View File

@ -1,10 +1,42 @@
import { System, SystemNames } from ".";
import { Game } from "../Game";
import { ComponentNames, NetworkUpdateable } from "../components";
export interface MessageQueueProvider {
getNewMessages(): any[];
clearMessages(): void;
}
export interface MessagePublisher {
addMessage(message: any): void;
publish(): void;
}
export class NetworkUpdate extends System {
constructor() {
private queueProvider: MessageQueueProvider;
private publisher: MessagePublisher;
constructor(
queueProvider: MessageQueueProvider,
publisher: MessagePublisher,
) {
super(SystemNames.NetworkUpdate);
this.queueProvider = queueProvider;
this.publisher = publisher;
}
public update(_dt: number, _game: Game) {}
public update(_dt: number, game: Game) {
const messages = this.queueProvider.getNewMessages();
this.queueProvider.clearMessages();
game.forEachEntityWithComponent(
ComponentNames.NetworkUpdateable,
(entity) => {
const networkUpdateComponent = entity.getComponent<NetworkUpdateable>(
ComponentNames.NetworkUpdateable,
);
},
);
}
}

View File

@ -1,37 +1,60 @@
import { Game } from "../../engine/Game";
import { Floor, Player } from "../../engine/entities";
import { WallBounds, Physics, Collision } from "../../engine/systems";
import {
WallBounds,
Physics,
Collision,
MessageQueueProvider,
MessagePublisher,
} from "../../engine/systems";
import { Miscellaneous } from "../../engine/config";
const TICK_RATE = 60 / 1000;
const game = new Game();
class Server {
private server: any;
private game: Game;
[
new Physics(),
new Collision({ width: Miscellaneous.WIDTH, height: Miscellaneous.HEIGHT }),
new WallBounds(Miscellaneous.WIDTH),
].forEach((system) => game.addSystem(system));
constructor() {
this.game = new Game();
[new Floor(160), new Player()].forEach((entity) => game.addEntity(entity));
[
new Physics(),
new Collision({
width: Miscellaneous.WIDTH,
height: Miscellaneous.HEIGHT,
}),
new WallBounds(Miscellaneous.WIDTH),
].forEach((system) => this.game.addSystem(system));
game.start();
setInterval(() => {
game.doGameLoop(performance.now());
}, TICK_RATE);
[new Floor(160), new Player()].forEach((entity) =>
this.game.addEntity(entity),
);
const server = Bun.serve<>({
port: 8080,
fetch(req, server) {
server.upgrade(req, {
data: {},
this.game.start();
setInterval(() => {
this.game.doGameLoop(performance.now());
}, TICK_RATE);
this.server = Bun.serve<any>({
websocket: {
open(ws) {
ws.subscribe("the-group-chat");
ws.publish("the-group-chat", msg);
},
message(ws, message) {
// this is a group chat
// so the server re-broadcasts incoming message to everyone
ws.publish("the-group-chat", `${ws.data.username}: ${message}`);
},
close(ws) {
const msg = `${ws.data.username} has left the chat`;
ws.unsubscribe("the-group-chat");
ws.publish("the-group-chat", msg);
},
},
});
},
websocket: {
// handler called when a message is received
async message(ws, message) {
console.log(`Received ${message}`);
},
},
});
console.log(`Listening on localhost:${server.port}`);
}
}
new Server();

View File

@ -1,21 +1,28 @@
{
"compilerOptions": {
"lib": ["ESNext"],
// add Bun type definitions
"types": ["bun-types"],
// enable latest features
"lib": ["esnext"],
"module": "esnext",
"target": "esnext",
// if TS 5.x+
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"noEmit": true,
"types": [
"bun-types" // add Bun global
]
"allowImportingTsExtensions": true,
"moduleDetection": "force",
// if TS 4.x or earlier
"moduleResolution": "nodenext",
"jsx": "react-jsx", // support JSX
"allowJs": true, // allow importing `.js` from `.ts`
"esModuleInterop": true, // allow default imports for CommonJS modules
// best practices
"strict": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
}
}