Modernise & make fun #3

Merged
andras merged 18 commits from asch/modernise into main 2026-06-21 10:43:50 +01:00
40 changed files with 5721 additions and 19306 deletions
Showing only changes of commit 913abb7642 - Show all commits

Bump dependencies

Andras Schmelczer 2026-06-04 17:46:28 +01:00

View file

@ -1,4 +0,0 @@
**/node_modules/**/*.js
node_modules/**/*.js
**/package-lock.json
package-lock.json

View file

@ -1,37 +0,0 @@
{
"root": true,
"env": {
"browser": true,
"es2020": true
},
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"plugins": ["unused-imports", "@typescript-eslint", "json-format", "prettier"],
"settings": {
"json/sort-package-json": true
},
"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",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-var-requires": "off"
}
}

View file

@ -4,10 +4,10 @@
# The frontend is a static site and is NOT built here — see .forgejo/workflows. # The frontend is a static site and is NOT built here — see .forgejo/workflows.
# ---- Stage 1: build the shared lib, then bundle the server ------------------- # ---- Stage 1: build the shared lib, then bundle the server -------------------
# Node 14 matches the project toolchain (webpack 4 / TypeScript 4, see # Node 22 (current LTS) matches the project toolchain (webpack 5 / TypeScript 6,
# .devcontainer). The full (non-slim) image carries the build tools that # see .devcontainer). The full (non-slim) image carries the build tools that
# socket.io's optional native deps (bufferutil/utf-8-validate) compile against. # socket.io's optional native deps (bufferutil/utf-8-validate) compile against.
FROM node:14-bullseye AS build FROM node:22-bookworm AS build
WORKDIR /app WORKDIR /app
# `shared` is consumed by the backend as `file:../shared` and is bundled into # `shared` is consumed by the backend as `file:../shared` and is bundled into
@ -28,7 +28,7 @@ RUN cd backend && npm run build
RUN cd backend && npm prune --production RUN cd backend && npm prune --production
# ---- Stage 2: minimal runtime ---------------------------------------------- # ---- Stage 2: minimal runtime ----------------------------------------------
FROM node:14-bullseye-slim FROM node:22-bookworm-slim
WORKDIR /app WORKDIR /app
ENV NODE_ENV=production ENV NODE_ENV=production

View file

@ -14,35 +14,27 @@
"try-build": "npm run build && cd dist && node main.js && cd -" "try-build": "npm run build && cd dist && node main.js && cd -"
}, },
"dependencies": { "dependencies": {
"@types/config": "0.0.36", "cors": "^2.8.6",
"cors": "^2.8.5", "express": "^5.2.1",
"express": "^4.17.1",
"gl-matrix": "3.3.0", "gl-matrix": "3.3.0",
"minimist": "^1.2.5", "minimist": "^1.2.8",
"socket.io": "^2.3.0", "socket.io": "^4.8.3",
"socket.io-msgpack-parser": "^2.0.0" "socket.io-msgpack-parser": "^3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/cors": "^2.8.7", "@types/cors": "^2.8.19",
"@types/express": "^4.17.8", "@types/express": "^5.0.6",
"@types/minimist": "^1.2.0", "@types/minimist": "^1.2.5",
"@types/node": "^14.11.2", "@types/node": "^22.0.0",
"@types/socket.io": "^2.1.11", "clean-webpack-plugin": "^4.0.0",
"clean-webpack-plugin": "^3.0.0", "concurrently": "^10.0.3",
"concurrently": "^5.3.0", "nodemon": "^3.1.14",
"file-loader": "^6.1.0",
"html-webpack-plugin": "^4.5.0",
"nodemon": "^2.0.4",
"raw-loader": "^4.0.1",
"resolve-url-loader": "^3.1.1",
"shared": "file:../shared", "shared": "file:../shared",
"terser-webpack-plugin": "^2.3.5", "terser-webpack-plugin": "^5.6.1",
"ts-config-webpack-plugin": "^2.0.0", "ts-loader": "^9.6.0",
"ts-loader": "^8.0.3", "typescript": "^6.0.3",
"typescript": "^4.0.3", "webpack": "^5.107.2",
"webpack": "^4.44.2", "webpack-cli": "^7.0.3",
"webpack-cli": "^3.3.12", "webpack-node-externals": "^3.0.0"
"webpack-dev-server": "^3.11.0",
"webpack-node-externals": "^2.5.2"
} }
} }

View file

