diff --git a/.do/apps.yml b/.do/apps.yml deleted file mode 100644 index 40e8b75..0000000 --- a/.do/apps.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: decla-red-server -region: fra -services: - - dockerfile_path: Dockerfile - github: - branch: main - deploy_on_push: true - repo: schmelczerandras/decla.red - http_port: 3000 - instance_count: 1 - instance_size_slug: basic-xxs - run_command: ' declared-server --name=Frankfurt --seed=102' - name: decla-red-server - routes: - - path: / diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index c6f8e04..0000000 --- a/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -.dockerignore -Dockerfile -**/node_modules -node_modules -**/package-lock.json -package-lock.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 80f6d36..18453b6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,9 @@ { "cSpell.words": [ + "Deserializable", + "Deserialization", "decla", + "overridable", "serializable" ] } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 1314703..0000000 --- a/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM node:14.13.0-alpine3.10 - -RUN npm i -g declared-server - -EXPOSE 3000 - -CMD ["--port=3000", "--name=Docker server", "--seed=500"] -ENTRYPOINT [ "NODE_ENV=production node", "declared-server" ] diff --git a/backend/package.json b/backend/package.json index 4b0be84..efaf4d4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "declared-server", - "version": "0.0.11", + "version": "0.0.12", "description": "Game server for decla.red", "keywords": [], "author": "András Schmelczer (https://schmelczer.dev/)", diff --git a/backend/src/map/create-world.ts b/backend/src/create-world.ts similarity index 87% rename from backend/src/map/create-world.ts rename to backend/src/create-world.ts index c94301a..13d7d3e 100644 --- a/backend/src/map/create-world.ts +++ b/backend/src/create-world.ts @@ -1,11 +1,10 @@ import { vec2 } from 'gl-matrix'; import { Random, PlanetBase, hsl, settings } from 'shared'; -import { LampPhysical } from '../objects/lamp-physical'; - -import { PlanetPhysical } from '../objects/planet-physical'; -import { PhysicalContainer } from '../physics/containers/physical-container'; -import { evaluateSdf } from '../physics/functions/evaluate-sdf'; -import { Physical } from '../physics/physicals/physical'; +import { LampPhysical } from './objects/lamp-physical'; +import { PlanetPhysical } from './objects/planet-physical'; +import { PhysicalContainer } from './physics/containers/physical-container'; +import { evaluateSdf } from './physics/functions/evaluate-sdf'; +import { Physical } from './physics/physicals/physical'; export const createWorld = (objectContainer: PhysicalContainer, worldRadius: number) => { const objects: Array = []; diff --git a/backend/src/game-server.ts b/backend/src/game-server.ts index 0fb8177..658294b 100644 --- a/backend/src/game-server.ts +++ b/backend/src/game-server.ts @@ -7,14 +7,12 @@ import { ServerInformation, PlayerInformation, UpdateGameState, - serialize, CharacterTeam, - GameEnd, - GameStart, + GameEndCommand, + GameStartCommand, Command, - last, } from 'shared'; -import { createWorld } from './map/create-world'; +import { createWorld } from './create-world'; import { DeltaTimeCalculator } from './helper/delta-time-calculator'; import { Options } from './options'; import { PlayerContainer } from './players/player-container'; @@ -52,7 +50,7 @@ export class GameServer { this.redPoints = 0; this.isInEndGame = false; this.timeScaling = 1; - previousPlayers?.queueCommandForEachClient(new GameStart()); + previousPlayers?.queueCommandForEachClient(new GameStartCommand()); previousPlayers?.sendQueuedCommands(); } @@ -119,7 +117,7 @@ export class GameServer { this.isInEndGame = true; const endTitleLength = 6000; this.players.queueCommandForEachClient( - new GameEnd(winningTeam, endTitleLength / 1000, true), + new GameEndCommand(winningTeam, endTitleLength / 1000, true), ); setTimeout(() => this.destroy(), endTitleLength * 1.1); } diff --git a/backend/src/helper/get-time-in-milliseconds.ts b/backend/src/helper/get-time-in-milliseconds.ts deleted file mode 100644 index 3ff0437..0000000 --- a/backend/src/helper/get-time-in-milliseconds.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const getTimeInMilliseconds = (): number => { - const [seconds, nanoSeconds] = process.hrtime(); - - return seconds * 1000 + nanoSeconds / 1000 / 1000; -}; diff --git a/backend/src/helper/interpolate-angles.ts b/backend/src/helper/interpolate-angles.ts index 13cf942..d0529c4 100644 --- a/backend/src/helper/interpolate-angles.ts +++ b/backend/src/helper/interpolate-angles.ts @@ -1,9 +1,6 @@ -const shortAngleDist = (a0: number, a1: number): number => { +export const interpolateAngles = (from: number, to: number, q: number) => { const max = Math.PI * 2; - const da = (a1 - a0) % max; - return ((2 * da) % max) - da; -}; - -export const interpolateAngles = (a0: number, a1: number, t: number) => { - return a0 + shortAngleDist(a0, a1) * t; + const possibleDistance = (to - from) % max; + const shortedDistance = ((2 * possibleDistance) % max) - possibleDistance; + return from + shortedDistance * q; }; diff --git a/backend/src/objects/circle-physical.ts b/backend/src/objects/circle-physical.ts index f67bfb8..38b7647 100644 --- a/backend/src/objects/circle-physical.ts +++ b/backend/src/objects/circle-physical.ts @@ -9,7 +9,7 @@ import { DynamicPhysical } from '../physics/physicals/dynamic-physical'; import { ReactsToCollision, reactsToCollision, -} from '../physics/physicals/reacts-to-collision'; +} from '../physics/functions/reacts-to-collision'; @serializesTo(Circle) export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollision { diff --git a/backend/src/objects/player-character-physical.ts b/backend/src/objects/player-character-physical.ts index 2f4648a..be5d67f 100644 --- a/backend/src/objects/player-character-physical.ts +++ b/backend/src/objects/player-character-physical.ts @@ -21,7 +21,7 @@ import { interpolateAngles } from '../helper/interpolate-angles'; import { forceAtPosition } from '../physics/functions/force-at-position'; import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle'; import { PlanetPhysical } from './planet-physical'; -import { ReactsToCollision } from '../physics/physicals/reacts-to-collision'; +import { ReactsToCollision } from '../physics/functions/reacts-to-collision'; @serializesTo(PlayerCharacterBase) export class PlayerCharacterPhysical @@ -479,6 +479,7 @@ export class PlayerCharacterPhysical public kill() { this.isDestroyed = true; + this.remoteCall('kill'); } private destroy() { diff --git a/backend/src/objects/projectile-physical.ts b/backend/src/objects/projectile-physical.ts index f0d77e4..68692c5 100644 --- a/backend/src/objects/projectile-physical.ts +++ b/backend/src/objects/projectile-physical.ts @@ -14,7 +14,7 @@ import { CirclePhysical } from './circle-physical'; import { DynamicPhysical } from '../physics/physicals/dynamic-physical'; import { PhysicalContainer } from '../physics/containers/physical-container'; import { PlanetPhysical } from './planet-physical'; -import { ReactsToCollision } from '../physics/physicals/reacts-to-collision'; +import { ReactsToCollision } from '../physics/functions/reacts-to-collision'; import { PlayerCharacterPhysical } from './player-character-physical'; import { moveCircle } from '../physics/functions/move-circle'; @@ -114,24 +114,6 @@ export class ProjectilePhysical return; } - const gravity = vec2.create(); - const intersecting = this.container.findIntersecting(this.boundingBox); - intersecting.forEach((i) => { - if (i instanceof PlanetPhysical) { - vec2.add(gravity, gravity, i.getForce(this.center)); - } - }); - - vec2.add( - this.velocity, - this.velocity, - vec2.scale( - vec2.create(), - gravity, - deltaTime * settings.gravityScalingForProjectiles, - ), - ); - vec2.copy(this.object.velocity, this.velocity); const { velocity } = this.object.step2(deltaTime); vec2.copy(this.velocity, velocity); diff --git a/backend/src/physics/bounding-boxes/bounding-box-base.ts b/backend/src/physics/bounding-boxes/bounding-box-base.ts index 753bbb0..ed17dc5 100644 --- a/backend/src/physics/bounding-boxes/bounding-box-base.ts +++ b/backend/src/physics/bounding-boxes/bounding-box-base.ts @@ -1,6 +1,7 @@ import { vec2 } from 'gl-matrix'; -export class BoundingBoxBase { +// axes aligned +export abstract class BoundingBoxBase { constructor( protected _xMin: number = 0, protected _xMax: number = 0, diff --git a/backend/src/physics/containers/bounding-box-tree.ts b/backend/src/physics/containers/bounding-box-tree.ts index 62ca08a..8e4dc09 100644 --- a/backend/src/physics/containers/bounding-box-tree.ts +++ b/backend/src/physics/containers/bounding-box-tree.ts @@ -1,7 +1,7 @@ import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base'; import { StaticPhysical } from '../physicals/static-physical'; -// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js +// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js class Node { public left: Node | null = null; public right: Node | null = null; diff --git a/backend/src/physics/functions/get-bounding-box-of-circle.ts b/backend/src/physics/functions/get-bounding-box-of-circle.ts index 4677bd2..9c5b2eb 100644 --- a/backend/src/physics/functions/get-bounding-box-of-circle.ts +++ b/backend/src/physics/functions/get-bounding-box-of-circle.ts @@ -1,8 +1,9 @@ import { Circle } from 'shared'; import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base'; +import { ImmutableBoundingBox } from '../bounding-boxes/immutable-bounding-box'; export const getBoundingBoxOfCircle = (circle: Circle): BoundingBoxBase => - new BoundingBoxBase( + new ImmutableBoundingBox( circle.center.x - circle.radius, circle.center.x + circle.radius, circle.center.y - circle.radius, diff --git a/backend/src/physics/functions/move-circle.ts b/backend/src/physics/functions/move-circle.ts index 9d9b16d..2ee50d8 100644 --- a/backend/src/physics/functions/move-circle.ts +++ b/backend/src/physics/functions/move-circle.ts @@ -1,7 +1,7 @@ import { vec2 } from 'gl-matrix'; import { Circle, GameObject, rotate90Deg } from 'shared'; import { CirclePhysical } from '../../objects/circle-physical'; -import { reactsToCollision } from '../physicals/reacts-to-collision'; +import { reactsToCollision } from './reacts-to-collision'; import { evaluateSdf } from './evaluate-sdf'; import { Physical } from '../physicals/physical'; diff --git a/backend/src/physics/physicals/reacts-to-collision.ts b/backend/src/physics/functions/reacts-to-collision.ts similarity index 100% rename from backend/src/physics/physicals/reacts-to-collision.ts rename to backend/src/physics/functions/reacts-to-collision.ts diff --git a/backend/src/players/player.ts b/backend/src/players/player.ts index 87d74b7..a1b52b4 100644 --- a/backend/src/players/player.ts +++ b/backend/src/players/player.ts @@ -10,7 +10,6 @@ import { SetAspectRatioActionCommand, calculateViewArea, SecondaryActionCommand, - PlayerDiedCommand, settings, PlayerInformation, CharacterTeam, @@ -23,21 +22,21 @@ import { ServerAnnouncement, PropertyUpdatesForObjects, PropertyUpdatesForObject, + PrimaryActionCommand, } from 'shared'; -import { getTimeInMilliseconds } from '../helper/get-time-in-milliseconds'; import { BoundingBox } from '../physics/bounding-boxes/bounding-box'; import { PhysicalContainer } from '../physics/containers/physical-container'; import { PlayerCharacterPhysical } from '../objects/player-character-physical'; import { PlayerContainer } from './player-container'; import { PlayerBase } from './player-base'; +import { DeltaTimeCalculator } from '../helper/delta-time-calculator'; export class Player extends PlayerBase { private aspectRatio: number = 16 / 9; private isActive = true; private objectsPreviouslyInViewArea: Array = []; - - private pingTime?: number; + private pingDeltaTime = new DeltaTimeCalculator(); private _latency?: number; protected commandExecutors: CommandExecutors = { @@ -45,7 +44,7 @@ export class Player extends PlayerBase { (this.aspectRatio = v.aspectRatio), [MoveActionCommand.type]: (c: MoveActionCommand) => this.character?.handleMovementAction(c), - [SecondaryActionCommand.type]: (c: SecondaryActionCommand) => { + [PrimaryActionCommand.type]: (c: PrimaryActionCommand) => { this.character?.shootTowards(c.position); }, }; @@ -63,7 +62,7 @@ export class Player extends PlayerBase { socket.on( TransportEvents.Pong, - () => (this._latency = getTimeInMilliseconds() - this.pingTime!), + () => (this._latency = this.pingDeltaTime.getNextDeltaTimeInSeconds()), ); this.measureLatency(); @@ -71,8 +70,9 @@ export class Player extends PlayerBase { } public measureLatency() { - this.pingTime = getTimeInMilliseconds(); + this.pingDeltaTime.getNextDeltaTimeInSeconds(true); this.socket.emit(TransportEvents.Ping); + if (this.isActive) { setTimeout(this.measureLatency.bind(this), 10000); } @@ -106,7 +106,6 @@ export class Player extends PlayerBase { this.sumDeaths++; this.sumKills = this.character.killCount; - this.queueCommandSend(new PlayerDiedCommand(settings.playerDiedTimeout)); this.character = null; this.timeUntilRespawn = settings.playerDiedTimeout; } diff --git a/frontend/src/scripts/commands/receivers/command-receiver-socket.ts b/frontend/src/scripts/commands/command-socket.ts similarity index 89% rename from frontend/src/scripts/commands/receivers/command-receiver-socket.ts rename to frontend/src/scripts/commands/command-socket.ts index 0808fb6..81ec8ba 100644 --- a/frontend/src/scripts/commands/receivers/command-receiver-socket.ts +++ b/frontend/src/scripts/commands/command-socket.ts @@ -1,6 +1,6 @@ import { Command, CommandReceiver, serialize, TransportEvents } from 'shared'; -export class CommandReceiverSocket extends CommandReceiver { +export class CommandSocket extends CommandReceiver { constructor(private readonly socket: SocketIOClient.Socket) { super(); } diff --git a/frontend/src/scripts/commands/generators/mouse-listener.ts b/frontend/src/scripts/commands/generators/mouse-listener.ts deleted file mode 100644 index fcc21ab..0000000 --- a/frontend/src/scripts/commands/generators/mouse-listener.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { vec2 } from 'gl-matrix'; -import { CommandGenerator, SecondaryActionCommand, TernaryActionCommand } from 'shared'; -import { Game } from '../../game'; - -export class MouseListener extends CommandGenerator { - constructor(target: HTMLElement, private readonly game: Game) { - super(); - - target.addEventListener('mousedown', (event: MouseEvent) => { - const position = this.positionFromEvent(event); - - if (event.button == 0) { - this.sendCommandToSubscribers(new SecondaryActionCommand(position)); - } - }); - - target.addEventListener('contextmenu', (event: MouseEvent) => { - event.preventDefault(); - const position = this.positionFromEvent(event); - this.sendCommandToSubscribers(new TernaryActionCommand(position)); - }); - } - - private positionFromEvent(event: MouseEvent): vec2 { - return this.game.displayToWorldCoordinates( - vec2.fromValues(event.clientX, event.clientY), - ); - } -} diff --git a/frontend/src/scripts/commands/generators/touch-joystick-listener.ts b/frontend/src/scripts/commands/generators/touch-joystick-listener.ts deleted file mode 100644 index 9321554..0000000 --- a/frontend/src/scripts/commands/generators/touch-joystick-listener.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { vec2 } from 'gl-matrix'; -import { - CommandGenerator, - SecondaryActionCommand, - MoveActionCommand, - last, -} from 'shared'; -import { Game } from '../../game'; - -export class TouchJoystickListener extends CommandGenerator { - private readonly deadZone = 8; - private readonly deltaScaling = 0.4; - - private joystick: HTMLElement; - private joystickButton: HTMLElement; - - private isJoystickActive = false; - private touchStartPosition!: vec2; - - constructor( - target: HTMLElement, - private overlay: HTMLElement, - private readonly game: Game, - ) { - super(); - - this.joystick = document.createElement('div'); - this.joystick.className = 'joystick'; - this.joystickButton = document.createElement('div'); - this.joystick.appendChild(this.joystickButton); - - target.addEventListener('touchstart', (event: TouchEvent) => { - event.preventDefault(); - if (this.isJoystickActive) { - const center = vec2.fromValues( - last(event.touches)!.clientX, - last(event.touches)!.clientY, - ); - this.sendCommandToSubscribers( - new SecondaryActionCommand(this.game.displayToWorldCoordinates(center)), - ); - } else { - this.touchStartPosition = vec2.fromValues( - event.touches[0].clientX, - event.touches[0].clientY, - ); - } - }); - - target.addEventListener('touchmove', (event: TouchEvent) => { - event.preventDefault(); - - const touchPosition = vec2.fromValues( - event.touches[0].clientX, - event.touches[0].clientY, - ); - - const delta = vec2.subtract(vec2.create(), touchPosition, this.touchStartPosition); - vec2.scale(delta, delta, this.deltaScaling); - const deltaLength = vec2.length(delta); - - if (!this.isJoystickActive && deltaLength > this.deadZone) { - this.isJoystickActive = true; - this.overlay.appendChild(this.joystick); - this.joystickButton.style.transform = `translateX(-50%) translateY(-50%)`; - this.joystick.style.transform = `translateX(${this.touchStartPosition.x}px) translateY(${this.touchStartPosition.y}px) translateX(-50%) translateY(-50%)`; - } - - const maxLength = 20; - vec2.scale(delta, delta, Math.min(1, maxLength / deltaLength)); - this.joystickButton.style.transform = `translateX(${delta.x}px) translateY(${delta.y}px) translateX(-50%) translateY(-50%)`; - - vec2.set(delta, delta.x, -delta.y); - if (deltaLength > this.deadZone) { - this.sendCommandToSubscribers( - new MoveActionCommand(vec2.normalize(delta, delta)), - ); - } else { - this.sendCommandToSubscribers(new MoveActionCommand(vec2.create())); - } - }); - - target.addEventListener('touchend', (event: TouchEvent) => { - event.preventDefault(); - - if (!this.isJoystickActive) { - const center = vec2.fromValues( - event.changedTouches[0].clientX, - event.changedTouches[0].clientY, - ); - this.sendCommandToSubscribers( - new SecondaryActionCommand(this.game.displayToWorldCoordinates(center)), - ); - } else if (event.touches.length === 0) { - this.isJoystickActive = false; - this.joystick.parentElement?.removeChild(this.joystick); - this.sendCommandToSubscribers(new MoveActionCommand(vec2.create())); - } - }); - } -} diff --git a/frontend/src/scripts/commands/generators/keyboard-listener.ts b/frontend/src/scripts/commands/keyboard-listener.ts similarity index 53% rename from frontend/src/scripts/commands/generators/keyboard-listener.ts rename to frontend/src/scripts/commands/keyboard-listener.ts index 8e858f9..f16ce42 100644 --- a/frontend/src/scripts/commands/generators/keyboard-listener.ts +++ b/frontend/src/scripts/commands/keyboard-listener.ts @@ -4,22 +4,29 @@ import { CommandGenerator, MoveActionCommand } from 'shared'; export class KeyboardListener extends CommandGenerator { private keysDown: Set = new Set(); - constructor(target: HTMLElement) { + constructor() { super(); - target.addEventListener('keydown', (event: KeyboardEvent) => { - const key = this.normalize(event.key); - this.keysDown.add(key); - this.generateCommands(); - }); - - target.addEventListener('keyup', (event: KeyboardEvent) => { - const key = this.normalize(event.key); - this.keysDown.delete(key); - this.generateCommands(); - }); + addEventListener('keydown', this.keyDownListener); + addEventListener('keyup', this.keyUpListener); + addEventListener('blur', this.blurListener); } + private keyDownListener = (event: KeyboardEvent) => { + this.keysDown.add(event.key.toLowerCase()); + this.generateCommands(); + }; + + private keyUpListener = (event: KeyboardEvent) => { + this.keysDown.delete(event.key.toLowerCase()); + this.generateCommands(); + }; + + private blurListener = () => { + this.keysDown.clear(); + this.generateCommands(); + }; + private generateCommands() { const up = ~~( this.keysDown.has('w') || @@ -34,10 +41,13 @@ export class KeyboardListener extends CommandGenerator { if (vec2.squaredLength(movement) > 0) { vec2.normalize(movement, movement); } + this.sendCommandToSubscribers(new MoveActionCommand(movement)); } - private normalize(key: string): string { - return key.toLowerCase(); + public destroy() { + removeEventListener('keydown', this.keyDownListener); + removeEventListener('keyup', this.keyUpListener); + removeEventListener('blur', this.blurListener); } } diff --git a/frontend/src/scripts/commands/mouse-listener.ts b/frontend/src/scripts/commands/mouse-listener.ts new file mode 100644 index 0000000..cf8b69e --- /dev/null +++ b/frontend/src/scripts/commands/mouse-listener.ts @@ -0,0 +1,38 @@ +import { vec2 } from 'gl-matrix'; +import { CommandGenerator, PrimaryActionCommand, SecondaryActionCommand } from 'shared'; +import { Game } from '../game'; + +export class MouseListener extends CommandGenerator { + constructor(private readonly game: Game) { + super(); + + addEventListener('mousedown', this.mouseDownListener); + addEventListener('contextmenu', this.contextMenuListener); + } + + private mouseDownListener = (event: MouseEvent) => { + const position = this.positionFromEvent(event); + + if (event.button === 0) { + this.sendCommandToSubscribers(new PrimaryActionCommand(position)); + } + }; + + private contextMenuListener = (event: MouseEvent) => { + event.preventDefault(); + + const position = this.positionFromEvent(event); + this.sendCommandToSubscribers(new SecondaryActionCommand(position)); + }; + + private positionFromEvent(event: MouseEvent): vec2 { + return this.game.displayToWorldCoordinates( + vec2.fromValues(event.clientX, event.clientY), + ); + } + + public destroy() { + removeEventListener('mousedown', this.mouseDownListener); + removeEventListener('contextmenu', this.contextMenuListener); + } +} diff --git a/frontend/src/scripts/commands/touch-listener.ts b/frontend/src/scripts/commands/touch-listener.ts new file mode 100644 index 0000000..09c5c53 --- /dev/null +++ b/frontend/src/scripts/commands/touch-listener.ts @@ -0,0 +1,104 @@ +import { vec2 } from 'gl-matrix'; +import { + CommandGenerator, + SecondaryActionCommand, + MoveActionCommand, + last, +} from 'shared'; +import { Game } from '../game'; + +export class TouchListener extends CommandGenerator { + private static readonly deadZone = 8; + private static readonly deltaScaling = 0.4; + + private joystick: HTMLElement; + private joystickButton: HTMLElement; + private isJoystickActive = false; + private touchStartPosition!: vec2; + + constructor(private overlay: HTMLElement, private readonly game: Game) { + super(); + + this.joystick = document.createElement('div'); + this.joystick.className = 'joystick'; + this.joystickButton = document.createElement('div'); + this.joystick.appendChild(this.joystickButton); + + addEventListener('touchstart', this.touchStartListener); + addEventListener('touchmove', this.touchMoveListener); + addEventListener('touchend', this.touchEndListener); + } + + private touchStartListener = (event: TouchEvent) => { + event.preventDefault(); + if (this.isJoystickActive) { + const center = vec2.fromValues( + last(event.touches)!.clientX, + last(event.touches)!.clientY, + ); + this.sendCommandToSubscribers( + new SecondaryActionCommand(this.game.displayToWorldCoordinates(center)), + ); + } else { + this.touchStartPosition = vec2.fromValues( + event.touches[0].clientX, + event.touches[0].clientY, + ); + } + }; + + private touchMoveListener = (event: TouchEvent) => { + event.preventDefault(); + + const touchPosition = vec2.fromValues( + event.touches[0].clientX, + event.touches[0].clientY, + ); + + const delta = vec2.subtract(vec2.create(), touchPosition, this.touchStartPosition); + vec2.scale(delta, delta, TouchListener.deltaScaling); + const deltaLength = vec2.length(delta); + + if (!this.isJoystickActive && deltaLength > TouchListener.deadZone) { + this.isJoystickActive = true; + this.overlay.appendChild(this.joystick); + this.joystickButton.style.transform = `translateX(-50%) translateY(-50%)`; + this.joystick.style.transform = `translateX(${this.touchStartPosition.x}px) translateY(${this.touchStartPosition.y}px) translateX(-50%) translateY(-50%)`; + } + + const maxLength = 20; + vec2.scale(delta, delta, Math.min(1, maxLength / deltaLength)); + this.joystickButton.style.transform = `translateX(${delta.x}px) translateY(${delta.y}px) translateX(-50%) translateY(-50%)`; + + vec2.set(delta, delta.x, -delta.y); + if (deltaLength > TouchListener.deadZone) { + this.sendCommandToSubscribers(new MoveActionCommand(vec2.normalize(delta, delta))); + } else { + this.sendCommandToSubscribers(new MoveActionCommand(vec2.create())); + } + }; + + private touchEndListener = (event: TouchEvent) => { + event.preventDefault(); + + if (!this.isJoystickActive) { + const center = vec2.fromValues( + event.changedTouches[0].clientX, + event.changedTouches[0].clientY, + ); + this.sendCommandToSubscribers( + new SecondaryActionCommand(this.game.displayToWorldCoordinates(center)), + ); + } else if (event.touches.length === 0) { + this.isJoystickActive = false; + this.joystick.parentElement?.removeChild(this.joystick); + this.sendCommandToSubscribers(new MoveActionCommand(vec2.create())); + } + }; + + public destroy() { + removeEventListener('touchstart', this.touchStartListener); + removeEventListener('touchmove', this.touchMoveListener); + removeEventListener('touchend', this.touchEndListener); + } +} diff --git a/frontend/src/scripts/commands/types/move-to.ts b/frontend/src/scripts/commands/types/move-to.ts deleted file mode 100644 index ed15ddb..0000000 --- a/frontend/src/scripts/commands/types/move-to.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { vec2 } from 'gl-matrix'; -import { Command } from 'shared'; - -export class MoveToCommand extends Command { - public constructor(public readonly position: vec2) { - super(); - } -} diff --git a/frontend/src/scripts/commands/types/render.ts b/frontend/src/scripts/commands/types/render.ts deleted file mode 100644 index 54a7563..0000000 --- a/frontend/src/scripts/commands/types/render.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Renderer } from 'sdf-2d'; -import { Command } from 'shared'; - -export class RenderCommand extends Command { - public constructor(public readonly renderer: Renderer) { - super(); - } -} diff --git a/frontend/src/scripts/config/configuration.ts b/frontend/src/scripts/configuration.ts similarity index 100% rename from frontend/src/scripts/config/configuration.ts rename to frontend/src/scripts/configuration.ts diff --git a/frontend/src/scripts/game.ts b/frontend/src/scripts/game.ts index 34f99df..9e74750 100644 --- a/frontend/src/scripts/game.ts +++ b/frontend/src/scripts/game.ts @@ -1,28 +1,26 @@ import { vec2 } from 'gl-matrix'; import { Renderer, renderNoise } from 'sdf-2d'; import { - broadcastCommands, deserialize, TransportEvents, SetAspectRatioActionCommand, PlayerInformation, - PlayerDiedCommand, UpdateOtherPlayerDirections, clamp, UpdateGameState, - GameEnd, + GameEndCommand, CharacterTeam, ServerAnnouncement, - GameStart, + GameStartCommand, CommandReceiver, CommandExecutors, Command, } from 'shared'; import io from 'socket.io-client'; -import { KeyboardListener } from './commands/generators/keyboard-listener'; -import { MouseListener } from './commands/generators/mouse-listener'; -import { TouchJoystickListener } from './commands/generators/touch-joystick-listener'; -import { CommandReceiverSocket } from './commands/receivers/command-receiver-socket'; +import { KeyboardListener } from './commands/keyboard-listener'; +import { MouseListener } from './commands/mouse-listener'; +import { TouchListener } from './commands/touch-listener'; +import { CommandSocket } from './commands/command-socket'; import { startAnimation } from './start-animation'; import { PlayerDecision } from './join-form-handler'; import { GameObjectContainer } from './objects/game-object-container'; @@ -38,12 +36,16 @@ export class Game extends CommandReceiver { public started: Promise; private resolveStarted!: () => unknown; + private keyboardListener: KeyboardListener; + private mouseListener: MouseListener; + private touchListener: TouchListener; + private declaPlanetCountElement = document.createElement('div'); private redPlanetCountElement = document.createElement('div'); private announcementText = document.createElement('h2'); private progressBar = document.createElement('div'); private arrows: { [id: number]: HTMLElement } = {}; - private socketReceiver!: CommandReceiverSocket; + private socketReceiver!: CommandSocket; constructor( private readonly playerDecision: PlayerDecision, @@ -56,6 +58,10 @@ export class Game extends CommandReceiver { this.progressBar.className = 'planet-progress'; this.progressBar.appendChild(this.declaPlanetCountElement); this.progressBar.appendChild(this.redPlanetCountElement); + + this.keyboardListener = new KeyboardListener(); + this.mouseListener = new MouseListener(this); + this.touchListener = new TouchListener(this.overlay, this); } private initialize() { @@ -96,15 +102,13 @@ export class Game extends CommandReceiver { commands.forEach((c) => this.sendCommand(c)); }); - this.socketReceiver = new CommandReceiverSocket(this.socket); - broadcastCommands( - [ - new KeyboardListener(document.body), - new MouseListener(this.canvas, this), - new TouchJoystickListener(this.canvas, this.overlay, this), - ], - [this.socketReceiver], - ); + this.socketReceiver = new CommandSocket(this.socket); + this.keyboardListener.clearSubscribers(); + this.keyboardListener.subscribe(this.socketReceiver); + this.mouseListener.clearSubscribers(); + this.mouseListener.subscribe(this.socketReceiver); + this.touchListener.clearSubscribers(); + this.touchListener.subscribe(this.socketReceiver); this.isBetweenGames = false; @@ -123,16 +127,15 @@ export class Game extends CommandReceiver { protected commandExecutors: CommandExecutors = { [ServerAnnouncement.type]: (c: ServerAnnouncement) => (this.lastAnnouncementText = c.text), - [PlayerDiedCommand.type]: (c: PlayerDiedCommand) => VibrationHandler.vibrate(150), [UpdateGameState.type]: (c: UpdateGameState) => (this.lastGameState = c), - [GameEnd.type]: (c: GameEnd) => { + [GameEndCommand.type]: (c: GameEndCommand) => { const team = `${c.winningTeam}`; this.lastAnnouncementText = `Team ${team} won 🎉`; this.isEnding = true; }, [UpdateOtherPlayerDirections.type]: (c: UpdateOtherPlayerDirections) => (this.lastOtherPlayerDirections = c), - [GameStart.type]: this.initialize.bind(this), + [GameStartCommand.type]: this.initialize.bind(this), }; private lastOtherPlayerDirections?: UpdateOtherPlayerDirections; @@ -195,18 +198,19 @@ export class Game extends CommandReceiver { public async start(): Promise { const noiseTexture = await renderNoise([256, 256], 2, 1); + this.initialize(); + await startAnimation(this.canvas, this.gameLoop.bind(this), noiseTexture); this.socket.close(); this.overlay.innerHTML = ''; + this.keyboardListener.destroy(); + this.mouseListener.destroy(); + this.touchListener.destroy(); } public displayToWorldCoordinates(p: vec2): vec2 { - const result = this.renderer?.displayToWorldCoordinates(p); - if (!result) { - return vec2.create(); - } - return result; + return this.renderer?.displayToWorldCoordinates(p) ?? vec2.create(); } public aspectRatioChanged(aspectRatio: number) { diff --git a/frontend/src/scripts/join-form-handler.ts b/frontend/src/scripts/join-form-handler.ts index 3af5463..0ac28b9 100644 --- a/frontend/src/scripts/join-form-handler.ts +++ b/frontend/src/scripts/join-form-handler.ts @@ -1,6 +1,6 @@ import { ServerInformation, serverInformationEndpoint, TransportEvents } from 'shared'; import io from 'socket.io-client'; -import { Configuration } from './config/configuration'; +import { Configuration } from './configuration'; import parser from 'socket.io-msgpack-parser'; import { SoundHandler, Sounds } from './sound-handler'; diff --git a/frontend/src/scripts/objects/lamp-view.ts b/frontend/src/scripts/objects/lamp-view.ts index df87fdf..e44c87d 100644 --- a/frontend/src/scripts/objects/lamp-view.ts +++ b/frontend/src/scripts/objects/lamp-view.ts @@ -1,16 +1,11 @@ import { vec2, vec3 } from 'gl-matrix'; import { CircleLight, Renderer } from 'sdf-2d'; import { CommandExecutors, Id, LampBase, UpdateProperty } from 'shared'; -import { RenderCommand } from '../commands/types/render'; import { ViewObject } from './view-object'; export class LampView extends LampBase implements ViewObject { private light: CircleLight; - protected commandExecutors: CommandExecutors = { - [RenderCommand.type]: (c: RenderCommand) => c.renderer.addDrawable(this.light), - }; - constructor(id: Id, center: vec2, color: vec3, lightness: number) { super(id, center, color, lightness); this.light = new CircleLight(center, color, lightness); diff --git a/frontend/src/scripts/objects/planet-view.ts b/frontend/src/scripts/objects/planet-view.ts index 8bed4bb..85fe69a 100644 --- a/frontend/src/scripts/objects/planet-view.ts +++ b/frontend/src/scripts/objects/planet-view.ts @@ -1,7 +1,6 @@ import { vec2 } from 'gl-matrix'; import { Renderer } from 'sdf-2d'; -import { CommandExecutors, Id, Random, PlanetBase, UpdateProperty } from 'shared'; -import { RenderCommand } from '../commands/types/render'; +import { Id, Random, PlanetBase, UpdateProperty } from 'shared'; import { PlanetShape } from '../shapes/planet-shape'; import { ViewObject } from './view-object'; @@ -17,10 +16,6 @@ export class PlanetView extends PlanetBase implements ViewObject { private shape: PlanetShape; private ownershipProgess: HTMLElement; - protected commandExecutors: CommandExecutors = { - [RenderCommand.type]: (c: RenderCommand) => c.renderer.addDrawable(this.shape), - }; - constructor(id: Id, vertices: Array, ownership: number) { super(id, vertices); this.shape = new PlanetShape(vertices, ownership); diff --git a/frontend/src/scripts/objects/player-character-view.ts b/frontend/src/scripts/objects/player-character-view.ts index d2b804a..f257f1e 100644 --- a/frontend/src/scripts/objects/player-character-view.ts +++ b/frontend/src/scripts/objects/player-character-view.ts @@ -19,7 +19,6 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje private nameElement: HTMLElement = document.createElement('div'); private statsElement: HTMLElement = document.createElement('div'); private healthElement: HTMLElement = document.createElement('div'); - private previousHealth; public isMainCharacter = false; @@ -45,7 +44,6 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje this.rightFootExtrapolator = new CircleExtrapolator(this.rightFoot!); this.headExtrapolator = new CircleExtrapolator(this.head!); - this.previousHealth = this.health; this.nameElement.className = 'player-tag ' + this.team; this.nameElement.innerText = this.name; this.nameElement.appendChild(this.healthElement); @@ -77,17 +75,19 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje Sounds.hit, (0.4 * 2 * (previousHealth - health)) / settings.playerMaxStrength, ); + + if (this.isMainCharacter) { + VibrationHandler.vibrate(Math.min(200, (previousHealth - this.health) * 4)); + } + } + + public kill() { + if (this.isMainCharacter) { + VibrationHandler.vibrate(150); + } } public step(deltaTimeInSeconds: number): void { - if (this.previousHealth > this.health) { - if (this.isMainCharacter) { - VibrationHandler.vibrate(Math.min(200, (this.previousHealth - this.health) * 4)); - } - - this.previousHealth = this.health; - } - this.head! = this.headExtrapolator.getValue(deltaTimeInSeconds); this.leftFoot! = this.leftFootExtrapolator.getValue(deltaTimeInSeconds); this.rightFoot! = this.rightFootExtrapolator.getValue(deltaTimeInSeconds); diff --git a/shared/src/commands/broadcast-commands.ts b/shared/src/commands/broadcast-commands.ts deleted file mode 100644 index a5da5b5..0000000 --- a/shared/src/commands/broadcast-commands.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { CommandReceiver } from './command-receiver'; -import { CommandGenerator } from './command-generator'; - -export const broadcastCommands = ( - commandGenerators: Array, - commandReceivers: Array, -) => commandReceivers.forEach((r) => commandGenerators.forEach((g) => g.subscribe(r))); diff --git a/shared/src/commands/command-generator.ts b/shared/src/commands/command-generator.ts index fb576d1..0ca004a 100644 --- a/shared/src/commands/command-generator.ts +++ b/shared/src/commands/command-generator.ts @@ -1,13 +1,17 @@ import { CommandReceiver } from './command-receiver'; import { Command } from './command'; -export class CommandGenerator { +export abstract class CommandGenerator { private subscribers: Array = []; public subscribe(subscriber: CommandReceiver): void { this.subscribers.push(subscriber); } + public clearSubscribers(): void { + this.subscribers = []; + } + protected sendCommandToSubscribers(command: Command): void { this.subscribers.forEach((s) => s.sendCommand(command)); } diff --git a/shared/src/commands/command-receiver.ts b/shared/src/commands/command-receiver.ts index 6495573..b066af8 100644 --- a/shared/src/commands/command-receiver.ts +++ b/shared/src/commands/command-receiver.ts @@ -14,7 +14,7 @@ export abstract class CommandReceiver { const commandType = command.type; if (Object.prototype.hasOwnProperty.call(this.commandExecutors, commandType)) { - this.commandExecutors[commandType](command); + this.commandExecutors[commandType]!(command); } else { this.defaultCommandExecutor(command); } diff --git a/shared/src/commands/types/move-action.ts b/shared/src/commands/types/actions/move-action.ts similarity index 67% rename from shared/src/commands/types/move-action.ts rename to shared/src/commands/types/actions/move-action.ts index 3e49e4b..67674fe 100644 --- a/shared/src/commands/types/move-action.ts +++ b/shared/src/commands/types/actions/move-action.ts @@ -1,6 +1,6 @@ import { vec2 } from 'gl-matrix'; -import { serializable } from '../../transport/serialization/serializable'; -import { Command } from '../command'; +import { serializable } from '../../../serialization/serializable'; +import { Command } from '../../command'; @serializable export class MoveActionCommand extends Command { diff --git a/shared/src/commands/types/primary-action.ts b/shared/src/commands/types/actions/primary-action.ts similarity index 68% rename from shared/src/commands/types/primary-action.ts rename to shared/src/commands/types/actions/primary-action.ts index fdc49f4..268828b 100644 --- a/shared/src/commands/types/primary-action.ts +++ b/shared/src/commands/types/actions/primary-action.ts @@ -1,6 +1,6 @@ import { vec2 } from 'gl-matrix'; -import { serializable } from '../../transport/serialization/serializable'; -import { Command } from '../command'; +import { serializable } from '../../../serialization/serializable'; +import { Command } from '../../command'; @serializable export class PrimaryActionCommand extends Command { diff --git a/shared/src/commands/types/secondary-action.ts b/shared/src/commands/types/actions/secondary-action.ts similarity index 68% rename from shared/src/commands/types/secondary-action.ts rename to shared/src/commands/types/actions/secondary-action.ts index 0edb39e..3ca0293 100644 --- a/shared/src/commands/types/secondary-action.ts +++ b/shared/src/commands/types/actions/secondary-action.ts @@ -1,6 +1,6 @@ import { vec2 } from 'gl-matrix'; -import { serializable } from '../../transport/serialization/serializable'; -import { Command } from '../command'; +import { serializable } from '../../../serialization/serializable'; +import { Command } from '../../command'; @serializable export class SecondaryActionCommand extends Command { diff --git a/shared/src/commands/types/set-aspect-ratio-action.ts b/shared/src/commands/types/actions/set-aspect-ratio-action.ts similarity index 66% rename from shared/src/commands/types/set-aspect-ratio-action.ts rename to shared/src/commands/types/actions/set-aspect-ratio-action.ts index 0806165..f2b0d5c 100644 --- a/shared/src/commands/types/set-aspect-ratio-action.ts +++ b/shared/src/commands/types/actions/set-aspect-ratio-action.ts @@ -1,5 +1,5 @@ -import { serializable } from '../../transport/serialization/serializable'; -import { Command } from '../command'; +import { serializable } from '../../../serialization/serializable'; +import { Command } from '../../command'; @serializable export class SetAspectRatioActionCommand extends Command { diff --git a/shared/src/commands/types/create-objects.ts b/shared/src/commands/types/create-objects.ts index 2579d8c..108dc06 100644 --- a/shared/src/commands/types/create-objects.ts +++ b/shared/src/commands/types/create-objects.ts @@ -1,5 +1,5 @@ import { GameObject } from '../../objects/game-object'; -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable diff --git a/shared/src/commands/types/create-player.ts b/shared/src/commands/types/create-player.ts index cab3b03..bd3694c 100644 --- a/shared/src/commands/types/create-player.ts +++ b/shared/src/commands/types/create-player.ts @@ -1,5 +1,5 @@ import { PlayerCharacterBase } from '../../objects/types/player-character-base'; -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable diff --git a/shared/src/commands/types/delete-objects.ts b/shared/src/commands/types/delete-objects.ts index 8a672e3..6bf9ecf 100644 --- a/shared/src/commands/types/delete-objects.ts +++ b/shared/src/commands/types/delete-objects.ts @@ -1,5 +1,5 @@ -import { Id } from '../../transport/identity'; -import { serializable } from '../../transport/serialization/serializable'; +import { Id } from '../../communication/id'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable diff --git a/shared/src/commands/types/game-end.ts b/shared/src/commands/types/game-end.ts index b6c1ca6..ba8c732 100644 --- a/shared/src/commands/types/game-end.ts +++ b/shared/src/commands/types/game-end.ts @@ -1,9 +1,9 @@ import { CharacterTeam } from '../../objects/types/character-team'; -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable -export class GameEnd extends Command { +export class GameEndCommand extends Command { constructor( public readonly winningTeam: CharacterTeam, public readonly endCardLengthInSeconds: number, diff --git a/shared/src/commands/types/game-start.ts b/shared/src/commands/types/game-start.ts index 602670e..15f14c8 100644 --- a/shared/src/commands/types/game-start.ts +++ b/shared/src/commands/types/game-start.ts @@ -1,8 +1,8 @@ -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable -export class GameStart extends Command { +export class GameStartCommand extends Command { constructor() { super(); } diff --git a/shared/src/commands/types/player-died.ts b/shared/src/commands/types/player-died.ts deleted file mode 100644 index bf43e0d..0000000 --- a/shared/src/commands/types/player-died.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { vec2 } from 'gl-matrix'; -import { serializable } from '../../transport/serialization/serializable'; -import { Command } from '../command'; - -@serializable -export class PlayerDiedCommand extends Command { - public constructor(public readonly timeout: number) { - super(); - } - - public toArray(): Array { - return [this.timeout]; - } -} diff --git a/shared/src/commands/types/property-updates-for-objects.ts b/shared/src/commands/types/property-updates-for-objects.ts new file mode 100644 index 0000000..a9e4985 --- /dev/null +++ b/shared/src/commands/types/property-updates-for-objects.ts @@ -0,0 +1,14 @@ +import { PropertyUpdatesForObject } from '../../objects/game-object'; +import { serializable } from '../../serialization/serializable'; +import { Command } from '../command'; + +@serializable +export class PropertyUpdatesForObjects extends Command { + constructor(public readonly updates: Array) { + super(); + } + + public toArray(): Array { + return [this.updates]; + } +} diff --git a/shared/src/commands/types/remote-calls-for-object.ts b/shared/src/commands/types/remote-calls-for-object.ts deleted file mode 100644 index f1713df..0000000 --- a/shared/src/commands/types/remote-calls-for-object.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { serializable } from '../../transport/serialization/serializable'; -import { Command } from '../command'; -import { RemoteCallsForObject } from './remote-calls-for-objects'; - -@serializable -export class RemoteCallsForObjects extends Command { - constructor(public readonly callsForObjects: Array) { - super(); - } - - public toArray(): Array { - return [this.callsForObjects]; - } -} diff --git a/shared/src/commands/types/remote-calls-for-objects.ts b/shared/src/commands/types/remote-calls-for-objects.ts index f50f0c1..3296ccb 100644 --- a/shared/src/commands/types/remote-calls-for-objects.ts +++ b/shared/src/commands/types/remote-calls-for-objects.ts @@ -1,15 +1,23 @@ -import { Id } from '../../main'; -import { RemoteCall } from '../../objects/game-object'; -import { serializable } from '../../transport/serialization/serializable'; +import { Id, RemoteCall } from '../../main'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable -export class RemoteCallsForObject extends Command { - constructor(public readonly id: Id, public readonly calls: Array) { - super(); - } +export class RemoteCallsForObject { + constructor(public readonly id: Id, public readonly calls: Array) {} public toArray(): Array { return [this.id, this.calls]; } } + +@serializable +export class RemoteCallsForObjects extends Command { + constructor(public readonly callsForObjects: Array) { + super(); + } + + public toArray(): Array { + return [this.callsForObjects]; + } +} diff --git a/shared/src/commands/types/server-announcement.ts b/shared/src/commands/types/server-announcement.ts index ae3c32a..49d2be0 100644 --- a/shared/src/commands/types/server-announcement.ts +++ b/shared/src/commands/types/server-announcement.ts @@ -1,4 +1,4 @@ -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable diff --git a/shared/src/commands/types/ternary-action.ts b/shared/src/commands/types/ternary-action.ts deleted file mode 100644 index 547396f..0000000 --- a/shared/src/commands/types/ternary-action.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { vec2 } from 'gl-matrix'; -import { serializable } from '../../transport/serialization/serializable'; -import { Command } from '../command'; - -@serializable -export class TernaryActionCommand extends Command { - public constructor(public readonly position: vec2) { - super(); - } - - public toArray(): Array { - return [this.position]; - } -} diff --git a/shared/src/commands/types/update-game-state.ts b/shared/src/commands/types/update-game-state.ts index 991a502..40ce4e1 100644 --- a/shared/src/commands/types/update-game-state.ts +++ b/shared/src/commands/types/update-game-state.ts @@ -1,4 +1,4 @@ -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable diff --git a/shared/src/commands/types/update-other-player-directions.ts b/shared/src/commands/types/update-other-player-directions.ts index ab0775e..0afe481 100644 --- a/shared/src/commands/types/update-other-player-directions.ts +++ b/shared/src/commands/types/update-other-player-directions.ts @@ -1,7 +1,7 @@ import { vec2 } from 'gl-matrix'; +import { Id } from '../../communication/id'; import { CharacterTeam } from '../../objects/types/character-team'; -import { Id } from '../../transport/identity'; -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { Command } from '../command'; @serializable diff --git a/shared/src/helper/id.ts b/shared/src/communication/id.ts similarity index 70% rename from shared/src/helper/id.ts rename to shared/src/communication/id.ts index fdd153e..c6c1e4d 100644 --- a/shared/src/helper/id.ts +++ b/shared/src/communication/id.ts @@ -1,3 +1,5 @@ +export type Id = number | null; + let currentId = 0; export const id = (): number => { diff --git a/shared/src/transport/transport-events.ts b/shared/src/communication/transport-events.ts similarity index 100% rename from shared/src/transport/transport-events.ts rename to shared/src/communication/transport-events.ts diff --git a/shared/src/helper/circle.ts b/shared/src/helper/circle.ts index 1fb223b..466bf8d 100644 --- a/shared/src/helper/circle.ts +++ b/shared/src/helper/circle.ts @@ -1,5 +1,5 @@ import { vec2 } from 'gl-matrix'; -import { serializable } from '../transport/serialization/serializable'; +import { serializable } from '../serialization/serializable'; @serializable export class Circle { diff --git a/shared/src/helper/hsl.ts b/shared/src/helper/hsl.ts index 6bece56..5b4aaf0 100644 --- a/shared/src/helper/hsl.ts +++ b/shared/src/helper/hsl.ts @@ -1,6 +1,7 @@ import { vec3 } from 'gl-matrix'; import { rgb } from './rgb'; +// source: https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion export const hsl = (hue: number, saturation: number, lightness: number): vec3 => { hue /= 360; saturation /= 100; diff --git a/shared/src/helper/pretty-print.ts b/shared/src/helper/pretty-print.ts deleted file mode 100644 index 704a6fc..0000000 --- a/shared/src/helper/pretty-print.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const prettyPrint = (o: any): string => - JSON.stringify(o, (_, v) => (v.toFixed ? Number(v.toFixed(3)) : v), ' ') - .replace(/("|,|{|^\n)/g, '') - .replace(/(\W*}\n?)+/g, '\n\n'); diff --git a/shared/src/helper/random.ts b/shared/src/helper/random.ts index 4f800bc..d7a0de3 100644 --- a/shared/src/helper/random.ts +++ b/shared/src/helper/random.ts @@ -1,4 +1,4 @@ -// src +// source // https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript // Mulberry32 diff --git a/shared/src/helper/rectangle.ts b/shared/src/helper/rectangle.ts index 25ea3cb..a4e4b35 100644 --- a/shared/src/helper/rectangle.ts +++ b/shared/src/helper/rectangle.ts @@ -1,5 +1,5 @@ import { vec2 } from 'gl-matrix'; -import { serializable } from '../transport/serialization/serializable'; +import { serializable } from '../serialization/serializable'; @serializable export class Rectangle { diff --git a/shared/src/helper/to-percent.ts b/shared/src/helper/to-percent.ts deleted file mode 100644 index 0047591..0000000 --- a/shared/src/helper/to-percent.ts +++ /dev/null @@ -1 +0,0 @@ -export const toPercent = (value: number) => `${Math.round(value * 100)}%`; diff --git a/shared/src/main.ts b/shared/src/main.ts index 2c35c0a..c525200 100644 --- a/shared/src/main.ts +++ b/shared/src/main.ts @@ -2,24 +2,21 @@ export * from './commands/command'; export * from './commands/types/create-objects'; export * from './commands/types/create-player'; export * from './commands/types/delete-objects'; -export * from './commands/types/ternary-action'; -export * from './commands/types/move-action'; -export * from './commands/types/primary-action'; export * from './commands/types/remote-calls-for-objects'; -export * from './commands/types/remote-calls-for-object'; -export * from './commands/types/player-died'; export * from './commands/types/server-announcement'; +export * from './commands/types/property-updates-for-objects'; export * from './commands/types/game-end'; export * from './commands/types/game-start'; export * from './commands/types/update-other-player-directions'; -export * from './commands/types/secondary-action'; export * from './commands/types/update-game-state'; export * from './commands/command-receiver'; export * from './commands/types/update-other-player-directions'; export * from './commands/command-executors'; export * from './commands/command-generator'; -export * from './commands/broadcast-commands'; -export * from './commands/types/set-aspect-ratio-action'; +export * from './commands/types/actions/move-action'; +export * from './commands/types/actions/primary-action'; +export * from './commands/types/actions/set-aspect-ratio-action'; +export * from './commands/types/actions/secondary-action'; export * from './helper/array'; export * from './helper/last'; export * from './helper/rgb'; @@ -28,27 +25,25 @@ export * from './helper/rgb255'; export * from './helper/mix-rgb'; export * from './helper/clamp'; export * from './helper/calculate-view-area'; -export * from './helper/pretty-print'; export * from './helper/circle'; export * from './helper/rectangle'; export * from './helper/mix'; export * from './helper/random'; -export * from './helper/id'; +export * from './communication/id'; export * from './communication/server-information'; export * from './communication/player-information'; export * from './helper/rotate-90-deg'; export * from './helper/rotate-minus-90-deg'; export * from './objects/game-object'; -export * from './transport/serialization/deserialize'; -export * from './transport/serialization/serialize'; -export * from './transport/serialization/serializes-to'; -export * from './transport/serialization/serializable'; -export * from './transport/serialization/override-deserialization'; +export * from './serialization/deserialize'; +export * from './serialization/serialize'; +export * from './serialization/serializes-to'; +export * from './serialization/serializable'; +export * from './serialization/override-deserialization'; export * from './objects/types/character-team'; export * from './objects/types/player-character-base'; export * from './objects/types/lamp-base'; export * from './objects/types/planet-base'; export * from './objects/types/projectile-base'; export * from './settings'; -export * from './transport/transport-events'; -export * from './transport/identity'; +export * from './communication/transport-events'; diff --git a/shared/src/objects/game-object.ts b/shared/src/objects/game-object.ts index 23e1ef8..c3cb937 100644 --- a/shared/src/objects/game-object.ts +++ b/shared/src/objects/game-object.ts @@ -1,6 +1,6 @@ import { Command } from '../commands/command'; -import { Id } from '../transport/identity'; -import { serializable } from '../transport/serialization/serializable'; +import { Id } from '../communication/id'; +import { serializable } from '../serialization/serializable'; @serializable export class RemoteCall { @@ -33,17 +33,6 @@ export class PropertyUpdatesForObject { } } -@serializable -export class PropertyUpdatesForObjects extends Command { - constructor(public readonly updates: Array) { - super(); - } - - public toArray(): Array { - return [this.updates]; - } -} - export abstract class GameObject { private remoteCalls: Array = []; diff --git a/shared/src/objects/types/planet-base.ts b/shared/src/objects/types/planet-base.ts index 2fe77f3..57aafd6 100644 --- a/shared/src/objects/types/planet-base.ts +++ b/shared/src/objects/types/planet-base.ts @@ -1,9 +1,9 @@ import { vec2 } from 'gl-matrix'; import { Random } from '../../helper/random'; import { settings } from '../../settings'; -import { Id } from '../../transport/identity'; -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { GameObject } from '../game-object'; +import { Id } from '../../communication/id'; @serializable export class PlanetBase extends GameObject { diff --git a/shared/src/objects/types/player-character-base.ts b/shared/src/objects/types/player-character-base.ts index 1c32675..0872bb3 100644 --- a/shared/src/objects/types/player-character-base.ts +++ b/shared/src/objects/types/player-character-base.ts @@ -1,6 +1,6 @@ +import { Id } from '../../communication/id'; import { Circle } from '../../helper/circle'; -import { Id } from '../../transport/identity'; -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { GameObject } from '../game-object'; import { CharacterTeam } from './character-team'; @@ -26,6 +26,8 @@ export class PlayerCharacterBase extends GameObject { this.health = health; } + public kill() {} + public setKillCount(killCount: number) { this.killCount = killCount; } diff --git a/shared/src/objects/types/projectile-base.ts b/shared/src/objects/types/projectile-base.ts index b03ca0f..07036c9 100644 --- a/shared/src/objects/types/projectile-base.ts +++ b/shared/src/objects/types/projectile-base.ts @@ -1,9 +1,9 @@ import { vec2 } from 'gl-matrix'; import { settings } from '../../settings'; -import { Id } from '../../transport/identity'; -import { serializable } from '../../transport/serialization/serializable'; +import { serializable } from '../../serialization/serializable'; import { GameObject } from '../game-object'; import { CharacterTeam } from './character-team'; +import { Id } from '../../communication/id'; @serializable export class ProjectileBase extends GameObject { diff --git a/shared/src/transport/serialization/deserializable-class.ts b/shared/src/serialization/deserializable-class.ts similarity index 100% rename from shared/src/transport/serialization/deserializable-class.ts rename to shared/src/serialization/deserializable-class.ts diff --git a/shared/src/transport/serialization/deserialize.ts b/shared/src/serialization/deserialize.ts similarity index 84% rename from shared/src/transport/serialization/deserialize.ts rename to shared/src/serialization/deserialize.ts index 84e6479..6ba3a67 100644 --- a/shared/src/transport/serialization/deserialize.ts +++ b/shared/src/serialization/deserialize.ts @@ -6,8 +6,7 @@ export const deserialize = (json: string): any => { const possibleType = v[0]; const overridableConstructor = serializableMapping.get(possibleType); if (overridableConstructor) { - v.shift(); - return new overridableConstructor.constructor(...v); + return new overridableConstructor.constructor(...v.slice(1)); } return v; } diff --git a/shared/src/serialization/mangled-type-key.ts b/shared/src/serialization/mangled-type-key.ts new file mode 100644 index 0000000..2460725 --- /dev/null +++ b/shared/src/serialization/mangled-type-key.ts @@ -0,0 +1 @@ +export const mangledTypeKey = '__serializable_type'; diff --git a/shared/src/transport/serialization/override-deserialization.ts b/shared/src/serialization/override-deserialization.ts similarity index 100% rename from shared/src/transport/serialization/override-deserialization.ts rename to shared/src/serialization/override-deserialization.ts diff --git a/shared/src/transport/serialization/serializable-class.ts b/shared/src/serialization/serializable-class.ts similarity index 100% rename from shared/src/transport/serialization/serializable-class.ts rename to shared/src/serialization/serializable-class.ts diff --git a/shared/src/transport/serialization/serializable-mapping.ts b/shared/src/serialization/serializable-mapping.ts similarity index 90% rename from shared/src/transport/serialization/serializable-mapping.ts rename to shared/src/serialization/serializable-mapping.ts index 7460b39..5c9d7cb 100644 --- a/shared/src/transport/serialization/serializable-mapping.ts +++ b/shared/src/serialization/serializable-mapping.ts @@ -1,8 +1,5 @@ import { DeserializableClass } from './deserializable-class'; -/** - * @internal - */ export const serializableMapping = new Map< string, { diff --git a/shared/src/transport/serialization/serializable.ts b/shared/src/serialization/serializable.ts similarity index 63% rename from shared/src/transport/serialization/serializable.ts rename to shared/src/serialization/serializable.ts index 7c12536..4605f65 100644 --- a/shared/src/transport/serialization/serializable.ts +++ b/shared/src/serialization/serializable.ts @@ -1,3 +1,4 @@ +import { mangledTypeKey } from './mangled-type-key'; import { SerializableClass } from './serializable-class'; import { serializableMapping } from './serializable-mapping'; @@ -9,8 +10,8 @@ export const serializable = (type: SerializableClass): any => { }); } - Object.defineProperty(type, '__serializable_type', { value: type.name }); - Object.defineProperty(type.prototype, '__serializable_type', { value: type.name }); + Object.defineProperty(type, mangledTypeKey, { value: type.name }); + Object.defineProperty(type.prototype, mangledTypeKey, { value: type.name }); return type; }; diff --git a/shared/src/transport/serialization/serialize.ts b/shared/src/serialization/serialize.ts similarity index 63% rename from shared/src/transport/serialization/serialize.ts rename to shared/src/serialization/serialize.ts index 7ad26bd..bd72218 100644 --- a/shared/src/transport/serialization/serialize.ts +++ b/shared/src/serialization/serialize.ts @@ -1,8 +1,10 @@ +import { mangledTypeKey } from './mangled-type-key'; + export const serialize = (object: any): string => { return JSON.stringify(object, (_, value) => { - if (value?.__serializable_type) { + if (value && value[mangledTypeKey]) { const props = value.toArray() as Array; - props.unshift(value.__serializable_type); + props.unshift(value[mangledTypeKey]); return props; } return value?.toFixed ? Number(value.toFixed(3)) : value; diff --git a/shared/src/transport/serialization/serializes-to.ts b/shared/src/serialization/serializes-to.ts similarity index 70% rename from shared/src/transport/serialization/serializes-to.ts rename to shared/src/serialization/serializes-to.ts index ab994ca..47030de 100644 --- a/shared/src/transport/serialization/serializes-to.ts +++ b/shared/src/serialization/serializes-to.ts @@ -1,3 +1,4 @@ +import { mangledTypeKey } from './mangled-type-key'; import { SerializableClass } from './serializable-class'; import { serializableMapping } from './serializable-mapping'; @@ -10,8 +11,8 @@ export const serializesTo = (target: SerializableClass) => { }); } - Object.defineProperty(actual, '__serializable_type', { value: target.name }); - Object.defineProperty(actual.prototype, '__serializable_type', { + Object.defineProperty(actual, mangledTypeKey, { value: target.name }); + Object.defineProperty(actual.prototype, mangledTypeKey, { value: target.name, }); diff --git a/shared/src/settings.ts b/shared/src/settings.ts index 12809c4..efbf51d 100644 --- a/shared/src/settings.ts +++ b/shared/src/settings.ts @@ -15,7 +15,6 @@ export const settings = { maxVelocityX: 1000, maxVelocityY: 1000, radiusSteps: 500, - gravityScalingForProjectiles: 5, spawnDespawnTime: 0.7, worldRadius: 10000, objectsOnCircleLength: 0.002, @@ -112,5 +111,5 @@ export const settings = { palette: [declaColor, neutralColor, redColor], paletteDim: [declaColorDim, neutralColor, redColorDim], targetPhysicsDeltaTimeInSeconds: 1 / 100, - inViewAreaSize: 1920 * 1080 * 3, + inViewAreaSize: 1920 * 1080 * 4, }; diff --git a/shared/src/transport/identity.ts b/shared/src/transport/identity.ts deleted file mode 100644 index 53b0c34..0000000 --- a/shared/src/transport/identity.ts +++ /dev/null @@ -1 +0,0 @@ -export type Id = number | null;