1
0
forked from noxious/server

Compare commits

...

139 Commits

Author SHA1 Message Date
47be8597bf Added pivot point logic 2025-02-15 16:39:39 +01:00
2ce9bbdedd Updated commands 2025-02-14 23:11:34 +01:00
3f8d36db5a Changed default port, added comment, fix for using mikro-orm with typescript 2025-02-14 22:36:41 +01:00
fc91eb9873 Saving teleports works again 2025-02-14 03:16:30 +01:00
606328dbef Updated packages 2025-02-13 14:50:54 +01:00
b508370eec Minor improvement 2025-02-12 15:58:37 +01:00
22b776ef0f Added extra walk checks 2025-02-12 15:27:56 +01:00
f76bf3df1f Disabled Mikro ORM debugging 2025-02-12 13:47:40 +01:00
7da0323153 Attempt fix memory leak 2025-02-12 12:47:50 +01:00
26405433a8 Walk fixes 2025-02-12 12:41:33 +01:00
4e7e13d6f4 Chat event & service callback improvements 2025-02-12 03:43:28 +01:00
785a232776 TS >:L 2025-02-12 03:19:44 +01:00
14d296e5a9 Date manager refactor 2025-02-12 03:16:46 +01:00
8cca44a3b4 Don't attack if isMoving 2025-02-12 03:01:07 +01:00
d75ed7a44f Moving improvements finished 2025-02-12 02:49:02 +01:00
e21c03ee3b Moving almost works 2025-02-12 02:44:59 +01:00
deac2892fb Disconnect fix, move improvements, baseEvent improvement 2025-02-12 01:44:57 +01:00
e40a56825a Cleanup 2025-02-12 00:50:51 +01:00
c47339dfcd Typo 2025-02-12 00:14:05 +01:00
fef0ae6e28 Paths refactor # > @/ 2025-02-12 00:12:26 +01:00
bebd5876da Console fix 2025-02-11 23:16:37 +01:00
fedb5c154b Typo 2025-02-11 23:15:14 +01:00
9e55ac7990 Replaced all event names with numbers for less bandwidth usage 2025-02-11 23:12:41 +01:00
8b51f6e16a Socket enum 2025-02-11 22:28:19 +01:00
e2ded75017 WIP event delays 2025-02-10 17:56:38 +01:00
0cead14e71 Updated move interval 2025-02-10 15:33:17 +01:00
f2905247ff Value update 2025-02-10 15:30:02 +01:00
e735522d76 Rate limit walking 2025-02-10 15:27:36 +01:00
94c619192b Minor improvement 2025-02-10 14:44:35 +01:00
a8a98d0083 Returned, it's platform issue 2025-02-10 14:08:46 +01:00
646c40db27 Removed old unsupported package 2025-02-10 14:08:03 +01:00
cfaea6ec7c WS improvements 2025-02-10 13:59:25 +01:00
9030550c0f Updated packages 2025-02-10 02:00:39 +01:00
c71c393a51 Import order 2025-02-09 20:01:52 +01:00
4d192bd5ec Added mysql code but commented out 2025-02-09 20:01:17 +01:00
f46fff5b69 Updated driver 2025-02-09 18:25:12 +01:00
f5cae0db9f Updated driver 2025-02-09 18:20:59 +01:00
c627ea2412 revert 2025-02-09 18:04:25 +01:00
30145e1662 frameCount 2025-02-09 17:59:30 +01:00
778e4402ba Count fix 2025-02-09 17:53:34 +01:00
e2bc151881 package update 2025-02-09 17:25:57 +01:00
2b022ee4e0 Cleaned character create event 2025-02-09 17:12:46 +01:00
3802b2cf9d Unnecessary 2025-02-09 16:20:59 +01:00
2ee6a72984 Unnecessary 2025-02-09 16:20:38 +01:00
f50e4c75a9 final 2025-02-09 03:26:36 +01:00
51cbe87755 alternative approach cmd 2025-02-09 03:24:32 +01:00
d6aa8da2de Take cmds 2025-02-09 03:22:48 +01:00
7b4674587a Added start bash file 2025-02-09 02:27:40 +01:00
8e652f8dcb Put back required config param 2025-02-09 02:25:44 +01:00
5349e2ffe5 Improvements for prod. 2025-02-09 02:24:53 +01:00
66759a87f2 Use https if config is present 2025-02-09 02:09:58 +01:00
b7748c254f Moved more of http logic into http manager 2025-02-09 02:04:14 +01:00
ee080b6987 added https package 2025-02-09 02:01:06 +01:00
67021f9ada SSL config 2025-02-09 02:00:59 +01:00
3270ea8729 Updated .env.example 2025-02-09 01:22:34 +01:00
04710edb73 Removed docker files 2025-02-09 01:20:59 +01:00
d398764b6d P 2025-02-09 01:17:55 +01:00
be479b11c5 ? 2025-02-09 01:15:38 +01:00
45964ba7f3 bug 2025-02-09 01:12:20 +01:00
e7e187da7c Attempt 9999 2025-02-08 23:50:43 +01:00
64c0d82d48 Reverted to working state of docker compose 2025-02-08 23:01:48 +01:00
12292ea4f2 i hate docker 2025-02-08 15:23:38 +01:00
0c8df9d175 toml > yml 2025-02-08 15:21:55 +01:00
86cbe3fdcb y 2025-02-08 15:19:07 +01:00
62e31c10d7 y 2025-02-08 15:12:04 +01:00
35fe0e6faa y 2025-02-08 15:07:54 +01:00
bcc5c0bca3 a 2025-02-08 15:05:00 +01:00
2de92ca51c Moved traefik config fille 2025-02-08 14:59:04 +01:00
2cdcfa7e56 Fix formatting 2025-02-08 14:57:18 +01:00
ee4eca6db3 Added traefik 2025-02-08 14:56:39 +01:00
daca3d306d SSL 2025-02-08 05:01:00 +01:00
47d38e36dd Node > TSX 2025-02-08 04:34:53 +01:00
14b4726546 Rm file 2025-02-08 04:32:03 +01:00
394ad13341 ? 2025-02-08 04:16:15 +01:00
fe18f8b54e docker 2025-02-08 04:09:51 +01:00
11f177c901 Updated Dockerfile 2025-02-08 03:28:55 +01:00
e1f9f8e523 Updated Dockerfile 2025-02-08 03:18:32 +01:00
10c8e493a7 Updated Dockerfile 2025-02-08 03:07:35 +01:00
9a5aa9a53d Run command in tmux 2025-02-08 02:59:49 +01:00
2d5a445af0 Single line 2025-02-08 02:54:07 +01:00
af43faf8f2 Run migrations 2025-02-08 02:47:18 +01:00
cbaadc0c26 Typo 2025-02-08 02:42:45 +01:00
1ed9c2a61f Updated Dockerfile 2025-02-08 02:36:25 +01:00
b46989d3af Updated README.md 2025-02-07 23:26:17 +01:00
30310bf0cf Updated Dockerfile 2025-02-07 23:23:03 +01:00
a28b1b9bee Updated Dockerfile 2025-02-07 23:20:44 +01:00
ccdc39e74b Updated Dockerfile 2025-02-07 23:16:04 +01:00
df860cb7d7 Updated Dockerfile 2025-02-07 23:13:39 +01:00
5e46597e7d Updated Dockerfile 2025-02-07 23:11:19 +01:00
aebe21f140 Updated Dockerfile 2025-02-07 23:03:03 +01:00
d4c1098a5e Updated Dockerfile 2025-02-07 23:00:42 +01:00
8b9afdf956 Updated Dockerfile 2025-02-07 22:57:05 +01:00
5f02aca6e4 Renamed file 2025-02-07 22:53:38 +01:00
e824f0f558 MariaDB fix 2025-02-07 22:38:36 +01:00
13252e056f MySQL > MariaDB 2025-02-07 22:35:54 +01:00
50f595f718 Typo 2025-02-07 22:30:27 +01:00
a7726387af Updated Dockerfile 2025-02-07 22:29:21 +01:00
cff5fed4f7 Removed TSC in favour of node's own Typescript exec. 2025-02-07 22:27:19 +01:00
52b8a9b7ad More typescript improvements 2025-02-07 20:54:55 +01:00
f5e7d10fb4 Updated tsconfig.json and edited all required files to work with it 2025-02-07 20:37:51 +01:00
fae428f239 hm 2025-02-07 20:07:36 +01:00
37f2cd90e6 a 2025-02-07 02:13:10 +01:00
3265bf7823 almost 2025-02-07 02:11:18 +01:00
209f474575 asd
asd
2025-02-07 02:08:20 +01:00
733a1c4956 pff 2025-02-07 02:07:10 +01:00
7e2c5eb529 ¿ 2025-02-07 02:03:12 +01:00
b2f7d45a1f ? 2025-02-07 02:01:16 +01:00
8b0746958e w 2025-02-07 01:55:36 +01:00
0c607fe39d a 2025-02-07 01:53:43 +01:00
aae125c6c6 :) 2025-02-07 01:52:53 +01:00
4ace8c1e84 Build fix attempt 2025-02-07 01:48:42 +01:00
cf3b274cd3 Reverted tsconfig.json, updated package.json 2025-02-07 01:42:54 +01:00
10fd2064ba TS fix 2025-02-07 01:40:01 +01:00
9ba8be51ab Missing package 2025-02-07 01:35:02 +01:00
1686a7a9a0 Higher node version 2025-02-07 01:29:14 +01:00
b82e2fd0fd Build fix 2025-02-07 01:19:44 +01:00
71dd1f240d Added default character type 2025-02-07 01:11:11 +01:00
f2917e67e3 Minor fixes 2025-02-07 01:08:40 +01:00
9ea12ee458 Moved bash code into .sh file 2025-02-07 00:59:59 +01:00
67a4c6763b Added MySQL to dockerfile 2025-02-07 00:54:44 +01:00
f0c0456121 Added titles to error notifications 2025-02-06 21:21:01 +01:00
4992ef69d4 Changed values for smoother movement 2025-02-06 14:07:46 +01:00
765a0468bc Walk improvements 2025-02-06 13:47:02 +01:00
ba96ae7dd4 Minor improvements 2025-02-06 13:12:19 +01:00
9baffd1327 Added title to error notification 2025-02-05 15:13:39 +01:00
a14074afcf Updated packages 2025-02-05 15:10:22 +01:00
6b03937c39 Fixed strings 2025-02-05 02:55:16 +01:00
89f8137dc3 npm update 2025-02-05 02:27:42 +01:00
53c232d0a3 Code order improvement 2025-02-05 02:27:35 +01:00
a5d8cf5ef9 Width / height bug fix 2025-02-05 02:27:27 +01:00
90559e8388 npm run format 2025-02-01 16:18:58 +01:00
925721be8a Improved offset values 2025-02-01 14:45:18 +01:00
70aa7345e0 Improved service names, added attack anim. sprite to init.ts, added attackService, added attack event 2025-02-01 04:30:54 +01:00
a5ca524bb4 Slightly altered movement delay 2025-02-01 02:31:43 +01:00
3b6c11090f Added data validation upon creating maps 2025-01-31 22:57:22 +01:00
f6a4bd3369 npm update 2025-01-31 18:48:58 +01:00
ccf43556a5 npm update 2025-01-31 17:06:28 +01:00
1be4a70fed New assets.zip containing improved sprites 2025-01-31 02:21:14 +01:00
f0bfa0b983 Formatted code 2025-01-31 02:20:24 +01:00
153 changed files with 4510 additions and 4216 deletions

