Compare commits
224 Commits
feature/#1
...
issue/#307
Author | SHA1 | Date | |
---|---|---|---|
7b90fa5c13 | |||
514ed87818 | |||
24586855cb | |||
be9ee97385 | |||
a05cd97430 | |||
cc1dbe5179 | |||
d7982493e1 | |||
57b21f1499 | |||
33afef5466 | |||
813ddbd8b1 | |||
4a55f47c06 | |||
097773995f | |||
04e4170c2d | |||
46cfb61cf5 | |||
db7121a4fa | |||
1dd0e73c4a | |||
747d05a92a | |||
48784a437f | |||
6b12d8e7b1 | |||
50c2b249f4 | |||
82aaf8a015 | |||
0e0854d365 | |||
b2e0ac47e7 | |||
f2d0e87e26 | |||
9bdafd5026 | |||
ae269be196 | |||
21f4c5328f | |||
47ec425acf | |||
1f0db75806 | |||
9a448542d3 | |||
067976c54a | |||
0b4420f956 | |||
e843213b0a | |||
4d50edd5dd | |||
0cadbc33b9 | |||
0c155347c4 | |||
149485634d | |||
bc67db7db7 | |||
a40b71140a | |||
9a016a1fb6 | |||
ce80eb223c | |||
fecdf222d7 | |||
11041fec83 | |||
887da447e0 | |||
347554998a | |||
f7dbf09bf5 | |||
ab89d0cbb0 | |||
664a74c973 | |||
0c77758351 | |||
e8ef160f2a | |||
2d6831b4ef | |||
0464538b1c | |||
e61aeed691 | |||
45e756fcd3 | |||
7c473de12b | |||
5982422e04 | |||
04e081c31a | |||
599264b362 | |||
6f84238503 | |||
586bb0ca83 | |||
465219276d | |||
11d30351ba | |||
85af73c079 | |||
9c28b10383 | |||
495e9f192e | |||
30b2028bd8 | |||
9d6a8730a9 | |||
ba12674e7c | |||
a5c941cbb0 | |||
0f017cfe10 | |||
c9cc5be519 | |||
ce073a67af | |||
cb6fcbcb8e | |||
ba0ceda03a | |||
4745fb5145 | |||
30cce5845c | |||
2d43b3f1d2 | |||
045c693329 | |||
ff01e41c8f | |||
8781bf43a1 | |||
1223ae42ef | |||
6e58de8840 | |||
f5c4f3df19 | |||
e1ff2fefe1 | |||
0bc81ba4cc | |||
baf0350102 | |||
5c94584cb2 | |||
4f1b9cf024 | |||
0b99d4098e | |||
5b386ae455 | |||
3da21a7856 | |||
e1a6f650fb | |||
6dda79f8b2 | |||
918f5141fc | |||
bd3bf6f580 | |||
bd85908014 | |||
474683082d | |||
0fe060ff99 | |||
30dc69ab4b | |||
343c67a110 | |||
5d6cb478cd | |||
a95c67b5fe | |||
e571cf2230 | |||
b7f448cb17 | |||
4a963b4359 | |||
691abb7c4f | |||
6a27ccff31 | |||
9d72995225 | |||
2de2bec705 | |||
bf64a6df70 | |||
1b87f1dd91 | |||
f4746722af | |||
f5a7a348e0 | |||
d70e25207b | |||
4dd71a25b5 | |||
5c87b7b4af | |||
aa3ee8f0af | |||
95f4c58110 | |||
b4989aac26 | |||
c35e27799e | |||
125d3a3f66 | |||
d299528c26 | |||
bc58d41c54 | |||
058988e874 | |||
72562f92f9 | |||
38395b6f19 | |||
88cc8f5b08 | |||
413a5cbcf5 | |||
cfae96bde8 | |||
42e7b7312e | |||
f76b758565 | |||
434f5df7c2 | |||
5990b6d6ac | |||
8377fe6545 | |||
8980691409 | |||
40c24cee10 | |||
e5df80647f | |||
75595515b5 | |||
e19f30b15a | |||
af5f4f97f2 | |||
241cfb3eb2 | |||
ac4cefa902 | |||
3dffad928d | |||
ce90d6f7a9 | |||
b520acc2db | |||
75333d2659 | |||
dd9e039649 | |||
1facd2d641 | |||
5af2e399c9 | |||
6e3c97d7d1 | |||
bf58fc4944 | |||
68f7db7aa4 | |||
bbcd122a6c | |||
23c437f0bc | |||
eb2648d31f | |||
7e8fcc766a | |||
5c47edd230 | |||
2c10b54582 | |||
2ad58aea9f | |||
bd8caaf27c | |||
3cf86e322c | |||
6f32fbdc79 | |||
743d4594df | |||
2be49c010f | |||
2ac9416fe6 | |||
43fe6ab33e | |||
1cbf116ad4 | |||
3f10b03d24 | |||
a525d80530 | |||
4748044ab3 | |||
3b0138130b | |||
54c75896f9 | |||
9467797dc9 | |||
a8934f8e40 | |||
65cae5d824 | |||
179ccdbc55 | |||
ff39628f0c | |||
d4680b198e | |||
550b961505 | |||
1839bd9a22 | |||
1017013032 | |||
d5c7cd0294 | |||
4a62bbb118 | |||
40c7f6289a | |||
72d731c6f2 | |||
fc92d9ea79 | |||
2e267a36aa | |||
6ee8bb8334 | |||
ec3bf0f51e | |||
27f8bc8784 | |||
3185c478a6 | |||
72ef04d683 | |||
446e8fa617 | |||
f7072acdd2 | |||
fda8cc532e | |||
821e742527 | |||
460308d555 | |||
3f8f8745eb | |||
bf7f585270 | |||
fee4277b4f | |||
ffc07b7403 | |||
344ddbaf39 | |||
86ed3ae4b0 | |||
719c75616e | |||
cf954979c5 | |||
01ed1bce29 | |||
d4e0cbe398 | |||
628b3bf1fa | |||
709d34d59b | |||
c4a42066ab | |||
26dbaa45a7 | |||
ae0241fecb | |||
3a566dae5a | |||
ad4f33676f | |||
44cfbd6ee8 | |||
881e3375ab | |||
6a76c4797a | |||
bf75ad001b | |||
3b473e5826 | |||
929a36554a | |||
3fbc5f4e87 | |||
1526e0947a | |||
7ec4303b40 | |||
f475b69022 |
22
.env.example
22
.env.example
@ -1,9 +1,17 @@
|
|||||||
# Server configuration
|
# Server configuration
|
||||||
ENV=development
|
ENV=development
|
||||||
|
HOST="0.0.0.0"
|
||||||
PORT=4000
|
PORT=4000
|
||||||
DATABASE_URL="mysql://root@localhost:3306/nq"
|
|
||||||
REDIS_URL="redis://@127.0.0.1:6379/4"
|
|
||||||
JWT_SECRET="secret"
|
JWT_SECRET="secret"
|
||||||
|
CLIENT_URL="http://192.168.3.4:5173"
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
|
REDIS_URL="redis://@127.0.0.1:6379/4"
|
||||||
|
DB_HOST="localhost"
|
||||||
|
DB_USER="root"
|
||||||
|
DB_PASS=""
|
||||||
|
DB_PORT="3306"
|
||||||
|
DB_NAME="game"
|
||||||
|
|
||||||
# Game configuration
|
# Game configuration
|
||||||
ALLOW_DIAGONAL_MOVEMENT=false
|
ALLOW_DIAGONAL_MOVEMENT=false
|
||||||
@ -13,8 +21,8 @@ DEFAULT_CHARACTER_ZONE="0"
|
|||||||
DEFAULT_CHARACTER_POS_X="0"
|
DEFAULT_CHARACTER_POS_X="0"
|
||||||
DEFAULT_CHARACTER_POS_Y="0"
|
DEFAULT_CHARACTER_POS_Y="0"
|
||||||
|
|
||||||
# SMTP configuration
|
# Email configuration
|
||||||
SMTP_HOST="my.directonline.io"
|
SMTP_HOST=my.directonline.io
|
||||||
SMTP_PORT="587"
|
SMTP_PORT=587
|
||||||
SMTP_USER="no-reply@sylvan.quest"
|
SMTP_USER=no-reply@noxious.gg
|
||||||
SMTP_PASSWORD="Z%kI*1xe67WuGg"
|
SMTP_PASSWORD=""
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -309,4 +309,8 @@ $RECYCLE.BIN/
|
|||||||
# Windows shortcuts
|
# Windows shortcuts
|
||||||
*.lnk
|
*.lnk
|
||||||
|
|
||||||
|
# MikroORM
|
||||||
|
temp/**
|
||||||
|
migrations/*.json
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/node,jetbrains+all,visualstudiocode,macos,windows
|
# End of https://www.toptal.com/developers/gitignore/api/node,jetbrains+all,visualstudiocode,macos,windows
|
@ -3,6 +3,6 @@
|
|||||||
"semi": false,
|
"semi": false,
|
||||||
"tabWidth": 2,
|
"tabWidth": 2,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"printWidth": 300,
|
"printWidth": 200,
|
||||||
"trailingComma": "none"
|
"trailingComma": "none"
|
||||||
}
|
}
|
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Noxious game server
|
||||||
|
|
||||||
|
This is the server for the Noxious game.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone the repository
|
||||||
|
2. Install dependencies with `npm install`
|
||||||
|
3. Copy the `.env.example` file to `.env` and fill in the required variables
|
||||||
|
4. Run the server with `npm run dev`
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### `npm run dev`
|
||||||
|
|
||||||
|
Starts the server in development mode.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the server for production.
|
||||||
|
|
||||||
|
### `npm run format`
|
||||||
|
|
||||||
|
Formats the code using Prettier.
|
||||||
|
|
||||||
|
## MikroORM
|
||||||
|
|
||||||
|
MikroORM is used as the ORM for the server.
|
||||||
|
|
||||||
|
### Create init. migrations
|
||||||
|
|
||||||
|
Run `npx mikro-orm migration:create --initial` to create a new initial migration.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### Apply migrations
|
||||||
|
|
||||||
|
Run `npx mikro-orm migration:up` to apply all pending migrations.
|
||||||
|
|
||||||
|
### Import default data
|
||||||
|
|
||||||
|
After running the server, write `init` in the console to import default data.
|
42
eslint.config.js
Normal file
42
eslint.config.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import eslint from '@eslint/js';
|
||||||
|
import tseslint from '@typescript-eslint/eslint-plugin';
|
||||||
|
import tsparser from '@typescript-eslint/parser';
|
||||||
|
import importPlugin from 'eslint-plugin-import';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
eslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsparser,
|
||||||
|
parserOptions: {
|
||||||
|
project: './tsconfig.json',
|
||||||
|
ecmaVersion: 2023,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': tseslint,
|
||||||
|
'import': importPlugin,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...tseslint.configs['recommended'].rules,
|
||||||
|
...tseslint.configs['recommended-requiring-type-checking'].rules,
|
||||||
|
'import/order': ['error', {
|
||||||
|
'groups': [
|
||||||
|
'builtin',
|
||||||
|
'external',
|
||||||
|
'internal',
|
||||||
|
['parent', 'sibling'],
|
||||||
|
'index',
|
||||||
|
'object',
|
||||||
|
'type'
|
||||||
|
],
|
||||||
|
'newlines-between': 'always',
|
||||||
|
'alphabetize': {
|
||||||
|
'order': 'asc',
|
||||||
|
'caseInsensitive': true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
104
migrations/Migration20250106170204.ts
Normal file
104
migrations/Migration20250106170204.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { Migration } from '@mikro-orm/migrations';
|
||||||
|
|
||||||
|
export class Migration20250106170204 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, \`is_animated\` tinyint(1) not null default false, \`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, \`depth\` int not null default 0, \`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, 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\` int not null default 0, \`origin_y\` int not null default 0, \`is_animated\` tinyint(1) not null default false, \`is_looping\` tinyint(1) not null default false, \`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, \`is_rain_enabled\` tinyint(1) not null default false, \`rain_percentage\` int not null default 0, \`is_fog_enabled\` tinyint(1) not null default false, \`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;`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
mikro-orm.config.ts
Normal file
28
mikro-orm.config.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// import { defineConfig, MariaDbDriver } from '@mikro-orm/mariadb'
|
||||||
|
import { Migrator } from '@mikro-orm/migrations'
|
||||||
|
import { defineConfig, MySqlDriver } from '@mikro-orm/mysql'
|
||||||
|
import { TsMorphMetadataProvider } from '@mikro-orm/reflection'
|
||||||
|
|
||||||
|
import serverConfig from './src/application/config'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
extensions: [Migrator],
|
||||||
|
metadataProvider: TsMorphMetadataProvider,
|
||||||
|
entities: ['./src/entities/*.js'],
|
||||||
|
entitiesTs: ['./src/entities/*.ts'],
|
||||||
|
driver: MySqlDriver,
|
||||||
|
host: serverConfig.DB_HOST,
|
||||||
|
port: serverConfig.DB_PORT,
|
||||||
|
user: serverConfig.DB_USER,
|
||||||
|
password: serverConfig.DB_PASS,
|
||||||
|
dbName: serverConfig.DB_NAME,
|
||||||
|
debug: serverConfig.ENV !== 'production',
|
||||||
|
// allowGlobalContext: true,
|
||||||
|
driverOptions: {
|
||||||
|
allowPublicKeyRetrieval: true
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
path: './migrations',
|
||||||
|
pathTs: './migrations',
|
||||||
|
}
|
||||||
|
})
|
5279
package-lock.json
generated
5279
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@ -1,12 +1,19 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npx prisma migrate deploy && node dist/server.js",
|
"start": "node dist/server.js",
|
||||||
"dev": "nodemon --ignore 'data/*' --exec ts-node src/server.ts",
|
"dev": "nodemon --exec tsx src/server.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "eslint . --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.17.0",
|
"@mikro-orm/core": "^6.4.2",
|
||||||
|
"@mikro-orm/mariadb": "^6.4.2",
|
||||||
|
"@mikro-orm/migrations": "^6.4.2",
|
||||||
|
"@mikro-orm/mysql": "^6.4.2",
|
||||||
|
"@mikro-orm/reflection": "^6.4.2",
|
||||||
|
"@prisma/client": "^6.1.0",
|
||||||
"@types/ioredis": "^4.28.10",
|
"@types/ioredis": "^4.28.10",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"bullmq": "^5.13.2",
|
"bullmq": "^5.13.2",
|
||||||
@ -17,7 +24,6 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"nodemailer": "^6.9.15",
|
"nodemailer": "^6.9.15",
|
||||||
"pino": "^9.3.2",
|
"pino": "^9.3.2",
|
||||||
"prisma": "^5.17.0",
|
|
||||||
"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",
|
||||||
@ -25,12 +31,20 @@
|
|||||||
"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",
|
||||||
"@types/node": "^20.14.11",
|
"@types/node": "^20.14.11",
|
||||||
"@types/nodemailer": "^6.4.16",
|
"@types/nodemailer": "^6.4.16",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
||||||
|
"@typescript-eslint/parser": "^8.18.2",
|
||||||
|
"eslint": "^9.17.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"nodemon": "^3.1.4",
|
"nodemon": "^3.1.4",
|
||||||
"prettier": "^3.3.3"
|
"prettier": "^3.3.3",
|
||||||
|
"prisma": "^6.1.0",
|
||||||
|
"tsx": "^4.19.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0
prisma/.gitignore
vendored
0
prisma/.gitignore
vendored
@ -1,255 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE `Chat` (
|
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
||||||
`characterId` INTEGER NOT NULL,
|
|
||||||
`zoneId` INTEGER NOT NULL,
|
|
||||||
`message` VARCHAR(191) NOT NULL,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `Sprite` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`name` VARCHAR(191) NOT NULL,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
||||||
`updatedAt` DATETIME(3) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `SpriteAction` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`spriteId` VARCHAR(191) NOT NULL,
|
|
||||||
`action` VARCHAR(191) NOT NULL,
|
|
||||||
`sprites` JSON NULL,
|
|
||||||
`originX` DECIMAL(65, 30) NOT NULL DEFAULT 0,
|
|
||||||
`originY` DECIMAL(65, 30) NOT NULL DEFAULT 0,
|
|
||||||
`isAnimated` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
`isLooping` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
`frameWidth` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`frameHeight` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`frameSpeed` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `User` (
|
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
||||||
`username` VARCHAR(191) NOT NULL,
|
|
||||||
`email` VARCHAR(191) NOT NULL,
|
|
||||||
`password` VARCHAR(191) NOT NULL,
|
|
||||||
`online` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
|
|
||||||
UNIQUE INDEX `User_username_key`(`username`),
|
|
||||||
UNIQUE INDEX `User_email_key`(`email`),
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `PasswordResetToken` (
|
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
||||||
`userId` INTEGER NOT NULL,
|
|
||||||
`token` VARCHAR(191) NOT NULL,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
||||||
|
|
||||||
UNIQUE INDEX `PasswordResetToken_token_key`(`token`),
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `CharacterType` (
|
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
||||||
`name` VARCHAR(191) NOT NULL,
|
|
||||||
`gender` ENUM('MALE', 'FEMALE') NOT NULL,
|
|
||||||
`race` ENUM('HUMAN', 'ELF', 'DWARF', 'ORC', 'GOBLIN') NOT NULL,
|
|
||||||
`spriteId` VARCHAR(191) NULL,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
||||||
`updatedAt` DATETIME(3) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `Character` (
|
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
||||||
`userId` INTEGER NOT NULL,
|
|
||||||
`name` VARCHAR(191) NOT NULL,
|
|
||||||
`online` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
`hitpoints` INTEGER NOT NULL DEFAULT 100,
|
|
||||||
`mana` INTEGER NOT NULL DEFAULT 100,
|
|
||||||
`level` INTEGER NOT NULL DEFAULT 1,
|
|
||||||
`experience` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`alignment` INTEGER NOT NULL DEFAULT 50,
|
|
||||||
`role` VARCHAR(191) NOT NULL DEFAULT 'player',
|
|
||||||
`positionX` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`positionY` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`rotation` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`zoneId` INTEGER NOT NULL DEFAULT 1,
|
|
||||||
`characterTypeId` INTEGER NULL,
|
|
||||||
|
|
||||||
UNIQUE INDEX `Character_name_key`(`name`),
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `CharacterItem` (
|
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
||||||
`characterId` INTEGER NOT NULL,
|
|
||||||
`itemId` VARCHAR(191) NOT NULL,
|
|
||||||
`quantity` INTEGER NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `Tile` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`name` VARCHAR(191) NOT NULL,
|
|
||||||
`tags` JSON NULL,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
||||||
`updatedAt` DATETIME(3) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `Object` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`name` VARCHAR(191) NOT NULL,
|
|
||||||
`tags` JSON NULL,
|
|
||||||
`originX` DECIMAL(65, 30) NOT NULL DEFAULT 0,
|
|
||||||
`originY` DECIMAL(65, 30) NOT NULL DEFAULT 0,
|
|
||||||
`isAnimated` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
`frameSpeed` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`frameWidth` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`frameHeight` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
||||||
`updatedAt` DATETIME(3) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `Item` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`name` VARCHAR(191) NOT NULL,
|
|
||||||
`description` VARCHAR(191) NULL,
|
|
||||||
`stackable` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
||||||
`updatedAt` DATETIME(3) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `Zone` (
|
|
||||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
||||||
`name` VARCHAR(191) NOT NULL,
|
|
||||||
`width` INTEGER NOT NULL DEFAULT 10,
|
|
||||||
`height` INTEGER NOT NULL DEFAULT 10,
|
|
||||||
`tiles` JSON NULL,
|
|
||||||
`pvp` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
|
||||||
`updatedAt` DATETIME(3) NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `ZoneEffect` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`zoneId` INTEGER NOT NULL,
|
|
||||||
`effect` VARCHAR(191) NOT NULL,
|
|
||||||
`strength` INTEGER NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `ZoneObject` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`zoneId` INTEGER NOT NULL,
|
|
||||||
`objectId` VARCHAR(191) NOT NULL,
|
|
||||||
`depth` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`isRotated` BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
`positionX` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
`positionY` INTEGER NOT NULL DEFAULT 0,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `ZoneEventTile` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`zoneId` INTEGER NOT NULL,
|
|
||||||
`type` ENUM('BLOCK', 'TELEPORT', 'NPC', 'ITEM') NOT NULL,
|
|
||||||
`positionX` INTEGER NOT NULL,
|
|
||||||
`positionY` INTEGER NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE `ZoneEventTileTeleport` (
|
|
||||||
`id` VARCHAR(191) NOT NULL,
|
|
||||||
`zoneEventTileId` VARCHAR(191) NOT NULL,
|
|
||||||
`toZoneId` INTEGER NOT NULL,
|
|
||||||
`toRotation` INTEGER NOT NULL,
|
|
||||||
`toPositionX` INTEGER NOT NULL,
|
|
||||||
`toPositionY` INTEGER NOT NULL,
|
|
||||||
|
|
||||||
UNIQUE INDEX `ZoneEventTileTeleport_zoneEventTileId_key`(`zoneEventTileId`),
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `Chat` ADD CONSTRAINT `Chat_characterId_fkey` FOREIGN KEY (`characterId`) REFERENCES `Character`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `Chat` ADD CONSTRAINT `Chat_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `SpriteAction` ADD CONSTRAINT `SpriteAction_spriteId_fkey` FOREIGN KEY (`spriteId`) REFERENCES `Sprite`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `PasswordResetToken` ADD CONSTRAINT `PasswordResetToken_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `CharacterType` ADD CONSTRAINT `CharacterType_spriteId_fkey` FOREIGN KEY (`spriteId`) REFERENCES `Sprite`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `Character` ADD CONSTRAINT `Character_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `Character` ADD CONSTRAINT `Character_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `Character` ADD CONSTRAINT `Character_characterTypeId_fkey` FOREIGN KEY (`characterTypeId`) REFERENCES `CharacterType`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `CharacterItem` ADD CONSTRAINT `CharacterItem_characterId_fkey` FOREIGN KEY (`characterId`) REFERENCES `Character`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `CharacterItem` ADD CONSTRAINT `CharacterItem_itemId_fkey` FOREIGN KEY (`itemId`) REFERENCES `Item`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `ZoneEffect` ADD CONSTRAINT `ZoneEffect_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `ZoneObject` ADD CONSTRAINT `ZoneObject_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `ZoneObject` ADD CONSTRAINT `ZoneObject_objectId_fkey` FOREIGN KEY (`objectId`) REFERENCES `Object`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `ZoneEventTile` ADD CONSTRAINT `ZoneEventTile_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `ZoneEventTileTeleport` ADD CONSTRAINT `ZoneEventTileTeleport_zoneEventTileId_fkey` FOREIGN KEY (`zoneEventTileId`) REFERENCES `ZoneEventTile`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE `ZoneEventTileTeleport` ADD CONSTRAINT `ZoneEventTileTeleport_toZoneId_fkey` FOREIGN KEY (`toZoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -1,3 +0,0 @@
|
|||||||
# Please do not edit this file manually
|
|
||||||
# It should be added in your version-control system (i.e. Git)
|
|
||||||
provider = "mysql"
|
|
@ -1,31 +0,0 @@
|
|||||||
// CHEAT SHEET
|
|
||||||
// 1. Create a new Prisma project
|
|
||||||
// npx prisma init
|
|
||||||
// 2. Create a new database schema
|
|
||||||
// npx prisma db push
|
|
||||||
// 3. Generate Prisma Client and type-safe models based on schema
|
|
||||||
// npx prisma generate
|
|
||||||
// 4. Create a new migration
|
|
||||||
// npx prisma migrate dev --name [migration-name]
|
|
||||||
// 5. Apply the migration
|
|
||||||
// npx prisma migrate deploy
|
|
||||||
|
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
previewFeatures = ["prismaSchemaFolder"]
|
|
||||||
}
|
|
||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "mysql"
|
|
||||||
url = env("DATABASE_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
model Chat {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
characterId Int
|
|
||||||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
|
||||||
zoneId Int
|
|
||||||
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
|
||||||
message String
|
|
||||||
createdAt DateTime
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
model Sprite {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
name String
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
spriteActions SpriteAction[]
|
|
||||||
characterTypes CharacterType[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model SpriteAction {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
spriteId String
|
|
||||||
sprite Sprite @relation(fields: [spriteId], references: [id], onDelete: Cascade)
|
|
||||||
action String
|
|
||||||
sprites Json?
|
|
||||||
originX Decimal @default(0)
|
|
||||||
originY Decimal @default(0)
|
|
||||||
isAnimated Boolean @default(false)
|
|
||||||
isLooping Boolean @default(false)
|
|
||||||
frameWidth Int @default(0)
|
|
||||||
frameHeight Int @default(0)
|
|
||||||
frameSpeed Int @default(0)
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
model User {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
username String @unique
|
|
||||||
email String @unique
|
|
||||||
password String
|
|
||||||
online Boolean @default(false)
|
|
||||||
characters Character[]
|
|
||||||
passwordResetTokens PasswordResetToken[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model PasswordResetToken {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
userId Int
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
token String @unique
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CharacterGender {
|
|
||||||
MALE
|
|
||||||
FEMALE
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CharacterRace {
|
|
||||||
HUMAN
|
|
||||||
ELF
|
|
||||||
DWARF
|
|
||||||
ORC
|
|
||||||
GOBLIN
|
|
||||||
}
|
|
||||||
|
|
||||||
model CharacterType {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String
|
|
||||||
gender CharacterGender
|
|
||||||
race CharacterRace
|
|
||||||
characters Character[]
|
|
||||||
spriteId String?
|
|
||||||
sprite Sprite? @relation(fields: [spriteId], references: [id], onDelete: Cascade)
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
model Character {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
userId Int
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
||||||
name String @unique
|
|
||||||
online Boolean @default(false)
|
|
||||||
hitpoints Int @default(100)
|
|
||||||
mana Int @default(100)
|
|
||||||
level Int @default(1)
|
|
||||||
experience Int @default(0)
|
|
||||||
alignment Int @default(50)
|
|
||||||
role String @default("player")
|
|
||||||
positionX Int @default(0)
|
|
||||||
positionY Int @default(0)
|
|
||||||
rotation Int @default(0)
|
|
||||||
zoneId Int @default(1)
|
|
||||||
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
|
||||||
characterTypeId Int?
|
|
||||||
characterType CharacterType? @relation(fields: [characterTypeId], references: [id], onDelete: Cascade)
|
|
||||||
chats Chat[]
|
|
||||||
items CharacterItem[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model CharacterItem {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
characterId Int
|
|
||||||
character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
|
|
||||||
itemId String
|
|
||||||
item Item @relation(fields: [itemId], references: [id], onDelete: Cascade)
|
|
||||||
quantity Int
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
model Tile {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
name String
|
|
||||||
tags Json?
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
model Object {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
name String
|
|
||||||
tags Json?
|
|
||||||
originX Decimal @default(0)
|
|
||||||
originY Decimal @default(0)
|
|
||||||
isAnimated Boolean @default(false)
|
|
||||||
frameSpeed Int @default(0)
|
|
||||||
frameWidth Int @default(0)
|
|
||||||
frameHeight Int @default(0)
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
ZoneObject ZoneObject[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model Item {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
name String
|
|
||||||
description String?
|
|
||||||
stackable Boolean @default(false)
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
characters CharacterItem[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model Zone {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
name String
|
|
||||||
width Int @default(10)
|
|
||||||
height Int @default(10)
|
|
||||||
tiles Json?
|
|
||||||
pvp Boolean @default(false)
|
|
||||||
zoneEffects ZoneEffect[]
|
|
||||||
zoneEventTiles ZoneEventTile[]
|
|
||||||
zoneEventTileTeleports ZoneEventTileTeleport[]
|
|
||||||
zoneObjects ZoneObject[]
|
|
||||||
characters Character[]
|
|
||||||
chats Chat[]
|
|
||||||
createdAt DateTime @default(now())
|
|
||||||
updatedAt DateTime @updatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
model ZoneEffect {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
zoneId Int
|
|
||||||
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
|
||||||
effect String
|
|
||||||
strength Int
|
|
||||||
}
|
|
||||||
|
|
||||||
model ZoneObject {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
zoneId Int
|
|
||||||
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
|
||||||
objectId String
|
|
||||||
object Object @relation(fields: [objectId], references: [id], onDelete: Cascade)
|
|
||||||
depth Int @default(0)
|
|
||||||
isRotated Boolean @default(false)
|
|
||||||
positionX Int @default(0)
|
|
||||||
positionY Int @default(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ZoneEventTileType {
|
|
||||||
BLOCK
|
|
||||||
TELEPORT
|
|
||||||
NPC
|
|
||||||
ITEM
|
|
||||||
}
|
|
||||||
|
|
||||||
model ZoneEventTile {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
zoneId Int
|
|
||||||
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
|
||||||
type ZoneEventTileType
|
|
||||||
positionX Int
|
|
||||||
positionY Int
|
|
||||||
teleport ZoneEventTileTeleport?
|
|
||||||
}
|
|
||||||
|
|
||||||
model ZoneEventTileTeleport {
|
|
||||||
id String @id @default(uuid())
|
|
||||||
zoneEventTileId String @unique
|
|
||||||
zoneEventTile ZoneEventTile @relation(fields: [zoneEventTileId], references: [id], onDelete: Cascade)
|
|
||||||
toZoneId Int
|
|
||||||
toZone Zone @relation(fields: [toZoneId], references: [id], onDelete: Cascade)
|
|
||||||
toRotation Int
|
|
||||||
toPositionX Int
|
|
||||||
toPositionY Int
|
|
||||||
}
|
|
3
public/.gitignore
vendored
3
public/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
|
**
|
||||||
!.gitignore
|
!.gitignore
|
||||||
**
|
!assets.zip
|
||||||
|
BIN
public/assets.zip
Normal file
BIN
public/assets.zip
Normal file
Binary file not shown.
8
src/application/base/baseCommand.ts
Normal file
8
src/application/base/baseCommand.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
|
||||||
|
export abstract class BaseCommand {
|
||||||
|
protected readonly logger = Logger.type(LoggerType.COMMAND)
|
||||||
|
constructor(readonly io: Server) {}
|
||||||
|
}
|
22
src/application/base/baseController.ts
Normal file
22
src/application/base/baseController.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
|
||||||
|
export abstract class BaseController {
|
||||||
|
protected readonly logger = Logger.type(LoggerType.HTTP)
|
||||||
|
|
||||||
|
protected sendSuccess(res: Response, data?: any, message?: string, status: number = 200) {
|
||||||
|
return res.status(status).json({
|
||||||
|
success: true,
|
||||||
|
message,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sendError(res: Response, message: string, status: number = 400) {
|
||||||
|
return res.status(status).json({
|
||||||
|
success: false,
|
||||||
|
message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
53
src/application/base/baseEntity.ts
Normal file
53
src/application/base/baseEntity.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { EntityManager } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import Database from '#application/database'
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
|
||||||
|
export abstract class BaseEntity {
|
||||||
|
protected readonly logger = Logger.type(LoggerType.ENTITY)
|
||||||
|
protected entityManager?: EntityManager
|
||||||
|
|
||||||
|
private getEntityManager(): EntityManager {
|
||||||
|
if (!this.entityManager) {
|
||||||
|
this.entityManager = Database.getORM().em.fork()
|
||||||
|
}
|
||||||
|
return this.entityManager
|
||||||
|
}
|
||||||
|
|
||||||
|
public setEntityManager(entityManager: EntityManager) {
|
||||||
|
this.entityManager = entityManager
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(): Promise<this> {
|
||||||
|
return this.execute('persist', 'save entity')
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(): Promise<this> {
|
||||||
|
return this.execute('remove', 'remove entity')
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(): Promise<this> {
|
||||||
|
return this.execute('merge', 'update entity')
|
||||||
|
}
|
||||||
|
|
||||||
|
private async execute(method: 'persist' | 'merge' | 'remove', actionDescription: string): Promise<this> {
|
||||||
|
try {
|
||||||
|
const em = this.getEntityManager()
|
||||||
|
|
||||||
|
await em.begin()
|
||||||
|
try {
|
||||||
|
em[method](this)
|
||||||
|
await em.flush()
|
||||||
|
await em.commit()
|
||||||
|
return this
|
||||||
|
} catch (error) {
|
||||||
|
await em.rollback()
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to ${actionDescription}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
src/application/base/baseEvent.ts
Normal file
36
src/application/base/baseEvent.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
import { TSocket } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
|
||||||
|
export abstract class BaseEvent {
|
||||||
|
protected readonly logger = Logger.type(LoggerType.GAME)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly io: Server,
|
||||||
|
readonly socket: TSocket
|
||||||
|
) {}
|
||||||
|
|
||||||
|
protected async getCharacter(): Promise<Character | null> {
|
||||||
|
const characterRepository = new CharacterRepository()
|
||||||
|
return characterRepository.getById(this.socket.characterId!)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async isCharacterGM(): Promise<boolean> {
|
||||||
|
const character = await this.getCharacter()
|
||||||
|
return character?.getRole() === 'gm'
|
||||||
|
}
|
||||||
|
|
||||||
|
protected emitError(message: string): void {
|
||||||
|
this.socket.emit('notification', { title: 'Server message', message })
|
||||||
|
this.logger.error('character:connect error', `Player ${this.socket.userId}: ${message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleError(context: string, error: unknown): void {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
|
this.emitError(`${context}: ${errorMessage}`)
|
||||||
|
this.logger.error('character:connect error', errorMessage)
|
||||||
|
}
|
||||||
|
}
|
17
src/application/base/baseRepository.ts
Normal file
17
src/application/base/baseRepository.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { EntityManager } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import Database from '../database'
|
||||||
|
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
|
||||||
|
export abstract class BaseRepository {
|
||||||
|
protected readonly logger = Logger.type(LoggerType.REPOSITORY)
|
||||||
|
private entityManager?: EntityManager
|
||||||
|
|
||||||
|
getEntityManager(): EntityManager {
|
||||||
|
if (!this.entityManager) {
|
||||||
|
this.entityManager = Database.getORM().em.fork()
|
||||||
|
}
|
||||||
|
return this.entityManager
|
||||||
|
}
|
||||||
|
}
|
5
src/application/base/baseService.ts
Normal file
5
src/application/base/baseService.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
|
||||||
|
export abstract class BaseService {
|
||||||
|
protected readonly logger = Logger.type(LoggerType.GAME)
|
||||||
|
}
|
@ -3,22 +3,33 @@ import dotenv from 'dotenv'
|
|||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
class config {
|
class config {
|
||||||
|
// Server configuration
|
||||||
static ENV: string = process.env.ENV || 'development'
|
static ENV: string = process.env.ENV || 'development'
|
||||||
static REDIS_URL: string = process.env.REDIS_URL || 'redis://@127.0.0.1:6379/4'
|
|
||||||
static HOST: string = process.env.HOST || '0.0.0.0'
|
static HOST: string = process.env.HOST || '0.0.0.0'
|
||||||
static PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 6969
|
static PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 6969
|
||||||
static CLIENT_URL: string = process.env.CLIENT_URL ? process.env.CLIENT_URL : 'https://sylvan.quest/'
|
|
||||||
static JWT_SECRET: string = process.env.JWT_SECRET || 'secret'
|
static JWT_SECRET: string = process.env.JWT_SECRET || 'secret'
|
||||||
|
static CLIENT_URL: string = process.env.CLIENT_URL ? process.env.CLIENT_URL : 'https://noxious.gg'
|
||||||
|
|
||||||
|
// Database configuration
|
||||||
|
static REDIS_URL: string = process.env.REDIS_URL || 'redis://@127.0.0.1:6379/4'
|
||||||
|
static DB_HOST: string = process.env.DB_HOST || 'localhost'
|
||||||
|
static DB_USER: string = process.env.DB_USER || 'root'
|
||||||
|
static DB_PASS: string = process.env.DB_PASS || ''
|
||||||
|
static DB_PORT: number = process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306
|
||||||
|
static DB_NAME: string = process.env.DB_NAME || 'game'
|
||||||
|
|
||||||
|
// Game configuration
|
||||||
static ALLOW_DIAGONAL_MOVEMENT: boolean = process.env.ALLOW_DIAGONAL_MOVEMENT === 'true'
|
static ALLOW_DIAGONAL_MOVEMENT: boolean = process.env.ALLOW_DIAGONAL_MOVEMENT === 'true'
|
||||||
|
|
||||||
|
// Default character create values
|
||||||
static DEFAULT_CHARACTER_ZONE: number = parseInt(process.env.DEFAULT_CHARACTER_ZONE || '1')
|
static DEFAULT_CHARACTER_ZONE: number = parseInt(process.env.DEFAULT_CHARACTER_ZONE || '1')
|
||||||
static DEFAULT_CHARACTER_X: number = parseInt(process.env.DEFAULT_CHARACTER_POS_X || '0')
|
static DEFAULT_CHARACTER_X: number = parseInt(process.env.DEFAULT_CHARACTER_POS_X || '0')
|
||||||
static DEFAULT_CHARACTER_Y: number = parseInt(process.env.DEFAULT_CHARACTER_POS_Y || '0')
|
static DEFAULT_CHARACTER_Y: number = parseInt(process.env.DEFAULT_CHARACTER_POS_Y || '0')
|
||||||
|
|
||||||
|
// Email configuration
|
||||||
static SMTP_HOST: string = process.env.SMTP_HOST || 'my.directonline.io'
|
static SMTP_HOST: string = process.env.SMTP_HOST || 'my.directonline.io'
|
||||||
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@sylvan.quest'
|
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'
|
||||||
}
|
}
|
||||||
|
|
57
src/application/console/commandRegistry.ts
Normal file
57
src/application/console/commandRegistry.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
import { pathToFileURL } from 'url'
|
||||||
|
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
import Storage from '#application/storage'
|
||||||
|
import { Command } from '#application/types'
|
||||||
|
|
||||||
|
export class CommandRegistry {
|
||||||
|
private readonly commands: Map<string, Command> = new Map()
|
||||||
|
private readonly logger = Logger.type(LoggerType.COMMAND)
|
||||||
|
|
||||||
|
public getCommand(name: string): Command | undefined {
|
||||||
|
return this.commands.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async loadCommands(): Promise<void> {
|
||||||
|
const directory = Storage.getAppPath('commands')
|
||||||
|
this.logger.info(`Loading commands from: ${directory}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const files = await fs.promises.readdir(directory, { withFileTypes: true })
|
||||||
|
await Promise.all(files.filter((file) => this.isValidCommandFile(file)).map((file) => this.loadCommandFile(file)))
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Failed to read commands directory: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isValidCommandFile(file: fs.Dirent): boolean {
|
||||||
|
return file.isFile() && (file.name.endsWith('.ts') || file.name.endsWith('.js'))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadCommandFile(file: fs.Dirent): Promise<void> {
|
||||||
|
try {
|
||||||
|
const filePath = Storage.getAppPath('commands', file.name)
|
||||||
|
const commandName = path.basename(file.name, path.extname(file.name))
|
||||||
|
|
||||||
|
const module = await import(pathToFileURL(filePath).href)
|
||||||
|
if (typeof module.default !== 'function') {
|
||||||
|
this.logger.warn(`Unrecognized export in ${file.name}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.registerCommand(commandName, module.default)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error loading command ${file.name}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerCommand(name: string, CommandClass: Command): void {
|
||||||
|
if (this.commands.has(name)) {
|
||||||
|
this.logger.warn(`Command '${name}' is already registered. Overwriting...`)
|
||||||
|
}
|
||||||
|
this.commands.set(name, CommandClass)
|
||||||
|
this.logger.info(`Registered command: ${name}`)
|
||||||
|
}
|
||||||
|
}
|
33
src/application/console/consolePrompt.ts
Normal file
33
src/application/console/consolePrompt.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import * as readline from 'readline'
|
||||||
|
|
||||||
|
export class ConsolePrompt {
|
||||||
|
private readonly rl: readline.Interface
|
||||||
|
private isClosed: boolean = false
|
||||||
|
|
||||||
|
constructor(private readonly commandHandler: (command: string) => void) {
|
||||||
|
this.rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
})
|
||||||
|
|
||||||
|
this.rl.on('close', () => {
|
||||||
|
this.isClosed = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public start(): void {
|
||||||
|
if (this.isClosed) return
|
||||||
|
this.promptCommand()
|
||||||
|
}
|
||||||
|
|
||||||
|
public close(): void {
|
||||||
|
this.rl.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private promptCommand(): void {
|
||||||
|
this.rl.question('> ', (command: string) => {
|
||||||
|
this.commandHandler(command)
|
||||||
|
this.promptCommand()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
77
src/application/console/logReader.ts
Normal file
77
src/application/console/logReader.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
|
||||||
|
export class LogReader {
|
||||||
|
private logger = Logger.type(LoggerType.CONSOLE)
|
||||||
|
private watchers: fs.FSWatcher[] = []
|
||||||
|
private readonly logsDirectory: string
|
||||||
|
|
||||||
|
constructor(rootPath: string) {
|
||||||
|
this.logsDirectory = path.join(rootPath, 'logs')
|
||||||
|
}
|
||||||
|
|
||||||
|
public start(): void {
|
||||||
|
this.logger.info('Starting log reader...')
|
||||||
|
this.watchLogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
public stop(): void {
|
||||||
|
this.watchers.forEach((watcher) => watcher.close())
|
||||||
|
this.watchers = []
|
||||||
|
}
|
||||||
|
|
||||||
|
private watchLogs(): void {
|
||||||
|
// Watch directory for new files
|
||||||
|
const directoryWatcher = fs.watch(this.logsDirectory, (_, filename) => {
|
||||||
|
if (filename?.endsWith('.log')) {
|
||||||
|
this.watchLogFile(filename)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.watchers.push(directoryWatcher)
|
||||||
|
|
||||||
|
// Watch existing files
|
||||||
|
try {
|
||||||
|
fs.readdirSync(this.logsDirectory)
|
||||||
|
.filter((file) => file.endsWith('.log'))
|
||||||
|
.forEach((file) => this.watchLogFile(file))
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error reading logs directory: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private watchLogFile(filename: string): void {
|
||||||
|
const filePath = path.join(this.logsDirectory, filename)
|
||||||
|
let currentPosition = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0
|
||||||
|
|
||||||
|
const watcher = fs.watch(filePath, () => {
|
||||||
|
try {
|
||||||
|
const stat = fs.statSync(filePath)
|
||||||
|
const newPosition = stat.size
|
||||||
|
|
||||||
|
if (newPosition < currentPosition) {
|
||||||
|
currentPosition = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPosition > currentPosition) {
|
||||||
|
const stream = fs.createReadStream(filePath, {
|
||||||
|
start: currentPosition,
|
||||||
|
end: newPosition
|
||||||
|
})
|
||||||
|
|
||||||
|
stream.on('data', (data) => {
|
||||||
|
console.log(`[${filename}]`);
|
||||||
|
console.log(data.toString()); //
|
||||||
|
})
|
||||||
|
|
||||||
|
currentPosition = newPosition
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
watcher.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.watchers.push(watcher)
|
||||||
|
}
|
||||||
|
}
|
25
src/application/database.ts
Normal file
25
src/application/database.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { MikroORM } from '@mikro-orm/mysql'
|
||||||
|
|
||||||
|
import Logger, { LoggerType } from './logger'
|
||||||
|
import config from '../../mikro-orm.config'
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
private static orm: MikroORM
|
||||||
|
private static logger = Logger.type(LoggerType.APP)
|
||||||
|
|
||||||
|
public static async initialize(): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.orm = await MikroORM.init(config)
|
||||||
|
this.logger.info('Database connection initialized')
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`MikroORM connection failed: ${error}`)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getORM(): MikroORM {
|
||||||
|
return this.orm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Database
|
63
src/application/enums.ts
Normal file
63
src/application/enums.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
export enum SocketEvent {
|
||||||
|
CHARACTER_CONNECT = 1,
|
||||||
|
CHARACTER_MOVE = 2,
|
||||||
|
CHARACTER_MOVE_ERROR = 3,
|
||||||
|
CHARACTER_TELEPORT = 4,
|
||||||
|
ZONE_CHARACTER_LEAVE = 5,
|
||||||
|
ZONE_CHARACTER_JOIN = 6,
|
||||||
|
ZONE_CHARACTER_LIST = 7,
|
||||||
|
ZONE_CHARACTER_DELETE = 8,
|
||||||
|
ZONE_CHARACTER_CREATE = 9,
|
||||||
|
ZONE_CHARACTER_UPDATE = 10,
|
||||||
|
ZONE_CHARACTER_HAIR_UPDATE = 11,
|
||||||
|
ZONE_CHARACTER_HAIR_LIST = 12,
|
||||||
|
ZONE_CHARACTER_TELEPORT = 13
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ItemType {
|
||||||
|
WEAPON = 'WEAPON',
|
||||||
|
HELMET = 'HELMET',
|
||||||
|
CHEST = 'CHEST',
|
||||||
|
LEGS = 'LEGS',
|
||||||
|
BOOTS = 'BOOTS',
|
||||||
|
GLOVES = 'GLOVES',
|
||||||
|
RING = 'RING',
|
||||||
|
NECKLACE = 'NECKLACE'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ItemRarity {
|
||||||
|
COMMON = 'COMMON',
|
||||||
|
UNCOMMON = 'UNCOMMON',
|
||||||
|
RARE = 'RARE',
|
||||||
|
EPIC = 'EPIC',
|
||||||
|
LEGENDARY = 'LEGENDARY'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CharacterGender {
|
||||||
|
MALE = 'MALE',
|
||||||
|
FEMALE = 'FEMALE'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CharacterRace {
|
||||||
|
HUMAN = 'HUMAN',
|
||||||
|
ELF = 'ELF',
|
||||||
|
DWARF = 'DWARF',
|
||||||
|
ORC = 'ORC',
|
||||||
|
GOBLIN = 'GOBLIN'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CharacterEquipmentSlotType {
|
||||||
|
HEAD = 'HEAD',
|
||||||
|
BODY = 'BODY',
|
||||||
|
ARMS = 'ARMS',
|
||||||
|
LEGS = 'LEGS',
|
||||||
|
NECK = 'NECK',
|
||||||
|
RING = 'RING'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MapEventTileType {
|
||||||
|
BLOCK = 'BLOCK',
|
||||||
|
TELEPORT = 'TELEPORT',
|
||||||
|
NPC = 'NPC',
|
||||||
|
ITEM = 'ITEM'
|
||||||
|
}
|
52
src/application/logger.ts
Normal file
52
src/application/logger.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import pino from 'pino'
|
||||||
|
|
||||||
|
export enum LoggerType {
|
||||||
|
HTTP = 'http',
|
||||||
|
GAME = 'game',
|
||||||
|
GAME_MASTER = 'gameMaster',
|
||||||
|
APP = 'app',
|
||||||
|
QUEUE = 'queue',
|
||||||
|
COMMAND = 'command',
|
||||||
|
REPOSITORY = 'repository',
|
||||||
|
ENTITY = 'entity',
|
||||||
|
CONSOLE = 'console'
|
||||||
|
}
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
private instances: Map<LoggerType, ReturnType<typeof pino>> = new Map()
|
||||||
|
|
||||||
|
private getLogger(type: LoggerType): ReturnType<typeof pino> {
|
||||||
|
if (!this.instances.has(type)) {
|
||||||
|
this.instances.set(
|
||||||
|
type,
|
||||||
|
pino({
|
||||||
|
level: process.env.LOG_LEVEL || 'debug',
|
||||||
|
transport: {
|
||||||
|
target: 'pino/file',
|
||||||
|
options: {
|
||||||
|
destination: `./logs/${type}.log`,
|
||||||
|
mkdir: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatters: {
|
||||||
|
level: (label) => ({ level: label.toUpperCase() })
|
||||||
|
},
|
||||||
|
timestamp: pino.stdTimeFunctions.isoTime,
|
||||||
|
base: null
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this.instances.get(type)!
|
||||||
|
}
|
||||||
|
|
||||||
|
type(type: LoggerType) {
|
||||||
|
return {
|
||||||
|
info: (message: string, ...args: any[]) => this.getLogger(type).info(message, ...args),
|
||||||
|
error: (message: string, ...args: any[]) => this.getLogger(type).error(message, ...args),
|
||||||
|
warn: (message: string, ...args: any[]) => this.getLogger(type).warn(message, ...args),
|
||||||
|
debug: (message: string, ...args: any[]) => this.getLogger(type).debug(message, ...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Logger()
|
71
src/application/storage.ts
Normal file
71
src/application/storage.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
import config from '#application/config'
|
||||||
|
|
||||||
|
class Storage {
|
||||||
|
private readonly baseDir: string
|
||||||
|
private readonly rootDir: string
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.rootDir = process.cwd()
|
||||||
|
this.baseDir = config.ENV === 'development' ? 'src' : 'dist'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets path relative to project root
|
||||||
|
*/
|
||||||
|
public getRootPath(folder: string, ...additionalSegments: string[]): string {
|
||||||
|
return path.join(this.rootDir, folder, ...additionalSegments)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets path relative to app directory (src/dist)
|
||||||
|
*/
|
||||||
|
public getAppPath(folder: string, ...additionalSegments: string[]): string {
|
||||||
|
return path.join(this.rootDir, this.baseDir, folder, ...additionalSegments)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets path relative to public directory
|
||||||
|
*/
|
||||||
|
public getPublicPath(folder: string, ...additionalSegments: string[]): string {
|
||||||
|
return path.join(this.rootDir, 'public', folder, ...additionalSegments)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a path exists
|
||||||
|
* @throws Error if path is empty or invalid
|
||||||
|
*/
|
||||||
|
public doesPathExist(pathToCheck: string): boolean {
|
||||||
|
if (!pathToCheck) {
|
||||||
|
throw new Error('Path cannot be empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.accessSync(pathToCheck, fs.constants.F_OK)
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a directory and any necessary parent directories
|
||||||
|
* @throws Error if directory creation fails
|
||||||
|
*/
|
||||||
|
public createDir(dirPath: string): void {
|
||||||
|
if (!dirPath) {
|
||||||
|
throw new Error('Directory path cannot be empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(dirPath, { recursive: true })
|
||||||
|
} catch (error) {
|
||||||
|
const typedError = error as Error
|
||||||
|
throw new Error(`Failed to create directory: ${typedError.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Storage()
|
@ -1,9 +1,14 @@
|
|||||||
import { Socket } from 'socket.io'
|
import { Server, Socket } from 'socket.io'
|
||||||
import { Character, User } from '@prisma/client'
|
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { MapEventTile } from '#entities/mapEventTile'
|
||||||
|
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
|
||||||
|
|
||||||
|
export type UUID = `${string}-${string}-${string}-${string}-${string}`
|
||||||
|
|
||||||
export type TSocket = Socket & {
|
export type TSocket = Socket & {
|
||||||
user?: User
|
userId?: UUID
|
||||||
characterId?: number
|
characterId?: UUID
|
||||||
handshake?: {
|
handshake?: {
|
||||||
query?: {
|
query?: {
|
||||||
token?: any
|
token?: any
|
||||||
@ -18,7 +23,11 @@ export type TSocket = Socket & {
|
|||||||
|
|
||||||
export type ExtendedCharacter = Character & {
|
export type ExtendedCharacter = Character & {
|
||||||
isMoving?: boolean
|
isMoving?: boolean
|
||||||
resetMovement: boolean
|
resetMovement?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MapEventTileWithTeleport = MapEventTile & {
|
||||||
|
teleport: MapEventTileTeleport
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AssetData = {
|
export type AssetData = {
|
||||||
@ -26,10 +35,13 @@ export type AssetData = {
|
|||||||
data: string
|
data: string
|
||||||
group: 'tiles' | 'objects' | 'sprites' | 'sprite_animations' | 'sound' | 'music' | 'ui' | 'font' | 'other'
|
group: 'tiles' | 'objects' | 'sprites' | 'sprite_animations' | 'sound' | 'music' | 'ui' | 'font' | 'other'
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
originX?: number
|
||||||
|
originY?: number
|
||||||
isAnimated?: boolean
|
isAnimated?: boolean
|
||||||
frameCount?: number
|
frameRate?: number
|
||||||
frameWidth?: number
|
frameWidth?: number
|
||||||
frameHeight?: number
|
frameHeight?: number
|
||||||
|
frameCount?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WorldSettings = {
|
export type WorldSettings = {
|
||||||
@ -39,6 +51,12 @@ export type WorldSettings = {
|
|||||||
fogDensity: number
|
fogDensity: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Command {
|
||||||
|
new (io: Server): {
|
||||||
|
execute(args: string[]): Promise<void>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// export type TCharacter = Socket & {
|
// export type TCharacter = Socket & {
|
||||||
// user?: User
|
// user?: User
|
||||||
// character?: Character
|
// character?: Character
|
@ -35,13 +35,14 @@ export const registerAccountSchema = z.object({
|
|||||||
|
|
||||||
export const resetPasswordSchema = z.object({
|
export const resetPasswordSchema = z.object({
|
||||||
email: z
|
email: z
|
||||||
.string()
|
.string()
|
||||||
.min(3, { message: 'Email must be at least 3 characters long' })
|
.min(3, { message: 'Email must be at least 3 characters long' })
|
||||||
.max(255, { message: 'Email must be at most 255 characters long' })
|
.max(255, { message: 'Email must be at most 255 characters long' })
|
||||||
.regex(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/, { message: 'Email must be valid' })
|
.regex(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/, { message: 'Email must be valid' })
|
||||||
})
|
})
|
||||||
|
|
||||||
export const newPasswordSchema = z.object({
|
export const newPasswordSchema = z.object({
|
||||||
|
urlToken: z.string().min(10, { message: 'Invalid request' }).max(255, { message: 'Invalid request' }),
|
||||||
password: z
|
password: z
|
||||||
.string()
|
.string()
|
||||||
.min(8, {
|
.min(8, {
|
@ -1,10 +1,10 @@
|
|||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
|
import { BaseCommand } from '#application/base/baseCommand'
|
||||||
|
|
||||||
type CommandInput = string[]
|
type CommandInput = string[]
|
||||||
|
|
||||||
export default class AlertCommand {
|
export default class AlertCommand extends BaseCommand {
|
||||||
constructor(private readonly io: Server) {}
|
|
||||||
|
|
||||||
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')
|
||||||
|
266
src/commands/init.ts
Normal file
266
src/commands/init.ts
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
import sharp from 'sharp'
|
||||||
|
|
||||||
|
import { BaseCommand } from '#application/base/baseCommand'
|
||||||
|
import { CharacterGender, CharacterRace } from '#application/enums'
|
||||||
|
import Storage from '#application/storage'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
import { Map } from '#entities/map'
|
||||||
|
import { MapEffect } from '#entities/mapEffect'
|
||||||
|
import { MapObject } from '#entities/mapObject'
|
||||||
|
import { Sprite } from '#entities/sprite'
|
||||||
|
import { SpriteAction } from '#entities/spriteAction'
|
||||||
|
import { Tile } from '#entities/tile'
|
||||||
|
import { User } from '#entities/user'
|
||||||
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
|
import MapRepository from '#repositories/mapRepository'
|
||||||
|
|
||||||
|
// @TODO : Replace this with seeding
|
||||||
|
// https://mikro-orm.io/docs/seeding
|
||||||
|
|
||||||
|
export default class InitCommand extends BaseCommand {
|
||||||
|
private readonly mapRepository = new MapRepository()
|
||||||
|
private readonly characterTypeRepository = new CharacterTypeRepository()
|
||||||
|
private readonly characterHairRepository = new CharacterHairRepository()
|
||||||
|
|
||||||
|
public async execute(): Promise<void> {
|
||||||
|
// Assets
|
||||||
|
await this.importTiles()
|
||||||
|
await this.importMapObjects()
|
||||||
|
await this.createCharacterType()
|
||||||
|
await this.createCharacterHair()
|
||||||
|
// await this.createCharacterEquipment()
|
||||||
|
|
||||||
|
// Map
|
||||||
|
await this.createMap()
|
||||||
|
|
||||||
|
// User
|
||||||
|
await this.createUser()
|
||||||
|
|
||||||
|
// Stop process
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async importTiles(): Promise<void> {
|
||||||
|
for (const tile of fs.readdirSync(Storage.getPublicPath('tiles'))) {
|
||||||
|
const newTile = new Tile()
|
||||||
|
newTile.setId(tile.split('.')[0] as UUID).setName('New tile')
|
||||||
|
|
||||||
|
await newTile.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async importMapObjects(): Promise<void> {
|
||||||
|
for (const mapObject of fs.readdirSync(Storage.getPublicPath('map_objects'))) {
|
||||||
|
const newMapObject = new MapObject()
|
||||||
|
newMapObject
|
||||||
|
.setId(mapObject.split('.')[0] as UUID)
|
||||||
|
.setName('New map object')
|
||||||
|
.setFrameWidth(
|
||||||
|
(await sharp(Storage.getPublicPath('map_objects', mapObject))
|
||||||
|
.metadata()
|
||||||
|
.then((metadata) => metadata.height)) ?? 0
|
||||||
|
)
|
||||||
|
.setFrameHeight(
|
||||||
|
(await sharp(Storage.getPublicPath('map_objects', mapObject))
|
||||||
|
.metadata()
|
||||||
|
.then((metadata) => metadata.width)) ?? 0
|
||||||
|
)
|
||||||
|
|
||||||
|
await newMapObject.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createCharacterType(): Promise<void> {
|
||||||
|
const characterSprite = new Sprite()
|
||||||
|
characterSprite.setId('023d1e9d-f57f-4faa-8412-86c07107cf85').setName('Character')
|
||||||
|
await characterSprite.save()
|
||||||
|
|
||||||
|
const idleRightDownAction = new SpriteAction()
|
||||||
|
await idleRightDownAction
|
||||||
|
.setAction('idle_right_down')
|
||||||
|
.setSprites([
|
||||||
|
'data:image/png;base64,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'
|
||||||
|
])
|
||||||
|
.setOriginX(0)
|
||||||
|
.setOriginY(0)
|
||||||
|
.setIsAnimated(false)
|
||||||
|
.setIsLooping(false)
|
||||||
|
.setFrameWidth(64)
|
||||||
|
.setFrameHeight(94)
|
||||||
|
.setFrameRate(0)
|
||||||
|
.setSprite(characterSprite)
|
||||||
|
.save()
|
||||||
|
|
||||||
|
const idleLeftUpAction = new SpriteAction()
|
||||||
|
await idleLeftUpAction
|
||||||
|
.setAction('idle_left_up')
|
||||||
|
.setSprites([
|
||||||
|
'data:image/png;base64,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'
|
||||||
|
])
|
||||||
|
.setOriginX(0)
|
||||||
|
.setOriginY(0)
|
||||||
|
.setIsAnimated(false)
|
||||||
|
.setIsLooping(false)
|
||||||
|
.setFrameWidth(64)
|
||||||
|
.setFrameHeight(94)
|
||||||
|
.setFrameRate(0)
|
||||||
|
.setSprite(characterSprite)
|
||||||
|
.save()
|
||||||
|
|
||||||
|
const walkRightDownAction = new SpriteAction()
|
||||||
|
await walkRightDownAction
|
||||||
|
.setAction('walk_right_down')
|
||||||
|
.setSprites([
|
||||||
|
'data:image/png;base64,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',
|
||||||
|
'data:image/png;base64,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',
|
||||||
|
'data:image/png;base64,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',
|
||||||
|
'data:image/png;base64,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'
|
||||||
|
])
|
||||||
|
.setOriginX(0)
|
||||||
|
.setOriginY(0)
|
||||||
|
.setIsAnimated(true)
|
||||||
|
.setIsLooping(false)
|
||||||
|
.setFrameWidth(64)
|
||||||
|
.setFrameHeight(94)
|
||||||
|
.setFrameRate(7)
|
||||||
|
.setSprite(characterSprite)
|
||||||
|
.save()
|
||||||
|
|
||||||
|
const walkLeftUpAction = new SpriteAction()
|
||||||
|
await walkLeftUpAction
|
||||||
|
.setAction('walk_left_up')
|
||||||
|
.setSprites([
|
||||||
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAABdCAYAAABZy21jAAAGdElEQVR4nK2aMWgbSRSGf8lBMXKMQRJSAoY1RIsaC6RKGIMbEZUurwqHq+O40q6OAxeCK2248jrBBQ6uFKks3BhCcGWB0uikgBbMJbvnLGdkC0cQ64r1G8/uzuzOrvQqaWY037yZ92bevFGi/vPvUJVMuzkTldu7hwnlTgA8iQLbb9SglUuuuovTM7Qe6lXhodBMuznbb9Sg1xt49vK5q+7m42dU6zvI5AuwLROtdnOmAg6EZtrN2W8H32NZ30Q6t4KlrO6qfwbgBoBWBjQ4M6ACTgYB9xs1pPKaEMg6WFtFKq85v8kXgljhUADQ6w0k11aVOgIArVzCXmVDanCBUNIySL59GWBydet0EmFgQMCaauUS62xydYs0Bq56AsYRJZcJg9xfjwEARq+v1FfgmlJnqmJbJrZ+/Ck+VHXUNLCL0zMlYCDU22lQ3fmffzDg3eBDKFS6prZlKoEBt3+qzFCgplPLCO1gahlsP74bfHANViZCTe3dw0Sr3Zxl8gVoZXddKq/5BmP0+tCwIOu1LTO0I6o3en0lLQOh9u5hotUd+cD87kPltmUqAwOhPJgHiIC8tLqj0HM11GXs3cPE8cl5WDO0uiMloBI0iqhGDpGhUbdGkSRkgRl/JtIx542PSC5OzwA4UwyEayyEZtrN2V5lg+00Wrnk80+KFqiMd5mwtfVtDgSs1ndcgOTaKlJwwPQ9nVvBzUMbipOMXh97leBYyQWliIGmkbQhwAQALKdtOrfC6u6vx6xt9Yca7PeOtcvAPkMi4LK+6SpfyuoMxJfx8uzlcyxldWS2aqjWd6TxEoPStBJQZKU8RBQd3nz8zOqW9U1pdCh0mfvrMQanJy7D+fZl4GvHl3kPgXRuBXq9IdTWBc3kC1jWNzG1DByfnPu2Ph4iGgRfvpTVkVxbFWor1FT1iPL+ZmoZvgBO5NtJbwOvscQR0lbWl09T2fVBJc41en3cDT6Etk0Cj5ZLbiI7G8M643837nfZZ68xuTaH1VIF/7594+vs/nosvDp4B/H2738AnKEKv5/z8uinD1Zm9Pps4/aCRULlpKUDdsonV7dI5TWfBTOoVi5J3UAksqm+mkzZqcP37YLy6zm5ug2MdbzairSnEIc2C9GyLDRyuJpM2eeL0zMW7We2ai5jcqz3Yc5FVwJZwB0lgljK6q51ZZrOsymo3AT4dU1SAW0KqhFdkPChKwnvQkprqqKJSIxeX7gMDCpyF96Sp5bB4PwgppahdEDwyxeqqaxDfhAysS1T2IZBvc7+pVDC0aenoZco1YsTf5AEJjpevXqFo04H+PQffoUfTLCjT0+dgkIJKhbogoo0ev36NUzTxC+djuDnT9ngAKDDtaE7brW+w+xlr7KBVrs5C9S0UCigWCyiWCxie3sbw+EQxWKR1Q+HQ/bZNIOnmF++QEPiAbLvVFYoiCM/o9d3kl+5FbYrMU1VsiIAsL6+7vp+eXmJYrHo0tor6dyKXFORFa6vrzOQFygrCxMGjRMBqohtmT53TBIwSs5gXnli7x4mjrmgid/s3717h+3tbdb48vJSuKYkWbMPWwLic0xPvCC+g07n0SpN05Ra6HA4DHQZr5EG+unBi684euOODmkj4AcDOBtDNqAvCvjs3cNEaL73r/3vMLUM5ymkO4JwY3oYYEuirNdmQqF0867Wd9DqjnDw4iuc9N1jJBBmiLZluoKD0IwZuRL/IqHXG1jWN7GsbyKV16QJEOrDG42EakpnIkEpt0+7THJtlaUEZGBvmVKajq6BpDG90/DRgOqbDKD4cGBbJtB7GGXE5xCRKOUGW90Rjk/OsVfZ8L1GUeBF9WEPQUpQAnvLRIHcQp694nQa+9lLVej0ILeSuc5CoEFXSpXZiAwl7fgzMuqxqPzWRnJ/PXZyhIif+40MnVoGUtBc30lUg4G5DImAre4IlFdUkYXexGkAc79WhEmcgG5hmkbZ8Bc6vXM9xEcRr7VW6zvYb9QCwZGg9Gox7/Gm/N8VSnBRxEDXfnIX/swN89VIm4Ms7fOYSRmxskjvMjIRWSdpFDUFpLymqsfWQqGLlLmgUTaEhUHjylzQuOs8t6a07amEnnND+bvNXmVDOeYFYkQOS1kdaQwwAZCC5vz5ohctToqk6Tx/fIoNvb8eu/7mFVeUp9fo9aGVwZ6jgcdcr/fSuxAoZWD2AYDLhhKM2iiOX/6XA5GIrDNOvv9/P1uP8Q0WHzUAAAAASUVORK5CYII=',
|
||||||
|
'data:image/png;base64,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',
|
||||||
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAABZCAYAAADVeL+8AAAFcElEQVR4nLVZzUtbWRT/JUqURAkkIWlBeIUmuDHQrqQI3Ygu/Qtk6LLMUldDqYtAF10ozKrMLjDC7LNsEMSdm7GQboIRfBDQ99BHxQ/aQM0sknM9971z33upmQOB5H78zu/ee+75uEks//EXoiTXqPWldm9tKxE2bzIO6MbqIqzqvNZ3tHeA+rDfpMQInmvU+huri6gsr2Lm+ROt7+bkHC+XXyNXLMFzHdQbtb6kQATPNWr9Pzd/w3RlAelCBhP5itY/A+AGgFUFLAxWJClImhinipYIrCZmZ5EqWoM5xZI8RmqsLK8imZ0VJ0hiVefx5sWzwMFr4MQ6TH5eHuPu4nYwOYJAYM+t6ryadHdxizSOtX4CjiOhphgFdn91DQCwW22xX9xzmhRXPNfBq7e/R4ObWJgIHO0diMAiuH9yWN/hP38r4O/HXwPjAnvuuU4sBYBu39KKReY91w4FpTHkb74ff9VIkWjMvbWtRL1R6+eKJVhVfWCqaAWU2q02LANrI3PPdSIPlvrtVltkLYJ7a1uJ+pfTgAJ+G6ndcx0jsJn5UAEHkoC51L+cBvy60RS9ta3EzudDIysOKgGHgo8ipkgUG3xUlwAACX+A5j6Z3K8/fpIc7R0AkPc7AJ5r1PpvXjxTN8+qzgfsm6IPtZEpkgFwJZMSMDFNFa1BOMNAAf1OFzK4Gc6jOCoFa+2GSsDpQgZ3AOAOxqQLGQADu7+/ulYrkYJ1krPmwCQT+YoC5G1cZp4/Qe7VoiJIoqyFGqVrz8Ek4Il8BRP5CqYrC1qwVuDcIvgB/rzUY6i/7ebkXP1OFzLaqjU7t1tt7Hw+DLDnYH5lPdfGzcm5WlUyO6t2QYFzjaMIEZFWOJbr7xfa4kjwuHnK3cVtgH0s5mEKRvbnJCZnZVLmb49kblLA200BfeQDlVhHBuieaxv3z88+rm8fmymSQu6HjOCmfZRYe66DnmurSoQu5KOYR2VmY9kWikIjgcfJGcPEmM5xBaSEK+u5dmTKZ2RumsiVRUkA/LI0j+2zqchkNCwBJRELrpWVFWw3m8DZN3xAUAGBbp9NAQDyQ2UUR2llxmpufX0djuPgXbMp9E4pEgDw7+4DAe52RfBSqYRyuYxyuYylpSUAQKfTEUk4jnlrRPByuSwO9rd3Oh2USnLdD7AD9R/e3Nyc+pgUmkgQlmiKBOj/7VcYJQo8yqyiCJBwf58EHsoUk48g6Xa7xj7Pdcxhjqe++/v7AVCylm63qz4mCyIJWMvm0x/Y3t0F8HBgBCKB+U2RF7xifv6hCLwbKiChC0OmR6DNZhN5BmxkzvPz3apeNYgXdbjSOiNvt9qqjNHA+dvW/dX1oERvAcApNp/+UAQAPSvmNSu3Os3OqZKg78CDiXJL4kUCT2BplWQcGjgFWH8lAehlOwEns7NIZmfx6eN7VezyV7rINy7OxP+qMZ1dUKv89PE9i06nD9tClsJLElPiQ2W73Wor6+BjxbKFREriRSXD7SFgKiUBBCsLXoXRNTaFOdp/u9VWMZUXw6RYYx6XtZ89MfcTCmwLdz6jekoSMgINnIDjZrH+1+fQS3R/da0dUFwxVYKhT3+mpw5ezoeVmGIF/RgxXn8uUXmgsuWQN/SxpNB+JxcA/5Xy3D8nVyxpjivgcklGtXGJ3CSdPAGTZzNZCpeeayMF/c1LA6cv6UJmpP8jPNcZRqm23uYH99+0qC0hv05+W+pX4L9i41FbBvxP7y2h4Ka/w8YCPi4RwcflZ4zMpT/yHgWeLmQwXVlAqmjh5fJrbKwuPkqBAh/lAsUVdUPvr64HD8LjBrdbbVhVaH4iToUcCe6tbSV2GrX+BgDyE6bH9lFFvfz7D+4xoCT/AaNCPNJBtKgMAAAAAElFTkSuQmCC',
|
||||||
|
'data:image/png;base64,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'
|
||||||
|
])
|
||||||
|
.setOriginX(0)
|
||||||
|
.setOriginY(0)
|
||||||
|
.setIsAnimated(true)
|
||||||
|
.setIsLooping(false)
|
||||||
|
.setFrameWidth(64)
|
||||||
|
.setFrameHeight(94)
|
||||||
|
.setFrameRate(7)
|
||||||
|
.setSprite(characterSprite)
|
||||||
|
.save()
|
||||||
|
|
||||||
|
const characterType = new CharacterType()
|
||||||
|
await characterType
|
||||||
|
.setId('75b70c78-17f0-44c0-a4fa-15043cb95be0')
|
||||||
|
.setName('New character type')
|
||||||
|
.setGender(CharacterGender.MALE)
|
||||||
|
.setRace(CharacterRace.HUMAN)
|
||||||
|
.setIsSelectable(true)
|
||||||
|
.setSprite(characterSprite)
|
||||||
|
.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createCharacterHair(): Promise<void> {
|
||||||
|
const hairSprite = new Sprite()
|
||||||
|
hairSprite.setId('922ee95f-1500-49c0-8ead-f8cc46dad136').setName('Hair 1')
|
||||||
|
await hairSprite.save()
|
||||||
|
|
||||||
|
const frontAction = new SpriteAction()
|
||||||
|
await frontAction
|
||||||
|
.setAction('front')
|
||||||
|
.setSprites([
|
||||||
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAASCAYAAABB7B6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA5ElEQVR4nK2VMQ7DIAxFc4AulViysTBUldgqReoGp+pReloqIhx9LOy4IoOVYBw//DFkyZ/vUs3db2Vkuc1rMTwOrRvUwPjwZXs9S0rv/d1BkuDXYx4N41SABAl+7ZJXPzcEqQALJApmroAnC22smbQPw5WPkjoAE7w++X6JAJSF9JRWS3EWiEnzwCrBWPJLkA4gdU1sPvoQIdjGGE+xp8mTULr1IO5Orjlt9NkGaif4kIgqwC6hSarMkkgFoLZ8hVvTeQoglXwJQNMV5bsUUJNKnZJnAHhVWO76vwCjH8hs8gr4ARPESmbfQUg4AAAAAElFTkSuQmCC'
|
||||||
|
])
|
||||||
|
.setOriginX(0.5)
|
||||||
|
.setOriginY(5.34)
|
||||||
|
.setIsAnimated(false)
|
||||||
|
.setIsLooping(false)
|
||||||
|
.setFrameWidth(64)
|
||||||
|
.setFrameHeight(18)
|
||||||
|
.setFrameRate(0)
|
||||||
|
.setSprite(hairSprite)
|
||||||
|
.save()
|
||||||
|
|
||||||
|
const backAction = new SpriteAction()
|
||||||
|
await backAction
|
||||||
|
.setAction('back')
|
||||||
|
.setSprites([
|
||||||
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAWCAYAAADafVyIAAAACXBIWXMAAAsTAAALEwEAmpwYAAABHUlEQVR4nJ1VuwrDMAzMB3QpZOmWxUMpdCsYsrlflU/p16YIrCJfTrLTQZCHfSflLufpvX0mVvP1sns1sk7fu8DP+7Ln16MpeZaWWwMkz0pZf++xgQO4AssmLEsi1QN3J5DFXqUT4JRASRQoBYWkpybQ7lIFssCsASUb0gDBM3x7Bm6NQF3EwKWYuBa4p8nBPbgpB+CeqxoC9pPoBgau99ixR+I6yAPPRhtLwCZ3CbzPkquQVh9Zp3uty8IJLHgpa9OV3ishyyYbOeFPhnZFh0UEeu0SoIC5dm8JoojoRgXqUCq4JcB4GIoKpkMiE2EGDUUFngnoILQiOx+6Bw46IZFYQMJIB1ccL7pn+BH/EtkjsZONgA8TMIeMgAvBF10K1sbmijzuAAAAAElFTkSuQmCC'
|
||||||
|
])
|
||||||
|
.setOriginX(0.5)
|
||||||
|
.setOriginY(4.34)
|
||||||
|
.setIsAnimated(false)
|
||||||
|
.setIsLooping(false)
|
||||||
|
.setFrameWidth(64)
|
||||||
|
.setFrameHeight(22)
|
||||||
|
.setFrameRate(0)
|
||||||
|
.setSprite(hairSprite)
|
||||||
|
.save()
|
||||||
|
|
||||||
|
const characterHair = new CharacterHair()
|
||||||
|
await characterHair.setId('a2471230-d238-4ffb-9eca-9eab869f1b67').setName('Hair 1').setGender(CharacterGender.MALE).setIsSelectable(true).setSprite(hairSprite).save()
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createCharacterEquipment(): Promise<void> {
|
||||||
|
const equipmentSprite = new Sprite()
|
||||||
|
equipmentSprite.id = '5b3932dd-0791-4bb7-bb1e-da9833c3cc50'
|
||||||
|
equipmentSprite.name = 'Male shirt'
|
||||||
|
|
||||||
|
// Create actions similar to createCharacterSprite()
|
||||||
|
// with appropriate sprite data and parameters
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
action: 'idle_right_down',
|
||||||
|
sprites: ['data:image/png;base64,...'],
|
||||||
|
originX: 0,
|
||||||
|
originY: 0,
|
||||||
|
isAnimated: false,
|
||||||
|
isLooping: false,
|
||||||
|
frameWidth: 64,
|
||||||
|
frameHeight: 94,
|
||||||
|
frameRate: 0
|
||||||
|
}
|
||||||
|
// Add other actions...
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const actionData of actions) {
|
||||||
|
const action = new SpriteAction()
|
||||||
|
Object.assign(action, actionData)
|
||||||
|
action.sprite = equipmentSprite
|
||||||
|
await action.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
await equipmentSprite.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createMap(): Promise<void> {
|
||||||
|
const map = new Map()
|
||||||
|
await map
|
||||||
|
.setName('New map')
|
||||||
|
.setWidth(100)
|
||||||
|
.setHeight(100)
|
||||||
|
.setTiles(Array.from({ length: 100 }, () => Array.from({ length: 100 }, () => 'a2fd8d6f-5042-437a-9c1e-c66b91ecc35b')))
|
||||||
|
.save()
|
||||||
|
|
||||||
|
const effect = new MapEffect()
|
||||||
|
await effect.setEffect('light').setStrength(100).setMap(map).save()
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createUser(): Promise<void> {
|
||||||
|
const user = new User()
|
||||||
|
await user.setId('6f9a58b4-172d-425e-b9ea-71e1d13d81ee').setUsername('root').setEmail('local@host').setPassword('password').setOnline(false).save()
|
||||||
|
|
||||||
|
const character = new Character()
|
||||||
|
await character
|
||||||
|
.setId('26850183-1757-4135-938f-aa1448c49654')
|
||||||
|
.setUser(user)
|
||||||
|
.setName('root')
|
||||||
|
.setRole('gm')
|
||||||
|
.setMap((await this.mapRepository.getFirst())!)
|
||||||
|
.setCharacterType((await this.characterTypeRepository.getFirst()) ?? undefined)
|
||||||
|
.setCharacterHair((await this.characterHairRepository.getFirst()) ?? undefined)
|
||||||
|
.save()
|
||||||
|
}
|
||||||
|
}
|
10
src/commands/listMaps.ts
Normal file
10
src/commands/listMaps.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { BaseCommand } from '#application/base/baseCommand'
|
||||||
|
import MapManager from '#managers/mapManager'
|
||||||
|
|
||||||
|
type CommandInput = string[]
|
||||||
|
|
||||||
|
export default class ListMapsCommand extends BaseCommand {
|
||||||
|
public execute(input: CommandInput): void {
|
||||||
|
console.log(MapManager.getLoadedMaps())
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
import { Server } from 'socket.io'
|
|
||||||
import ZoneManager from '../managers/zoneManager'
|
|
||||||
|
|
||||||
type CommandInput = string[]
|
|
||||||
|
|
||||||
export default class ListZonesCommand {
|
|
||||||
constructor(private readonly io: Server) {}
|
|
||||||
|
|
||||||
public execute(input: CommandInput): void {
|
|
||||||
console.log(ZoneManager.getLoadedZones())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,14 @@
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import { commandLogger } from '../utilities/logger'
|
|
||||||
import { Server } from 'socket.io'
|
|
||||||
import { getPublicPath } from '../utilities/storage'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
export default class TilesCommand {
|
import { BaseCommand } from '#application/base/baseCommand'
|
||||||
constructor(private readonly io: Server) {}
|
import Storage from '#application/storage'
|
||||||
|
|
||||||
|
export default class TilesCommand extends BaseCommand {
|
||||||
public async execute(): Promise<void> {
|
public async execute(): Promise<void> {
|
||||||
// Get all tiles
|
// Get all tiles
|
||||||
const tilesDir = getPublicPath('tiles')
|
const tilesDir = Storage.getPublicPath('tiles')
|
||||||
const tiles = fs.readdirSync(tilesDir).filter((file) => file.endsWith('.png'))
|
const tiles = fs.readdirSync(tilesDir).filter((file) => file.endsWith('.png'))
|
||||||
|
|
||||||
// Create output directory if it doesn't exist
|
// Create output directory if it doesn't exist
|
||||||
@ -20,14 +18,14 @@ export default class TilesCommand {
|
|||||||
|
|
||||||
for (const tile of tiles) {
|
for (const tile of tiles) {
|
||||||
// Check if tile is already 66x34
|
// Check if tile is already 66x34
|
||||||
const metadata = await sharp(getPublicPath('tiles', tile)).metadata()
|
const metadata = await sharp(Storage.getPublicPath('tiles', tile)).metadata()
|
||||||
if (metadata.width === 66 && metadata.height === 34) {
|
if (metadata.width === 66 && metadata.height === 34) {
|
||||||
commandLogger.info(`Tile ${tile} already processed`)
|
this.logger.info(`Tile ${tile} already processed`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputPath = getPublicPath('tiles', tile)
|
const inputPath = Storage.getPublicPath('tiles', tile)
|
||||||
const tempPath = getPublicPath('tiles', `temp_${tile}`)
|
const tempPath = Storage.getPublicPath('tiles', `temp_${tile}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sharp(inputPath)
|
await sharp(inputPath)
|
||||||
@ -43,7 +41,7 @@ export default class TilesCommand {
|
|||||||
fs.unlinkSync(inputPath)
|
fs.unlinkSync(inputPath)
|
||||||
fs.renameSync(tempPath, inputPath)
|
fs.renameSync(tempPath, inputPath)
|
||||||
|
|
||||||
commandLogger.info(`Processed and replaced: ${tile}`)
|
this.logger.info(`Processed and replaced: ${tile}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error processing ${tile}:`, error)
|
console.error(`Error processing ${tile}:`, error)
|
||||||
// Clean up temp file if it exists
|
// Clean up temp file if it exists
|
||||||
@ -53,6 +51,6 @@ export default class TilesCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commandLogger.info('Tile processing completed.')
|
this.logger.info('Tile processing completed.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
296
src/entities/base/character.ts
Normal file
296
src/entities/base/character.ts
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { CharacterEquipment } from '#entities/characterEquipment'
|
||||||
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
|
import { CharacterItem } from '#entities/characterItem'
|
||||||
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
import { Chat } from '#entities/chat'
|
||||||
|
import { Map } from '#entities/map'
|
||||||
|
import { User } from '#entities/user'
|
||||||
|
|
||||||
|
|
||||||
|
export class BaseCharacter extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
user!: User
|
||||||
|
|
||||||
|
@Property({ unique: true })
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
online = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
role = 'player'
|
||||||
|
|
||||||
|
@OneToMany(() => Chat, (chat) => chat.character)
|
||||||
|
chats = new Collection<Chat>(this)
|
||||||
|
|
||||||
|
// Position - @TODO: Update to spawn point when current map is not found
|
||||||
|
@ManyToOne()
|
||||||
|
map!: Map
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
positionX = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
positionY = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
rotation = 0
|
||||||
|
|
||||||
|
// Customization
|
||||||
|
@ManyToOne({ deleteRule: 'set null' })
|
||||||
|
characterType?: CharacterType | null | undefined
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'set null' })
|
||||||
|
characterHair?: CharacterHair | null | undefined
|
||||||
|
|
||||||
|
// Inventory
|
||||||
|
@OneToMany({ mappedBy: 'character' })
|
||||||
|
items = new Collection<CharacterItem>(this)
|
||||||
|
|
||||||
|
@OneToMany({ mappedBy: 'character' })
|
||||||
|
equipment = new Collection<CharacterEquipment>(this)
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
@Property()
|
||||||
|
alignment = 50
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
hitpoints = 100
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
mana = 100
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
level = 1
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
experience = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
strength = 10
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
dexterity = 10
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
intelligence = 10
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
wisdom = 10
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(user: User) {
|
||||||
|
this.user = user
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser() {
|
||||||
|
return this.user
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnline(online: boolean) {
|
||||||
|
this.online = online
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getOnline() {
|
||||||
|
return this.online
|
||||||
|
}
|
||||||
|
|
||||||
|
setRole(role: string) {
|
||||||
|
this.role = role
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getRole() {
|
||||||
|
return this.role
|
||||||
|
}
|
||||||
|
|
||||||
|
setChats(chats: Collection<Chat>) {
|
||||||
|
this.chats = chats
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getChats() {
|
||||||
|
return this.chats
|
||||||
|
}
|
||||||
|
|
||||||
|
setMap(map: Map) {
|
||||||
|
this.map = map
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
|
setPositionX(positionX: number) {
|
||||||
|
this.positionX = positionX
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPositionX() {
|
||||||
|
return this.positionX
|
||||||
|
}
|
||||||
|
|
||||||
|
setPositionY(positionY: number) {
|
||||||
|
this.positionY = positionY
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPositionY() {
|
||||||
|
return this.positionY
|
||||||
|
}
|
||||||
|
|
||||||
|
setRotation(rotation: number) {
|
||||||
|
this.rotation = rotation
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getRotation() {
|
||||||
|
return this.rotation
|
||||||
|
}
|
||||||
|
|
||||||
|
setCharacterType(characterType: CharacterType | null | undefined) {
|
||||||
|
this.characterType = characterType
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCharacterType() {
|
||||||
|
return this.characterType
|
||||||
|
}
|
||||||
|
|
||||||
|
setCharacterHair(characterHair: CharacterHair | null | undefined) {
|
||||||
|
this.characterHair = characterHair
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCharacterHair() {
|
||||||
|
return this.characterHair
|
||||||
|
}
|
||||||
|
|
||||||
|
setItems(items: Collection<CharacterItem>) {
|
||||||
|
this.items = items
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getItems() {
|
||||||
|
return this.items
|
||||||
|
}
|
||||||
|
|
||||||
|
setEquipment(equipment: Collection<CharacterEquipment>) {
|
||||||
|
this.equipment = equipment
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getEquipment() {
|
||||||
|
return this.equipment
|
||||||
|
}
|
||||||
|
|
||||||
|
setAlignment(alignment: number) {
|
||||||
|
this.alignment = alignment
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getAlignment() {
|
||||||
|
return this.alignment
|
||||||
|
}
|
||||||
|
|
||||||
|
setHitpoints(hitpoints: number) {
|
||||||
|
this.hitpoints = hitpoints
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getHitpoints() {
|
||||||
|
return this.hitpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
setMana(mana: number) {
|
||||||
|
this.mana = mana
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMana() {
|
||||||
|
return this.mana
|
||||||
|
}
|
||||||
|
|
||||||
|
setLevel(level: number) {
|
||||||
|
this.level = level
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getLevel() {
|
||||||
|
return this.level
|
||||||
|
}
|
||||||
|
|
||||||
|
setExperience(experience: number) {
|
||||||
|
this.experience = experience
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getExperience() {
|
||||||
|
return this.experience
|
||||||
|
}
|
||||||
|
|
||||||
|
setStrength(strength: number) {
|
||||||
|
this.strength = strength
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getStrength() {
|
||||||
|
return this.strength
|
||||||
|
}
|
||||||
|
|
||||||
|
setDexterity(dexterity: number) {
|
||||||
|
this.dexterity = dexterity
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getDexterity() {
|
||||||
|
return this.dexterity
|
||||||
|
}
|
||||||
|
|
||||||
|
setIntelligence(intelligence: number) {
|
||||||
|
this.intelligence = intelligence
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIntelligence() {
|
||||||
|
return this.intelligence
|
||||||
|
}
|
||||||
|
|
||||||
|
setWisdom(wisdom: number) {
|
||||||
|
this.wisdom = wisdom
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getWisdom() {
|
||||||
|
return this.wisdom
|
||||||
|
}
|
||||||
|
}
|
60
src/entities/base/characterEquipment.ts
Normal file
60
src/entities/base/characterEquipment.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { CharacterEquipmentSlotType } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { CharacterItem } from '#entities/characterItem'
|
||||||
|
|
||||||
|
export class BaseCharacterEquipment extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Enum(() => CharacterEquipmentSlotType)
|
||||||
|
slot!: CharacterEquipmentSlotType
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
character!: Character
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
characterItem!: CharacterItem
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setSlot(slot: CharacterEquipmentSlotType) {
|
||||||
|
this.slot = slot
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getSlot() {
|
||||||
|
return this.slot
|
||||||
|
}
|
||||||
|
|
||||||
|
setCharacter(character: Character) {
|
||||||
|
this.character = character
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCharacter() {
|
||||||
|
return this.character
|
||||||
|
}
|
||||||
|
|
||||||
|
setCharacterItem(characterItem: CharacterItem) {
|
||||||
|
this.characterItem = characterItem
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCharacterItem() {
|
||||||
|
return this.characterItem
|
||||||
|
}
|
||||||
|
}
|
72
src/entities/base/characterHair.ts
Normal file
72
src/entities/base/characterHair.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { CharacterGender } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { Sprite } from '#entities/sprite'
|
||||||
|
|
||||||
|
export class BaseCharacterHair extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
gender: CharacterGender = CharacterGender.MALE
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isSelectable = false
|
||||||
|
|
||||||
|
@ManyToOne()
|
||||||
|
sprite?: Sprite
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setGender(gender: CharacterGender) {
|
||||||
|
this.gender = gender
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getGender() {
|
||||||
|
return this.gender
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSelectable(isSelectable: boolean) {
|
||||||
|
this.isSelectable = isSelectable
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsSelectable() {
|
||||||
|
return this.isSelectable
|
||||||
|
}
|
||||||
|
|
||||||
|
setSprite(sprite: Sprite) {
|
||||||
|
this.sprite = sprite
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getSprite() {
|
||||||
|
return this.sprite
|
||||||
|
}
|
||||||
|
}
|
60
src/entities/base/characterItem.ts
Normal file
60
src/entities/base/characterItem.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { CharacterEquipment } from '#entities/characterEquipment'
|
||||||
|
import { Item } from '#entities/item'
|
||||||
|
|
||||||
|
|
||||||
|
export class BaseCharacterItem extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
character!: Character
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
item!: Item
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
quantity!: number
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setCharacter(character: Character) {
|
||||||
|
this.character = character
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCharacter() {
|
||||||
|
return this.character
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(item: Item) {
|
||||||
|
this.item = item
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem() {
|
||||||
|
return this.item
|
||||||
|
}
|
||||||
|
|
||||||
|
setQuantity(quantity: number) {
|
||||||
|
this.quantity = quantity
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getQuantity() {
|
||||||
|
return this.quantity
|
||||||
|
}
|
||||||
|
}
|
108
src/entities/base/characterType.ts
Normal file
108
src/entities/base/characterType.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { CharacterGender, CharacterRace } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { Sprite } from '#entities/sprite'
|
||||||
|
|
||||||
|
export class BaseCharacterType extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@Enum(() => CharacterGender)
|
||||||
|
gender!: CharacterGender
|
||||||
|
|
||||||
|
@Enum(() => CharacterRace)
|
||||||
|
race!: CharacterRace
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isSelectable = false
|
||||||
|
|
||||||
|
@ManyToOne()
|
||||||
|
sprite?: Sprite
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
updatedAt = new Date()
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setGender(gender: CharacterGender) {
|
||||||
|
this.gender = gender
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getGender() {
|
||||||
|
return this.gender
|
||||||
|
}
|
||||||
|
|
||||||
|
setRace(race: CharacterRace) {
|
||||||
|
this.race = race
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getRace() {
|
||||||
|
return this.race
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSelectable(isSelectable: boolean) {
|
||||||
|
this.isSelectable = isSelectable
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsSelectable() {
|
||||||
|
return this.isSelectable
|
||||||
|
}
|
||||||
|
|
||||||
|
setSprite(sprite: Sprite) {
|
||||||
|
this.sprite = sprite
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getSprite() {
|
||||||
|
return this.sprite
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdatedAt(updatedAt: Date) {
|
||||||
|
this.updatedAt = updatedAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdatedAt() {
|
||||||
|
return this.updatedAt
|
||||||
|
}
|
||||||
|
}
|
71
src/entities/base/chat.ts
Normal file
71
src/entities/base/chat.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { Map } from '#entities/map'
|
||||||
|
|
||||||
|
|
||||||
|
export class BaseChat extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
character!: Character
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
map!: Map
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
message!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setCharacter(character: Character) {
|
||||||
|
this.character = character
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCharacter() {
|
||||||
|
return this.character
|
||||||
|
}
|
||||||
|
|
||||||
|
setMap(map: Map) {
|
||||||
|
this.map = map
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
|
setMessage(message: string) {
|
||||||
|
this.message = message
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessage() {
|
||||||
|
return this.message
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
}
|
120
src/entities/base/item.ts
Normal file
120
src/entities/base/item.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { ItemType, ItemRarity } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { CharacterItem } from '#entities/characterItem'
|
||||||
|
import { Sprite } from '#entities/sprite'
|
||||||
|
|
||||||
|
export class BaseItem extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
description: string = ''
|
||||||
|
|
||||||
|
@Enum(() => ItemType)
|
||||||
|
itemType!: ItemType
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
stackable = false
|
||||||
|
|
||||||
|
@Enum(() => ItemRarity)
|
||||||
|
rarity: ItemRarity = ItemRarity.COMMON
|
||||||
|
|
||||||
|
@ManyToOne(() => Sprite)
|
||||||
|
sprite?: Sprite
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
updatedAt = new Date()
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setDescription(description: string) {
|
||||||
|
this.description = description
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getDescription() {
|
||||||
|
return this.description
|
||||||
|
}
|
||||||
|
|
||||||
|
setItemType(itemType: ItemType) {
|
||||||
|
this.itemType = itemType
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemType() {
|
||||||
|
return this.itemType
|
||||||
|
}
|
||||||
|
|
||||||
|
setStackable(stackable: boolean) {
|
||||||
|
this.stackable = stackable
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getStackable() {
|
||||||
|
return this.stackable
|
||||||
|
}
|
||||||
|
|
||||||
|
setRarity(rarity: ItemRarity) {
|
||||||
|
this.rarity = rarity
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getRarity() {
|
||||||
|
return this.rarity
|
||||||
|
}
|
||||||
|
|
||||||
|
setSprite(sprite: Sprite) {
|
||||||
|
this.sprite = sprite
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getSprite() {
|
||||||
|
return this.sprite
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdatedAt(updatedAt: Date) {
|
||||||
|
this.updatedAt = updatedAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdatedAt() {
|
||||||
|
return this.updatedAt
|
||||||
|
}
|
||||||
|
}
|
143
src/entities/base/map.ts
Normal file
143
src/entities/base/map.ts
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { MapEffect } from '#entities/mapEffect'
|
||||||
|
import { MapEventTile } from '#entities/mapEventTile'
|
||||||
|
import { PlacedMapObject } from '#entities/placedMapObject'
|
||||||
|
|
||||||
|
export class BaseMap extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
width = 10
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
height = 10
|
||||||
|
|
||||||
|
@Property({ type: 'json', nullable: true })
|
||||||
|
tiles?: any
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
pvp = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
updatedAt = new Date()
|
||||||
|
|
||||||
|
@OneToMany(() => MapEffect, (effect) => effect.map, { orphanRemoval: true })
|
||||||
|
mapEffects = new Collection<MapEffect>(this)
|
||||||
|
|
||||||
|
@OneToMany(() => MapEventTile, (tile) => tile.map, { orphanRemoval: true })
|
||||||
|
mapEventTiles = new Collection<MapEventTile>(this)
|
||||||
|
|
||||||
|
@OneToMany(() => PlacedMapObject, (placedMapObject) => placedMapObject.map, { orphanRemoval: true })
|
||||||
|
placedMapObjects = new Collection<PlacedMapObject>(this)
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setWidth(width: number) {
|
||||||
|
this.width = width
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidth() {
|
||||||
|
return this.width
|
||||||
|
}
|
||||||
|
|
||||||
|
setHeight(height: number) {
|
||||||
|
this.height = height
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeight() {
|
||||||
|
return this.height
|
||||||
|
}
|
||||||
|
|
||||||
|
setTiles(tiles: any) {
|
||||||
|
this.tiles = tiles
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getTiles() {
|
||||||
|
return this.tiles
|
||||||
|
}
|
||||||
|
|
||||||
|
setPvp(pvp: boolean) {
|
||||||
|
this.pvp = pvp
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPvp() {
|
||||||
|
return this.pvp
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdatedAt(updatedAt: Date) {
|
||||||
|
this.updatedAt = updatedAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdatedAt() {
|
||||||
|
return this.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
setMapEffects(mapEffects: Collection<MapEffect>) {
|
||||||
|
this.mapEffects = mapEffects
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMapEffects() {
|
||||||
|
return this.mapEffects
|
||||||
|
}
|
||||||
|
|
||||||
|
setMapEventTiles(mapEventTiles: Collection<MapEventTile>) {
|
||||||
|
this.mapEventTiles = mapEventTiles
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMapEventTiles() {
|
||||||
|
return this.mapEventTiles
|
||||||
|
}
|
||||||
|
|
||||||
|
setPlacedMapObjects(placedMapObjects: Collection<PlacedMapObject>) {
|
||||||
|
this.placedMapObjects = placedMapObjects
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlacedMapObjects() {
|
||||||
|
return this.placedMapObjects
|
||||||
|
}
|
||||||
|
}
|
58
src/entities/base/mapEffect.ts
Normal file
58
src/entities/base/mapEffect.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Map } from '#entities/map'
|
||||||
|
|
||||||
|
export class BaseMapEffect extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
map!: Map
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
effect!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
strength!: number
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setMap(map: Map) {
|
||||||
|
this.map = map
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
|
setEffect(effect: string) {
|
||||||
|
this.effect = effect
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getEffect() {
|
||||||
|
return this.effect
|
||||||
|
}
|
||||||
|
|
||||||
|
setStrength(strength: number) {
|
||||||
|
this.strength = strength
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getStrength() {
|
||||||
|
return this.strength
|
||||||
|
}
|
||||||
|
}
|
84
src/entities/base/mapEventTile.ts
Normal file
84
src/entities/base/mapEventTile.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, Enum, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { MapEventTileType } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Map } from '#entities/map'
|
||||||
|
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
|
||||||
|
|
||||||
|
export class BaseMapEventTile extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
map!: Map
|
||||||
|
|
||||||
|
@Enum(() => MapEventTileType)
|
||||||
|
type!: MapEventTileType
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
positionX!: number
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
positionY!: number
|
||||||
|
|
||||||
|
@OneToOne(() => MapEventTileTeleport, (teleport) => teleport.mapEventTile, { eager: true })
|
||||||
|
teleport?: MapEventTileTeleport
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setMap(map: Map) {
|
||||||
|
this.map = map
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
|
setType(type: MapEventTileType) {
|
||||||
|
this.type = type
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getType() {
|
||||||
|
return this.type
|
||||||
|
}
|
||||||
|
|
||||||
|
setPositionX(positionX: number) {
|
||||||
|
this.positionX = positionX
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPositionX() {
|
||||||
|
return this.positionX
|
||||||
|
}
|
||||||
|
|
||||||
|
setPositionY(positionY: number) {
|
||||||
|
this.positionY = positionY
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPositionY() {
|
||||||
|
return this.positionY
|
||||||
|
}
|
||||||
|
|
||||||
|
setTeleport(teleport: MapEventTileTeleport) {
|
||||||
|
this.teleport = teleport
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getTeleport() {
|
||||||
|
return this.teleport
|
||||||
|
}
|
||||||
|
}
|
83
src/entities/base/mapEventTileTeleport.ts
Normal file
83
src/entities/base/mapEventTileTeleport.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Map } from '#entities/map'
|
||||||
|
import { MapEventTile } from '#entities/mapEventTile'
|
||||||
|
|
||||||
|
|
||||||
|
export class BaseMapEventTileTeleport extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@OneToOne({ deleteRule: 'cascade' })
|
||||||
|
mapEventTile!: MapEventTile
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade', eager: true })
|
||||||
|
toMap!: Map
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
toRotation!: number
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
toPositionX!: number
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
toPositionY!: number
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setMapEventTile(mapEventTile: MapEventTile) {
|
||||||
|
this.mapEventTile = mapEventTile
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMapEventTile() {
|
||||||
|
return this.mapEventTile
|
||||||
|
}
|
||||||
|
|
||||||
|
setToMap(toMap: Map) {
|
||||||
|
this.toMap = toMap
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getToMap() {
|
||||||
|
return this.toMap
|
||||||
|
}
|
||||||
|
|
||||||
|
setToRotation(toRotation: number) {
|
||||||
|
this.toRotation = toRotation
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getToRotation() {
|
||||||
|
return this.toRotation
|
||||||
|
}
|
||||||
|
|
||||||
|
setToPositionX(toPositionX: number) {
|
||||||
|
this.toPositionX = toPositionX
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getToPositionX() {
|
||||||
|
return this.toPositionX
|
||||||
|
}
|
||||||
|
|
||||||
|
setToPositionY(toPositionY: number) {
|
||||||
|
this.toPositionY = toPositionY
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getToPositionY() {
|
||||||
|
return this.toPositionY
|
||||||
|
}
|
||||||
|
}
|
140
src/entities/base/mapObject.ts
Normal file
140
src/entities/base/mapObject.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
|
export class BaseMapObject extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@Property({ type: 'json', nullable: true })
|
||||||
|
tags?: any
|
||||||
|
|
||||||
|
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||||
|
originX = 0
|
||||||
|
|
||||||
|
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||||
|
originY = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isAnimated = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
frameRate = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
frameWidth = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
frameHeight = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
updatedAt = new Date()
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setTags(tags: any) {
|
||||||
|
this.tags = tags
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getTags() {
|
||||||
|
return this.tags
|
||||||
|
}
|
||||||
|
|
||||||
|
setOriginX(originX: number) {
|
||||||
|
this.originX = originX
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getOriginX() {
|
||||||
|
return this.originX
|
||||||
|
}
|
||||||
|
|
||||||
|
setOriginY(originY: number) {
|
||||||
|
this.originY = originY
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getOriginY() {
|
||||||
|
return this.originY
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsAnimated(isAnimated: boolean) {
|
||||||
|
this.isAnimated = isAnimated
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsAnimated() {
|
||||||
|
return this.isAnimated
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrameRate(frameRate: number) {
|
||||||
|
this.frameRate = frameRate
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrameRate() {
|
||||||
|
return this.frameRate
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrameWidth(frameWidth: number) {
|
||||||
|
this.frameWidth = frameWidth
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrameWidth() {
|
||||||
|
return this.frameWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrameHeight(frameHeight: number) {
|
||||||
|
this.frameHeight = frameHeight
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrameHeight() {
|
||||||
|
return this.frameHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdatedAt(updatedAt: Date) {
|
||||||
|
this.updatedAt = updatedAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdatedAt() {
|
||||||
|
return this.updatedAt
|
||||||
|
}
|
||||||
|
}
|
58
src/entities/base/passwordResetToken.ts
Normal file
58
src/entities/base/passwordResetToken.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { User } from '#entities/user'
|
||||||
|
|
||||||
|
export class BasePasswordResetToken extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
user!: User
|
||||||
|
|
||||||
|
@Property({ unique: true })
|
||||||
|
token!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser(user: User) {
|
||||||
|
this.user = user
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUser() {
|
||||||
|
return this.user
|
||||||
|
}
|
||||||
|
|
||||||
|
setToken(token: string) {
|
||||||
|
this.token = token
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getToken() {
|
||||||
|
return this.token
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
}
|
97
src/entities/base/placedMapObject.ts
Normal file
97
src/entities/base/placedMapObject.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Map } from '#entities/map'
|
||||||
|
import { MapObject } from '#entities/mapObject'
|
||||||
|
|
||||||
|
//@TODO : Rename mapObject
|
||||||
|
|
||||||
|
export class BasePlacedMapObject extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
map!: Map
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade', eager: true })
|
||||||
|
mapObject!: MapObject
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
depth = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isRotated = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
positionX = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
positionY = 0
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setMap(map: Map) {
|
||||||
|
this.map = map
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
|
setMapObject(mapObject: MapObject) {
|
||||||
|
this.mapObject = mapObject
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getMapObject() {
|
||||||
|
return this.mapObject
|
||||||
|
}
|
||||||
|
|
||||||
|
setDepth(depth: number) {
|
||||||
|
this.depth = depth
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepth() {
|
||||||
|
return this.depth
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsRotated(isRotated: boolean) {
|
||||||
|
this.isRotated = isRotated
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsRotated() {
|
||||||
|
return this.isRotated
|
||||||
|
}
|
||||||
|
|
||||||
|
setPositionX(positionX: number) {
|
||||||
|
this.positionX = positionX
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPositionX() {
|
||||||
|
return this.positionX
|
||||||
|
}
|
||||||
|
|
||||||
|
setPositionY(positionY: number) {
|
||||||
|
this.positionY = positionY
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPositionY() {
|
||||||
|
return this.positionY
|
||||||
|
}
|
||||||
|
}
|
70
src/entities/base/sprite.ts
Normal file
70
src/entities/base/sprite.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { SpriteAction } from '#entities/spriteAction'
|
||||||
|
|
||||||
|
export class BaseSprite extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@OneToMany(() => SpriteAction, (action) => action.sprite)
|
||||||
|
spriteActions = new Collection<SpriteAction>(this)
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
updatedAt = new Date()
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setSpriteActions(spriteActions: Collection<SpriteAction>) {
|
||||||
|
this.spriteActions = spriteActions
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpriteActions() {
|
||||||
|
return this.spriteActions
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdatedAt(updatedAt: Date) {
|
||||||
|
this.updatedAt = updatedAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdatedAt() {
|
||||||
|
return this.updatedAt
|
||||||
|
}
|
||||||
|
}
|
142
src/entities/base/spriteAction.ts
Normal file
142
src/entities/base/spriteAction.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Sprite } from '#entities/sprite'
|
||||||
|
|
||||||
|
export class BaseSpriteAction extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ManyToOne({ deleteRule: 'cascade' })
|
||||||
|
sprite!: Sprite
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
action!: string
|
||||||
|
|
||||||
|
@Property({ type: 'json', nullable: true })
|
||||||
|
sprites?: string[]
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
originX = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
originY = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isAnimated = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isLooping = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
frameWidth = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
frameHeight = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
frameRate = 0
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setSprite(sprite: Sprite) {
|
||||||
|
this.sprite = sprite
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getSprite() {
|
||||||
|
return this.sprite
|
||||||
|
}
|
||||||
|
|
||||||
|
setAction(action: string) {
|
||||||
|
this.action = action
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getAction() {
|
||||||
|
return this.action
|
||||||
|
}
|
||||||
|
|
||||||
|
setSprites(sprites: string[]) {
|
||||||
|
this.sprites = sprites
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getSprites() {
|
||||||
|
return this.sprites
|
||||||
|
}
|
||||||
|
|
||||||
|
setOriginX(originX: number) {
|
||||||
|
this.originX = originX
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getOriginX() {
|
||||||
|
return this.originX
|
||||||
|
}
|
||||||
|
|
||||||
|
setOriginY(originY: number) {
|
||||||
|
this.originY = originY
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getOriginY() {
|
||||||
|
return this.originY
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsAnimated(isAnimated: boolean) {
|
||||||
|
this.isAnimated = isAnimated
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsAnimated() {
|
||||||
|
return this.isAnimated
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLooping(isLooping: boolean) {
|
||||||
|
this.isLooping = isLooping
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsLooping() {
|
||||||
|
return this.isLooping
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrameWidth(frameWidth: number) {
|
||||||
|
this.frameWidth = frameWidth
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrameWidth() {
|
||||||
|
return this.frameWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrameHeight(frameHeight: number) {
|
||||||
|
this.frameHeight = frameHeight
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrameHeight() {
|
||||||
|
return this.frameHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
setFrameRate(frameRate: number) {
|
||||||
|
this.frameRate = frameRate
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrameRate() {
|
||||||
|
return this.frameRate
|
||||||
|
}
|
||||||
|
}
|
68
src/entities/base/tile.ts
Normal file
68
src/entities/base/tile.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
|
export class BaseTile extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
name!: string
|
||||||
|
|
||||||
|
@Property({ type: 'json', nullable: true })
|
||||||
|
tags?: any
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
createdAt = new Date()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
updatedAt = new Date()
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string) {
|
||||||
|
this.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
setTags(tags: any) {
|
||||||
|
this.tags = tags
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getTags() {
|
||||||
|
return this.tags
|
||||||
|
}
|
||||||
|
|
||||||
|
setCreatedAt(createdAt: Date) {
|
||||||
|
this.createdAt = createdAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreatedAt() {
|
||||||
|
return this.createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdatedAt(updatedAt: Date) {
|
||||||
|
this.updatedAt = updatedAt
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUpdatedAt() {
|
||||||
|
return this.updatedAt
|
||||||
|
}
|
||||||
|
}
|
97
src/entities/base/user.ts
Normal file
97
src/entities/base/user.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
import bcrypt from 'bcryptjs'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import { PasswordResetToken } from '#entities/passwordResetToken'
|
||||||
|
|
||||||
|
|
||||||
|
export class BaseUser extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
id = randomUUID()
|
||||||
|
|
||||||
|
@Property({ unique: true })
|
||||||
|
username!: string
|
||||||
|
|
||||||
|
@Property({ unique: true })
|
||||||
|
email!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
password!: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
online = false
|
||||||
|
|
||||||
|
@OneToMany(() => Character, (character) => character.user)
|
||||||
|
characters = new Collection<Character>(this)
|
||||||
|
|
||||||
|
@OneToMany(() => PasswordResetToken, (token) => token.user)
|
||||||
|
passwordResetTokens = new Collection<PasswordResetToken>(this)
|
||||||
|
|
||||||
|
setId(id: UUID) {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.id
|
||||||
|
}
|
||||||
|
|
||||||
|
setUsername(username: string) {
|
||||||
|
this.username = username
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getUsername() {
|
||||||
|
return this.username
|
||||||
|
}
|
||||||
|
|
||||||
|
setEmail(email: string) {
|
||||||
|
this.email = email
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmail() {
|
||||||
|
return this.email
|
||||||
|
}
|
||||||
|
|
||||||
|
setPassword(password: string) {
|
||||||
|
this.password = bcrypt.hashSync(password, 10)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPassword() {
|
||||||
|
return this.password
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnline(online: boolean) {
|
||||||
|
this.online = online
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getOnline() {
|
||||||
|
return this.online
|
||||||
|
}
|
||||||
|
|
||||||
|
setCharacters(characters: Collection<Character>) {
|
||||||
|
this.characters = characters
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getCharacters() {
|
||||||
|
return this.characters
|
||||||
|
}
|
||||||
|
|
||||||
|
setPasswordResetTokens(passwordResetTokens: Collection<PasswordResetToken>) {
|
||||||
|
this.passwordResetTokens = passwordResetTokens
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getPasswordResetTokens() {
|
||||||
|
return this.passwordResetTokens
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
65
src/entities/base/world.ts
Normal file
65
src/entities/base/world.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
|
||||||
|
export class BaseWorld extends BaseEntity {
|
||||||
|
@PrimaryKey()
|
||||||
|
date = new Date()
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isRainEnabled = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
rainPercentage = 0
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
isFogEnabled = false
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
fogDensity = 0
|
||||||
|
|
||||||
|
setDate(date: Date) {
|
||||||
|
this.date = date
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getDate() {
|
||||||
|
return this.date
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsRainEnabled(isRainEnabled: boolean) {
|
||||||
|
this.isRainEnabled = isRainEnabled
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsRainEnabled() {
|
||||||
|
return this.isRainEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
setRainPercentage(rainPercentage: number) {
|
||||||
|
this.rainPercentage = rainPercentage
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getRainPercentage() {
|
||||||
|
return this.rainPercentage
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsFogEnabled(isFogEnabled: boolean) {
|
||||||
|
this.isFogEnabled = isFogEnabled
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsFogEnabled() {
|
||||||
|
return this.isFogEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
setFogDensity(fogDensity: number) {
|
||||||
|
this.fogDensity = fogDensity
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
getFogDensity() {
|
||||||
|
return this.fogDensity
|
||||||
|
}
|
||||||
|
}
|
6
src/entities/character.ts
Normal file
6
src/entities/character.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseCharacter } from '#entities/base/character'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Character extends BaseCharacter {}
|
6
src/entities/characterEquipment.ts
Normal file
6
src/entities/characterEquipment.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseCharacterEquipment } from '#entities/base/characterEquipment'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class CharacterEquipment extends BaseCharacterEquipment {}
|
6
src/entities/characterHair.ts
Normal file
6
src/entities/characterHair.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseCharacterHair } from '#entities/base/characterHair'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class CharacterHair extends BaseCharacterHair {}
|
6
src/entities/characterItem.ts
Normal file
6
src/entities/characterItem.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseCharacterItem } from '#entities/base/characterItem'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class CharacterItem extends BaseCharacterItem {}
|
6
src/entities/characterType.ts
Normal file
6
src/entities/characterType.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseCharacterType } from '#entities/base/characterType'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class CharacterType extends BaseCharacterType {}
|
6
src/entities/chat.ts
Normal file
6
src/entities/chat.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseChat } from '#entities/base/chat'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Chat extends BaseChat {}
|
6
src/entities/item.ts
Normal file
6
src/entities/item.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseItem } from '#entities/base/item'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Item extends BaseItem {}
|
6
src/entities/map.ts
Normal file
6
src/entities/map.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseMap } from '#entities/base/map'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Map extends BaseMap {}
|
6
src/entities/mapEffect.ts
Normal file
6
src/entities/mapEffect.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseMapEffect } from '#entities/base/mapEffect'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class MapEffect extends BaseMapEffect {}
|
6
src/entities/mapEventTile.ts
Normal file
6
src/entities/mapEventTile.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseMapEventTile } from '#entities/base/mapEventTile'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class MapEventTile extends BaseMapEventTile {}
|
6
src/entities/mapEventTileTeleport.ts
Normal file
6
src/entities/mapEventTileTeleport.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseMapEventTileTeleport } from '#entities/base/mapEventTileTeleport'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class MapEventTileTeleport extends BaseMapEventTileTeleport {}
|
6
src/entities/mapObject.ts
Normal file
6
src/entities/mapObject.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseMapObject } from '#entities/base/mapObject'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class MapObject extends BaseMapObject {}
|
6
src/entities/passwordResetToken.ts
Normal file
6
src/entities/passwordResetToken.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BasePasswordResetToken } from '#entities/base/passwordResetToken'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class PasswordResetToken extends BasePasswordResetToken {}
|
6
src/entities/placedMapObject.ts
Normal file
6
src/entities/placedMapObject.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BasePlacedMapObject } from '#entities/base/placedMapObject'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class PlacedMapObject extends BasePlacedMapObject {}
|
6
src/entities/sprite.ts
Normal file
6
src/entities/sprite.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseSprite } from '#entities/base/sprite'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Sprite extends BaseSprite {}
|
6
src/entities/spriteAction.ts
Normal file
6
src/entities/spriteAction.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseSpriteAction } from '#entities/base/spriteAction'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class SpriteAction extends BaseSpriteAction {}
|
6
src/entities/tile.ts
Normal file
6
src/entities/tile.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseTile } from '#entities/base/tile'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Tile extends BaseTile {}
|
6
src/entities/user.ts
Normal file
6
src/entities/user.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseUser } from '#entities/base/user'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class User extends BaseUser {}
|
6
src/entities/world.ts
Normal file
6
src/entities/world.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Entity } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
import { BaseWorld } from '#entities/base/world'
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class World extends BaseWorld {}
|
23
src/events/character/charactersScreen/characterHairList.ts
Normal file
23
src/events/character/charactersScreen/characterHairList.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
|
|
||||||
|
interface IPayload {}
|
||||||
|
|
||||||
|
export default class characterHairListEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('character:hair:list', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
const characterHairRepository = new CharacterHairRepository()
|
||||||
|
const items: CharacterHair[] = await characterHairRepository.getAllSelectable(['sprite'])
|
||||||
|
|
||||||
|
return callback(items)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('character:hair:list error', error)
|
||||||
|
return callback([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
src/events/character/connect.ts
Normal file
70
src/events/character/connect.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import MapManager from '#managers/mapManager'
|
||||||
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import TeleportService from '#services/teleportService'
|
||||||
|
|
||||||
|
interface CharacterConnectPayload {
|
||||||
|
characterId: UUID
|
||||||
|
characterHairId?: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CharacterConnectEvent extends BaseEvent {
|
||||||
|
private readonly characterHairRepository = new CharacterHairRepository()
|
||||||
|
private readonly characterRepository = new CharacterRepository()
|
||||||
|
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('character:connect', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (await this.checkForActiveCharacters()) {
|
||||||
|
this.emitError('You are already connected to another character')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const character = await this.characterRepository.getByUserAndId(this.socket.userId!, data.characterId)
|
||||||
|
|
||||||
|
if (!character) {
|
||||||
|
this.emitError('Character not found or does not belong to this user')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate character with characterType and characterHair
|
||||||
|
await this.characterRepository.getEntityManager().populate(character, ['characterType', 'characterHair'])
|
||||||
|
|
||||||
|
// Set character id
|
||||||
|
this.socket.characterId = character.id
|
||||||
|
|
||||||
|
// Set character hair
|
||||||
|
if (data.characterHairId !== undefined && data.characterHairId !== null) {
|
||||||
|
const characterHair = await this.characterHairRepository.getById(data.characterHairId)
|
||||||
|
await character.setCharacterHair(characterHair).save()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit character connect event
|
||||||
|
callback({ character })
|
||||||
|
|
||||||
|
// wait 300 ms, @TODO: Find a better way to do this, race condition
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||||
|
|
||||||
|
await TeleportService.teleportCharacter(character.id, {
|
||||||
|
targetMapId: character.map.id,
|
||||||
|
targetX: character.positionX,
|
||||||
|
targetY: character.positionY,
|
||||||
|
rotation: character.rotation,
|
||||||
|
isInitialJoin: true,
|
||||||
|
character
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
this.handleError('Failed to connect character', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async checkForActiveCharacters(): Promise<boolean> {
|
||||||
|
const characters = await this.characterRepository.getByUserId(this.socket.userId!)
|
||||||
|
return characters?.some((char) => MapManager.getCharacterById(char.id)) ?? false
|
||||||
|
}
|
||||||
|
}
|
67
src/events/character/create.ts
Normal file
67
src/events/character/create.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { ZodError } from 'zod'
|
||||||
|
|
||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { ZCharacterCreate } from '#application/zodTypes'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import MapRepository from '#repositories/mapRepository'
|
||||||
|
import UserRepository from '#repositories/userRepository'
|
||||||
|
|
||||||
|
export default class CharacterCreateEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('character:create', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: any): Promise<any> {
|
||||||
|
// zod validate
|
||||||
|
try {
|
||||||
|
data = ZCharacterCreate.parse(data)
|
||||||
|
|
||||||
|
const userRepository = new UserRepository()
|
||||||
|
const characterRepository = new CharacterRepository()
|
||||||
|
const mapRepository = new MapRepository()
|
||||||
|
|
||||||
|
const user = await userRepository.getById(this.socket.userId!)
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return this.socket.emit('notification', { message: 'User not found' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if character name already exists
|
||||||
|
const characterExists = await characterRepository.getByName(data.name)
|
||||||
|
|
||||||
|
if (characterExists) {
|
||||||
|
return this.socket.emit('notification', { message: 'Character name already exists' })
|
||||||
|
}
|
||||||
|
|
||||||
|
let characters: Character[] = await characterRepository.getByUserId(user.getId())
|
||||||
|
|
||||||
|
if (characters.length >= 4) {
|
||||||
|
return this.socket.emit('notification', { message: 'You can only have 4 characters' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO: Change to default location
|
||||||
|
const map = await mapRepository.getFirst()
|
||||||
|
|
||||||
|
const newCharacter = new Character()
|
||||||
|
await newCharacter.setName(data.name).setUser(user).setMap(map!).save()
|
||||||
|
|
||||||
|
if (!newCharacter) {
|
||||||
|
return this.socket.emit('notification', { message: 'Failed to create character. Please try again (later).' })
|
||||||
|
}
|
||||||
|
|
||||||
|
characters = [...characters, newCharacter]
|
||||||
|
|
||||||
|
this.socket.emit('character:create:success')
|
||||||
|
this.socket.emit('character:list', characters)
|
||||||
|
|
||||||
|
this.logger.info('character:create success')
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error(`character:create error: ${error.message}`)
|
||||||
|
if (error instanceof ZodError) {
|
||||||
|
return this.socket.emit('notification', { message: error.issues[0].message })
|
||||||
|
}
|
||||||
|
return this.socket.emit('notification', { message: 'Could not create character. Please try again (later).' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/events/character/delete.ts
Normal file
30
src/events/character/delete.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
|
||||||
|
type TypePayload = {
|
||||||
|
characterId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeResponse = {
|
||||||
|
characters: Character[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CharacterDeleteEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('character:delete', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
|
||||||
|
try {
|
||||||
|
const characterRepository = new CharacterRepository()
|
||||||
|
await (await characterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete()
|
||||||
|
const characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
|
||||||
|
|
||||||
|
this.socket.emit('character:list', characters)
|
||||||
|
} catch (error: any) {
|
||||||
|
return this.socket.emit('notification', { message: 'Character delete failed. Please try again.' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/events/character/list.ts
Normal file
23
src/events/character/list.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
|
||||||
|
export default class CharacterListEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('character:list', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const characterRepository = new CharacterRepository()
|
||||||
|
let characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
|
||||||
|
|
||||||
|
// Populate characters with characterType and characterHair
|
||||||
|
await characterRepository.getEntityManager().populate(characters, ['characterType', 'characterHair'])
|
||||||
|
|
||||||
|
this.socket.emit('character:list', characters)
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('character:list error', error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
src/events/chat/gameMaster/alertCommand.ts
Normal file
35
src/events/chat/gameMaster/alertCommand.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import ChatService from '#services/chatService'
|
||||||
|
|
||||||
|
type TypePayload = {
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AlertCommandEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Check if command is alert
|
||||||
|
if (!ChatService.isCommand(data.message, 'alert')) return
|
||||||
|
|
||||||
|
// Check if character exists
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const args = ChatService.getArgs('alert', data.message)
|
||||||
|
|
||||||
|
if (!args) {
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.io.emit('notification', { title: 'Message from GM', message: args.join(' ') })
|
||||||
|
return callback(true)
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('chat:alert_command error', error.message)
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
src/events/chat/gameMaster/setTimeCommand.ts
Normal file
42
src/events/chat/gameMaster/setTimeCommand.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import DateManager from '#managers/dateManager'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import ChatService from '#services/chatService'
|
||||||
|
|
||||||
|
type TypePayload = {
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SetTimeCommand extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Check if command is time
|
||||||
|
if (!ChatService.isCommand(data.message, 'time')) return
|
||||||
|
|
||||||
|
// Check if character exists and is GM
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
// Get arguments
|
||||||
|
const args = ChatService.getArgs('time', data.message)
|
||||||
|
|
||||||
|
if (!args) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const time = args[0] // 24h time, e.g. 17:34
|
||||||
|
|
||||||
|
if (!time) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await DateManager.setTime(time)
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('command error', error.message)
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
src/events/chat/gameMaster/teleportCommand.ts
Normal file
95
src/events/chat/gameMaster/teleportCommand.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import MapManager from '#managers/mapManager'
|
||||||
|
import MapRepository from '#repositories/mapRepository'
|
||||||
|
import ChatService from '#services/chatService'
|
||||||
|
import TeleportService from '#services/teleportService'
|
||||||
|
|
||||||
|
type TypePayload = {
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TeleportCommandEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void) {
|
||||||
|
try {
|
||||||
|
// Check if command is teleport
|
||||||
|
if (!ChatService.isCommand(data.message, 'teleport')) return
|
||||||
|
|
||||||
|
// Check if character exists and is GM
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const character = await this.getCharacter()
|
||||||
|
if (!character) return
|
||||||
|
|
||||||
|
const args = ChatService.getArgs('teleport', data.message)
|
||||||
|
|
||||||
|
if (!args || args.length === 0 || args.length > 3) {
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Usage: /teleport <mapId> [x] [y]'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapId = args[0] as UUID
|
||||||
|
const targetX = args[1] ? parseInt(args[1], 10) : 0
|
||||||
|
const targetY = args[2] ? parseInt(args[2], 10) : 0
|
||||||
|
|
||||||
|
if (!mapId || isNaN(targetX) || isNaN(targetY)) {
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Invalid parameters. X and Y coordinates must be numbers.'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapRepository = new MapRepository()
|
||||||
|
const map = await mapRepository.getById(mapId)
|
||||||
|
if (!map) {
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Map not found'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character.map.id === map.id && targetX === character.positionX && targetY === character.positionY) {
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'You are already at that location'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const success = await TeleportService.teleportCharacter(character.id, {
|
||||||
|
targetMapId: map.id,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
rotation: character.rotation
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Failed to teleport'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: `Teleported to ${map.name} (${targetX}, ${targetY})`
|
||||||
|
})
|
||||||
|
this.logger.info('teleport', `Character ${character.id} teleported to map ${map.id} at position (${targetX}, ${targetY})`)
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error(`Error in teleport command: ${error.message}`)
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'An error occurred while teleporting'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/events/chat/gameMaster/toggleFogCommand.ts
Normal file
29
src/events/chat/gameMaster/toggleFogCommand.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import WeatherManager from '#managers/weatherManager'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import ChatService from '#services/chatService'
|
||||||
|
|
||||||
|
type TypePayload = {
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ToggleFogCommand extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Check if command is fog
|
||||||
|
if (!ChatService.isCommand(data.message, 'fog')) return
|
||||||
|
|
||||||
|
// Check if character exists and is GM
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
await WeatherManager.toggleFog()
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('command error', error.message)
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/events/chat/gameMaster/toggleRainCommand.ts
Normal file
29
src/events/chat/gameMaster/toggleRainCommand.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import WeatherManager from '#managers/weatherManager'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import ChatService from '#services/chatService'
|
||||||
|
|
||||||
|
type TypePayload = {
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ToggleRainCommand extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Check if command is rain
|
||||||
|
if (!ChatService.isCommand(data.message, 'rain')) return
|
||||||
|
|
||||||
|
// Check if character exists and is GM
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
await WeatherManager.toggleRain()
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('command error', error.message)
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/events/chat/message.ts
Normal file
46
src/events/chat/message.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import MapManager from '#managers/mapManager'
|
||||||
|
import MapRepository from '#repositories/mapRepository'
|
||||||
|
import ChatService from '#services/chatService'
|
||||||
|
|
||||||
|
type TypePayload = {
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ChatMessageEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!data.message || ChatService.isCommand(data.message)) {
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||||
|
if (!mapCharacter) {
|
||||||
|
this.logger.error('chat:message error', 'Character not found')
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const character = mapCharacter.character
|
||||||
|
|
||||||
|
const mapRepository = new MapRepository()
|
||||||
|
const map = await mapRepository.getById(character.map.id)
|
||||||
|
if (!map) {
|
||||||
|
this.logger.error('chat:message error', 'Map not found')
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await ChatService.sendMapMessage(character.getId(), map.getId(), data.message)) {
|
||||||
|
return callback(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(false)
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('chat:message error', error.message)
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/events/disconnect.ts
Normal file
30
src/events/disconnect.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import MapManager from '#managers/mapManager'
|
||||||
|
|
||||||
|
export default class DisconnectEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('disconnect', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!this.socket.userId) {
|
||||||
|
this.logger.info('User disconnected but had no user set')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.io.emit('user:disconnect', this.socket.userId)
|
||||||
|
|
||||||
|
const mapCharacter = MapManager.getCharacterById(this.socket.characterId!)
|
||||||
|
if (!mapCharacter) {
|
||||||
|
this.logger.info('User disconnected but had no character set')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await mapCharacter.disconnect(this.socket, this.io)
|
||||||
|
this.logger.info('User disconnected along with their character')
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('disconnect error: ' + error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/events/gameMaster/assetManager/characterHair/create.ts
Normal file
22
src/events/gameMaster/assetManager/characterHair/create.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
|
|
||||||
|
export default class CharacterHairCreateEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterHair:create', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const newCharacterHair = new CharacterHair()
|
||||||
|
await newCharacterHair.setName('New hair').save()
|
||||||
|
|
||||||
|
return callback(true)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating character hair:', error)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/events/gameMaster/assetManager/characterHair/delete.ts
Normal file
27
src/events/gameMaster/assetManager/characterHair/delete.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
|
|
||||||
|
interface IPayload {
|
||||||
|
id: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class characterHairDeleteEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterHair:remove', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const characterHair = await CharacterHairRepository.getById(data.id)
|
||||||
|
await (await CharacterHairRepository.getById(data.id))?.delete()
|
||||||
|
|
||||||
|
return callback(true)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/events/gameMaster/assetManager/characterHair/list.ts
Normal file
26
src/events/gameMaster/assetManager/characterHair/list.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
|
|
||||||
|
interface IPayload {}
|
||||||
|
|
||||||
|
export default class characterHairListEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterHair:list', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const characterHairRepository = new CharacterHairRepository()
|
||||||
|
const items = await characterHairRepository.getAll()
|
||||||
|
await characterHairRepository.getEntityManager().populate(items, ['sprite'])
|
||||||
|
|
||||||
|
return callback(items)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('gm:characterHair:list error', error)
|
||||||
|
return callback([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
src/events/gameMaster/assetManager/characterHair/update.ts
Normal file
39
src/events/gameMaster/assetManager/characterHair/update.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterGender } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
|
||||||
|
type Payload = {
|
||||||
|
id: UUID
|
||||||
|
name: string
|
||||||
|
gender: CharacterGender
|
||||||
|
isSelectable: boolean
|
||||||
|
spriteId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CharacterHairUpdateEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterHair:update', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const spriteRepository = new SpriteRepository()
|
||||||
|
const sprite = await spriteRepository.getById(data.spriteId)
|
||||||
|
if (!sprite) return callback(false)
|
||||||
|
|
||||||
|
const characterHairRepository = new CharacterHairRepository()
|
||||||
|
const characterHair = await characterHairRepository.getById(data.id)
|
||||||
|
if (!characterHair) return callback(false)
|
||||||
|
|
||||||
|
await characterHair.setName(data.name).setGender(data.gender).setIsSelectable(data.isSelectable).setSprite(sprite).save()
|
||||||
|
return callback(true)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error updating character hair: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/events/gameMaster/assetManager/characterType/create.ts
Normal file
22
src/events/gameMaster/assetManager/characterType/create.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
|
||||||
|
export default class CharacterTypeCreateEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterType:create', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const newCharacterType = new CharacterType()
|
||||||
|
await newCharacterType.setName('New character type').save()
|
||||||
|
|
||||||
|
return callback(true, newCharacterType)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating character type:', error)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/events/gameMaster/assetManager/characterType/delete.ts
Normal file
29
src/events/gameMaster/assetManager/characterType/delete.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
|
|
||||||
|
interface IPayload {
|
||||||
|
id: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CharacterTypeDeleteEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterType:remove', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const characterTypeRepository = new CharacterTypeRepository()
|
||||||
|
const characterType = await characterTypeRepository.getById(data.id)
|
||||||
|
if (!characterType) return callback(false)
|
||||||
|
|
||||||
|
await characterType.delete()
|
||||||
|
return callback(true)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/events/gameMaster/assetManager/characterType/list.ts
Normal file
26
src/events/gameMaster/assetManager/characterType/list.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
|
|
||||||
|
interface IPayload {}
|
||||||
|
|
||||||
|
export default class CharacterTypeListEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterType:list', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const characterTypeRepository = new CharacterTypeRepository()
|
||||||
|
const items = await characterTypeRepository.getAll()
|
||||||
|
await characterTypeRepository.getEntityManager().populate(items, ['sprite'])
|
||||||
|
|
||||||
|
return callback(items)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('gm:characterType:list error', error)
|
||||||
|
return callback([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
src/events/gameMaster/assetManager/characterType/update.ts
Normal file
41
src/events/gameMaster/assetManager/characterType/update.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterGender, CharacterRace } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
|
||||||
|
type Payload = {
|
||||||
|
id: UUID
|
||||||
|
name: string
|
||||||
|
gender: CharacterGender
|
||||||
|
race: CharacterRace
|
||||||
|
isSelectable: boolean
|
||||||
|
spriteId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CharacterTypeUpdateEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:characterType:update', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const characterTypeRepository = new CharacterTypeRepository()
|
||||||
|
const characterType = await characterTypeRepository.getById(data.id)
|
||||||
|
if (!characterType) return callback(false)
|
||||||
|
|
||||||
|
const spriteRepository = new SpriteRepository()
|
||||||
|
const sprite = await spriteRepository.getById(data.spriteId)
|
||||||
|
if (!sprite) return callback(false)
|
||||||
|
|
||||||
|
await characterType.setName(data.name).setGender(data.gender).setRace(data.race).setIsSelectable(data.isSelectable).setSprite(sprite).save()
|
||||||
|
|
||||||
|
return callback(true)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/events/gameMaster/assetManager/item/create.ts
Normal file
22
src/events/gameMaster/assetManager/item/create.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { Item } from '#entities/item'
|
||||||
|
|
||||||
|
export default class ItemCreateEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:item:create', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: undefined, callback: (response: boolean, item?: any) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const newItem = new Item()
|
||||||
|
await newItem.setName('New Item').setItemType('WEAPON').setStackable(false).setRarity('COMMON').setSprite(null).save()
|
||||||
|
|
||||||
|
return callback(true, newItem)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating item:', error)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/events/gameMaster/assetManager/item/delete.ts
Normal file
30
src/events/gameMaster/assetManager/item/delete.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
import ItemRepository from '#repositories/itemRepository'
|
||||||
|
|
||||||
|
interface IPayload {
|
||||||
|
id: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ItemDeleteEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:item:remove', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const itemRepository = new ItemRepository()
|
||||||
|
const item = await itemRepository.getById(data.id)
|
||||||
|
if (!item) return callback(false)
|
||||||
|
|
||||||
|
await item.delete()
|
||||||
|
|
||||||
|
return callback(true)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error deleting item ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user