Add server screen
This commit is contained in:
parent
89fafeafd3
commit
e2129bbb26
20 changed files with 672 additions and 174 deletions
|
|
@ -1,20 +1,24 @@
|
|||
{
|
||||
"name": "decla.red-server",
|
||||
"private": true,
|
||||
"name": "declared-server",
|
||||
"description": "Game server for decla.red",
|
||||
"keywords": [],
|
||||
"author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)",
|
||||
"main": "index.js",
|
||||
"version": "0.0.1",
|
||||
"main": "dist/main.js",
|
||||
"scripts": {
|
||||
"build": "npx webpack --mode production",
|
||||
"initialize": "npm install",
|
||||
"start": "concurrently --kill-others \"npx webpack --mode development -w\" \"nodemon --legacy-watch dist/main.js\"",
|
||||
"try-build": "npm run build && node dist/main.js"
|
||||
"start": "concurrently --kill-others \"npx webpack --mode development -w\" \"cd dist && nodemon --legacy-watch main.js\"",
|
||||
"try-build": "npm run build && cd dist && node main.js && cd -"
|
||||
},
|
||||
"bin": {
|
||||
"declared-server": "./dist/main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/config": "0.0.36",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"http": "0.0.1-security",
|
||||
"minimist": "^1.2.5",
|
||||
"socket.io": "^2.3.0",
|
||||
"uws": "^10.148.1",
|
||||
"webpack-node-externals": "^2.5.2"
|
||||
|
|
|
|||
8
backend/src/default-options.ts
Normal file
8
backend/src/default-options.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { Options } from './options';
|
||||
|
||||
export const defaultOptions: Options = {
|
||||
port: 3000,
|
||||
name: 'Localhost',
|
||||
playerLimit: 16,
|
||||
seed: 51,
|
||||
};
|
||||
88
backend/src/game-server.ts
Normal file
88
backend/src/game-server.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { PhysicalContainer } from './physics/containers/physical-container';
|
||||
import { Player } from './players/player';
|
||||
import ioserver from 'socket.io';
|
||||
import { TransportEvents, deserialize, settings, ServerInformation } from 'shared';
|
||||
import { createWorld } from './map/create-world';
|
||||
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||
import { Options } from './options';
|
||||
|
||||
export class GameServer {
|
||||
private objects = new PhysicalContainer();
|
||||
private players: Array<Player> = [];
|
||||
private deltaTimes: Array<number> = [];
|
||||
private deltaTimeCalculator = new DeltaTimeCalculator();
|
||||
|
||||
private serverName: string;
|
||||
private playerLimit: number;
|
||||
constructor(io: ioserver.Server, options: Options) {
|
||||
this.serverName = options.name;
|
||||
this.playerLimit = options.playerLimit;
|
||||
|
||||
createWorld(this.objects);
|
||||
this.objects.initialize();
|
||||
|
||||
io.on('connection', (socket: SocketIO.Socket) => {
|
||||
socket.on(TransportEvents.PlayerJoining, () => {
|
||||
const player = new Player(this.objects, socket);
|
||||
this.players.push(player);
|
||||
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
||||
const command = deserialize(json);
|
||||
player.sendCommand(command);
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
player.destroy();
|
||||
this.players = this.players.filter((p) => p !== player);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('join', (room_name: string) => {
|
||||
socket.join(room_name);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public start() {
|
||||
this.handlePhysics();
|
||||
}
|
||||
|
||||
private handlePhysics() {
|
||||
const delta = this.deltaTimeCalculator.getNextDeltaTimeInMilliseconds();
|
||||
this.deltaTimes.push(delta);
|
||||
const framesBetweenDeltaTimeCalculation = 1000;
|
||||
|
||||
if (this.deltaTimes.length > framesBetweenDeltaTimeCalculation) {
|
||||
this.deltaTimes.sort((a, b) => a - b);
|
||||
console.log(
|
||||
`Median physics time: ${this.deltaTimes[
|
||||
framesBetweenDeltaTimeCalculation
|
||||
].toFixed(2)} ms`,
|
||||
);
|
||||
console.log(
|
||||
`Memory used: ${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`,
|
||||
);
|
||||
this.deltaTimes = [];
|
||||
console.log(this.players.map((p) => p.latency));
|
||||
}
|
||||
|
||||
this.objects.stepObjects(delta / 1000);
|
||||
this.players.forEach((p) => p.step(delta / 1000));
|
||||
|
||||
const physicsDelta = this.deltaTimeCalculator.getDeltaTimeInMilliseconds();
|
||||
this.deltaTimes.push(physicsDelta);
|
||||
const sleepTime = settings.targetPhysicsDeltaTimeInMilliseconds - physicsDelta;
|
||||
if (sleepTime >= settings.minPhysicsSleepTime) {
|
||||
setTimeout(this.handlePhysics.bind(this), sleepTime);
|
||||
} else {
|
||||
setImmediate(this.handlePhysics.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
public get serverInfo(): ServerInformation {
|
||||
return {
|
||||
serverName: this.serverName,
|
||||
playerCount: this.players.length,
|
||||
playerLimit: this.playerLimit,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.1/socket.io.js"
|
||||
integrity="sha512-AcZyhRP/tbAEsXCCGlziPun5iFvcSUpEz2jKkx0blkYKbxU81F+iq8FURwPn1sYFeksJ+sDDrI5XujsqSobWdQ=="
|
||||
crossorigin="anonymous"></script>
|
||||
<title>Server info</title>
|
||||
|
||||
</head>
|
||||
|
||||
<body></body>
|
||||
<script>
|
||||
const socket = io({
|
||||
transports: ['websocket'],
|
||||
});
|
||||
|
||||
socket.on('reconnect_attempt', () => {
|
||||
socket.io.opts.transports = ['polling', 'websocket'];
|
||||
});
|
||||
|
||||
socket.emit('join', 'insights');
|
||||
|
||||
socket.on('insights', (message) => {
|
||||
document.body.innerText += message;
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,36 +1,32 @@
|
|||
import ioserver, { Socket } from 'socket.io';
|
||||
import ioserver from 'socket.io';
|
||||
import express from 'express';
|
||||
import { Server } from 'http';
|
||||
import cors from 'cors';
|
||||
import {
|
||||
applyArrayPlugins,
|
||||
Random,
|
||||
TransportEvents,
|
||||
deserialize,
|
||||
settings,
|
||||
} from 'shared';
|
||||
import './index.html';
|
||||
import { Player } from './players/player';
|
||||
import { PhysicalContainer } from './physics/containers/physical-container';
|
||||
import { createDungeon } from './map/create-dungeon';
|
||||
import { applyArrayPlugins, Random, serverInformationEndpoint } from 'shared';
|
||||
import minimist from 'minimist';
|
||||
|
||||
import { glMatrix } from 'gl-matrix';
|
||||
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||
|
||||
import { GameServer } from './game-server';
|
||||
|
||||
import { defaultOptions } from './default-options';
|
||||
|
||||
glMatrix.setMatrixArrayType(Array);
|
||||
|
||||
applyArrayPlugins();
|
||||
|
||||
Random.seed = 44;
|
||||
const optionOverrides = minimist(process.argv.slice(2));
|
||||
const options = {
|
||||
...defaultOptions,
|
||||
...optionOverrides,
|
||||
};
|
||||
|
||||
const objects = new PhysicalContainer();
|
||||
createDungeon(objects);
|
||||
|
||||
objects.initialize();
|
||||
|
||||
let players: Array<Player> = [];
|
||||
Random.seed = options.seed;
|
||||
|
||||
const app = express();
|
||||
const deltaTimeCalculator = new DeltaTimeCalculator();
|
||||
const server = new Server(app);
|
||||
const io = ioserver(server);
|
||||
|
||||
const gameServer = new GameServer(io, options);
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
|
|
@ -41,70 +37,12 @@ app.use(
|
|||
}),
|
||||
);
|
||||
|
||||
const port = 3000;
|
||||
const server = new Server(app);
|
||||
const io = ioserver(server);
|
||||
|
||||
/*
|
||||
const log = (text: string) => {
|
||||
io.to('insights').emit('insights', text + '\n');
|
||||
};
|
||||
*/
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
res.sendFile('dist/index.html', { root: '.' });
|
||||
app.get(serverInformationEndpoint, (req, res) => {
|
||||
res.json(gameServer.serverInfo);
|
||||
});
|
||||
|
||||
io.on('connection', (socket: SocketIO.Socket) => {
|
||||
socket.on(TransportEvents.PlayerJoining, () => {
|
||||
const player = new Player(objects, socket);
|
||||
players.push(player);
|
||||
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
||||
const command = deserialize(json);
|
||||
player.sendCommand(command);
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
player.destroy();
|
||||
players = players.filter((p) => p !== player);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('join', (room_name: string) => {
|
||||
socket.join(room_name);
|
||||
});
|
||||
server.listen(options.port, () => {
|
||||
console.log(`server started at http://localhost:${options.port}`);
|
||||
});
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(`server started at http://localhost:${port}`);
|
||||
});
|
||||
|
||||
let deltas: Array<number> = [];
|
||||
|
||||
const handlePhysics = () => {
|
||||
const delta = deltaTimeCalculator.getNextDeltaTimeInMilliseconds();
|
||||
deltas.push(delta);
|
||||
if (deltas.length > 1000) {
|
||||
deltas.sort((a, b) => a - b);
|
||||
console.log(`Median physics time: ${deltas[50].toFixed(2)} ms`);
|
||||
console.log(
|
||||
`Memory used: ${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`,
|
||||
);
|
||||
deltas = [];
|
||||
console.log(players.map((p) => p.latency));
|
||||
}
|
||||
|
||||
objects.stepObjects(delta / 1000);
|
||||
players.forEach((p) => p.step(delta / 1000));
|
||||
|
||||
const physicsDelta = deltaTimeCalculator.getDeltaTimeInMilliseconds();
|
||||
deltas.push(physicsDelta);
|
||||
const sleepTime = settings.targetPhysicsDeltaTimeInMilliseconds - physicsDelta;
|
||||
if (sleepTime >= settings.minPhysicsSleepTime) {
|
||||
setTimeout(handlePhysics, sleepTime);
|
||||
} else {
|
||||
setImmediate(handlePhysics);
|
||||
}
|
||||
};
|
||||
|
||||
handlePhysics();
|
||||
gameServer.start();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { vec2, vec3 } from 'gl-matrix';
|
||||
import { Random, settings } from 'shared';
|
||||
import { Random, settings, PlanetBase } 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 createDungeon = (objectContainer: PhysicalContainer) => {
|
||||
export const createWorld = (objectContainer: PhysicalContainer) => {
|
||||
const objects: Array<Physical> = [];
|
||||
const lights: Array<Physical> = [];
|
||||
|
||||
|
|
@ -33,11 +33,13 @@ export const createDungeon = (objectContainer: PhysicalContainer) => {
|
|||
);
|
||||
|
||||
objects.push(
|
||||
createBlob(
|
||||
position,
|
||||
Random.getRandomInRange(300, 800),
|
||||
Random.getRandomInRange(300, 800),
|
||||
Random.getRandomInRange(20, 40),
|
||||
new PlanetPhysical(
|
||||
PlanetBase.createPlanetVertices(
|
||||
position,
|
||||
Random.getRandomInRange(300, 800),
|
||||
Random.getRandomInRange(300, 800),
|
||||
Random.getRandomInRange(20, 40),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -72,26 +74,3 @@ export const createDungeon = (objectContainer: PhysicalContainer) => {
|
|||
|
||||
[...objects, ...lights].forEach((o) => objectContainer.addObject(o));
|
||||
};
|
||||
|
||||
const createBlob = (
|
||||
center: vec2,
|
||||
width: number,
|
||||
height: number,
|
||||
randomness: number,
|
||||
): PlanetPhysical => {
|
||||
const vertices = [];
|
||||
|
||||
for (let i = 0; i < settings.polygonEdgeCount; i++) {
|
||||
vertices.push(
|
||||
vec2.fromValues(
|
||||
center.x +
|
||||
(width / 2) * Math.cos((i / settings.polygonEdgeCount) * -Math.PI * 2) +
|
||||
Random.getRandomInRange(-randomness, randomness),
|
||||
center.y +
|
||||
(height / 2) * Math.sin((i / settings.polygonEdgeCount) * -Math.PI * 2) +
|
||||
Random.getRandomInRange(-randomness, randomness),
|
||||
),
|
||||
);
|
||||
}
|
||||
return new PlanetPhysical(vertices);
|
||||
};
|
||||
6
backend/src/options.ts
Normal file
6
backend/src/options.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export interface Options {
|
||||
port: number;
|
||||
name: string;
|
||||
playerLimit: number;
|
||||
seed: number;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue