This commit is contained in:
schmelczerandras 2020-11-04 16:01:17 +01:00
commit 99cdb62928
76 changed files with 338 additions and 431 deletions

View file

@ -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: /

View file

@ -1,6 +0,0 @@
.dockerignore
Dockerfile
**/node_modules
node_modules
**/package-lock.json
package-lock.json

View file

@ -1,6 +1,9 @@
{ {
"cSpell.words": [ "cSpell.words": [
"Deserializable",
"Deserialization",
"decla", "decla",
"overridable",
"serializable" "serializable"
] ]
} }

View file

@ -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" ]

View file

@ -1,6 +1,6 @@
{ {
"name": "declared-server", "name": "declared-server",
"version": "0.0.11", "version": "0.0.12",
"description": "Game server for decla.red", "description": "Game server for decla.red",
"keywords": [], "keywords": [],
"author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)", "author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)",

View 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));
};

View file

@ -7,14 +7,12 @@ import {
ServerInformation, ServerInformation,
PlayerInformation, PlayerInformation,
UpdateGameState, UpdateGameState,
serialize,
CharacterTeam, CharacterTeam,
GameEnd, GameEndCommand,
GameStart, GameStartCommand,
Command, Command,
last,
} from 'shared'; } from 'shared';
import { createWorld } from './map/create-world'; import { createWorld } from './create-world';
import { DeltaTimeCalculator } from './helper/delta-time-calculator'; import { DeltaTimeCalculator } from './helper/delta-time-calculator';
import { Options } from './options'; import { Options } from './options';
import { PlayerContainer } from './players/player-container'; import { PlayerContainer } from './players/player-container';
@ -52,7 +50,7 @@ export class GameServer {
this.redPoints = 0; this.redPoints = 0;
this.isInEndGame = false; this.isInEndGame = false;
this.timeScaling = 1; this.timeScaling = 1;
previousPlayers?.queueCommandForEachClient(new GameStart()); previousPlayers?.queueCommandForEachClient(new GameStartCommand());
previousPlayers?.sendQueuedCommands(); previousPlayers?.sendQueuedCommands();
} }
@ -119,7 +117,7 @@ export class GameServer {
this.isInEndGame = true; this.isInEndGame = true;
const endTitleLength = 6000; const endTitleLength = 6000;
this.players.queueCommandForEachClient( this.players.queueCommandForEachClient(
new GameEnd(winningTeam, endTitleLength / 1000, true), new GameEndCommand(winningTeam, endTitleLength / 1000, true),
); );
setTimeout(() => this.destroy(), endTitleLength * 1.1); setTimeout(() => this.destroy(), endTitleLength * 1.1);
} }

View file

@ -1,5 +0,0 @@
export const getTimeInMilliseconds = (): number => {
const [seconds, nanoSeconds] = process.hrtime();
return seconds * 1000 + nanoSeconds / 1000 / 1000;
};

View file

@ -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 max = Math.PI * 2;
const da = (a1 - a0) % max; const possibleDistance = (to - from) % max;
return ((2 * da) % max) - da; const shortedDistance = ((2 * possibleDistance) % max) - possibleDistance;
}; return from + shortedDistance * q;
export const interpolateAngles = (a0: number, a1: number, t: number) => {
return a0 + shortAngleDist(a0, a1) * t;
}; };

View file

