Compare commits

...

117 Commits

Author SHA1 Message Date
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
121 changed files with 3397 additions and 838 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" CLIENT_URL="http://localhost:5173"
# Database configuration # Database configuration
REDIS_URL="redis://@127.0.0.1:6379/4" REDIS_URL="redis://@redis:6379/4"
DB_HOST="localhost" DB_HOST="mariadb"
DB_USER="root" DB_USER="mariadb"
DB_PASS="" DB_PASS="mariadb"
DB_PORT="3306" DB_PORT="3306"
DB_NAME="game" DB_NAME="game"
@ -25,4 +25,8 @@ DEFAULT_CHARACTER_POS_Y="0"
SMTP_HOST=my.directonline.io SMTP_HOST=my.directonline.io
SMTP_PORT=587 SMTP_PORT=587
SMTP_USER=no-reply@noxious.gg SMTP_USER=no-reply@noxious.gg
SMTP_PASSWORD="" SMTP_PASSWORD=""
# SSL
#PUBLIC_KEY_PATH=
#PRIVATE_KEY_PATH=

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 ## Projects requirements
- NodeJS 20.x or higher - NodeJS 20.x or higher
- MySQL 8.x or higher - MariaDB 11.x or higher
- Redis 7.x or higher - Redis 7.x or higher
## Installation ## Installation
@ -14,7 +14,7 @@ This is the server for the Noxious game.
2. Install dependencies with `npm install` 2. Install dependencies with `npm install`
3. Copy the `.env.example` file to `.env` and fill in the required variables 3. Copy the `.env.example` file to `.env` and fill in the required variables
4. Extract assets.zip to the `public` folder 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` 6. Run the server with `npm run dev`
7. Write `init` in the console to import default data and restart the server 7. Write `init` in the console to import default data and restart the server
8. Write `tiles` in the console to fix tile sizes 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 ### Create init. migrations
Run `npx mikro-orm migration:create --initial` to create a new initial migration. Run `npx mikro-orm-esm migration:create --initial` to create a new initial migration.
### Create migrations ### 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 `npx mikro-orm-esm 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 ### Apply migrations
Run `npx mikro-orm migration:up` to apply all pending migrations. Run `npx mikro-orm-esm migration:up` to apply all pending migrations.
### Import default data ### Import default data

View File

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

View File

@ -1,104 +0,0 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20250128165214 extends Migration {
override async up(): Promise<void> {
this.addSql(`create table \`map\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`width\` int not null default 10, \`height\` int not null default 10, \`tiles\` json null, \`pvp\` tinyint(1) not null default false, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`create table \`map_effect\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`effect\` varchar(255) not null, \`strength\` int not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`map_effect\` add index \`map_effect_map_id_index\`(\`map_id\`);`);
this.addSql(`create table \`map_event_tile\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`type\` enum('BLOCK', 'TELEPORT', 'NPC', 'ITEM') not null, \`position_x\` int not null, \`position_y\` int not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`map_event_tile\` add index \`map_event_tile_map_id_index\`(\`map_id\`);`);
this.addSql(`create table \`map_event_tile_teleport\` (\`id\` varchar(255) not null, \`map_event_tile_id\` varchar(255) not null, \`to_map_id\` varchar(255) not null, \`to_rotation\` int not null, \`to_position_x\` int not null, \`to_position_y\` int not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`map_event_tile_teleport\` add unique \`map_event_tile_teleport_map_event_tile_id_unique\`(\`map_event_tile_id\`);`);
this.addSql(`alter table \`map_event_tile_teleport\` add index \`map_event_tile_teleport_to_map_id_index\`(\`to_map_id\`);`);
this.addSql(`create table \`map_object\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`tags\` json null, \`origin_x\` numeric(10,2) not null default 0, \`origin_y\` numeric(10,2) not null default 0, \`frame_rate\` int not null default 0, \`frame_width\` int not null default 0, \`frame_height\` int not null default 0, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`create table \`placed_map_object\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`map_object_id\` varchar(255) not null, \`is_rotated\` tinyint(1) not null default false, \`position_x\` int not null default 0, \`position_y\` int not null default 0, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`placed_map_object\` add index \`placed_map_object_map_id_index\`(\`map_id\`);`);
this.addSql(`alter table \`placed_map_object\` add index \`placed_map_object_map_object_id_index\`(\`map_object_id\`);`);
this.addSql(`create table \`sprite\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`create table \`item\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`description\` varchar(255) not null default '', \`item_type\` enum('WEAPON', 'HELMET', 'CHEST', 'LEGS', 'BOOTS', 'GLOVES', 'RING', 'NECKLACE') not null, \`stackable\` tinyint(1) not null default false, \`rarity\` enum('COMMON', 'UNCOMMON', 'RARE', 'EPIC', 'LEGENDARY') not null default 'COMMON', \`sprite_id\` varchar(255) null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`item\` add index \`item_sprite_id_index\`(\`sprite_id\`);`);
this.addSql(`create table \`character_type\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`gender\` enum('MALE', 'FEMALE') not null, \`race\` enum('HUMAN', 'ELF', 'DWARF', 'ORC', 'GOBLIN') not null, \`is_selectable\` tinyint(1) not null default false, \`sprite_id\` varchar(255) null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`character_type\` add index \`character_type_sprite_id_index\`(\`sprite_id\`);`);
this.addSql(`create table \`character_hair\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`gender\` varchar(255) not null default 'MALE', \`is_selectable\` tinyint(1) not null default false, \`sprite_id\` varchar(255) null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`character_hair\` add index \`character_hair_sprite_id_index\`(\`sprite_id\`);`);
this.addSql(`create table \`sprite_action\` (\`id\` varchar(255) not null, \`sprite_id\` varchar(255) not null, \`action\` varchar(255) not null, \`sprites\` json null, \`origin_x\` numeric(5,2) not null default 0, \`origin_y\` numeric(5,2) not null default 0, \`frame_width\` int not null default 0, \`frame_height\` int not null default 0, \`frame_rate\` int not null default 0, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`sprite_action\` add index \`sprite_action_sprite_id_index\`(\`sprite_id\`);`);
this.addSql(`create table \`tile\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`tags\` json null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`create table \`user\` (\`id\` varchar(255) not null, \`username\` varchar(255) not null, \`email\` varchar(255) not null, \`password\` varchar(255) not null, \`online\` tinyint(1) not null default false, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`user\` add unique \`user_username_unique\`(\`username\`);`);
this.addSql(`alter table \`user\` add unique \`user_email_unique\`(\`email\`);`);
this.addSql(`create table \`password_reset_token\` (\`id\` varchar(255) not null, \`user_id\` varchar(255) not null, \`token\` varchar(255) not null, \`created_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`password_reset_token\` add index \`password_reset_token_user_id_index\`(\`user_id\`);`);
this.addSql(`alter table \`password_reset_token\` add unique \`password_reset_token_token_unique\`(\`token\`);`);
this.addSql(`create table \`character\` (\`id\` varchar(255) not null, \`user_id\` varchar(255) not null, \`name\` varchar(255) not null, \`online\` tinyint(1) not null default false, \`role\` varchar(255) not null default 'player', \`map_id\` varchar(255) not null, \`position_x\` int not null default 0, \`position_y\` int not null default 0, \`rotation\` int not null default 0, \`character_type_id\` varchar(255) null, \`character_hair_id\` varchar(255) null, \`alignment\` int not null default 50, \`hitpoints\` int not null default 100, \`mana\` int not null default 100, \`level\` int not null default 1, \`experience\` int not null default 0, \`strength\` int not null default 10, \`dexterity\` int not null default 10, \`intelligence\` int not null default 10, \`wisdom\` int not null default 10, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`character\` add index \`character_user_id_index\`(\`user_id\`);`);
this.addSql(`alter table \`character\` add unique \`character_name_unique\`(\`name\`);`);
this.addSql(`alter table \`character\` add index \`character_map_id_index\`(\`map_id\`);`);
this.addSql(`alter table \`character\` add index \`character_character_type_id_index\`(\`character_type_id\`);`);
this.addSql(`alter table \`character\` add index \`character_character_hair_id_index\`(\`character_hair_id\`);`);
this.addSql(`create table \`chat\` (\`id\` varchar(255) not null, \`character_id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`message\` varchar(255) not null, \`created_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`chat\` add index \`chat_character_id_index\`(\`character_id\`);`);
this.addSql(`alter table \`chat\` add index \`chat_map_id_index\`(\`map_id\`);`);
this.addSql(`create table \`character_item\` (\`id\` varchar(255) not null, \`character_id\` varchar(255) not null, \`item_id\` varchar(255) not null, \`quantity\` int not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`character_item\` add index \`character_item_character_id_index\`(\`character_id\`);`);
this.addSql(`alter table \`character_item\` add index \`character_item_item_id_index\`(\`item_id\`);`);
this.addSql(`create table \`character_equipment\` (\`id\` varchar(255) not null, \`slot\` enum('HEAD', 'BODY', 'ARMS', 'LEGS', 'NECK', 'RING') not null, \`character_id\` varchar(255) not null, \`character_item_id\` varchar(255) not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`character_equipment\` add index \`character_equipment_character_id_index\`(\`character_id\`);`);
this.addSql(`alter table \`character_equipment\` add index \`character_equipment_character_item_id_index\`(\`character_item_id\`);`);
this.addSql(`create table \`world\` (\`date\` datetime not null, \`rain_percentage\` int not null default 0, \`fog_density\` int not null default 0, primary key (\`date\`)) default character set utf8mb4 engine = InnoDB;`);
this.addSql(`alter table \`map_effect\` add constraint \`map_effect_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`map_event_tile\` add constraint \`map_event_tile_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`map_event_tile_teleport\` add constraint \`map_event_tile_teleport_map_event_tile_id_foreign\` foreign key (\`map_event_tile_id\`) references \`map_event_tile\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`map_event_tile_teleport\` add constraint \`map_event_tile_teleport_to_map_id_foreign\` foreign key (\`to_map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`placed_map_object\` add constraint \`placed_map_object_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`placed_map_object\` add constraint \`placed_map_object_map_object_id_foreign\` foreign key (\`map_object_id\`) references \`map_object\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`item\` add constraint \`item_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete set null;`);
this.addSql(`alter table \`character_type\` add constraint \`character_type_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete set null;`);
this.addSql(`alter table \`character_hair\` add constraint \`character_hair_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete set null;`);
this.addSql(`alter table \`sprite_action\` add constraint \`sprite_action_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`password_reset_token\` add constraint \`password_reset_token_user_id_foreign\` foreign key (\`user_id\`) references \`user\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`character\` add constraint \`character_user_id_foreign\` foreign key (\`user_id\`) references \`user\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`character\` add constraint \`character_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade;`);
this.addSql(`alter table \`character\` add constraint \`character_character_type_id_foreign\` foreign key (\`character_type_id\`) references \`character_type\` (\`id\`) on update cascade on delete set null;`);
this.addSql(`alter table \`character\` add constraint \`character_character_hair_id_foreign\` foreign key (\`character_hair_id\`) references \`character_hair\` (\`id\`) on update cascade on delete set null;`);
this.addSql(`alter table \`chat\` add constraint \`chat_character_id_foreign\` foreign key (\`character_id\`) references \`character\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`chat\` add constraint \`chat_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`character_item\` add constraint \`character_item_character_id_foreign\` foreign key (\`character_id\`) references \`character\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`character_item\` add constraint \`character_item_item_id_foreign\` foreign key (\`item_id\`) references \`item\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`character_equipment\` add constraint \`character_equipment_character_id_foreign\` foreign key (\`character_id\`) references \`character\` (\`id\`) on update cascade on delete cascade;`);
this.addSql(`alter table \`character_equipment\` add constraint \`character_equipment_character_item_id_foreign\` foreign key (\`character_item_id\`) references \`character_item\` (\`id\`) on update cascade on delete cascade;`);
}
}

433
package-lock.json generated
View File

