Optimize performance
This commit is contained in:
parent
e4cd3284b7
commit
b8ef90c100
31 changed files with 319 additions and 206 deletions
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Program",
|
||||||
|
"skipFiles": ["<node_internals>/**"],
|
||||||
|
"program": "${workspaceFolder}/backend/dist/main.js",
|
||||||
|
"outFiles": ["${workspaceFolder}/**/*.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -5,4 +5,4 @@ RUN npm i -g declared-server
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["--port=3000", "--name=Docker server", "--seed=500"]
|
CMD ["--port=3000", "--name=Docker server", "--seed=500"]
|
||||||
ENTRYPOINT [ "declared-server" ]
|
ENTRYPOINT [ "NODE_ENV=production node", "declared-server" ]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "declared-server",
|
"name": "declared-server",
|
||||||
"version": "0.0.7",
|
"version": "0.0.8",
|
||||||
"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/)",
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
"gl-matrix": "^3.3.0",
|
"gl-matrix": "^3.3.0",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"socket.io": "^2.3.0",
|
"socket.io": "^2.3.0",
|
||||||
|
"socket.io-msgpack-parser": "^2.0.0",
|
||||||
"uws": "^10.148.1"
|
"uws": "^10.148.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ export const defaultOptions: Options = {
|
||||||
port: 3000,
|
port: 3000,
|
||||||
name: 'Test server',
|
name: 'Test server',
|
||||||
playerLimit: 8,
|
playerLimit: 8,
|
||||||
|
npcCount: 4,
|
||||||
seed: 0,
|
seed: 0,
|
||||||
scoreLimit: 500,
|
scoreLimit: 500,
|
||||||
worldSize: 15000,
|
worldSize: 8000,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
CharacterTeam,
|
CharacterTeam,
|
||||||
GameEnd,
|
GameEnd,
|
||||||
GameStart,
|
GameStart,
|
||||||
|
Command,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { createWorld } from './map/create-world';
|
import { createWorld } from './map/create-world';
|
||||||
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||||
|
|
@ -39,14 +40,19 @@ export class GameServer {
|
||||||
this.objects = new PhysicalContainer();
|
this.objects = new PhysicalContainer();
|
||||||
createWorld(this.objects, this.options.worldSize);
|
createWorld(this.objects, this.options.worldSize);
|
||||||
this.objects.initialize();
|
this.objects.initialize();
|
||||||
this.players = new PlayerContainer(this.objects, this.options.playerLimit);
|
this.players = new PlayerContainer(
|
||||||
|
this.objects,
|
||||||
|
this.options.playerLimit,
|
||||||
|
this.options.npcCount,
|
||||||
|
);
|
||||||
this.deltaTimeCalculator = new DeltaTimeCalculator();
|
this.deltaTimeCalculator = new DeltaTimeCalculator();
|
||||||
this.deltaTimes = [];
|
this.deltaTimes = [];
|
||||||
this.declaPoints = 0;
|
this.declaPoints = 0;
|
||||||
this.redPoints = 0;
|
this.redPoints = 0;
|
||||||
this.isInEndGame = false;
|
this.isInEndGame = false;
|
||||||
this.timeScaling = 1;
|
this.timeScaling = 1;
|
||||||
previousPlayers?.sendOnSocket(serialize(new GameStart()));
|
previousPlayers?.queueCommandForEachClient(new GameStart());
|
||||||
|
previousPlayers?.sendQueuedCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private readonly io: ioserver.Server, private options: Options) {
|
constructor(private readonly io: ioserver.Server, private options: Options) {
|
||||||
|
|
@ -60,8 +66,12 @@ export class GameServer {
|
||||||
try {
|
try {
|
||||||
const player = this.players.createPlayer(playerInfo, socket);
|
const player = this.players.createPlayer(playerInfo, socket);
|
||||||
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
||||||
const command = deserialize(json);
|
try {
|
||||||
player.sendCommand(command);
|
const commands: Array<Command> = deserialize(json);
|
||||||
|
commands.forEach((c) => player.sendCommand(c));
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error while processing command', e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sendServerStateUpdate();
|
this.sendServerStateUpdate();
|
||||||
|
|
@ -94,9 +104,6 @@ export class GameServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private updatePoints() {
|
private updatePoints() {
|
||||||
if (this.isInEndGame) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { decla, red } = this.objects.getPointsGenerated();
|
const { decla, red } = this.objects.getPointsGenerated();
|
||||||
this.declaPoints += decla;
|
this.declaPoints += decla;
|
||||||
this.redPoints += red;
|
this.redPoints += red;
|
||||||
|
|
@ -110,8 +117,8 @@ export class GameServer {
|
||||||
private endGame(winningTeam: CharacterTeam) {
|
private endGame(winningTeam: CharacterTeam) {
|
||||||
this.isInEndGame = true;
|
this.isInEndGame = true;
|
||||||
const endTitleLength = 6000;
|
const endTitleLength = 6000;
|
||||||
this.players.sendOnSocket(
|
this.players.queueCommandForEachClient(
|
||||||
serialize(new GameEnd(winningTeam, endTitleLength / 1000, true)),
|
new GameEnd(winningTeam, endTitleLength / 1000, true),
|
||||||
);
|
);
|
||||||
setTimeout(() => this.destroy(), endTitleLength * 1.1);
|
setTimeout(() => this.destroy(), endTitleLength * 1.1);
|
||||||
}
|
}
|
||||||
|
|
@ -120,6 +127,7 @@ export class GameServer {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private timeSinceLastPointUpdate = 0;
|
||||||
private handlePhysics() {
|
private handlePhysics() {
|
||||||
let delta = this.deltaTimeCalculator.getNextDeltaTimeInSeconds();
|
let delta = this.deltaTimeCalculator.getNextDeltaTimeInSeconds();
|
||||||
const framesBetweenDeltaTimeCalculation = 1000;
|
const framesBetweenDeltaTimeCalculation = 1000;
|
||||||
|
|
@ -137,26 +145,31 @@ export class GameServer {
|
||||||
this.deltaTimes = [];
|
this.deltaTimes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((this.timeSinceLastServerStateUpdate += delta) > 5) {
|
if ((this.timeSinceLastServerStateUpdate += delta) > 4) {
|
||||||
this.timeSinceLastServerStateUpdate = 0;
|
this.timeSinceLastServerStateUpdate = 0;
|
||||||
this.sendServerStateUpdate();
|
this.sendServerStateUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((this.timeSinceLastPointUpdate += delta) > 0.5) {
|
||||||
|
this.timeSinceLastPointUpdate = 0;
|
||||||
|
this.players.queueCommandForEachClient(
|
||||||
|
new UpdateGameState(this.declaPoints, this.redPoints, this.options.scoreLimit),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isInEndGame) {
|
if (this.isInEndGame) {
|
||||||
this.timeScaling *= Math.pow(settings.endGameDeltaScaling, delta);
|
this.timeScaling *= Math.pow(settings.endGameDeltaScaling, delta);
|
||||||
delta /= this.timeScaling;
|
delta /= this.timeScaling;
|
||||||
|
} else {
|
||||||
|
this.updatePoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.objects.stepObjects(delta);
|
this.objects.stepObjects(delta);
|
||||||
this.updatePoints();
|
|
||||||
this.players.sendOnSocket(
|
|
||||||
serialize(
|
|
||||||
new UpdateGameState(this.declaPoints, this.redPoints, this.options.scoreLimit),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.players.step(delta);
|
this.players.step(delta);
|
||||||
this.objects.resetRemoteCalls();
|
this.objects.resetRemoteCalls();
|
||||||
|
|
||||||
|
this.players.sendQueuedCommands();
|
||||||
|
|
||||||
const physicsDelta = this.deltaTimeCalculator.getDeltaTimeInSeconds() * 1000;
|
const physicsDelta = this.deltaTimeCalculator.getDeltaTimeInSeconds() * 1000;
|
||||||
this.deltaTimes.push(physicsDelta);
|
this.deltaTimes.push(physicsDelta);
|
||||||
const sleepTime = settings.targetPhysicsDeltaTimeInMilliseconds - physicsDelta;
|
const sleepTime = settings.targetPhysicsDeltaTimeInMilliseconds - physicsDelta;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import minimist from 'minimist';
|
||||||
import { glMatrix } from 'gl-matrix';
|
import { glMatrix } from 'gl-matrix';
|
||||||
import { GameServer } from './game-server';
|
import { GameServer } from './game-server';
|
||||||
import { defaultOptions } from './default-options';
|
import { defaultOptions } from './default-options';
|
||||||
|
import parser from 'socket.io-msgpack-parser';
|
||||||
|
|
||||||
glMatrix.setMatrixArrayType(Array);
|
glMatrix.setMatrixArrayType(Array);
|
||||||
applyArrayPlugins();
|
applyArrayPlugins();
|
||||||
|
|
@ -21,7 +22,7 @@ Random.seed = options.seed;
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = new Server(app);
|
const server = new Server(app);
|
||||||
const io = ioserver(server);
|
const io = ioserver(server, { parser } as any);
|
||||||
|
|
||||||
const gameServer = new GameServer(io, options);
|
const gameServer = new GameServer(io, options);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,10 @@ export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollisio
|
||||||
|
|
||||||
const delta = vec2.scale(vec2.create(), this.velocity, deltaTimeInSeconds);
|
const delta = vec2.scale(vec2.create(), this.velocity, deltaTimeInSeconds);
|
||||||
|
|
||||||
|
this.radius += vec2.length(delta);
|
||||||
|
const intersecting = this.container.findIntersecting(this.boundingBox);
|
||||||
|
this.radius -= vec2.length(delta);
|
||||||
|
|
||||||
const stepCount = Math.ceil(vec2.length(delta) / settings.physicsMaxStep);
|
const stepCount = Math.ceil(vec2.length(delta) / settings.physicsMaxStep);
|
||||||
vec2.scale(delta, delta, 1 / stepCount);
|
vec2.scale(delta, delta, 1 / stepCount);
|
||||||
|
|
||||||
|
|
@ -115,9 +119,6 @@ export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollisio
|
||||||
this.velocity,
|
this.velocity,
|
||||||
deltaTimeInSeconds / stepCount,
|
deltaTimeInSeconds / stepCount,
|
||||||
);
|
);
|
||||||
this.radius += vec2.length(distance);
|
|
||||||
const intersecting = this.container.findIntersecting(this.boundingBox);
|
|
||||||
this.radius -= vec2.length(distance);
|
|
||||||
|
|
||||||
const { normal, hitSurface, hitObject } = moveCircle(
|
const { normal, hitSurface, hitObject } = moveCircle(
|
||||||
this,
|
this,
|
||||||
|
|
@ -144,16 +145,16 @@ export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollisio
|
||||||
}
|
}
|
||||||
|
|
||||||
public tryMove(delta: vec2): GameObject | undefined {
|
public tryMove(delta: vec2): GameObject | undefined {
|
||||||
|
this.radius += vec2.length(delta);
|
||||||
|
const intersecting = this.container.findIntersecting(this.boundingBox);
|
||||||
|
this.radius -= vec2.length(delta);
|
||||||
|
|
||||||
const stepCount = Math.ceil(vec2.length(delta) / settings.physicsMaxStep);
|
const stepCount = Math.ceil(vec2.length(delta) / settings.physicsMaxStep);
|
||||||
vec2.scale(delta, delta, 1 / stepCount);
|
vec2.scale(delta, delta, 1 / stepCount);
|
||||||
|
|
||||||
let lastHit: GameObject | undefined;
|
let lastHit: GameObject | undefined;
|
||||||
|
|
||||||
for (let i = 0; i < stepCount; i++) {
|
for (let i = 0; i < stepCount; i++) {
|
||||||
this.radius += vec2.length(delta);
|
|
||||||
const intersecting = this.container.findIntersecting(this.boundingBox);
|
|
||||||
this.radius -= vec2.length(delta);
|
|
||||||
|
|
||||||
const { tangent, hitSurface, hitObject } = moveCircle(
|
const { tangent, hitSurface, hitObject } = moveCircle(
|
||||||
this,
|
this,
|
||||||
vec2.clone(delta),
|
vec2.clone(delta),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ export interface Options {
|
||||||
name: string;
|
name: string;
|
||||||
playerLimit: number;
|
playerLimit: number;
|
||||||
scoreLimit: number;
|
scoreLimit: number;
|
||||||
|
npcCount: number;
|
||||||
seed: number;
|
seed: number;
|
||||||
worldSize: number;
|
worldSize: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
import {
|
import { CharacterTeam, PlayerInformation, Random, settings, Command } from 'shared';
|
||||||
CharacterTeam,
|
|
||||||
PlayerInformation,
|
|
||||||
Random,
|
|
||||||
TransportEvents,
|
|
||||||
settings,
|
|
||||||
} from 'shared';
|
|
||||||
import { PhysicalContainer } from '../physics/containers/physical-container';
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
import { NPC } from './npc';
|
import { NPC } from './npc';
|
||||||
import { Player } from './player';
|
import { Player } from './player';
|
||||||
|
|
@ -17,12 +11,16 @@ export class PlayerContainer {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly objects: PhysicalContainer,
|
private readonly objects: PhysicalContainer,
|
||||||
private readonly playerMaxCount: number,
|
private readonly playerMaxCount: number,
|
||||||
|
private readonly npcMaxCount: number,
|
||||||
) {
|
) {
|
||||||
this.createNPCs();
|
this.createNPCs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public createNPCs() {
|
public createNPCs() {
|
||||||
const newNpcCount = this.playerMaxCount - this._players.length - this._npcs.length;
|
const newNpcCount = Math.min(
|
||||||
|
this.playerMaxCount - this._players.length - this._npcs.length,
|
||||||
|
this.npcMaxCount - this._npcs.length,
|
||||||
|
);
|
||||||
for (let i = 0; i < newNpcCount; i++) {
|
for (let i = 0; i < newNpcCount; i++) {
|
||||||
const name = `BOT ${Random.choose(settings.npcNames)}`;
|
const name = `BOT ${Random.choose(settings.npcNames)}`;
|
||||||
this._npcs.push(
|
this._npcs.push(
|
||||||
|
|
@ -62,8 +60,12 @@ export class PlayerContainer {
|
||||||
this.players.forEach((p) => p.step(deltaTimeInSeconds));
|
this.players.forEach((p) => p.step(deltaTimeInSeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendOnSocket(message: any) {
|
public queueCommandForEachClient(command: Command) {
|
||||||
this._players.forEach((p) => p.socket.emit(TransportEvents.ServerToPlayer, message));
|
this._players.forEach((p) => p.queueCommandSend(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendQueuedCommands() {
|
||||||
|
this._players.forEach((p) => p.sendQueuedCommandsToClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTeamOfNextPlayer(isNpc = false): CharacterTeam {
|
private getTeamOfNextPlayer(isNpc = false): CharacterTeam {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export class Player extends PlayerBase {
|
||||||
playerContainer: PlayerContainer,
|
playerContainer: PlayerContainer,
|
||||||
objectContainer: PhysicalContainer,
|
objectContainer: PhysicalContainer,
|
||||||
team: CharacterTeam,
|
team: CharacterTeam,
|
||||||
public readonly socket: SocketIO.Socket,
|
private readonly socket: SocketIO.Socket,
|
||||||
) {
|
) {
|
||||||
super(playerInfo, playerContainer, objectContainer, team);
|
super(playerInfo, playerContainer, objectContainer, team);
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ export class Player extends PlayerBase {
|
||||||
super.createCharacter();
|
super.createCharacter();
|
||||||
|
|
||||||
this.objectsPreviouslyInViewArea.push(this.character!);
|
this.objectsPreviouslyInViewArea.push(this.character!);
|
||||||
this.sendToPlayer(new CreatePlayerCommand(this.character!));
|
this.queueCommandSend(new CreatePlayerCommand(this.character!));
|
||||||
}
|
}
|
||||||
|
|
||||||
private timeUntilRespawn = 0;
|
private timeUntilRespawn = 0;
|
||||||
|
|
@ -98,21 +98,18 @@ export class Player extends PlayerBase {
|
||||||
this.sumDeaths++;
|
this.sumDeaths++;
|
||||||
this.sumKills = this.character.killCount;
|
this.sumKills = this.character.killCount;
|
||||||
|
|
||||||
this.socket.emit(
|
this.queueCommandSend(new PlayerDiedCommand(settings.playerDiedTimeout));
|
||||||
TransportEvents.ServerToPlayer,
|
|
||||||
serialize(new PlayerDiedCommand(settings.playerDiedTimeout)),
|
|
||||||
);
|
|
||||||
this.character = null;
|
this.character = null;
|
||||||
this.timeUntilRespawn = settings.playerDiedTimeout;
|
this.timeUntilRespawn = settings.playerDiedTimeout;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.sendToPlayer(
|
this.queueCommandSend(
|
||||||
new ServerAnnouncement(`Reviving in ${Math.round(this.timeUntilRespawn)}…`),
|
new ServerAnnouncement(`Reviving in ${Math.round(this.timeUntilRespawn)}…`),
|
||||||
);
|
);
|
||||||
if ((this.timeUntilRespawn -= deltaTimeInSeconds) < 0) {
|
if ((this.timeUntilRespawn -= deltaTimeInSeconds) < 0) {
|
||||||
this.createCharacter();
|
this.createCharacter();
|
||||||
this.center = this.character!.center;
|
this.center = this.character!.center;
|
||||||
this.sendToPlayer(new ServerAnnouncement(''));
|
this.queueCommandSend(new ServerAnnouncement(''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,14 +133,16 @@ export class Player extends PlayerBase {
|
||||||
this.objectsPreviouslyInViewArea = objectsInViewArea;
|
this.objectsPreviouslyInViewArea = objectsInViewArea;
|
||||||
|
|
||||||
if (noLongerIntersecting.length > 0) {
|
if (noLongerIntersecting.length > 0) {
|
||||||
this.sendToPlayer(new DeleteObjectsCommand(noLongerIntersecting.map((g) => g.id)));
|
this.queueCommandSend(
|
||||||
|
new DeleteObjectsCommand(noLongerIntersecting.map((g) => g.id)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newlyIntersecting.length > 0) {
|
if (newlyIntersecting.length > 0) {
|
||||||
this.sendToPlayer(new CreateObjectsCommand(newlyIntersecting));
|
this.queueCommandSend(new CreateObjectsCommand(newlyIntersecting));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sendToPlayer(
|
this.queueCommandSend(
|
||||||
new RemoteCallsForObjects(
|
new RemoteCallsForObjects(
|
||||||
this.objectsPreviouslyInViewArea.map(
|
this.objectsPreviouslyInViewArea.map(
|
||||||
(g) => new RemoteCallsForObject(g.id, g.getRemoteCalls()),
|
(g) => new RemoteCallsForObject(g.id, g.getRemoteCalls()),
|
||||||
|
|
@ -151,7 +150,7 @@ export class Player extends PlayerBase {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.sendToPlayer(new UpdateOtherPlayerDirections(this.getOtherPlayers()));
|
this.queueCommandSend(new UpdateOtherPlayerDirections(this.getOtherPlayers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOtherPlayers(): Array<OtherPlayerDirection> {
|
private getOtherPlayers(): Array<OtherPlayerDirection> {
|
||||||
|
|
@ -185,8 +184,14 @@ export class Player extends PlayerBase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendToPlayer(command: Command) {
|
private commandsToBeSent: Array<Command> = [];
|
||||||
this.socket.emit(TransportEvents.ServerToPlayer, serialize(command));
|
public queueCommandSend(command: Command) {
|
||||||
|
this.commandsToBeSent.push(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendQueuedCommandsToClient() {
|
||||||
|
this.socket.emit(TransportEvents.ServerToPlayer, serialize(this.commandsToBeSent));
|
||||||
|
this.commandsToBeSent = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"lib": ["dom", "es2017"]
|
"lib": ["dom", "es2017"],
|
||||||
|
"typeRoots": ["./types", "./node_modules/@types"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts"]
|
"include": ["src/**/*.ts"],
|
||||||
|
"exclude": ["node_modules", "typings"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
backend/types/socket.io-msgpack-parser/index.d.ts
vendored
Normal file
1
backend/types/socket.io-msgpack-parser/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
declare module 'socket.io-msgpack-parser';
|
||||||
93
frontend/package-lock.json
generated
93
frontend/package-lock.json
generated
|
|
@ -33,9 +33,10 @@
|
||||||
"resolve-url-loader": "^3.1.1",
|
"resolve-url-loader": "^3.1.1",
|
||||||
"sass": "^1.27.0",
|
"sass": "^1.27.0",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"sdf-2d": "file:..\\..\\sdf-2d",
|
"sdf-2d": "^0.6.2",
|
||||||
"shared": "file:../shared",
|
"shared": "file:../shared",
|
||||||
"socket.io-client": "^2.3.1",
|
"socket.io-client": "^2.3.1",
|
||||||
|
"socket.io-msgpack-parser": "^2.0.0",
|
||||||
"source-map-loader": "^1.1.1",
|
"source-map-loader": "^1.1.1",
|
||||||
"svg-url-loader": "^6.0.0",
|
"svg-url-loader": "^6.0.0",
|
||||||
"terser-webpack-plugin": "^4.2.2",
|
"terser-webpack-plugin": "^4.2.2",
|
||||||
|
|
@ -46,32 +47,6 @@
|
||||||
"webpack-dev-server": "^3.11.0"
|
"webpack-dev-server": "^3.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"../../sdf-2d": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"gl-matrix": "^3.3.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@typescript-eslint/eslint-plugin": "^3.10.1",
|
|
||||||
"@typescript-eslint/parser": "^3.10.1",
|
|
||||||
"eslint": "^7.9.0",
|
|
||||||
"eslint-config-prettier": "^6.11.0",
|
|
||||||
"eslint-plugin-import": "^2.22.0",
|
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
|
||||||
"eslint-plugin-unused-imports": "^0.1.3",
|
|
||||||
"prettier": "^2.1.2",
|
|
||||||
"raw-loader": "^4.0.1",
|
|
||||||
"terser-webpack-plugin": "^2.3.8",
|
|
||||||
"ts-loader": "^8.0.3",
|
|
||||||
"typedoc": "^0.19.2",
|
|
||||||
"typedoc-plugin-extras": "^1.1.6",
|
|
||||||
"typescript": "^3.9.7",
|
|
||||||
"webpack": "^4.44.2",
|
|
||||||
"webpack-cli": "^3.3.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"../shared": {
|
"../shared": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|
@ -11687,6 +11662,12 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/notepack.io": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-9b5w3t5VSH6ZPosoYnyDONnUTF8o0UkBw7JLA6eBlYJWyGT1Q3vQa8Hmuj1/X6RYvHjjygBDgw6fJhe0JEojfw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/npmlog": {
|
"node_modules/npmlog": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||||
|
|
@ -15147,8 +15128,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sdf-2d": {
|
"node_modules/sdf-2d": {
|
||||||
"resolved": "../../sdf-2d",
|
"version": "0.6.2",
|
||||||
"link": true
|
"resolved": "https://registry.npmjs.org/sdf-2d/-/sdf-2d-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-VUeToQdZn96ZhliHTRcfHYGgQ/4TyUDicy6GaUug1skZdxmh/er9KRcihRIVjyiJg/6DGY87J71y+CcygwZqwA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"gl-matrix": "^3.3.0",
|
||||||
|
"resize-observer-polyfill": "^1.5.1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/select-hose": {
|
"node_modules/select-hose": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
|
@ -15700,6 +15687,16 @@
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-msgpack-parser": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-msgpack-parser/-/socket.io-msgpack-parser-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-T2/vHDxuKBHqmLDAFeJ714OJsV1QbeVo6WApx1gSOwXE96OVCJErOTyjzDnISMbn/yxLIrUex9vJv4IO5/skdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"notepack.io": "~2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/socket.io-parser": {
|
"node_modules/socket.io-parser": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
|
||||||
|
|
@ -28988,6 +28985,12 @@
|
||||||
"sort-keys": "^1.0.0"
|
"sort-keys": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"notepack.io": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-9b5w3t5VSH6ZPosoYnyDONnUTF8o0UkBw7JLA6eBlYJWyGT1Q3vQa8Hmuj1/X6RYvHjjygBDgw6fJhe0JEojfw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"npmlog": {
|
"npmlog": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||||
|
|
@ -31851,25 +31854,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sdf-2d": {
|
"sdf-2d": {
|
||||||
"version": "file:../../sdf-2d",
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sdf-2d/-/sdf-2d-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-VUeToQdZn96ZhliHTRcfHYGgQ/4TyUDicy6GaUug1skZdxmh/er9KRcihRIVjyiJg/6DGY87J71y+CcygwZqwA==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/eslint-plugin": "^3.10.1",
|
|
||||||
"@typescript-eslint/parser": "^3.10.1",
|
|
||||||
"eslint": "^7.9.0",
|
|
||||||
"eslint-config-prettier": "^6.11.0",
|
|
||||||
"eslint-plugin-import": "^2.22.0",
|
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
|
||||||
"eslint-plugin-unused-imports": "^0.1.3",
|
|
||||||
"gl-matrix": "^3.3.0",
|
"gl-matrix": "^3.3.0",
|
||||||
"prettier": "^2.1.2",
|
"resize-observer-polyfill": "^1.5.1"
|
||||||
"raw-loader": "^4.0.1",
|
|
||||||
"terser-webpack-plugin": "^2.3.8",
|
|
||||||
"ts-loader": "^8.0.3",
|
|
||||||
"typedoc": "^0.19.2",
|
|
||||||
"typedoc-plugin-extras": "^1.1.6",
|
|
||||||
"typescript": "^3.9.7",
|
|
||||||
"webpack": "^4.44.2",
|
|
||||||
"webpack-cli": "^3.3.11"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"select-hose": {
|
"select-hose": {
|
||||||
|
|
@ -32369,6 +32360,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"socket.io-msgpack-parser": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-msgpack-parser/-/socket.io-msgpack-parser-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-T2/vHDxuKBHqmLDAFeJ714OJsV1QbeVo6WApx1gSOwXE96OVCJErOTyjzDnISMbn/yxLIrUex9vJv4IO5/skdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"component-emitter": "~1.3.0",
|
||||||
|
"notepack.io": "~2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"socket.io-parser": {
|
"socket.io-parser": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
"sdf-2d": "^0.6.2",
|
"sdf-2d": "^0.6.2",
|
||||||
"shared": "file:../shared",
|
"shared": "file:../shared",
|
||||||
"socket.io-client": "^2.3.1",
|
"socket.io-client": "^2.3.1",
|
||||||
|
"socket.io-msgpack-parser": "^2.0.0",
|
||||||
"source-map-loader": "^1.1.1",
|
"source-map-loader": "^1.1.1",
|
||||||
"svg-url-loader": "^6.0.0",
|
"svg-url-loader": "^6.0.0",
|
||||||
"terser-webpack-plugin": "^4.2.2",
|
"terser-webpack-plugin": "^4.2.2",
|
||||||
|
|
|
||||||
|
|
@ -114,9 +114,8 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-tag {
|
.player-tag {
|
||||||
transform: translateX(-50%) translateY(-50%) rotate(-15deg);
|
|
||||||
transition: left 150ms, top 150ms;
|
|
||||||
border-radius: 1000px;
|
border-radius: 1000px;
|
||||||
|
transition: transform 200ms;
|
||||||
|
|
||||||
&.decla {
|
&.decla {
|
||||||
color: $bright-decla;
|
color: $bright-decla;
|
||||||
|
|
@ -163,8 +162,6 @@ body {
|
||||||
|
|
||||||
.ownership {
|
.ownership {
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
transform: translateX(-50%) translateY(-50%);
|
|
||||||
|
|
||||||
box-shadow: inset 0 0 3px 0px rgba(0, 0, 0, 0.2);
|
box-shadow: inset 0 0 3px 0px rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
@include square(50px);
|
@include square(50px);
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,11 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import {
|
import { CommandGenerator, SecondaryActionCommand, TernaryActionCommand } from 'shared';
|
||||||
CommandGenerator,
|
|
||||||
PrimaryActionCommand,
|
|
||||||
SecondaryActionCommand,
|
|
||||||
TernaryActionCommand,
|
|
||||||
} from 'shared';
|
|
||||||
import { Game } from '../../game';
|
import { Game } from '../../game';
|
||||||
|
|
||||||
export class MouseListener extends CommandGenerator {
|
export class MouseListener extends CommandGenerator {
|
||||||
constructor(target: HTMLElement, private readonly game: Game) {
|
constructor(target: HTMLElement, private readonly game: Game) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
target.addEventListener('mousemove', (event: MouseEvent) => {
|
|
||||||
const position = this.positionFromEvent(event);
|
|
||||||
this.sendCommandToSubcribers(new PrimaryActionCommand(position));
|
|
||||||
});
|
|
||||||
|
|
||||||
target.addEventListener('mousedown', (event: MouseEvent) => {
|
target.addEventListener('mousedown', (event: MouseEvent) => {
|
||||||
const position = this.positionFromEvent(event);
|
const position = this.positionFromEvent(event);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,15 @@ export class CommandReceiverSocket extends CommandReceiver {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private commandQueue: Array<Command> = [];
|
||||||
protected defaultCommandExecutor(command: Command) {
|
protected defaultCommandExecutor(command: Command) {
|
||||||
this.socket.emit(TransportEvents.PlayerToServer, serialize(command));
|
this.commandQueue.push(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendQueuedCommands() {
|
||||||
|
if (this.commandQueue.length > 0) {
|
||||||
|
this.socket.emit(TransportEvents.PlayerToServer, serialize(this.commandQueue));
|
||||||
|
this.commandQueue = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import { startAnimation } from './start-animation';
|
||||||
import { PlayerDecision } from './join-form-handler';
|
import { PlayerDecision } from './join-form-handler';
|
||||||
import { GameObjectContainer } from './objects/game-object-container';
|
import { GameObjectContainer } from './objects/game-object-container';
|
||||||
import { OptionsHandler } from './options-handler';
|
import { OptionsHandler } from './options-handler';
|
||||||
|
import parser from 'socket.io-msgpack-parser';
|
||||||
|
|
||||||
export class Game extends CommandReceiver {
|
export class Game extends CommandReceiver {
|
||||||
public gameObjects = new GameObjectContainer(this);
|
public gameObjects = new GameObjectContainer(this);
|
||||||
|
|
@ -43,6 +44,7 @@ export class Game extends CommandReceiver {
|
||||||
private announcementText = document.createElement('h2');
|
private announcementText = document.createElement('h2');
|
||||||
private progressBar = document.createElement('div');
|
private progressBar = document.createElement('div');
|
||||||
private arrowElements: Array<HTMLElement> = [];
|
private arrowElements: Array<HTMLElement> = [];
|
||||||
|
private socketReceiver!: CommandReceiverSocket;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly playerDecision: PlayerDecision,
|
private readonly playerDecision: PlayerDecision,
|
||||||
|
|
@ -71,7 +73,8 @@ export class Game extends CommandReceiver {
|
||||||
reconnectionDelayMax: 10000,
|
reconnectionDelayMax: 10000,
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
forceNew: true,
|
forceNew: true,
|
||||||
});
|
parser,
|
||||||
|
} as any);
|
||||||
|
|
||||||
this.socket.on('reconnect_attempt', () => {
|
this.socket.on('reconnect_attempt', () => {
|
||||||
this.socket.io.opts.transports = ['polling', 'websocket'];
|
this.socket.io.opts.transports = ['polling', 'websocket'];
|
||||||
|
|
@ -87,17 +90,19 @@ export class Game extends CommandReceiver {
|
||||||
this.socket.emit(TransportEvents.Pong);
|
this.socket.emit(TransportEvents.Pong);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on(TransportEvents.ServerToPlayer, (serialized: string) =>
|
this.socket.on(TransportEvents.ServerToPlayer, (serializedCommands: string) => {
|
||||||
this.sendCommand(deserialize(serialized)),
|
const commands: Array<Command> = deserialize(serializedCommands);
|
||||||
);
|
commands.forEach((c) => this.sendCommand(c));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socketReceiver = new CommandReceiverSocket(this.socket);
|
||||||
broadcastCommands(
|
broadcastCommands(
|
||||||
[
|
[
|
||||||
new KeyboardListener(document.body),
|
new KeyboardListener(document.body),
|
||||||
new MouseListener(this.canvas, this),
|
new MouseListener(this.canvas, this),
|
||||||
new TouchJoystickListener(this.canvas, this.overlay, this),
|
new TouchJoystickListener(this.canvas, this.overlay, this),
|
||||||
],
|
],
|
||||||
[this.gameObjects, new CommandReceiverSocket(this.socket)],
|
[this.socketReceiver],
|
||||||
);
|
);
|
||||||
|
|
||||||
this.isBetweenGames = false;
|
this.isBetweenGames = false;
|
||||||
|
|
@ -111,29 +116,31 @@ export class Game extends CommandReceiver {
|
||||||
this.gameObjects.sendCommand(c);
|
this.gameObjects.sendCommand(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lastGameState?: UpdateGameState;
|
||||||
|
|
||||||
|
private lastAnnouncementText = '';
|
||||||
protected commandExecutors: CommandExecutors = {
|
protected commandExecutors: CommandExecutors = {
|
||||||
[ServerAnnouncement.type]: (c: ServerAnnouncement) =>
|
[ServerAnnouncement.type]: (c: ServerAnnouncement) =>
|
||||||
(this.announcementText.innerText = c.text),
|
(this.lastAnnouncementText = c.text),
|
||||||
[PlayerDiedCommand.type]: (c: PlayerDiedCommand) => {
|
[PlayerDiedCommand.type]: (c: PlayerDiedCommand) => {
|
||||||
if (OptionsHandler.options.vibrationEnabled) {
|
if (OptionsHandler.options.vibrationEnabled) {
|
||||||
navigator.vibrate(150);
|
navigator.vibrate(150);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[UpdateGameState.type]: (c: UpdateGameState) => {
|
[UpdateGameState.type]: (c: UpdateGameState) => (this.lastGameState = c),
|
||||||
this.declaPlanetCountElement.style.width = (c.declaCount / c.limit) * 50 + '%';
|
|
||||||
this.redPlanetCountElement.style.width = (c.redCount / c.limit) * 50 + '%';
|
|
||||||
},
|
|
||||||
[GameEnd.type]: (c: GameEnd) => {
|
[GameEnd.type]: (c: GameEnd) => {
|
||||||
const team =
|
const team =
|
||||||
c.winningTeam === CharacterTeam.decla
|
c.winningTeam === CharacterTeam.decla
|
||||||
? '<span class="decla">decla</span>'
|
? '<span class="decla">decla</span>'
|
||||||
: '<span class="red">red</span>';
|
: '<span class="red">red</span>';
|
||||||
this.announcementText.innerHTML = `Team ${team} won 🎉`;
|
this.lastAnnouncementText = `Team ${team} won 🎉`;
|
||||||
},
|
},
|
||||||
[UpdateOtherPlayerDirections.type]: this.handleOtherPlayerDirections.bind(this),
|
[UpdateOtherPlayerDirections.type]: (c: UpdateOtherPlayerDirections) =>
|
||||||
|
(this.lastOtherPlayerDirections = c),
|
||||||
[GameStart.type]: this.initialize.bind(this),
|
[GameStart.type]: this.initialize.bind(this),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private lastOtherPlayerDirections?: UpdateOtherPlayerDirections;
|
||||||
private handleOtherPlayerDirections(command: UpdateOtherPlayerDirections) {
|
private handleOtherPlayerDirections(command: UpdateOtherPlayerDirections) {
|
||||||
this.arrowElements
|
this.arrowElements
|
||||||
.splice(command.otherPlayerDirections.length, this.arrowElements.length)
|
.splice(command.otherPlayerDirections.length, this.arrowElements.length)
|
||||||
|
|
@ -205,10 +212,7 @@ export class Game extends CommandReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public aspectRatioChanged(aspectRatio: number) {
|
public aspectRatioChanged(aspectRatio: number) {
|
||||||
this.socket.emit(
|
this.socketReceiver.sendCommand(new SetAspectRatioActionCommand(aspectRatio));
|
||||||
TransportEvents.PlayerToServer,
|
|
||||||
serialize(new SetAspectRatioActionCommand(aspectRatio)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isActive = true;
|
private isActive = true;
|
||||||
|
|
@ -216,6 +220,7 @@ export class Game extends CommandReceiver {
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private framesSinceLastLayoutUpdate = 0;
|
||||||
private gameLoop(
|
private gameLoop(
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
_: DOMHighResTimeStamp,
|
_: DOMHighResTimeStamp,
|
||||||
|
|
@ -224,10 +229,34 @@ export class Game extends CommandReceiver {
|
||||||
this.resolveStarted();
|
this.resolveStarted();
|
||||||
deltaTime /= 1000;
|
deltaTime /= 1000;
|
||||||
|
|
||||||
|
let shouldChangeLayout = false;
|
||||||
|
if (++this.framesSinceLastLayoutUpdate > 1) {
|
||||||
|
shouldChangeLayout = true;
|
||||||
|
this.framesSinceLastLayoutUpdate = 0;
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
|
|
||||||
|
this.socketReceiver.sendQueuedCommands();
|
||||||
this.gameObjects.stepObjects(deltaTime);
|
this.gameObjects.stepObjects(deltaTime);
|
||||||
this.gameObjects.drawObjects(this.renderer, this.overlay);
|
this.gameObjects.drawObjects(this.renderer, this.overlay, shouldChangeLayout);
|
||||||
|
|
||||||
return this.isActive;
|
return this.isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private draw() {
|
||||||
|
if (this.lastGameState) {
|
||||||
|
this.declaPlanetCountElement.style.width =
|
||||||
|
(this.lastGameState.declaCount / this.lastGameState.limit) * 50 + '%';
|
||||||
|
this.redPlanetCountElement.style.width =
|
||||||
|
(this.lastGameState.redCount / this.lastGameState.limit) * 50 + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.lastOtherPlayerDirections) {
|
||||||
|
this.handleOtherPlayerDirections(this.lastOtherPlayerDirections);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.announcementText.innerHTML = this.lastAnnouncementText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
import { ServerInformation, serverInformationEndpoint, TransportEvents } from 'shared';
|
import { ServerInformation, serverInformationEndpoint, TransportEvents } from 'shared';
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
import { Configuration } from './config/configuration';
|
import { Configuration } from './config/configuration';
|
||||||
|
import parser from 'socket.io-msgpack-parser';
|
||||||
|
|
||||||
export type PlayerDecision = {
|
export type PlayerDecision = {
|
||||||
playerName: string;
|
playerName: string;
|
||||||
server: string;
|
server: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const pollInterval = 10000;
|
const pollInterval = 8000;
|
||||||
export class JoinFormHandler {
|
export class JoinFormHandler {
|
||||||
private waitingForDecision: Promise<PlayerDecision>;
|
private waitingForDecision: Promise<PlayerDecision>;
|
||||||
private resolvePlayerDecision!: (d: PlayerDecision) => void;
|
private resolvePlayerDecision!: (d: PlayerDecision) => void;
|
||||||
|
|
@ -122,8 +123,9 @@ class ServerChooserOption {
|
||||||
|
|
||||||
this.socket = io(url, {
|
this.socket = io(url, {
|
||||||
reconnection: false,
|
reconnection: false,
|
||||||
timeout: 1500,
|
timeout: 4000,
|
||||||
});
|
parser,
|
||||||
|
} as any);
|
||||||
|
|
||||||
this.socket.on('connect_error', this.destroy.bind(this));
|
this.socket.on('connect_error', this.destroy.bind(this));
|
||||||
this.socket.on('connect_timeout', this.destroy.bind(this));
|
this.socket.on('connect_timeout', this.destroy.bind(this));
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export class Camera extends GameObject implements ViewObject {
|
||||||
|
|
||||||
public step(deltaTimeInSeconds: number): void {}
|
public step(deltaTimeInSeconds: number): void {}
|
||||||
|
|
||||||
public draw(renderer: Renderer, overlay: HTMLElement) {
|
public draw(renderer: Renderer, overlay: HTMLElement, shouldChangeLayout: boolean) {
|
||||||
const canvasAspectRatio = renderer.canvasSize.x / renderer.canvasSize.y;
|
const canvasAspectRatio = renderer.canvasSize.x / renderer.canvasSize.y;
|
||||||
if (canvasAspectRatio !== this.aspectRatio) {
|
if (canvasAspectRatio !== this.aspectRatio) {
|
||||||
this.aspectRatio = canvasAspectRatio;
|
this.aspectRatio = canvasAspectRatio;
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,12 @@ export class GameObjectContainer extends CommandReceiver {
|
||||||
this.objects.forEach((o) => o.step(deltaTimeInSeconds));
|
this.objects.forEach((o) => o.step(deltaTimeInSeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawObjects(renderer: Renderer, overlay: HTMLElement) {
|
public drawObjects(
|
||||||
this.objects.forEach((o) => o.draw(renderer, overlay));
|
renderer: Renderer,
|
||||||
|
overlay: HTMLElement,
|
||||||
|
shouldChangeLayout: boolean,
|
||||||
|
) {
|
||||||
|
this.objects.forEach((o) => o.draw(renderer, overlay, shouldChangeLayout));
|
||||||
}
|
}
|
||||||
|
|
||||||
private addObject(object: ViewObject) {
|
private addObject(object: ViewObject) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@ export class LampView extends LampBase implements ViewObject {
|
||||||
|
|
||||||
public beforeDestroy(): void {}
|
public beforeDestroy(): void {}
|
||||||
|
|
||||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
public draw(
|
||||||
|
renderer: Renderer,
|
||||||
|
overlay: HTMLElement,
|
||||||
|
shouldChangeLayout: boolean,
|
||||||
|
): void {
|
||||||
renderer.addDrawable(this.light);
|
renderer.addDrawable(this.light);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,31 +47,76 @@ export class PlanetView extends PlanetBase implements ViewObject {
|
||||||
vec2.scale(vec2.create(), p.velocity, deltaTimeInSeconds),
|
vec2.scale(vec2.create(), p.velocity, deltaTimeInSeconds),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ((p.timeToLive -= deltaTimeInSeconds) <= 0) {
|
p.timeToLive -= deltaTimeInSeconds;
|
||||||
p.element.parentElement?.removeChild(p.element);
|
|
||||||
} else {
|
|
||||||
p.element.style.opacity = Math.min(1, p.timeToLive).toString();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.generatedPointElements = this.generatedPointElements.filter(
|
|
||||||
(p) => p.timeToLive > 0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private generatedPointElements: Array<FallingPoint> = [];
|
private generatedPointElements: Array<FallingPoint> = [];
|
||||||
|
|
||||||
|
private lastGeneratedPoint?: number;
|
||||||
public generatedPoints(value: number) {
|
public generatedPoints(value: number) {
|
||||||
const element = document.createElement('div');
|
this.lastGeneratedPoint = value;
|
||||||
element.className = 'falling-point ' + (this.ownership < 0.5 ? 'decla' : 'red');
|
}
|
||||||
element.innerText = '+' + value;
|
|
||||||
this.generatedPointElements.push({
|
public beforeDestroy(): void {
|
||||||
element,
|
this.ownershipProgess.parentElement?.removeChild(this.ownershipProgess);
|
||||||
addedToOverlay: false,
|
this.generatedPointElements.forEach((p) =>
|
||||||
timeToLive: Random.getRandomInRange(2, 3),
|
p.element.parentElement?.removeChild(p.element),
|
||||||
position: vec2.create(),
|
);
|
||||||
velocity: vec2.fromValues(Random.getRandomInRange(-30, 30), 0),
|
}
|
||||||
});
|
|
||||||
|
public draw(
|
||||||
|
renderer: Renderer,
|
||||||
|
overlay: HTMLElement,
|
||||||
|
shouldChangeLayout: boolean,
|
||||||
|
): void {
|
||||||
|
if (shouldChangeLayout) {
|
||||||
|
if (!this.ownershipProgess.parentElement) {
|
||||||
|
overlay.appendChild(this.ownershipProgess);
|
||||||
|
}
|
||||||
|
|
||||||
|
const screenPosition = renderer.worldToDisplayCoordinates(this.center);
|
||||||
|
|
||||||
|
this.generatedPointElements.forEach((p) => {
|
||||||
|
if (!p.addedToOverlay) {
|
||||||
|
overlay.appendChild(p.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.element.style.transform = `translateX(${
|
||||||
|
screenPosition.x + p.position.x
|
||||||
|
}px) translateY(${screenPosition.y + p.position.y}px)`;
|
||||||
|
|
||||||
|
if (p.timeToLive <= 0) {
|
||||||
|
p.element.parentElement?.removeChild(p.element);
|
||||||
|
} else {
|
||||||
|
p.element.style.opacity = Math.min(1, p.timeToLive).toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.generatedPointElements = this.generatedPointElements.filter(
|
||||||
|
(p) => p.timeToLive > 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.ownershipProgess.style.transform = `translateX(${screenPosition.x}px) translateY(${screenPosition.y}px) translateX(-50%) translateY(-50%)`;
|
||||||
|
this.ownershipProgess.style.background = this.getGradient();
|
||||||
|
|
||||||
|
if (this.lastGeneratedPoint !== undefined) {
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.className = 'falling-point ' + (this.ownership < 0.5 ? 'decla' : 'red');
|
||||||
|
element.innerText = '+' + this.lastGeneratedPoint;
|
||||||
|
this.generatedPointElements.push({
|
||||||
|
element,
|
||||||
|
addedToOverlay: false,
|
||||||
|
timeToLive: Random.getRandomInRange(2, 3),
|
||||||
|
position: vec2.create(),
|
||||||
|
velocity: vec2.fromValues(Random.getRandomInRange(-30, 30), 0),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.lastGeneratedPoint = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.addDrawable(this.shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getGradient(): string {
|
private getGradient(): string {
|
||||||
|
|
@ -91,33 +136,4 @@ export class PlanetView extends PlanetBase implements ViewObject {
|
||||||
var(--bright-red) 100%
|
var(--bright-red) 100%
|
||||||
)`;
|
)`;
|
||||||
}
|
}
|
||||||
public beforeDestroy(): void {
|
|
||||||
this.ownershipProgess.parentElement?.removeChild(this.ownershipProgess);
|
|
||||||
this.generatedPointElements.forEach((p) =>
|
|
||||||
p.element.parentElement?.removeChild(p.element),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
|
||||||
if (!this.ownershipProgess.parentElement) {
|
|
||||||
overlay.appendChild(this.ownershipProgess);
|
|
||||||
}
|
|
||||||
|
|
||||||
const screenPosition = renderer.worldToDisplayCoordinates(this.center);
|
|
||||||
|
|
||||||
this.generatedPointElements.forEach((p) => {
|
|
||||||
if (!p.addedToOverlay) {
|
|
||||||
overlay.appendChild(p.element);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.element.style.left = screenPosition.x + p.position.x + 'px';
|
|
||||||
p.element.style.top = screenPosition.y + p.position.y + 'px';
|
|
||||||
});
|
|
||||||
|
|
||||||
this.ownershipProgess.style.left = screenPosition.x + 'px';
|
|
||||||
this.ownershipProgess.style.top = screenPosition.y + 'px';
|
|
||||||
this.ownershipProgess.style.background = this.getGradient();
|
|
||||||
|
|
||||||
renderer.addDrawable(this.shape);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,6 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
|
||||||
}
|
}
|
||||||
|
|
||||||
public step(deltaTimeInSeconds: number): void {
|
public step(deltaTimeInSeconds: number): void {
|
||||||
this.healthElement.style.width = (50 * this.health) / settings.playerMaxHealth + 'px';
|
|
||||||
this.statsElement.innerText = this.getStatsText();
|
|
||||||
if (this.previousHealth > this.health) {
|
if (this.previousHealth > this.health) {
|
||||||
this.previousHealth = this.health;
|
this.previousHealth = this.health;
|
||||||
if (OptionsHandler.options.vibrationEnabled) {
|
if (OptionsHandler.options.vibrationEnabled) {
|
||||||
|
|
@ -68,16 +66,26 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
|
||||||
this.nameElement.parentElement?.removeChild(this.nameElement);
|
this.nameElement.parentElement?.removeChild(this.nameElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
public draw(
|
||||||
if (!this.nameElement.parentElement) {
|
renderer: Renderer,
|
||||||
overlay.appendChild(this.nameElement);
|
overlay: HTMLElement,
|
||||||
}
|
shouldChangeLayout: boolean,
|
||||||
|
): void {
|
||||||
|
if (shouldChangeLayout) {
|
||||||
|
if (!this.nameElement.parentElement) {
|
||||||
|
overlay.appendChild(this.nameElement);
|
||||||
|
}
|
||||||
|
|
||||||
const screenPosition = renderer.worldToDisplayCoordinates(
|
const screenPosition = renderer.worldToDisplayCoordinates(
|
||||||
this.calculateTextPosition(),
|
this.calculateTextPosition(),
|
||||||
);
|
);
|
||||||
this.nameElement.style.left = screenPosition.x + 'px';
|
|
||||||
this.nameElement.style.top = screenPosition.y + 'px';
|
this.nameElement.style.transform = `translateX(${screenPosition.x}px) translateY(${screenPosition.y}px) translateX(-50%) translateY(-50%) rotate(-15deg)`;
|
||||||
|
|
||||||
|
this.healthElement.style.width =
|
||||||
|
(50 * this.health) / settings.playerMaxHealth + 'px';
|
||||||
|
this.statsElement.innerText = this.getStatsText();
|
||||||
|
}
|
||||||
|
|
||||||
this.shape.setCircles([this.head!, this.leftFoot!, this.rightFoot!]);
|
this.shape.setCircles([this.head!, this.leftFoot!, this.rightFoot!]);
|
||||||
renderer.addDrawable(this.shape);
|
renderer.addDrawable(this.shape);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,11 @@ export class ProjectileView extends ProjectileBase implements ViewObject {
|
||||||
|
|
||||||
public beforeDestroy(): void {}
|
public beforeDestroy(): void {}
|
||||||
|
|
||||||
public draw(renderer: Renderer, overlay: HTMLElement): void {
|
public draw(
|
||||||
|
renderer: Renderer,
|
||||||
|
overlay: HTMLElement,
|
||||||
|
shouldChangeLayout: boolean,
|
||||||
|
): void {
|
||||||
renderer.addDrawable(this.circle);
|
renderer.addDrawable(this.circle);
|
||||||
renderer.addDrawable(this.light);
|
renderer.addDrawable(this.light);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,6 @@ import { GameObject } from 'shared';
|
||||||
|
|
||||||
export interface ViewObject extends GameObject {
|
export interface ViewObject extends GameObject {
|
||||||
step(deltaTimeInMilliseconds: number): void;
|
step(deltaTimeInMilliseconds: number): void;
|
||||||
draw(renderer: Renderer, overlay: HTMLElement): void;
|
draw(renderer: Renderer, overlay: HTMLElement, shouldChangeLayout: boolean): void;
|
||||||
beforeDestroy(): void;
|
beforeDestroy(): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"lib": ["dom", "es2017"]
|
"lib": ["dom", "es2017"],
|
||||||
|
"typeRoots": ["./types", "./node_modules/@types"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/*.ts"]
|
"include": ["src/**/*.ts", "src/*.ts"],
|
||||||
|
"exclude": ["node_modules", "typings"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
frontend/types/socket.io-msgpack-parser/index.d.ts
vendored
Normal file
1
frontend/types/socket.io-msgpack-parser/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
declare module 'socket.io-msgpack-parser';
|
||||||
|
|
@ -13,7 +13,7 @@ const redPlanetColor = redColor;
|
||||||
|
|
||||||
export const settings = {
|
export const settings = {
|
||||||
lightCutoffDistance: 600,
|
lightCutoffDistance: 600,
|
||||||
physicsMaxStep: 2,
|
physicsMaxStep: 8,
|
||||||
maxVelocityX: 1000,
|
maxVelocityX: 1000,
|
||||||
maxVelocityY: 1000,
|
maxVelocityY: 1000,
|
||||||
radiusSteps: 500,
|
radiusSteps: 500,
|
||||||
|
|
@ -33,7 +33,7 @@ export const settings = {
|
||||||
playerMaxStrength: 80,
|
playerMaxStrength: 80,
|
||||||
endGameDeltaScaling: 4,
|
endGameDeltaScaling: 4,
|
||||||
playerDiedTimeout: 5,
|
playerDiedTimeout: 5,
|
||||||
playerStrengthRegenerationPerSeconds: 40,
|
playerStrengthRegenerationPerSeconds: 60,
|
||||||
projectileMaxStrength: 40,
|
projectileMaxStrength: 40,
|
||||||
projectileSpeed: 4000,
|
projectileSpeed: 4000,
|
||||||
projectileMaxBounceCount: 1,
|
projectileMaxBounceCount: 1,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { serializableMapping } from './serializable-mapping';
|
import { serializableMapping } from './serializable-mapping';
|
||||||
|
|
||||||
export const deserialize = (json: string): any => {
|
export const deserialize = (json: string): any => {
|
||||||
return JSON.parse(json, (k, v) => {
|
return JSON.parse(json, (_, v) => {
|
||||||
if (v !== null && Object.prototype.hasOwnProperty.call(v, '0')) {
|
if (v !== null && Object.prototype.hasOwnProperty.call(v, '0')) {
|
||||||
const possibleType = v[0];
|
const possibleType = v[0];
|
||||||
const overridableConstructor = serializableMapping.get(possibleType);
|
const overridableConstructor = serializableMapping.get(possibleType);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
export const serialize = (object: any): string => {
|
export const serialize = (object: any): string => {
|
||||||
return JSON.stringify(object, (_, value) => {
|
return JSON.stringify(object, (_, value) => {
|
||||||
if (value?.__serializable_type) {
|
if (value?.__serializable_type) {
|
||||||
return [value.__serializable_type, ...value.toArray()];
|
const props = value.toArray() as Array<any>;
|
||||||
|
props.unshift(value.__serializable_type);
|
||||||
|
return props;
|
||||||
}
|
}
|
||||||
return value?.toFixed ? Number(value.toFixed(3)) : value;
|
return value?.toFixed ? Number(value.toFixed(3)) : value;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue