Refactor
This commit is contained in:
parent
1ce961d6c2
commit
99cdb62928
76 changed files with 338 additions and 431 deletions
15
.do/apps.yml
15
.do/apps.yml
|
|
@ -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: /
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
.dockerignore
|
||||
Dockerfile
|
||||
**/node_modules
|
||||
node_modules
|
||||
**/package-lock.json
|
||||
package-lock.json
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,6 +1,9 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"Deserializable",
|
||||
"Deserialization",
|
||||
"decla",
|
||||
"overridable",
|
||||
"serializable"
|
||||
]
|
||||
}
|
||||
|
|
@ -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" ]
|
||||
|
|
@ -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 <andras@schmelczer.dev> (https://schmelczer.dev/)",
|
||||
|
|
|
|||
74
backend/src/create-world.ts
Normal file
74
backend/src/create-world.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
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';
|
||||
|
||||
export const createWorld = (objectContainer: PhysicalContainer, worldRadius: number) => {
|
||||
const objects: Array<Physical> = [];
|
||||
const lights: Array<Physical> = [];
|
||||
|
||||
for (let r = 0; r < worldRadius; r += settings.radiusSteps) {
|
||||
const circumference = 2 * Math.PI * r;
|
||||
const stepCount = circumference * settings.objectsOnCircleLength;
|
||||
for (let rad = 0; rad < 2 * Math.PI; rad += (2 * Math.PI) / stepCount) {
|
||||
const position = vec2.rotate(
|
||||
vec2.create(),
|
||||
vec2.fromValues(r, 0),
|
||||
vec2.create(),
|
||||
rad,
|
||||
);
|
||||
|
||||
if (objects.length !== 0 && Random.getRandom() > 0.5) {
|
||||
if (
|
||||
evaluateSdf(position, objects) > 200 &&
|
||||
!lights.find((l) => l.distance(position) < 2500)
|
||||
) {
|
||||
lights.push(
|
||||
new LampPhysical(
|
||||
position,
|
||||
hsl(
|
||||
(rad / (2 * Math.PI)) * 360,
|
||||
Random.getRandomInRange(50, 100),
|
||||
Random.getRandomInRange(40, 50),
|
||||
),
|
||||
Random.getRandomInRange(0.35, 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
evaluateSdf(position, objects) > 1400 &&
|
||||
!lights.find((l) => l.distance(position) < 1700)
|
||||
) {
|
||||
const planet =
|
||||
objects.length === 0
|
||||
? new PlanetPhysical(
|
||||
PlanetBase.createPlanetVertices(
|
||||
position,
|
||||
Random.getRandomInRange(1600, 2400),
|
||||
Random.getRandomInRange(1600, 2400),
|
||||
Random.getRandomInRange(80, 300),
|
||||
),
|
||||
)
|
||||
: new PlanetPhysical(
|
||||
PlanetBase.createPlanetVertices(
|
||||
position,
|
||||
Random.getRandomInRange(300, 1600),
|
||||
Random.getRandomInRange(300, 1600),
|
||||
Random.getRandomInRange(20, 100),
|
||||
),
|
||||
);
|
||||
|
||||
objects.push(planet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('Generated planet count', objects.length);
|
||||
console.log('Generated light count', lights.length);
|
||||
|
||||
[...objects, ...lights].forEach((o) => objectContainer.addObject(o));
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
export const getTimeInMilliseconds = (): number => {
|
||||
const [seconds, nanoSeconds] = process.hrtime();
|
||||
|
||||
return seconds * 1000 + nanoSeconds / 1000 / 1000;
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
|
|
|
|||
8
backend/src/physics/functions/reacts-to-collision.ts
Normal file
8
backend/src/physics/functions/reacts-to-collision.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { GameObject } from 'shared';
|
||||
|
||||
export interface ReactsToCollision {
|
||||
onCollision(other: GameObject): void;
|
||||
}
|
||||
|
||||
export const reactsToCollision = (a: any): a is ReactsToCollision =>
|
||||
a && 'onCollision' in a;
|
||||
|
|
@ -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<GameObject> = [];
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
19
frontend/src/scripts/commands/command-socket.ts
Normal file
19
frontend/src/scripts/commands/command-socket.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { Command, CommandReceiver, serialize, TransportEvents } from 'shared';
|
||||
|
||||
export class CommandSocket extends CommandReceiver {
|
||||
constructor(private readonly socket: SocketIOClient.Socket) {
|
||||
super();
|
||||
}
|
||||
|
||||
private commandQueue: Array<Command> = [];
|
||||
protected defaultCommandExecutor(command: Command) {
|
||||
this.commandQueue.push(command);
|
||||
}
|
||||
|
||||
public sendQueuedCommands() {
|
||||
if (this.commandQueue.length > 0) {
|
||||
this.socket.emit(TransportEvents.PlayerToServer, serialize(this.commandQueue));
|
||||
this.commandQueue = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue