From 0f0a1eaf672e7a16d5b16ca6bb36d64e66e59132 Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Sun, 4 Oct 2020 19:05:32 +0200 Subject: [PATCH] Add backend --- frontend/.gitattributes => .gitattributes | 0 backend/.eslintignore | 1 + backend/.eslintrc.json | 29 ++++++++ backend/.gitignore | 4 ++ backend/.prettierrc | 7 ++ backend/package.json | 51 +++++++++++++ backend/src/index.html | 29 ++++++++ backend/src/main.ts | 61 ++++++++++++++++ backend/src/players/player-container.ts | 13 ++++ backend/src/players/player.ts | 9 +++ backend/tsconfig.json | 19 +++++ backend/webpack.config.js | 75 ++++++++++++++++++++ docker-compose.yml | 29 -------- frontend/package.json | 11 +-- frontend/src/index.ts | 13 ++++ frontend/src/scripts/config/configuration.ts | 30 +++++--- frontend/src/scripts/game.ts | 14 ++++ frontend/tsconfig.json | 1 + shared/src/transport/transport-events.ts | 4 ++ 19 files changed, 355 insertions(+), 45 deletions(-) rename frontend/.gitattributes => .gitattributes (100%) create mode 100644 backend/.eslintignore create mode 100644 backend/.eslintrc.json create mode 100644 backend/.gitignore create mode 100644 backend/.prettierrc create mode 100644 backend/package.json create mode 100644 backend/src/index.html create mode 100644 backend/src/main.ts create mode 100644 backend/src/players/player-container.ts create mode 100644 backend/src/players/player.ts create mode 100644 backend/tsconfig.json create mode 100644 backend/webpack.config.js delete mode 100644 docker-compose.yml create mode 100644 shared/src/transport/transport-events.ts diff --git a/frontend/.gitattributes b/.gitattributes similarity index 100% rename from frontend/.gitattributes rename to .gitattributes diff --git a/backend/.eslintignore b/backend/.eslintignore new file mode 100644 index 0000000..2cb7d2a --- /dev/null +++ b/backend/.eslintignore @@ -0,0 +1 @@ +**/*.js diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json new file mode 100644 index 0000000..8275ed8 --- /dev/null +++ b/backend/.eslintrc.json @@ -0,0 +1,29 @@ +{ + "root": true, + "env": { + "browser": true, + "es2020": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "plugins": ["unused-imports", "@typescript-eslint", "prettier"], + "rules": { + "prettier/prettier": "error", + "no-unused-vars": "off", + "unused-imports/no-unused-imports-ts": "error", + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-non-null-assertion": "off" + } +} diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..bf0cd0d --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,4 @@ +dist +node_modules +package-lock.json +.firebase diff --git a/backend/.prettierrc b/backend/.prettierrc new file mode 100644 index 0000000..5aae580 --- /dev/null +++ b/backend/.prettierrc @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "printWidth": 90, + "tabWidth": 2, + "singleQuote": true, + "endOfLine": "lf" +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..89bbad7 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,51 @@ +{ + "name": "decla.red-server", + "version": "0.0.0", + "description": "Game server for decla.red", + "private": true, + "main": "index.js", + "scripts": { + "start": "concurrently --kill-others \"webpack --mode development -w\" \"npx nodemon dist/main.js\"", + "lint": "npx eslint --fix \"src/**/*.ts\" && npx prettier --write \"src/**/*.ts\"", + "build": "webpack --mode production" + }, + "keywords": [], + "author": "AndrĂ¡s Schmelczer (https://schmelczer.dev/)", + "devDependencies": { + "@types/express": "^4.17.8", + "@types/gl-matrix": "^2.4.5", + "@types/node": "^14.11.2", + "@types/uuid": "^8.0.0", + "@typescript-eslint/eslint-plugin": "^3.9.1", + "@typescript-eslint/parser": "^3.9.1", + "clean-webpack-plugin": "^3.0.0", + "concurrently": "^5.3.0", + "eslint": "^7.2.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-unused-imports": "^0.1.3", + "firebase": "^7.22.0", + "gl-matrix": "^3.3.0", + "nodemon": "^2.0.4", + "prettier": "^2.0.5", + "terser-webpack-plugin": "^2.3.5", + "ts-loader": "^8.0.1", + "typescript": "^3.8.3", + "uuid": "^8.2.0", + "webpack": "^4.43.0", + "webpack-cli": "^3.3.11", + "webpack-dev-server": "^3.10.3", + "file-loader": "^6.1.0" + }, + "dependencies": { + "@types/cors": "^2.8.7", + "@types/socket.io": "^2.1.11", + "cors": "^2.8.5", + "express": "^4.17.1", + "http": "0.0.1-security", + "socket.io": "^2.3.0", + "uws": "^10.148.1", + "webpack-node-externals": "^2.5.2" + } +} diff --git a/backend/src/index.html b/backend/src/index.html new file mode 100644 index 0000000..a06fb07 --- /dev/null +++ b/backend/src/index.html @@ -0,0 +1,29 @@ + + + + + + + Server info + + + + diff --git a/backend/src/main.ts b/backend/src/main.ts new file mode 100644 index 0000000..e3872df --- /dev/null +++ b/backend/src/main.ts @@ -0,0 +1,61 @@ +import ioserver, { Socket } from 'socket.io'; +import express from 'express'; +import { Server } from 'http'; +import cors from 'cors'; + +import { PlayerContainer } from './players/player-container'; +import './index.html'; +import { TransportEvents } from '../../shared/src/transport/transport-events'; +import { Player } from './players/player'; + +const app = express(); + +app.use( + cors({ + origin: (origin, callback) => { + callback(null, true); + }, + credentials: true, + }) +); + +const port = 3000; +const server = new Server(app); +const io = ioserver(server); + +const players = new PlayerContainer(); + +const log = (text: string) => { + io.to('insights').emit('insights', text + '\n'); +}; + +app.get('/', function (req, res) { + res.sendFile('dist/index.html', { root: '.' }); +}); + +io.on('connection', (socket: SocketIO.Socket) => { + socket.on(TransportEvents.PlayerJoining, () => { + log('player joined'); + + const player = new Player(socket); + players.addPlayer(player); + + socket.on(TransportEvents.PlayerSendingInfo, () => {}); + + socket.on('disconnect', () => { + log('player disconnected'); + + players.removePlayerBySocketId(player.socketId); + }); + }); + + socket.on('join', (room_name: string) => { + socket.join(room_name); + }); + + //socket.on('disconnect', () => {}); +}); + +server.listen(port, () => { + console.log(`server started at http://localhost:${port}`); +}); diff --git a/backend/src/players/player-container.ts b/backend/src/players/player-container.ts new file mode 100644 index 0000000..5c4f732 --- /dev/null +++ b/backend/src/players/player-container.ts @@ -0,0 +1,13 @@ +import { Player } from './player'; + +export class PlayerContainer { + private socketIdToPlayer = new Map(); + + public addPlayer(player: Player) { + this.socketIdToPlayer.set(player.socketId, player); + } + + public removePlayerBySocketId(socketId: string) { + this.socketIdToPlayer.delete(socketId); + } +} diff --git a/backend/src/players/player.ts b/backend/src/players/player.ts new file mode 100644 index 0000000..a88b375 --- /dev/null +++ b/backend/src/players/player.ts @@ -0,0 +1,9 @@ +import { Socket } from 'dgram'; + +export class Player { + constructor(private readonly socket: SocketIO.Socket) {} + + public get socketId(): string { + return this.socket.id; + } +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..140b170 --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "noImplicitAny": false, + "target": "es6", + "downlevelIteration": true, + "allowJs": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "module": "commonjs", + "esModuleInterop": true, + "baseUrl": ".", + "paths": { + "*": ["node_modules/*"] + } + }, + "include": ["src/**/*"] +} diff --git a/backend/webpack.config.js b/backend/webpack.config.js new file mode 100644 index 0000000..6d3e794 --- /dev/null +++ b/backend/webpack.config.js @@ -0,0 +1,75 @@ +const path = require('path'); +const TerserJSPlugin = require('terser-webpack-plugin'); +const nodeExternals = require('webpack-node-externals'); + +const PATHS = { + entryPoint: path.resolve(__dirname, 'src/main.ts'), + bundles: path.resolve(__dirname, 'dist'), +}; + +module.exports = { + entry: { + main: [PATHS.entryPoint], + }, + externals: [nodeExternals()], + target: 'node', + output: { + filename: '[name].js', + path: PATHS.bundles, + }, + devtool: 'source-map', + watchOptions: { + aggregateTimeout: 600, + ignored: /node_modules/, + }, + optimization: { + minimize: true, + usedExports: true, + minimizer: [ + new TerserJSPlugin({ + sourceMap: true, + cache: true, + test: /\.ts$/i, + terserOptions: { + ecma: 5, + warnings: true, + parse: {}, + compress: { defaults: true }, + mangle: true, + module: false, + output: null, + toplevel: true, + nameCache: null, + ie8: false, + keep_classnames: false, + keep_fnames: false, + safari10: false, + }, + }), + ], + }, + module: { + rules: [ + { + test: /\.html$/i, + use: { + loader: 'file-loader', + query: { + outputPath: '/', + name: '[name].[ext]', + }, + }, + }, + { + test: /\.ts$/, + use: { + loader: 'ts-loader', + }, + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: ['.ts', '.js'], + }, +}; diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 35401a9..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: "3.8" - -services: - declared-frontend: - init: true - image: schmelczera/declared-frontend - networks: - - network - deploy: - replicas: 2 - resources: - limits: - cpus: "0.1" - memory: 16M - reservations: - cpus: "0.1" - memory: 16M - update_config: - parallelism: 1 - failure_action: rollback - delay: 10s - monitor: 10s - restart_policy: - condition: on-failure - window: 30s - -networks: - network: - driver: overlay diff --git a/frontend/package.json b/frontend/package.json index ed8c3a3..97e8f23 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -32,7 +32,7 @@ "css-loader": "^3.5.2", "cssnano": "^4.1.10", "eslint": "^7.2.0", - "sdf-2d": "^0.3.4", + "sdf-2d": "^0.4.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-import": "^2.21.2", "eslint-plugin-prettier": "^3.1.4", @@ -57,11 +57,12 @@ "ts-loader": "^8.0.1", "typescript": "^3.8.3", "uuid": "^8.2.0", + "file-loader": "^6.1.0", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", - "webpack-dev-server": "^3.10.3" - }, - "dependencies": { - "firebase": "^7.22.0" + "webpack-dev-server": "^3.10.3", + "firebase": "^7.22.0", + "@types/socket.io-client": "^1.4.34", + "socket.io-client": "^2.3.1" } } diff --git a/frontend/src/index.ts b/frontend/src/index.ts index b148ed8..1889116 100644 --- a/frontend/src/index.ts +++ b/frontend/src/index.ts @@ -1,4 +1,6 @@ import { glMatrix } from 'gl-matrix'; +import io from 'socket.io-client'; +import { TransportEvents } from '../../shared/src/transport/transport-events'; import { Configuration } from './scripts/config/configuration'; import { Game } from './scripts/game'; import { Random } from './scripts/helper/random'; @@ -9,6 +11,17 @@ glMatrix.setMatrixArrayType(Array); const main = async () => { await Configuration.initialize(); + const socket = io(Configuration.servers[0], { + reconnectionDelayMax: 10000, + transports: ['websocket'], + }); + + socket.on('reconnect_attempt', () => { + socket.io.opts.transports = ['polling', 'websocket']; + }); + + socket.emit(TransportEvents.PlayerJoining, null); + try { Random.seed = 42; await new Game().start(); diff --git a/frontend/src/scripts/config/configuration.ts b/frontend/src/scripts/config/configuration.ts index 38d07e1..fedb2e5 100644 --- a/frontend/src/scripts/config/configuration.ts +++ b/frontend/src/scripts/config/configuration.ts @@ -1,7 +1,10 @@ -import * as firebase from 'firebase/app'; +import firebase from 'firebase/app'; import 'firebase/firebase-remote-config'; -export class Configuration { +export abstract class Configuration { + private static remoteConfig: firebase.remoteConfig.RemoteConfig; + private static initialized = false; + public static async initialize(): Promise { const firebaseConfig = { apiKey: 'AIzaSyBG85dp-AhaCW-qi_6mu77wDPSipzipIF4', @@ -12,19 +15,24 @@ export class Configuration { firebase.initializeApp(firebaseConfig); - const remoteConfig = firebase.remoteConfig(); - remoteConfig.defaultConfig = { - online_servers: 'hi', - }; + this.remoteConfig = firebase.remoteConfig(); - remoteConfig.settings = { - minimumFetchIntervalMillis: 3600 * 1000, + this.remoteConfig.settings = { + minimumFetchIntervalMillis: 0, // todo: 3600 * 1000, fetchTimeoutMillis: 15 * 1000, } as any; - await remoteConfig.ensureInitialized(); - await remoteConfig.fetchAndActivate(); + await this.remoteConfig.ensureInitialized(); + await this.remoteConfig.fetchAndActivate(); - console.log(remoteConfig.getValue('online_servers')); + this.initialized = true; + } + + public static get servers(): Array { + if (!this.initialized) { + throw new Error('Configuration should be initialized'); + } + + return JSON.parse(this.remoteConfig.getValue('online_servers').asString()); } } diff --git a/frontend/src/scripts/game.ts b/frontend/src/scripts/game.ts index 2d2434c..c7d752b 100644 --- a/frontend/src/scripts/game.ts +++ b/frontend/src/scripts/game.ts @@ -3,9 +3,12 @@ import { Circle, CircleLight, compile, + FilteringOptions, Flashlight, InvertedTunnel, Renderer, + renderNoise, + WrapOptions, } from 'sdf-2d'; import { CommandBroadcaster } from './commands/command-broadcaster'; import { MoveToCommand } from './commands/move-to'; @@ -82,6 +85,8 @@ export class Game implements IGame { } public async start(): Promise { + const noiseTexture = await renderNoise([1024, 1], 60, 1 / 8); + this.renderer = await this.rendererPromise; this.renderer.setRuntimeSettings({ isWorldInverted: true, @@ -97,6 +102,15 @@ export class Game implements IGame { ], enableHighDpiRendering: false, lightCutoffDistance: settings.lightCutoffDistance, + textures: { + noiseTexture: { + source: noiseTexture, + overrides: { + maxFilter: FilteringOptions.LINEAR, + wrapS: WrapOptions.MIRRORED_REPEAT, + }, + }, + }, }); this.initializeScene(); this.physics.start(); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 468fc39..3ce307e 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -7,6 +7,7 @@ "downlevelIteration": true, "allowJs": true, "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, "moduleResolution": "Node", "module": "es6", "lib": ["es2015", "dom"] diff --git a/shared/src/transport/transport-events.ts b/shared/src/transport/transport-events.ts new file mode 100644 index 0000000..1f4333e --- /dev/null +++ b/shared/src/transport/transport-events.ts @@ -0,0 +1,4 @@ +export enum TransportEvents { + PlayerJoining = "PlayerJoining", + PlayerSendingInfo = "PlayerSendingInfo", +}