@ -1,7 +1,10 @@
import { Command } from 'shared'; import { Command } from 'shared';
export class GeneratePointsCommand extends Command { export class GeneratePointsCommand extends Command {
public constructor(public readonly decla: number, public readonly red: number) { public constructor(
public readonly decla: number,
public readonly red: number,
) {
super(); super();
} }
} }

View file

@ -1,5 +1,5 @@
import { PhysicalContainer } from './physics/containers/physical-container'; import { PhysicalContainer } from './physics/containers/physical-container';
import ioserver from 'socket.io'; import { Server, Socket } from 'socket.io';
import { import {
TransportEvents, TransportEvents,
deserialize, deserialize,
@ -62,7 +62,10 @@ export class GameServer extends CommandReceiver {
[GeneratePointsCommand.type]: this.addPoints.bind(this), [GeneratePointsCommand.type]: this.addPoints.bind(this),
}; };
constructor(private readonly io: ioserver.Server, private options: Options) { constructor(
private readonly io: Server,
private options: Options,
) {
super(); super();
this.serverName = options.name; this.serverName = options.name;
@ -70,7 +73,7 @@ export class GameServer extends CommandReceiver {
this.initialize(); this.initialize();
io.on('connection', (socket: SocketIO.Socket) => { io.on('connection', (socket: Socket) => {
socket.on(TransportEvents.PlayerJoining, (playerInfo: PlayerInformation) => { socket.on(TransportEvents.PlayerJoining, (playerInfo: PlayerInformation) => {
try { try {
const player = this.players.createPlayer(playerInfo, socket); const player = this.players.createPlayer(playerInfo, socket);

View file

@ -1,4 +1,4 @@
import ioserver from 'socket.io'; import { Server as IoServer } from 'socket.io';
import express from 'express'; import express from 'express';
import { Server } from 'http'; import { Server } from 'http';
import cors from 'cors'; import cors from 'cors';
@ -22,7 +22,13 @@ Random.seed = options.seed;
const app = express(); const app = express();
const server = new Server(app); const server = new Server(app);
const io = ioserver(server, { parser } as any); const io = new IoServer(server, {
parser,
cors: {
origin: true,
credentials: true,
},
} as any);
const gameServer = new GameServer(io, options); const gameServer = new GameServer(io, options);

View file

@ -88,9 +88,10 @@ export class CirclePhysical extends CommandReceiver implements Circle, DynamicPh
); );
} }
public stepManually( public stepManually(deltaTimeInSeconds: number): {
deltaTimeInSeconds: number, hitObject: GameObject | undefined;
): { hitObject: GameObject | undefined; velocity: vec2 } { velocity: vec2;
} {
let delta = vec2.scale(vec2.create(), this.velocity, deltaTimeInSeconds); let delta = vec2.scale(vec2.create(), this.velocity, deltaTimeInSeconds);
this.radius += vec2.length(delta); this.radius += vec2.length(delta);

View file

@ -72,8 +72,8 @@ export class PlanetPhysical extends PlanetBase implements StaticPhysical {
return Math.abs(this.ownership - 0.5) < 0.1 return Math.abs(this.ownership - 0.5) < 0.1
? CharacterTeam.neutral ? CharacterTeam.neutral
: this.ownership < 0.5 : this.ownership < 0.5
? CharacterTeam.decla ? CharacterTeam.decla
: CharacterTeam.red; : CharacterTeam.red;
} }
private timeSinceLastPointGeneration = 0; private timeSinceLastPointGeneration = 0;

View file

@ -5,7 +5,10 @@ import { StaticPhysical } from '../physicals/static-physical';
class Node { class Node {
public left: Node | null = null; public left: Node | null = null;
public right: Node | null = null; public right: Node | null = null;
constructor(public object: StaticPhysical, public parent: Node | null) {} constructor(
public object: StaticPhysical,
public parent: Node | null,
) {}
} }
export class BoundingBoxTree { export class BoundingBoxTree {

View file

@ -56,9 +56,8 @@ export abstract class PlayerBase extends CommandReceiver {
); );
const playerBoundingBox = getBoundingBoxOfCircle(playerBoundingCircle); const playerBoundingBox = getBoundingBoxOfCircle(playerBoundingCircle);
const possibleIntersectors = this.objectContainer.findIntersecting( const possibleIntersectors =
playerBoundingBox, this.objectContainer.findIntersecting(playerBoundingBox);
);
if (!isCircleIntersecting(playerBoundingCircle, possibleIntersectors)) { if (!isCircleIntersecting(playerBoundingCircle, possibleIntersectors)) {
return playerPosition; return playerPosition;
} }

View file

@ -1,4 +1,5 @@
import { CharacterTeam, PlayerInformation, Random, settings, Command } from 'shared'; import { CharacterTeam, PlayerInformation, Random, settings, Command } from 'shared';
import { Socket } from 'socket.io';
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';
@ -29,7 +30,7 @@ export class PlayerContainer {
} }
} }
public createPlayer(playerInfo: PlayerInformation, socket: SocketIO.Socket): Player { public createPlayer(playerInfo: PlayerInformation, socket: Socket): Player {
if (this._players.length === this.playerMaxCount) { if (this._players.length === this.playerMaxCount) {
throw new Error('Too many players'); throw new Error('Too many players');
} }

View file

@ -23,6 +23,7 @@ import {
PropertyUpdatesForObject, PropertyUpdatesForObject,
PrimaryActionCommand, PrimaryActionCommand,
} from 'shared'; } from 'shared';
import { Socket } from 'socket.io';
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 { CharacterPhysical } from '../objects/character-physical'; import { CharacterPhysical } from '../objects/character-physical';
@ -51,7 +52,7 @@ export class Player extends PlayerBase {
playerContainer: PlayerContainer, playerContainer: PlayerContainer,
objectContainer: PhysicalContainer, objectContainer: PhysicalContainer,
team: CharacterTeam, team: CharacterTeam,
private readonly socket: SocketIO.Socket, private readonly socket: Socket,
) { ) {
super(playerInfo, playerContainer, objectContainer, team); super(playerInfo, playerContainer, objectContainer, team);
this.createCharacter(); this.createCharacter();

View file

@ -1,13 +1,14 @@
{ {
"compilerOptions": { "compilerOptions": {
"outDir": "dist", "outDir": "dist",
"rootDir": "src",
"skipLibCheck": true, "skipLibCheck": true,
"target": "es6", "target": "es6",
"esModuleInterop": true, "esModuleInterop": true,
"strict": true, "strict": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"downlevelIteration": true,
"moduleResolution": "node", "moduleResolution": "node",
"ignoreDeprecations": "6.0",
"module": "commonjs", "module": "commonjs",
"lib": ["dom", "es2017"], "lib": ["dom", "es2017"],
"typeRoots": ["./types", "./node_modules/@types"] "typeRoots": ["./types", "./node_modules/@types"]

View file

@ -32,9 +32,9 @@ module.exports = (env, argv) => ({
minimize: argv.mode !== 'development', minimize: argv.mode !== 'development',
minimizer: [ minimizer: [
new TerserJSPlugin({ new TerserJSPlugin({
sourceMap: false,
test: /\.js$/,
exclude: /node_modules/, exclude: /node_modules/,
// The custom serialization protocol keys on class names, so they must
// survive minification (see shared/src/serialization).
terserOptions: { terserOptions: {
keep_classnames: true, keep_classnames: true,
}, },
@ -50,16 +50,6 @@ module.exports = (env, argv) => ({
], ],
module: { module: {
rules: [ rules: [
{
test: /\.html$/,
use: {
loader: 'file-loader',
query: {
outputPath: '/',
name: '[name].[ext]',
},
},
},
{ {
test: /\.ts$/, test: /\.ts$/,
use: { use: {

32
eslint.config.js Normal file
View file

@ -0,0 +1,32 @@
// Flat config (ESLint 9+/10). Ported from the former .eslintrc.json.
const tseslint = require('typescript-eslint');
const prettierRecommended = require('eslint-plugin-prettier/recommended');
const unusedImports = require('eslint-plugin-unused-imports');
module.exports = tseslint.config(
{
ignores: ['**/node_modules/**', '**/dist/**', '**/lib/**'],
},
...tseslint.configs.recommended,
prettierRecommended,
{
files: ['**/*.ts'],
plugins: {
'unused-imports': unusedImports,
},
languageOptions: {
sourceType: 'module',
},
rules: {
'no-unused-vars': 'off',
'unused-imports/no-unused-imports': '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',
'@typescript-eslint/no-empty-function': 'off',
// Renamed from the former no-var-requires; keep require() allowed.
'@typescript-eslint/no-require-imports': 'off',
},
},
);

25290
frontend/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -22,32 +22,31 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@types/socket.io-client": "^1.4.34", "@plausible-analytics/tracker": "^0.4.5",
"clean-webpack-plugin": "^3.0.0", "autoprefixer": "^10.5.0",
"common-config-webpack-plugin": "^2.0.1", "clean-webpack-plugin": "^4.0.0",
"css-loader": "^1.0.1", "copy-webpack-plugin": "^14.0.0",
"css-loader": "^7.1.4",
"file-loader": "^6.2.0",
"gl-matrix": "3.3.0", "gl-matrix": "3.3.0",
"html-webpack-inline-source-plugin": "^1.0.0-beta.2", "html-inline-css-webpack-plugin": "^1.11.2",
"html-webpack-inline-svg-plugin": "^2.3.0", "html-webpack-plugin": "^5.6.7",
"html-webpack-plugin": "^4.5.0", "mini-css-extract-plugin": "^2.10.2",
"image-config-webpack-plugin": "^2.0.0", "postcss": "^8.5.15",
"mini-css-extract-plugin": "^0.9.0", "postcss-loader": "^8.2.1",
"postcss-loader": "^3.0.0",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"resolve-url-loader": "^3.1.1", "sass": "^1.100.0",
"sass": "^1.27.0", "sass-loader": "^17.0.0",
"sass-loader": "^8.0.2", "sdf-2d": "^0.7.6",
"sdf-2d": "^0.7.2",
"shared": "file:../shared", "shared": "file:../shared",
"socket.io-client": "^2.3.1", "socket.io-client": "^4.8.3",
"socket.io-msgpack-parser": "^2.0.0", "socket.io-msgpack-parser": "^3.0.2",
"source-map-loader": "^1.1.1", "source-map-loader": "^5.0.0",
"svg-url-loader": "^6.0.0", "terser-webpack-plugin": "^5.6.1",
"terser-webpack-plugin": "^4.2.2", "ts-loader": "^9.6.0",
"ts-config-webpack-plugin": "^2.0.0", "typescript": "^6.0.3",
"typescript": "^4.0.3", "webpack": "^5.107.2",
"webpack": "^4.43.0", "webpack-cli": "^7.0.3",
"webpack-cli": "^3.3.11", "webpack-dev-server": "^5.2.4"
"webpack-dev-server": "^3.11.0"
} }
} }

View file

@ -2,3 +2,14 @@ declare module '*.mp3' {
const content: string; const content: string;
export default content; export default content;
} }
// Asset imports handled by webpack loaders. TypeScript 5.x+ requires ambient
// declarations for side-effect imports of these, so declare them here.
declare module '*.png';
declare module '*.ico';
declare module '*.svg';
declare module '*.scss';
// webpack's `mode` inlines `process.env.NODE_ENV` via DefinePlugin. Declare just
// that one global so browser code can read it without pulling in all of @types/node.
declare const process: { env: { NODE_ENV?: string } };

View file

@ -68,13 +68,13 @@
<img <img
title="Enable sounds" title="Enable sounds"
alt="speaker with sound waves" alt="speaker with sound waves"
src="../static/volume.svg" src="static/volume.svg"
/> />
</label> </label>
<label for="enable-music"> <label for="enable-music">
<input id="enable-music" type="checkbox" /> <input id="enable-music" type="checkbox" />
<img title="Enable music" alt="music note" src="../static/music.svg" /> <img title="Enable music" alt="music note" src="static/music.svg" />
</label> </label>
<label for="enable-vibration"> <label for="enable-vibration">
@ -82,7 +82,7 @@
<img <img
title="Enable vibration on mobile" title="Enable vibration on mobile"
alt="vibrating mobile" alt="vibrating mobile"
src="../static/vibrate.svg" src="static/vibrate.svg"
/> />
</label> </label>
@ -90,7 +90,7 @@
title="Exit from current game" title="Exit from current game"
id="logout" id="logout"
alt="logout" alt="logout"
src="../static/logout.svg" src="static/logout.svg"
/> />
</section> </section>
</div> </div>
@ -101,7 +101,7 @@
id="toggle-settings" id="toggle-settings"
class="icon" class="icon"
alt="toggle-settings" alt="toggle-settings"
src="../static/settings.svg" src="static/settings.svg"
/> />
</div> </div>
@ -109,17 +109,17 @@
class="full-screen-controllers" class="full-screen-controllers"
id="minimize" id="minimize"
alt="minimize-application" alt="minimize-application"
src="../static/minimize.svg" src="static/minimize.svg"
/> />
<img <img
class="full-screen-controllers" class="full-screen-controllers"
id="maximize" id="maximize"
alt="maximize-application" alt="maximize-application"
src="../static/maximize.svg" src="static/maximize.svg"
/> />
<div id="spinner-container"> <div id="spinner-container">
<img id="spinner" alt="waiting" src="../static/spinner.svg" /> <img id="spinner" alt="waiting" src="static/spinner.svg" />
</div> </div>
</body> </body>
</html> </html>

Some files were not shown because too many files have changed in this diff Show more