17
.dockerignore Normal file
View File

@ -0,0 +1,17 @@
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
.env
.env.*
docker-compose*
*.md
coverage
.vscode
.idea
dist
build
temp

View File

@ -6,10 +6,10 @@ JWT_SECRET="secret"
CLIENT_URL="http://localhost:5173"
# Database configuration
REDIS_URL="redis://@127.0.0.1:6379/4"
DB_HOST="localhost"
DB_USER="root"
DB_PASS=""
REDIS_URL="redis://@redis:6379/4"
DB_HOST="mariadb"
DB_USER="mariadb"
DB_PASS="mariadb"
DB_PORT="3306"
DB_NAME="game"
@ -25,4 +25,8 @@ DEFAULT_CHARACTER_POS_Y="0"
SMTP_HOST=my.directonline.io
SMTP_PORT=587
SMTP_USER=no-reply@noxious.gg
SMTP_PASSWORD=""
SMTP_PASSWORD=""
# SSL
#PUBLIC_KEY_PATH=
#PRIVATE_KEY_PATH=

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
migrations

View File

@ -4,5 +4,8 @@
"tabWidth": 2,
"singleQuote": true,
"printWidth": 200,
"trailingComma": "none"
"trailingComma": "none",
"plugins": ["@ianvs/prettier-plugin-sort-imports"],
"importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy", "classProperties"],
"importOrderCaseSensitive": false
}

View File

@ -1,41 +0,0 @@
# Use the official Node.js 22.4.1 image
FROM node:22.4.1-alpine
# Install Redis and tmux
RUN apk add --no-cache redis tmux
# Set the working directory in the container
WORKDIR /usr/src/
# Copy package.json and package-lock.json (if available)
COPY package*.json ./
# Install application dependencies
RUN npm install
# Copy prisma schema
COPY prisma ./prisma/
# Generate Prisma client
RUN npx prisma generate
# Copy the rest of your application code to the container
COPY . .
# Build the application
RUN npm run build
# Expose the ports your Node.js application and Redis will listen on
EXPOSE 80 6379
# Create a shell script to run Redis, run migrations, and start the application in a tmux session
RUN echo '#!/bin/sh' > /usr/src/start.sh && \
echo 'redis-server --daemonize yes' >> /usr/src/start.sh && \
echo 'npx prisma migrate deploy' >> /usr/src/start.sh && \
echo 'tmux new-session -d -s nodeapp "node dist/server.js"' >> /usr/src/start.sh && \
echo 'echo "App is running in tmux session. Attach with: tmux attach-session -t nodeapp"' >> /usr/src/start.sh && \
echo 'tail -f /dev/null' >> /usr/src/start.sh && \
chmod +x /usr/src/start.sh
# Use the shell script as the entry point
CMD ["/usr/src/start.sh"]

View File

@ -5,7 +5,7 @@ This is the server for the Noxious game.
## Projects requirements
- NodeJS 20.x or higher
- MySQL 8.x or higher
- MariaDB 11.x or higher
- Redis 7.x or higher
## Installation
@ -14,7 +14,7 @@ This is the server for the Noxious game.
2. Install dependencies with `npm install`
3. Copy the `.env.example` file to `.env` and fill in the required variables
4. Extract assets.zip to the `public` folder
5. After MySQL and Redis are running, run `npx mikro-orm migration:up` to create the database schema
5. After MySQL and Redis are running, run `npx mikro-orm-esm migration:up` to create the database schema
6. Run the server with `npm run dev`
7. Write `init` in the console to import default data and restart the server
8. Write `tiles` in the console to fix tile sizes
@ -39,15 +39,15 @@ MikroORM is used as the ORM for the server.
### Create init. migrations
Run `npx mikro-orm migration:create --initial` to create a new initial migration.
Run `npm run mikro-orm migration:create --initial` to create a new initial migration.
### Create migrations
Run `npx mikro-orm migration:create` to create a new migration. You do this when you want to add a new table or change an existing one.
Run `npm run mikro-orm migration:create` to create a new migration. You do this when you want to add a new table or change an existing one.
### Apply migrations
Run `npx mikro-orm migration:up` to apply all pending migrations.
Run `npm run mikro-orm migration:up` to apply all pending migrations.
### Import default data

View File

@ -1,4 +0,0 @@
{
"schemaVersion": 2,
"dockerfilePath" :"./Dockerfile"
}

View File

@ -1,42 +0,0 @@
import eslint from '@eslint/js';
import tseslint from '@typescript-eslint/eslint-plugin';
import tsparser from '@typescript-eslint/parser';
import importPlugin from 'eslint-plugin-import';
export default [
eslint.configs.recommended,
{
files: ['**/*.ts'],
languageOptions: {
parser: tsparser,
parserOptions: {
project: './tsconfig.json',
ecmaVersion: 2023,
},
},
plugins: {
'@typescript-eslint': tseslint,
'import': importPlugin,
},
rules: {
...tseslint.configs['recommended'].rules,
...tseslint.configs['recommended-requiring-type-checking'].rules,
'import/order': ['error', {
'groups': [
'builtin',
'external',
'internal',
['parent', 'sibling'],
'index',
'object',
'type'
],
'newlines-between': 'always',
'alphabetize': {
'order': 'asc',
'caseInsensitive': true
}
}]
}
}
];

