holy fuck we actually got somewhere
This commit is contained in:
parent
d64ffb5016
commit
dec7b614d8
@ -1,4 +1,5 @@
|
||||
import { Game } from "@engine/Game";
|
||||
import { Entity } from "@engine/entities";
|
||||
import { Grid } from "@engine/structures";
|
||||
import {
|
||||
WallBounds,
|
||||
@ -7,67 +8,116 @@ import {
|
||||
Physics,
|
||||
Input,
|
||||
Collision,
|
||||
MessageQueueProvider,
|
||||
MessagePublisher,
|
||||
NetworkUpdate,
|
||||
} from "@engine/systems";
|
||||
import {
|
||||
type MessageQueueProvider,
|
||||
type MessagePublisher,
|
||||
type MessageProcessor,
|
||||
type Message,
|
||||
type EntityAddBody,
|
||||
MessageType,
|
||||
} from "@engine/network";
|
||||
import { stringify, parse } from "@engine/utils";
|
||||
|
||||
class ClientMessageProcessor implements MessageProcessor {
|
||||
private game: Game;
|
||||
|
||||
constructor(game: Game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public process(message: Message) {
|
||||
switch (message.type) {
|
||||
case MessageType.NEW_ENTITY:
|
||||
const entityAddBody = message.body as unknown as EntityAddBody;
|
||||
this.game.addEntity(
|
||||
Entity.from(entityAddBody.entityName, entityAddBody.args),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
class ClientSocketMessageQueueProvider implements MessageQueueProvider {
|
||||
private socket: WebSocket;
|
||||
private messages: any[];
|
||||
private messages: Message[];
|
||||
|
||||
constructor(socket: WebSocket) {
|
||||
this.socket = socket;
|
||||
this.messages = [];
|
||||
|
||||
this.socket.addEventListener("message", (e) => {
|
||||
console.log(e);
|
||||
const message = parse<Message>(e.data);
|
||||
this.messages.push(message);
|
||||
});
|
||||
}
|
||||
|
||||
getNewMessages() {
|
||||
public getNewMessages() {
|
||||
return this.messages;
|
||||
}
|
||||
|
||||
clearMessages() {
|
||||
public clearMessages() {
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
||||
class ClientSocketMessagePublisher implements MessagePublisher {
|
||||
private socket: WebSocket;
|
||||
private messages: any[];
|
||||
private messages: Message[];
|
||||
|
||||
constructor(socket: WebSocket) {
|
||||
this.socket = socket;
|
||||
this.messages = [];
|
||||
|
||||
this.socket.addEventListener("message", (e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
|
||||
addMessage(_message: any) {}
|
||||
public addMessage(message: Message) {
|
||||
this.messages.push(message);
|
||||
}
|
||||
|
||||
publish() {}
|
||||
public publish() {
|
||||
this.messages.forEach((message: Message) =>
|
||||
this.socket.send(stringify(message)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class JumpStorm {
|
||||
private game: Game;
|
||||
private clientId: string;
|
||||
|
||||
constructor(ctx: CanvasRenderingContext2D) {
|
||||
this.game = new Game();
|
||||
constructor(game: Game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
const socket = new WebSocket("ws://localhost:8080");
|
||||
setInterval(() => socket.send(JSON.stringify({ x: 1 })), 1_000);
|
||||
const clientSocketMessageQueueProvider =
|
||||
new ClientSocketMessageQueueProvider(socket);
|
||||
const clientSocketMessagePublisher = new ClientSocketMessagePublisher(
|
||||
socket
|
||||
);
|
||||
public async init(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
httpMethod: string,
|
||||
wsMethod: string,
|
||||
host: string,
|
||||
) {
|
||||
await fetch(`${httpMethod}://${host}/assign`)
|
||||
.then((resp) => {
|
||||
if (resp.ok) {
|
||||
return resp.text();
|
||||
}
|
||||
throw resp;
|
||||
})
|
||||
.then((cookie) => {
|
||||
this.clientId = cookie;
|
||||
});
|
||||
|
||||
const grid = new Grid();
|
||||
|
||||
const socket = new WebSocket(`${wsMethod}://${host}/game`);
|
||||
const clientSocketMessageQueueProvider =
|
||||
new ClientSocketMessageQueueProvider(socket);
|
||||
const clientSocketMessagePublisher = new ClientSocketMessagePublisher(
|
||||
socket,
|
||||
);
|
||||
const clientMessageProcessor = new ClientMessageProcessor(this.game);
|
||||
[
|
||||
this.createInputSystem(),
|
||||
new FacingDirection(),
|
||||
@ -76,7 +126,8 @@ export class JumpStorm {
|
||||
new WallBounds(ctx.canvas.width),
|
||||
new NetworkUpdate(
|
||||
clientSocketMessageQueueProvider,
|
||||
clientSocketMessagePublisher
|
||||
clientSocketMessagePublisher,
|
||||
clientMessageProcessor,
|
||||
),
|
||||
new Render(ctx),
|
||||
].forEach((system) => this.game.addSystem(system));
|
||||
@ -93,7 +144,7 @@ export class JumpStorm {
|
||||
}
|
||||
|
||||
private createInputSystem(): Input {
|
||||
const inputSystem = new Input();
|
||||
const inputSystem = new Input(this.clientId);
|
||||
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (!e.repeat) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { loadAssets } from "@engine/config";
|
||||
import { Game } from "@engine/Game";
|
||||
import { JumpStorm } from "../JumpStorm";
|
||||
|
||||
let canvas: HTMLCanvasElement;
|
||||
@ -9,16 +10,18 @@
|
||||
export let width: number;
|
||||
export let height: number;
|
||||
|
||||
let jumpStorm: JumpStorm;
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
ctx = canvas.getContext("2d");
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
|
||||
loadAssets().then(() => {
|
||||
jumpStorm = new JumpStorm(ctx);
|
||||
jumpStorm.play();
|
||||
});
|
||||
await loadAssets();
|
||||
|
||||
const game = new Game();
|
||||
const jumpStorm = new JumpStorm(game);
|
||||
|
||||
const url = new URL(document.location);
|
||||
await jumpStorm.init(ctx, "http", "ws", url.host + "/api");
|
||||
jumpStorm.play();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"extends": ["@tsconfig/svelte/tsconfig.json", "../tsconfig.engine.json"],
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
@ -24,8 +24,5 @@
|
||||
"src/**/*.js",
|
||||
"src/**/*.svelte"
|
||||
],
|
||||
"paths": {
|
||||
"@engine/*": ["../engine/*"]
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
@ -4,6 +4,16 @@ import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
server: {
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8080",
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ""),
|
||||
},
|
||||
},
|
||||
},
|
||||
cors: true,
|
||||
plugins: [svelte()],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
@ -60,7 +60,7 @@ export class Game {
|
||||
return this.systems.get(name);
|
||||
}
|
||||
|
||||
public doGameLoop = (timeStamp: number) => {
|
||||
public doGameLoop(timeStamp: number) {
|
||||
if (!this.running) {
|
||||
return;
|
||||
}
|
||||
@ -86,5 +86,5 @@ export class Game {
|
||||
this.systemOrder.forEach((systemName) => {
|
||||
this.systems.get(systemName)?.update(dt, this);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,15 @@ import { Component, ComponentNames, Velocity } from ".";
|
||||
|
||||
export class Control extends Component {
|
||||
public controlVelocityComponent: Velocity;
|
||||
public controllableBy: string;
|
||||
|
||||
constructor(
|
||||
controllableBy: string,
|
||||
controlVelocityComponent: Velocity = new Velocity(),
|
||||
controllableBy: string
|
||||
) {
|
||||
super(ComponentNames.Control);
|
||||
|
||||
this.controllableBy = controllableBy;
|
||||
this.controlVelocityComponent = controlVelocityComponent;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export namespace KeyConstants {
|
||||
|
||||
// value -> [key] from KeyActions
|
||||
export const ActionKeys: Map<Action, string[]> = Object.keys(
|
||||
KeyActions
|
||||
KeyActions,
|
||||
).reduce((acc: Map<Action, string[]>, key) => {
|
||||
const action = KeyActions[key];
|
||||
|
||||
@ -42,6 +42,4 @@ export namespace Miscellaneous {
|
||||
|
||||
export const DEFAULT_GRID_WIDTH = 30;
|
||||
export const DEFAULT_GRID_HEIGHT = 30;
|
||||
|
||||
export const SERVER_TICK_RATE = 5 / 100;
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { EntityNames, Player } from ".";
|
||||
import type { Component } from "../components";
|
||||
|
||||
export abstract class Entity {
|
||||
public readonly id: string;
|
||||
public readonly components: Map<string, Component>;
|
||||
public id: string;
|
||||
public components: Map<string, Component>;
|
||||
public name: string;
|
||||
|
||||
constructor(id: string = crypto.randomUUID()) {
|
||||
constructor(name: string, id: string = crypto.randomUUID()) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.components = new Map();
|
||||
}
|
||||
@ -27,4 +30,13 @@ export abstract class Entity {
|
||||
public hasComponent(name: string): boolean {
|
||||
return this.components.has(name);
|
||||
}
|
||||
|
||||
static from(entityName: string, args: any): Entity {
|
||||
switch (entityName) {
|
||||
case EntityNames.Player:
|
||||
return new Player(args.playerId);
|
||||
default:
|
||||
throw new Error(".from() Entity type not implemented: " + entityName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from "../config";
|
||||
import { BoundingBox, Sprite } from "../components";
|
||||
import { TopCollidable } from "../components/TopCollidable";
|
||||
import { Entity } from "../entities";
|
||||
import { Entity, EntityNames } from "../entities";
|
||||
|
||||
export class Floor extends Entity {
|
||||
private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(
|
||||
@ -9,7 +9,7 @@ export class Floor extends Entity {
|
||||
) as SpriteSpec;
|
||||
|
||||
constructor(width: number) {
|
||||
super();
|
||||
super(EntityNames.Floor);
|
||||
|
||||
this.addComponent(
|
||||
new Sprite(
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Entity } from ".";
|
||||
import { Entity, EntityNames } from ".";
|
||||
import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from "../config";
|
||||
import {
|
||||
Jump,
|
||||
@ -21,11 +21,11 @@ export class Player extends Entity {
|
||||
private static MOI: number = 100;
|
||||
|
||||
private static spriteSpec: SpriteSpec = SPRITE_SPECS.get(
|
||||
Sprites.COFFEE
|
||||
Sprites.COFFEE,
|
||||
) as SpriteSpec;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
constructor(playerId: string) {
|
||||
super(EntityNames.Player);
|
||||
|
||||
this.addComponent(
|
||||
new BoundingBox(
|
||||
@ -34,12 +34,12 @@ export class Player extends Entity {
|
||||
y: 100,
|
||||
},
|
||||
{ width: Player.spriteSpec.width, height: Player.spriteSpec.height },
|
||||
0
|
||||
)
|
||||
0,
|
||||
),
|
||||
);
|
||||
|
||||
this.addComponent(
|
||||
new Velocity({ dCartesian: { dx: 0, dy: 0 }, dTheta: 0 })
|
||||
new Velocity({ dCartesian: { dx: 0, dy: 0 }, dTheta: 0 }),
|
||||
);
|
||||
|
||||
this.addComponent(new Mass(Player.MASS));
|
||||
@ -48,7 +48,7 @@ export class Player extends Entity {
|
||||
this.addComponent(new Gravity());
|
||||
|
||||
this.addComponent(new Jump());
|
||||
this.addComponent(new Control());
|
||||
this.addComponent(new Control(playerId));
|
||||
|
||||
this.addComponent(new Collide());
|
||||
this.addComponent(new WallBounded());
|
||||
@ -64,8 +64,8 @@ export class Player extends Entity {
|
||||
{ x: 0, y: 0 },
|
||||
{ width: Player.spriteSpec.width, height: Player.spriteSpec.height },
|
||||
Player.spriteSpec.msPerFrame,
|
||||
Player.spriteSpec.frames
|
||||
)
|
||||
Player.spriteSpec.frames,
|
||||
),
|
||||
);
|
||||
|
||||
this.addComponent(new FacingDirection(leftSprite, rightSprite));
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from "./Entity";
|
||||
export * from "./Floor";
|
||||
export * from "./Player";
|
||||
export * from "./Entity";
|
||||
export * from "./Floor";
|
||||
export * from "./Player";
|
||||
export * from "./names";
|
||||
|
4
engine/entities/names.ts
Normal file
4
engine/entities/names.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export namespace EntityNames {
|
||||
export const Player = "Player";
|
||||
export const Floor = "Floor";
|
||||
}
|
29
engine/network/index.ts
Normal file
29
engine/network/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export enum MessageType {
|
||||
NEW_ENTITY = "NEW_ENTITY",
|
||||
REMOVE_ENTITY = "REMOVE_ENTITY",
|
||||
UPDATE_ENTITY = "UPDATE_ENTITY",
|
||||
}
|
||||
|
||||
export type EntityAddBody = {
|
||||
entityName: string;
|
||||
args: any;
|
||||
};
|
||||
|
||||
export type Message = {
|
||||
type: MessageType;
|
||||
body: any;
|
||||
};
|
||||
|
||||
export interface MessageQueueProvider {
|
||||
getNewMessages(): Message[];
|
||||
clearMessages(): void;
|
||||
}
|
||||
|
||||
export interface MessagePublisher {
|
||||
addMessage(message: Message): void;
|
||||
publish(): void;
|
||||
}
|
||||
|
||||
export interface MessageProcessor {
|
||||
process(message: Message): void;
|
||||
}
|
@ -12,12 +12,14 @@ import { Action } from "../interfaces";
|
||||
import { System, SystemNames } from ".";
|
||||
|
||||
export class Input extends System {
|
||||
public clientId: string;
|
||||
private keys: Set<string>;
|
||||
private actionTimeStamps: Map<Action, number>;
|
||||
|
||||
constructor() {
|
||||
constructor(clientId: string) {
|
||||
super(SystemNames.Input);
|
||||
|
||||
this.clientId = clientId;
|
||||
this.keys = new Set<string>();
|
||||
this.actionTimeStamps = new Map<Action, number>();
|
||||
}
|
||||
@ -42,6 +44,7 @@ export class Input extends System {
|
||||
const controlComponent = entity.getComponent<Control>(
|
||||
ComponentNames.Control,
|
||||
);
|
||||
if (controlComponent.controllableBy != this.clientId) return;
|
||||
|
||||
if (this.hasSomeKey(KeyConstants.ActionKeys.get(Action.MOVE_RIGHT))) {
|
||||
controlComponent.controlVelocityComponent.velocity.dCartesian.dx +=
|
||||
|
@ -1,43 +1,44 @@
|
||||
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;
|
||||
}
|
||||
import {
|
||||
type MessageQueueProvider,
|
||||
type MessagePublisher,
|
||||
type MessageProcessor,
|
||||
} from "../network";
|
||||
|
||||
export class NetworkUpdate extends System {
|
||||
private queueProvider: MessageQueueProvider;
|
||||
private publisher: MessagePublisher;
|
||||
private messageProcessor: MessageProcessor;
|
||||
|
||||
constructor(
|
||||
queueProvider: MessageQueueProvider,
|
||||
publisher: MessagePublisher
|
||||
publisher: MessagePublisher,
|
||||
messageProcessor: MessageProcessor,
|
||||
) {
|
||||
super(SystemNames.NetworkUpdate);
|
||||
|
||||
this.queueProvider = queueProvider;
|
||||
this.publisher = publisher;
|
||||
this.messageProcessor = messageProcessor;
|
||||
}
|
||||
|
||||
public update(_dt: number, game: Game) {
|
||||
const messages = this.queueProvider.getNewMessages();
|
||||
if (messages.length) console.log(messages);
|
||||
this.queueProvider
|
||||
.getNewMessages()
|
||||
.forEach((message) => this.messageProcessor.process(message));
|
||||
this.queueProvider.clearMessages();
|
||||
|
||||
game.forEachEntityWithComponent(
|
||||
ComponentNames.NetworkUpdateable,
|
||||
(entity) => {
|
||||
const networkUpdateComponent = entity.getComponent<NetworkUpdateable>(
|
||||
ComponentNames.NetworkUpdateable
|
||||
ComponentNames.NetworkUpdateable,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.publisher.publish();
|
||||
}
|
||||
}
|
||||
|
27
engine/utils/coding.ts
Normal file
27
engine/utils/coding.ts
Normal file
@ -0,0 +1,27 @@
|
||||
const replacer = (_key: any, value: any) => {
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
dataType: "Map",
|
||||
value: Array.from(value.entries()),
|
||||
};
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
const reviver = (_key: any, value: any) => {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (value.dataType === "Map") {
|
||||
return new Map(value.value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
export const stringify = (obj: any) => {
|
||||
return JSON.stringify(obj, replacer);
|
||||
};
|
||||
|
||||
export const parse = <T>(str: string) => {
|
||||
return JSON.parse(str, reviver) as unknown as T;
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
export * from "./rotateVector";
|
||||
export * from "./dotProduct";
|
||||
export * from "./clamp";
|
||||
export * from "./coding";
|
||||
|
@ -1,109 +1,179 @@
|
||||
import { Game } from "@engine/Game";
|
||||
import { Floor, Player } from "@engine/entities";
|
||||
import { EntityNames, Player } from "@engine/entities";
|
||||
import { WallBounds, Physics, Collision, NetworkUpdate } from "@engine/systems";
|
||||
import {
|
||||
WallBounds,
|
||||
Physics,
|
||||
Collision,
|
||||
NetworkUpdate,
|
||||
MessageQueueProvider,
|
||||
MessagePublisher,
|
||||
} from "@engine/systems";
|
||||
type MessageQueueProvider,
|
||||
type MessagePublisher,
|
||||
MessageType,
|
||||
type MessageProcessor,
|
||||
type Message,
|
||||
} from "@engine/network";
|
||||
import { stringify, parse } from "@engine/utils";
|
||||
import { Grid } from "@engine/structures";
|
||||
import { Miscellaneous } from "@engine/config";
|
||||
import { Server } from "bun";
|
||||
|
||||
const SERVER_PORT = 8080;
|
||||
const SERVER_TICK_RATE = (1 / 100) * 1000;
|
||||
const GAME_TOPIC = "game";
|
||||
|
||||
type SessionData = { sessionId: string };
|
||||
|
||||
interface ServerMessage extends Message {
|
||||
sessionData: SessionData;
|
||||
}
|
||||
|
||||
class ServerSocketMessageReceiver implements MessageQueueProvider {
|
||||
private messages: any[];
|
||||
private messages: ServerMessage[];
|
||||
|
||||
constructor() {
|
||||
this.messages = [];
|
||||
}
|
||||
|
||||
addMessage(message: any) {
|
||||
public addMessage(message: ServerMessage) {
|
||||
this.messages.push(message);
|
||||
}
|
||||
|
||||
getNewMessages() {
|
||||
public getNewMessages() {
|
||||
return this.messages;
|
||||
}
|
||||
|
||||
clearMessages() {
|
||||
public clearMessages() {
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
||||
class ServerSocketMessagePublisher implements MessagePublisher {
|
||||
private server: Server;
|
||||
private messages: any[];
|
||||
class ServerMessageProcessor implements MessageProcessor {
|
||||
constructor() {}
|
||||
|
||||
public process(_message: ServerMessage) {}
|
||||
}
|
||||
|
||||
class ServerSocketMessagePublisher implements MessagePublisher {
|
||||
private server?: Server;
|
||||
private messages: Message[];
|
||||
|
||||
constructor(server?: Server) {
|
||||
if (server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
constructor(server: Server) {
|
||||
this.server = server;
|
||||
this.messages = [];
|
||||
}
|
||||
|
||||
addMessage(_message: any) {}
|
||||
public setServer(server: Server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
publish() {}
|
||||
public addMessage(message: Message) {
|
||||
this.messages.push(message);
|
||||
}
|
||||
|
||||
public publish() {
|
||||
this.messages.forEach(
|
||||
(message) => this.server?.publish(GAME_TOPIC, stringify(message)),
|
||||
);
|
||||
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
||||
const game = new Game();
|
||||
|
||||
const messageReceiver = new ServerSocketMessageReceiver();
|
||||
const messagePublisher = new ServerSocketMessagePublisher();
|
||||
const messageProcessor = new ServerMessageProcessor();
|
||||
const sessionControllableEntities: Map<string, Set<string>> = new Map();
|
||||
|
||||
const server = Bun.serve<{ sessionId: string }>({
|
||||
port: 8080,
|
||||
fetch: async (req, server): Promise<string> => {
|
||||
const sessionId = crypto.randomUUID();
|
||||
const server = Bun.serve<SessionData>({
|
||||
port: SERVER_PORT,
|
||||
fetch: async (req, server): Promise<Response> => {
|
||||
const url = new URL(req.url);
|
||||
|
||||
server.upgrade(req, {
|
||||
headers: {
|
||||
"Set-Cookie": `SessionId=${sessionId}`,
|
||||
},
|
||||
data: {
|
||||
sessionId,
|
||||
},
|
||||
});
|
||||
const headers = new Headers();
|
||||
headers.set("Access-Control-Allow-Origin", "*");
|
||||
|
||||
return sessionId;
|
||||
if (url.pathname == "/assign") {
|
||||
const sessionId = crypto.randomUUID();
|
||||
headers.set("Set-Cookie", `SessionId=${sessionId};`);
|
||||
|
||||
return new Response(sessionId, { headers });
|
||||
}
|
||||
|
||||
const cookie = req.headers.get("cookie");
|
||||
if (!cookie) {
|
||||
return new Response("No session", { headers, status: 401 });
|
||||
}
|
||||
|
||||
const sessionId = cookie.split(";").at(0)!.split("SessionId=").at(1);
|
||||
|
||||
if (url.pathname == "/game") {
|
||||
headers.set(
|
||||
"Set-Cookie",
|
||||
`SessionId=${sessionId}; HttpOnly; SameSite=Strict;`,
|
||||
);
|
||||
server.upgrade(req, {
|
||||
headers,
|
||||
data: {
|
||||
sessionId,
|
||||
},
|
||||
});
|
||||
|
||||
return new Response("upgraded", { headers });
|
||||
}
|
||||
if (url.pathname == "/me") {
|
||||
return new Response(sessionId, { headers });
|
||||
}
|
||||
|
||||
return new Response("Not found", { headers, status: 404 });
|
||||
},
|
||||
websocket: {
|
||||
open(ws) {
|
||||
const { sessionId } = ws.data;
|
||||
|
||||
if (sessionControllableEntities.has(sessionId)) {
|
||||
// no need to add player
|
||||
return;
|
||||
}
|
||||
|
||||
const player = new Player();
|
||||
const player = new Player(sessionId);
|
||||
game.addEntity(player);
|
||||
|
||||
sessionControllableEntities.set(sessionId, new Set(player.id));
|
||||
|
||||
messagePublisher.addMessage({
|
||||
type: MessageType.NEW_ENTITY,
|
||||
body: {
|
||||
entityName: EntityNames.Player,
|
||||
args: { playerId: sessionId },
|
||||
},
|
||||
});
|
||||
|
||||
ws.subscribe(GAME_TOPIC);
|
||||
},
|
||||
message(ws, message) {
|
||||
console.log(JSON.parse(message));
|
||||
messageReceiver.addMessage(message);
|
||||
if (typeof message == "string") {
|
||||
const receivedMessage = parse<ServerMessage>(message);
|
||||
receivedMessage.sessionData = ws.data;
|
||||
|
||||
messageReceiver.addMessage(receivedMessage);
|
||||
}
|
||||
},
|
||||
close(ws) {},
|
||||
close(_ws) {},
|
||||
},
|
||||
});
|
||||
|
||||
const messagePublisher = new ServerSocketMessagePublisher(server);
|
||||
messagePublisher.setServer(server);
|
||||
|
||||
[
|
||||
new Physics(),
|
||||
new Collision(new Grid()),
|
||||
new WallBounds(Miscellaneous.WIDTH),
|
||||
new NetworkUpdate(messageReceiver, messagePublisher),
|
||||
new NetworkUpdate(messageReceiver, messagePublisher, messageProcessor),
|
||||
].forEach((system) => game.addSystem(system));
|
||||
|
||||
[new Floor(160), new Player()].forEach((entity) => game.addEntity(entity));
|
||||
|
||||
game.start();
|
||||
|
||||
setInterval(() => {
|
||||
game.doGameLoop(performance.now());
|
||||
}, Miscellaneous.SERVER_TICK_RATE);
|
||||
|
||||
const sessionControllableEntities: Map<string, Set<string>> = new Map();
|
||||
}, SERVER_TICK_RATE);
|
||||
|
||||
console.log(`Listening on ${server.hostname}:${server.port}`);
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"extends": "../tsconfig.engine.json",
|
||||
"compilerOptions": {
|
||||
// add Bun type definitions
|
||||
"types": ["bun-types"],
|
||||
@ -21,18 +22,6 @@
|
||||
// best practices
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
// engine path
|
||||
"paths": {
|
||||
"@engine/*": ["../engine/*"],
|
||||
"@engine/components": ["../engine/components"],
|
||||
"@engine/config": ["../engine/config"],
|
||||
"@engine/entities": ["../engine/entities"],
|
||||
"@engine/interfaces": ["../engine/interfaces"],
|
||||
"@engine/structures": ["../engine/structures"],
|
||||
"@engine/systems": ["../engine/systems"],
|
||||
"@engine/utils": ["../engine/utils"],
|
||||
}
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
|
15
tsconfig.engine.json
Normal file
15
tsconfig.engine.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@engine/*": ["./engine/*"],
|
||||
"@engine/components": ["./engine/components"],
|
||||
"@engine/config": ["./engine/config"],
|
||||
"@engine/entities": ["./engine/entities"],
|
||||
"@engine/interfaces": ["./engine/interfaces"],
|
||||
"@engine/structures": ["./engine/structures"],
|
||||
"@engine/systems": ["./engine/systems"],
|
||||
"@engine/utils": ["./engine/utils"],
|
||||
"@engine/network": ["./engine/network"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user