@ -9,7 +9,7 @@ import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
import { import {
ReactsToCollision, ReactsToCollision,
reactsToCollision, reactsToCollision,
} from '../physics/physicals/reacts-to-collision'; } from '../physics/functions/reacts-to-collision';
@serializesTo(Circle) @serializesTo(Circle)
export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollision { export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollision {

View file

@ -21,7 +21,7 @@ import { interpolateAngles } from '../helper/interpolate-angles';
import { forceAtPosition } from '../physics/functions/force-at-position'; import { forceAtPosition } from '../physics/functions/force-at-position';
import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle'; import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle';
import { PlanetPhysical } from './planet-physical'; import { PlanetPhysical } from './planet-physical';
import { ReactsToCollision } from '../physics/physicals/reacts-to-collision'; import { ReactsToCollision } from '../physics/functions/reacts-to-collision';
@serializesTo(PlayerCharacterBase) @serializesTo(PlayerCharacterBase)
export class PlayerCharacterPhysical export class PlayerCharacterPhysical
@ -479,6 +479,7 @@ export class PlayerCharacterPhysical
public kill() { public kill() {
this.isDestroyed = true; this.isDestroyed = true;
this.remoteCall('kill');
} }
private destroy() { private destroy() {

View file

@ -14,7 +14,7 @@ import { CirclePhysical } from './circle-physical';
import { DynamicPhysical } from '../physics/physicals/dynamic-physical'; import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
import { PhysicalContainer } from '../physics/containers/physical-container'; import { PhysicalContainer } from '../physics/containers/physical-container';
import { PlanetPhysical } from './planet-physical'; 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 { PlayerCharacterPhysical } from './player-character-physical';
import { moveCircle } from '../physics/functions/move-circle'; import { moveCircle } from '../physics/functions/move-circle';
@ -114,24 +114,6 @@ export class ProjectilePhysical
return; 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); vec2.copy(this.object.velocity, this.velocity);
const { velocity } = this.object.step2(deltaTime); const { velocity } = this.object.step2(deltaTime);
vec2.copy(this.velocity, velocity); vec2.copy(this.velocity, velocity);

View file

@ -1,6 +1,7 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
export class BoundingBoxBase { // axes aligned
export abstract class BoundingBoxBase {
constructor( constructor(
protected _xMin: number = 0, protected _xMin: number = 0,
protected _xMax: number = 0, protected _xMax: number = 0,

View file

@ -1,7 +1,7 @@
import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base'; import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base';
import { StaticPhysical } from '../physicals/static-physical'; 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 { class Node {
public left: Node | null = null; public left: Node | null = null;
public right: Node | null = null; public right: Node | null = null;

View file

@ -1,8 +1,9 @@
import { Circle } from 'shared'; import { Circle } from 'shared';
import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base'; import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base';
import { ImmutableBoundingBox } from '../bounding-boxes/immutable-bounding-box';
export const getBoundingBoxOfCircle = (circle: Circle): BoundingBoxBase => export const getBoundingBoxOfCircle = (circle: Circle): BoundingBoxBase =>
new BoundingBoxBase( new ImmutableBoundingBox(
circle.center.x - circle.radius, circle.center.x - circle.radius,
circle.center.x + circle.radius, circle.center.x + circle.radius,
circle.center.y - circle.radius, circle.center.y - circle.radius,

View file

@ -1,7 +1,7 @@
import { vec2 } from 'gl-matrix'; import { vec2 } from 'gl-matrix';
import { Circle, GameObject, rotate90Deg } from 'shared'; import { Circle, GameObject, rotate90Deg } from 'shared';
import { CirclePhysical } from '../../objects/circle-physical'; 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 { evaluateSdf } from './evaluate-sdf';
import { Physical } from '../physicals/physical'; import { Physical } from '../physicals/physical';

View 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;

View file

@ -10,7 +10,6 @@ import {
SetAspectRatioActionCommand, SetAspectRatioActionCommand,
calculateViewArea, calculateViewArea,
SecondaryActionCommand, SecondaryActionCommand,
PlayerDiedCommand,
settings, settings,
PlayerInformation, PlayerInformation,
CharacterTeam, CharacterTeam,
@ -23,21 +22,21 @@ import {
ServerAnnouncement, ServerAnnouncement,
PropertyUpdatesForObjects, PropertyUpdatesForObjects,
PropertyUpdatesForObject, PropertyUpdatesForObject,
PrimaryActionCommand,
} from 'shared'; } from 'shared';
import { getTimeInMilliseconds } from '../helper/get-time-in-milliseconds';
import { BoundingBox } from '../physics/bounding-boxes/bounding-box'; import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
import { PhysicalContainer } from '../physics/containers/physical-container'; import { PhysicalContainer } from '../physics/containers/physical-container';
import { PlayerCharacterPhysical } from '../objects/player-character-physical'; import { PlayerCharacterPhysical } from '../objects/player-character-physical';
import { PlayerContainer } from './player-container'; import { PlayerContainer } from './player-container';
import { PlayerBase } from './player-base'; import { PlayerBase } from './player-base';
import { DeltaTimeCalculator } from '../helper/delta-time-calculator';
export class Player extends PlayerBase { export class Player extends PlayerBase {
private aspectRatio: number = 16 / 9; private aspectRatio: number = 16 / 9;
private isActive = true; private isActive = true;
private objectsPreviouslyInViewArea: Array<GameObject> = []; private objectsPreviouslyInViewArea: Array<GameObject> = [];
private pingDeltaTime = new DeltaTimeCalculator();
private pingTime?: number;
private _latency?: number; private _latency?: number;
protected commandExecutors: CommandExecutors = { protected commandExecutors: CommandExecutors = {
@ -45,7 +44,7 @@ export class Player extends PlayerBase {
(this.aspectRatio = v.aspectRatio), (this.aspectRatio = v.aspectRatio),
[MoveActionCommand.type]: (c: MoveActionCommand) => [MoveActionCommand.type]: (c: MoveActionCommand) =>
this.character?.handleMovementAction(c), this.character?.handleMovementAction(c),
[SecondaryActionCommand.type]: (c: SecondaryActionCommand) => { [PrimaryActionCommand.type]: (c: PrimaryActionCommand) => {
this.character?.shootTowards(c.position); this.character?.shootTowards(c.position);
}, },
}; };
@ -63,7 +62,7 @@ export class Player extends PlayerBase {
socket.on( socket.on(
TransportEvents.Pong, TransportEvents.Pong,
() => (this._latency = getTimeInMilliseconds() - this.pingTime!), () => (this._latency = this.pingDeltaTime.getNextDeltaTimeInSeconds()),
); );
this.measureLatency(); this.measureLatency();
@ -71,8 +70,9 @@ export class Player extends PlayerBase {
} }
public measureLatency() { public measureLatency() {
this.pingTime = getTimeInMilliseconds(); this.pingDeltaTime.getNextDeltaTimeInSeconds(true);
this.socket.emit(TransportEvents.Ping); this.socket.emit(TransportEvents.Ping);
if (this.isActive) { if (this.isActive) {
setTimeout(this.measureLatency.bind(this), 10000); setTimeout(this.measureLatency.bind(this), 10000);
} }
@ -106,7 +106,6 @@ export class Player extends PlayerBase {
this.sumDeaths++; this.sumDeaths++;
this.sumKills = this.character.killCount; this.sumKills = this.character.killCount;
this.queueCommandSend(new PlayerDiedCommand(settings.playerDiedTimeout));
this.character = null; this.character = null;
this.timeUntilRespawn = settings.playerDiedTimeout; this.timeUntilRespawn = settings.playerDiedTimeout;
} }

View 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 = [];
}
}
}

View file

@ -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