3384
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,14 @@
{
"type": "module",
"tsNode": true,
"scripts": {
"start": "node dist/server.js",
"start": "tsx src/server.ts",
"dev": "nodemon --exec tsx src/server.ts",
"build": "tsc",
"format": "prettier --write src/",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
"mikro-orm": "tsx ./node_modules/.bin/mikro-orm-esm"
},
"dependencies": {
"@mikro-orm/cli": "^6.4.2",
"@mikro-orm/core": "^6.4.2",
"@mikro-orm/mariadb": "^6.4.2",
"@mikro-orm/migrations": "^6.4.2",
@ -15,32 +16,31 @@
"@mikro-orm/reflection": "^6.4.2",
"@types/ioredis": "^4.28.10",
"bcryptjs": "^2.4.3",
"bufferutil": "^4.0.9",
"bullmq": "^5.13.2",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"https": "^1.0.0",
"ioredis": "^5.4.1",
"jsonwebtoken": "^9.0.2",
"nodemailer": "^6.9.15",
"pino": "^9.3.2",
"reflect-metadata": "^0.2.2",
"sharp": "^0.33.4",
"socket.io": "^4.7.5",
"ts-node": "^10.9.2",
"typescript": "^5.5.3",
"utf-8-validate": "^6.0.5",
"zod": "^3.23.8"
},
"devDependencies": {
"@mikro-orm/cli": "^6.4.2",
"ts-node": "^10.9.2",
"@ianvs/prettier-plugin-sort-imports": "^4.4.0",
"@types/bcryptjs": "^2.4.6",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^20.14.11",
"@types/nodemailer": "^6.4.16",
"@typescript-eslint/eslint-plugin": "^8.18.2",
"@typescript-eslint/parser": "^8.18.2",
"eslint": "^9.17.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.31.0",
"nodemon": "^3.1.4",
"prettier": "^3.3.3",
"tsx": "^4.19.2"

Binary file not shown.

View File

@ -1,7 +1,6 @@
import Logger, { LoggerType } from '@/application/logger'
import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseCommand {
protected readonly logger = Logger.type(LoggerType.COMMAND)
constructor(readonly io: Server) {}

View File

@ -1,8 +1,6 @@
import fs from 'fs'
import { Response } from 'express'
import Logger, { LoggerType } from '#application/logger'
import Logger, { LoggerType } from '@/application/logger'
import type { Response } from 'express'
export abstract class BaseController {
protected readonly logger = Logger.type(LoggerType.HTTP)

View File

@ -1,8 +1,7 @@
import Database from '@/application/database'
import Logger, { LoggerType } from '@/application/logger'
import { EntityManager } from '@mikro-orm/core'
import Database from '#application/database'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseEntity {
protected readonly logger = Logger.type(LoggerType.ENTITY)
protected entityManager?: EntityManager

View File

@ -1,21 +1,43 @@
import { SocketEvent } from '@/application/enums'
import Logger, { LoggerType } from '@/application/logger'
import type { TSocket } from '@/application/types'
import { Character } from '@/entities/character'
import MapManager from '@/managers/mapManager'
import type MapCharacter from '@/models/mapCharacter'
import CharacterRepository from '@/repositories/characterRepository'
import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger'
import { TSocket } from '#application/types'
import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository'
export abstract class BaseEvent {
protected readonly logger = Logger.type(LoggerType.GAME)
private lastActionTimes: Map<string, number> = new Map()
constructor(
readonly io: Server,
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 getMapCharacter(): MapCharacter | null {
if (!this.socket.characterId) return null
return MapManager.getCharacterById(this.socket.characterId)
}
protected async getCharacter(): Promise<Character | null> {
if (!this.socket.characterId) return null
const characterRepository = new CharacterRepository()
return characterRepository.getById(this.socket.characterId!)
return characterRepository.getById(this.socket.characterId)
}
protected async isCharacterGM(): Promise<boolean> {
@ -24,13 +46,14 @@ export abstract class BaseEvent {
}
protected emitError(message: string): void {
this.socket.emit('notification', { title: 'Server message', message })
this.logger.error('character:connect error', `Player ${this.socket.userId}: ${message}`)
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Server message', message })
this.logger.error('Base event error', `Player ${this.socket.userId}: ${message}`)
}
protected handleError(context: string, error: unknown): void {
const errorMessage = error instanceof Error ? error.message : String(error)
this.emitError(`${context}: ${errorMessage}`)
this.logger.error('character:connect error', errorMessage)
console.log(error)
const errorMessage = error instanceof Error ? error.message : error && typeof error === 'object' && 'toString' in error ? error.toString() : String(error)
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Server message', message: `Server error occured. Please contact the server administrator.` })
this.logger.error('Base event error', errorMessage)
}
}

View File

@ -1,9 +1,7 @@
import Database from '@/application/database'
import Logger, { LoggerType } from '@/application/logger'
import { EntityManager } from '@mikro-orm/core'
import Database from '../database'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseRepository {
protected readonly logger = Logger.type(LoggerType.REPOSITORY)
private entityManager?: EntityManager

View File

@ -1,4 +1,4 @@
import Logger, { LoggerType } from '#application/logger'
import Logger, { LoggerType } from '@/application/logger'
export abstract class BaseService {
protected readonly logger = Logger.type(LoggerType.GAME)

View File

@ -6,7 +6,7 @@ class config {
// Server configuration
static ENV: string = process.env.ENV || 'development'
static HOST: string = process.env.HOST || '0.0.0.0'
static PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 6969
static PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 4000
static JWT_SECRET: string = process.env.JWT_SECRET || 'secret'
static CLIENT_URL: string = process.env.CLIENT_URL ? process.env.CLIENT_URL : 'https://noxious.gg'
@ -31,6 +31,10 @@ class config {
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_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

View File

@ -1,10 +1,9 @@
import * as fs from 'fs'
import * as path from 'path'
import { pathToFileURL } from 'url'
import Logger, { LoggerType } from '#application/logger'
import Storage from '#application/storage'
import { Command } from '#application/types'
import Logger, { LoggerType } from '@/application/logger'
import Storage from '@/application/storage'
import type { Command } from '@/application/types'
export class CommandRegistry {
private readonly commands: Map<string, Command> = new Map()

View File

@ -1,7 +1,6 @@
import * as fs from 'fs'
import * as path from 'path'
import Logger, { LoggerType } from '#application/logger'
import Logger, { LoggerType } from '@/application/logger'
export class LogReader {
private logger = Logger.type(LoggerType.CONSOLE)

View File

@ -1,7 +1,8 @@
import { MikroORM } from '@mikro-orm/mysql'
// import { MikroORM } from '@mikro-orm/mysql'
import Logger, { LoggerType } from './logger'
import config from '../../mikro-orm.config'
import Logger, { LoggerType } from '@/application/logger'
import config from '@/root/mikro-orm.config'
import { MikroORM } from '@mikro-orm/mariadb'
class Database {
private static orm: MikroORM

View File

@ -1,17 +1,57 @@
export enum SocketEvent {
CHARACTER_CONNECT = 1,
CHARACTER_MOVE = 2,
CHARACTER_MOVE_ERROR = 3,
CHARACTER_TELEPORT = 4,
ZONE_CHARACTER_LEAVE = 5,
ZONE_CHARACTER_JOIN = 6,
ZONE_CHARACTER_LIST = 7,
ZONE_CHARACTER_DELETE = 8,
ZONE_CHARACTER_CREATE = 9,
ZONE_CHARACTER_UPDATE = 10,
ZONE_CHARACTER_HAIR_UPDATE = 11,
ZONE_CHARACTER_HAIR_LIST = 12,
ZONE_CHARACTER_TELEPORT = 13
CLOSE = '52',
DATA = '51',
CHARACTER_CONNECT = '50',
CHARACTER_CREATE = '49',
CHARACTER_DELETE = '48',
CHARACTER_LIST = '47',
GM_CHARACTERHAIR_CREATE = '46',
GM_CHARACTERHAIR_REMOVE = '45',
GM_CHARACTERHAIR_LIST = '44',
GM_CHARACTERHAIR_UPDATE = '43',
GM_CHARACTERTYPE_CREATE = '42',
GM_CHARACTERTYPE_REMOVE = '41',
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 {

View File

@ -1,5 +1,7 @@
import pino from 'pino'
const logger = pino.pino
export enum LoggerType {
HTTP = 'http',
GAME = 'game',
@ -13,13 +15,13 @@ export enum LoggerType {
}
class Logger {
private instances: Map<LoggerType, ReturnType<typeof pino>> = new Map()
private instances: Map<LoggerType, pino.Logger> = new Map()
private getLogger(type: LoggerType): ReturnType<typeof pino> {
private getLogger(type: LoggerType): pino.Logger {
if (!this.instances.has(type)) {
this.instances.set(
type,
pino({
logger({
level: process.env.LOG_LEVEL || 'debug',
transport: {
target: 'pino/file',

View File

@ -1,7 +1,6 @@
import fs from 'fs'
import path from 'path'
import config from '#application/config'
import config from '@/application/config'
class Storage {
private readonly baseDir: string
@ -9,7 +8,7 @@ class Storage {
constructor() {
this.rootDir = process.cwd()
this.baseDir = config.ENV === 'development' ? 'src' : 'dist'
this.baseDir = config.ENV === 'development' ? 'src' : 'src'
}
/**

View File

@ -1,9 +1,8 @@
import { Character } from '@/entities/character'
import { MapEventTile } from '@/entities/mapEventTile'
import { MapEventTileTeleport } from '@/entities/mapEventTileTeleport'
import { Server, Socket } from 'socket.io'
import { Character } from '#entities/character'
import { MapEventTile } from '#entities/mapEventTile'
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
export type UUID = `${string}-${string}-${string}-${string}-${string}`
export type TSocket = Socket & {

View File

@ -1,13 +1,13 @@
import { BaseCommand } from '@/application/base/baseCommand'
import { SocketEvent } from '@/application/enums'
import { Server } from 'socket.io'
import { BaseCommand } from '#application/base/baseCommand'
type CommandInput = string[]
export default class AlertCommand extends BaseCommand {
public execute(input: CommandInput): void {
const message: string = input.join(' ') ?? null
if (!message) return console.log('message is required')
this.io.emit('notification', { message: message })
this.io.emit(SocketEvent.NOTIFICATION, { message: message })
}
}

View File

@ -1,25 +1,23 @@
import fs from 'fs'
import { BaseCommand } from '@/application/base/baseCommand'
import { CharacterGender, CharacterRace } from '@/application/enums'
import Storage from '@/application/storage'
import type { UUID } from '@/application/types'
import { Character } from '@/entities/character'
import { CharacterHair } from '@/entities/characterHair'
import { CharacterType } from '@/entities/characterType'
import { Map } from '@/entities/map'
import { MapEffect } from '@/entities/mapEffect'
import { MapObject } from '@/entities/mapObject'
import { Sprite } from '@/entities/sprite'
import { SpriteAction } from '@/entities/spriteAction'
import { Tile } from '@/entities/tile'
import { User } from '@/entities/user'
import CharacterHairRepository from '@/repositories/characterHairRepository'
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
import MapRepository from '@/repositories/mapRepository'
import sharp from 'sharp'
import { BaseCommand } from '#application/base/baseCommand'
import { CharacterGender, CharacterRace } from '#application/enums'
import Storage from '#application/storage'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { CharacterHair } from '#entities/characterHair'
import { CharacterType } from '#entities/characterType'
import { Map } from '#entities/map'
import { MapEffect } from '#entities/mapEffect'
import { MapObject } from '#entities/mapObject'
import { Sprite } from '#entities/sprite'
import { SpriteAction } from '#entities/spriteAction'
import { Tile } from '#entities/tile'
import { User } from '#entities/user'
import CharacterHairRepository from '#repositories/characterHairRepository'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import MapRepository from '#repositories/mapRepository'
// @TODO : Replace this with seeding
// https://mikro-orm.io/docs/seeding
@ -64,12 +62,12 @@ export default class InitCommand extends BaseCommand {
.setFrameWidth(
(await sharp(Storage.getPublicPath('map_objects', mapObject))
.metadata()
.then((metadata) => metadata.height)) ?? 0
.then((metadata) => metadata.width)) ?? 0
)
.setFrameHeight(
(await sharp(Storage.getPublicPath('map_objects', mapObject))
.metadata()
.then((metadata) => metadata.width)) ?? 0
.then((metadata) => metadata.height)) ?? 0
)
await newMapObject.save()
@ -95,7 +93,7 @@ export default class InitCommand extends BaseCommand {
])
.setOriginX(0)
.setOriginY(0)
.setFrameWidth(64)
.setFrameWidth(28)
.setFrameHeight(94)
.setFrameRate(0)
.setSprite(characterSprite)
@ -115,8 +113,8 @@ export default class InitCommand extends BaseCommand {
])
.setOriginX(0)
.setOriginY(0)
.setFrameWidth(64)
.setFrameHeight(94)
.setFrameWidth(26)
.setFrameHeight(93)
.setFrameRate(0)
.setSprite(characterSprite)
.save()
@ -156,8 +154,8 @@ export default class InitCommand extends BaseCommand {
])
.setOriginX(0)
.setOriginY(0)
.setFrameWidth(64)
.setFrameHeight(94)
.setFrameWidth(36)
.setFrameHeight(102)
.setFrameRate(7)
.setSprite(characterSprite)
.save()
@ -197,12 +195,80 @@ export default class InitCommand extends BaseCommand {
])
.setOriginX(0)
.setOriginY(0)
.setFrameWidth(64)
.setFrameHeight(94)
.setFrameWidth(34)
.setFrameHeight(101)
.setFrameRate(7)
.setSprite(characterSprite)
.save()
const attackRightDownAction = new SpriteAction()
await attackRightDownAction
.setAction('attack_right_down')
.setSprites([
{
url: 'data:image/png;base64,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',
offset: {
x: 20,
y: 0
}
},
{
url: 'data:image/png;base64,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',
offset: {
x: 19,
y: 8
}
},
{
url: 'data:image/png;base64,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',
offset: {
x: 17,
y: 3
}
}
])
.setOriginX(0)
.setOriginY(0)
.setFrameWidth(69)
.setFrameHeight(111)
.setFrameRate(5)
.setSprite(characterSprite)
.save()
const attackLeftUpAction = new SpriteAction()
await attackLeftUpAction
.setAction('attack_left_up')
.setSprites([
{
url: 'data:image/png;base64,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',
offset: {
x: 2,
y: 0
}
},
{
url: 'data:image/png;base64,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',
offset: {
x: 5,
y: 0
}
},
{
url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAABcCAYAAACFtS4PAAAFtUlEQVR4nKVaPWgbSRT+ZB9KsGMcZLHKcYY1xELFRcSpQpo0wi7dXXFgDpd3VzpVOHAhcBnDcVU6QwLXi6ss3BhcuLJBbnRywAvmkl1kcca28AkiXbF+4/l5MxrZXxNrd+Z73868efPeTDKVt+/hQq5WHXDPO8vrGWdHAN8MI11beomwXFLeHezsYuvmvcsIS56rVQdrSy9RrCzh0dMnyrvLT1/wovIauaCAThJjq1Yd2AwY5LladfD7m5/wsPgME/lJjM8UlfePAFwCCMtAiPSLbAbGOMXZIGSJRafpKWSDMO0TFNg2BjkAFCtLGJuesnbQEZZLWF2YYydekJNqF76etdBtX6UdPQQoYx6WS6JTt32FCbSUxkTsC6srDiPrn18AAKJG09rGGHPq5ItOEuPVz78OJ3ep4AQc7OxaiQ1yvbPr3f6fHwTxdeuIbauMeSeJvQwAqn/bvthQ3ksiJym1oXhz3TpSRMkQyjvL65mtWnWQCwoIy2qjbBAaRqNGE6FDNau8k8RDJ5beR42mVbVB3llez2wdnhgG5NVIzztJ7CTmld8YkIk4YhlbhydsXGddsbO8ntnc3neqIlIbsZVch8stXTtRhvZQPWRShNS3OMLBzi6AVL3NSKby9j1ytepgdWEuNXKzOMJyyXBB2iDomewtnBFlherEY9NTyCI1QL8n8pO4vGkvb3Xcnpr54fvvBqsLc0gXT0koJKJu+wrXrSNkg1Bs1t32FTsPvSRKM4ObSRYTSmqBW78enyliIj+pEBgb9tMnwmg2CJWYM0afRMQ6ZDJuwx6fKWJ8pigMFCtLYk8VyuVVSJ/89awFHdwzMkIGSL0g759foJdExqqUyXTiXhJZjSnkgP9OJOPy0xc7OS31XhINDUS+IB7n8h+WShzs7LKbi+GKAPDX3/94G/DZsazKbcHKZoybWEHOTabNQP/8QmlPxN32lfJFXiGXIKvWJ59CAkVLL3JdvfybVjYR95JI2TwE+V3cUI/1+tCyym2ewM0BhQ1unYwB6qbsC3JbPWoa5KNC/jJbaWOQt7s9lkCHLTw7yQF1YntJJIzIxnpJpAgh6GnGrbdID23RUTYmQ19UrPKzQgnvPj8Ymi/6hmajJlpcXMS7eh34/C82YJLY1gP3nC24VlZWEMcxfqvXmbcPAAAzTs0W8kKhgPn5eWEEgPgt449ffhxKbngLEen/2uDaQ511KBHPzs4qz09PT1WFlmqaXaEyGf2dz+eRz+eVZ1GjmVba+Un2gMG6/HW1NlDKx8E6LPqnt9ttth0p52Ao39vbE8THx8eGEd2wt/KZuIl6PXVHAIhj9wbSSWJn+mEMy5tv/8O7jx+VZ4uLiwD8jVrJAWDj+eM0kT88werCXBoONMgr1FZFs+RhuYTOzm3jjeePlf0yajSxuX1LPJJygNLgE+SCgnLu1T+/SMt3qZSMGk22ZGQraADY3N7H6sKcOJoid9NXo6tEZxcRxWvKS+gYkPNnmhuuVGSHRVZiixurC3POGtRKTgjLJePwknIXV1kuhBmqb8adil6CfKbou80585ZsELJ5iW/qZyXPBQVRncmqKdm8FzkHWwphw9AT0Qm0nHn5ncn75xfoSn+PCie5TupTZMmwjrme2BMx56Yjk3OQw8K9yOU0mVSPWtoY5PKRlAwKqxQpbVcJTnIdtkn0GRqWnOtIQ0Jh2Ec9S2477vMRYSWn8X5YfGac3+ou6KOeVe4q/2RkgxAvKq+H3xMJ9Y7xHtWAQR6WS87aEjArOxtGWqFyHJcrO1sYFoGLbhNzr9QrNDknydWqg04SAw2VZOidhQ/oXgM4sb53kuvjHZZLSnblcz1M8BpznzhyZ/JcULiTAYOcSr+J/ORIl65e5KPeeY5E3j+/UPKU+0DxlqjRRFiGuDagU+m7nvEqd3ObtepgDQCk0wr5wmMUNwSkqxzCff77g47/AX/tU4BIJmZXAAAAAElFTkSuQmCC',
offset: {
x: 6,
y: 1
}
}
])
.setOriginX(0)
.setOriginY(0)
.setFrameWidth(34)
.setFrameHeight(100)
.setFrameRate(5)
.setSprite(characterSprite)
.save()
const characterType = new CharacterType()
await characterType
.setId('75b70c78-17f0-44c0-a4fa-15043cb95be0')
@ -233,7 +299,7 @@ export default class InitCommand extends BaseCommand {
])
.setOriginX(0.5)
.setOriginY(5.34)
.setFrameWidth(64)
.setFrameWidth(24)
.setFrameHeight(18)
.setFrameRate(0)
.setSprite(hairSprite)
@ -253,7 +319,7 @@ export default class InitCommand extends BaseCommand {
])
.setOriginX(0.5)
.setOriginY(4.34)
.setFrameWidth(64)
.setFrameWidth(24)
.setFrameHeight(22)
.setFrameRate(0)
.setSprite(hairSprite)
@ -286,7 +352,7 @@ export default class InitCommand extends BaseCommand {
.setUser(user)
.setName('root')
.setRole('gm')
.setMap((await this.mapRepository.getFirst())!)
.setMap(await this.mapRepository.getFirst())
.setCharacterType(await this.characterTypeRepository.getFirst())
.setCharacterHair(await this.characterHairRepository.getFirst())
.save()

View File

@ -1,5 +1,5 @@
import { BaseCommand } from '#application/base/baseCommand'
import MapManager from '#managers/mapManager'
import { BaseCommand } from '@/application/base/baseCommand'
import MapManager from '@/managers/mapManager'
type CommandInput = string[]

View File

@ -1,10 +1,8 @@
import fs from 'fs'
import { BaseCommand } from '@/application/base/baseCommand'
import Storage from '@/application/storage'
import sharp from 'sharp'
import { BaseCommand } from '#application/base/baseCommand'
import Storage from '#application/storage'
export default class TilesCommand extends BaseCommand {
public async execute(): Promise<void> {
// Get all tiles

View File

@ -1,11 +1,10 @@
import { Request, Response } from 'express'
import { BaseController } from '@/application/base/baseController'
import config from '@/application/config'
import { loginAccountSchema, newPasswordSchema, registerAccountSchema, resetPasswordSchema } from '@/application/zodTypes'
import UserService from '@/services/userService'
import type { Request, Response } from 'express'
import jwt from 'jsonwebtoken'
import { BaseController } from '#application/base/baseController'
import config from '#application/config'
import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from '#application/zodTypes'
import UserService from '#services/userService'
export class AuthController extends BaseController {
/**
* Login user

View File

@ -1,15 +1,13 @@
import fs from 'fs'
import { Request, Response } from 'express'
import { BaseController } from '@/application/base/baseController'
import Storage from '@/application/storage'
import type { UUID } from '@/application/types'
import CharacterHairRepository from '@/repositories/characterHairRepository'
import CharacterRepository from '@/repositories/characterRepository'
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
import type { Request, Response } from 'express'
import sharp from 'sharp'
import { BaseController } from '#application/base/baseController'
import Storage from '#application/storage'
import { UUID } from '#application/types'
import CharacterHairRepository from '#repositories/characterHairRepository'
import CharacterRepository from '#repositories/characterRepository'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
interface AvatarOptions {
characterTypeId: UUID
characterHairId?: UUID
@ -26,7 +24,7 @@ export class AvatarController extends BaseController {
* @param res
*/
public async getByName(req: Request, res: Response) {
const character = await this.characterRepository.getByName(req.params.characterName)
const character = await this.characterRepository.getByName(req.params.characterName!)
if (!character?.characterType) {
return this.sendError(res, 'Character or character type not found', 404)
}

View File

@ -1,12 +1,11 @@
import { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController'
import CharacterHairRepository from '#repositories/characterHairRepository'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import MapObjectRepository from '#repositories/mapObjectRepository'
import MapRepository from '#repositories/mapRepository'
import SpriteRepository from '#repositories/spriteRepository'
import TileRepository from '#repositories/tileRepository'
import { BaseController } from '@/application/base/baseController'
import CharacterHairRepository from '@/repositories/characterHairRepository'
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
import MapObjectRepository from '@/repositories/mapObjectRepository'
import MapRepository from '@/repositories/mapRepository'
import SpriteRepository from '@/repositories/spriteRepository'
import TileRepository from '@/repositories/tileRepository'
import type { Request, Response } from 'express'
export class CacheController extends BaseController {
/**

View File

@ -1,7 +1,6 @@
import { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController'
import Storage from '#application/storage'
import { BaseController } from '@/application/base/baseController'
import Storage from '@/application/storage'
import type { Request, Response } from 'express'
export class TexturesController extends BaseController {
/**
@ -12,6 +11,10 @@ export class TexturesController extends BaseController {
public async download(req: Request, res: Response) {
const { type, spriteId, file } = req.params
if (!type || !file) {
return this.sendError(res, 'Invalid request', 400)
}
const texture = type === 'sprites' && spriteId ? Storage.getPublicPath(type, spriteId, file) : Storage.getPublicPath(type, file)
this.sendFile(res, texture)

View File

@ -1,16 +1,14 @@
import { randomUUID } from 'node:crypto'
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { CharacterEquipment } from '#entities/characterEquipment'
import { CharacterHair } from '#entities/characterHair'
import { CharacterItem } from '#entities/characterItem'
import { CharacterType } from '#entities/characterType'
import { Chat } from '#entities/chat'
import { Map } from '#entities/map'
import { User } from '#entities/user'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { CharacterEquipment } from '@/entities/characterEquipment'
import type { CharacterHair } from '@/entities/characterHair'
import type { CharacterItem } from '@/entities/characterItem'
import type { CharacterType } from '@/entities/characterType'
import type { Chat } from '@/entities/chat'
import type { Map } from '@/entities/map'
import type { User } from '@/entities/user'
import { Collection, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
export class BaseCharacter extends BaseEntity {
@PrimaryKey()
@ -28,7 +26,7 @@ export class BaseCharacter extends BaseEntity {
@Property()
role = 'player'
@OneToMany(() => Chat, (chat) => chat.character)
@OneToMany({ mappedBy: 'character' })
chats = new Collection<Chat>(this)
// Position - @TODO: Update to spawn point when current map is not found

View File

@ -1,13 +1,11 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import { CharacterEquipmentSlotType } from '@/application/enums'
import type { UUID } from '@/application/types'
import type { Character } from '@/entities/character'
import type { CharacterItem } from '@/entities/characterItem'
import { Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { CharacterEquipmentSlotType } from '#application/enums'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { CharacterItem } from '#entities/characterItem'
export class BaseCharacterEquipment extends BaseEntity {
@PrimaryKey()
id = randomUUID()

View File

@ -1,13 +1,11 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import { CharacterGender } from '@/application/enums'
import type { UUID } from '@/application/types'
import { Character } from '@/entities/character'
import { Sprite } from '@/entities/sprite'
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { CharacterGender } from '#application/enums'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { Sprite } from '#entities/sprite'
export class BaseCharacterHair extends BaseEntity {
@PrimaryKey()
id = randomUUID()

View File

@ -1,12 +1,9 @@
import { randomUUID } from 'node:crypto'
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { CharacterEquipment } from '#entities/characterEquipment'
import { Item } from '#entities/item'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { Character } from '@/entities/character'
import type { Item } from '@/entities/item'
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
export class BaseCharacterItem extends BaseEntity {
@PrimaryKey()

View File

@ -1,13 +1,11 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import { CharacterGender, CharacterRace } from '@/application/enums'
import type { UUID } from '@/application/types'
import { Character } from '@/entities/character'
import { Sprite } from '@/entities/sprite'
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { CharacterGender, CharacterRace } from '#application/enums'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { Sprite } from '#entities/sprite'
export class BaseCharacterType extends BaseEntity {
@PrimaryKey()
id = randomUUID()

View File

@ -1,11 +1,9 @@
import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { Map } from '#entities/map'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { Character } from '@/entities/character'
import type { Map } from '@/entities/map'
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
export class BaseChat extends BaseEntity {
@PrimaryKey()

View File

@ -1,13 +1,11 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import { ItemRarity, ItemType } from '@/application/enums'
import type { UUID } from '@/application/types'
import { CharacterItem } from '@/entities/characterItem'
import { Sprite } from '@/entities/sprite'
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { ItemType, ItemRarity } from '#application/enums'
import { UUID } from '#application/types'
import { CharacterItem } from '#entities/characterItem'
import { Sprite } from '#entities/sprite'
export class BaseItem extends BaseEntity {
@PrimaryKey()
id = randomUUID()

View File

@ -1,12 +1,10 @@
import { randomUUID } from 'node:crypto'
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { MapEffect } from '#entities/mapEffect'
import { MapEventTile } from '#entities/mapEventTile'
import { PlacedMapObject } from '#entities/placedMapObject'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { MapEffect } from '@/entities/mapEffect'
import type { MapEventTile } from '@/entities/mapEventTile'
import type { PlacedMapObject } from '@/entities/placedMapObject'
import { Collection, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
export class BaseMap extends BaseEntity {
@PrimaryKey()
@ -22,7 +20,7 @@ export class BaseMap extends BaseEntity {
height = 10
@Property({ type: 'json', nullable: true })
tiles?: any
tiles: Array<Array<string>> = []
@Property()
pvp = false
@ -33,13 +31,13 @@ export class BaseMap extends BaseEntity {
@Property()
updatedAt = new Date()
@OneToMany(() => MapEffect, (effect) => effect.map, { orphanRemoval: true })
@OneToMany({ mappedBy: 'map', orphanRemoval: true })
mapEffects = new Collection<MapEffect>(this)
@OneToMany(() => MapEventTile, (tile) => tile.map, { orphanRemoval: true })
@OneToMany({ mappedBy: 'map', orphanRemoval: true })
mapEventTiles = new Collection<MapEventTile>(this)
@OneToMany(() => PlacedMapObject, (placedMapObject) => placedMapObject.map, { orphanRemoval: true })
@OneToMany({ mappedBy: 'map', orphanRemoval: true })
placedMapObjects = new Collection<PlacedMapObject>(this)
setId(id: UUID) {

View File

@ -1,10 +1,8 @@
import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Map } from '#entities/map'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { Map } from '@/entities/map'
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
export class BaseMapEffect extends BaseEntity {
@PrimaryKey()

View File

@ -1,12 +1,10 @@
import { randomUUID } from 'node:crypto'
import { Entity, Enum, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { MapEventTileType } from '#application/enums'
import { UUID } from '#application/types'
import { Map } from '#entities/map'
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
import { BaseEntity } from '@/application/base/baseEntity'
import { MapEventTileType } from '@/application/enums'
import type { UUID } from '@/application/types'
import type { Map } from '@/entities/map'
import type { MapEventTileTeleport } from '@/entities/mapEventTileTeleport'
import { Enum, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
export class BaseMapEventTile extends BaseEntity {
@PrimaryKey()
@ -24,7 +22,7 @@ export class BaseMapEventTile extends BaseEntity {
@Property()
positionY!: number
@OneToOne(() => MapEventTileTeleport, (teleport) => teleport.mapEventTile, { eager: true })
@OneToOne({ eager: true })
teleport?: MapEventTileTeleport
setId(id: UUID) {

View File

@ -1,11 +1,9 @@
import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Map } from '#entities/map'
import { MapEventTile } from '#entities/mapEventTile'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { Map } from '@/entities/map'
import type { MapEventTile } from '@/entities/mapEventTile'
import { ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
export class BaseMapEventTileTeleport extends BaseEntity {
@PrimaryKey()

View File

@ -1,10 +1,8 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
export class BaseMapObject extends BaseEntity {
@PrimaryKey()
id = randomUUID()
@ -12,8 +10,11 @@ export class BaseMapObject extends BaseEntity {
@Property()
name!: string
@Property({ type: 'json', nullable: true })
tags?: any
@Property({ type: 'json' })
tags: string[] = []
@Property({ type: 'json' })
pivotPoints: { x: number; y: number; }[] = []
@Property({ type: 'decimal', precision: 10, scale: 2 })
originX = 0
@ -63,6 +64,15 @@ export class BaseMapObject extends BaseEntity {
return this.tags
}
setPivotPoints(pivotPoints: { x: number; y: number; }[]) {
this.pivotPoints = pivotPoints
return this
}
getPivotPoints() {
return this.pivotPoints
}
setOriginX(originX: number) {
this.originX = originX
return this

View File

@ -1,10 +1,8 @@
import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { User } from '#entities/user'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { User } from '@/entities/user'
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
export class BasePasswordResetToken extends BaseEntity {
@PrimaryKey()

View File

@ -1,13 +1,9 @@
import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Map } from '#entities/map'
import { MapObject } from '#entities/mapObject'
//@TODO : Rename mapObject
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { Map } from '@/entities/map'
import type { MapObject } from '@/entities/mapObject'
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
export class BasePlacedMapObject extends BaseEntity {
@PrimaryKey()

View File

@ -1,11 +1,9 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import { SpriteAction } from '@/entities/spriteAction'
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { SpriteAction } from '#entities/spriteAction'
export class BaseSprite extends BaseEntity {
@PrimaryKey()
id = randomUUID()

View File

@ -1,10 +1,8 @@
import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Sprite } from '#entities/sprite'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import type { Sprite } from '@/entities/sprite'
import { ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
export interface SpriteImage {
url: string

View File

@ -1,10 +1,8 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
export class BaseTile extends BaseEntity {
@PrimaryKey()
id = randomUUID()

View File

@ -1,13 +1,11 @@
import { randomUUID } from 'node:crypto'
import { BaseEntity } from '@/application/base/baseEntity'
import type { UUID } from '@/application/types'
import { Character } from '@/entities/character'
import { PasswordResetToken } from '@/entities/passwordResetToken'
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import bcrypt from 'bcryptjs'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { PasswordResetToken } from '#entities/passwordResetToken'
export class BaseUser extends BaseEntity {
@PrimaryKey()
id = randomUUID()

View File

@ -1,7 +1,6 @@
import { BaseEntity } from '@/application/base/baseEntity'
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity'
export class BaseWorld extends BaseEntity {
@PrimaryKey()
date = new Date()

View File

@ -1,6 +1,5 @@
import { BaseCharacter } from '@/entities/base/character'
import { Entity } from '@mikro-orm/core'
import { BaseCharacter } from '#entities/base/character'
@Entity()
export class Character extends BaseCharacter {}

View File

@ -1,6 +1,5 @@
import { BaseCharacterEquipment } from '@/entities/base/characterEquipment'
import { Entity } from '@mikro-orm/core'
import { BaseCharacterEquipment } from '#entities/base/characterEquipment'
@Entity()
export class CharacterEquipment extends BaseCharacterEquipment {}

View File

@ -1,7 +1,6 @@
import { BaseCharacterHair } from '@/entities/base/characterHair'
import { Entity } from '@mikro-orm/core'
import { BaseCharacterHair } from '#entities/base/characterHair'
@Entity()
export class CharacterHair extends BaseCharacterHair {
public async cache() {

View File

@ -1,6 +1,5 @@
import { BaseCharacterItem } from '@/entities/base/characterItem'
import { Entity } from '@mikro-orm/core'
import { BaseCharacterItem } from '#entities/base/characterItem'
@Entity()
export class CharacterItem extends BaseCharacterItem {}

View File

@ -1,7 +1,6 @@
import { BaseCharacterType } from '@/entities/base/characterType'
import { Entity } from '@mikro-orm/core'
import { BaseCharacterType } from '#entities/base/characterType'
@Entity()
export class CharacterType extends BaseCharacterType {
public async cache() {

View File

@ -1,6 +1,5 @@
import { BaseChat } from '@/entities/base/chat'
import { Entity } from '@mikro-orm/core'
import { BaseChat } from '#entities/base/chat'
@Entity()
export class Chat extends BaseChat {}

View File

@ -1,6 +1,5 @@
import { BaseItem } from '@/entities/base/item'
import { Entity } from '@mikro-orm/core'
import { BaseItem } from '#entities/base/item'
@Entity()
export class Item extends BaseItem {}

View File

@ -1,7 +1,6 @@
import { BaseMap } from '@/entities/base/map'
import { Entity } from '@mikro-orm/core'
import { BaseMap } from '#entities/base/map'
export type MapCacheT = ReturnType<Map['cache']> | {}
@Entity()

View File

@ -1,6 +1,5 @@
import { BaseMapEffect } from '@/entities/base/mapEffect'
import { Entity } from '@mikro-orm/core'
import { BaseMapEffect } from '#entities/base/mapEffect'
@Entity()
export class MapEffect extends BaseMapEffect {}

View File

@ -1,6 +1,5 @@
import { BaseMapEventTile } from '@/entities/base/mapEventTile'
import { Entity } from '@mikro-orm/core'
import { BaseMapEventTile } from '#entities/base/mapEventTile'
@Entity()
export class MapEventTile extends BaseMapEventTile {}

View File

@ -1,6 +1,5 @@
import { BaseMapEventTileTeleport } from '@/entities/base/mapEventTileTeleport'
import { Entity } from '@mikro-orm/core'
import { BaseMapEventTileTeleport } from '#entities/base/mapEventTileTeleport'
@Entity()
export class MapEventTileTeleport extends BaseMapEventTileTeleport {}

View File

@ -1,7 +1,6 @@
import { BaseMapObject } from '@/entities/base/mapObject'
import { Entity } from '@mikro-orm/core'
import { BaseMapObject } from '#entities/base/mapObject'
@Entity()
export class MapObject extends BaseMapObject {
public async cache() {

View File

@ -1,6 +1,5 @@
import { BasePasswordResetToken } from '@/entities/base/passwordResetToken'
import { Entity } from '@mikro-orm/core'
import { BasePasswordResetToken } from '#entities/base/passwordResetToken'
@Entity()
export class PasswordResetToken extends BasePasswordResetToken {}

View File

@ -1,6 +1,5 @@
import { BasePlacedMapObject } from '@/entities/base/placedMapObject'
import { Entity } from '@mikro-orm/core'
import { BasePlacedMapObject } from '#entities/base/placedMapObject'
@Entity()
export class PlacedMapObject extends BasePlacedMapObject {}

View File

@ -1,7 +1,6 @@
import { BaseSprite } from '@/entities/base/sprite'
import { Entity } from '@mikro-orm/core'
import { BaseSprite } from '#entities/base/sprite'
@Entity()
export class Sprite extends BaseSprite {
public async cache() {

View File

@ -1,6 +1,5 @@
import { BaseSpriteAction } from '@/entities/base/spriteAction'
import { Entity } from '@mikro-orm/core'
import { BaseSpriteAction } from '#entities/base/spriteAction'
@Entity()
export class SpriteAction extends BaseSpriteAction {}

View File

@ -1,7 +1,6 @@
import { BaseTile } from '@/entities/base/tile'
import { Entity } from '@mikro-orm/core'
import { BaseTile } from '#entities/base/tile'
@Entity()
export class Tile extends BaseTile {
public async cache() {

View File

@ -1,6 +1,5 @@
import { BaseUser } from '@/entities/base/user'
import { Entity } from '@mikro-orm/core'
import { BaseUser } from '#entities/base/user'
@Entity()
export class User extends BaseUser {}

View File

@ -1,6 +1,5 @@
import { BaseWorld } from '@/entities/base/world'
import { Entity } from '@mikro-orm/core'
import { BaseWorld } from '#entities/base/world'
@Entity()
export class World extends BaseWorld {}

View File

@ -1,9 +1,10 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapManager from '#managers/mapManager'
import CharacterHairRepository from '#repositories/characterHairRepository'
import CharacterRepository from '#repositories/characterRepository'
import TeleportService from '#services/teleportService'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import MapManager from '@/managers/mapManager'
import CharacterHairRepository from '@/repositories/characterHairRepository'
import CharacterRepository from '@/repositories/characterRepository'
import TeleportService from '@/services/characterTeleportService'
interface CharacterConnectPayload {
characterId: UUID
@ -15,7 +16,7 @@ export default class CharacterConnectEvent extends BaseEvent {
private readonly characterRepository = new CharacterRepository()
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> {
@ -25,7 +26,7 @@ export default class CharacterConnectEvent extends BaseEvent {
return
}
const character = await this.characterRepository.getByUserAndId(this.socket.userId!, data.characterId)
const character = await this.characterRepository.getByUserAndId(this.socket.userId, data.characterId)
if (!character) {
this.emitError('Character not found or does not belong to this user')
@ -61,7 +62,7 @@ export default class CharacterConnectEvent extends BaseEvent {
}
private async checkForActiveCharacters(): Promise<boolean> {
const characters = await this.characterRepository.getByUserId(this.socket.userId!)
const characters = await this.characterRepository.getByUserId(this.socket.userId)
return characters?.some((char) => MapManager.getCharacterById(char.id)) ?? false
}
}

View File

@ -1,67 +1,84 @@
import { ZodError } from 'zod'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { ZCharacterCreate } from '@/application/zodTypes'
import { Character } from '@/entities/character'
import CharacterRepository from '@/repositories/characterRepository'
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
import MapRepository from '@/repositories/mapRepository'
import UserRepository from '@/repositories/userRepository'
import { z, ZodError } from 'zod'
import { BaseEvent } from '#application/base/baseEvent'
import { ZCharacterCreate } from '#application/zodTypes'
import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository'
import MapRepository from '#repositories/mapRepository'
import UserRepository from '#repositories/userRepository'
const MAX_CHARACTERS = 4
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 {
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> {
// zod validate
private async handleEvent(data: z.infer<typeof ZCharacterCreate>, callback: (success: boolean) => void): Promise<void> {
try {
data = ZCharacterCreate.parse(data)
const userRepository = new UserRepository()
const characterRepository = new CharacterRepository()
const mapRepository = new MapRepository()
const user = await userRepository.getById(this.socket.userId!)
if (!user) {
return this.socket.emit('notification', { message: 'User not found' })
}
// Check if character name already exists
const characterExists = await characterRepository.getByName(data.name)
if (characterExists) {
return this.socket.emit('notification', { message: 'Character name already exists' })
}
let characters: Character[] = await characterRepository.getByUserId(user.getId())
if (characters.length >= 4) {
return this.socket.emit('notification', { message: 'You can only have 4 characters' })
}
// @TODO: Change to default location
const map = await mapRepository.getFirst()
const newCharacter = new Character()
await newCharacter.setName(data.name).setUser(user).setMap(map!).save()
if (!newCharacter) {
return this.socket.emit('notification', { 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', { message: error.issues[0].message })
}
return this.socket.emit('notification', { message: 'Could not create character. Please try again (later).' })
const validatedData = ZCharacterCreate.parse(data)
await this.createCharacter(validatedData)
callback(true)
} catch (error: unknown) {
this.returnError(error)
callback(false)
}
}
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
})
}
}

View File

@ -1,7 +1,8 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import { Character } from '@/entities/character'
import CharacterRepository from '@/repositories/characterRepository'
type TypePayload = {
characterId: UUID
@ -13,18 +14,18 @@ type TypeResponse = {
export default class CharacterDeleteEvent extends BaseEvent {
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> {
try {
const characterRepository = new CharacterRepository()
await (await characterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete()
const characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
await (await characterRepository.getByUserAndId(this.socket.userId, data.characterId))?.delete()
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) {
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.' })
}
}
}

View File

@ -1,18 +1,19 @@
import { BaseEvent } from '#application/base/baseEvent'
import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { Character } from '@/entities/character'
import CharacterRepository from '@/repositories/characterRepository'
export default class CharacterListEvent extends BaseEvent {
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> {
try {
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) {
this.logger.error('character:list error', error.message)
}

View File

@ -1,6 +1,7 @@
import { BaseEvent } from '#application/base/baseEvent'
import CharacterRepository from '#repositories/characterRepository'
import ChatService from '#services/chatService'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import CharacterRepository from '@/repositories/characterRepository'
import ChatService from '@/services/chatService'
type TypePayload = {
message: string
@ -8,7 +9,7 @@ type TypePayload = {
export default class AlertCommandEvent extends BaseEvent {
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> {
@ -25,7 +26,7 @@ export default class AlertCommandEvent extends BaseEvent {
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)
} catch (error: any) {
this.logger.error('chat:alert_command error', error.message)

View File

@ -1,7 +1,8 @@
import { BaseEvent } from '#application/base/baseEvent'
import DateManager from '#managers/dateManager'
import CharacterRepository from '#repositories/characterRepository'
import ChatService from '#services/chatService'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import DateManager from '@/managers/dateManager'
import CharacterRepository from '@/repositories/characterRepository'
import ChatService from '@/services/chatService'
type TypePayload = {
message: string
@ -9,7 +10,7 @@ type TypePayload = {
export default class SetTimeCommand extends BaseEvent {
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> {

View File

@ -1,9 +1,10 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapManager from '#managers/mapManager'
import MapRepository from '#repositories/mapRepository'
import ChatService from '#services/chatService'
import TeleportService from '#services/teleportService'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import MapManager from '@/managers/mapManager'
import MapRepository from '@/repositories/mapRepository'
import TeleportService from '@/services/characterTeleportService'
import ChatService from '@/services/chatService'
type TypePayload = {
message: string
@ -11,7 +12,7 @@ type TypePayload = {
export default class TeleportCommandEvent extends BaseEvent {
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) {
@ -28,7 +29,7 @@ export default class TeleportCommandEvent extends BaseEvent {
const args = ChatService.getArgs('teleport', data.message)
if (!args || args.length === 0 || args.length > 3) {
this.socket.emit('notification', {
this.socket.emit(SocketEvent.NOTIFICATION, {
title: 'Server message',
message: 'Usage: /teleport <mapId> [x] [y]'
})
@ -40,7 +41,7 @@ export default class TeleportCommandEvent extends BaseEvent {
const targetY = args[2] ? parseInt(args[2], 10) : 0
if (!mapId || isNaN(targetX) || isNaN(targetY)) {
this.socket.emit('notification', {
this.socket.emit(SocketEvent.NOTIFICATION, {
title: 'Server message',
message: 'Invalid parameters. X and Y coordinates must be numbers.'
})
@ -50,7 +51,7 @@ export default class TeleportCommandEvent extends BaseEvent {
const mapRepository = new MapRepository()
const map = await mapRepository.getById(mapId)
if (!map) {
this.socket.emit('notification', {
this.socket.emit(SocketEvent.NOTIFICATION, {
title: 'Server message',
message: 'Map not found'
})
@ -58,7 +59,7 @@ export default class TeleportCommandEvent extends BaseEvent {
}
if (character.map.id === map.id && targetX === character.positionX && targetY === character.positionY) {
this.socket.emit('notification', {
this.socket.emit(SocketEvent.NOTIFICATION, {
title: 'Server message',
message: 'You are already at that location'
})
@ -73,20 +74,20 @@ export default class TeleportCommandEvent extends BaseEvent {
})
if (!success) {
return this.socket.emit('notification', {
return this.socket.emit(SocketEvent.NOTIFICATION, {
title: 'Server message',
message: 'Failed to teleport'
})
}
this.socket.emit('notification', {
this.socket.emit(SocketEvent.NOTIFICATION, {
title: 'Server message',
message: `Teleported to ${map.name} (${targetX}, ${targetY})`
})
this.logger.info('teleport', `Character ${character.id} teleported to map ${map.id} at position (${targetX}, ${targetY})`)
} catch (error: any) {
this.logger.error(`Error in teleport command: ${error.message}`)
this.socket.emit('notification', {
this.socket.emit(SocketEvent.NOTIFICATION, {
title: 'Server message',
message: 'An error occurred while teleporting'
})

View File

@ -1,6 +1,7 @@
import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager'
import ChatService from '#services/chatService'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import WeatherManager from '@/managers/weatherManager'
import ChatService from '@/services/chatService'
type TypePayload = {
message: string
@ -8,7 +9,7 @@ type TypePayload = {
export default class ToggleFogCommand extends BaseEvent {
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> {

View File

@ -1,6 +1,7 @@
import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager'
import ChatService from '#services/chatService'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import WeatherManager from '@/managers/weatherManager'
import ChatService from '@/services/chatService'
type TypePayload = {
message: string
@ -8,7 +9,7 @@ type TypePayload = {
export default class ToggleRainCommand extends BaseEvent {
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> {

View File

@ -1,7 +1,7 @@
import { BaseEvent } from '#application/base/baseEvent'
import MapManager from '#managers/mapManager'
import MapRepository from '#repositories/mapRepository'
import ChatService from '#services/chatService'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import MapManager from '@/managers/mapManager'
import ChatService from '@/services/chatService'
type TypePayload = {
message: string
@ -9,31 +9,22 @@ type TypePayload = {
export default class ChatMessageEvent extends BaseEvent {
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> {
try {
if (!data.message || ChatService.isCommand(data.message)) {
return callback(false)
}
if (!data.message || ChatService.isCommand(data.message)) return
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
if (!mapCharacter) {
this.logger.error('chat:message error', 'Character not found')
return callback(false)
return
}
const character = mapCharacter.character
const mapRepository = new MapRepository()
const map = await mapRepository.getById(character.map.id)
if (!map) {
this.logger.error('chat:message error', 'Map not found')
return callback(false)
}
if (await ChatService.sendMapMessage(character.getId(), map.getId(), data.message)) {
if (await ChatService.sendMapMessage(character.getId(), data.message)) {
return callback(true)
}

View File

@ -1,5 +1,6 @@
import { BaseEvent } from '#application/base/baseEvent'
import MapManager from '#managers/mapManager'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import MapManager from '@/managers/mapManager'
export default class DisconnectEvent extends BaseEvent {
public listen(): void {
@ -13,7 +14,7 @@ export default class DisconnectEvent extends BaseEvent {
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!)
if (!mapCharacter) {

View File

@ -1,9 +1,10 @@
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterHair } from '#entities/characterHair'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { CharacterHair } from '@/entities/characterHair'
export default class CharacterHairCreateEvent extends BaseEvent {
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> {

View File

@ -1,27 +1,31 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import CharacterHairRepository from '#repositories/characterHairRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import CharacterHairRepository from '@/repositories/characterHairRepository'
interface IPayload {
id: UUID
}
export default class characterHairDeleteEvent extends BaseEvent {
export default class CharacterHairDeleteEvent extends BaseEvent {
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> {
try {
if (!(await this.isCharacterGM())) return
const characterHair = await CharacterHairRepository.getById(data.id)
await (await CharacterHairRepository.getById(data.id))?.delete()
const characterHairRepository = new CharacterHairRepository()
const characterHair = await characterHairRepository.getById(data.id)
if (!characterHair) return callback(false)
await characterHair.delete()
return callback(true)
} catch (error) {
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
callback(false)
this.logger.error(`Error deleting character hair ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
return callback(false)
}
}
}

View File

@ -1,12 +1,13 @@
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterHair } from '#entities/characterHair'
import CharacterHairRepository from '#repositories/characterHairRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { CharacterHair } from '@/entities/characterHair'
import CharacterHairRepository from '@/repositories/characterHairRepository'
interface IPayload {}
export default class characterHairListEvent extends BaseEvent {
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> {

View File

@ -1,8 +1,8 @@
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterGender } from '#application/enums'
import { UUID } from '#application/types'
import CharacterHairRepository from '#repositories/characterHairRepository'
import SpriteRepository from '#repositories/spriteRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { CharacterGender, SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import CharacterHairRepository from '@/repositories/characterHairRepository'
import SpriteRepository from '@/repositories/spriteRepository'
type Payload = {
id: UUID
@ -14,7 +14,7 @@ type Payload = {
export default class CharacterHairUpdateEvent extends BaseEvent {
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> {

View File

@ -1,9 +1,10 @@
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterType } from '#entities/characterType'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { CharacterType } from '@/entities/characterType'
export default class CharacterTypeCreateEvent extends BaseEvent {
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> {

View File

@ -1,6 +1,7 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
interface IPayload {
id: UUID
@ -8,7 +9,7 @@ interface IPayload {
export default class CharacterTypeDeleteEvent extends BaseEvent {
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> {

View File

@ -1,12 +1,13 @@
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterType } from '#entities/characterType'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { CharacterType } from '@/entities/characterType'
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
interface IPayload {}
export default class CharacterTypeListEvent extends BaseEvent {
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> {

View File

@ -1,8 +1,8 @@
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterGender, CharacterRace } from '#application/enums'
import { UUID } from '#application/types'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import SpriteRepository from '#repositories/spriteRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { CharacterGender, CharacterRace, SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import CharacterTypeRepository from '@/repositories/characterTypeRepository'
import SpriteRepository from '@/repositories/spriteRepository'
type Payload = {
id: UUID
@ -15,7 +15,7 @@ type Payload = {
export default class CharacterTypeUpdateEvent extends BaseEvent {
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> {

View File

@ -1,17 +1,23 @@
import { BaseEvent } from '#application/base/baseEvent'
import { Item } from '#entities/item'
import { BaseEvent } from '@/application/base/baseEvent'
import { ItemRarity, ItemType, SocketEvent } from '@/application/enums'
import { Item } from '@/entities/item'
import SpriteRepository from '@/repositories/spriteRepository'
export default class ItemCreateEvent extends BaseEvent {
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> {
try {
if (!(await this.isCharacterGM())) return
const spriteRepository = new SpriteRepository()
const sprite = await spriteRepository.getFirst()
if (!sprite) return callback(false)
const newItem = new Item()
await newItem.setName('New Item').setItemType('WEAPON').setStackable(false).setRarity('COMMON').setSprite(null).save()
await newItem.setName('New Item').setItemType(ItemType.WEAPON).setStackable(false).setRarity(ItemRarity.COMMON).setSprite(sprite).save()
return callback(true, newItem)
} catch (error) {

View File

@ -1,6 +1,7 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import ItemRepository from '#repositories/itemRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import ItemRepository from '@/repositories/itemRepository'
interface IPayload {
id: UUID
@ -8,7 +9,7 @@ interface IPayload {
export default class ItemDeleteEvent extends BaseEvent {
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> {

View File

@ -1,12 +1,13 @@
import { BaseEvent } from '#application/base/baseEvent'
import { Item } from '#entities/item'
import ItemRepository from '#repositories/itemRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { Item } from '@/entities/item'
import ItemRepository from '@/repositories/itemRepository'
interface IPayload {}
export default class ItemListEvent extends BaseEvent {
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> {

View File

@ -1,8 +1,8 @@
import { BaseEvent } from '#application/base/baseEvent'
import { ItemType, ItemRarity } from '#application/enums'
import { UUID } from '#application/types'
import ItemRepository from '#repositories/itemRepository'
import SpriteRepository from '#repositories/spriteRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { ItemRarity, ItemType, SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import ItemRepository from '@/repositories/itemRepository'
import SpriteRepository from '@/repositories/spriteRepository'
type Payload = {
id: UUID
@ -16,7 +16,7 @@ type Payload = {
export default class ItemUpdateEvent extends BaseEvent {
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> {

View File

@ -1,12 +1,13 @@
import { BaseEvent } from '#application/base/baseEvent'
import { MapObject } from '#entities/mapObject'
import MapObjectRepository from '#repositories/mapObjectRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import { MapObject } from '@/entities/mapObject'
import MapObjectRepository from '@/repositories/mapObjectRepository'
interface IPayload {}
export default class MapObjectListEvent extends BaseEvent {
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> {

View File

@ -1,9 +1,9 @@
import fs from 'fs'
import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage'
import { UUID } from '#application/types'
import MapObjectRepository from '#repositories/mapObjectRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import Storage from '@/application/storage'
import type { UUID } from '@/application/types'
import MapObjectRepository from '@/repositories/mapObjectRepository'
interface IPayload {
mapObjectId: UUID
@ -11,7 +11,7 @@ interface IPayload {
export default class MapObjectRemoveEvent extends BaseEvent {
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> {

View File

@ -1,11 +1,13 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapObjectRepository from '#repositories/mapObjectRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import MapObjectRepository from '@/repositories/mapObjectRepository'
type Payload = {
id: UUID
name: string
tags: string[]
pivotPoints: { x: number; y: number; }[]
originX: number
originY: number
frameRate: number
@ -15,7 +17,7 @@ type Payload = {
export default class MapObjectUpdateEvent extends BaseEvent {
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> {
@ -27,19 +29,20 @@ export default class MapObjectUpdateEvent extends BaseEvent {
const mapObject = await mapObjectRepository.getById(data.id)
if (!mapObject) return callback(false)
await mapObject
.setName(data.name)
.setTags(data.tags)
.setOriginX(data.originX)
.setOriginY(data.originY)
.setFrameRate(data.frameRate)
.setFrameWidth(data.frameWidth)
.setFrameHeight(data.frameHeight)
.save()
if (data.name !== undefined) mapObject.name = data.name
if (data.tags !== undefined) mapObject.tags = data.tags
if (data.pivotPoints !== undefined) mapObject.pivotPoints = data.pivotPoints
if (data.originX !== undefined) mapObject.originX = data.originX
if (data.originY !== undefined) mapObject.originY = data.originY
if (data.frameRate !== undefined) mapObject.frameRate = data.frameRate
if (data.frameWidth !== undefined) mapObject.frameWidth = data.frameWidth
if (data.frameHeight !== undefined) mapObject.frameHeight = data.frameHeight
await mapObject.save()
return callback(true)
} catch (error) {
console.error(error)
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Failed to update mapObject.' })
return callback(false)
}
}

View File

@ -1,19 +1,18 @@
import fs from 'fs/promises'
import { writeFile } from 'node:fs/promises'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import Storage from '@/application/storage'
import { MapObject } from '@/entities/mapObject'
import sharp from 'sharp'
import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage'
import { MapObject } from '#entities/mapObject'
interface IObjectData {
[key: string]: Buffer
}
export default class MapObjectUploadEvent extends BaseEvent {
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> {

View File

@ -1,7 +1,8 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import { Sprite } from '#entities/sprite'
import SpriteRepository from '#repositories/spriteRepository'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import type { UUID } from '@/application/types'
import { Sprite } from '@/entities/sprite'
import SpriteRepository from '@/repositories/spriteRepository'
interface CopyPayload {
id: UUID
@ -9,7 +10,7 @@ interface CopyPayload {
export default class SpriteCopyEvent extends BaseEvent {
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> {

View File

@ -1,12 +1,12 @@
import fs from 'fs/promises'
import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage'
import { Sprite } from '#entities/sprite'
import { BaseEvent } from '@/application/base/baseEvent'
import { SocketEvent } from '@/application/enums'
import Storage from '@/application/storage'
import { Sprite } from '@/entities/sprite'
export default class SpriteCreateEvent extends BaseEvent {
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> {

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