forked from Hri7566/mpp-server-dev2
Add channels and migrate to bun
This commit is contained in:
parent
c068545958
commit
99609bf2ef
|
@ -6,9 +6,20 @@ lobbySettings:
|
|||
lobby: true
|
||||
chat: true
|
||||
crownsolo: false
|
||||
color: "#eeeeee"
|
||||
color2: "#888888"
|
||||
visible: true
|
||||
|
||||
defaultSettings:
|
||||
chat: true
|
||||
crownsolo: false
|
||||
color: "#480505"
|
||||
color2: "#000000"
|
||||
visible: true
|
||||
|
||||
lobbyRegexes:
|
||||
- "^lobby\\d\\d$"
|
||||
- "^test/.*$"
|
||||
- "^lobby[1-9]?[1-9]?$"
|
||||
- "^test/.+$"
|
||||
|
||||
lobbyBackdoor: "lolwutsecretlobbybackdoor"
|
||||
fullChannel: "test/awkward"
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
"description": "Hri7566's MPP Server",
|
||||
"main": "out/index.js",
|
||||
"scripts": {
|
||||
"start": "node .",
|
||||
"build": "npx tsc",
|
||||
"dev": "pnpm build && pnpm start"
|
||||
"start": "bun .",
|
||||
"build": "bun build ./src/index.ts --outdir=out",
|
||||
"dev": "bun run src/index.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Hri7566",
|
||||
|
@ -14,13 +14,14 @@
|
|||
"dependencies": {
|
||||
"@prisma/client": "5.2.0",
|
||||
"@t3-oss/env-core": "^0.6.1",
|
||||
"bun": "^1.0.0",
|
||||
"bun-types": "^1.0.1",
|
||||
"date-holidays": "^3.21.5",
|
||||
"dotenv": "^8.6.0",
|
||||
"events": "^3.3.0",
|
||||
"fancy-text-converter": "^1.0.9",
|
||||
"keccak": "^2.1.0",
|
||||
"mppclone-client": "^1.1.3",
|
||||
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.31.0",
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"yaml": "^2.3.2",
|
||||
"zod": "^3.22.2"
|
||||
|
|
292
pnpm-lock.yaml
292
pnpm-lock.yaml
|
@ -1,292 +0,0 @@
|
|||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@prisma/client':
|
||||
specifier: 5.2.0
|
||||
version: 5.2.0(prisma@5.2.0)
|
||||
'@t3-oss/env-core':
|
||||
specifier: ^0.6.1
|
||||
version: 0.6.1(typescript@5.2.2)(zod@3.22.2)
|
||||
date-holidays:
|
||||
specifier: ^3.21.5
|
||||
version: 3.21.5
|
||||
dotenv:
|
||||
specifier: ^8.6.0
|
||||
version: 8.6.0
|
||||
events:
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0
|
||||
fancy-text-converter:
|
||||
specifier: ^1.0.9
|
||||
version: 1.0.9
|
||||
keccak:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
mppclone-client:
|
||||
specifier: ^1.1.3
|
||||
version: 1.1.3
|
||||
uWebSockets.js:
|
||||
specifier: github:uNetworking/uWebSockets.js#v20.31.0
|
||||
version: github.com/uNetworking/uWebSockets.js/809b99d2d7d12e2cbf89b7135041e9b41ff84084
|
||||
unique-names-generator:
|
||||
specifier: ^4.7.1
|
||||
version: 4.7.1
|
||||
yaml:
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2
|
||||
zod:
|
||||
specifier: ^3.22.2
|
||||
version: 3.22.2
|
||||
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^20.5.9
|
||||
version: 20.5.9
|
||||
prisma:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0
|
||||
typescript:
|
||||
specifier: ^5.2.2
|
||||
version: 5.2.2
|
||||
|
||||
packages:
|
||||
|
||||
/@prisma/client@5.2.0(prisma@5.2.0):
|
||||
resolution: {integrity: sha512-AiTjJwR4J5Rh6Z/9ZKrBBLel3/5DzUNntMohOy7yObVnVoTNVFi2kvpLZlFuKO50d7yDspOtW6XBpiAd0BVXbQ==}
|
||||
engines: {node: '>=16.13'}
|
||||
requiresBuild: true
|
||||
peerDependencies:
|
||||
prisma: '*'
|
||||
peerDependenciesMeta:
|
||||
prisma:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@prisma/engines-version': 5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f
|
||||
prisma: 5.2.0
|
||||
dev: false
|
||||
|
||||
/@prisma/engines-version@5.2.0-25.2804dc98259d2ea960602aca6b8e7fdc03c1758f:
|
||||
resolution: {integrity: sha512-jsnKT5JIDIE01lAeCj2ghY9IwxkedhKNvxQeoyLs6dr4ZXynetD0vTy7u6wMJt8vVPv8I5DPy/I4CFaoXAgbtg==}
|
||||
dev: false
|
||||
|
||||
/@prisma/engines@5.2.0:
|
||||
resolution: {integrity: sha512-dT7FOLUCdZmq+AunLqB1Iz+ZH/IIS1Fz2THmKZQ6aFONrQD/BQ5ecJ7g2wGS2OgyUFf4OaLam6/bxmgdOBDqig==}
|
||||
requiresBuild: true
|
||||
|
||||
/@t3-oss/env-core@0.6.1(typescript@5.2.2)(zod@3.22.2):
|
||||
resolution: {integrity: sha512-KQD7qEDJtkWIWWmTVjNvk0wnHpkvAQ6CRbUxbWMFNG/fiosBQDQvtRpBNu6USxBscJCoC4z6y7P9MN52/mLOzw==}
|
||||
peerDependencies:
|
||||
typescript: '>=4.7.2'
|
||||
zod: ^3.0.0
|
||||
dependencies:
|
||||
typescript: 5.2.2
|
||||
zod: 3.22.2
|
||||
dev: false
|
||||
|
||||
/@types/node@20.5.9:
|
||||
resolution: {integrity: sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==}
|
||||
dev: true
|
||||
|
||||
/argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
dev: false
|
||||
|
||||
/astronomia@4.1.1:
|
||||
resolution: {integrity: sha512-TcJD9lUC5eAo0/Ji7rnQauX/yQbi0yZWM+JsNr77W3OA5fsrgvuFgubLMFwfw4VlZ29cu9dG/yfJbfvuTSftjg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/bindings@1.5.0:
|
||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||
dependencies:
|
||||
file-uri-to-path: 1.0.0
|
||||
dev: false
|
||||
|
||||
/caldate@2.0.5:
|
||||
resolution: {integrity: sha512-JndhrUuDuE975KUhFqJaVR1OQkCHZqpOrJur/CFXEIEhWhBMjxO85cRSK8q4FW+B+yyPq6GYua2u4KvNzTcq0w==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
moment-timezone: 0.5.43
|
||||
dev: false
|
||||
|
||||
/date-bengali-revised@2.0.2:
|
||||
resolution: {integrity: sha512-q9iDru4+TSA9k4zfm0CFHJj6nBsxP7AYgWC/qodK/i7oOIlj5K2z5IcQDtESfs/Qwqt/xJYaP86tkazd/vRptg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/date-chinese@2.1.4:
|
||||
resolution: {integrity: sha512-WY+6+Qw92ZGWFvGtStmNQHEYpNa87b8IAQ5T8VKt4wqrn24lBXyyBnWI5jAIyy7h/KVwJZ06bD8l/b7yss82Ww==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
astronomia: 4.1.1
|
||||
dev: false
|
||||
|
||||
/date-easter@1.0.2:
|
||||
resolution: {integrity: sha512-mpC1izx7lUSLYl4B88V2W57eNB4xS2ic+ahxK2AYUsaBTsCeHzT6K5ymUKzL9YPFf/GlygFqpiD4/NO1hxDsLw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/date-holidays-parser@3.4.4:
|
||||
resolution: {integrity: sha512-R5aO4oT8H51ZKdvApqHrqYEiNBrqT6tRj2PFXNcZfqMI4nxY7KKKly0ZsmquR5gY+x9ldKR8SAMdozzIInaoXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dependencies:
|
||||
astronomia: 4.1.1
|
||||
caldate: 2.0.5
|
||||
date-bengali-revised: 2.0.2
|
||||
date-chinese: 2.1.4
|
||||
date-easter: 1.0.2
|
||||
deepmerge: 4.3.1
|
||||
jalaali-js: 1.2.6
|
||||
moment-timezone: 0.5.43
|
||||
dev: false
|
||||
|
||||
/date-holidays@3.21.5:
|
||||
resolution: {integrity: sha512-5X/UK7FunfIiM/q7CwepNfzh1XkkukdZNfTPyKlD5kx01MQzJ9DqKyTcFNzlQJ+HgpAxqUqSs3+F8mwV9bzo/w==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
date-holidays-parser: 3.4.4
|
||||
js-yaml: 4.1.0
|
||||
lodash.omit: 4.5.0
|
||||
lodash.pick: 4.4.0
|
||||
prepin: 1.0.3
|
||||
dev: false
|
||||
|
||||
/deepmerge@4.3.1:
|
||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/dotenv@8.6.0:
|
||||
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/events@3.3.0:
|
||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
dev: false
|
||||
|
||||
/fancy-text-converter@1.0.9:
|
||||
resolution: {integrity: sha512-tFUAWpEfZOYhdsjILVu7c0PL9Ud9pTQmonm/2mdvFC7WcEHIYi9NYS5irJYFdBJDIRSqi64XV+IhHPc/ngxtyw==}
|
||||
dev: false
|
||||
|
||||
/file-uri-to-path@1.0.0:
|
||||
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
|
||||
dev: false
|
||||
|
||||
/inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
dev: false
|
||||
|
||||
/jalaali-js@1.2.6:
|
||||
resolution: {integrity: sha512-io974va+Qyu+UfuVX3UIAgJlxLhAMx9Y8VMfh+IG00Js7hXQo1qNQuwSiSa0xxco0SVgx5HWNkaiCcV+aZ8WPw==}
|
||||
dev: false
|
||||
|
||||
/js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
dev: false
|
||||
|
||||
/keccak@2.1.0:
|
||||
resolution: {integrity: sha512-m1wbJRTo+gWbctZWay9i26v5fFnYkOn7D5PCxJ3fZUGUEb49dE1Pm4BREUYCt/aoO6di7jeoGmhvqN9Nzylm3Q==}
|
||||
engines: {node: '>=5.12.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
bindings: 1.5.0
|
||||
inherits: 2.0.4
|
||||
nan: 2.17.0
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/lodash.omit@4.5.0:
|
||||
resolution: {integrity: sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==}
|
||||
dev: false
|
||||
|
||||
/lodash.pick@4.4.0:
|
||||
resolution: {integrity: sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==}
|
||||
dev: false
|
||||
|
||||
/moment-timezone@0.5.43:
|
||||
resolution: {integrity: sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==}
|
||||
dependencies:
|
||||
moment: 2.29.4
|
||||
dev: false
|
||||
|
||||
/moment@2.29.4:
|
||||
resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
|
||||
dev: false
|
||||
|
||||
/mppclone-client@1.1.3:
|
||||
resolution: {integrity: sha512-5DSkQmZOj823/BPwi6CQa4UWkoAX7itfNxf6L26NJS/qj9AljuKoqnIZxhtSKdak75qZd5Jgx+zD1aXflRNxHg==}
|
||||
dependencies:
|
||||
ws: 8.14.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/nan@2.17.0:
|
||||
resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==}
|
||||
dev: false
|
||||
|
||||
/prepin@1.0.3:
|
||||
resolution: {integrity: sha512-0XL2hreherEEvUy0fiaGEfN/ioXFV+JpImqIzQjxk6iBg4jQ2ARKqvC4+BmRD8w/pnpD+lbxvh0Ub+z7yBEjvA==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/prisma@5.2.0:
|
||||
resolution: {integrity: sha512-FfFlpjVCkZwrqxDnP4smlNYSH1so+CbfjgdpioFzGGqlQAEm6VHAYSzV7jJgC3ebtY9dNOhDMS2+4/1DDSM7bQ==}
|
||||
engines: {node: '>=16.13'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@prisma/engines': 5.2.0
|
||||
|
||||
/safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
dev: false
|
||||
|
||||
/typescript@5.2.2:
|
||||
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
/unique-names-generator@4.7.1:
|
||||
resolution: {integrity: sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/ws@8.14.0:
|
||||
resolution: {integrity: sha512-WR0RJE9Ehsio6U4TuM+LmunEsjQ5ncHlw4sn9ihD6RoJKZrVyH9FWV3dmnwu8B2aNib1OvG2X6adUCyFpQyWcg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/yaml@2.3.2:
|
||||
resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==}
|
||||
engines: {node: '>= 14'}
|
||||
dev: false
|
||||
|
||||
/zod@3.22.2:
|
||||
resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==}
|
||||
dev: false
|
||||
|
||||
github.com/uNetworking/uWebSockets.js/809b99d2d7d12e2cbf89b7135041e9b41ff84084:
|
||||
resolution: {tarball: https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/809b99d2d7d12e2cbf89b7135041e9b41ff84084}
|
||||
name: uWebSockets.js
|
||||
version: 20.31.0
|
||||
dev: false
|
|
@ -1,11 +1,218 @@
|
|||
// TODO Load channel config file
|
||||
import { Logger } from "../util/Logger";
|
||||
import { loadConfig } from "../util/config";
|
||||
import {
|
||||
ChannelSettingValue,
|
||||
ChannelSettings,
|
||||
Participant
|
||||
} from "../util/types";
|
||||
import { Socket } from "../ws/Socket";
|
||||
import { app, findSocketByPartID } from "../ws/server";
|
||||
import { validateChannelSettings } from "./settings";
|
||||
|
||||
interface ChannelConfig {
|
||||
forceLoad: string[];
|
||||
lobbySettings: Partial<ChannelSettings>;
|
||||
defaultSettings: Partial<ChannelSettings>;
|
||||
lobbyRegexes: string[];
|
||||
lobbyBackdoor: string;
|
||||
fullChannel: string;
|
||||
}
|
||||
|
||||
export const config = loadConfig<ChannelConfig>("config/channels.yml", {
|
||||
forceLoad: ["lobby", "test/awkward"],
|
||||
lobbySettings: {
|
||||
lobby: true,
|
||||
chat: true,
|
||||
crownsolo: false,
|
||||
visible: true
|
||||
},
|
||||
defaultSettings: {
|
||||
chat: true,
|
||||
crownsolo: false,
|
||||
color: "#480505",
|
||||
color2: "#000000",
|
||||
visible: true
|
||||
},
|
||||
// TODO Test this regex
|
||||
lobbyRegexes: ["^lobby[1-9]?[1-9]?$", "^test/.+$"],
|
||||
lobbyBackdoor: "lolwutsecretlobbybackdoor",
|
||||
fullChannel: "test/awkward"
|
||||
});
|
||||
|
||||
export const channelList = new Array<Channel>();
|
||||
|
||||
export class Channel {
|
||||
constructor(private _id: string) {}
|
||||
private settings: Partial<ChannelSettings> = config.defaultSettings;
|
||||
private ppl = new Array<Participant>();
|
||||
|
||||
getID() {
|
||||
public logger: Logger;
|
||||
|
||||
// TODO Add the crown
|
||||
|
||||
constructor(
|
||||
private _id: string,
|
||||
set: Partial<ChannelSettings> = config.defaultSettings
|
||||
) {
|
||||
this.logger = new Logger("Channel - " + _id);
|
||||
// Verify default settings just in case
|
||||
this.changeSettings(this.settings, true);
|
||||
|
||||
if (this.isLobby()) {
|
||||
set = config.lobbySettings;
|
||||
}
|
||||
|
||||
this.changeSettings(set);
|
||||
}
|
||||
|
||||
public getID() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
isLobby() {}
|
||||
public isLobby() {
|
||||
for (const reg of config.lobbyRegexes) {
|
||||
let exp = new RegExp(reg, "g");
|
||||
|
||||
if (this.getID().match(exp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public changeSettings(
|
||||
set: Partial<ChannelSettings>,
|
||||
admin: boolean = false
|
||||
) {
|
||||
if (!admin) {
|
||||
if (set.lobby) set.lobby = undefined;
|
||||
if (set.owner_id) set.owner_id = undefined;
|
||||
}
|
||||
|
||||
// Verify settings
|
||||
const validSettings = validateChannelSettings(set);
|
||||
|
||||
for (const key of Object.keys(validSettings)) {
|
||||
// Setting is valid?
|
||||
if ((validSettings as Record<string, boolean>)[key]) {
|
||||
// Change setting
|
||||
(this.settings as Record<string, ChannelSettingValue>)[key] = (
|
||||
set as Record<string, ChannelSettingValue>
|
||||
)[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public join(socket: Socket) {
|
||||
const part = socket.getParticipant();
|
||||
|
||||
// Unknown side-effects, but for type safety...
|
||||
if (!part) return;
|
||||
|
||||
let hasChangedChannel = false;
|
||||
|
||||
this.logger.debug("Has user?", this.hasUser(part));
|
||||
|
||||
// Is user in this channel?
|
||||
if (this.hasUser(part)) {
|
||||
// Alreay in channel, disconnect old
|
||||
|
||||
const oldSocket = findSocketByPartID(part.id);
|
||||
|
||||
if (oldSocket) {
|
||||
oldSocket.destroy();
|
||||
}
|
||||
|
||||
// Add to channel
|
||||
this.ppl.push(part);
|
||||
hasChangedChannel = true;
|
||||
} else {
|
||||
// Are we full?
|
||||
if (!this.isFull()) {
|
||||
// Add to channel
|
||||
this.ppl.push(part);
|
||||
hasChangedChannel = true;
|
||||
} else {
|
||||
// Put us in full channel
|
||||
socket.setChannel(config.fullChannel);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChangedChannel) {
|
||||
// Is user in any channel that isn't this one?
|
||||
for (const ch of channelList) {
|
||||
if (ch == this) continue;
|
||||
if (ch.hasUser(part)) {
|
||||
ch.leave(socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug("Participant list:", this.ppl);
|
||||
|
||||
// Send our data back
|
||||
socket.sendArray([
|
||||
{
|
||||
m: "ch",
|
||||
ch: this.getInfo(),
|
||||
p: part.id,
|
||||
ppl: this.getParticipantList()
|
||||
}
|
||||
]);
|
||||
|
||||
// TODO Broadcast channel update
|
||||
}
|
||||
|
||||
public leave(socket: Socket) {
|
||||
this.logger.debug("Leave called");
|
||||
const part = socket.getParticipant();
|
||||
|
||||
// Same as above...
|
||||
if (!part) return;
|
||||
|
||||
if (this.hasUser(part)) {
|
||||
this.ppl.splice(this.ppl.indexOf(part), 1);
|
||||
}
|
||||
// TODO Broadcast channel update
|
||||
}
|
||||
|
||||
public isFull() {
|
||||
// TODO Use limit setting
|
||||
|
||||
if (this.isLobby() && this.ppl.length >= 20) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public getInfo() {
|
||||
return {
|
||||
_id: this.getID(),
|
||||
id: this.getID(),
|
||||
count: this.ppl.length,
|
||||
settings: this.settings
|
||||
};
|
||||
}
|
||||
|
||||
public getParticipantList() {
|
||||
return this.ppl;
|
||||
}
|
||||
|
||||
public hasUser(part: Participant) {
|
||||
const foundPart = this.ppl.find(p => p._id == part._id);
|
||||
return !!foundPart;
|
||||
}
|
||||
}
|
||||
|
||||
// Forceloader
|
||||
let hasFullChannel = false;
|
||||
|
||||
for (const id of config.forceLoad) {
|
||||
channelList.push(new Channel(id));
|
||||
if (id == config.fullChannel) hasFullChannel = true;
|
||||
}
|
||||
|
||||
if (!hasFullChannel) {
|
||||
channelList.push(new Channel(config.fullChannel));
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import env from "./util/env";
|
||||
import { app } from "./ws/server";
|
||||
// import { app } from "./ws/server";
|
||||
import "./ws/server";
|
||||
import { Logger } from "./util/Logger";
|
||||
|
||||
const logger = new Logger("Main");
|
||||
|
||||
// No IPv6 (yet)
|
||||
app.listen("0.0.0.0", env.PORT, () => {
|
||||
logger.info("Listening on :" + env.PORT);
|
||||
});
|
||||
|
|
|
@ -100,9 +100,10 @@ declare interface ChannelInfo {
|
|||
id: string;
|
||||
_id: string;
|
||||
crown?: Crown;
|
||||
settings: ChannelSettings;
|
||||
settings: Partial<ChannelSettings>;
|
||||
}
|
||||
|
||||
// Events copied from Hri7566/mppclone-client typedefs
|
||||
declare interface ServerEvents {
|
||||
a: {
|
||||
m: "a";
|
||||
|
@ -246,6 +247,7 @@ declare interface ClientEvents {
|
|||
m: "ch";
|
||||
p: string;
|
||||
ch: ChannelInfo;
|
||||
ppl: Participant[];
|
||||
};
|
||||
|
||||
custom: {
|
||||
|
@ -284,6 +286,7 @@ declare interface ClientEvents {
|
|||
};
|
||||
|
||||
notification: {
|
||||
m: "notification";
|
||||
duration?: number;
|
||||
class?: string;
|
||||
id?: string;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { WebSocket } from "uWebSockets.js";
|
||||
import { createColor, createID, createUserID } from "../util/id";
|
||||
import { decoder, encoder } from "../util/helpers";
|
||||
import EventEmitter from "events";
|
||||
|
@ -8,6 +7,8 @@ import { createUser, readUser } from "../data/user";
|
|||
import { eventGroups } from "./events";
|
||||
import { loadConfig } from "../util/config";
|
||||
import { Gateway } from "./Gateway";
|
||||
import { Channel, channelList } from "../channel/Channel";
|
||||
import { ServerWebSocket } from "bun";
|
||||
|
||||
interface UsersConfig {
|
||||
defaultName: string;
|
||||
|
@ -31,17 +32,17 @@ export class Socket extends EventEmitter {
|
|||
|
||||
public desiredChannel: {
|
||||
_id: string | undefined;
|
||||
set: Partial<ChannelSettings>;
|
||||
set: Partial<ChannelSettings> | undefined;
|
||||
} = {
|
||||
_id: undefined,
|
||||
set: {}
|
||||
};
|
||||
|
||||
constructor(private ws: WebSocket<unknown>) {
|
||||
super();
|
||||
this.ip = decoder.decode(this.ws.getRemoteAddressAsText());
|
||||
public currentChannelID: string | undefined;
|
||||
|
||||
// Participant ID
|
||||
constructor(private ws: ServerWebSocket<unknown>) {
|
||||
super();
|
||||
this.ip = ws.remoteAddress; // Participant ID
|
||||
this.id = createID();
|
||||
|
||||
// User ID
|
||||
|
@ -61,9 +62,40 @@ export class Socket extends EventEmitter {
|
|||
return this._id;
|
||||
}
|
||||
|
||||
public setChannel(_id: string, set: Partial<ChannelSettings>) {
|
||||
public getParticipantID() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public setChannel(_id: string, set?: Partial<ChannelSettings>) {
|
||||
if (this.isDestroyed()) return;
|
||||
|
||||
this.desiredChannel._id = _id;
|
||||
this.desiredChannel.set = set;
|
||||
|
||||
let channel;
|
||||
try {
|
||||
for (const ch of channelList.values()) {
|
||||
if (ch.getID() == this.desiredChannel._id) {
|
||||
channel = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
// Does channel exist?
|
||||
if (channel) {
|
||||
// Exists, join normally
|
||||
channel.join(this);
|
||||
} else {
|
||||
// Doesn't exist, join with crown
|
||||
channel = new Channel(
|
||||
this.desiredChannel._id,
|
||||
this.desiredChannel.set
|
||||
);
|
||||
|
||||
channel.join(this);
|
||||
// TODO Give the crown upon joining
|
||||
}
|
||||
}
|
||||
|
||||
private bindEventListeners() {
|
||||
|
@ -80,7 +112,8 @@ export class Socket extends EventEmitter {
|
|||
public sendArray<EventID extends keyof ClientEvents>(
|
||||
arr: ClientEvents[EventID][]
|
||||
) {
|
||||
this.ws.send(encoder.encode(JSON.stringify(arr)));
|
||||
if (this.isDestroyed()) return;
|
||||
this.ws.send(JSON.stringify(arr));
|
||||
}
|
||||
|
||||
private async loadUser() {
|
||||
|
@ -142,7 +175,27 @@ export class Socket extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
public getParticipantID() {
|
||||
return this.id;
|
||||
private destroyed = false;
|
||||
|
||||
public destroy() {
|
||||
// Socket was closed or should be closed, clear data
|
||||
|
||||
// Simulate closure
|
||||
try {
|
||||
this.ws.close();
|
||||
} catch (err) {}
|
||||
|
||||
if (this.currentChannelID) {
|
||||
const foundCh = channelList.find(
|
||||
ch => ch.getID() == this.currentChannelID
|
||||
);
|
||||
if (foundCh) foundCh.leave(this);
|
||||
}
|
||||
|
||||
this.destroyed = true;
|
||||
}
|
||||
|
||||
public isDestroyed() {
|
||||
return this.destroyed == true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
import "./events/user";
|
||||
import "./events/admin";
|
||||
// Bun hoists import, but not require?
|
||||
require("./events/user");
|
||||
require("./events/admin");
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { ServerEventListener } from "../../../../util/types";
|
||||
import { eventGroups } from "../../../events";
|
||||
|
||||
export const ch: ServerEventListener<"ch"> = {
|
||||
id: "ch",
|
||||
callback: (msg, socket) => {
|
||||
// Switch channel
|
||||
if (!msg._id) return;
|
||||
socket.setChannel(msg._id, msg.set);
|
||||
}
|
||||
};
|
|
@ -12,7 +12,7 @@ export const hi: ServerEventListener<"hi"> = {
|
|||
_id: socket.getUserID(),
|
||||
name: "Anonymous",
|
||||
color: "#777",
|
||||
id: socket.getParticipantID()
|
||||
id: socket.getUserID()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ export const EVENTGROUP_USER = new EventGroup("user");
|
|||
|
||||
import { hi } from "./handlers/hi";
|
||||
import { devices } from "./handlers/devices";
|
||||
import { ch } from "./handlers/ch";
|
||||
|
||||
EVENTGROUP_USER.add(hi);
|
||||
EVENTGROUP_USER.add(devices);
|
||||
EVENTGROUP_USER.add(ch);
|
||||
|
||||
eventGroups.push(EVENTGROUP_USER);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { WebSocket } from "uWebSockets.js";
|
||||
import { Logger } from "../util/Logger";
|
||||
import { Socket } from "./Socket";
|
||||
import { hasOwn } from "../util/helpers";
|
||||
|
|
222
src/ws/server.ts
222
src/ws/server.ts
|
@ -1,97 +1,165 @@
|
|||
import {
|
||||
App,
|
||||
DEDICATED_COMPRESSOR_8KB,
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
WebSocket
|
||||
} from "uWebSockets.js";
|
||||
// import {
|
||||
// App,
|
||||
// DEDICATED_COMPRESSOR_8KB,
|
||||
// HttpRequest,
|
||||
// HttpResponse,
|
||||
// WebSocket
|
||||
// } from "uWebSockets.js";
|
||||
import { Logger } from "../util/Logger";
|
||||
import { createUserID } from "../util/id";
|
||||
import { readFileSync, lstatSync } from "fs";
|
||||
import { join } from "path/posix";
|
||||
import fs from "fs";
|
||||
// import { join } from "path";
|
||||
import path from "path";
|
||||
import { handleMessage } from "./message";
|
||||
import { decoder } from "../util/helpers";
|
||||
import { Socket } from "./Socket";
|
||||
import { serve, file } from "bun";
|
||||
import env from "../util/env";
|
||||
|
||||
const logger = new Logger("WebSocket Server");
|
||||
|
||||
export const app = App()
|
||||
.get("/*", async (res, req) => {
|
||||
const url = req.getUrl();
|
||||
const ip = decoder.decode(res.getRemoteAddressAsText());
|
||||
// logger.debug(`${req.getMethod()} ${url} ${ip}`);
|
||||
// res.writeStatus(`200 OK`).end("HI!");
|
||||
const file = join("./public/", url);
|
||||
const usersByPartID = new Map<string, Socket>();
|
||||
|
||||
// TODO Cleaner file serving
|
||||
try {
|
||||
const stats = lstatSync(file);
|
||||
export function findSocketByPartID(id: string) {
|
||||
for (const key of usersByPartID.keys()) {
|
||||
if (key == id) return usersByPartID.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
let data;
|
||||
if (!stats.isDirectory()) {
|
||||
data = readFileSync(file);
|
||||
}
|
||||
// Original uWebSockets code
|
||||
// export const app = App()
|
||||
// .get("/*", async (res, req) => {
|
||||
// const url = req.getUrl();
|
||||
// const ip = decoder.decode(res.getRemoteAddressAsText());
|
||||
// // logger.debug(`${req.getMethod()} ${url} ${ip}`);
|
||||
// // res.writeStatus(`200 OK`).end("HI!");
|
||||
// const file = join("./public/", url);
|
||||
|
||||
// logger.debug(filename);
|
||||
// // TODO Cleaner file serving
|
||||
// try {
|
||||
// const stats = lstatSync(file);
|
||||
|
||||
if (!data) {
|
||||
const index = readFileSync("./public/index.html");
|
||||
// let data;
|
||||
// if (!stats.isDirectory()) {
|
||||
// data = readFileSync(file);
|
||||
// }
|
||||
|
||||
if (!index) {
|
||||
return void res
|
||||
.writeStatus(`404 Not Found`)
|
||||
.end("uh oh :(");
|
||||
// // logger.debug(filename);
|
||||
|
||||
// if (!data) {
|
||||
// const index = readFileSync("./public/index.html");
|
||||
|
||||
// if (!index) {
|
||||
// return void res
|
||||
// .writeStatus(`404 Not Found`)
|
||||
// .end("uh oh :(");
|
||||
// } else {
|
||||
// return void res.writeStatus(`200 OK`).end(index);
|
||||
// }
|
||||
// }
|
||||
|
||||
// res.writeStatus(`200 OK`).end(data);
|
||||
// } catch (err) {
|
||||
// logger.warn("Unable to serve file at", file);
|
||||
// logger.error(err);
|
||||
// const index = readFileSync("./public/index.html");
|
||||
|
||||
// if (!index) {
|
||||
// return void res.writeStatus(`404 Not Found`).end("uh oh :(");
|
||||
// } else {
|
||||
// return void res.writeStatus(`200 OK`).end(index);
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .ws("/*", {
|
||||
// idleTimeout: 25,
|
||||
// maxBackpressure: 1024,
|
||||
// maxPayloadLength: 8192,
|
||||
// compression: DEDICATED_COMPRESSOR_8KB,
|
||||
|
||||
// open: ((ws: WebSocket<unknown> & { socket: Socket }) => {
|
||||
// ws.socket = new Socket(ws);
|
||||
// // logger.debug("Connection at " + ws.socket.getIP());
|
||||
|
||||
// usersByPartID.set(ws.socket.getParticipantID(), ws.socket);
|
||||
// }) as (ws: WebSocket<unknown>) => void,
|
||||
|
||||
// message: ((
|
||||
// ws: WebSocket<unknown> & { socket: Socket },
|
||||
// message,
|
||||
// isBinary
|
||||
// ) => {
|
||||
// const msg = decoder.decode(message);
|
||||
// handleMessage(ws.socket, msg);
|
||||
// }) as (
|
||||
// ws: WebSocket<unknown>,
|
||||
// message: ArrayBuffer,
|
||||
// isBinary: boolean
|
||||
// ) => void,
|
||||
|
||||
// close: ((
|
||||
// ws: WebSocket<unknown> & { socket: Socket },
|
||||
// code: number,
|
||||
// message: ArrayBuffer
|
||||
// ) => {
|
||||
// logger.debug("Close called");
|
||||
// ws.socket.destroy();
|
||||
// usersByPartID.delete(ws.socket.getParticipantID());
|
||||
// }) as (
|
||||
// ws: WebSocket<unknown>,
|
||||
// code: number,
|
||||
// message: ArrayBuffer
|
||||
// ) => void
|
||||
// });
|
||||
|
||||
export const app = Bun.serve({
|
||||
port: env.PORT,
|
||||
fetch: (req, server) => {
|
||||
if (server.upgrade(req)) {
|
||||
return;
|
||||
} else {
|
||||
const url = new URL(req.url).pathname;
|
||||
// const ip = decoder.decode(res.getRemoteAddressAsText());
|
||||
// logger.debug(`${req.getMethod()} ${url} ${ip}`);
|
||||
// res.writeStatus(`200 OK`).end("HI!");
|
||||
const file = path.join("./public/", url);
|
||||
|
||||
try {
|
||||
if (fs.lstatSync(file).isFile()) {
|
||||
const data = Bun.file(file);
|
||||
|
||||
if (data) {
|
||||
return new Response(data);
|
||||
} else {
|
||||
return new Response(Bun.file("./public/index.html"));
|
||||
}
|
||||
} else {
|
||||
return void res.writeStatus(`200 OK`).end(index);
|
||||
return new Response(Bun.file("./public/index.html"));
|
||||
}
|
||||
}
|
||||
|
||||
res.writeStatus(`200 OK`).end(data);
|
||||
} catch (err) {
|
||||
logger.warn("Unable to serve file at", file);
|
||||
logger.error(err);
|
||||
const index = readFileSync("./public/index.html");
|
||||
|
||||
if (!index) {
|
||||
return void res.writeStatus(`404 Not Found`).end("uh oh :(");
|
||||
} else {
|
||||
return void res.writeStatus(`200 OK`).end(index);
|
||||
} catch (err) {
|
||||
return new Response(Bun.file("./public/index.html"));
|
||||
}
|
||||
}
|
||||
})
|
||||
.ws("/*", {
|
||||
idleTimeout: 180,
|
||||
maxBackpressure: 1024,
|
||||
maxPayloadLength: 8192,
|
||||
compression: DEDICATED_COMPRESSOR_8KB,
|
||||
},
|
||||
websocket: {
|
||||
open: ws => {
|
||||
const socket = new Socket(ws);
|
||||
(ws as unknown as any).socket = socket;
|
||||
logger.debug("Connection at " + socket.getIP());
|
||||
|
||||
open: ((ws: WebSocket<unknown> & { socket: Socket }) => {
|
||||
ws.socket = new Socket(ws);
|
||||
// logger.debug("Connection at " + ws.socket.getIP());
|
||||
}) as (ws: WebSocket<unknown>) => void,
|
||||
usersByPartID.set(socket.getParticipantID(), socket);
|
||||
},
|
||||
|
||||
message: ((
|
||||
ws: WebSocket<unknown> & { socket: Socket },
|
||||
message,
|
||||
isBinary
|
||||
) => {
|
||||
const msg = decoder.decode(message);
|
||||
handleMessage(ws.socket, msg);
|
||||
}) as (
|
||||
ws: WebSocket<unknown>,
|
||||
message: ArrayBuffer,
|
||||
isBinary: boolean
|
||||
) => void,
|
||||
message: (ws, message) => {
|
||||
const msg = message.toString();
|
||||
handleMessage((ws as unknown as any).socket, msg);
|
||||
},
|
||||
|
||||
close: ((
|
||||
ws: WebSocket<unknown> & { socket: Socket },
|
||||
code: number,
|
||||
message: ArrayBuffer
|
||||
) => {
|
||||
// TODO handle close event
|
||||
}) as (
|
||||
ws: WebSocket<unknown>,
|
||||
code: number,
|
||||
message: ArrayBuffer
|
||||
) => void
|
||||
});
|
||||
close: (ws, code, message) => {
|
||||
logger.debug("Close called");
|
||||
const socket = (ws as unknown as any).socket as Socket;
|
||||
socket.destroy();
|
||||
usersByPartID.delete(socket.getParticipantID());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,8 +11,10 @@
|
|||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
"target": "ESNext",
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"lib": ["ESNext"],
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
|
@ -23,20 +25,25 @@
|
|||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
"moduleDetection": "force",
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
// "module": "commonjs" /* Specify what module code is generated. */,
|
||||
"module": "ESNext",
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"rootDir": "./src/",
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"moduleResolution": "Bundler",
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
"types": ["bun-types"],
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
"allowImportingTsExtensions": true,
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
|
@ -60,6 +67,7 @@
|
|||
"outDir": "./out/",
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
"noEmit": true,
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
|
|
Loading…
Reference in New Issue