diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..32ebab4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "none" +} diff --git a/client/.eslintrc.js b/client/.eslintrc.js index f200fbf..a9b1e2d 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -1,11 +1,11 @@ module.exports = { extends: [ // add more generic rule sets here, such as: - "eslint:recommended", - "plugin:svelte/recommended", + 'eslint:recommended', + 'plugin:svelte/recommended' ], rules: { // override/add rules settings here, such as: // 'svelte/rule-name': 'error' - }, + } }; diff --git a/client/index.html b/client/index.html index 00b94e7..892a3af 100644 --- a/client/index.html +++ b/client/index.html @@ -1,4 +1,4 @@ - + diff --git a/client/public/css/colors.css b/client/public/css/colors.css index 067ddcd..6108028 100644 --- a/client/public/css/colors.css +++ b/client/public/css/colors.css @@ -10,7 +10,7 @@ --orange: #af3a03; } -[data-theme="dark"] { +[data-theme='dark'] { --bg: #282828; --text: #f9f5d7; --red: #fb4934; diff --git a/client/public/css/style.css b/client/public/css/style.css index cdfef76..4dfe605 100644 --- a/client/public/css/style.css +++ b/client/public/css/style.css @@ -1,15 +1,15 @@ -@import url("./theme.css"); -@import url("./tf.css"); +@import url('./theme.css'); +@import url('./tf.css'); @font-face { - font-family: "scientifica"; - src: url("/fonts/scientifica.ttf"); + font-family: 'scientifica'; + src: url('/fonts/scientifica.ttf'); } * { padding: 0; margin: 0; - font-family: "scientifica", monospace; + font-family: 'scientifica', monospace; transition: background 0.2s ease-in-out; font-smooth: never; } diff --git a/client/public/css/tf.css b/client/public/css/tf.css index c1acd72..855fe0d 100644 --- a/client/public/css/tf.css +++ b/client/public/css/tf.css @@ -17,7 +17,7 @@ rgba(162, 254, 254, 1) 100% ); - content: ""; + content: ''; width: 100%; height: 100%; top: 0; diff --git a/client/public/css/theme.css b/client/public/css/theme.css index c65b2a8..eeb15ee 100644 --- a/client/public/css/theme.css +++ b/client/public/css/theme.css @@ -1,4 +1,4 @@ -@import url("./colors.css"); +@import url('./colors.css'); .primary { color: var(--aqua); diff --git a/client/src/JumpStorm.ts b/client/src/JumpStorm.ts index 01cc8d8..92bddcf 100644 --- a/client/src/JumpStorm.ts +++ b/client/src/JumpStorm.ts @@ -1,6 +1,6 @@ -import { Game } from "@engine/Game"; -import { Entity } from "@engine/entities"; -import { Grid } from "@engine/structures"; +import { Game } from '@engine/Game'; +import { Entity, Floor } from '@engine/entities'; +import { Grid } from '@engine/structures'; import { WallBounds, FacingDirection, @@ -8,17 +8,19 @@ import { Physics, Input, Collision, - NetworkUpdate, -} from "@engine/systems"; + 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"; + MessageType +} from '@engine/network'; +import { stringify, parse } from '@engine/utils'; +import { BoundingBox, Sprite } from '@engine/components'; +import { Miscellaneous } from '@engine/config'; class ClientMessageProcessor implements MessageProcessor { private game: Game; @@ -29,14 +31,19 @@ class ClientMessageProcessor implements MessageProcessor { 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), + case MessageType.NEW_ENTITIES: + const entityAdditions = message.body as unknown as EntityAddBody[]; + entityAdditions.forEach((addBody) => + this.game.addEntity(Entity.from(addBody.entityName, addBody.args)) ); break; + case MessageType.REMOVE_ENTITIES: + const ids = message.body as unknown as string[]; + ids.forEach((id) => this.game.removeEntity(id)); + break; + default: + break; } - console.log(message); } } @@ -49,9 +56,9 @@ class ClientSocketMessageQueueProvider implements MessageQueueProvider { this.socket = socket; this.messages = []; - this.socket.addEventListener("message", (e) => { - const message = parse(e.data); - this.messages.push(message); + this.socket.addEventListener('message', (e) => { + const messages = parse(e.data); + this.messages = this.messages.concat(messages); }); } @@ -79,7 +86,7 @@ class ClientSocketMessagePublisher implements MessagePublisher { public publish() { this.messages.forEach((message: Message) => - this.socket.send(stringify(message)), + this.socket.send(stringify(message)) ); } } @@ -96,7 +103,7 @@ export class JumpStorm { ctx: CanvasRenderingContext2D, httpMethod: string, wsMethod: string, - host: string, + host: string ) { await fetch(`${httpMethod}://${host}/assign`) .then((resp) => { @@ -115,7 +122,7 @@ export class JumpStorm { const clientSocketMessageQueueProvider = new ClientSocketMessageQueueProvider(socket); const clientSocketMessagePublisher = new ClientSocketMessagePublisher( - socket, + socket ); const clientMessageProcessor = new ClientMessageProcessor(this.game); [ @@ -123,14 +130,28 @@ export class JumpStorm { new FacingDirection(), new Physics(), new Collision(grid), - new WallBounds(ctx.canvas.width), + new WallBounds(), new NetworkUpdate( clientSocketMessageQueueProvider, clientSocketMessagePublisher, - clientMessageProcessor, + clientMessageProcessor ), - new Render(ctx), + new Render(ctx) ].forEach((system) => this.game.addSystem(system)); + + const floor = new Floor(160); + const floorHeight = 40; + + floor.addComponent( + new BoundingBox( + { + x: Miscellaneous.WIDTH / 2, + y: Miscellaneous.HEIGHT - floorHeight / 2 + }, + { width: Miscellaneous.WIDTH, height: floorHeight } + ) + ); + this.game.addEntity(floor); } public play() { @@ -146,13 +167,13 @@ export class JumpStorm { private createInputSystem(): Input { const inputSystem = new Input(this.clientId); - window.addEventListener("keydown", (e) => { + window.addEventListener('keydown', (e) => { if (!e.repeat) { inputSystem.keyPressed(e.key); } }); - window.addEventListener("keyup", (e) => inputSystem.keyReleased(e.key)); + window.addEventListener('keyup', (e) => inputSystem.keyReleased(e.key)); return inputSystem; } diff --git a/client/src/components/GameCanvas.svelte b/client/src/components/GameCanvas.svelte index ed16f33..ea7dd15 100644 --- a/client/src/components/GameCanvas.svelte +++ b/client/src/components/GameCanvas.svelte @@ -3,7 +3,7 @@ import { loadAssets } from "@engine/config"; import { Game } from "@engine/Game"; import { JumpStorm } from "../JumpStorm"; - + let canvas: HTMLCanvasElement; let ctx: CanvasRenderingContext2D; @@ -19,8 +19,7 @@ const game = new Game(); const jumpStorm = new JumpStorm(game); - const url = new URL(document.location); - await jumpStorm.init(ctx, "http", "ws", url.host + "/api"); + await jumpStorm.init(ctx, "http", "ws", document.location.host + "/api"); jumpStorm.play(); }); diff --git a/client/src/components/LeaderBoard.svelte b/client/src/components/LeaderBoard.svelte index 8343c56..2f3e411 100644 --- a/client/src/components/LeaderBoard.svelte +++ b/client/src/components/LeaderBoard.svelte @@ -3,7 +3,7 @@ const MAX_ENTRIES = 8; - export let entries: { name: string, score: number }[] = []; + export let entries: { name: string; score: number }[] = [];
diff --git a/client/src/main.ts b/client/src/main.ts index 5332616..aa7431f 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -1,7 +1,7 @@ -import App from "./App.svelte"; +import App from './App.svelte'; const app = new App({ - target: document.getElementById("app"), + target: document.getElementById('app') }); export default app; diff --git a/client/src/routes/Home.svelte b/client/src/routes/Home.svelte index 9ada10e..71ad324 100644 --- a/client/src/routes/Home.svelte +++ b/client/src/routes/Home.svelte @@ -3,10 +3,9 @@ import LeaderBoard from "../components/LeaderBoard.svelte"; import { Miscellaneous } from "@engine/config"; - + let width: number = Miscellaneous.WIDTH; let height: number = Miscellaneous.HEIGHT; -
diff --git a/client/svelte.config.js b/client/svelte.config.js index b0683fd..db735be 100644 --- a/client/svelte.config.js +++ b/client/svelte.config.js @@ -1,7 +1,7 @@ -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; export default { // Consult https://svelte.dev/docs#compile-time-svelte-preprocess // for more information about preprocessors - preprocess: vitePreprocess(), -} + preprocess: vitePreprocess() +}; diff --git a/client/vite.config.ts b/client/vite.config.ts index cdf1ab1..d8b999c 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -1,23 +1,23 @@ -import { defineConfig } from "vite"; -import { svelte } from "@sveltejs/vite-plugin-svelte"; -import { fileURLToPath, URL } from "node:url"; +import { defineConfig } from 'vite'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; +import { fileURLToPath, URL } from 'node:url'; // https://vitejs.dev/config/ export default defineConfig({ server: { proxy: { - "/api": { - target: "http://localhost:8080", + '/api': { + target: 'http://localhost:8080', ws: true, - rewrite: (path) => path.replace(/^\/api/, ""), - }, - }, + rewrite: (path) => path.replace(/^\/api/, '') + } + } }, cors: true, plugins: [svelte()], resolve: { alias: { - "@engine": fileURLToPath(new URL("../engine", import.meta.url)), - }, - }, + '@engine': fileURLToPath(new URL('../engine', import.meta.url)) + } + } }); diff --git a/engine/Game.ts b/engine/Game.ts index 301c8df..cdd3507 100644 --- a/engine/Game.ts +++ b/engine/Game.ts @@ -1,5 +1,5 @@ -import { Entity } from "./entities"; -import { System } from "./systems"; +import { Entity } from './entities'; +import { System } from './systems'; export class Game { private systemOrder: string[]; @@ -39,7 +39,7 @@ export class Game { public forEachEntityWithComponent( componentName: string, - callback: (entity: Entity) => void, + callback: (entity: Entity) => void ) { this.componentEntities.get(componentName)?.forEach((entityId) => { const entity = this.getEntity(entityId); @@ -75,12 +75,12 @@ export class Game { if (!this.componentEntities.has(component.name)) { this.componentEntities.set( component.name, - new Set([entity.id]), + new Set([entity.id]) ); return; } this.componentEntities.get(component.name)?.add(entity.id); - }), + }) ); this.systemOrder.forEach((systemName) => { diff --git a/engine/components/BoundingBox.ts b/engine/components/BoundingBox.ts index 26b404d..dbe083e 100644 --- a/engine/components/BoundingBox.ts +++ b/engine/components/BoundingBox.ts @@ -1,6 +1,6 @@ -import { Component, ComponentNames } from "."; -import type { Coord2D, Dimension2D } from "../interfaces"; -import { dotProduct, rotateVector } from "../utils"; +import { Component, ComponentNames } from '.'; +import type { Coord2D, Dimension2D } from '../interfaces'; +import { dotProduct, rotateVector } from '../utils'; export class BoundingBox extends Component { public center: Coord2D; @@ -48,8 +48,8 @@ export class BoundingBox extends Component { const projection = dotProduct(normal, vertex); return [Math.min(min, projection), Math.max(max, projection)]; }, - [Infinity, -Infinity], - ), + [Infinity, -Infinity] + ) ); if (maxThis < minBox || maxBox < minThis) return false; @@ -64,14 +64,14 @@ export class BoundingBox extends Component { { x: -this.dimension.width / 2, y: -this.dimension.height / 2 }, { x: -this.dimension.width / 2, y: this.dimension.height / 2 }, { x: this.dimension.width / 2, y: this.dimension.height / 2 }, - { x: this.dimension.width / 2, y: -this.dimension.height / 2 }, + { x: this.dimension.width / 2, y: -this.dimension.height / 2 } ] .map((vertex) => rotateVector(vertex, this.rotation)) // rotate .map((vertex) => { // translate return { x: vertex.x + this.center.x, - y: vertex.y + this.center.y, + y: vertex.y + this.center.y }; }); } @@ -92,28 +92,28 @@ export class BoundingBox extends Component { if (rads <= Math.PI / 2) { return { width: Math.abs(height * Math.sin(rads) + width * Math.cos(rads)), - height: Math.abs(width * Math.sin(rads) + height * Math.cos(rads)), + height: Math.abs(width * Math.sin(rads) + height * Math.cos(rads)) }; } rads -= Math.PI / 2; return { width: Math.abs(height * Math.cos(rads) + width * Math.sin(rads)), - height: Math.abs(width * Math.cos(rads) + height * Math.sin(rads)), + height: Math.abs(width * Math.cos(rads) + height * Math.sin(rads)) }; } public getTopLeft(): Coord2D { return { x: this.center.x - this.dimension.width / 2, - y: this.center.y - this.dimension.height / 2, + y: this.center.y - this.dimension.height / 2 }; } public getBottomRight(): Coord2D { return { x: this.center.x + this.dimension.width / 2, - y: this.center.y + this.dimension.height / 2, + y: this.center.y + this.dimension.height / 2 }; } } diff --git a/engine/components/Collide.ts b/engine/components/Collide.ts index 889ecf8..ed72b92 100644 --- a/engine/components/Collide.ts +++ b/engine/components/Collide.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames } from "."; +import { Component, ComponentNames } from '.'; export class Collide extends Component { constructor() { diff --git a/engine/components/Control.ts b/engine/components/Control.ts index a8dae59..beec82c 100644 --- a/engine/components/Control.ts +++ b/engine/components/Control.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames, Velocity } from "."; +import { Component, ComponentNames, Velocity } from '.'; export class Control extends Component { public controlVelocityComponent: Velocity; @@ -6,7 +6,7 @@ export class Control extends Component { constructor( controllableBy: string, - controlVelocityComponent: Velocity = new Velocity(), + controlVelocityComponent: Velocity = new Velocity() ) { super(ComponentNames.Control); diff --git a/engine/components/FacingDirection.ts b/engine/components/FacingDirection.ts index 1c701a3..8c2a9d2 100644 --- a/engine/components/FacingDirection.ts +++ b/engine/components/FacingDirection.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames, Sprite } from "."; +import { Component, ComponentNames, Sprite } from '.'; export class FacingDirection extends Component { public readonly facingLeftSprite: Sprite; diff --git a/engine/components/Forces.ts b/engine/components/Forces.ts index 91ae1c1..e397985 100644 --- a/engine/components/Forces.ts +++ b/engine/components/Forces.ts @@ -1,6 +1,6 @@ -import type { Force2D } from "../interfaces"; -import { Component } from "./Component"; -import { ComponentNames } from "."; +import type { Force2D } from '../interfaces'; +import { Component } from './Component'; +import { ComponentNames } from '.'; /** * A list of forces and torque, (in newtons, and newton-meters respectively) diff --git a/engine/components/Gravity.ts b/engine/components/Gravity.ts index 89fcb67..dd6dd2e 100644 --- a/engine/components/Gravity.ts +++ b/engine/components/Gravity.ts @@ -1,7 +1,7 @@ -import { ComponentNames, Component } from "."; +import { ComponentNames, Component } from '.'; export class Gravity extends Component { - private static DEFAULT_TERMINAL_VELOCITY = 5; + private static DEFAULT_TERMINAL_VELOCITY = 4.5; public terminalVelocity: number; diff --git a/engine/components/Jump.ts b/engine/components/Jump.ts index 0b40767..6cbfb08 100644 --- a/engine/components/Jump.ts +++ b/engine/components/Jump.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames } from "."; +import { Component, ComponentNames } from '.'; export class Jump extends Component { public canJump: boolean; diff --git a/engine/components/Mass.ts b/engine/components/Mass.ts index daa2d71..a7f98fd 100644 --- a/engine/components/Mass.ts +++ b/engine/components/Mass.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames } from "."; +import { Component, ComponentNames } from '.'; export class Mass extends Component { public mass: number; diff --git a/engine/components/Moment.ts b/engine/components/Moment.ts index 3d0dd2f..cd76294 100644 --- a/engine/components/Moment.ts +++ b/engine/components/Moment.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames } from "."; +import { Component, ComponentNames } from '.'; export class Moment extends Component { public inertia: number; diff --git a/engine/components/NetworkUpdateable.ts b/engine/components/NetworkUpdateable.ts index 980b064..7fb6d18 100644 --- a/engine/components/NetworkUpdateable.ts +++ b/engine/components/NetworkUpdateable.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames } from "."; +import { Component, ComponentNames } from '.'; export class NetworkUpdateable extends Component { public isPublish: boolean; diff --git a/engine/components/Sprite.ts b/engine/components/Sprite.ts index bdb4982..36b944e 100644 --- a/engine/components/Sprite.ts +++ b/engine/components/Sprite.ts @@ -1,5 +1,5 @@ -import { Component, ComponentNames } from "."; -import type { Dimension2D, DrawArgs, Coord2D } from "../interfaces"; +import { Component, ComponentNames } from '.'; +import type { Dimension2D, DrawArgs, Coord2D } from '../interfaces'; export class Sprite extends Component { private sheet: HTMLImageElement; @@ -17,7 +17,7 @@ export class Sprite extends Component { spriteImgPos: Coord2D, spriteImgDimensions: Dimension2D, msPerFrame: number, - numFrames: number, + numFrames: number ) { super(ComponentNames.Sprite); @@ -56,12 +56,12 @@ export class Sprite extends Component { ctx.drawImage( this.sheet, ...this.getSpriteArgs(), - ...this.getDrawArgs(drawArgs), + ...this.getDrawArgs(drawArgs) ); if (tint) { ctx.globalAlpha = 0.5; - ctx.globalCompositeOperation = "source-atop"; + ctx.globalCompositeOperation = 'source-atop'; ctx.fillStyle = tint; ctx.fillRect(...this.getDrawArgs(drawArgs)); } @@ -74,19 +74,23 @@ export class Sprite extends Component { this.spriteImgPos.x + this.currentFrame * this.spriteImgDimensions.width, this.spriteImgPos.y, this.spriteImgDimensions.width, - this.spriteImgDimensions.height, + this.spriteImgDimensions.height ]; } private getDrawArgs({ center, - dimension, + dimension }: DrawArgs): [dx: number, dy: number, dw: number, dh: number] { return [ center.x - dimension.width / 2, center.y - dimension.height / 2, dimension.width, - dimension.height, + dimension.height ]; } + + public getSpriteDimensions() { + return this.spriteImgDimensions; + } } diff --git a/engine/components/TopCollidable.ts b/engine/components/TopCollidable.ts index 7fb147d..05ce484 100644 --- a/engine/components/TopCollidable.ts +++ b/engine/components/TopCollidable.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames } from "."; +import { Component, ComponentNames } from '.'; export class TopCollidable extends Component { constructor() { diff --git a/engine/components/Velocity.ts b/engine/components/Velocity.ts index aec0c03..0071891 100644 --- a/engine/components/Velocity.ts +++ b/engine/components/Velocity.ts @@ -1,12 +1,12 @@ -import type { Velocity2D } from "../interfaces"; -import { Component } from "./Component"; -import { ComponentNames } from "."; +import type { Velocity2D } from '../interfaces'; +import { Component } from './Component'; +import { ComponentNames } from '.'; export class Velocity extends Component { public velocity: Velocity2D; constructor( - velocity: Velocity2D = { dCartesian: { dx: 0, dy: 0 }, dTheta: 0 }, + velocity: Velocity2D = { dCartesian: { dx: 0, dy: 0 }, dTheta: 0 } ) { super(ComponentNames.Velocity); diff --git a/engine/components/WallBounded.ts b/engine/components/WallBounded.ts index 5f787e1..c1745a8 100644 --- a/engine/components/WallBounded.ts +++ b/engine/components/WallBounded.ts @@ -1,4 +1,4 @@ -import { Component, ComponentNames } from "."; +import { Component, ComponentNames } from '.'; export class WallBounded extends Component { constructor() { diff --git a/engine/components/index.ts b/engine/components/index.ts index 90f4965..6d7c1e5 100644 --- a/engine/components/index.ts +++ b/engine/components/index.ts @@ -1,16 +1,16 @@ -export * from "./Component"; -export * from "./BoundingBox"; -export * from "./Velocity"; -export * from "./Forces"; -export * from "./Sprite"; -export * from "./FacingDirection"; -export * from "./Jump"; -export * from "./TopCollidable"; -export * from "./Collide"; -export * from "./Control"; -export * from "./WallBounded"; -export * from "./Gravity"; -export * from "./Mass"; -export * from "./Moment"; -export * from "./NetworkUpdateable"; -export * from "./names"; +export * from './Component'; +export * from './BoundingBox'; +export * from './Velocity'; +export * from './Forces'; +export * from './Sprite'; +export * from './FacingDirection'; +export * from './Jump'; +export * from './TopCollidable'; +export * from './Collide'; +export * from './Control'; +export * from './WallBounded'; +export * from './Gravity'; +export * from './Mass'; +export * from './Moment'; +export * from './NetworkUpdateable'; +export * from './names'; diff --git a/engine/components/names.ts b/engine/components/names.ts index 02ee064..97b4edd 100644 --- a/engine/components/names.ts +++ b/engine/components/names.ts @@ -1,16 +1,16 @@ export namespace ComponentNames { - export const Sprite = "Sprite"; - export const BoundingBox = "BoundingBox"; - export const Velocity = "Velocity"; - export const FacingDirection = "FacingDirection"; - export const Control = "Control"; - export const Jump = "Jump"; - export const TopCollidable = "TopCollidable"; - export const Collide = "Collide"; - export const WallBounded = "WallBounded"; - export const Gravity = "Gravity"; - export const Forces = "Forces"; - export const Mass = "Mass"; - export const Moment = "Moment"; - export const NetworkUpdateable = "NetworkUpdateable"; + export const Sprite = 'Sprite'; + export const BoundingBox = 'BoundingBox'; + export const Velocity = 'Velocity'; + export const FacingDirection = 'FacingDirection'; + export const Control = 'Control'; + export const Jump = 'Jump'; + export const TopCollidable = 'TopCollidable'; + export const Collide = 'Collide'; + export const WallBounded = 'WallBounded'; + export const Gravity = 'Gravity'; + export const Forces = 'Forces'; + export const Mass = 'Mass'; + export const Moment = 'Moment'; + export const NetworkUpdateable = 'NetworkUpdateable'; } diff --git a/engine/config/assets.ts b/engine/config/assets.ts index 173bab3..289f181 100644 --- a/engine/config/assets.ts +++ b/engine/config/assets.ts @@ -1,10 +1,10 @@ -import type { SpriteSpec } from "./sprites"; -import { SPRITE_SPECS } from "./sprites"; +import type { SpriteSpec } from './sprites'; +import { SPRITE_SPECS } from './sprites'; export const IMAGES = new Map(); export const loadSpritesIntoImageElements = ( - spriteSpecs: Partial[], + spriteSpecs: Partial[] ): Promise[] => { const spritePromises: Promise[] = []; @@ -17,13 +17,13 @@ export const loadSpritesIntoImageElements = ( spritePromises.push( new Promise((resolve) => { img.onload = () => resolve(); - }), + }) ); } if (spriteSpec.states) { spritePromises.push( - ...loadSpritesIntoImageElements(Array.from(spriteSpec.states.values())), + ...loadSpritesIntoImageElements(Array.from(spriteSpec.states.values())) ); } } @@ -35,8 +35,8 @@ export const loadAssets = () => Promise.all([ ...loadSpritesIntoImageElements( Array.from(SPRITE_SPECS.keys()).map( - (key) => SPRITE_SPECS.get(key) as SpriteSpec, - ), - ), + (key) => SPRITE_SPECS.get(key) as SpriteSpec + ) + ) // TODO: Sound ]); diff --git a/engine/config/constants.ts b/engine/config/constants.ts index e93986b..45b0301 100644 --- a/engine/config/constants.ts +++ b/engine/config/constants.ts @@ -1,4 +1,4 @@ -import { Action } from "../interfaces"; +import { Action } from '../interfaces'; export namespace KeyConstants { export const KeyActions: Record = { @@ -10,11 +10,13 @@ export namespace KeyConstants { w: Action.JUMP, ArrowUp: Action.JUMP, + + ' ': Action.JUMP }; // value -> [key] from KeyActions export const ActionKeys: Map = Object.keys( - KeyActions, + KeyActions ).reduce((acc: Map, key) => { const action = KeyActions[key]; diff --git a/engine/config/index.ts b/engine/config/index.ts index 7a1052a..03b2246 100644 --- a/engine/config/index.ts +++ b/engine/config/index.ts @@ -1,3 +1,3 @@ -export * from "./constants"; -export * from "./assets.ts"; -export * from "./sprites.ts"; +export * from './constants'; +export * from './assets.ts'; +export * from './sprites.ts'; diff --git a/engine/config/sprites.ts b/engine/config/sprites.ts index 1f65c18..e5fcd31 100644 --- a/engine/config/sprites.ts +++ b/engine/config/sprites.ts @@ -1,7 +1,7 @@ export enum Sprites { FLOOR, TRAMPOLINE, - COFFEE, + COFFEE } export interface SpriteSpec { @@ -22,12 +22,12 @@ const floorSpriteSpec = { height: 40, frames: 3, msPerFrame: 125, - states: new Map>(), + states: new Map>() }; [40, 80, 120, 160].forEach((width) => { floorSpriteSpec.states.set(width, { width, - sheet: `/assets/floor_tile_${width}.png`, + sheet: `/assets/floor_tile_${width}.png` }); }); SPRITE_SPECS.set(Sprites.FLOOR, floorSpriteSpec); @@ -37,12 +37,12 @@ const coffeeSpriteSpec = { width: 60, height: 45, frames: 3, - states: new Map>(), + states: new Map>() }; -coffeeSpriteSpec.states.set("LEFT", { - sheet: "/assets/coffee_left.png", +coffeeSpriteSpec.states.set('LEFT', { + sheet: '/assets/coffee_left.png' }); -coffeeSpriteSpec.states.set("RIGHT", { - sheet: "/assets/coffee_right.png", +coffeeSpriteSpec.states.set('RIGHT', { + sheet: '/assets/coffee_right.png' }); SPRITE_SPECS.set(Sprites.COFFEE, coffeeSpriteSpec); diff --git a/engine/entities/Entity.ts b/engine/entities/Entity.ts index 88982cb..b016fc0 100644 --- a/engine/entities/Entity.ts +++ b/engine/entities/Entity.ts @@ -1,5 +1,5 @@ -import { EntityNames, Player } from "."; -import type { Component } from "../components"; +import { EntityNames, Player } from '.'; +import type { Component } from '../components'; export abstract class Entity { public id: string; @@ -18,7 +18,7 @@ export abstract class Entity { public getComponent(name: string): T { if (!this.hasComponent(name)) { - throw new Error("Entity does not have component " + name); + throw new Error('Entity does not have component ' + name); } return this.components.get(name) as T; } @@ -34,9 +34,11 @@ export abstract class Entity { static from(entityName: string, args: any): Entity { switch (entityName) { case EntityNames.Player: - return new Player(args.playerId); + const player = new Player(args.playerId); + player.id = args.id; + return player; default: - throw new Error(".from() Entity type not implemented: " + entityName); + throw new Error('.from() Entity type not implemented: ' + entityName); } } } diff --git a/engine/entities/Floor.ts b/engine/entities/Floor.ts index 6cfc276..6f9b13b 100644 --- a/engine/entities/Floor.ts +++ b/engine/entities/Floor.ts @@ -1,11 +1,11 @@ -import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from "../config"; -import { BoundingBox, Sprite } from "../components"; -import { TopCollidable } from "../components/TopCollidable"; -import { Entity, EntityNames } from "../entities"; +import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from '../config'; +import { BoundingBox, Sprite } from '../components'; +import { TopCollidable } from '../components/TopCollidable'; +import { Entity, EntityNames } from '../entities'; export class Floor extends Entity { private static spriteSpec: SpriteSpec = SPRITE_SPECS.get( - Sprites.FLOOR, + Sprites.FLOOR ) as SpriteSpec; constructor(width: number) { @@ -17,18 +17,8 @@ export class Floor extends Entity { { x: 0, y: 0 }, { width, height: Floor.spriteSpec.height }, Floor.spriteSpec.msPerFrame, - Floor.spriteSpec.frames, - ), - ); - - this.addComponent( - new BoundingBox( - { - x: 300, - y: 300, - }, - { width, height: Floor.spriteSpec.height }, - ), + Floor.spriteSpec.frames + ) ); this.addComponent(new TopCollidable()); diff --git a/engine/entities/Player.ts b/engine/entities/Player.ts index cfe4dd2..947fbd6 100644 --- a/engine/entities/Player.ts +++ b/engine/entities/Player.ts @@ -1,5 +1,5 @@ -import { Entity, EntityNames } from "."; -import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from "../config"; +import { Entity, EntityNames } from '.'; +import { IMAGES, SPRITE_SPECS, Sprites, type SpriteSpec } from '../config'; import { Jump, FacingDirection, @@ -12,16 +12,16 @@ import { Collide, Control, Mass, - Moment, -} from "../components"; -import { Direction } from "../interfaces"; + Moment +} from '../components'; +import { Direction } from '../interfaces'; export class Player extends Entity { private static MASS: number = 10; private static MOI: number = 100; private static spriteSpec: SpriteSpec = SPRITE_SPECS.get( - Sprites.COFFEE, + Sprites.COFFEE ) as SpriteSpec; constructor(playerId: string) { @@ -31,15 +31,15 @@ export class Player extends Entity { new BoundingBox( { x: 300, - y: 100, + 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)); @@ -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)); diff --git a/engine/entities/index.ts b/engine/entities/index.ts index dd3dba9..8aee83c 100644 --- a/engine/entities/index.ts +++ b/engine/entities/index.ts @@ -1,4 +1,4 @@ -export * from "./Entity"; -export * from "./Floor"; -export * from "./Player"; -export * from "./names"; +export * from './Entity'; +export * from './Floor'; +export * from './Player'; +export * from './names'; diff --git a/engine/entities/names.ts b/engine/entities/names.ts index 21594c8..cf65f9f 100644 --- a/engine/entities/names.ts +++ b/engine/entities/names.ts @@ -1,4 +1,4 @@ export namespace EntityNames { - export const Player = "Player"; - export const Floor = "Floor"; + export const Player = 'Player'; + export const Floor = 'Floor'; } diff --git a/engine/interfaces/Action.ts b/engine/interfaces/Action.ts index 61c89e1..f0e6a66 100644 --- a/engine/interfaces/Action.ts +++ b/engine/interfaces/Action.ts @@ -1,5 +1,5 @@ export enum Action { MOVE_LEFT, MOVE_RIGHT, - JUMP, + JUMP } diff --git a/engine/interfaces/Direction.ts b/engine/interfaces/Direction.ts index 0bc6ef3..af1c7ac 100644 --- a/engine/interfaces/Direction.ts +++ b/engine/interfaces/Direction.ts @@ -1,6 +1,6 @@ export enum Direction { - UP = "UP", - DOWN = "DOWN", - LEFT = "LEFT", - RIGHT = "RIGHT", + UP = 'UP', + DOWN = 'DOWN', + LEFT = 'LEFT', + RIGHT = 'RIGHT' } diff --git a/engine/interfaces/Draw.ts b/engine/interfaces/Draw.ts index 6561a01..8479fe4 100644 --- a/engine/interfaces/Draw.ts +++ b/engine/interfaces/Draw.ts @@ -1,4 +1,4 @@ -import type { Coord2D, Dimension2D } from "./"; +import type { Coord2D, Dimension2D } from './'; export interface DrawArgs { center: Coord2D; diff --git a/engine/interfaces/index.ts b/engine/interfaces/index.ts index 8cdf4d8..c2f6896 100644 --- a/engine/interfaces/index.ts +++ b/engine/interfaces/index.ts @@ -1,4 +1,4 @@ -export * from "./Vec2"; -export * from "./Draw"; -export * from "./Direction"; -export * from "./Action"; +export * from './Vec2'; +export * from './Draw'; +export * from './Direction'; +export * from './Action'; diff --git a/engine/network/index.ts b/engine/network/index.ts index 1726ffc..1bf95fb 100644 --- a/engine/network/index.ts +++ b/engine/network/index.ts @@ -1,7 +1,7 @@ export enum MessageType { - NEW_ENTITY = "NEW_ENTITY", - REMOVE_ENTITY = "REMOVE_ENTITY", - UPDATE_ENTITY = "UPDATE_ENTITY", + NEW_ENTITIES = 'NEW_ENTITIES', + REMOVE_ENTITIES = 'REMOVE_ENTITIES', + UPDATE_ENTITY = 'UPDATE_ENTITY' } export type EntityAddBody = { diff --git a/engine/structures/Grid.ts b/engine/structures/Grid.ts index 6e8c0cb..5f0e053 100644 --- a/engine/structures/Grid.ts +++ b/engine/structures/Grid.ts @@ -1,6 +1,6 @@ -import type { Coord2D, Dimension2D } from "../interfaces"; -import type { BoxedEntry, RefreshingCollisionFinderBehavior } from "."; -import { Miscellaneous } from "../config/constants"; +import type { Coord2D, Dimension2D } from '../interfaces'; +import type { BoxedEntry, RefreshingCollisionFinderBehavior } from '.'; +import { Miscellaneous } from '../config/constants'; export class Grid implements RefreshingCollisionFinderBehavior { private cellEntities: Map; @@ -12,11 +12,11 @@ export class Grid implements RefreshingCollisionFinderBehavior { constructor( gridDimension: Dimension2D = { width: Miscellaneous.WIDTH, - height: Miscellaneous.HEIGHT, + height: Miscellaneous.HEIGHT }, cellDimension: Dimension2D = { width: Miscellaneous.DEFAULT_GRID_WIDTH, - height: Miscellaneous.DEFAULT_GRID_HEIGHT, + height: Miscellaneous.DEFAULT_GRID_HEIGHT }, topLeft = { x: 0, y: 0 } ) { @@ -73,7 +73,7 @@ export class Grid implements RefreshingCollisionFinderBehavior { const translated: Coord2D = { y: center.y - this.topLeft.y, - x: center.x - this.topLeft.x, + x: center.x - this.topLeft.x }; const topLeftBox = { @@ -82,7 +82,7 @@ export class Grid implements RefreshingCollisionFinderBehavior { ), y: Math.floor( (translated.y - dimension.height / 2) / this.cellDimension.height - ), + ) }; const bottomRightBox = { x: Math.floor( @@ -90,7 +90,7 @@ export class Grid implements RefreshingCollisionFinderBehavior { ), y: Math.floor( (translated.y + dimension.height / 2) / this.cellDimension.height - ), + ) }; const cells: number[] = []; diff --git a/engine/structures/QuadTree.ts b/engine/structures/QuadTree.ts index 1ab2d1d..93702d0 100644 --- a/engine/structures/QuadTree.ts +++ b/engine/structures/QuadTree.ts @@ -1,11 +1,11 @@ -import type { Coord2D, Dimension2D } from "../interfaces"; -import type { BoxedEntry, RefreshingCollisionFinderBehavior } from "."; +import type { Coord2D, Dimension2D } from '../interfaces'; +import type { BoxedEntry, RefreshingCollisionFinderBehavior } from '.'; enum Quadrant { I, II, III, - IV, + IV } /* @@ -102,8 +102,8 @@ export class QuadTree implements RefreshingCollisionFinderBehavior { [Quadrant.III, { x: this.topLeft.x, y: this.topLeft.y + halfHeight }], [ Quadrant.IV, - { x: this.topLeft.x + halfWidth, y: this.topLeft.y + halfHeight }, - ], + { x: this.topLeft.x + halfWidth, y: this.topLeft.y + halfHeight } + ] ] as [Quadrant, Coord2D][] ).forEach(([quadrant, pos]) => { this.children.set( @@ -122,27 +122,27 @@ export class QuadTree implements RefreshingCollisionFinderBehavior { private getQuadrants(boxedEntry: BoxedEntry): Quadrant[] { const treeCenter: Coord2D = { x: this.topLeft.x + this.dimension.width / 2, - y: this.topLeft.y + this.dimension.height / 2, + y: this.topLeft.y + this.dimension.height / 2 }; return ( [ [ Quadrant.I, - (x: number, y: number) => x >= treeCenter.x && y < treeCenter.y, + (x: number, y: number) => x >= treeCenter.x && y < treeCenter.y ], [ Quadrant.II, - (x: number, y: number) => x < treeCenter.x && y < treeCenter.y, + (x: number, y: number) => x < treeCenter.x && y < treeCenter.y ], [ Quadrant.III, - (x: number, y: number) => x < treeCenter.x && y >= treeCenter.y, + (x: number, y: number) => x < treeCenter.x && y >= treeCenter.y ], [ Quadrant.IV, - (x: number, y: number) => x >= treeCenter.x && y >= treeCenter.y, - ], + (x: number, y: number) => x >= treeCenter.x && y >= treeCenter.y + ] ] as [Quadrant, (x: number, y: number) => boolean][] ) .filter( diff --git a/engine/structures/RefreshingCollisionFinderBehavior.ts b/engine/structures/RefreshingCollisionFinderBehavior.ts index 21d690d..573ddd8 100644 --- a/engine/structures/RefreshingCollisionFinderBehavior.ts +++ b/engine/structures/RefreshingCollisionFinderBehavior.ts @@ -1,4 +1,4 @@ -import type { Coord2D, Dimension2D } from "../interfaces"; +import type { Coord2D, Dimension2D } from '../interfaces'; export interface BoxedEntry { id: string; @@ -7,8 +7,8 @@ export interface BoxedEntry { } export interface RefreshingCollisionFinderBehavior { - public clear(): void; - public insert(boxedEntry: BoxedEntry): void; - public getNeighborIds(boxedEntry: BoxedEntry): Set; - public setTopLeft(topLeft: Coord2d): void; + clear(): void; + insert(boxedEntry: BoxedEntry): void; + getNeighborIds(boxedEntry: BoxedEntry): Set; + setTopLeft(topLeft: Coord2D): void; } diff --git a/engine/structures/index.ts b/engine/structures/index.ts index 49a84af..679dbd4 100644 --- a/engine/structures/index.ts +++ b/engine/structures/index.ts @@ -1,3 +1,3 @@ -export * from "./RefreshingCollisionFinderBehavior"; -export * from "./QuadTree"; -export * from "./Grid"; +export * from './RefreshingCollisionFinderBehavior'; +export * from './QuadTree'; +export * from './Grid'; diff --git a/engine/systems/Collision.ts b/engine/systems/Collision.ts index 2dd920e..4a838dd 100644 --- a/engine/systems/Collision.ts +++ b/engine/systems/Collision.ts @@ -1,22 +1,22 @@ -import { SystemNames, System } from "."; +import { SystemNames, System } from '.'; import { Mass, BoundingBox, ComponentNames, Jump, Velocity, - Forces, -} from "../components"; -import { Game } from "../Game"; -import { Miscellaneous, PhysicsConstants } from "../config"; -import { Entity } from "../entities"; -import type { Coord2D, Dimension2D, Velocity2D } from "../interfaces"; -import { BoxedEntry, RefreshingCollisionFinderBehavior } from "../structures"; + Forces +} from '../components'; +import { Game } from '../Game'; +import { Miscellaneous, PhysicsConstants } from '../config'; +import { Entity } from '../entities'; +import type { Coord2D, Dimension2D, Velocity2D } from '../interfaces'; +import { BoxedEntry, RefreshingCollisionFinderBehavior } from '../structures'; export class Collision extends System { private static readonly COLLIDABLE_COMPONENT_NAMES = [ ComponentNames.Collide, - ComponentNames.TopCollidable, + ComponentNames.TopCollidable ]; private collisionFinder: RefreshingCollisionFinderBehavior; @@ -38,7 +38,7 @@ export class Collision extends System { return; } entitiesToAddToCollisionFinder.push(entity); - }), + }) ); this.insertEntitiesAndUpdateBounds(entitiesToAddToCollisionFinder); @@ -53,7 +53,7 @@ export class Collision extends System { entities.forEach((entity) => { const boundingBox = entity.getComponent( - ComponentNames.BoundingBox, + ComponentNames.BoundingBox ); let dimension = { ...boundingBox.dimension }; @@ -73,7 +73,7 @@ export class Collision extends System { collisionFinderInsertions.push({ id: entity.id, dimension, - center, + center }); }); @@ -82,13 +82,13 @@ export class Collision extends System { this.collisionFinder.setTopLeft(topLeft); this.collisionFinder.setDimension({ width: bottomRight.x - topLeft.x, - height: bottomRight.y - topLeft.y, + height: bottomRight.y - topLeft.y }); } // then, begin insertions collisionFinderInsertions.forEach((boxedEntry: BoxedEntry) => - this.collisionFinder.insert(boxedEntry), + this.collisionFinder.insert(boxedEntry) ); } @@ -97,7 +97,7 @@ export class Collision extends System { collidingEntities.forEach(([entityAId, entityBId]) => { const [entityA, entityB] = [entityAId, entityBId].map((id) => - game.entities.get(id), + game.entities.get(id) ); if (entityA && entityB) { this.performCollision(entityA, entityB); @@ -107,13 +107,13 @@ export class Collision extends System { private performCollision(entityA: Entity, entityB: Entity) { const [entityABoundingBox, entityBBoundingBox] = [entityA, entityB].map( - (entity) => entity.getComponent(ComponentNames.BoundingBox), + (entity) => entity.getComponent(ComponentNames.BoundingBox) ); let velocity: Velocity2D = { dCartesian: { dx: 0, dy: 0 }, dTheta: 0 }; if (entityA.hasComponent(ComponentNames.Velocity)) { velocity = entityA.getComponent( - ComponentNames.Velocity, + ComponentNames.Velocity ).velocity; } @@ -125,7 +125,7 @@ export class Collision extends System { ) { if (entityBBoundingBox.rotation != 0) { throw new Error( - `entity with id ${entityB.id} has TopCollidable component and a non-zero rotation. that is not (yet) supported.`, + `entity with id ${entityB.id} has TopCollidable component and a non-zero rotation. that is not (yet) supported.` ); } @@ -139,7 +139,7 @@ export class Collision extends System { entityA.getComponent(ComponentNames.Forces).forces.push({ fCartesian: { fy: F_n, fx: 0 }, - torque: 0, + torque: 0 }); } @@ -157,19 +157,19 @@ export class Collision extends System { private getCollidingEntities( collidableEntities: Entity[], - game: Game, + game: Game ): [string, string][] { const collidingEntityIds: [string, string][] = []; for (const entity of collidableEntities) { const boundingBox = entity.getComponent( - ComponentNames.BoundingBox, + ComponentNames.BoundingBox ); const neighborIds = this.collisionFinder.getNeighborIds({ id: entity.id, dimension: boundingBox.dimension, - center: boundingBox.center, + center: boundingBox.center }); for (const neighborId of neighborIds) { @@ -177,7 +177,7 @@ export class Collision extends System { if (!neighbor) return; const neighborBoundingBox = neighbor.getComponent( - ComponentNames.BoundingBox, + ComponentNames.BoundingBox ); if (boundingBox.isCollidingWith(neighborBoundingBox)) { @@ -192,11 +192,11 @@ export class Collision extends System { // ramblings: https://excalidraw.com/#json=z-xD86Za4a3duZuV2Oky0,KaGe-5iHJu1Si8inEo4GLQ private getDyToPushOutOfFloor( entityBoundingBox: BoundingBox, - floorBoundingBox: BoundingBox, + floorBoundingBox: BoundingBox ): number { const { dimension: { width, height }, - center: { x }, + center: { x } } = entityBoundingBox; const outScribedRectangle = entityBoundingBox.getOutscribedBoxDims(); @@ -215,7 +215,7 @@ export class Collision extends System { if (x >= floorBoundingBox.center.x) { boundedCollisionX = Math.min( floorBoundingBox.center.x + floorBoundingBox.dimension.width / 2, - clippedX, + clippedX ); return ( outScribedRectangle.height / 2 - @@ -225,7 +225,7 @@ export class Collision extends System { boundedCollisionX = Math.max( floorBoundingBox.center.x - floorBoundingBox.dimension.width / 2, - clippedX, + clippedX ); return ( diff --git a/engine/systems/FacingDirection.ts b/engine/systems/FacingDirection.ts index daf639f..01f32cf 100644 --- a/engine/systems/FacingDirection.ts +++ b/engine/systems/FacingDirection.ts @@ -2,10 +2,10 @@ import { ComponentNames, Velocity, FacingDirection as FacingDirectionComponent, - Control, -} from "../components"; -import { Game } from "../Game"; -import { System, SystemNames } from "./"; + Control +} from '../components'; +import { Game } from '../Game'; +import { System, SystemNames } from './'; export class FacingDirection extends System { constructor() { @@ -23,7 +23,7 @@ export class FacingDirection extends System { const totalVelocityComponent = new Velocity(); const control = entity.getComponent(ComponentNames.Control); const velocity = entity.getComponent( - ComponentNames.Velocity, + ComponentNames.Velocity ).velocity; totalVelocityComponent.add(velocity); @@ -32,7 +32,7 @@ export class FacingDirection extends System { } const facingDirection = entity.getComponent( - ComponentNames.FacingDirection, + ComponentNames.FacingDirection ); if (totalVelocityComponent.velocity.dCartesian.dx > 0) { @@ -40,7 +40,7 @@ export class FacingDirection extends System { } else if (totalVelocityComponent.velocity.dCartesian.dx < 0) { entity.addComponent(facingDirection.facingLeftSprite); } - }, + } ); } } diff --git a/engine/systems/Input.ts b/engine/systems/Input.ts index a32ba9a..4a5a3c3 100644 --- a/engine/systems/Input.ts +++ b/engine/systems/Input.ts @@ -4,12 +4,12 @@ import { ComponentNames, Velocity, Mass, - Control, -} from "../components"; -import { Game } from "../Game"; -import { KeyConstants, PhysicsConstants } from "../config"; -import { Action } from "../interfaces"; -import { System, SystemNames } from "."; + Control +} from '../components'; +import { Game } from '../Game'; +import { KeyConstants, PhysicsConstants } from '../config'; +import { Action } from '../interfaces'; +import { System, SystemNames } from '.'; export class Input extends System { public clientId: string; @@ -42,7 +42,7 @@ export class Input extends System { public update(_dt: number, game: Game) { game.forEachEntityWithComponent(ComponentNames.Control, (entity) => { const controlComponent = entity.getComponent( - ComponentNames.Control, + ComponentNames.Control ); if (controlComponent.controllableBy != this.clientId) return; @@ -58,7 +58,7 @@ export class Input extends System { if (entity.hasComponent(ComponentNames.Jump)) { const velocity = entity.getComponent( - ComponentNames.Velocity, + ComponentNames.Velocity ).velocity; const jump = entity.getComponent(ComponentNames.Jump); @@ -78,9 +78,9 @@ export class Input extends System { entity.getComponent(ComponentNames.Forces)?.forces.push({ fCartesian: { fy: mass * PhysicsConstants.PLAYER_JUMP_ACC, - fx: 0, + fx: 0 }, - torque: 0, + torque: 0 }); } } diff --git a/engine/systems/NetworkUpdate.ts b/engine/systems/NetworkUpdate.ts index 6c1d3e4..bcfb71e 100644 --- a/engine/systems/NetworkUpdate.ts +++ b/engine/systems/NetworkUpdate.ts @@ -1,11 +1,11 @@ -import { System, SystemNames } from "."; -import { Game } from "../Game"; -import { ComponentNames, NetworkUpdateable } from "../components"; +import { System, SystemNames } from '.'; +import { Game } from '../Game'; +import { ComponentNames, NetworkUpdateable } from '../components'; import { type MessageQueueProvider, type MessagePublisher, - type MessageProcessor, -} from "../network"; + type MessageProcessor +} from '../network'; export class NetworkUpdate extends System { private queueProvider: MessageQueueProvider; @@ -15,7 +15,7 @@ export class NetworkUpdate extends System { constructor( queueProvider: MessageQueueProvider, publisher: MessagePublisher, - messageProcessor: MessageProcessor, + messageProcessor: MessageProcessor ) { super(SystemNames.NetworkUpdate); @@ -34,9 +34,9 @@ export class NetworkUpdate extends System { ComponentNames.NetworkUpdateable, (entity) => { const networkUpdateComponent = entity.getComponent( - ComponentNames.NetworkUpdateable, + ComponentNames.NetworkUpdateable ); - }, + } ); this.publisher.publish(); diff --git a/engine/systems/Physics.ts b/engine/systems/Physics.ts index e324c97..35afb3f 100644 --- a/engine/systems/Physics.ts +++ b/engine/systems/Physics.ts @@ -1,4 +1,4 @@ -import { System, SystemNames } from "."; +import { System, SystemNames } from '.'; import { BoundingBox, ComponentNames, @@ -8,11 +8,11 @@ import { Mass, Jump, Moment, - Control, -} from "../components"; -import { PhysicsConstants } from "../config"; -import type { Force2D, Velocity2D } from "../interfaces"; -import { Game } from "../Game"; + Control +} from '../components'; +import { PhysicsConstants } from '../config'; +import type { Force2D, Velocity2D } from '../interfaces'; +import { Game } from '../Game'; export class Physics extends System { constructor() { @@ -24,10 +24,10 @@ export class Physics extends System { const mass = entity.getComponent(ComponentNames.Mass).mass; const forces = entity.getComponent(ComponentNames.Forces).forces; const velocity = entity.getComponent( - ComponentNames.Velocity, + ComponentNames.Velocity ).velocity; const inertia = entity.getComponent( - ComponentNames.Moment, + ComponentNames.Moment ).inertia; // F_g = mg, applied only until terminal velocity is reached @@ -37,9 +37,9 @@ export class Physics extends System { forces.push({ fCartesian: { fy: mass * PhysicsConstants.GRAVITY, - fx: 0, + fx: 0 }, - torque: 0, + torque: 0 }); } } @@ -49,17 +49,17 @@ export class Physics extends System { (accum: Force2D, { fCartesian, torque }: Force2D) => ({ fCartesian: { fx: accum.fCartesian.fx + (fCartesian?.fx ?? 0), - fy: accum.fCartesian.fy + (fCartesian?.fy ?? 0), + fy: accum.fCartesian.fy + (fCartesian?.fy ?? 0) }, - torque: accum.torque + (torque ?? 0), + torque: accum.torque + (torque ?? 0) }), - { fCartesian: { fx: 0, fy: 0 }, torque: 0 }, + { fCartesian: { fx: 0, fy: 0 }, torque: 0 } ); // integrate accelerations const [ddy, ddx] = [ sumOfForces.fCartesian.fy, - sumOfForces.fCartesian.fx, + sumOfForces.fCartesian.fx ].map((x) => x / mass); velocity.dCartesian.dx += ddx * dt; velocity.dCartesian.dy += ddy * dt; @@ -79,14 +79,14 @@ export class Physics extends System { const control = entity.getComponent(ComponentNames.Control); velocityComponent.add( - entity.getComponent(ComponentNames.Velocity).velocity, + entity.getComponent(ComponentNames.Velocity).velocity ); if (control) { velocityComponent.add(control.controlVelocityComponent.velocity); } const boundingBox = entity.getComponent( - ComponentNames.BoundingBox, + ComponentNames.BoundingBox ); // integrate velocity diff --git a/engine/systems/Render.ts b/engine/systems/Render.ts index 9bb4091..4a4500d 100644 --- a/engine/systems/Render.ts +++ b/engine/systems/Render.ts @@ -1,7 +1,7 @@ -import { System, SystemNames } from "."; -import { BoundingBox, ComponentNames, Sprite } from "../components"; -import { Game } from "../Game"; -import { clamp } from "../utils"; +import { System, SystemNames } from '.'; +import { BoundingBox, ComponentNames, Sprite } from '../components'; +import { Game } from '../Game'; +import { clamp } from '../utils'; export class Render extends System { private ctx: CanvasRenderingContext2D; @@ -19,7 +19,7 @@ export class Render extends System { sprite.update(dt); const boundingBox = entity.getComponent( - ComponentNames.BoundingBox, + ComponentNames.BoundingBox ); // don't render if we're outside the screen @@ -27,12 +27,12 @@ export class Render extends System { clamp( boundingBox.center.y, -boundingBox.dimension.height / 2, - this.ctx.canvas.height + boundingBox.dimension.height / 2, + this.ctx.canvas.height + boundingBox.dimension.height / 2 ) != boundingBox.center.y || clamp( boundingBox.center.x, -boundingBox.dimension.width / 2, - this.ctx.canvas.width + boundingBox.dimension.width / 2, + this.ctx.canvas.width + boundingBox.dimension.width / 2 ) != boundingBox.center.x ) { return; @@ -41,7 +41,7 @@ export class Render extends System { const drawArgs = { center: boundingBox.center, dimension: boundingBox.dimension, - rotation: boundingBox.rotation, + rotation: boundingBox.rotation }; sprite.draw(this.ctx, drawArgs); diff --git a/engine/systems/System.ts b/engine/systems/System.ts index 8b00dc5..de41988 100644 --- a/engine/systems/System.ts +++ b/engine/systems/System.ts @@ -1,4 +1,4 @@ -import { Game } from "../Game"; +import { Game } from '../Game'; export abstract class System { public readonly name: string; diff --git a/engine/systems/WallBounds.ts b/engine/systems/WallBounds.ts index a0d4a9c..7da84e4 100644 --- a/engine/systems/WallBounds.ts +++ b/engine/systems/WallBounds.ts @@ -1,28 +1,24 @@ -import { System, SystemNames } from "."; -import { BoundingBox, ComponentNames } from "../components"; -import { Game } from "../Game"; -import type { Entity } from "../entities"; -import { clamp } from "../utils"; +import { System, SystemNames } from '.'; +import { BoundingBox, ComponentNames } from '../components'; +import { Game } from '../Game'; +import { clamp } from '../utils'; +import { Miscellaneous } from '../config'; export class WallBounds extends System { - private screenWidth: number; - - constructor(screenWidth: number) { + constructor() { super(SystemNames.WallBounds); - - this.screenWidth = screenWidth; } public update(_dt: number, game: Game) { game.forEachEntityWithComponent(ComponentNames.WallBounded, (entity) => { const boundingBox = entity.getComponent( - ComponentNames.BoundingBox, + ComponentNames.BoundingBox ); boundingBox.center.x = clamp( boundingBox.center.x, boundingBox.dimension.width / 2, - this.screenWidth - boundingBox.dimension.width / 2, + Miscellaneous.WIDTH - boundingBox.dimension.width / 2 ); }); } diff --git a/engine/systems/index.ts b/engine/systems/index.ts index 075fc4e..43181e9 100644 --- a/engine/systems/index.ts +++ b/engine/systems/index.ts @@ -1,9 +1,9 @@ -export * from "./names"; -export * from "./System"; -export * from "./Render"; -export * from "./Physics"; -export * from "./Input"; -export * from "./FacingDirection"; -export * from "./Collision"; -export * from "./WallBounds"; -export * from "./NetworkUpdate"; +export * from './names'; +export * from './System'; +export * from './Render'; +export * from './Physics'; +export * from './Input'; +export * from './FacingDirection'; +export * from './Collision'; +export * from './WallBounds'; +export * from './NetworkUpdate'; diff --git a/engine/systems/names.ts b/engine/systems/names.ts index cf66422..ddf6f19 100644 --- a/engine/systems/names.ts +++ b/engine/systems/names.ts @@ -1,9 +1,9 @@ export namespace SystemNames { - export const Render = "Render"; - export const Physics = "Physics"; - export const FacingDirection = "FacingDirection"; - export const Input = "Input"; - export const Collision = "Collision"; - export const WallBounds = "WallBounds"; - export const NetworkUpdate = "NetworkUpdate"; + export const Render = 'Render'; + export const Physics = 'Physics'; + export const FacingDirection = 'FacingDirection'; + export const Input = 'Input'; + export const Collision = 'Collision'; + export const WallBounds = 'WallBounds'; + export const NetworkUpdate = 'NetworkUpdate'; } diff --git a/engine/utils/coding.ts b/engine/utils/coding.ts index 4c1b17f..3f78889 100644 --- a/engine/utils/coding.ts +++ b/engine/utils/coding.ts @@ -1,8 +1,8 @@ const replacer = (_key: any, value: any) => { if (value instanceof Map) { return { - dataType: "Map", - value: Array.from(value.entries()), + dataType: 'Map', + value: Array.from(value.entries()) }; } else { return value; @@ -10,8 +10,8 @@ const replacer = (_key: any, value: any) => { }; const reviver = (_key: any, value: any) => { - if (typeof value === "object" && value !== null) { - if (value.dataType === "Map") { + if (typeof value === 'object' && value !== null) { + if (value.dataType === 'Map') { return new Map(value.value); } } diff --git a/engine/utils/dotProduct.ts b/engine/utils/dotProduct.ts index 59f8857..82bcdea 100644 --- a/engine/utils/dotProduct.ts +++ b/engine/utils/dotProduct.ts @@ -1,4 +1,4 @@ -import type { Coord2D } from "../interfaces"; +import type { Coord2D } from '../interfaces'; export const dotProduct = (vector1: Coord2D, vector2: Coord2D): number => vector1.x * vector2.x + vector1.y * vector2.y; diff --git a/engine/utils/index.ts b/engine/utils/index.ts index b70734f..65446d1 100644 --- a/engine/utils/index.ts +++ b/engine/utils/index.ts @@ -1,4 +1,4 @@ -export * from "./rotateVector"; -export * from "./dotProduct"; -export * from "./clamp"; -export * from "./coding"; +export * from './rotateVector'; +export * from './dotProduct'; +export * from './clamp'; +export * from './coding'; diff --git a/engine/utils/rotateVector.ts b/engine/utils/rotateVector.ts index 82bb54d..221ffb2 100644 --- a/engine/utils/rotateVector.ts +++ b/engine/utils/rotateVector.ts @@ -1,4 +1,4 @@ -import type { Coord2D } from "../interfaces"; +import type { Coord2D } from '../interfaces'; /** * ([[cos(θ), -sin(θ),]) ([x,) @@ -10,6 +10,6 @@ export const rotateVector = (vector: Coord2D, theta: number): Coord2D => { return { x: vector.x * cos - vector.y * sin, - y: vector.x * sin + vector.y * cos, + y: vector.x * sin + vector.y * cos }; }; diff --git a/server/package.json b/server/package.json index 17d3c25..388cff2 100644 --- a/server/package.json +++ b/server/package.json @@ -8,6 +8,5 @@ "peerDependencies": { "typescript": "^5.0.0" }, - "dependencies": { - } + "dependencies": {} } diff --git a/server/src/server.ts b/server/src/server.ts index b3eb1ea..c77bfef 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,21 +1,22 @@ -import { Game } from "@engine/Game"; -import { EntityNames, Player } from "@engine/entities"; -import { WallBounds, Physics, Collision, NetworkUpdate } from "@engine/systems"; +import { Game } from '@engine/Game'; +import { EntityNames, Player } from '@engine/entities'; +import { WallBounds, Physics, Collision, NetworkUpdate } from '@engine/systems'; import { 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"; + 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"; +const SERVER_TICK_RATE = (1 / 60) * 1000; +const GAME_TOPIC = 'game'; +const MAX_PLAYERS = 8; type SessionData = { sessionId: string }; @@ -70,11 +71,11 @@ class ServerSocketMessagePublisher implements MessagePublisher { } public publish() { - this.messages.forEach( - (message) => this.server?.publish(GAME_TOPIC, stringify(message)), - ); + if (this.messages.length) { + this.server?.publish(GAME_TOPIC, stringify(this.messages)); - this.messages = []; + this.messages = []; + } } } @@ -85,81 +86,102 @@ const messagePublisher = new ServerSocketMessagePublisher(); const messageProcessor = new ServerMessageProcessor(); const sessionControllableEntities: Map> = new Map(); +const sessions = new Set(); + const server = Bun.serve({ port: SERVER_PORT, fetch: async (req, server): Promise => { const url = new URL(req.url); const headers = new Headers(); - headers.set("Access-Control-Allow-Origin", "*"); + headers.set('Access-Control-Allow-Origin', '*'); + + if (url.pathname == '/assign') { + if (sessions.size > MAX_PLAYERS) + return new Response('too many players', { headers, status: 400 }); - if (url.pathname == "/assign") { const sessionId = crypto.randomUUID(); - headers.set("Set-Cookie", `SessionId=${sessionId};`); + headers.set('Set-Cookie', `SessionId=${sessionId};`); + + sessions.add(sessionId); return new Response(sessionId, { headers }); } - const cookie = req.headers.get("cookie"); + const cookie = req.headers.get('cookie'); if (!cookie) { - return new Response("No session", { headers, status: 401 }); + return new Response('No session', { headers, status: 401 }); } - const sessionId = cookie.split(";").at(0)!.split("SessionId=").at(1); + const sessionId = cookie.split(';').at(0)!.split('SessionId=').at(1); - if (url.pathname == "/game") { + if (url.pathname == '/game') { headers.set( - "Set-Cookie", - `SessionId=${sessionId}; HttpOnly; SameSite=Strict;`, + 'Set-Cookie', + `SessionId=${sessionId}; HttpOnly; SameSite=Strict;` ); server.upgrade(req, { headers, data: { - sessionId, - }, + sessionId + } }); - return new Response("upgraded", { headers }); + return new Response('upgraded', { headers }); } - if (url.pathname == "/me") { + + if (url.pathname == '/me') { return new Response(sessionId, { headers }); } - return new Response("Not found", { headers, status: 404 }); + 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(sessionId); game.addEntity(player); - sessionControllableEntities.set(sessionId, new Set(player.id)); + sessionControllableEntities.set(sessionId, new Set([player.id])); messagePublisher.addMessage({ - type: MessageType.NEW_ENTITY, - body: { - entityName: EntityNames.Player, - args: { playerId: sessionId }, - }, + type: MessageType.NEW_ENTITIES, + body: [ + { + entityName: EntityNames.Player, + args: { playerId: sessionId, id: player.id } + } + ] }); ws.subscribe(GAME_TOPIC); }, message(ws, message) { - if (typeof message == "string") { + if (typeof message == 'string') { const receivedMessage = parse(message); receivedMessage.sessionData = ws.data; messageReceiver.addMessage(receivedMessage); } }, - close(_ws) {}, - }, + close(ws) { + const { sessionId } = ws.data; + + sessions.delete(sessionId); + + const sessionEntities = sessionControllableEntities.get(sessionId); + if (!sessionEntities) return; + + messagePublisher.addMessage({ + type: MessageType.REMOVE_ENTITIES, + body: Array.from(sessionEntities) + }); + } + } }); messagePublisher.setServer(server); @@ -167,8 +189,8 @@ messagePublisher.setServer(server); [ new Physics(), new Collision(new Grid()), - new WallBounds(Miscellaneous.WIDTH), - new NetworkUpdate(messageReceiver, messagePublisher, messageProcessor), + new WallBounds(), + new NetworkUpdate(messageReceiver, messagePublisher, messageProcessor) ].forEach((system) => game.addSystem(system)); game.start();