@ -5,6 +5,7 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@mikro-orm/cli": "^6.4.2",
"@mikro-orm/core": "^6.4.2", "@mikro-orm/core": "^6.4.2",
"@mikro-orm/mariadb": "^6.4.2", "@mikro-orm/mariadb": "^6.4.2",
"@mikro-orm/migrations": "^6.4.2", "@mikro-orm/migrations": "^6.4.2",
@ -12,22 +13,25 @@
"@mikro-orm/reflection": "^6.4.2", "@mikro-orm/reflection": "^6.4.2",
"@types/ioredis": "^4.28.10", "@types/ioredis": "^4.28.10",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"bufferutil": "^4.0.9",
"bullmq": "^5.13.2", "bullmq": "^5.13.2",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"https": "^1.0.0",
"ioredis": "^5.4.1", "ioredis": "^5.4.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"nodemailer": "^6.9.15", "nodemailer": "^6.9.15",
"pino": "^9.3.2", "pino": "^9.3.2",
"reflect-metadata": "^0.2.2",
"sharp": "^0.33.4", "sharp": "^0.33.4",
"socket.io": "^4.7.5", "socket.io": "^4.7.5",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"utf-8-validate": "^6.0.5",
"zod": "^3.23.8" "zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@mikro-orm/cli": "^6.4.2",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.6",
@ -503,13 +507,13 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.19.1", "version": "0.19.2",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
"integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/object-schema": "^2.1.5", "@eslint/object-schema": "^2.1.6",
"debug": "^4.3.1", "debug": "^4.3.1",
"minimatch": "^3.1.2" "minimatch": "^3.1.2"
}, },
@ -542,9 +546,9 @@
} }
}, },
"node_modules/@eslint/core": { "node_modules/@eslint/core": {
"version": "0.10.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz",
"integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@ -603,9 +607,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.19.0", "version": "9.20.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz",
"integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -613,9 +617,9 @@
} }
}, },
"node_modules/@eslint/object-schema": { "node_modules/@eslint/object-schema": {
"version": "2.1.5", "version": "2.1.6",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
"integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@ -636,6 +640,19 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz",
"integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.1", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@ -1073,7 +1090,6 @@
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/@jercle/yargonaut/-/yargonaut-1.1.5.tgz", "resolved": "https://registry.npmjs.org/@jercle/yargonaut/-/yargonaut-1.1.5.tgz",
"integrity": "sha512-zBp2myVvBHp1UaJsNTyS6q4UDKT7eRiqTS4oNTS6VQMd6mpxYOdbeK4pY279cDCdakGy6hG0J3ejoXZVsPwHqw==", "integrity": "sha512-zBp2myVvBHp1UaJsNTyS6q4UDKT7eRiqTS4oNTS6VQMd6mpxYOdbeK4pY279cDCdakGy6hG0J3ejoXZVsPwHqw==",
"dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"chalk": "^4.1.2", "chalk": "^4.1.2",
@ -1107,15 +1123,14 @@
} }
}, },
"node_modules/@mikro-orm/cli": { "node_modules/@mikro-orm/cli": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/@mikro-orm/cli/-/cli-6.4.4.tgz", "resolved": "https://registry.npmjs.org/@mikro-orm/cli/-/cli-6.4.5.tgz",
"integrity": "sha512-GgYpwZFLndijNa1YxPtGeHyqQM3uwXxxoXEh7qbG2MDggkpt8VYGMj66NLjykNv25meARCEdijE02DpVZh8eoQ==", "integrity": "sha512-Ujmpy6ZFs//2TYzi0Q1tzmrOjq+SwtkT7Iv1RUsniaF21N6R71qhQnSHdkJgVuaGGE5a6Qyp52mDWSwW4qb9EQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jercle/yargonaut": "1.1.5", "@jercle/yargonaut": "1.1.5",
"@mikro-orm/core": "6.4.4", "@mikro-orm/core": "6.4.5",
"@mikro-orm/knex": "6.4.4", "@mikro-orm/knex": "6.4.5",
"fs-extra": "11.3.0", "fs-extra": "11.3.0",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"yargs": "17.7.2" "yargs": "17.7.2"
@ -1129,9 +1144,9 @@
} }
}, },
"node_modules/@mikro-orm/core": { "node_modules/@mikro-orm/core": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-6.4.4.tgz", "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-6.4.5.tgz",
"integrity": "sha512-1Ff+fJX7pZFaZt9ebFxPW2w8yDp/uz2VhSMqqop1538VRvTqHseTRI+2Tmm9oX5pm5y5YS7+FdgnyAx6FLWjHg==", "integrity": "sha512-9/CZ5oSbf4P1oBZA2HHKzuxh5yYKDNUZZq/RvJmzMdJDgV8fpTt26po/7J6UytABqZp9yXj1jXc+Kqv73vGGOA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"dataloader": "2.2.3", "dataloader": "2.2.3",
@ -1139,7 +1154,7 @@
"esprima": "4.0.1", "esprima": "4.0.1",
"fs-extra": "11.3.0", "fs-extra": "11.3.0",
"globby": "11.1.0", "globby": "11.1.0",
"mikro-orm": "6.4.4", "mikro-orm": "6.4.5",
"reflect-metadata": "0.2.2" "reflect-metadata": "0.2.2"
}, },
"engines": { "engines": {
@ -1150,9 +1165,9 @@
} }
}, },
"node_modules/@mikro-orm/knex": { "node_modules/@mikro-orm/knex": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/@mikro-orm/knex/-/knex-6.4.4.tgz", "resolved": "https://registry.npmjs.org/@mikro-orm/knex/-/knex-6.4.5.tgz",
"integrity": "sha512-lzTce9SrdD3zO9mQP0MXqXTPM1+X7DXP19SgFnhygLEcBfWPgx1iRxjxOWbLjdM6p41p0Fh+5W73bwF/4jrIfQ==", "integrity": "sha512-5D4NGD9bxmROUf8MVhR4mmZjzQtuM/Sg+eOnfOPzVaIkvuQItE4zR0Bg7gRr1XSgZ/OcJ/6q2ZoXH1HxSjykkw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fs-extra": "11.3.0", "fs-extra": "11.3.0",
@ -1181,12 +1196,12 @@
} }
}, },
"node_modules/@mikro-orm/mariadb": { "node_modules/@mikro-orm/mariadb": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/@mikro-orm/mariadb/-/mariadb-6.4.4.tgz", "resolved": "https://registry.npmjs.org/@mikro-orm/mariadb/-/mariadb-6.4.5.tgz",
"integrity": "sha512-mxTGdOPOyNR7kh18CL2xcFexFkYpyD6jIAQFJlUAmKrXEHpkcOuY8fIHGw8fqJW9eVfBjdNqyDEDjz9iAxFxLA==", "integrity": "sha512-fXyMZ5x1MIkQ+N7TyLgc5SjECpXRF+g0G6dvyYexdyoAo80RWPKksoZzRBrEDDbmMg8eEtpkqJ4bm29szuJijA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@mikro-orm/knex": "6.4.4", "@mikro-orm/knex": "6.4.5",
"mariadb": "3.4.0" "mariadb": "3.4.0"
}, },
"engines": { "engines": {
@ -1197,12 +1212,12 @@
} }
}, },
"node_modules/@mikro-orm/migrations": { "node_modules/@mikro-orm/migrations": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/@mikro-orm/migrations/-/migrations-6.4.4.tgz", "resolved": "https://registry.npmjs.org/@mikro-orm/migrations/-/migrations-6.4.5.tgz",
"integrity": "sha512-eeN91BjV4RDizWu5r+z2taVhFivbLmj04udkJ4ZGrrYGB+DIH6nb7zqLKiLbKvHUknXkzNX10wEPOuav/wW+Kg==", "integrity": "sha512-cMvBLJGVXNT4iUsgjtMi3wtU9I3AtrI+JF68VFUc6tGkHgt3SS2rfrAw5MNAQaa47NXo9OfUWda4/MbRlV0VjA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@mikro-orm/knex": "6.4.4", "@mikro-orm/knex": "6.4.5",
"fs-extra": "11.3.0", "fs-extra": "11.3.0",
"umzug": "3.8.2" "umzug": "3.8.2"
}, },
@ -1214,12 +1229,12 @@
} }
}, },
"node_modules/@mikro-orm/mysql": { "node_modules/@mikro-orm/mysql": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/@mikro-orm/mysql/-/mysql-6.4.4.tgz", "resolved": "https://registry.npmjs.org/@mikro-orm/mysql/-/mysql-6.4.5.tgz",
"integrity": "sha512-OnSsYnr/o8TLjmFXtwI2anuAyNj5Nrgde2uUY5pECGnUkN2jH9+8AdXQmlgljodYNmZrc04QbhbvG65PjL0LsA==", "integrity": "sha512-DAVroZqmXyAgIUXdiTCSRC3wG00FxE8FTi6YqcI0tE5SptJxc2t87lloRoU0oqzrwwKrlIDuBQ8le/opeiogkQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@mikro-orm/knex": "6.4.4", "@mikro-orm/knex": "6.4.5",
"mysql2": "3.12.0" "mysql2": "3.12.0"
}, },
"engines": { "engines": {
@ -1230,9 +1245,9 @@
} }
}, },
"node_modules/@mikro-orm/reflection": { "node_modules/@mikro-orm/reflection": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/@mikro-orm/reflection/-/reflection-6.4.4.tgz", "resolved": "https://registry.npmjs.org/@mikro-orm/reflection/-/reflection-6.4.5.tgz",
"integrity": "sha512-qNScd9vlQdbu5o34N834QGpXTH0OEgjZKZbZYqDzBbEzvMbz4VByXFiMFo0kw39/y2+RlB+hrH7kPfcAYIYQxA==", "integrity": "sha512-2yXbwAEKRmsJ0+Yt/6lAG+NCRgHdSOGTlzWmLvow+PfCUjSubfUM6rlowBchOAH+7swmsUFmHeeKWV6YG2SeMg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"globby": "11.1.0", "globby": "11.1.0",
@ -1366,15 +1381,15 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@rushstack/node-core-library": { "node_modules/@rushstack/node-core-library": {
"version": "5.10.2", "version": "5.11.0",
"resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.10.2.tgz", "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.11.0.tgz",
"integrity": "sha512-xOF/2gVJZTfjTxbo4BDj9RtQq/HFnrrKdtem4JkyRLnwsRz2UDTg8gA1/et10fBx5RxmZD9bYVGST69W8ME5OQ==", "integrity": "sha512-I8+VzG9A0F3nH2rLpPd7hF8F7l5Xb7D+ldrWVZYegXM6CsKkvWc670RlgK3WX8/AseZfXA/vVrh0bpXe2Y2UDQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ajv": "~8.13.0", "ajv": "~8.13.0",
"ajv-draft-04": "~1.0.0", "ajv-draft-04": "~1.0.0",
"ajv-formats": "~3.0.1", "ajv-formats": "~3.0.1",
"fs-extra": "~7.0.1", "fs-extra": "~11.3.0",
"import-lazy": "~4.0.0", "import-lazy": "~4.0.0",
"jju": "~1.4.0", "jju": "~1.4.0",
"resolve": "~1.22.1", "resolve": "~1.22.1",
@ -1419,35 +1434,12 @@
} }
} }
}, },
"node_modules/@rushstack/node-core-library/node_modules/fs-extra": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/@rushstack/node-core-library/node_modules/json-schema-traverse": { "node_modules/@rushstack/node-core-library/node_modules/json-schema-traverse": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@rushstack/node-core-library/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"license": "MIT",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/@rushstack/node-core-library/node_modules/lru-cache": { "node_modules/@rushstack/node-core-library/node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -1475,22 +1467,13 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/@rushstack/node-core-library/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/@rushstack/terminal": { "node_modules/@rushstack/terminal": {
"version": "0.14.5", "version": "0.14.6",
"resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.5.tgz", "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.6.tgz",
"integrity": "sha512-TEOpNwwmsZVrkp0omnuTUTGZRJKTr6n6m4OITiNjkqzLAkcazVpwR1SOtBg6uzpkIBLgrcNHETqI8rbw3uiUfw==", "integrity": "sha512-4nMUy4h0u5PGXVG71kEA9uYI3l8GjVqewoHOFONiM6fuqS51ORdaJZ5ZXB2VZEGUyfg1TOTSy88MF2cdAy+lqA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@rushstack/node-core-library": "5.10.2", "@rushstack/node-core-library": "5.11.0",
"supports-color": "~8.1.1" "supports-color": "~8.1.1"
}, },
"peerDependencies": { "peerDependencies": {
@ -1518,12 +1501,12 @@
} }
}, },
"node_modules/@rushstack/ts-command-line": { "node_modules/@rushstack/ts-command-line": {
"version": "4.23.3", "version": "4.23.4",
"resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.3.tgz", "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.4.tgz",
"integrity": "sha512-HazKL8fv4HMQMzrKJCrOrhyBPPdzk7iajUXgsASwjQ8ROo1cmgyqxt/k9+SdmrNLGE1zATgRqMUH3s/6smbRMA==", "integrity": "sha512-pqmzDJCm0TS8VyeqnzcJ7ncwXgiLDQ6LVmXXfqv2nPL6VIz+UpyTpNVfZRJpyyJ+UDxqob1vIj2liaUfBjv8/A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@rushstack/terminal": "0.14.5", "@rushstack/terminal": "0.14.6",
"@types/argparse": "1.0.38", "@types/argparse": "1.0.38",
"argparse": "~1.0.9", "argparse": "~1.0.9",
"string-argv": "~0.3.1" "string-argv": "~0.3.1"
@ -1545,9 +1528,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@ts-morph/common": { "node_modules/@ts-morph/common": {
"version": "0.26.0", "version": "0.26.1",
"resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.26.0.tgz", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.26.1.tgz",
"integrity": "sha512-/RmKAtctStXqM5nECMQ46duT74Hoig/DBzhWXGHcodlDNrgRbsbwwHqSKFNbca6z9Xt/CUWMeXOsC9QEN1+rqw==", "integrity": "sha512-Sn28TGl/4cFpcM+jwsH1wLncYq3FtN/BIpem+HOygfBWPT5pAeS5dB4VFVzV8FbnOKHpDLZmvAl4AjPEev5idA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
@ -1717,9 +1700,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.17.16", "version": "20.17.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz",
"integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.19.2" "undici-types": "~6.19.2"
@ -1773,21 +1756,21 @@
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz",
"integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", "integrity": "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.22.0", "@typescript-eslint/scope-manager": "8.24.0",
"@typescript-eslint/type-utils": "8.22.0", "@typescript-eslint/type-utils": "8.24.0",
"@typescript-eslint/utils": "8.22.0", "@typescript-eslint/utils": "8.24.0",
"@typescript-eslint/visitor-keys": "8.22.0", "@typescript-eslint/visitor-keys": "8.24.0",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.3.1", "ignore": "^5.3.1",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
"ts-api-utils": "^2.0.0" "ts-api-utils": "^2.0.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1803,16 +1786,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.0.tgz",
"integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", "integrity": "sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.22.0", "@typescript-eslint/scope-manager": "8.24.0",
"@typescript-eslint/types": "8.22.0", "@typescript-eslint/types": "8.24.0",
"@typescript-eslint/typescript-estree": "8.22.0", "@typescript-eslint/typescript-estree": "8.24.0",
"@typescript-eslint/visitor-keys": "8.22.0", "@typescript-eslint/visitor-keys": "8.24.0",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -1828,14 +1811,14 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz",
"integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", "integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.22.0", "@typescript-eslint/types": "8.24.0",
"@typescript-eslint/visitor-keys": "8.22.0" "@typescript-eslint/visitor-keys": "8.24.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1846,16 +1829,16 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.0.tgz",
"integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", "integrity": "sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "8.22.0", "@typescript-eslint/typescript-estree": "8.24.0",
"@typescript-eslint/utils": "8.22.0", "@typescript-eslint/utils": "8.24.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^2.0.0" "ts-api-utils": "^2.0.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1870,9 +1853,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz",
"integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", "integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -1884,20 +1867,20 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz",
"integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", "integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.22.0", "@typescript-eslint/types": "8.24.0",
"@typescript-eslint/visitor-keys": "8.22.0", "@typescript-eslint/visitor-keys": "8.24.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"minimatch": "^9.0.4", "minimatch": "^9.0.4",
"semver": "^7.6.0", "semver": "^7.6.0",
"ts-api-utils": "^2.0.0" "ts-api-utils": "^2.0.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1911,16 +1894,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz",
"integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", "integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.22.0", "@typescript-eslint/scope-manager": "8.24.0",
"@typescript-eslint/types": "8.22.0", "@typescript-eslint/types": "8.24.0",
"@typescript-eslint/typescript-estree": "8.22.0" "@typescript-eslint/typescript-estree": "8.24.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1935,13 +1918,13 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.22.0", "version": "8.24.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz",
"integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", "integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.22.0", "@typescript-eslint/types": "8.24.0",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.0"
}, },
"engines": { "engines": {
@ -2072,7 +2055,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -2082,7 +2064,6 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
@ -2399,10 +2380,23 @@
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/bufferutil": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
"integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/bullmq": { "node_modules/bullmq": {
"version": "5.39.0", "version": "5.40.2",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.39.0.tgz", "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.2.tgz",
"integrity": "sha512-aS+wtZsjW4/9mv2iZRynkSvEXCFS5h3Ko+OgArJco5rEgTE7v/TehDKv6gYEsKVbavOgiNlUNjgEhWhrzDSLBg==", "integrity": "sha512-Cn4NUpwGAF4WnuXR2kTZCTAUEUHajSCn/IqiDG9ry1kVvAwwwg1Ati3J5HN2uZjqD5PBfNDXYnsc2+0PzakDwg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cron-parser": "^4.9.0", "cron-parser": "^4.9.0",
@ -2485,7 +2479,6 @@
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ansi-styles": "^4.1.0", "ansi-styles": "^4.1.0",
@ -2540,7 +2533,6 @@
"version": "8.0.1", "version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"string-width": "^4.2.0", "string-width": "^4.2.0",
@ -2959,7 +2951,6 @@
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/encodeurl": { "node_modules/encodeurl": {
@ -3235,18 +3226,18 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.19.0", "version": "9.20.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz",
"integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.0", "@eslint/config-array": "^0.19.0",
"@eslint/core": "^0.10.0", "@eslint/core": "^0.11.0",
"@eslint/eslintrc": "^3.2.0", "@eslint/eslintrc": "^3.2.0",
"@eslint/js": "9.19.0", "@eslint/js": "9.20.0",
"@eslint/plugin-kit": "^0.2.5", "@eslint/plugin-kit": "^0.2.5",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
@ -3771,9 +3762,9 @@
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/fastq": { "node_modules/fastq": {
"version": "1.18.0", "version": "1.19.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
"integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"reusify": "^1.0.4" "reusify": "^1.0.4"
@ -3783,7 +3774,6 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/figlet/-/figlet-1.8.0.tgz", "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.8.0.tgz",
"integrity": "sha512-chzvGjd+Sp7KUvPHZv6EXV5Ir3Q7kYNpCr4aHrRW79qFtTefmQZNny+W1pW9kf5zeE6dikku2W50W/wAH2xWgw==", "integrity": "sha512-chzvGjd+Sp7KUvPHZv6EXV5Ir3Q7kYNpCr4aHrRW79qFtTefmQZNny+W1pW9kf5zeE6dikku2W50W/wAH2xWgw==",
"dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"figlet": "bin/index.js" "figlet": "bin/index.js"
@ -3889,9 +3879,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/for-each": { "node_modules/for-each": {
"version": "0.3.4", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
"integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4004,7 +3994,6 @@
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC", "license": "ISC",
"engines": { "engines": {
"node": "6.* || 8.* || >= 10.*" "node": "6.* || 8.* || >= 10.*"
@ -4288,6 +4277,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/https": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
"integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==",
"license": "ISC"
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -4317,9 +4312,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/import-fresh": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4383,9 +4378,9 @@
} }
}, },
"node_modules/ioredis": { "node_modules/ioredis": {
"version": "5.4.2", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.2.tgz", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.5.0.tgz",
"integrity": "sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg==", "integrity": "sha512-7CutT89g23FfSa8MDoIFs2GYYa0PaNiW/OrT+nRyjRXHDZd17HmIgy+reOQ/yhh72NznNjGuS8kbCAcA4Ro4mw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ioredis/commands": "^1.1.1", "@ioredis/commands": "^1.1.1",
@ -4489,13 +4484,13 @@
} }
}, },
"node_modules/is-boolean-object": { "node_modules/is-boolean-object": {
"version": "1.2.1", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
"integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"call-bound": "^1.0.2", "call-bound": "^1.0.3",
"has-tostringtag": "^1.0.2" "has-tostringtag": "^1.0.2"
}, },
"engines": { "engines": {
@ -4597,7 +4592,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -4792,13 +4786,13 @@
} }
}, },
"node_modules/is-weakref": { "node_modules/is-weakref": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
"integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"call-bound": "^1.0.2" "call-bound": "^1.0.3"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@ -4882,7 +4876,6 @@
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"json5": "lib/cli.js" "json5": "lib/cli.js"
@ -5137,9 +5130,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/long": { "node_modules/long": {
"version": "5.2.4", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", "resolved": "https://registry.npmjs.org/long/-/long-5.3.0.tgz",
"integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==", "integrity": "sha512-5vvY5yF1zF/kXk+L94FRiTDa1Znom46UjPCH6/XbSvS8zBKMFBHTJk8KDMqJ+2J6QezQFi7k1k8v21ClJYHPaw==",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/lru-cache": { "node_modules/lru-cache": {
@ -5195,9 +5188,9 @@
} }
}, },
"node_modules/mariadb/node_modules/@types/node": { "node_modules/mariadb/node_modules/@types/node": {
"version": "22.12.0", "version": "22.13.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
"integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.20.0" "undici-types": "~6.20.0"
@ -5280,9 +5273,9 @@
} }
}, },
"node_modules/mikro-orm": { "node_modules/mikro-orm": {
"version": "6.4.4", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-6.4.4.tgz", "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-6.4.5.tgz",
"integrity": "sha512-nHV0lZnPUwidqeVyJwBegKDW9KwTxfH5yNp0aGSud9EI0mFkjK/M4EVfh+vwg/y6gdSIu9UnwDORYP2hJw96dQ==", "integrity": "sha512-CFQf87MG4c1N3J/3ELtmwFKXHBzp62o/da7dt43V9cGXygBs65KSk3EBCAdyd5VYNtu/09mt/rztanQSWNWzog==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 18.12.0" "node": ">= 18.12.0"
@ -5340,7 +5333,6 @@
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@ -5458,6 +5450,17 @@
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/node-gyp-build": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
"license": "MIT",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/node-gyp-build-optional-packages": { "node_modules/node-gyp-build-optional-packages": {
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
@ -5578,9 +5581,9 @@
} }
}, },
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.3", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@ -5779,7 +5782,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz",
"integrity": "sha512-2MXDNZC4aXdkkap+rBBMv0lUsfJqvX5/2FiYYnfCnorZt3Pk06/IOR5KeaoghgS2w07MLWgjbsnyaq6PdHn2LQ==", "integrity": "sha512-2MXDNZC4aXdkkap+rBBMv0lUsfJqvX5/2FiYYnfCnorZt3Pk06/IOR5KeaoghgS2w07MLWgjbsnyaq6PdHn2LQ==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
@ -5905,9 +5907,9 @@
} }
}, },
"node_modules/possible-typed-array-names": { "node_modules/possible-typed-array-names": {
"version": "1.0.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
"integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -5925,9 +5927,9 @@
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "3.4.2", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.0.tgz",
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "integrity": "sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@ -6159,7 +6161,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -6338,9 +6339,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.0", "version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
@ -6790,7 +6791,6 @@
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"emoji-regex": "^8.0.0", "emoji-regex": "^8.0.0",
@ -6864,7 +6864,6 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
@ -6877,7 +6876,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=4" "node": ">=4"
@ -6900,7 +6898,6 @@
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"
@ -6980,9 +6977,9 @@
} }
}, },
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
"integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -7049,7 +7046,6 @@
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"json5": "^2.2.2", "json5": "^2.2.2",
@ -7100,9 +7096,9 @@
} }
}, },
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "4.33.0", "version": "4.34.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.34.1.tgz",
"integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==", "integrity": "sha512-6kSc32kT0rbwxD6QL1CYe8IqdzN/J/ILMrNK+HMQCKH3insCDRY/3ITb0vcBss0a3t72fzh2YSzj8ko1HgwT3g==",
"license": "(MIT OR CC0-1.0)", "license": "(MIT OR CC0-1.0)",
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -7290,6 +7286,19 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/utf-8-validate": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.5.tgz",
"integrity": "sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -7445,7 +7454,6 @@
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ansi-styles": "^4.0.0", "ansi-styles": "^4.0.0",
@ -7484,7 +7492,6 @@
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC", "license": "ISC",
"engines": { "engines": {
"node": ">=10" "node": ">=10"
@ -7500,7 +7507,6 @@
"version": "17.7.2", "version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cliui": "^8.0.1", "cliui": "^8.0.1",
@ -7519,7 +7525,6 @@
"version": "21.1.1", "version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC", "license": "ISC",
"engines": { "engines": {
"node": ">=12" "node": ">=12"

View File

@ -1,13 +1,29 @@
{ {
"type": "module",
"tsNode": true,
"scripts": { "scripts": {
"start": "node dist/server.js", "start": "tsx src/server.ts",
"dev": "nodemon --exec tsx src/server.ts", "dev": "nodemon --exec tsx src/server.ts",
"build": "tsc",
"format": "prettier --write src/", "format": "prettier --write src/",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix" "lint:fix": "eslint . --fix"
}, },
"imports": {
"#root/*": "./src/*.js",
"#application/*": "./src/application/*.js",
"#commands/*": "./src/commands/*.js",
"#entities/*": "./src/entities/*.js",
"#controllers/*": "./src/controllers/*.js",
"#jobs/*": "./src/jobs/*.js",
"#managers/*": "./src/managers/*.js",
"#middleware/*": "./src/middleware/*.js",
"#models/*": "./src/models/*.js",
"#repositories/*": "./src/repositories/*.js",
"#services/*": "./src/services/*.js",
"#events/*": "./src/events/*.js"
},
"dependencies": { "dependencies": {
"@mikro-orm/cli": "^6.4.2",
"@mikro-orm/core": "^6.4.2", "@mikro-orm/core": "^6.4.2",
"@mikro-orm/mariadb": "^6.4.2", "@mikro-orm/mariadb": "^6.4.2",
"@mikro-orm/migrations": "^6.4.2", "@mikro-orm/migrations": "^6.4.2",
@ -15,22 +31,25 @@
"@mikro-orm/reflection": "^6.4.2", "@mikro-orm/reflection": "^6.4.2",
"@types/ioredis": "^4.28.10", "@types/ioredis": "^4.28.10",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"bufferutil": "^4.0.9",
"bullmq": "^5.13.2", "bullmq": "^5.13.2",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"https": "^1.0.0",
"ioredis": "^5.4.1", "ioredis": "^5.4.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"nodemailer": "^6.9.15", "nodemailer": "^6.9.15",
"pino": "^9.3.2", "pino": "^9.3.2",
"reflect-metadata": "^0.2.2",
"sharp": "^0.33.4", "sharp": "^0.33.4",
"socket.io": "^4.7.5", "socket.io": "^4.7.5",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"utf-8-validate": "^6.0.5",
"zod": "^3.23.8" "zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@mikro-orm/cli": "^6.4.2",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.6",

Binary file not shown.

View File

@ -1,6 +1,6 @@
import fs from 'fs' import fs from 'fs'
import { Response } from 'express' import type { Response } from 'express'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'

View File

@ -1,18 +1,34 @@
import { SocketEvent } from '#application/enums';
import { Server } from 'socket.io' import { Server } from 'socket.io'
import type { TSocket } from '#application/types'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
import { TSocket } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
export abstract class BaseEvent { export abstract class BaseEvent {
protected readonly logger = Logger.type(LoggerType.GAME) protected readonly logger = Logger.type(LoggerType.GAME)
private lastActionTimes: Map<string, number> = new Map()
constructor( constructor(
readonly io: Server, readonly io: Server,
readonly socket: TSocket readonly socket: TSocket
) {} ) {}
protected isThrottled(actionId: string, throttleTime: number): boolean {
const now = Date.now()
const lastActionTime = this.lastActionTimes.get(actionId) || 0
if (now - lastActionTime < throttleTime) {
return true
}
this.lastActionTimes.set(actionId, now)
return false
}
protected async getCharacter(): Promise<Character | null> { protected async getCharacter(): Promise<Character | null> {
const characterRepository = new CharacterRepository() const characterRepository = new CharacterRepository()
return characterRepository.getById(this.socket.characterId!) return characterRepository.getById(this.socket.characterId!)
@ -24,13 +40,14 @@ export abstract class BaseEvent {
} }
protected emitError(message: string): void { protected emitError(message: string): void {
this.socket.emit('notification', { title: 'Server message', message }) this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Server message', message })
this.logger.error('character:connect error', `Player ${this.socket.userId}: ${message}`) this.logger.error('Base event error', `Player ${this.socket.userId}: ${message}`)
} }
protected handleError(context: string, error: unknown): void { protected handleError(context: string, error: unknown): void {
const errorMessage = error instanceof Error ? error.message : String(error) console.log(error)
this.emitError(`${context}: ${errorMessage}`) const errorMessage = error instanceof Error ? error.message : error && typeof error === 'object' && 'toString' in error ? error.toString() : String(error)
this.logger.error('character:connect error', errorMessage) 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,7 +1,6 @@
import { EntityManager } from '@mikro-orm/core' import { EntityManager } from '@mikro-orm/core'
import Database from '../database' import Database from '#application/database'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
export abstract class BaseRepository { export abstract class BaseRepository {

View File

@ -31,6 +31,10 @@ class config {
static SMTP_PORT: number = process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT) : 587 static SMTP_PORT: number = process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT) : 587
static SMTP_USER: string = process.env.SMTP_USER || 'no-reply@noxious.gg' static SMTP_USER: string = process.env.SMTP_USER || 'no-reply@noxious.gg'
static SMTP_PASSWORD: string = process.env.SMTP_PASSWORD || 'password' static SMTP_PASSWORD: string = process.env.SMTP_PASSWORD || 'password'
// SSL
static PUBLIC_KEY_PATH: string = process.env.PUBLIC_KEY_PATH || ''
static PRIVATE_KEY_PATH: string = process.env.PRIVATE_KEY_PATH || ''
} }
export default config export default config

View File

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

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import * as readline from 'readline' import * as readline from 'readline'
export class ConsolePrompt { export class ConsolePrompt {
@ -10,7 +11,7 @@ export class ConsolePrompt {
output: process.stdout output: process.stdout
}) })
this.rl.on('close', () => { this.rl.on(SocketEvent.CLOSE, () => {
this.isClosed = true this.isClosed = true
}) })
} }

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import * as fs from 'fs' import * as fs from 'fs'
import * as path from 'path' import * as path from 'path'
@ -60,7 +61,7 @@ export class LogReader {
end: newPosition end: newPosition
}) })
stream.on('data', (data) => { stream.on(SocketEvent.DATA, (data) => {
console.log(`[${filename}]`) console.log(`[${filename}]`)
console.log(data.toString()) // console.log(data.toString()) //
}) })

View File

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

View File

@ -1,17 +1,57 @@
export enum SocketEvent { export enum SocketEvent {
CHARACTER_CONNECT = 1, CLOSE = '52',
CHARACTER_MOVE = 2, DATA = '51',
CHARACTER_MOVE_ERROR = 3, CHARACTER_CONNECT = '50',
CHARACTER_TELEPORT = 4, CHARACTER_CREATE = '49',
ZONE_CHARACTER_LEAVE = 5, CHARACTER_DELETE = '48',
ZONE_CHARACTER_JOIN = 6, CHARACTER_LIST = '47',
ZONE_CHARACTER_LIST = 7, GM_CHARACTERHAIR_CREATE = '46',
ZONE_CHARACTER_DELETE = 8, GM_CHARACTERHAIR_REMOVE = '45',
ZONE_CHARACTER_CREATE = 9, GM_CHARACTERHAIR_LIST = '44',
ZONE_CHARACTER_UPDATE = 10, GM_CHARACTERHAIR_UPDATE = '43',
ZONE_CHARACTER_HAIR_UPDATE = 11, GM_CHARACTERTYPE_CREATE = '42',
ZONE_CHARACTER_HAIR_LIST = 12, GM_CHARACTERTYPE_REMOVE = '41',
ZONE_CHARACTER_TELEPORT = 13 GM_CHARACTERTYPE_LIST = '40',
GM_CHARACTERTYPE_UPDATE = '39',
GM_ITEM_CREATE = '38',
GM_ITEM_REMOVE = '37',
GM_ITEM_LIST = '36',
GM_ITEM_UPDATE = '35',
GM_MAPOBJECT_LIST = '34',
GM_MAPOBJECT_REMOVE = '33',
GM_MAPOBJECT_UPDATE = '32',
GM_MAPOBJECT_UPLOAD = '31',
GM_SPRITE_COPY = '30',
GM_SPRITE_CREATE = '29',
GM_SPRITE_DELETE = '28',
GM_SPRITE_LIST = '27',
GM_SPRITE_UPDATE = '26',
GM_TILE_DELETE = '25',
GM_TILE_LIST = '24',
GM_TILE_UPDATE = '23',
GM_TILE_UPLOAD = '22',
GM_MAP_CREATE = '21',
GM_MAP_DELETE = '20',
GM_MAP_REQUEST = '19',
GM_MAP_UPDATE = '18',
MAP_CHARACTER_MOVEERROR = '17',
DISCONNECT = '16',
USER_DISCONNECT = '15',
LOGIN = '14',
LOGGED_IN = '13',
NOTIFICATION = '12',
DATE = '11',
FAILED = '10',
COMPLETED = '9',
CONNECTION = '8',
WEATHER = '7',
CHARACTER_DISCONNECT = '6',
MAP_CHARACTER_ATTACK = '5',
MAP_CHARACTER_TELEPORT = '4',
MAP_CHARACTER_JOIN = '3',
MAP_CHARACTER_LEAVE = '2',
MAP_CHARACTER_MOVE = '1',
CHAT_MESSAGE = '0',
} }
export enum ItemType { export enum ItemType {

View File

@ -1,4 +1,5 @@
import pino from 'pino' import pino from 'pino'
const logger = pino.pino
export enum LoggerType { export enum LoggerType {
HTTP = 'http', HTTP = 'http',
@ -13,13 +14,13 @@ export enum LoggerType {
} }
class Logger { 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)) { if (!this.instances.has(type)) {
this.instances.set( this.instances.set(
type, type,
pino({ logger({
level: process.env.LOG_LEVEL || 'debug', level: process.env.LOG_LEVEL || 'debug',
transport: { transport: {
target: 'pino/file', target: 'pino/file',

View File

@ -9,7 +9,7 @@ class Storage {
constructor() { constructor() {
this.rootDir = process.cwd() this.rootDir = process.cwd()
this.baseDir = config.ENV === 'development' ? 'src' : 'dist' this.baseDir = config.ENV === 'development' ? 'src' : 'src'
} }
/** /**

View File

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

View File

@ -1,11 +1,12 @@
import fs from 'fs' wimport fs from 'fs'
import sharp from 'sharp' import sharp from 'sharp'
import type { UUID } from '#application/types'
import { BaseCommand } from '#application/base/baseCommand' import { BaseCommand } from '#application/base/baseCommand'
import { CharacterGender, CharacterRace } from '#application/enums' import { CharacterGender, CharacterRace } from '#application/enums'
import Storage from '#application/storage' import Storage from '#application/storage'
import { UUID } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import { CharacterHair } from '#entities/characterHair' import { CharacterHair } from '#entities/characterHair'
import { CharacterType } from '#entities/characterType' import { CharacterType } from '#entities/characterType'
@ -64,12 +65,12 @@ export default class InitCommand extends BaseCommand {
.setFrameWidth( .setFrameWidth(
(await sharp(Storage.getPublicPath('map_objects', mapObject)) (await sharp(Storage.getPublicPath('map_objects', mapObject))
.metadata() .metadata()
.then((metadata) => metadata.height)) ?? 0 .then((metadata) => metadata.width)) ?? 0
) )
.setFrameHeight( .setFrameHeight(
(await sharp(Storage.getPublicPath('map_objects', mapObject)) (await sharp(Storage.getPublicPath('map_objects', mapObject))
.metadata() .metadata()
.then((metadata) => metadata.width)) ?? 0 .then((metadata) => metadata.height)) ?? 0
) )
await newMapObject.save() await newMapObject.save()
@ -95,7 +96,7 @@ export default class InitCommand extends BaseCommand {
]) ])
.setOriginX(0) .setOriginX(0)
.setOriginY(0) .setOriginY(0)
.setFrameWidth(64) .setFrameWidth(28)
.setFrameHeight(94) .setFrameHeight(94)
.setFrameRate(0) .setFrameRate(0)
.setSprite(characterSprite) .setSprite(characterSprite)
@ -115,8 +116,8 @@ export default class InitCommand extends BaseCommand {
]) ])
.setOriginX(0) .setOriginX(0)
.setOriginY(0) .setOriginY(0)
.setFrameWidth(64) .setFrameWidth(26)
.setFrameHeight(94) .setFrameHeight(93)
.setFrameRate(0) .setFrameRate(0)
.setSprite(characterSprite) .setSprite(characterSprite)
.save() .save()
@ -156,8 +157,8 @@ export default class InitCommand extends BaseCommand {
]) ])
.setOriginX(0) .setOriginX(0)
.setOriginY(0) .setOriginY(0)
.setFrameWidth(64) .setFrameWidth(36)
.setFrameHeight(94) .setFrameHeight(102)
.setFrameRate(7) .setFrameRate(7)
.setSprite(characterSprite) .setSprite(characterSprite)
.save() .save()
@ -197,12 +198,80 @@ export default class InitCommand extends BaseCommand {
]) ])
.setOriginX(0) .setOriginX(0)
.setOriginY(0) .setOriginY(0)
.setFrameWidth(64) .setFrameWidth(34)
.setFrameHeight(94) .setFrameHeight(101)
.setFrameRate(7) .setFrameRate(7)
.setSprite(characterSprite) .setSprite(characterSprite)
.save() .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,iVBORw0KGgoAAAANSUhEUgAAACMAAABbCAYAAAD5qDz1AAAHtElEQVR4nL1aTWgbRxT+LAfF2BEqa0drQ2ANsdBFpjKUCDckF1fuJfiQnErdoINoTCgYYgjkEB9cSunBgVza5GbSQ0mPPjrooosRFOQgQxFyQAJDsouzxCg2jiF2D6s3ml3t7M6u5X6woP3/9s1733vzRn0zj54jDJT1lVO34+bccl+oBwK4EJZEPjOOqZmbXecXV63zYUgFIqOsr5zmM+NQEiq0yRQGkmkMjgzZrnn+LI2j+jYWV1dOgxKKBCUyNXPTRqR/OGnbBkeGMJBM4+nSXeFQnpkMACgJFQAwkEwLrzkLISkyZBVtMiX7XADepEOTEeFw7wCf39c9rwlincDR5EZoEPWuY2FwZjJnebkT0sM0vXAfzWoNAHCy35J+weLqi96TOapvI3X7DirFEo6Npu3cvYWHuLfwsOueewsPUUpP9J4MWWV64T4qxVLXC1++bnQRevm6IU0kEBnT0HFU3wZg6Q0NFb1w7PtvAr88FBlzbrlvbavBrKNNpnBsNHGy38KHzb/Zdfxv2q98+7U0mUDRRNZpVmuMUBQa/v3pR9t1fL468pYhG+SHibOOaejsODnz4MgQ2whBQz6QAptzy31PNsoAYHPik/0WDvcOuraT/RYWV19IlxOh0sHaVsNGiPzHuTklwA+hFDifGWeECO6FlrxVQpMhQkDHSk5yQPBqr0+2BubLTar0RGhWayDfCkLK1zI8CQIRoWrvcO+ACeJAMo1UMo2nkykWeWvrciWop2WoqAI6VZ6bRfhwd7NapVjC2lbD10JCMs7iW5nOAgDMzbJrBceLIdCp8ug4ADzZKHsS8hwmnkj/cLJ91PKFWCrDrrOqvTS09j5frAOABst6D2azeOIxZK46w9e8A8k0RwSIJjSOgAX+vNsxsphfDe0petGEZpN3ngDVv7Q54XY8mtCsaBTUxMJhUhIqIvEY+8LP7+s43DtgyTESj9lyD1/9WZGV5n5bRC5dHW0HQsP1nV2WUdZXTh/MZpGcmWVWadW2upKeSPrJWY/q2zjZb7FIi1+7gf7hpOdQCYeJrMKb+qi+7Vp20n6lWIJp6KgUS6gUSyj/9ScTP/45oqFyHSayCg0NfTl9NU+ASAAdLSLwpYa5WcblW0lcvjUPcBlfSIaiKBKPAejUI0SEHl5xPGxq5iaLMp6sWbSuX9tqQEmoUKbr6B9OCv3GNx2c7Le6Xs6rbDShMfKUGgAgCjs509Dx8c07xMlvuNwlJEOm5oeHMjKlBicRXuBiw+3Ic/mwenEDU1dHhR/OHJiGKHX7Djt5bDRZTUJlJ4G3iFP0nPt0r2noMDfLQr2xRZOSUFk48znFC85mkd9xeqbT2bvI8BrgLLxlwKuuU5ecltUmU13WsfmMMxvLpH2eCCOwtwUArlFjGrql4o7oAwSid7LfkrZKkOmIczLoHKoI0HFeGmeKoCA1bCBS7Q91pgZmGb66l3FcoJMcnSnDk0jbOvXiBpTpLP747THzG9swUUgGdVwesh/Cv4/AyARtBhJoNkkgnZJpKJFFKap60kYL0skikPX5GodZRiRSbmhWa4Gnrm74+OYdgI4jR5T1ldOnS3dtF/lFkmnojJCI1LHRFPqeW2oBAk5v36sprL5t77z9ALwuY2nsEwB7Jif1Xn17EVBTEH3VsdEEuMQZ2GdyuZxtf/XVKxu5Di4il8vhFZ13QaVYQjahIX7tRjgyPFRVxfz8vPC8rvtLxLHRxOf3o2Iy+cy47/xYVS0pn5jotFavXLnCfu/u7nqSMOeW+9bWV06VhMp8pis3dcpCf4iI0P7ExARyuRxOyy+FaweUOG1kSLj++fXxmRTYSej69evC8xRVVMBFALuEm4YunSR3dnbY793dXdehcVrMjRC96wIRCKOiTkJOAoVCAcN6DVDl1qkiZCrLq+u+VqFQ1XXdNVp2dnZQKBRQKBSwNPYJ+cy4RUgCLJoqxRJm2j0YEYb1GvKZ8Y62CLA09omJYLNaQz4Dqe6VNUztMJuWLJB++fIL2z45/NpWw9bziyY0aJNgkzk/2EJ789nvvjfw7TSSf5EUROIxRBOatFQwMmxuIxFFfLlIv+leZwv20tVR15mAJxkiJPMFBFJOkS5RWSJbuIVevSUiblMOJ7nBkSGpoTrTUjIQbiZxZjJ8T5hHkAK8Z2TC4FzXm9zg9A8+omgOdbh3IBVRof6YAXTaaCJ/oeH7+OaddN4L9McM54tkZwiy4heq7DQNHah6zzxNQ4eGFOtsuXWynJCPJu6rlIQqXfeQ8MnMy3zJ8EMk0/8H7K0PiqjDvQPfCjLwMFEmRhUQtd15HNW3pde25RzYZxmwVwhkGbc8JANKlEq1Bi9relrGLQVQt9xvVc2JnjhwGPAf4fZfvtBkeH/pRRskFBmvLC07RNpkKlDf51yzdlBIR1Mv6xYRpC1D6itbXPPdBUCutpEmE01obMt+94PsbQwyZYQ0GT6S6sWNQERkKz6hz9AsM5+xZoRW1rb8JkjLhF9EC02GJ2TtNbrOed1L2hSku+EbTb2Ygsji3HQmTFL930TPqoNSeDCbFcrCuZHhHTYSjyESj0GZzuKrRz8LdercyFA7n1/zBqyVFNEsoSerKm6gGQR5Dl96iqRB+t9oQeGXLtyi9D+rtYwUcNakSwAAAABJRU5ErkJggg==',
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,iVBORw0KGgoAAAANSUhEUgAAAB0AAABjCAYAAABnuwu+AAAGeklEQVR4nO2av2sbSRTHv7KDYqQYgySk5DCsIdK5scGuTAi4EVZ1+C8wh8vjSqc6DlwYrnTgyusEFzjSmlQRbtwEVxbIzUYKeMEk2UVeTsgWjiDyFas3ntmd3ZnVD1f3IGDtTOYzb+fNm+/MbKL8218gyxwd3AGAu72fwBRthgfuri1hd22JwacKJWAmX5gmS4QCQCZfgLG6/DBQ8vKhgADnKQDMlVYeBPoI8F6tLjAsyOJE/CPdigTbq2wEhuLs+ATVGNNNgKZy6WEEXwSAe5UNlMoVPHn+VCi7/vQV6+VNZPIFuI6N6tHBnQrMoKlcGr32jdTDP1/9jLnSClK5NGazJaH8CYBrAMYqYMB7AyowC6Re+wapXFrqYTJvSIGskYV5JPOG93805jqDhnlaKlcwszCvbIjMWF1WZrUZAHAdm3lKQUJeRtn3qybraJyOCWMq6zU11mvfIIWmUC57Mzo2427vJ6r1C8xmS6FjxkP4f7wNOl0AgNUw1VBVBWpM11zHxotfftWDfr9qBgp1es137Oz4RAkUoGQU+rJGo4Cn//zNgLfN80goC6Re+wbzWbHQdWwtMCDOT9UbYp5SozML89hdW2IV+o4V2QDVoal22zwXOiuz0ISfSyVRrV8MF3exLJk3Ap2xGiYMjBC9smByHVvZEJVbDVPppQDtO1Zg7tEc9oP57EPPXcfWAgpQ4H5c+aAgMA+QAXmr1i8i11UG5Rv0L9Lu9n7i9ftTpQfV+oUSKEBdx0bfsZDKpZHMG/jpxx9G0r86ykGaBmcW5iPXxbip0W8J2laQQsi82ECvfYPb5rmnfYbjSctcmFQ9Oz4BAFY/ymPpPPWWuRVkGib2KgUG889PSpl9x8J6eRNWw8TumlcWJdQC0F77BvPLa0C7LniVzBueLIEHpt+pXBrXwzq8TooSagLUaphYHurfVC6NQScI6AGAw78RLwYGnS7znBRjmFATAsl1bBYks9mSILjomV9hBNTh86eYzZaQebHBPPZbIHpVCZ6HyJTG9aevrGyutCIVavfzdJh5rIYp5GC+E7Lc7K/bPH7PftOc91vAU9ex4X44xferJm6b54HUx0PCFgh6TkPkf8XimHLeuh9OteUKmdUw8e7j58DC4Z/bQU+Hefbs+ER71ZAZeSuTttI0yK8svOnq3EGnG1k3VIKOA6bg65p19oyPYKXuJQtL8rJOnB2fRC4K2tAoMD2nGHj38TP6jsVeczJvCBEcC+q3qFftj3w+gmND/d6GeU+iAAju6LTPHMYxWjzItDwNy8e6CsKfo8caU974jrV7/ci6E4PyQNkc58dVG6qzp/Gb1TClQ6CE8vm371gMznei71jKxYHPwVqehjXIdyKsw7LySOhVYRmHXx4rN1E6Gyc+gpXzdGtrC4e1GvDlX/yBIJhgh18eew8Ky1BJfK3ksLOzA9u28XutJil9zDoHADWuTrV+gfXyJltbd9eWUD06uFNCC4UCisUiisUiXr58iVarhWKxyMpbrRb727bvX7G7vZ/gxRifp5WBxAPCftOzQiEoN62GyU7jaKXRit7FxUUsLi5Kn9Fzf2d48587KqE8jP6WdSDK/CesE0+DMovt6bhGJ6y8KaFv375lf19eXqLVauHy8lKow//O2mqtHDllsraJWu0+Kvkp4bdWqxVZzh9qKefpq2ffcPjmjfBsa2sLtm0HOlOr1ZANtBA8K9TKSG92vG0fHQdIE9Owg9UQZ62GyU5elND18ibbFK/DS22vnn0L3M1FJX1/2UjRm8kXUCpXMFdawVxpBcm8gVK5ElrfdWzhfEkL6peQdLbPb/+lsKFs8R9ojSxB6Z4mhfsbC9ntlex05UEy0kShsp341KGj2thbxalAeUEW9wxiZCigvvKYChQQxTVJEEDUProHIyPP00Gn650TIv54x4L6txhJGMJvXRtpyvhfIwF1zu9Hho5rE4PGmU5aUNlJ5jimhNJCzYPH/Zon1no6qU+HlFDy0L9Qk4SZClRmvHIYBRwLygsx2TnuVKCTsv+hzGR3a1OHTsO05IrOehnnazzto9dBp6tcM+lyQHXDHAmlOxqrYUae//FfXel82qlUDu72fuL10cEdJBfxbFsBIAnD+wijodZKWnKFVwPkgexbF10bOXoHna7wuVccG0kNWg0TxirYtTRwf+br34tOBEpjvAcA3KkowahOVBsJ/kvmOCaLTt3vQf8DHb+mhueQ5GsAAAAASUVORK5CYII=',
offset: {
x: 2,
y: 0
}
},
{
url: 'data:image/png;base64,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',
offset: {
x: 5,
y: 0
}
},
{
url: 'data:image/png;base64,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',
offset: {
x: 6,
y: 1
}
}
])
.setOriginX(0)
.setOriginY(0)
.setFrameWidth(34)
.setFrameHeight(100)
.setFrameRate(5)
.setSprite(characterSprite)
.save()
const characterType = new CharacterType() const characterType = new CharacterType()
await characterType await characterType
.setId('75b70c78-17f0-44c0-a4fa-15043cb95be0') .setId('75b70c78-17f0-44c0-a4fa-15043cb95be0')
@ -233,7 +302,7 @@ export default class InitCommand extends BaseCommand {
]) ])
.setOriginX(0.5) .setOriginX(0.5)
.setOriginY(5.34) .setOriginY(5.34)
.setFrameWidth(64) .setFrameWidth(24)
.setFrameHeight(18) .setFrameHeight(18)
.setFrameRate(0) .setFrameRate(0)
.setSprite(hairSprite) .setSprite(hairSprite)
@ -253,7 +322,7 @@ export default class InitCommand extends BaseCommand {
]) ])
.setOriginX(0.5) .setOriginX(0.5)
.setOriginY(4.34) .setOriginY(4.34)
.setFrameWidth(64) .setFrameWidth(24)
.setFrameHeight(22) .setFrameHeight(22)
.setFrameRate(0) .setFrameRate(0)
.setSprite(hairSprite) .setSprite(hairSprite)

View File

@ -1,6 +1,7 @@
import { Request, Response } from 'express'
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import type { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController' import { BaseController } from '#application/base/baseController'
import config from '#application/config' import config from '#application/config'
import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from '#application/zodTypes' import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from '#application/zodTypes'

View File

@ -1,11 +1,12 @@
import fs from 'fs' import fs from 'fs'
import { Request, Response } from 'express'
import sharp from 'sharp' import sharp from 'sharp'
import type { UUID } from '#application/types'
import type { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController' import { BaseController } from '#application/base/baseController'
import Storage from '#application/storage' import Storage from '#application/storage'
import { UUID } from '#application/types'
import CharacterHairRepository from '#repositories/characterHairRepository' import CharacterHairRepository from '#repositories/characterHairRepository'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
import CharacterTypeRepository from '#repositories/characterTypeRepository' import CharacterTypeRepository from '#repositories/characterTypeRepository'
@ -26,7 +27,7 @@ export class AvatarController extends BaseController {
* @param res * @param res
*/ */
public async getByName(req: Request, res: Response) { 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) { if (!character?.characterType) {
return this.sendError(res, 'Character or character type not found', 404) return this.sendError(res, 'Character or character type not found', 404)
} }

View File

@ -1,4 +1,4 @@
import { Request, Response } from 'express' import type { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController' import { BaseController } from '#application/base/baseController'
import CharacterHairRepository from '#repositories/characterHairRepository' import CharacterHairRepository from '#repositories/characterHairRepository'

View File

@ -1,4 +1,4 @@
import { Request, Response } from 'express' import type { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController' import { BaseController } from '#application/base/baseController'
import Storage from '#application/storage' import Storage from '#application/storage'
@ -12,6 +12,10 @@ export class TexturesController extends BaseController {
public async download(req: Request, res: Response) { public async download(req: Request, res: Response) {
const { type, spriteId, file } = req.params 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) const texture = type === 'sprites' && spriteId ? Storage.getPublicPath(type, spriteId, file) : Storage.getPublicPath(type, file)
this.sendFile(res, texture) this.sendFile(res, texture)

View File

@ -1,16 +1,17 @@
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
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 { BaseEntity } from '#application/base/baseEntity' 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'
export class BaseCharacter extends BaseEntity { export class BaseCharacter extends BaseEntity {
@PrimaryKey() @PrimaryKey()
@ -28,7 +29,7 @@ export class BaseCharacter extends BaseEntity {
@Property() @Property()
role = 'player' role = 'player'
@OneToMany(() => Chat, (chat) => chat.character) @OneToMany({ mappedBy: 'character' })
chats = new Collection<Chat>(this) chats = new Collection<Chat>(this)
// Position - @TODO: Update to spawn point when current map is not found // Position - @TODO: Update to spawn point when current map is not found

View File

@ -2,11 +2,12 @@ import { randomUUID } from 'node:crypto'
import { Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core' import { Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
import type { UUID } from '#application/types'
import type { Character } from '#entities/character'
import type { CharacterItem } from '#entities/characterItem'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { CharacterEquipmentSlotType } from '#application/enums' 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 { export class BaseCharacterEquipment extends BaseEntity {
@PrimaryKey() @PrimaryKey()

View File

@ -2,9 +2,10 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import type { UUID } from '#application/types'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { CharacterGender } from '#application/enums' import { CharacterGender } from '#application/enums'
import { UUID } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import { Sprite } from '#entities/sprite' import { Sprite } from '#entities/sprite'

View File

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

View File

@ -2,9 +2,10 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import type { UUID } from '#application/types'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { CharacterGender, CharacterRace } from '#application/enums' import { CharacterGender, CharacterRace } from '#application/enums'
import { UUID } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import { Sprite } from '#entities/sprite' import { Sprite } from '#entities/sprite'

View File

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

View File

@ -2,9 +2,10 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import type { UUID } from '#application/types'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { ItemType, ItemRarity } from '#application/enums' import { ItemType, ItemRarity } from '#application/enums'
import { UUID } from '#application/types'
import { CharacterItem } from '#entities/characterItem' import { CharacterItem } from '#entities/characterItem'
import { Sprite } from '#entities/sprite' import { Sprite } from '#entities/sprite'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,8 +3,9 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import type { UUID } from '#application/types'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import { PasswordResetToken } from '#entities/passwordResetToken' import { PasswordResetToken } from '#entities/passwordResetToken'

View File

@ -1,9 +1,11 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapManager from '#managers/mapManager' import MapManager from '#managers/mapManager'
import CharacterHairRepository from '#repositories/characterHairRepository' import CharacterHairRepository from '#repositories/characterHairRepository'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
import TeleportService from '#services/teleportService' import TeleportService from '#services/characterTeleportService'
interface CharacterConnectPayload { interface CharacterConnectPayload {
characterId: UUID characterId: UUID
@ -15,7 +17,7 @@ export default class CharacterConnectEvent extends BaseEvent {
private readonly characterRepository = new CharacterRepository() private readonly characterRepository = new CharacterRepository()
public listen(): void { public listen(): void {
this.socket.on('character:connect', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHARACTER_CONNECT, this.handleEvent.bind(this))
} }
private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> { private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> {

View File

@ -1,67 +1,85 @@
import { ZodError } from 'zod' import { SocketEvent } from '#application/enums';
import { ZodError, z } from 'zod'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { ZCharacterCreate } from '#application/zodTypes' import { ZCharacterCreate } from '#application/zodTypes'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import MapRepository from '#repositories/mapRepository' import MapRepository from '#repositories/mapRepository'
import UserRepository from '#repositories/userRepository' import UserRepository from '#repositories/userRepository'
const MAX_CHARACTERS = 4
export default class CharacterCreateEvent extends BaseEvent { export default class CharacterCreateEvent extends BaseEvent {
private readonly userRepository: UserRepository = new UserRepository()
private readonly characterRepository: CharacterRepository = new CharacterRepository()
private readonly characterTypeRepository: CharacterTypeRepository = new CharacterTypeRepository()
private readonly mapRepository: MapRepository = new MapRepository()
public listen(): void { public listen(): void {
this.socket.on('character:create', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHARACTER_CREATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: any): Promise<any> { private async handleEvent(data: z.infer<typeof ZCharacterCreate>, callback: (success: boolean) => void): Promise<void> {
// zod validate
try { try {
data = ZCharacterCreate.parse(data) const validatedData = ZCharacterCreate.parse(data)
await this.createCharacter(validatedData)
const userRepository = new UserRepository() callback(true)
const characterRepository = new CharacterRepository() } catch (error: unknown) {
const mapRepository = new MapRepository() this.returnError(error)
callback(false)
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).' })
} }
} }
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,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
@ -13,7 +15,7 @@ type TypeResponse = {
export default class CharacterDeleteEvent extends BaseEvent { export default class CharacterDeleteEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('character:delete', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHARACTER_DELETE, this.handleEvent.bind(this))
} }
private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> { private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
@ -22,9 +24,9 @@ export default class CharacterDeleteEvent extends BaseEvent {
await (await characterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete() await (await characterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete()
const characters: Character[] = await characterRepository.getByUserId(this.socket.userId!) const characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
this.socket.emit('character:list', characters) this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
} catch (error: any) { } catch (error: any) {
return this.socket.emit('notification', { message: 'Character delete failed. Please try again.' }) return this.socket.emit(SocketEvent.NOTIFICATION, { message: 'Character delete failed. Please try again.' })
} }
} }
} }

View File

@ -1,10 +1,11 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
export default class CharacterListEvent extends BaseEvent { export default class CharacterListEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('character:list', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHARACTER_LIST, this.handleEvent.bind(this))
} }
private async handleEvent(data: any): Promise<void> { private async handleEvent(data: any): Promise<void> {
@ -12,7 +13,7 @@ export default class CharacterListEvent extends BaseEvent {
const characterRepository = new CharacterRepository() const characterRepository = new CharacterRepository()
let characters: Character[] = await characterRepository.getByUserId(this.socket.userId!) let characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
this.socket.emit('character:list', characters) this.socket.emit(SocketEvent.CHARACTER_LIST, characters)
} catch (error: any) { } catch (error: any) {
this.logger.error('character:list error', error.message) this.logger.error('character:list error', error.message)
} }

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
import ChatService from '#services/chatService' import ChatService from '#services/chatService'
@ -8,7 +9,7 @@ type TypePayload = {
export default class AlertCommandEvent extends BaseEvent { export default class AlertCommandEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('chat:message', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
} }
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
@ -25,7 +26,7 @@ export default class AlertCommandEvent extends BaseEvent {
return callback(false) return callback(false)
} }
this.io.emit('notification', { title: 'Message from GM', message: args.join(' ') }) this.io.emit(SocketEvent.NOTIFICATION, { title: 'Message from GM', message: args.join(' ') })
return callback(true) return callback(true)
} catch (error: any) { } catch (error: any) {
this.logger.error('chat:alert_command error', error.message) this.logger.error('chat:alert_command error', error.message)

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import DateManager from '#managers/dateManager' import DateManager from '#managers/dateManager'
import CharacterRepository from '#repositories/characterRepository' import CharacterRepository from '#repositories/characterRepository'
@ -9,7 +10,7 @@ type TypePayload = {
export default class SetTimeCommand extends BaseEvent { export default class SetTimeCommand extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('chat:message', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
} }
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {

View File

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

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager' import WeatherManager from '#managers/weatherManager'
import ChatService from '#services/chatService' import ChatService from '#services/chatService'
@ -8,7 +9,7 @@ type TypePayload = {
export default class ToggleFogCommand extends BaseEvent { export default class ToggleFogCommand extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('chat:message', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
} }
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager' import WeatherManager from '#managers/weatherManager'
import ChatService from '#services/chatService' import ChatService from '#services/chatService'
@ -8,7 +9,7 @@ type TypePayload = {
export default class ToggleRainCommand extends BaseEvent { export default class ToggleRainCommand extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('chat:message', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
} }
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import MapManager from '#managers/mapManager' import MapManager from '#managers/mapManager'
import MapRepository from '#repositories/mapRepository' import MapRepository from '#repositories/mapRepository'
@ -9,7 +10,7 @@ type TypePayload = {
export default class ChatMessageEvent extends BaseEvent { export default class ChatMessageEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('chat:message', this.handleEvent.bind(this)) this.socket.on(SocketEvent.CHAT_MESSAGE, this.handleEvent.bind(this))
} }
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,9 +1,10 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import MapManager from '#managers/mapManager' import MapManager from '#managers/mapManager'
export default class DisconnectEvent extends BaseEvent { export default class DisconnectEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('disconnect', this.handleEvent.bind(this)) this.socket.on(SocketEvent.DISCONNECT, this.handleEvent.bind(this))
} }
private async handleEvent(): Promise<void> { private async handleEvent(): Promise<void> {
@ -13,7 +14,7 @@ export default class DisconnectEvent extends BaseEvent {
return return
} }
this.io.emit('user:disconnect', this.socket.userId) this.io.emit(SocketEvent.USER_DISCONNECT, this.socket.userId)
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!) const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
if (!mapCharacter) { if (!mapCharacter) {

View File

@ -1,9 +1,10 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { CharacterHair } from '#entities/characterHair' import { CharacterHair } from '#entities/characterHair'
export default class CharacterHairCreateEvent extends BaseEvent { export default class CharacterHairCreateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterHair:create', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERHAIR_CREATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,27 +1,32 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import CharacterHairRepository from '#repositories/characterHairRepository' import CharacterHairRepository from '#repositories/characterHairRepository'
interface IPayload { interface IPayload {
id: UUID id: UUID
} }
export default class characterHairDeleteEvent extends BaseEvent { export default class CharacterHairDeleteEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterHair:remove', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERHAIR_REMOVE, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
try { try {
if (!(await this.isCharacterGM())) return if (!(await this.isCharacterGM())) return
const characterHair = await CharacterHairRepository.getById(data.id) const characterHairRepository = new CharacterHairRepository()
await (await CharacterHairRepository.getById(data.id))?.delete() const characterHair = await characterHairRepository.getById(data.id)
if (!characterHair) return callback(false)
await characterHair.delete()
return callback(true) return callback(true)
} catch (error) { } catch (error) {
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`) this.logger.error(`Error deleting character hair ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
callback(false) return callback(false)
} }
} }
} }

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { CharacterHair } from '#entities/characterHair' import { CharacterHair } from '#entities/characterHair'
import CharacterHairRepository from '#repositories/characterHairRepository' import CharacterHairRepository from '#repositories/characterHairRepository'
@ -6,7 +7,7 @@ interface IPayload {}
export default class characterHairListEvent extends BaseEvent { export default class characterHairListEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterHair:list', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERHAIR_LIST, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {

View File

@ -1,6 +1,8 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { CharacterGender } from '#application/enums' import { CharacterGender } from '#application/enums'
import { UUID } from '#application/types'
import CharacterHairRepository from '#repositories/characterHairRepository' import CharacterHairRepository from '#repositories/characterHairRepository'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
@ -14,7 +16,7 @@ type Payload = {
export default class CharacterHairUpdateEvent extends BaseEvent { export default class CharacterHairUpdateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterHair:update', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERHAIR_UPDATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {

View File

@ -1,9 +1,10 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { CharacterType } from '#entities/characterType' import { CharacterType } from '#entities/characterType'
export default class CharacterTypeCreateEvent extends BaseEvent { export default class CharacterTypeCreateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterType:create', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERTYPE_CREATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> { private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {

View File

@ -1,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import CharacterTypeRepository from '#repositories/characterTypeRepository' import CharacterTypeRepository from '#repositories/characterTypeRepository'
interface IPayload { interface IPayload {
@ -8,7 +10,7 @@ interface IPayload {
export default class CharacterTypeDeleteEvent extends BaseEvent { export default class CharacterTypeDeleteEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterType:remove', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERTYPE_REMOVE, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { CharacterType } from '#entities/characterType' import { CharacterType } from '#entities/characterType'
import CharacterTypeRepository from '#repositories/characterTypeRepository' import CharacterTypeRepository from '#repositories/characterTypeRepository'
@ -6,7 +7,7 @@ interface IPayload {}
export default class CharacterTypeListEvent extends BaseEvent { export default class CharacterTypeListEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterType:list', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERTYPE_LIST, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> {

View File

@ -1,6 +1,8 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { CharacterGender, CharacterRace } from '#application/enums' import { CharacterGender, CharacterRace } from '#application/enums'
import { UUID } from '#application/types'
import CharacterTypeRepository from '#repositories/characterTypeRepository' import CharacterTypeRepository from '#repositories/characterTypeRepository'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
@ -15,7 +17,7 @@ type Payload = {
export default class CharacterTypeUpdateEvent extends BaseEvent { export default class CharacterTypeUpdateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:characterType:update', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_CHARACTERTYPE_UPDATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {

View File

@ -1,17 +1,24 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { ItemRarity, ItemType } from '#application/enums'
import { Item } from '#entities/item' import { Item } from '#entities/item'
import SpriteRepository from '#repositories/spriteRepository'
export default class ItemCreateEvent extends BaseEvent { export default class ItemCreateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:item:create', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_ITEM_CREATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: undefined, callback: (response: boolean, item?: any) => void): Promise<void> { private async handleEvent(data: undefined, callback: (response: boolean, item?: any) => void): Promise<void> {
try { try {
if (!(await this.isCharacterGM())) return if (!(await this.isCharacterGM())) return
const spriteRepository = new SpriteRepository()
const sprite = await spriteRepository.getFirst()
if (!sprite) return callback(false)
const newItem = new Item() 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) return callback(true, newItem)
} catch (error) { } catch (error) {

View File

@ -1,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import ItemRepository from '#repositories/itemRepository' import ItemRepository from '#repositories/itemRepository'
interface IPayload { interface IPayload {
@ -8,7 +10,7 @@ interface IPayload {
export default class ItemDeleteEvent extends BaseEvent { export default class ItemDeleteEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:item:remove', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_ITEM_REMOVE, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { Item } from '#entities/item' import { Item } from '#entities/item'
import ItemRepository from '#repositories/itemRepository' import ItemRepository from '#repositories/itemRepository'
@ -6,7 +7,7 @@ interface IPayload {}
export default class ItemListEvent extends BaseEvent { export default class ItemListEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:item:list', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_ITEM_LIST, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: Item[]) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: Item[]) => void): Promise<void> {

View File

@ -1,6 +1,8 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { ItemType, ItemRarity } from '#application/enums' import { ItemType, ItemRarity } from '#application/enums'
import { UUID } from '#application/types'
import ItemRepository from '#repositories/itemRepository' import ItemRepository from '#repositories/itemRepository'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
@ -16,7 +18,7 @@ type Payload = {
export default class ItemUpdateEvent extends BaseEvent { export default class ItemUpdateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:item:update', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_ITEM_UPDATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { MapObject } from '#entities/mapObject' import { MapObject } from '#entities/mapObject'
import MapObjectRepository from '#repositories/mapObjectRepository' import MapObjectRepository from '#repositories/mapObjectRepository'
@ -6,7 +7,7 @@ interface IPayload {}
export default class MapObjectListEvent extends BaseEvent { export default class MapObjectListEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:mapObject:list', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAPOBJECT_LIST, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: MapObject[]) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: MapObject[]) => void): Promise<void> {

View File

@ -1,8 +1,10 @@
import { SocketEvent } from '#application/enums';
import fs from 'fs' import fs from 'fs'
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage' import Storage from '#application/storage'
import { UUID } from '#application/types'
import MapObjectRepository from '#repositories/mapObjectRepository' import MapObjectRepository from '#repositories/mapObjectRepository'
interface IPayload { interface IPayload {
@ -11,7 +13,7 @@ interface IPayload {
export default class MapObjectRemoveEvent extends BaseEvent { export default class MapObjectRemoveEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:mapObject:remove', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAPOBJECT_REMOVE, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapObjectRepository from '#repositories/mapObjectRepository' import MapObjectRepository from '#repositories/mapObjectRepository'
type Payload = { type Payload = {
@ -15,7 +17,7 @@ type Payload = {
export default class MapObjectUpdateEvent extends BaseEvent { export default class MapObjectUpdateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:mapObject:update', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAPOBJECT_UPDATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
@ -27,19 +29,19 @@ export default class MapObjectUpdateEvent extends BaseEvent {
const mapObject = await mapObjectRepository.getById(data.id) const mapObject = await mapObjectRepository.getById(data.id)
if (!mapObject) return callback(false) if (!mapObject) return callback(false)
await mapObject if (data.name !== undefined) mapObject.name = data.name
.setName(data.name) if (data.tags !== undefined) mapObject.tags = data.tags
.setTags(data.tags) if (data.originX !== undefined) mapObject.originX = data.originX
.setOriginX(data.originX) if (data.originY !== undefined) mapObject.originY = data.originY
.setOriginY(data.originY) if (data.frameRate !== undefined) mapObject.frameRate = data.frameRate
.setFrameRate(data.frameRate) if (data.frameWidth !== undefined) mapObject.frameWidth = data.frameWidth
.setFrameWidth(data.frameWidth) if (data.frameHeight !== undefined) mapObject.frameHeight = data.frameHeight
.setFrameHeight(data.frameHeight)
.save() await mapObject.save()
return callback(true) return callback(true)
} catch (error) { } catch (error) {
console.error(error) this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Failed to update mapObject.' })
return callback(false) return callback(false)
} }
} }

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import fs from 'fs/promises' import fs from 'fs/promises'
import { writeFile } from 'node:fs/promises' import { writeFile } from 'node:fs/promises'
@ -13,7 +14,7 @@ interface IObjectData {
export default class MapObjectUploadEvent extends BaseEvent { export default class MapObjectUploadEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:mapObject:upload', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAPOBJECT_UPLOAD, this.handleEvent.bind(this))
} }
private async handleEvent(data: IObjectData, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: IObjectData, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import { Sprite } from '#entities/sprite' import { Sprite } from '#entities/sprite'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
@ -9,7 +11,7 @@ interface CopyPayload {
export default class SpriteCopyEvent extends BaseEvent { export default class SpriteCopyEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:sprite:copy', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_SPRITE_COPY, this.handleEvent.bind(this))
} }
private async handleEvent(payload: CopyPayload, callback: (success: boolean) => void): Promise<void> { private async handleEvent(payload: CopyPayload, callback: (success: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import fs from 'fs/promises' import fs from 'fs/promises'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
@ -6,7 +7,7 @@ import { Sprite } from '#entities/sprite'
export default class SpriteCreateEvent extends BaseEvent { export default class SpriteCreateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:sprite:create', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_SPRITE_CREATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,8 +1,10 @@
import { SocketEvent } from '#application/enums';
import fs from 'fs' import fs from 'fs'
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage' import Storage from '#application/storage'
import { UUID } from '#application/types'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
type Payload = { type Payload = {
@ -11,7 +13,7 @@ type Payload = {
export default class GMSpriteDeleteEvent extends BaseEvent { export default class GMSpriteDeleteEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:sprite:delete', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_SPRITE_DELETE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { Sprite } from '#entities/sprite' import { Sprite } from '#entities/sprite'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
@ -6,7 +7,7 @@ interface IPayload {}
export default class SpriteListEvent extends BaseEvent { export default class SpriteListEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:sprite:list', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_SPRITE_LIST, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: Sprite[]) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: Sprite[]) => void): Promise<void> {

View File

@ -1,10 +1,14 @@
import { BaseEvent } from '#application/base/baseEvent' import { SocketEvent } from '#application/enums';
import { UUID } from '#application/types'
import SpriteRepository from '#repositories/spriteRepository'
import { SpriteAction } from '#entities/spriteAction'
import sharp from 'sharp'
import fs from 'fs' import fs from 'fs'
import sharp from 'sharp'
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import { SpriteAction } from '#entities/spriteAction'
import SpriteRepository from '#repositories/spriteRepository'
interface SpriteImage { interface SpriteImage {
url: string url: string
offset: { offset: {
@ -41,7 +45,7 @@ type Payload = {
export default class SpriteUpdateEvent extends BaseEvent { export default class SpriteUpdateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:sprite:update', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_SPRITE_UPDATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
@ -59,7 +63,7 @@ export default class SpriteUpdateEvent extends BaseEvent {
// First verify all sprite sheets can be generated // First verify all sprite sheets can be generated
for (const actionData of data.spriteActions) { for (const actionData of data.spriteActions) {
if (!await this.generateSpriteSheet(actionData.sprites, sprite.getId(), actionData.action)) { if (!(await this.generateSpriteSheet(actionData.sprites, sprite.getId(), actionData.action))) {
return callback(false) return callback(false)
} }
} }
@ -74,13 +78,13 @@ export default class SpriteUpdateEvent extends BaseEvent {
// Create new actions // Create new actions
for (const actionData of data.spriteActions) { for (const actionData of data.spriteActions) {
// Process images and calculate dimensions // Process images and calculate dimensions
const imageData = await Promise.all(actionData.sprites.map(sprite => this.processImage(sprite))) const imageData = await Promise.all(actionData.sprites.map((sprite) => this.processImage(sprite)))
const effectiveDimensions = imageData.map(dimensions => this.calculateEffectiveDimensions(dimensions)) const effectiveDimensions = imageData.map((dimensions) => this.calculateEffectiveDimensions(dimensions))
// Calculate total height needed for the sprite sheet // Calculate total height needed for the sprite sheet
const maxHeight = Math.max(...effectiveDimensions.map(d => d.height)) const maxHeight = Math.max(...effectiveDimensions.map((d) => d.height))
const maxTop = Math.max(...effectiveDimensions.map(d => d.top)) const maxTop = Math.max(...effectiveDimensions.map((d) => d.top))
const maxBottom = Math.max(...effectiveDimensions.map(d => d.bottom)) const maxBottom = Math.max(...effectiveDimensions.map((d) => d.bottom))
const totalHeight = maxHeight + maxTop + maxBottom const totalHeight = maxHeight + maxTop + maxBottom
const spriteAction = new SpriteAction() const spriteAction = new SpriteAction()
@ -111,14 +115,14 @@ export default class SpriteUpdateEvent extends BaseEvent {
if (!sprites.length) return true if (!sprites.length) return true
// Process all images and get their dimensions // Process all images and get their dimensions
const imageData = await Promise.all(sprites.map(sprite => this.processImage(sprite))) const imageData = await Promise.all(sprites.map((sprite) => this.processImage(sprite)))
const effectiveDimensions = imageData.map(dimensions => this.calculateEffectiveDimensions(dimensions)) const effectiveDimensions = imageData.map((dimensions) => this.calculateEffectiveDimensions(dimensions))
// Calculate maximum dimensions // Calculate maximum dimensions
const maxWidth = Math.max(...effectiveDimensions.map(d => d.width)) const maxWidth = Math.max(...effectiveDimensions.map((d) => d.width))
const maxHeight = Math.max(...effectiveDimensions.map(d => d.height)) const maxHeight = Math.max(...effectiveDimensions.map((d) => d.height))
const maxTop = Math.max(...effectiveDimensions.map(d => d.top)) const maxTop = Math.max(...effectiveDimensions.map((d) => d.top))
const maxBottom = Math.max(...effectiveDimensions.map(d => d.bottom)) const maxBottom = Math.max(...effectiveDimensions.map((d) => d.bottom))
// Calculate total height needed // Calculate total height needed
const totalHeight = maxHeight + maxTop + maxBottom const totalHeight = maxHeight + maxTop + maxBottom
@ -142,9 +146,9 @@ export default class SpriteUpdateEvent extends BaseEvent {
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
} }
}) })
.composite([{ input: buffer, left, top: verticalOffset }]) .composite([{ input: buffer, left, top: verticalOffset }])
.png() .png()
.toBuffer() .toBuffer()
}) })
) )
@ -157,15 +161,15 @@ export default class SpriteUpdateEvent extends BaseEvent {
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
} }
}) })
.composite( .composite(
processedImages.map((buffer, index) => ({ processedImages.map((buffer, index) => ({
input: buffer, input: buffer,
left: index * maxWidth, left: index * maxWidth,
top: 0 top: 0
})) }))
) )
.png() .png()
.toBuffer() .toBuffer()
// Ensure directory exists // Ensure directory exists
const dir = `public/sprites/${spriteId}` const dir = `public/sprites/${spriteId}`
@ -207,10 +211,10 @@ export default class SpriteUpdateEvent extends BaseEvent {
if (!sprites.length) return 0 if (!sprites.length) return 0
// Process all images and get their dimensions // Process all images and get their dimensions
const imageData = await Promise.all(sprites.map(sprite => this.processImage(sprite))) const imageData = await Promise.all(sprites.map((sprite) => this.processImage(sprite)))
const effectiveDimensions = imageData.map(dimensions => this.calculateEffectiveDimensions(dimensions)) const effectiveDimensions = imageData.map((dimensions) => this.calculateEffectiveDimensions(dimensions))
// Calculate maximum width needed // Calculate maximum width needed
return Math.max(...effectiveDimensions.map(d => d.width)) return Math.max(...effectiveDimensions.map((d) => d.width))
} }
} }

View File

@ -1,8 +1,10 @@
import { SocketEvent } from '#application/enums';
import fs from 'fs/promises' import fs from 'fs/promises'
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage' import Storage from '#application/storage'
import { UUID } from '#application/types'
import TileRepository from '#repositories/tileRepository' import TileRepository from '#repositories/tileRepository'
type Payload = { type Payload = {
@ -11,7 +13,7 @@ type Payload = {
export default class GMTileDeleteEvent extends BaseEvent { export default class GMTileDeleteEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:tile:delete', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_TILE_DELETE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { Tile } from '#entities/tile' import { Tile } from '#entities/tile'
import TileRepository from '#repositories/tileRepository' import TileRepository from '#repositories/tileRepository'
@ -6,7 +7,7 @@ interface IPayload {}
export default class TileListEven extends BaseEvent { export default class TileListEven extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:tile:list', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_TILE_LIST, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: Tile[]) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: Tile[]) => void): Promise<void> {

View File

@ -1,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import TileRepository from '#repositories/tileRepository' import TileRepository from '#repositories/tileRepository'
type Payload = { type Payload = {
@ -10,7 +12,7 @@ type Payload = {
export default class TileUpdateEvent extends BaseEvent { export default class TileUpdateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:tile:update', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_TILE_UPDATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import fs from 'fs/promises' import fs from 'fs/promises'
import { writeFile } from 'node:fs/promises' import { writeFile } from 'node:fs/promises'
@ -11,7 +12,7 @@ interface ITileData {
export default class TileUploadEvent extends BaseEvent { export default class TileUploadEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:tile:upload', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_TILE_UPLOAD, this.handleEvent.bind(this))
} }
private async handleEvent(data: ITileData, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: ITileData, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,5 +1,8 @@
import { SocketEvent } from '#application/enums';
import type { MapCacheT } from '#entities/map'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { Map, MapCacheT } from '#entities/map' import { Map } from '#entities/map'
type Payload = { type Payload = {
name: string name: string
@ -9,7 +12,7 @@ type Payload = {
export default class MapCreateEvent extends BaseEvent { export default class MapCreateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:map:create', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAP_CREATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (response: MapCacheT | false) => void): Promise<void> { private async handleEvent(data: Payload, callback: (response: MapCacheT | false) => void): Promise<void> {
@ -18,6 +21,16 @@ export default class MapCreateEvent extends BaseEvent {
this.logger.info(`GM ${(await this.getCharacter())!.getId()} has created a new map via map editor.`) this.logger.info(`GM ${(await this.getCharacter())!.getId()} has created a new map via map editor.`)
if (data.name === '') {
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Map name cannot be empty.' })
return callback(false)
}
if (data.width < 1 || data.height < 1) {
this.socket.emit(SocketEvent.NOTIFICATION, { title: 'Error', message: 'Map width and height must be greater than 0.' })
return callback(false)
}
const map = new Map() const map = new Map()
await map await map
.setName(data.name) .setName(data.name)
@ -29,7 +42,7 @@ export default class MapCreateEvent extends BaseEvent {
return callback(await map.cache()) return callback(await map.cache())
} catch (error: any) { } catch (error: any) {
this.logger.error('gm:map:create error', error.message) this.logger.error('gm:map:create error', error.message)
this.socket.emit('notification', { message: 'Failed to create map.' }) this.socket.emit(SocketEvent.NOTIFICATION, { message: 'Failed to create map.' })
return callback(false) return callback(false)
} }
} }

View File

@ -1,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapRepository from '#repositories/mapRepository' import MapRepository from '#repositories/mapRepository'
type Payload = { type Payload = {
@ -8,7 +10,7 @@ type Payload = {
export default class MapDeleteEvent extends BaseEvent { export default class MapDeleteEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:map:delete', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAP_DELETE, this.handleEvent.bind(this))
} }
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> { private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {

View File

@ -1,5 +1,7 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import { Map } from '#entities/map' import { Map } from '#entities/map'
import MapRepository from '#repositories/mapRepository' import MapRepository from '#repositories/mapRepository'
@ -9,7 +11,7 @@ interface IPayload {
export default class MapRequestEvent extends BaseEvent { export default class MapRequestEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:map:request', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAP_REQUEST, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
@ -25,13 +27,14 @@ export default class MapRequestEvent extends BaseEvent {
const mapRepository = new MapRepository() const mapRepository = new MapRepository()
const map = await mapRepository.getById(data.mapId) const map = await mapRepository.getById(data.mapId)
await mapRepository.getEntityManager().populate(map!, mapRepository.POPULATE_MAP_EDITOR as any)
if (!map) { if (!map) {
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map ${data.mapId} but it does not exist.`) this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map ${data.mapId} but it does not exist.`)
return callback(null) return callback(null)
} }
await mapRepository.getEntityManager().populate(map, mapRepository.POPULATE_MAP_EDITOR as any)
return callback(map) return callback(map)
} catch (error: any) { } catch (error: any) {
this.logger.error('gm:map:request error', error.message) this.logger.error('gm:map:request error', error.message)

View File

@ -1,6 +1,8 @@
import { SocketEvent } from '#application/enums';
import type { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { MapEventTileType } from '#application/enums' import { MapEventTileType } from '#application/enums'
import { UUID } from '#application/types'
import { Map } from '#entities/map' import { Map } from '#entities/map'
import { MapEffect } from '#entities/mapEffect' import { MapEffect } from '#entities/mapEffect'
import { MapEventTile } from '#entities/mapEventTile' import { MapEventTile } from '#entities/mapEventTile'
@ -33,7 +35,7 @@ interface IPayload {
export default class MapUpdateEvent extends BaseEvent { export default class MapUpdateEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('gm:map:update', this.handleEvent.bind(this)) this.socket.on(SocketEvent.GM_MAP_UPDATE, this.handleEvent.bind(this))
} }
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> { private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
@ -62,9 +64,11 @@ export default class MapUpdateEvent extends BaseEvent {
if (data.tiles.length > data.height) { if (data.tiles.length > data.height) {
data.tiles = data.tiles.slice(0, data.height) data.tiles = data.tiles.slice(0, data.height)
} }
for (let i = 0; i < data.tiles.length; i++) { for (let i = 0; i < data.tiles.length; i++) {
if (data.tiles[i].length > data.width) { const row = data.tiles[i]
data.tiles[i] = data.tiles[i].slice(0, data.width) if (row !== undefined && row.length > data.width) {
data.tiles[i] = row.slice(0, data.width)
} }
} }

View File

@ -1,9 +1,10 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import UserRepository from '#repositories/userRepository' import UserRepository from '#repositories/userRepository'
export default class LoginEvent extends BaseEvent { export default class LoginEvent extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('login', this.handleEvent.bind(this)) this.socket.on(SocketEvent.LOGIN, this.handleEvent.bind(this))
} }
private async handleEvent() { private async handleEvent() {
@ -14,7 +15,7 @@ export default class LoginEvent extends BaseEvent {
} }
const userRepository = new UserRepository() const userRepository = new UserRepository()
this.socket.emit('logged_in', { user: userRepository.getById(this.socket.userId) }) this.socket.emit(SocketEvent.LOGGED_IN, { user: userRepository.getById(this.socket.userId) })
this.logger.info(`User logged in: ${this.socket.userId}`) this.logger.info(`User logged in: ${this.socket.userId}`)
} catch (error: any) { } catch (error: any) {
this.logger.error('login error: ' + error.message) this.logger.error('login error: ' + error.message)

View File

@ -0,0 +1,21 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent'
import CharacterAttackService from '#services/characterAttackService'
export default class CharacterMove extends BaseEvent {
private readonly characterAttackService = CharacterAttackService
public listen(): void {
this.socket.on(SocketEvent.MAP_CHARACTER_ATTACK, this.handleEvent.bind(this))
}
private async handleEvent(data: any, callback: (response: any) => void): Promise<void> {
try {
console.log('attack', this.socket.characterId)
await this.characterAttackService.attack(this.socket.characterId!)
} catch (error) {
this.logger.error('map:character:attack error', error)
return callback(false)
}
}
}

View File

@ -1,16 +1,19 @@
import { SocketEvent } from '#application/enums';
import type { MapEventTileWithTeleport } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import { MapEventTileWithTeleport } from '#application/types'
import MapManager from '#managers/mapManager' import MapManager from '#managers/mapManager'
import MapCharacter from '#models/mapCharacter' import MapCharacter from '#models/mapCharacter'
import MapEventTileRepository from '#repositories/mapEventTileRepository' import MapEventTileRepository from '#repositories/mapEventTileRepository'
import CharacterService from '#services/characterService' import CharacterService from '#services/characterMoveService'
import TeleportService from '#services/teleportService' import TeleportService from '#services/characterTeleportService'
export default class CharacterMove extends BaseEvent { export default class CharacterMove extends BaseEvent {
private readonly characterService = CharacterService private readonly characterService = CharacterService
private readonly MOVEMENT_THROTTLE = 230 // Minimum time between movement requests
public listen(): void { public listen(): void {
this.socket.on('map:character:move', this.handleEvent.bind(this)) this.socket.on(SocketEvent.MAP_CHARACTER_MOVE, this.handleEvent.bind(this))
} }
private async handleEvent({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> { private async handleEvent({ positionX, positionY }: { positionX: number; positionY: number }): Promise<void> {
@ -20,65 +23,100 @@ export default class CharacterMove extends BaseEvent {
return return
} }
// If already moving, cancel current movement and wait for it to fully stop if (this.isThrottled(`movement_${this.socket.characterId}`, this.MOVEMENT_THROTTLE)) {
if (mapCharacter.isMoving) { // Only cancel current movement if the new target is different
mapCharacter.isMoving = false if (mapCharacter.isMoving &&
await new Promise((resolve) => setTimeout(resolve, 100)) (Math.floor(positionX) !== Math.floor(mapCharacter.character.positionX) ||
Math.floor(positionY) !== Math.floor(mapCharacter.character.positionY))) {
mapCharacter.isMoving = false
mapCharacter.currentPath = null
// this.finalizeMovement(mapCharacter)
}
return
} }
// If already moving, cancel current movement
if (mapCharacter.isMoving && mapCharacter.currentPath && mapCharacter.currentPath.length > 2) {
mapCharacter.isMoving = false
mapCharacter.currentPath = null
}
// Validate target position is within reasonable range
const currentX = mapCharacter.character.positionX
const currentY = mapCharacter.character.positionY
const distance = Math.sqrt(Math.pow(positionX - currentX, 2) + Math.pow(positionY - currentY, 2))
const path = await this.characterService.calculatePath(mapCharacter.character, positionX, positionY) const path = await this.characterService.calculatePath(mapCharacter.character, positionX, positionY)
if (!path) { if (!path?.length) {
this.io.in(mapCharacter.character.map.id).emit('map:character:moveError', 'No valid path found') this.io.in(mapCharacter.character.map.id).emit(SocketEvent.MAP_CHARACTER_MOVEERROR, 'No valid path found')
return return
} }
// Start new movement // Start new movement
mapCharacter.isMoving = true mapCharacter.isMoving = true
mapCharacter.currentPath = path // Add this property to MapCharacter class mapCharacter.currentPath = path
await this.moveAlongPath(mapCharacter, path) await this.moveAlongPath(mapCharacter, path)
} }
private async moveAlongPath(mapCharacter: MapCharacter, path: Array<{ positionX: number; positionY: number }>): Promise<void> { private async moveAlongPath(mapCharacter: MapCharacter, path: Array<{ positionX: number; positionY: number }>): Promise<void> {
const character = mapCharacter.getCharacter() const character = mapCharacter.getCharacter()
for (let i = 0; i < path.length - 1; i++) { try {
if (!mapCharacter.isMoving || mapCharacter.currentPath !== path) { for (let i = 0; i < path.length - 1; i++) {
return if (!mapCharacter.isMoving || mapCharacter.currentPath !== path) {
return
}
const [start, end] = [path[i], path[i + 1]]
if (!start || !end) {
this.logger.error('Invalid path step detected')
break
}
if (i !== 0) {
await this.characterService.applyMovementDelay()
}
// Validate each step
if (Math.abs(end.positionX - start.positionX) > 1 || Math.abs(end.positionY - start.positionY) > 1) {
this.logger.error('Invalid path step detected')
break
}
character.setRotation(CharacterService.calculateRotation(start.positionX, start.positionY, end.positionX, end.positionY))
const mapEventTileRepository = new MapEventTileRepository()
const mapEventTile = await mapEventTileRepository.getEventTileByMapIdAndPosition(character.getMap().getId(), Math.floor(end.positionX), Math.floor(end.positionY))
if (mapEventTile?.type === 'BLOCK') break
if (mapEventTile?.type === 'TELEPORT' && mapEventTile.teleport) {
await this.handleTeleportMapEventTile(mapEventTile as MapEventTileWithTeleport)
return
}
// Update position first
character.setPositionX(end.positionX).setPositionY(end.positionY)
// Then emit with the same properties
this.io.in(character.map.id).emit(SocketEvent.MAP_CHARACTER_MOVE, {
characterId: character.id,
positionX: character.getPositionX(),
positionY: character.getPositionY(),
rotation: character.getRotation(),
isMoving: true
})
await this.characterService.applyMovementDelay()
} }
} finally {
const [start, end] = [path[i], path[i + 1]] if (mapCharacter.isMoving && mapCharacter.currentPath === path) {
character.setRotation(CharacterService.calculateRotation(start.positionX, start.positionY, end.positionX, end.positionY)) this.finalizeMovement(mapCharacter)
const mapEventTileRepository = new MapEventTileRepository()
const mapEventTile = await mapEventTileRepository.getEventTileByMapIdAndPosition(character.getMap().getId(), Math.floor(end.positionX), Math.floor(end.positionY))
if (mapEventTile?.type === 'BLOCK') break
if (mapEventTile?.type === 'TELEPORT' && mapEventTile.teleport) {
await this.handleMapEventTile(mapEventTile as MapEventTileWithTeleport)
return
} }
// Update position first
character.setPositionX(end.positionX).setPositionY(end.positionY)
// Then emit with the same properties
this.io.in(character.map.id).emit('map:character:move', {
characterId: character.id,
positionX: character.getPositionX(),
positionY: character.getPositionY(),
rotation: character.getRotation(),
isMoving: true
})
await this.characterService.applyMovementDelay()
}
if (mapCharacter.isMoving && mapCharacter.currentPath === path) {
this.finalizeMovement(mapCharacter)
} }
} }
private async handleMapEventTile(mapEventTile: MapEventTileWithTeleport): Promise<void> { private async handleTeleportMapEventTile(mapEventTile: MapEventTileWithTeleport): Promise<void> {
if (mapEventTile.getTeleport()) { if (mapEventTile.getTeleport()) {
await TeleportService.teleportCharacter(this.socket.characterId!, { await TeleportService.teleportCharacter(this.socket.characterId!, {
targetMapId: mapEventTile.getTeleport()!.getToMap().getId(), targetMapId: mapEventTile.getTeleport()!.getToMap().getId(),
@ -91,12 +129,12 @@ export default class CharacterMove extends BaseEvent {
private finalizeMovement(mapCharacter: MapCharacter): void { private finalizeMovement(mapCharacter: MapCharacter): void {
mapCharacter.isMoving = false mapCharacter.isMoving = false
this.io.in(mapCharacter.character.map.id).emit('map:character:move', { this.io.in(mapCharacter.character.map.id).emit(SocketEvent.MAP_CHARACTER_MOVE, {
characterId: mapCharacter.character.id, characterId: mapCharacter.character.id,
positionX: mapCharacter.character.positionX, positionX: mapCharacter.character.positionX,
positionY: mapCharacter.character.positionY, positionY: mapCharacter.character.positionY,
rotation: mapCharacter.character.rotation, rotation: mapCharacter.character.rotation,
isMoving: false isMoving: mapCharacter.isMoving
}) })
} }
} }

View File

@ -1,15 +1,16 @@
import { SocketEvent } from '#application/enums';
import { BaseEvent } from '#application/base/baseEvent' import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager' import WeatherManager from '#managers/weatherManager'
export default class Weather extends BaseEvent { export default class Weather extends BaseEvent {
public listen(): void { public listen(): void {
this.socket.on('weather', this.handleEvent.bind(this)) this.socket.on(SocketEvent.WEATHER, this.handleEvent.bind(this))
} }
private async handleEvent(): Promise<void> { private async handleEvent(): Promise<void> {
try { try {
const weather = WeatherManager.getWeatherState() const weather = WeatherManager.getWeatherState()
this.socket.emit('weather', weather) this.socket.emit(SocketEvent.WEATHER, weather)
} catch (error: any) { } catch (error: any) {
this.logger.error('weather error: ' + error.message) this.logger.error('weather error: ' + error.message)
} }

View File

@ -1,6 +1,7 @@
import { SocketEvent } from '#application/enums';
import { Server as SocketServer } from 'socket.io' import { Server as SocketServer } from 'socket.io'
import { TSocket } from '#application/types' import type { TSocket } from '#application/types'
export default class SomeJob { export default class SomeJob {
constructor(private params: any) {} constructor(private params: any) {}
@ -8,9 +9,9 @@ export default class SomeJob {
async execute(io: SocketServer, socket?: TSocket) { async execute(io: SocketServer, socket?: TSocket) {
// Handle the event // Handle the event
if (socket) { if (socket) {
socket.emit('notification', { message: 'Something happened with socket' }) socket.emit(SocketEvent.NOTIFICATION, { message: 'Something happened with socket' })
} }
// Use io for broadcasting if needed // Use io for broadcasting if needed
io.emit('notification', { message: 'Something happened' }) io.emit(SocketEvent.NOTIFICATION, { message: 'Something happened' })
} }
} }

View File

@ -28,6 +28,11 @@ export class ConsoleManager {
private async processCommand(commandLine: string): Promise<void> { private async processCommand(commandLine: string): Promise<void> {
const [cmd, ...args] = commandLine.trim().split(' ') const [cmd, ...args] = commandLine.trim().split(' ')
if (!cmd) {
console.log('No command provided')
return
}
if (cmd === 'exit') { if (cmd === 'exit') {
this.prompt.close() this.prompt.close()
return return

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { Server } from 'socket.io' import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
@ -8,16 +9,14 @@ import WorldRepository from '#repositories/worldRepository'
class DateManager { class DateManager {
private static readonly CONFIG = { private static readonly CONFIG = {
GAME_SPEED: 8, // 24 game hours / 3 real hours GAME_SPEED: 8, // 24 game hours / 3 real hours
UPDATE_INTERVAL: 1000 // 1 second UPDATE_INTERVAL: 1000 // 1 minute
} as const } as const
private io: Server | null = null
private intervalId: NodeJS.Timeout | null = null
private currentDate = new Date()
private readonly logger = Logger.type(LoggerType.APP) private readonly logger = Logger.type(LoggerType.APP)
private currentDate = new Date()
private intervalId: NodeJS.Timeout | null = null
public async boot(): Promise<void> { public async boot(): Promise<void> {
this.io = SocketManager.getIO()
await this.loadDate() await this.loadDate()
this.startDateLoop() this.startDateLoop()
this.logger.info('Date manager loaded') this.logger.info('Date manager loaded')
@ -61,6 +60,7 @@ class DateManager {
if (timeOnlyPattern.test(timeString)) { if (timeOnlyPattern.test(timeString)) {
const [hours, minutes] = timeString.split(':').map(Number) const [hours, minutes] = timeString.split(':').map(Number)
if (!hours || !minutes) return null
const newDate = new Date(this.currentDate) const newDate = new Date(this.currentDate)
newDate.setHours(hours, minutes) newDate.setHours(hours, minutes)
return newDate return newDate
@ -84,7 +84,8 @@ class DateManager {
} }
private emitDate(): void { private emitDate(): void {
this.io?.emit('date', this.currentDate) const io = SocketManager.getIO()
io?.emit(SocketEvent.DATE, this.currentDate)
} }
private async saveDate(): Promise<void> { private async saveDate(): Promise<void> {

View File

@ -1,7 +1,12 @@
import fs from 'fs'
import { createServer as httpServer, Server as HTTPServer } from 'http'
import { createServer as httpsServer, Server as HTTPSServer } from 'https'
import cors from 'cors' import cors from 'cors'
import { Application } from 'express' import express, { type Application } from 'express'
import config from '#application/config' import config from '#application/config'
import Logger, { LoggerType } from '#application/logger.js'
import { AuthController } from '#controllers/auth' import { AuthController } from '#controllers/auth'
import { AvatarController } from '#controllers/avatar' import { AvatarController } from '#controllers/avatar'
import { CacheController } from '#controllers/cache' import { CacheController } from '#controllers/cache'
@ -11,16 +16,40 @@ import { TexturesController } from '#controllers/textures'
* HTTP manager * HTTP manager
*/ */
class HttpManager { class HttpManager {
private readonly app: Application
private readonly server: HTTPServer | HTTPSServer
private readonly logger = Logger.type(LoggerType.APP)
private readonly authController: AuthController = new AuthController() private readonly authController: AuthController = new AuthController()
private readonly avatarController: AvatarController = new AvatarController() private readonly avatarController: AvatarController = new AvatarController()
private readonly texturesController: TexturesController = new TexturesController() private readonly texturesController: TexturesController = new TexturesController()
private readonly cacheController: CacheController = new CacheController() private readonly cacheController: CacheController = new CacheController()
constructor() {
this.app = express()
this.app.use(cors())
this.app.use(express.json())
this.app.use(express.urlencoded({ extended: true }))
if (config.PUBLIC_KEY_PATH && config.PRIVATE_KEY_PATH) {
const credentials = {
key: fs.readFileSync(config.PRIVATE_KEY_PATH),
cert: fs.readFileSync(config.PUBLIC_KEY_PATH)
}
this.server = httpsServer(credentials, this.app)
this.logger.info('HTTPS server initialized')
} else {
this.server = httpServer(this.app)
this.logger.info('HTTP server initialized')
}
}
/** /**
* Initialize HTTP manager * Initialize HTTP manager
* @param app * @param app
*/ */
public async boot(app: Application) { public async boot(app: Application) {
this.server.listen(config.PORT, config.HOST)
// Add CORS middleware // Add CORS middleware
app.use( app.use(
cors({ cors({
@ -33,6 +62,8 @@ class HttpManager {
// Add routes // Add routes
await this.addRoutes(app) await this.addRoutes(app)
this.logger.info(`HTTP running on port ${config.PORT}`)
} }
private async addRoutes(app: Application) { private async addRoutes(app: Application) {
@ -57,6 +88,14 @@ class HttpManager {
app.get('/cache/character_types', (req, res) => this.cacheController.characterTypes(req, res)) app.get('/cache/character_types', (req, res) => this.cacheController.characterTypes(req, res))
app.get('/cache/character_hair', (req, res) => this.cacheController.characterHair(req, res)) app.get('/cache/character_hair', (req, res) => this.cacheController.characterHair(req, res))
} }
getAppInstance(): Application {
return this.app
}
getServerInstance(): HTTPServer | HTTPSServer {
return this.server
}
} }
export default new HttpManager() export default new HttpManager()

View File

@ -1,5 +1,6 @@
import type { UUID } from '#application/types'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
import { UUID } from '#application/types'
import { Map } from '#entities/map' import { Map } from '#entities/map'
import LoadedMap from '#models/loadedMap' import LoadedMap from '#models/loadedMap'
import MapCharacter from '#models/mapCharacter' import MapCharacter from '#models/mapCharacter'

View File

@ -4,14 +4,15 @@ import { Job, Queue, Worker } from 'bullmq'
import IORedis from 'ioredis' import IORedis from 'ioredis'
import { Server as SocketServer } from 'socket.io' import { Server as SocketServer } from 'socket.io'
import type { TSocket } from '#application/types'
import config from '#application/config' import config from '#application/config'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
import Storage from '#application/storage' import Storage from '#application/storage'
import { TSocket } from '#application/types'
import SocketManager from '#managers/socketManager' import SocketManager from '#managers/socketManager'
class QueueManager { class QueueManager {
private connection!: IORedis private connection!: IORedis.Redis
private queue!: Queue private queue!: Queue
private worker!: Worker private worker!: Worker
private io!: SocketServer private io!: SocketServer
@ -20,7 +21,7 @@ class QueueManager {
public async boot() { public async boot() {
this.io = SocketManager.getIO() this.io = SocketManager.getIO()
this.connection = new IORedis(config.REDIS_URL, { this.connection = new IORedis.Redis(config.REDIS_URL, {
maxRetriesPerRequest: null maxRetriesPerRequest: null
}) })

View File

@ -2,12 +2,13 @@ import fs from 'fs'
import { Server as HTTPServer } from 'http' import { Server as HTTPServer } from 'http'
import { pathToFileURL } from 'url' import { pathToFileURL } from 'url'
import { Application } from 'express'
import { Server as SocketServer } from 'socket.io' import { Server as SocketServer } from 'socket.io'
import type { TSocket, UUID } from '#application/types'
import type { Application } from 'express'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
import Storage from '#application/storage' import Storage from '#application/storage'
import { TSocket, UUID } from '#application/types'
import { Authentication } from '#middleware/authentication' import { Authentication } from '#middleware/authentication'
class SocketManager { class SocketManager {

View File

@ -1,6 +1,5 @@
import { User } from '@prisma/client'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
import { User } from '#entities/user'
type TLoggedInUsers = { type TLoggedInUsers = {
users: User[] users: User[]

View File

@ -1,3 +1,4 @@
import { SocketEvent } from '#application/enums';
import { Server } from 'socket.io' import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
@ -20,7 +21,6 @@ class WeatherManager {
} as const } as const
private readonly logger = Logger.type(LoggerType.APP) private readonly logger = Logger.type(LoggerType.APP)
private io: Server | null = null
private intervalId: NodeJS.Timeout | null = null private intervalId: NodeJS.Timeout | null = null
private weatherState: WeatherState = { private weatherState: WeatherState = {
@ -29,7 +29,6 @@ class WeatherManager {
} }
public async boot(): Promise<void> { public async boot(): Promise<void> {
this.io = SocketManager.getIO()
await this.loadWeather() await this.loadWeather()
this.startWeatherLoop() this.startWeatherLoop()
this.logger.info('Weather manager loaded') this.logger.info('Weather manager loaded')
@ -113,7 +112,8 @@ class WeatherManager {
} }
private emitWeather(): void { private emitWeather(): void {
this.io?.emit('weather', this.weatherState) const io = SocketManager.getIO()
io?.emit(SocketEvent.WEATHER, this.weatherState)
} }
private async saveWeather(): Promise<void> { private async saveWeather(): Promise<void> {

View File

@ -1,8 +1,9 @@
import { verify } from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import type { TSocket } from '#application/types'
import config from '#application/config' import config from '#application/config'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
import { TSocket } from '#application/types'
class SocketAuthenticator { class SocketAuthenticator {
private socket: TSocket private socket: TSocket
@ -39,7 +40,7 @@ class SocketAuthenticator {
} }
private verifyToken(token: string): void { private verifyToken(token: string): void {
verify(token, config.JWT_SECRET, (err: any, decoded: any) => { jwt.verify(token, config.JWT_SECRET, (err: any, decoded: any) => {
if (err) { if (err) {
this.logger.error('Invalid token') this.logger.error('Invalid token')
return this.next(new Error('Authentication error')) return this.next(new Error('Authentication error'))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
import { Migration } from '@mikro-orm/migrations'
export class Migration20250207212301 extends Migration {
override async up(): Promise<void> {
this.addSql(
`create table \`map\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`width\` int not null default 10, \`height\` int not null default 10, \`tiles\` json null, \`pvp\` tinyint(1) not null default false, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(
`create table \`map_effect\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`effect\` varchar(255) not null, \`strength\` int not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`map_effect\` add index \`map_effect_map_id_index\`(\`map_id\`);`)
this.addSql(
`create table \`map_event_tile\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`type\` enum('BLOCK', 'TELEPORT', 'NPC', 'ITEM') not null, \`position_x\` int not null, \`position_y\` int not null, \`teleport_id\` varchar(255) null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`map_event_tile\` add index \`map_event_tile_map_id_index\`(\`map_id\`);`)
this.addSql(`alter table \`map_event_tile\` add unique \`map_event_tile_teleport_id_unique\`(\`teleport_id\`);`)
this.addSql(
`create table \`map_event_tile_teleport\` (\`id\` varchar(255) not null, \`map_event_tile_id\` varchar(255) not null, \`to_map_id\` varchar(255) not null, \`to_rotation\` int not null, \`to_position_x\` int not null, \`to_position_y\` int not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`map_event_tile_teleport\` add unique \`map_event_tile_teleport_map_event_tile_id_unique\`(\`map_event_tile_id\`);`)
this.addSql(`alter table \`map_event_tile_teleport\` add index \`map_event_tile_teleport_to_map_id_index\`(\`to_map_id\`);`)
this.addSql(
`create table \`map_object\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`tags\` json null, \`origin_x\` numeric(10,2) not null default 0, \`origin_y\` numeric(10,2) not null default 0, \`frame_rate\` int not null default 0, \`frame_width\` int not null default 0, \`frame_height\` int not null default 0, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(
`create table \`placed_map_object\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`map_object_id\` varchar(255) not null, \`is_rotated\` tinyint(1) not null default false, \`position_x\` int not null default 0, \`position_y\` int not null default 0, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`placed_map_object\` add index \`placed_map_object_map_id_index\`(\`map_id\`);`)
this.addSql(`alter table \`placed_map_object\` add index \`placed_map_object_map_object_id_index\`(\`map_object_id\`);`)
this.addSql(
`create table \`sprite\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(
`create table \`item\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`description\` varchar(255) not null default '', \`item_type\` enum('WEAPON', 'HELMET', 'CHEST', 'LEGS', 'BOOTS', 'GLOVES', 'RING', 'NECKLACE') not null, \`stackable\` tinyint(1) not null default false, \`rarity\` enum('COMMON', 'UNCOMMON', 'RARE', 'EPIC', 'LEGENDARY') not null default 'COMMON', \`sprite_id\` varchar(255) null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`item\` add index \`item_sprite_id_index\`(\`sprite_id\`);`)
this.addSql(
`create table \`character_type\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`gender\` enum('MALE', 'FEMALE') not null, \`race\` enum('HUMAN', 'ELF', 'DWARF', 'ORC', 'GOBLIN') not null, \`is_selectable\` tinyint(1) not null default false, \`sprite_id\` varchar(255) null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`character_type\` add index \`character_type_sprite_id_index\`(\`sprite_id\`);`)
this.addSql(
`create table \`character_hair\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`gender\` varchar(255) not null default 'MALE', \`is_selectable\` tinyint(1) not null default false, \`sprite_id\` varchar(255) null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`character_hair\` add index \`character_hair_sprite_id_index\`(\`sprite_id\`);`)
this.addSql(
`create table \`sprite_action\` (\`id\` varchar(255) not null, \`sprite_id\` varchar(255) not null, \`action\` varchar(255) not null, \`sprites\` json null, \`origin_x\` numeric(5,2) not null default 0, \`origin_y\` numeric(5,2) not null default 0, \`frame_width\` int not null default 0, \`frame_height\` int not null default 0, \`frame_rate\` int not null default 0, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`sprite_action\` add index \`sprite_action_sprite_id_index\`(\`sprite_id\`);`)
this.addSql(
`create table \`tile\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`tags\` json null, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(
`create table \`user\` (\`id\` varchar(255) not null, \`username\` varchar(255) not null, \`email\` varchar(255) not null, \`password\` varchar(255) not null, \`online\` tinyint(1) not null default false, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`user\` add unique \`user_username_unique\`(\`username\`);`)
this.addSql(`alter table \`user\` add unique \`user_email_unique\`(\`email\`);`)
this.addSql(
`create table \`password_reset_token\` (\`id\` varchar(255) not null, \`user_id\` varchar(255) not null, \`token\` varchar(255) not null, \`created_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`password_reset_token\` add index \`password_reset_token_user_id_index\`(\`user_id\`);`)
this.addSql(`alter table \`password_reset_token\` add unique \`password_reset_token_token_unique\`(\`token\`);`)
this.addSql(
`create table \`character\` (\`id\` varchar(255) not null, \`user_id\` varchar(255) not null, \`name\` varchar(255) not null, \`online\` tinyint(1) not null default false, \`role\` varchar(255) not null default 'player', \`map_id\` varchar(255) not null, \`position_x\` int not null default 0, \`position_y\` int not null default 0, \`rotation\` int not null default 0, \`character_type_id\` varchar(255) null, \`character_hair_id\` varchar(255) null, \`alignment\` int not null default 50, \`hitpoints\` int not null default 100, \`mana\` int not null default 100, \`level\` int not null default 1, \`experience\` int not null default 0, \`strength\` int not null default 10, \`dexterity\` int not null default 10, \`intelligence\` int not null default 10, \`wisdom\` int not null default 10, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`character\` add index \`character_user_id_index\`(\`user_id\`);`)
this.addSql(`alter table \`character\` add unique \`character_name_unique\`(\`name\`);`)
this.addSql(`alter table \`character\` add index \`character_map_id_index\`(\`map_id\`);`)
this.addSql(`alter table \`character\` add index \`character_character_type_id_index\`(\`character_type_id\`);`)
this.addSql(`alter table \`character\` add index \`character_character_hair_id_index\`(\`character_hair_id\`);`)
this.addSql(
`create table \`chat\` (\`id\` varchar(255) not null, \`character_id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`message\` varchar(255) not null, \`created_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`chat\` add index \`chat_character_id_index\`(\`character_id\`);`)
this.addSql(`alter table \`chat\` add index \`chat_map_id_index\`(\`map_id\`);`)
this.addSql(
`create table \`character_item\` (\`id\` varchar(255) not null, \`character_id\` varchar(255) not null, \`item_id\` varchar(255) not null, \`quantity\` int not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`character_item\` add index \`character_item_character_id_index\`(\`character_id\`);`)
this.addSql(`alter table \`character_item\` add index \`character_item_item_id_index\`(\`item_id\`);`)
this.addSql(
`create table \`character_equipment\` (\`id\` varchar(255) not null, \`slot\` enum('HEAD', 'BODY', 'ARMS', 'LEGS', 'NECK', 'RING') not null, \`character_id\` varchar(255) not null, \`character_item_id\` varchar(255) not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`character_equipment\` add index \`character_equipment_character_id_index\`(\`character_id\`);`)
this.addSql(`alter table \`character_equipment\` add index \`character_equipment_character_item_id_index\`(\`character_item_id\`);`)
this.addSql(
`create table \`world\` (\`date\` datetime not null, \`rain_percentage\` int not null default 0, \`fog_density\` int not null default 0, primary key (\`date\`)) default character set utf8mb4 engine = InnoDB;`
)
this.addSql(`alter table \`map_effect\` add constraint \`map_effect_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(`alter table \`map_event_tile\` add constraint \`map_event_tile_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(
`alter table \`map_event_tile\` add constraint \`map_event_tile_teleport_id_foreign\` foreign key (\`teleport_id\`) references \`map_event_tile_teleport\` (\`id\`) on update cascade on delete set null;`
)
this.addSql(
`alter table \`map_event_tile_teleport\` add constraint \`map_event_tile_teleport_map_event_tile_id_foreign\` foreign key (\`map_event_tile_id\`) references \`map_event_tile\` (\`id\`) on update cascade on delete cascade;`
)
this.addSql(
`alter table \`map_event_tile_teleport\` add constraint \`map_event_tile_teleport_to_map_id_foreign\` foreign key (\`to_map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`
)
this.addSql(`alter table \`placed_map_object\` add constraint \`placed_map_object_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(
`alter table \`placed_map_object\` add constraint \`placed_map_object_map_object_id_foreign\` foreign key (\`map_object_id\`) references \`map_object\` (\`id\`) on update cascade on delete cascade;`
)
this.addSql(`alter table \`item\` add constraint \`item_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete set null;`)
this.addSql(`alter table \`character_type\` add constraint \`character_type_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete set null;`)
this.addSql(`alter table \`character_hair\` add constraint \`character_hair_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete set null;`)
this.addSql(`alter table \`sprite_action\` add constraint \`sprite_action_sprite_id_foreign\` foreign key (\`sprite_id\`) references \`sprite\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(
`alter table \`password_reset_token\` add constraint \`password_reset_token_user_id_foreign\` foreign key (\`user_id\`) references \`user\` (\`id\`) on update cascade on delete cascade;`
)
this.addSql(`alter table \`character\` add constraint \`character_user_id_foreign\` foreign key (\`user_id\`) references \`user\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(`alter table \`character\` add constraint \`character_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade;`)
this.addSql(
`alter table \`character\` add constraint \`character_character_type_id_foreign\` foreign key (\`character_type_id\`) references \`character_type\` (\`id\`) on update cascade on delete set null;`
)
this.addSql(
`alter table \`character\` add constraint \`character_character_hair_id_foreign\` foreign key (\`character_hair_id\`) references \`character_hair\` (\`id\`) on update cascade on delete set null;`
)
this.addSql(`alter table \`chat\` add constraint \`chat_character_id_foreign\` foreign key (\`character_id\`) references \`character\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(`alter table \`chat\` add constraint \`chat_map_id_foreign\` foreign key (\`map_id\`) references \`map\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(
`alter table \`character_item\` add constraint \`character_item_character_id_foreign\` foreign key (\`character_id\`) references \`character\` (\`id\`) on update cascade on delete cascade;`
)
this.addSql(`alter table \`character_item\` add constraint \`character_item_item_id_foreign\` foreign key (\`item_id\`) references \`item\` (\`id\`) on update cascade on delete cascade;`)
this.addSql(
`alter table \`character_equipment\` add constraint \`character_equipment_character_id_foreign\` foreign key (\`character_id\`) references \`character\` (\`id\`) on update cascade on delete cascade;`
)
this.addSql(
`alter table \`character_equipment\` add constraint \`character_equipment_character_item_id_foreign\` foreign key (\`character_item_id\`) references \`character_item\` (\`id\`) on update cascade on delete cascade;`
)
}
}

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