Compare commits
41 Commits
v0.0.5
...
feature/mo
Author | SHA1 | Date | |
---|---|---|---|
9e55ac7990 | |||
8b51f6e16a | |||
e2ded75017 | |||
0cead14e71 | |||
f2905247ff | |||
e735522d76 | |||
94c619192b | |||
a8a98d0083 | |||
646c40db27 | |||
cfaea6ec7c | |||
9030550c0f | |||
c71c393a51 | |||
4d192bd5ec | |||
f46fff5b69 | |||
f5cae0db9f | |||
c627ea2412 | |||
30145e1662 | |||
778e4402ba | |||
e2bc151881 | |||
2b022ee4e0 | |||
3802b2cf9d | |||
2ee6a72984 | |||
f50e4c75a9 | |||
51cbe87755 | |||
d6aa8da2de | |||
7b4674587a | |||
8e652f8dcb | |||
5349e2ffe5 | |||
66759a87f2 | |||
b7748c254f | |||
ee080b6987 | |||
67021f9ada | |||
3270ea8729 | |||
04710edb73 | |||
d398764b6d | |||
be479b11c5 | |||
45964ba7f3 | |||
e7e187da7c | |||
64c0d82d48 | |||
12292ea4f2 | |||
0c8df9d175 |
@ -25,4 +25,8 @@ DEFAULT_CHARACTER_POS_Y="0"
|
|||||||
SMTP_HOST=my.directonline.io
|
SMTP_HOST=my.directonline.io
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
SMTP_USER=no-reply@noxious.gg
|
SMTP_USER=no-reply@noxious.gg
|
||||||
SMTP_PASSWORD=""
|
SMTP_PASSWORD=""
|
||||||
|
|
||||||
|
# SSL
|
||||||
|
#PUBLIC_KEY_PATH=
|
||||||
|
#PRIVATE_KEY_PATH=
|
16
Dockerfile
16
Dockerfile
@ -1,16 +0,0 @@
|
|||||||
FROM node:lts-alpine
|
|
||||||
|
|
||||||
# Install packages
|
|
||||||
RUN apk update
|
|
||||||
RUN apk add --no-cache tmux coreutils
|
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
RUN npm ci
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Modify CMD to use tmux
|
|
||||||
CMD npx mikro-orm-esm migration:up && npm run start
|
|
@ -5,7 +5,7 @@ This is the server for the Noxious game.
|
|||||||
## Projects requirements
|
## Projects requirements
|
||||||
|
|
||||||
- NodeJS 20.x or higher
|
- NodeJS 20.x or higher
|
||||||
- MySQL 8.x or higher
|
- MariaDB 11.x or higher
|
||||||
- Redis 7.x or higher
|
- Redis 7.x or higher
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
ports:
|
|
||||||
- "${PORT}:${PORT}"
|
|
||||||
environment:
|
|
||||||
- ENV=${ENV}
|
|
||||||
- HOST=${HOST}
|
|
||||||
- PORT=${PORT}
|
|
||||||
- JWT_SECRET=${JWT_SECRET}
|
|
||||||
- CLIENT_URL=${CLIENT_URL}
|
|
||||||
- REDIS_URL=${REDIS_URL}
|
|
||||||
- DB_HOST=${DB_HOST}
|
|
||||||
- DB_USER=${DB_USER}
|
|
||||||
- DB_PASS=${DB_PASS}
|
|
||||||
- DB_PORT=${DB_PORT}
|
|
||||||
- DB_NAME=${DB_NAME}
|
|
||||||
- ALLOW_DIAGONAL_MOVEMENT=${ALLOW_DIAGONAL_MOVEMENT}
|
|
||||||
- DEFAULT_CHARACTER_ZONE=${DEFAULT_CHARACTER_ZONE}
|
|
||||||
- DEFAULT_CHARACTER_POS_X=${DEFAULT_CHARACTER_POS_X}
|
|
||||||
- DEFAULT_CHARACTER_POS_Y=${DEFAULT_CHARACTER_POS_Y}
|
|
||||||
- SMTP_HOST=${SMTP_HOST}
|
|
||||||
- SMTP_PORT=${SMTP_PORT}
|
|
||||||
- SMTP_USER=${SMTP_USER}
|
|
||||||
- SMTP_PASSWORD=${SMTP_PASSWORD}
|
|
||||||
volumes:
|
|
||||||
- app-public:/user/src/app/public
|
|
||||||
- app-logs:/user/src/app/logs
|
|
||||||
depends_on:
|
|
||||||
- mariadb
|
|
||||||
- redis
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- app-network
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.app.rule=Host(`${HOST}`)"
|
|
||||||
- "traefik.http.routers.app.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.app.tls.certresolver=le"
|
|
||||||
- "traefik.http.services.app.loadbalancer.server.port=${PORT}"
|
|
||||||
- "traefik.http.routers.app.middlewares=websocket"
|
|
||||||
|
|
||||||
traefik:
|
|
||||||
image: traefik:v3.3.3
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
- "8080:8080"
|
|
||||||
volumes:
|
|
||||||
- traefik_data:/data
|
|
||||||
- ./traefik.toml:/etc/traefik/traefik.toml
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- app-network
|
|
||||||
|
|
||||||
mariadb:
|
|
||||||
image: mariadb:lts
|
|
||||||
environment:
|
|
||||||
- MARIADB_USER=${DB_USER}
|
|
||||||
- MARIADB_PASSWORD=${DB_PASS}
|
|
||||||
- MARIADB_DATABASE=${DB_NAME}
|
|
||||||
- MARIADB_RANDOM_ROOT_PASSWORD=yes
|
|
||||||
volumes:
|
|
||||||
- mariadb-data:/var/lib/mysql
|
|
||||||
ports:
|
|
||||||
- "${DB_PORT}:3306"
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- app-network
|
|
||||||
command: [ 'mariadbd', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ]
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7.4.2-alpine
|
|
||||||
command: redis-server --appendonly yes
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- app-network
|
|
||||||
|
|
||||||
networks:
|
|
||||||
app-network:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
app-public:
|
|
||||||
app-logs:
|
|
||||||
mariadb-data:
|
|
||||||
redis-data:
|
|
||||||
traefik_data:
|
|
160
package-lock.json
generated
160
package-lock.json
generated
@ -13,10 +13,12 @@
|
|||||||
"@mikro-orm/reflection": "^6.4.2",
|
"@mikro-orm/reflection": "^6.4.2",
|
||||||
"@types/ioredis": "^4.28.10",
|
"@types/ioredis": "^4.28.10",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
"bufferutil": "^4.0.9",
|
||||||
"bullmq": "^5.13.2",
|
"bullmq": "^5.13.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
|
"https": "^1.0.0",
|
||||||
"ioredis": "^5.4.1",
|
"ioredis": "^5.4.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.9.15",
|
||||||
@ -26,6 +28,7 @@
|
|||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
|
"utf-8-validate": "^6.0.5",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -1753,17 +1756,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz",
|
||||||
"integrity": "sha512-vBz65tJgRrA1Q5gWlRfvoH+w943dq9K1p1yDBY2pc+a1nbBLZp7fB9+Hk8DaALUbzjqlMfgaqlVPT1REJdkt/w==",
|
"integrity": "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.23.0",
|
"@typescript-eslint/scope-manager": "8.24.0",
|
||||||
"@typescript-eslint/type-utils": "8.23.0",
|
"@typescript-eslint/type-utils": "8.24.0",
|
||||||
"@typescript-eslint/utils": "8.23.0",
|
"@typescript-eslint/utils": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
"@typescript-eslint/visitor-keys": "8.24.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@ -1783,16 +1786,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.0.tgz",
|
||||||
"integrity": "sha512-h2lUByouOXFAlMec2mILeELUbME5SZRN/7R9Cw2RD2lRQQY08MWMM+PmVVKKJNK1aIwqTo9t/0CvOxwPbRIE2Q==",
|
"integrity": "sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.23.0",
|
"@typescript-eslint/scope-manager": "8.24.0",
|
||||||
"@typescript-eslint/types": "8.23.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.23.0",
|
"@typescript-eslint/typescript-estree": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
"@typescript-eslint/visitor-keys": "8.24.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1808,14 +1811,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz",
|
||||||
"integrity": "sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw==",
|
"integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.23.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.23.0"
|
"@typescript-eslint/visitor-keys": "8.24.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -1826,14 +1829,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz",
|
||||||
"integrity": "sha512-iIuLdYpQWZKbiH+RkCGc6iu+VwscP5rCtQ1lyQ7TYuKLrcZoeJVpcLiG8DliXVkUxirW/PWlmS+d6yD51L9jvA==",
|
"integrity": "sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.23.0",
|
"@typescript-eslint/typescript-estree": "8.24.0",
|
||||||
"@typescript-eslint/utils": "8.23.0",
|
"@typescript-eslint/utils": "8.24.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.0.1"
|
"ts-api-utils": "^2.0.1"
|
||||||
},
|
},
|
||||||
@ -1850,9 +1853,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz",
|
||||||
"integrity": "sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ==",
|
"integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1864,14 +1867,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz",
|
||||||
"integrity": "sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ==",
|
"integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.23.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.23.0",
|
"@typescript-eslint/visitor-keys": "8.24.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@ -1891,16 +1894,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz",
|
||||||
"integrity": "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA==",
|
"integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "8.23.0",
|
"@typescript-eslint/scope-manager": "8.24.0",
|
||||||
"@typescript-eslint/types": "8.23.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.23.0"
|
"@typescript-eslint/typescript-estree": "8.24.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -1915,13 +1918,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz",
|
||||||
"integrity": "sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ==",
|
"integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.23.0",
|
"@typescript-eslint/types": "8.24.0",
|
||||||
"eslint-visitor-keys": "^4.2.0"
|
"eslint-visitor-keys": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -2377,6 +2380,19 @@
|
|||||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/bufferutil": {
|
||||||
|
"version": "4.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
|
||||||
|
"integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-gyp-build": "^4.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bullmq": {
|
"node_modules/bullmq": {
|
||||||
"version": "5.40.2",
|
"version": "5.40.2",
|
||||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.2.tgz",
|
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.2.tgz",
|
||||||
@ -3210,9 +3226,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "9.20.0",
|
"version": "9.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz",
|
||||||
"integrity": "sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA==",
|
"integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -3863,9 +3879,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.4",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
"integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==",
|
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -4261,6 +4277,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/https": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
@ -5108,9 +5130,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/long": {
|
"node_modules/long": {
|
||||||
"version": "5.2.4",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/long/-/long-5.3.0.tgz",
|
||||||
"integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==",
|
"integrity": "sha512-5vvY5yF1zF/kXk+L94FRiTDa1Znom46UjPCH6/XbSvS8zBKMFBHTJk8KDMqJ+2J6QezQFi7k1k8v21ClJYHPaw==",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
@ -5428,6 +5450,17 @@
|
|||||||
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
|
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/node-gyp-build": {
|
||||||
|
"version": "4.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||||
|
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"node-gyp-build": "bin.js",
|
||||||
|
"node-gyp-build-optional": "optional.js",
|
||||||
|
"node-gyp-build-test": "build-test.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-gyp-build-optional-packages": {
|
"node_modules/node-gyp-build-optional-packages": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
|
||||||
@ -5894,9 +5927,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.4.2",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.0.tgz",
|
||||||
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
"integrity": "sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -7063,9 +7096,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/type-fest": {
|
"node_modules/type-fest": {
|
||||||
"version": "4.33.0",
|
"version": "4.34.1",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.34.1.tgz",
|
||||||
"integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==",
|
"integrity": "sha512-6kSc32kT0rbwxD6QL1CYe8IqdzN/J/ILMrNK+HMQCKH3insCDRY/3ITb0vcBss0a3t72fzh2YSzj8ko1HgwT3g==",
|
||||||
"license": "(MIT OR CC0-1.0)",
|
"license": "(MIT OR CC0-1.0)",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
@ -7253,6 +7286,19 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/utf-8-validate": {
|
||||||
|
"version": "6.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.5.tgz",
|
||||||
|
"integrity": "sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-gyp-build": "^4.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/utils-merge": {
|
"node_modules/utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
@ -23,18 +23,20 @@
|
|||||||
"#events/*": "./src/events/*.js"
|
"#events/*": "./src/events/*.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mikro-orm/cli": "^6.4.2",
|
||||||
"@mikro-orm/core": "^6.4.2",
|
"@mikro-orm/core": "^6.4.2",
|
||||||
"@mikro-orm/mariadb": "^6.4.2",
|
"@mikro-orm/mariadb": "^6.4.2",
|
||||||
"@mikro-orm/migrations": "^6.4.2",
|
"@mikro-orm/migrations": "^6.4.2",
|
||||||
"@mikro-orm/mysql": "^6.4.2",
|
"@mikro-orm/mysql": "^6.4.2",
|
||||||
"@mikro-orm/reflection": "^6.4.2",
|
"@mikro-orm/reflection": "^6.4.2",
|
||||||
"@mikro-orm/cli": "^6.4.2",
|
|
||||||
"@types/ioredis": "^4.28.10",
|
"@types/ioredis": "^4.28.10",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
"bufferutil": "^4.0.9",
|
||||||
"bullmq": "^5.13.2",
|
"bullmq": "^5.13.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
|
"https": "^1.0.0",
|
||||||
"ioredis": "^5.4.1",
|
"ioredis": "^5.4.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.9.15",
|
||||||
@ -44,6 +46,7 @@
|
|||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
|
"utf-8-validate": "^6.0.5",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import type { TSocket } from '#application/types'
|
import type { TSocket } from '#application/types'
|
||||||
@ -8,12 +9,26 @@ import CharacterRepository from '#repositories/characterRepository'
|
|||||||
|
|
||||||
export abstract class BaseEvent {
|
export abstract class BaseEvent {
|
||||||
protected readonly logger = Logger.type(LoggerType.GAME)
|
protected readonly logger = Logger.type(LoggerType.GAME)
|
||||||
|
private lastActionTimes: Map<string, number> = new Map()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly io: Server,
|
readonly io: Server,
|
||||||
readonly socket: TSocket
|
readonly socket: TSocket
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
protected isThrottled(actionId: string, throttleTime: number): boolean {
|
||||||
|
const now = Date.now()
|
||||||
|
const lastActionTime = this.lastActionTimes.get(actionId) || 0
|
||||||
|
|
||||||
|
if (now - lastActionTime < throttleTime) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastActionTimes.set(actionId, now)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
protected async getCharacter(): Promise<Character | null> {
|
protected async getCharacter(): Promise<Character | null> {
|
||||||
const characterRepository = new CharacterRepository()
|
const characterRepository = new CharacterRepository()
|
||||||
return characterRepository.getById(this.socket.characterId!)
|
return characterRepository.getById(this.socket.characterId!)
|
||||||
@ -25,14 +40,14 @@ export abstract class BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected emitError(message: string): void {
|
protected emitError(message: string): void {
|
||||||
this.socket.emit('notification', { title: 'Server message', message })
|
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Server message', message })
|
||||||
this.logger.error('Base event error', `Player ${this.socket.userId}: ${message}`)
|
this.logger.error('Base event error', `Player ${this.socket.userId}: ${message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleError(context: string, error: unknown): void {
|
protected handleError(context: string, error: unknown): void {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
const errorMessage = error instanceof Error ? error.message : error && typeof error === 'object' && 'toString' in error ? error.toString() : String(error)
|
const errorMessage = error instanceof Error ? error.message : error && typeof error === 'object' && 'toString' in error ? error.toString() : String(error)
|
||||||
this.socket.emit('notification', { title: 'Server message', message: `Server error occured. Please contact the server administrator.` })
|
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Server message', message: `Server error occured. Please contact the server administrator.` })
|
||||||
this.logger.error('Base event error', errorMessage)
|
this.logger.error('Base event error', errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,10 @@ class config {
|
|||||||
static SMTP_PORT: number = process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT) : 587
|
static SMTP_PORT: number = process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT) : 587
|
||||||
static SMTP_USER: string = process.env.SMTP_USER || 'no-reply@noxious.gg'
|
static SMTP_USER: string = process.env.SMTP_USER || 'no-reply@noxious.gg'
|
||||||
static SMTP_PASSWORD: string = process.env.SMTP_PASSWORD || 'password'
|
static SMTP_PASSWORD: string = process.env.SMTP_PASSWORD || 'password'
|
||||||
|
|
||||||
|
// SSL
|
||||||
|
static PUBLIC_KEY_PATH: string = process.env.PUBLIC_KEY_PATH || ''
|
||||||
|
static PRIVATE_KEY_PATH: string = process.env.PRIVATE_KEY_PATH || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config
|
export default config
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import * as readline from 'readline'
|
import * as readline from 'readline'
|
||||||
|
|
||||||
export class ConsolePrompt {
|
export class ConsolePrompt {
|
||||||
@ -10,7 +11,7 @@ export class ConsolePrompt {
|
|||||||
output: process.stdout
|
output: process.stdout
|
||||||
})
|
})
|
||||||
|
|
||||||
this.rl.on('close', () => {
|
this.rl.on(SocketEvent.CLOSE, () => {
|
||||||
this.isClosed = true
|
this.isClosed = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ export class LogReader {
|
|||||||
end: newPosition
|
end: newPosition
|
||||||
})
|
})
|
||||||
|
|
||||||
stream.on('data', (data) => {
|
stream.on(SocketEvent.DATA, (data) => {
|
||||||
console.log(`[${filename}]`)
|
console.log(`[${filename}]`)
|
||||||
console.log(data.toString()) //
|
console.log(data.toString()) //
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { MikroORM } from '@mikro-orm/mysql'
|
import { MikroORM } from '@mikro-orm/mariadb'
|
||||||
|
// import { MikroORM } from '@mikro-orm/mysql'
|
||||||
|
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
import config from '#root/mikro-orm.config'
|
import config from '#root/mikro-orm.config'
|
||||||
|
@ -1,17 +1,57 @@
|
|||||||
export enum SocketEvent {
|
export enum SocketEvent {
|
||||||
CHARACTER_CONNECT = 1,
|
CLOSE = '52',
|
||||||
CHARACTER_MOVE = 2,
|
DATA = '51',
|
||||||
CHARACTER_MOVE_ERROR = 3,
|
CHARACTER_CONNECT = '50',
|
||||||
CHARACTER_TELEPORT = 4,
|
CHARACTER_CREATE = '49',
|
||||||
ZONE_CHARACTER_LEAVE = 5,
|
CHARACTER_DELETE = '48',
|
||||||
ZONE_CHARACTER_JOIN = 6,
|
CHARACTER_LIST = '47',
|
||||||
ZONE_CHARACTER_LIST = 7,
|
GM_CHARACTERHAIR_CREATE = '46',
|
||||||
ZONE_CHARACTER_DELETE = 8,
|
GM_CHARACTERHAIR_REMOVE = '45',
|
||||||
ZONE_CHARACTER_CREATE = 9,
|
GM_CHARACTERHAIR_LIST = '44',
|
||||||
ZONE_CHARACTER_UPDATE = 10,
|
GM_CHARACTERHAIR_UPDATE = '43',
|
||||||
ZONE_CHARACTER_HAIR_UPDATE = 11,
|
GM_CHARACTERTYPE_CREATE = '42',
|
||||||
ZONE_CHARACTER_HAIR_LIST = 12,
|
GM_CHARACTERTYPE_REMOVE = '41',
|
||||||
ZONE_CHARACTER_TELEPORT = 13
|
GM_CHARACTERTYPE_LIST = '40',
|
||||||
|
GM_CHARACTERTYPE_UPDATE = '39',
|
||||||
|
GM_ITEM_CREATE = '38',
|
||||||
|
GM_ITEM_REMOVE = '37',
|
||||||
|
GM_ITEM_LIST = '36',
|
||||||
|
GM_ITEM_UPDATE = '35',
|
||||||
|
GM_MAPOBJECT_LIST = '34',
|
||||||
|
GM_MAPOBJECT_REMOVE = '33',
|
||||||
|
GM_MAPOBJECT_UPDATE = '32',
|
||||||
|
GM_MAPOBJECT_UPLOAD = '31',
|
||||||
|
GM_SPRITE_COPY = '30',
|
||||||
|
GM_SPRITE_CREATE = '29',
|
||||||
|
GM_SPRITE_DELETE = '28',
|
||||||
|
GM_SPRITE_LIST = '27',
|
||||||
|
GM_SPRITE_UPDATE = '26',
|
||||||
|
GM_TILE_DELETE = '25',
|
||||||
|
GM_TILE_LIST = '24',
|
||||||
|
GM_TILE_UPDATE = '23',
|
||||||
|
GM_TILE_UPLOAD = '22',
|
||||||
|
GM_MAP_CREATE = '21',
|
||||||
|
GM_MAP_DELETE = '20',
|
||||||
|
GM_MAP_REQUEST = '19',
|
||||||
|
GM_MAP_UPDATE = '18',
|
||||||
|
MAP_CHARACTER_MOVEERROR = '17',
|
||||||
|
DISCONNECT = '16',
|
||||||
|
USER_DISCONNECT = '15',
|
||||||
|
LOGIN = '14',
|
||||||
|
LOGGED_IN = '13',
|
||||||
|
NOTIFICATION = '12',
|
||||||
|
DATE = '11',
|
||||||
|
FAILED = '10',
|
||||||
|
COMPLETED = '9',
|
||||||
|
CONNECTION = '8',
|
||||||
|
WEATHER = '7',
|
||||||
|
CHARACTER_DISCONNECT = '6',
|
||||||
|
MAP_CHARACTER_ATTACK = '5',
|
||||||
|
MAP_CHARACTER_TELEPORT = '4',
|
||||||
|
MAP_CHARACTER_JOIN = '3',
|
||||||
|
MAP_CHARACTER_LEAVE = '2',
|
||||||
|
MAP_CHARACTER_MOVE = '1',
|
||||||
|
CHAT_MESSAGE = '0',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ItemType {
|
export enum ItemType {
|
||||||
|
@ -9,7 +9,7 @@ class Storage {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.rootDir = process.cwd()
|
this.rootDir = process.cwd()
|
||||||
this.baseDir = config.ENV === 'development' ? 'src' : 'dist'
|
this.baseDir = config.ENV === 'development' ? 'src' : 'src'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import { BaseCommand } from '#application/base/baseCommand'
|
import { BaseCommand } from '#application/base/baseCommand'
|
||||||
@ -8,6 +9,6 @@ export default class AlertCommand extends BaseCommand {
|
|||||||
public execute(input: CommandInput): void {
|
public execute(input: CommandInput): void {
|
||||||
const message: string = input.join(' ') ?? null
|
const message: string = input.join(' ') ?? null
|
||||||
if (!message) return console.log('message is required')
|
if (!message) return console.log('message is required')
|
||||||
this.io.emit('notification', { message: message })
|
this.io.emit(SocketEvent.NOTIFICATION, { message: message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import fs from 'fs'
|
wimport fs from 'fs'
|
||||||
|
|
||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -16,7 +17,7 @@ export default class CharacterConnectEvent extends BaseEvent {
|
|||||||
private readonly characterRepository = new CharacterRepository()
|
private readonly characterRepository = new CharacterRepository()
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('character:connect', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHARACTER_CONNECT, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> {
|
private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ZodError } from 'zod'
|
import { SocketEvent } from '#application/enums';
|
||||||
|
import { ZodError, z } from 'zod'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { ZCharacterCreate } from '#application/zodTypes'
|
import { ZCharacterCreate } from '#application/zodTypes'
|
||||||
@ -8,65 +9,77 @@ import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
|||||||
import MapRepository from '#repositories/mapRepository'
|
import MapRepository from '#repositories/mapRepository'
|
||||||
import UserRepository from '#repositories/userRepository'
|
import UserRepository from '#repositories/userRepository'
|
||||||
|
|
||||||
|
const MAX_CHARACTERS = 4
|
||||||
|
|
||||||
export default class CharacterCreateEvent extends BaseEvent {
|
export default class CharacterCreateEvent extends BaseEvent {
|
||||||
|
private readonly userRepository: UserRepository = new UserRepository()
|
||||||
|
private readonly characterRepository: CharacterRepository = new CharacterRepository()
|
||||||
|
private readonly characterTypeRepository: CharacterTypeRepository = new CharacterTypeRepository()
|
||||||
|
private readonly mapRepository: MapRepository = new MapRepository()
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('character:create', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHARACTER_CREATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: any): Promise<any> {
|
private async handleEvent(data: z.infer<typeof ZCharacterCreate>, callback: (success: boolean) => void): Promise<void> {
|
||||||
// zod validate
|
|
||||||
try {
|
try {
|
||||||
data = ZCharacterCreate.parse(data)
|
const validatedData = ZCharacterCreate.parse(data)
|
||||||
|
await this.createCharacter(validatedData)
|
||||||
const userRepository = new UserRepository()
|
callback(true)
|
||||||
const characterRepository = new CharacterRepository()
|
} catch (error: unknown) {
|
||||||
const characterTypeRepository = new CharacterTypeRepository()
|
this.returnError(error)
|
||||||
const mapRepository = new MapRepository()
|
callback(false)
|
||||||
|
|
||||||
const user = await userRepository.getById(this.socket.userId!)
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return this.socket.emit('notification', { title: 'Error', message: 'You are not logged in' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if character name already exists
|
|
||||||
const characterExists = await characterRepository.getByName(data.name)
|
|
||||||
|
|
||||||
if (characterExists) {
|
|
||||||
return this.socket.emit('notification', { title: 'Error', message: 'Character name already exists' })
|
|
||||||
}
|
|
||||||
|
|
||||||
let characters: Character[] = await characterRepository.getByUserId(user.getId())
|
|
||||||
|
|
||||||
if (characters.length >= 4) {
|
|
||||||
return this.socket.emit('notification', { title: 'Error', message: 'You can only create 4 characters' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// @TODO: Change to default location
|
|
||||||
const map = await mapRepository.getFirst()
|
|
||||||
|
|
||||||
// @TODO: Change to selected character type
|
|
||||||
const characterType = await characterTypeRepository.getFirst()
|
|
||||||
|
|
||||||
const newCharacter = new Character()
|
|
||||||
await newCharacter.setName(data.name).setUser(user).setMap(map!).setCharacterType(characterType).save()
|
|
||||||
|
|
||||||
if (!newCharacter) {
|
|
||||||
return this.socket.emit('notification', { title: 'Error', message: 'Failed to create character. Please try again (later).' })
|
|
||||||
}
|
|
||||||
|
|
||||||
characters = [...characters, newCharacter]
|
|
||||||
|
|
||||||
this.socket.emit('character:create:success')
|
|
||||||
this.socket.emit('character:list', characters)
|
|
||||||
|
|
||||||
this.logger.info('character:create success')
|
|
||||||
} catch (error: any) {
|
|
||||||
this.logger.error(`character:create error: ${error.message}`)
|
|
||||||
if (error instanceof ZodError) {
|
|
||||||
return this.socket.emit('notification', { title: 'Error', message: error.issues[0]!.message })
|
|
||||||
}
|
|
||||||
return this.socket.emit('notification', { title: 'Error', message: 'Could not create character. Please try again (later).' })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createCharacter(data: z.infer<typeof ZCharacterCreate>): Promise<void> {
|
||||||
|
const user = await this.userRepository.getById(this.socket.userId!)
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('You are not logged in')
|
||||||
|
}
|
||||||
|
|
||||||
|
const characterExists = await this.characterRepository.getByName(data.name)
|
||||||
|
if (characterExists) {
|
||||||
|
throw new Error('Character name already exists')
|
||||||
|
}
|
||||||
|
|
||||||
|
let characters = await this.characterRepository.getByUserId(user.getId())
|
||||||
|
if (characters.length >= MAX_CHARACTERS) {
|
||||||
|
throw new Error(`You can only create ${MAX_CHARACTERS} characters`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const map = await this.mapRepository.getFirst()
|
||||||
|
if (!map) {
|
||||||
|
throw new Error('No default map found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const characterType = await this.characterTypeRepository.getFirst()
|
||||||
|
if (!characterType) {
|
||||||
|
throw new Error('No character type found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCharacter = new Character()
|
||||||
|
await newCharacter.setName(data.name).setUser(user).setMap(map).setCharacterType(characterType).save()
|
||||||
|
characters = await this.characterRepository.getByUserId(user.getId())
|
||||||
|
|
||||||
|
this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
|
||||||
|
this.logger.info('character:create success')
|
||||||
|
}
|
||||||
|
|
||||||
|
private returnError(error: unknown): void {
|
||||||
|
this.logger.error(`character:create error: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
||||||
|
|
||||||
|
let errorMessage = 'Could not create character. Please try again later.'
|
||||||
|
|
||||||
|
if (error instanceof ZodError) {
|
||||||
|
errorMessage = error.issues[0]?.message || errorMessage
|
||||||
|
} else if (error instanceof Error) {
|
||||||
|
errorMessage = error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
|
title: 'Error',
|
||||||
|
message: errorMessage
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -14,7 +15,7 @@ type TypeResponse = {
|
|||||||
|
|
||||||
export default class CharacterDeleteEvent extends BaseEvent {
|
export default class CharacterDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('character:delete', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHARACTER_DELETE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
|
private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
|
||||||
@ -23,9 +24,9 @@ export default class CharacterDeleteEvent extends BaseEvent {
|
|||||||
await (await characterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete()
|
await (await characterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete()
|
||||||
const characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
|
const characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
|
||||||
|
|
||||||
this.socket.emit('character:list', characters)
|
this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return this.socket.emit('notification', { message: 'Character delete failed. Please try again.' })
|
return this.socket.emit(SocketEvent.NOTIFICATION, { message: 'Character delete failed. Please try again.' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Character } from '#entities/character'
|
import { Character } from '#entities/character'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
|
||||||
export default class CharacterListEvent extends BaseEvent {
|
export default class CharacterListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('character:list', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHARACTER_LIST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: any): Promise<void> {
|
private async handleEvent(data: any): Promise<void> {
|
||||||
@ -12,7 +13,7 @@ export default class CharacterListEvent extends BaseEvent {
|
|||||||
const characterRepository = new CharacterRepository()
|
const characterRepository = new CharacterRepository()
|
||||||
let characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
|
let characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
|
||||||
|
|
||||||
this.socket.emit('character:list', characters)
|
this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('character:list error', error.message)
|
this.logger.error('character:list error', error.message)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
import ChatService from '#services/chatService'
|
import ChatService from '#services/chatService'
|
||||||
@ -8,7 +9,7 @@ type TypePayload = {
|
|||||||
|
|
||||||
export default class AlertCommandEvent extends BaseEvent {
|
export default class AlertCommandEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
@ -25,7 +26,7 @@ export default class AlertCommandEvent extends BaseEvent {
|
|||||||
return callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.io.emit('notification', { title: 'Message from GM', message: args.join(' ') })
|
this.io.emit(SocketEvent.NOTIFICATION, { title: 'Message from GM', message: args.join(' ') })
|
||||||
return callback(true)
|
return callback(true)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('chat:alert_command error', error.message)
|
this.logger.error('chat:alert_command error', error.message)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import DateManager from '#managers/dateManager'
|
import DateManager from '#managers/dateManager'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
@ -9,7 +10,7 @@ type TypePayload = {
|
|||||||
|
|
||||||
export default class SetTimeCommand extends BaseEvent {
|
export default class SetTimeCommand extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -12,7 +13,7 @@ type TypePayload = {
|
|||||||
|
|
||||||
export default class TeleportCommandEvent extends BaseEvent {
|
export default class TeleportCommandEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void) {
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void) {
|
||||||
@ -29,7 +30,7 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
const args = ChatService.getArgs('teleport', data.message)
|
const args = ChatService.getArgs('teleport', data.message)
|
||||||
|
|
||||||
if (!args || args.length === 0 || args.length > 3) {
|
if (!args || args.length === 0 || args.length > 3) {
|
||||||
this.socket.emit('notification', {
|
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
title: 'Server message',
|
title: 'Server message',
|
||||||
message: 'Usage: /teleport <mapId> [x] [y]'
|
message: 'Usage: /teleport <mapId> [x] [y]'
|
||||||
})
|
})
|
||||||
@ -41,7 +42,7 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
const targetY = args[2] ? parseInt(args[2], 10) : 0
|
const targetY = args[2] ? parseInt(args[2], 10) : 0
|
||||||
|
|
||||||
if (!mapId || isNaN(targetX) || isNaN(targetY)) {
|
if (!mapId || isNaN(targetX) || isNaN(targetY)) {
|
||||||
this.socket.emit('notification', {
|
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
title: 'Server message',
|
title: 'Server message',
|
||||||
message: 'Invalid parameters. X and Y coordinates must be numbers.'
|
message: 'Invalid parameters. X and Y coordinates must be numbers.'
|
||||||
})
|
})
|
||||||
@ -51,7 +52,7 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
const mapRepository = new MapRepository()
|
const mapRepository = new MapRepository()
|
||||||
const map = await mapRepository.getById(mapId)
|
const map = await mapRepository.getById(mapId)
|
||||||
if (!map) {
|
if (!map) {
|
||||||
this.socket.emit('notification', {
|
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
title: 'Server message',
|
title: 'Server message',
|
||||||
message: 'Map not found'
|
message: 'Map not found'
|
||||||
})
|
})
|
||||||
@ -59,7 +60,7 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (character.map.id === map.id && targetX === character.positionX && targetY === character.positionY) {
|
if (character.map.id === map.id && targetX === character.positionX && targetY === character.positionY) {
|
||||||
this.socket.emit('notification', {
|
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
title: 'Server message',
|
title: 'Server message',
|
||||||
message: 'You are already at that location'
|
message: 'You are already at that location'
|
||||||
})
|
})
|
||||||
@ -74,20 +75,20 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return this.socket.emit('notification', {
|
return this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
title: 'Server message',
|
title: 'Server message',
|
||||||
message: 'Failed to teleport'
|
message: 'Failed to teleport'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.socket.emit('notification', {
|
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
title: 'Server message',
|
title: 'Server message',
|
||||||
message: `Teleported to ${map.name} (${targetX}, ${targetY})`
|
message: `Teleported to ${map.name} (${targetX}, ${targetY})`
|
||||||
})
|
})
|
||||||
this.logger.info('teleport', `Character ${character.id} teleported to map ${map.id} at position (${targetX}, ${targetY})`)
|
this.logger.info('teleport', `Character ${character.id} teleported to map ${map.id} at position (${targetX}, ${targetY})`)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Error in teleport command: ${error.message}`)
|
this.logger.error(`Error in teleport command: ${error.message}`)
|
||||||
this.socket.emit('notification', {
|
this.socket.emit(SocketEvent.NOTIFICATION, {
|
||||||
title: 'Server message',
|
title: 'Server message',
|
||||||
message: 'An error occurred while teleporting'
|
message: 'An error occurred while teleporting'
|
||||||
})
|
})
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import WeatherManager from '#managers/weatherManager'
|
import WeatherManager from '#managers/weatherManager'
|
||||||
import ChatService from '#services/chatService'
|
import ChatService from '#services/chatService'
|
||||||
@ -8,7 +9,7 @@ type TypePayload = {
|
|||||||
|
|
||||||
export default class ToggleFogCommand extends BaseEvent {
|
export default class ToggleFogCommand extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import WeatherManager from '#managers/weatherManager'
|
import WeatherManager from '#managers/weatherManager'
|
||||||
import ChatService from '#services/chatService'
|
import ChatService from '#services/chatService'
|
||||||
@ -8,7 +9,7 @@ type TypePayload = {
|
|||||||
|
|
||||||
export default class ToggleRainCommand extends BaseEvent {
|
export default class ToggleRainCommand extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import MapManager from '#managers/mapManager'
|
import MapManager from '#managers/mapManager'
|
||||||
import MapRepository from '#repositories/mapRepository'
|
import MapRepository from '#repositories/mapRepository'
|
||||||
@ -9,7 +10,7 @@ type TypePayload = {
|
|||||||
|
|
||||||
export default class ChatMessageEvent extends BaseEvent {
|
export default class ChatMessageEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import MapManager from '#managers/mapManager'
|
import MapManager from '#managers/mapManager'
|
||||||
|
|
||||||
export default class DisconnectEvent extends BaseEvent {
|
export default class DisconnectEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('disconnect', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.DISCONNECT, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(): Promise<void> {
|
private async handleEvent(): Promise<void> {
|
||||||
@ -13,7 +14,7 @@ export default class DisconnectEvent extends BaseEvent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.io.emit('user:disconnect', this.socket.userId)
|
this.io.emit(SocketEvent.USER_DISCONNECT, this.socket.userId)
|
||||||
|
|
||||||
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||||
if (!mapCharacter) {
|
if (!mapCharacter) {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { CharacterHair } from '#entities/characterHair'
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
|
|
||||||
export default class CharacterHairCreateEvent extends BaseEvent {
|
export default class CharacterHairCreateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterHair:create', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERHAIR_CREATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -9,7 +10,7 @@ interface IPayload {
|
|||||||
|
|
||||||
export default class CharacterHairDeleteEvent extends BaseEvent {
|
export default class CharacterHairDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterHair:remove', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERHAIR_REMOVE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { CharacterHair } from '#entities/characterHair'
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
@ -6,7 +7,7 @@ interface IPayload {}
|
|||||||
|
|
||||||
export default class characterHairListEvent extends BaseEvent {
|
export default class characterHairListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterHair:list', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERHAIR_LIST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -15,7 +16,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class CharacterHairUpdateEvent extends BaseEvent {
|
export default class CharacterHairUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterHair:update', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERHAIR_UPDATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { CharacterType } from '#entities/characterType'
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
|
||||||
export default class CharacterTypeCreateEvent extends BaseEvent {
|
export default class CharacterTypeCreateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterType:create', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERTYPE_CREATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {
|
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -9,7 +10,7 @@ interface IPayload {
|
|||||||
|
|
||||||
export default class CharacterTypeDeleteEvent extends BaseEvent {
|
export default class CharacterTypeDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterType:remove', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERTYPE_REMOVE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { CharacterType } from '#entities/characterType'
|
import { CharacterType } from '#entities/characterType'
|
||||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
@ -6,7 +7,7 @@ interface IPayload {}
|
|||||||
|
|
||||||
export default class CharacterTypeListEvent extends BaseEvent {
|
export default class CharacterTypeListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterType:list', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERTYPE_LIST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -16,7 +17,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class CharacterTypeUpdateEvent extends BaseEvent {
|
export default class CharacterTypeUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterType:update', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_CHARACTERTYPE_UPDATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { ItemRarity, ItemType } from '#application/enums'
|
import { ItemRarity, ItemType } from '#application/enums'
|
||||||
import { Item } from '#entities/item'
|
import { Item } from '#entities/item'
|
||||||
@ -5,7 +6,7 @@ import SpriteRepository from '#repositories/spriteRepository'
|
|||||||
|
|
||||||
export default class ItemCreateEvent extends BaseEvent {
|
export default class ItemCreateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:item:create', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_ITEM_CREATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: undefined, callback: (response: boolean, item?: any) => void): Promise<void> {
|
private async handleEvent(data: undefined, callback: (response: boolean, item?: any) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -9,7 +10,7 @@ interface IPayload {
|
|||||||
|
|
||||||
export default class ItemDeleteEvent extends BaseEvent {
|
export default class ItemDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:item:remove', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_ITEM_REMOVE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Item } from '#entities/item'
|
import { Item } from '#entities/item'
|
||||||
import ItemRepository from '#repositories/itemRepository'
|
import ItemRepository from '#repositories/itemRepository'
|
||||||
@ -6,7 +7,7 @@ interface IPayload {}
|
|||||||
|
|
||||||
export default class ItemListEvent extends BaseEvent {
|
export default class ItemListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:item:list', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_ITEM_LIST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Item[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Item[]) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -17,7 +18,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class ItemUpdateEvent extends BaseEvent {
|
export default class ItemUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:item:update', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_ITEM_UPDATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { MapObject } from '#entities/mapObject'
|
import { MapObject } from '#entities/mapObject'
|
||||||
import MapObjectRepository from '#repositories/mapObjectRepository'
|
import MapObjectRepository from '#repositories/mapObjectRepository'
|
||||||
@ -6,7 +7,7 @@ interface IPayload {}
|
|||||||
|
|
||||||
export default class MapObjectListEvent extends BaseEvent {
|
export default class MapObjectListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:mapObject:list', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAPOBJECT_LIST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: MapObject[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: MapObject[]) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
@ -12,7 +13,7 @@ interface IPayload {
|
|||||||
|
|
||||||
export default class MapObjectRemoveEvent extends BaseEvent {
|
export default class MapObjectRemoveEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:mapObject:remove', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAPOBJECT_REMOVE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -16,7 +17,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class MapObjectUpdateEvent extends BaseEvent {
|
export default class MapObjectUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:mapObject:update', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAPOBJECT_UPDATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
@ -40,7 +41,7 @@ export default class MapObjectUpdateEvent extends BaseEvent {
|
|||||||
|
|
||||||
return callback(true)
|
return callback(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.socket.emit('notification', { title: 'Error', message: 'Failed to update mapObject.' })
|
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Failed to update mapObject.' })
|
||||||
return callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import { writeFile } from 'node:fs/promises'
|
import { writeFile } from 'node:fs/promises'
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ interface IObjectData {
|
|||||||
|
|
||||||
export default class MapObjectUploadEvent extends BaseEvent {
|
export default class MapObjectUploadEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:mapObject:upload', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAPOBJECT_UPLOAD, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IObjectData, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: IObjectData, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -10,7 +11,7 @@ interface CopyPayload {
|
|||||||
|
|
||||||
export default class SpriteCopyEvent extends BaseEvent {
|
export default class SpriteCopyEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:copy', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_SPRITE_COPY, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(payload: CopyPayload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(payload: CopyPayload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -6,7 +7,7 @@ import { Sprite } from '#entities/sprite'
|
|||||||
|
|
||||||
export default class SpriteCreateEvent extends BaseEvent {
|
export default class SpriteCreateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:create', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_SPRITE_CREATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
@ -12,7 +13,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class GMSpriteDeleteEvent extends BaseEvent {
|
export default class GMSpriteDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:delete', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_SPRITE_DELETE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Sprite } from '#entities/sprite'
|
import { Sprite } from '#entities/sprite'
|
||||||
import SpriteRepository from '#repositories/spriteRepository'
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
@ -6,7 +7,7 @@ interface IPayload {}
|
|||||||
|
|
||||||
export default class SpriteListEvent extends BaseEvent {
|
export default class SpriteListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:list', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_SPRITE_LIST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Sprite[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Sprite[]) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
@ -44,7 +45,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class SpriteUpdateEvent extends BaseEvent {
|
export default class SpriteUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:update', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_SPRITE_UPDATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
|
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
@ -12,7 +13,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class GMTileDeleteEvent extends BaseEvent {
|
export default class GMTileDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:tile:delete', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_TILE_DELETE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Tile } from '#entities/tile'
|
import { Tile } from '#entities/tile'
|
||||||
import TileRepository from '#repositories/tileRepository'
|
import TileRepository from '#repositories/tileRepository'
|
||||||
@ -6,7 +7,7 @@ interface IPayload {}
|
|||||||
|
|
||||||
export default class TileListEven extends BaseEvent {
|
export default class TileListEven extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:tile:list', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_TILE_LIST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Tile[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Tile[]) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -11,7 +12,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class TileUpdateEvent extends BaseEvent {
|
export default class TileUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:tile:update', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_TILE_UPDATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import { writeFile } from 'node:fs/promises'
|
import { writeFile } from 'node:fs/promises'
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ interface ITileData {
|
|||||||
|
|
||||||
export default class TileUploadEvent extends BaseEvent {
|
export default class TileUploadEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:tile:upload', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_TILE_UPLOAD, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: ITileData, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: ITileData, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { MapCacheT } from '#entities/map'
|
import type { MapCacheT } from '#entities/map'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -11,7 +12,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class MapCreateEvent extends BaseEvent {
|
export default class MapCreateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map:create', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAP_CREATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: MapCacheT | false) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: MapCacheT | false) => void): Promise<void> {
|
||||||
@ -21,12 +22,12 @@ export default class MapCreateEvent extends BaseEvent {
|
|||||||
this.logger.info(`GM ${(await this.getCharacter())!.getId()} has created a new map via map editor.`)
|
this.logger.info(`GM ${(await this.getCharacter())!.getId()} has created a new map via map editor.`)
|
||||||
|
|
||||||
if (data.name === '') {
|
if (data.name === '') {
|
||||||
this.socket.emit('notification', { title: 'Error', message: 'Map name cannot be empty.' })
|
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Map name cannot be empty.' })
|
||||||
return callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.width < 1 || data.height < 1) {
|
if (data.width < 1 || data.height < 1) {
|
||||||
this.socket.emit('notification', { title: 'Error', message: 'Map width and height must be greater than 0.' })
|
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Map width and height must be greater than 0.' })
|
||||||
return callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ export default class MapCreateEvent extends BaseEvent {
|
|||||||
return callback(await map.cache())
|
return callback(await map.cache())
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('gm:map:create error', error.message)
|
this.logger.error('gm:map:create error', error.message)
|
||||||
this.socket.emit('notification', { message: 'Failed to create map.' })
|
this.socket.emit(SocketEvent.NOTIFICATION, { message: 'Failed to create map.' })
|
||||||
return callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -9,7 +10,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class MapDeleteEvent extends BaseEvent {
|
export default class MapDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map:delete', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAP_DELETE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -10,7 +11,7 @@ interface IPayload {
|
|||||||
|
|
||||||
export default class MapRequestEvent extends BaseEvent {
|
export default class MapRequestEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map:request', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAP_REQUEST, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -34,7 +35,7 @@ interface IPayload {
|
|||||||
|
|
||||||
export default class MapUpdateEvent extends BaseEvent {
|
export default class MapUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map:update', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.GM_MAP_UPDATE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import UserRepository from '#repositories/userRepository'
|
import UserRepository from '#repositories/userRepository'
|
||||||
|
|
||||||
export default class LoginEvent extends BaseEvent {
|
export default class LoginEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('login', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.LOGIN, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent() {
|
private async handleEvent() {
|
||||||
@ -14,7 +15,7 @@ export default class LoginEvent extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userRepository = new UserRepository()
|
const userRepository = new UserRepository()
|
||||||
this.socket.emit('logged_in', { user: userRepository.getById(this.socket.userId) })
|
this.socket.emit(SocketEvent.LOGGED_IN, { user: userRepository.getById(this.socket.userId) })
|
||||||
this.logger.info(`User logged in: ${this.socket.userId}`)
|
this.logger.info(`User logged in: ${this.socket.userId}`)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('login error: ' + error.message)
|
this.logger.error('login error: ' + error.message)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import CharacterAttackService from '#services/characterAttackService'
|
import CharacterAttackService from '#services/characterAttackService'
|
||||||
|
|
||||||
@ -5,7 +6,7 @@ export default class CharacterMove extends BaseEvent {
|
|||||||
private readonly characterAttackService = CharacterAttackService
|
private readonly characterAttackService = CharacterAttackService
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('map:character:attack', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.MAP_CHARACTER_ATTACK, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: any, callback: (response: any) => void): Promise<void> {
|
private async handleEvent(data: any, callback: (response: any) => void): Promise<void> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { MapEventTileWithTeleport } from '#application/types'
|
import type { MapEventTileWithTeleport } from '#application/types'
|
||||||
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
@ -9,11 +10,10 @@ import TeleportService from '#services/characterTeleportService'
|
|||||||
|
|
||||||
export default class CharacterMove extends BaseEvent {
|
export default class CharacterMove extends BaseEvent {
|
||||||
private readonly characterService = CharacterService
|
private readonly characterService = CharacterService
|
||||||
private readonly MOVEMENT_CANCEL_DELAY = 250
|
private readonly MOVEMENT_THROTTLE = 230 // Minimum time between movement requests
|
||||||
private movementTimeouts: Map<string, NodeJS.Timeout> = new Map()
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('map:character:move', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.MAP_CHARACTER_MOVE, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> {
|
private async handleEvent({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> {
|
||||||
@ -23,23 +23,22 @@ export default class CharacterMove extends BaseEvent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear any existing movement timeout
|
if (this.isThrottled(`movement_${this.socket.characterId}`, this.MOVEMENT_THROTTLE)) {
|
||||||
const existingTimeout = this.movementTimeouts.get(this.socket.characterId!)
|
// Only cancel current movement if the new target is different
|
||||||
if (existingTimeout) {
|
if (mapCharacter.isMoving &&
|
||||||
clearTimeout(existingTimeout)
|
(Math.floor(positionX) !== Math.floor(mapCharacter.character.positionX) ||
|
||||||
this.movementTimeouts.delete(this.socket.characterId!)
|
Math.floor(positionY) !== Math.floor(mapCharacter.character.positionY))) {
|
||||||
|
mapCharacter.isMoving = false
|
||||||
|
mapCharacter.currentPath = null
|
||||||
|
// this.finalizeMovement(mapCharacter)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If already moving, cancel current movement
|
// If already moving, cancel current movement
|
||||||
if (mapCharacter.isMoving) {
|
if (mapCharacter.isMoving && mapCharacter.currentPath && mapCharacter.currentPath.length > 2) {
|
||||||
mapCharacter.isMoving = false
|
mapCharacter.isMoving = false
|
||||||
mapCharacter.currentPath = null
|
mapCharacter.currentPath = null
|
||||||
|
|
||||||
// Add small delay before starting new movement
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
const timeout = setTimeout(resolve, this.MOVEMENT_CANCEL_DELAY)
|
|
||||||
this.movementTimeouts.set(this.socket.characterId!, timeout)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate target position is within reasonable range
|
// Validate target position is within reasonable range
|
||||||
@ -47,15 +46,9 @@ export default class CharacterMove extends BaseEvent {
|
|||||||
const currentY = mapCharacter.character.positionY
|
const currentY = mapCharacter.character.positionY
|
||||||
const distance = Math.sqrt(Math.pow(positionX - currentX, 2) + Math.pow(positionY - currentY, 2))
|
const distance = Math.sqrt(Math.pow(positionX - currentX, 2) + Math.pow(positionY - currentY, 2))
|
||||||
|
|
||||||
if (distance > 20) {
|
|
||||||
// Maximum allowed distance
|
|
||||||
this.io.in(mapCharacter.character.map.id).emit('map:character:moveError', 'Target position too far')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = await this.characterService.calculatePath(mapCharacter.character, positionX, positionY)
|
const path = await this.characterService.calculatePath(mapCharacter.character, positionX, positionY)
|
||||||
if (!path?.length) {
|
if (!path?.length) {
|
||||||
this.io.in(mapCharacter.character.map.id).emit('map:character:moveError', 'No valid path found')
|
this.io.in(mapCharacter.character.map.id).emit(SocketEvent.MAP_CHARACTER_MOVEERROR, 'No valid path found')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +74,10 @@ export default class CharacterMove extends BaseEvent {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i !== 0) {
|
||||||
|
await this.characterService.applyMovementDelay()
|
||||||
|
}
|
||||||
|
|
||||||
// Validate each step
|
// Validate each step
|
||||||
if (Math.abs(end.positionX - start.positionX) > 1 || Math.abs(end.positionY - start.positionY) > 1) {
|
if (Math.abs(end.positionX - start.positionX) > 1 || Math.abs(end.positionY - start.positionY) > 1) {
|
||||||
this.logger.error('Invalid path step detected')
|
this.logger.error('Invalid path step detected')
|
||||||
@ -102,7 +99,7 @@ export default class CharacterMove extends BaseEvent {
|
|||||||
character.setPositionX(end.positionX).setPositionY(end.positionY)
|
character.setPositionX(end.positionX).setPositionY(end.positionY)
|
||||||
|
|
||||||
// Then emit with the same properties
|
// Then emit with the same properties
|
||||||
this.io.in(character.map.id).emit('map:character:move', {
|
this.io.in(character.map.id).emit(SocketEvent.MAP_CHARACTER_MOVE, {
|
||||||
characterId: character.id,
|
characterId: character.id,
|
||||||
positionX: character.getPositionX(),
|
positionX: character.getPositionX(),
|
||||||
positionY: character.getPositionY(),
|
positionY: character.getPositionY(),
|
||||||
@ -132,7 +129,7 @@ export default class CharacterMove extends BaseEvent {
|
|||||||
|
|
||||||
private finalizeMovement(mapCharacter: MapCharacter): void {
|
private finalizeMovement(mapCharacter: MapCharacter): void {
|
||||||
mapCharacter.isMoving = false
|
mapCharacter.isMoving = false
|
||||||
this.io.in(mapCharacter.character.map.id).emit('map:character:move', {
|
this.io.in(mapCharacter.character.map.id).emit(SocketEvent.MAP_CHARACTER_MOVE, {
|
||||||
characterId: mapCharacter.character.id,
|
characterId: mapCharacter.character.id,
|
||||||
positionX: mapCharacter.character.positionX,
|
positionX: mapCharacter.character.positionX,
|
||||||
positionY: mapCharacter.character.positionY,
|
positionY: mapCharacter.character.positionY,
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import WeatherManager from '#managers/weatherManager'
|
import WeatherManager from '#managers/weatherManager'
|
||||||
|
|
||||||
export default class Weather extends BaseEvent {
|
export default class Weather extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('weather', this.handleEvent.bind(this))
|
this.socket.on(SocketEvent.WEATHER, this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(): Promise<void> {
|
private async handleEvent(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const weather = WeatherManager.getWeatherState()
|
const weather = WeatherManager.getWeatherState()
|
||||||
this.socket.emit('weather', weather)
|
this.socket.emit(SocketEvent.WEATHER, weather)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('weather error: ' + error.message)
|
this.logger.error('weather error: ' + error.message)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { Server as SocketServer } from 'socket.io'
|
import { Server as SocketServer } from 'socket.io'
|
||||||
|
|
||||||
import type { TSocket } from '#application/types'
|
import type { TSocket } from '#application/types'
|
||||||
@ -8,9 +9,9 @@ export default class SomeJob {
|
|||||||
async execute(io: SocketServer, socket?: TSocket) {
|
async execute(io: SocketServer, socket?: TSocket) {
|
||||||
// Handle the event
|
// Handle the event
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.emit('notification', { message: 'Something happened with socket' })
|
socket.emit(SocketEvent.NOTIFICATION, { message: 'Something happened with socket' })
|
||||||
}
|
}
|
||||||
// Use io for broadcasting if needed
|
// Use io for broadcasting if needed
|
||||||
io.emit('notification', { message: 'Something happened' })
|
io.emit(SocketEvent.NOTIFICATION, { message: 'Something happened' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
@ -8,16 +9,14 @@ import WorldRepository from '#repositories/worldRepository'
|
|||||||
class DateManager {
|
class DateManager {
|
||||||
private static readonly CONFIG = {
|
private static readonly CONFIG = {
|
||||||
GAME_SPEED: 8, // 24 game hours / 3 real hours
|
GAME_SPEED: 8, // 24 game hours / 3 real hours
|
||||||
UPDATE_INTERVAL: 1000 // 1 second
|
UPDATE_INTERVAL: 1000 // 1 minute
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
private io: Server | null = null
|
|
||||||
private intervalId: NodeJS.Timeout | null = null
|
|
||||||
private currentDate = new Date()
|
|
||||||
private readonly logger = Logger.type(LoggerType.APP)
|
private readonly logger = Logger.type(LoggerType.APP)
|
||||||
|
private currentDate = new Date()
|
||||||
|
private intervalId: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
public async boot(): Promise<void> {
|
public async boot(): Promise<void> {
|
||||||
this.io = SocketManager.getIO()
|
|
||||||
await this.loadDate()
|
await this.loadDate()
|
||||||
this.startDateLoop()
|
this.startDateLoop()
|
||||||
this.logger.info('Date manager loaded')
|
this.logger.info('Date manager loaded')
|
||||||
@ -85,7 +84,8 @@ class DateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private emitDate(): void {
|
private emitDate(): void {
|
||||||
this.io?.emit('date', this.currentDate)
|
const io = SocketManager.getIO()
|
||||||
|
io?.emit(SocketEvent.DATE, this.currentDate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveDate(): Promise<void> {
|
private async saveDate(): Promise<void> {
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import cors from 'cors'
|
import fs from 'fs'
|
||||||
|
import { createServer as httpServer, Server as HTTPServer } from 'http'
|
||||||
|
import { createServer as httpsServer, Server as HTTPSServer } from 'https'
|
||||||
|
|
||||||
import type { Application } from 'express'
|
import cors from 'cors'
|
||||||
|
import express, { type Application } from 'express'
|
||||||
|
|
||||||
import config from '#application/config'
|
import config from '#application/config'
|
||||||
|
import Logger, { LoggerType } from '#application/logger.js'
|
||||||
import { AuthController } from '#controllers/auth'
|
import { AuthController } from '#controllers/auth'
|
||||||
import { AvatarController } from '#controllers/avatar'
|
import { AvatarController } from '#controllers/avatar'
|
||||||
import { CacheController } from '#controllers/cache'
|
import { CacheController } from '#controllers/cache'
|
||||||
@ -12,16 +16,40 @@ import { TexturesController } from '#controllers/textures'
|
|||||||
* HTTP manager
|
* HTTP manager
|
||||||
*/
|
*/
|
||||||
class HttpManager {
|
class HttpManager {
|
||||||
|
private readonly app: Application
|
||||||
|
private readonly server: HTTPServer | HTTPSServer
|
||||||
|
private readonly logger = Logger.type(LoggerType.APP)
|
||||||
private readonly authController: AuthController = new AuthController()
|
private readonly authController: AuthController = new AuthController()
|
||||||
private readonly avatarController: AvatarController = new AvatarController()
|
private readonly avatarController: AvatarController = new AvatarController()
|
||||||
private readonly texturesController: TexturesController = new TexturesController()
|
private readonly texturesController: TexturesController = new TexturesController()
|
||||||
private readonly cacheController: CacheController = new CacheController()
|
private readonly cacheController: CacheController = new CacheController()
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.app = express()
|
||||||
|
this.app.use(cors())
|
||||||
|
this.app.use(express.json())
|
||||||
|
this.app.use(express.urlencoded({ extended: true }))
|
||||||
|
|
||||||
|
if (config.PUBLIC_KEY_PATH && config.PRIVATE_KEY_PATH) {
|
||||||
|
const credentials = {
|
||||||
|
key: fs.readFileSync(config.PRIVATE_KEY_PATH),
|
||||||
|
cert: fs.readFileSync(config.PUBLIC_KEY_PATH)
|
||||||
|
}
|
||||||
|
this.server = httpsServer(credentials, this.app)
|
||||||
|
this.logger.info('HTTPS server initialized')
|
||||||
|
} else {
|
||||||
|
this.server = httpServer(this.app)
|
||||||
|
this.logger.info('HTTP server initialized')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize HTTP manager
|
* Initialize HTTP manager
|
||||||
* @param app
|
* @param app
|
||||||
*/
|
*/
|
||||||
public async boot(app: Application) {
|
public async boot(app: Application) {
|
||||||
|
this.server.listen(config.PORT, config.HOST)
|
||||||
|
|
||||||
// Add CORS middleware
|
// Add CORS middleware
|
||||||
app.use(
|
app.use(
|
||||||
cors({
|
cors({
|
||||||
@ -34,6 +62,8 @@ class HttpManager {
|
|||||||
|
|
||||||
// Add routes
|
// Add routes
|
||||||
await this.addRoutes(app)
|
await this.addRoutes(app)
|
||||||
|
|
||||||
|
this.logger.info(`HTTP running on port ${config.PORT}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addRoutes(app: Application) {
|
private async addRoutes(app: Application) {
|
||||||
@ -58,6 +88,14 @@ class HttpManager {
|
|||||||
app.get('/cache/character_types', (req, res) => this.cacheController.characterTypes(req, res))
|
app.get('/cache/character_types', (req, res) => this.cacheController.characterTypes(req, res))
|
||||||
app.get('/cache/character_hair', (req, res) => this.cacheController.characterHair(req, res))
|
app.get('/cache/character_hair', (req, res) => this.cacheController.characterHair(req, res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAppInstance(): Application {
|
||||||
|
return this.app
|
||||||
|
}
|
||||||
|
|
||||||
|
getServerInstance(): HTTPServer | HTTPSServer {
|
||||||
|
return this.server
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new HttpManager()
|
export default new HttpManager()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
@ -20,7 +21,6 @@ class WeatherManager {
|
|||||||
} as const
|
} as const
|
||||||
|
|
||||||
private readonly logger = Logger.type(LoggerType.APP)
|
private readonly logger = Logger.type(LoggerType.APP)
|
||||||
private io: Server | null = null
|
|
||||||
private intervalId: NodeJS.Timeout | null = null
|
private intervalId: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
private weatherState: WeatherState = {
|
private weatherState: WeatherState = {
|
||||||
@ -29,7 +29,6 @@ class WeatherManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async boot(): Promise<void> {
|
public async boot(): Promise<void> {
|
||||||
this.io = SocketManager.getIO()
|
|
||||||
await this.loadWeather()
|
await this.loadWeather()
|
||||||
this.startWeatherLoop()
|
this.startWeatherLoop()
|
||||||
this.logger.info('Weather manager loaded')
|
this.logger.info('Weather manager loaded')
|
||||||
@ -113,7 +112,8 @@ class WeatherManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private emitWeather(): void {
|
private emitWeather(): void {
|
||||||
this.io?.emit('weather', this.weatherState)
|
const io = SocketManager.getIO()
|
||||||
|
io?.emit(SocketEvent.WEATHER, this.weatherState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveWeather(): Promise<void> {
|
private async saveWeather(): Promise<void> {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// import { defineConfig, MariaDbDriver } from '@mikro-orm/mariadb'
|
import { defineConfig, MariaDbDriver } from '@mikro-orm/mariadb'
|
||||||
|
// import { defineConfig, MySqlDriver } from '@mikro-orm/mysql'
|
||||||
import { Migrator } from '@mikro-orm/migrations'
|
import { Migrator } from '@mikro-orm/migrations'
|
||||||
import { defineConfig, MySqlDriver } from '@mikro-orm/mysql'
|
|
||||||
import { TsMorphMetadataProvider } from '@mikro-orm/reflection'
|
import { TsMorphMetadataProvider } from '@mikro-orm/reflection'
|
||||||
|
|
||||||
import serverConfig from '#application/config'
|
import serverConfig from '#application/config'
|
||||||
@ -10,7 +10,7 @@ export default defineConfig({
|
|||||||
metadataProvider: TsMorphMetadataProvider,
|
metadataProvider: TsMorphMetadataProvider,
|
||||||
entities: ['./dist/entities/*.js'],
|
entities: ['./dist/entities/*.js'],
|
||||||
entitiesTs: ['./src/entities/*.ts'],
|
entitiesTs: ['./src/entities/*.ts'],
|
||||||
driver: MySqlDriver,
|
driver: MariaDbDriver,
|
||||||
host: serverConfig.DB_HOST,
|
host: serverConfig.DB_HOST,
|
||||||
port: serverConfig.DB_PORT,
|
port: serverConfig.DB_PORT,
|
||||||
user: serverConfig.DB_USER,
|
user: serverConfig.DB_USER,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import type { TSocket, UUID } from '#application/types'
|
import type { TSocket, UUID } from '#application/types'
|
||||||
@ -44,11 +45,11 @@ class MapCharacter {
|
|||||||
MapManager.removeCharacter(this.character.id)
|
MapManager.removeCharacter(this.character.id)
|
||||||
|
|
||||||
// Notify map players
|
// Notify map players
|
||||||
io.in(this.character.map.id).emit('map:character:leave', this.character.id)
|
io.in(this.character.map.id).emit(SocketEvent.MAP_CHARACTER_LEAVE, this.character.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify all players
|
// Notify all players
|
||||||
io.emit('character:disconnect', this.character.id)
|
io.emit(SocketEvent.CHARACTER_DISCONNECT, this.character.id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error disconnecting character ${this.character.id}:`, error)
|
console.error(`Error disconnecting character ${this.character.id}:`, error)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,4 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
import { createServer as httpServer, Server as HTTPServer } from 'http'
|
|
||||||
|
|
||||||
import cors from 'cors'
|
|
||||||
import express from 'express'
|
|
||||||
|
|
||||||
import type { Application } from 'express'
|
|
||||||
|
|
||||||
import config from '#application/config'
|
|
||||||
import Database from '#application/database'
|
import Database from '#application/database'
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
import ConsoleManager from '#managers/consoleManager'
|
import ConsoleManager from '#managers/consoleManager'
|
||||||
@ -19,31 +11,17 @@ import UserManager from '#managers/userManager'
|
|||||||
import WeatherManager from '#managers/weatherManager'
|
import WeatherManager from '#managers/weatherManager'
|
||||||
|
|
||||||
export class Server {
|
export class Server {
|
||||||
private readonly app: Application
|
|
||||||
private readonly http: HTTPServer
|
|
||||||
private readonly logger = Logger.type(LoggerType.APP)
|
private readonly logger = Logger.type(LoggerType.APP)
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.app = express()
|
|
||||||
this.app.use(cors())
|
|
||||||
this.app.use(express.json())
|
|
||||||
this.app.use(express.urlencoded({ extended: true }))
|
|
||||||
this.http = httpServer(this.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Initialize database
|
// Initialize database
|
||||||
await Database.initialize()
|
await Database.initialize()
|
||||||
|
|
||||||
// Start HTTP server
|
|
||||||
this.http.listen(config.PORT, config.HOST)
|
|
||||||
this.logger.info(`Server running on port ${config.PORT}`)
|
|
||||||
|
|
||||||
// Initialize managers
|
// Initialize managers
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
HttpManager.boot(this.app),
|
HttpManager.boot(HttpManager.getAppInstance()),
|
||||||
SocketManager.boot(this.app, this.http),
|
SocketManager.boot(HttpManager.getAppInstance(), HttpManager.getServerInstance()),
|
||||||
QueueManager.boot(),
|
QueueManager.boot(),
|
||||||
UserManager.boot(),
|
UserManager.boot(),
|
||||||
MapManager.boot(),
|
MapManager.boot(),
|
||||||
@ -52,6 +30,7 @@ export class Server {
|
|||||||
ConsoleManager.boot()
|
ConsoleManager.boot()
|
||||||
])
|
])
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
console.error(error)
|
||||||
this.logger.error(`Server failed to start: ${error.message}`)
|
this.logger.error(`Server failed to start: ${error.message}`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseService } from '#application/base/baseService'
|
import { BaseService } from '#application/base/baseService'
|
||||||
@ -27,7 +28,7 @@ class CharacterAttackService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Emit attack event
|
// Emit attack event
|
||||||
io.in(character.character.map.id).emit('map:character:attack', character.character.id)
|
io.in(character.character.map.id).emit(SocketEvent.MAP_CHARACTER_ATTACK, character.character.id)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@ type Position = { positionX: number; positionY: number }
|
|||||||
export type Node = Position & { parent?: Node; g: number; h: number; f: number }
|
export type Node = Position & { parent?: Node; g: number; h: number; f: number }
|
||||||
|
|
||||||
class CharacterMoveService extends BaseService {
|
class CharacterMoveService extends BaseService {
|
||||||
private readonly MOVEMENT_DELAY_MS = 200
|
private readonly MOVEMENT_DELAY_MS = 90
|
||||||
private readonly MAX_PATH_LENGTH = 20 // Limit maximum path length
|
|
||||||
|
|
||||||
private readonly DIRECTIONS = [
|
private readonly DIRECTIONS = [
|
||||||
{ x: 0, y: -1 }, // Up
|
{ x: 0, y: -1 }, // Up
|
||||||
@ -46,21 +45,7 @@ class CharacterMoveService extends BaseService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add maximum distance check
|
return this.findPath(start, end, grid)
|
||||||
const directDistance = Math.sqrt(Math.pow(targetX - character.positionX, 2) + Math.pow(targetY - character.positionY, 2))
|
|
||||||
|
|
||||||
if (directDistance > this.MAX_PATH_LENGTH) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = this.findPath(start, end, grid)
|
|
||||||
|
|
||||||
// Validate path length
|
|
||||||
if (path.length > this.MAX_PATH_LENGTH) {
|
|
||||||
return path.slice(0, this.MAX_PATH_LENGTH)
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public calculateRotation(X1: number, Y1: number, X2: number, Y2: number): number {
|
public calculateRotation(X1: number, Y1: number, X2: number, Y2: number): number {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
@ -61,7 +62,7 @@ class CharacterTeleportService {
|
|||||||
// If the current map is the target map and we are not joining, send move event
|
// If the current map is the target map and we are not joining, send move event
|
||||||
if (currentMapId === options.targetMapId && !options.isInitialJoin) {
|
if (currentMapId === options.targetMapId && !options.isInitialJoin) {
|
||||||
// If the current map is the target map, send move event
|
// If the current map is the target map, send move event
|
||||||
io.in(currentMapId).emit('map:character:move', {
|
io.in(currentMapId).emit(SocketEvent.MAP_CHARACTER_MOVE, {
|
||||||
characterId: mapCharacter.character.id,
|
characterId: mapCharacter.character.id,
|
||||||
positionX: options.targetX,
|
positionX: options.targetX,
|
||||||
positionY: options.targetY,
|
positionY: options.targetY,
|
||||||
@ -75,7 +76,7 @@ class CharacterTeleportService {
|
|||||||
if (currentMapId) {
|
if (currentMapId) {
|
||||||
socket.leave(currentMapId)
|
socket.leave(currentMapId)
|
||||||
MapManager.removeCharacter(characterId)
|
MapManager.removeCharacter(characterId)
|
||||||
io.in(currentMapId).emit('map:character:leave', characterId)
|
io.in(currentMapId).emit(SocketEvent.MAP_CHARACTER_LEAVE, characterId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join new map
|
// Join new map
|
||||||
@ -86,8 +87,8 @@ class CharacterTeleportService {
|
|||||||
await mapRepository.getEntityManager().populate(map!, mapRepository.POPULATE_TELEPORT as any)
|
await mapRepository.getEntityManager().populate(map!, mapRepository.POPULATE_TELEPORT as any)
|
||||||
|
|
||||||
// Notify clients
|
// Notify clients
|
||||||
io.in(options.targetMapId).emit('map:character:join', mapCharacter)
|
io.in(options.targetMapId).emit(SocketEvent.MAP_CHARACTER_JOIN, mapCharacter)
|
||||||
socket.emit('map:character:teleport', {
|
socket.emit(SocketEvent.MAP_CHARACTER_TELEPORT, {
|
||||||
mapId: options.targetMapId,
|
mapId: options.targetMapId,
|
||||||
characters: targetMap.getCharactersInMap()
|
characters: targetMap.getCharactersInMap()
|
||||||
})
|
})
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { SocketEvent } from '#application/enums';
|
||||||
import type { UUID } from '#application/types'
|
import type { UUID } from '#application/types'
|
||||||
|
|
||||||
import { BaseService } from '#application/base/baseService'
|
import { BaseService } from '#application/base/baseService'
|
||||||
@ -22,7 +23,7 @@ class ChatService extends BaseService {
|
|||||||
await chat.setCharacter(character).setMap(map).setMessage(message).save()
|
await chat.setCharacter(character).setMap(map).setMessage(message).save()
|
||||||
|
|
||||||
const io = SocketManager.getIO()
|
const io = SocketManager.getIO()
|
||||||
io.to(mapId).emit('chat:message', chat)
|
io.to(mapId).emit(SocketEvent.CHAT_MESSAGE, chat)
|
||||||
return true
|
return true
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Failed to save chat message: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`Failed to save chat message: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
59
traefik.toml
59
traefik.toml
@ -1,59 +0,0 @@
|
|||||||
[entryPoints]
|
|
||||||
[entryPoints.web]
|
|
||||||
address = ":80"
|
|
||||||
[entryPoints.web.http.redirections.entryPoint]
|
|
||||||
to = "websecure"
|
|
||||||
scheme = "https"
|
|
||||||
|
|
||||||
[entryPoints.websecure]
|
|
||||||
address = ":443"
|
|
||||||
|
|
||||||
[providers.docker]
|
|
||||||
endpoint = "unix:///var/run/docker.sock"
|
|
||||||
exposedByDefault = false
|
|
||||||
|
|
||||||
[certificatesResolvers.le.acme]
|
|
||||||
email = "your-email@example.com"
|
|
||||||
storage = "/data/acme.json"
|
|
||||||
[certificatesResolvers.le.acme.tlsChallenge]
|
|
||||||
|
|
||||||
[api]
|
|
||||||
dashboard = true
|
|
||||||
|
|
||||||
[ping] # Health check
|
|
||||||
entryPoint = "websecure"
|
|
||||||
|
|
||||||
[http.routers.api]
|
|
||||||
rule = "PathPrefix(`/api`)"
|
|
||||||
service = "api"
|
|
||||||
entryPoints = ["websecure"]
|
|
||||||
|
|
||||||
[http.services.api.loadBalancer]
|
|
||||||
[[http.services.api.loadBalancer.servers]]
|
|
||||||
url = "http://app:${PORT}"
|
|
||||||
|
|
||||||
# Added for websocket
|
|
||||||
[http.services.app.loadBalancer]
|
|
||||||
sticky = true
|
|
||||||
[[http.services.app.loadBalancer.servers]]
|
|
||||||
url = "http://app:${PORT}"
|
|
||||||
|
|
||||||
# Added for websocket
|
|
||||||
[http.routers.app]
|
|
||||||
rule = "Host(`${HOST}`)"
|
|
||||||
entrypoints = ["websecure"]
|
|
||||||
service = "app"
|
|
||||||
|
|
||||||
[http.routers.app.tls]
|
|
||||||
certresolver = "le"
|
|
||||||
|
|
||||||
[http.routers.app.middlewares]
|
|
||||||
# Enable websockets
|
|
||||||
- "websocket"
|
|
||||||
|
|
||||||
[http.middlewares]
|
|
||||||
[http.middlewares.websocket.headers]
|
|
||||||
accessControlAllowHeaders = ["Origin", "Content-Type", "Accept", "Authorization"]
|
|
||||||
accessControlAllowMethods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]
|
|
||||||
accessControlAllowOrigin = ["*"]
|
|
||||||
accessControlExposeHeaders = ["Content-Length", "Content-Range"]
|
|
@ -20,8 +20,6 @@
|
|||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"baseUrl": "./src",
|
"baseUrl": "./src",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"outDir": "./dist",
|
|
||||||
"sourceMap": true,
|
|
||||||
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"#root/*": ["./*"],
|
"#root/*": ["./*"],
|
||||||
|
Reference in New Issue
Block a user