diff --git a/src/application/base/baseController.ts b/src/application/base/baseController.ts index f222148..cf3242e 100644 --- a/src/application/base/baseController.ts +++ b/src/application/base/baseController.ts @@ -1,4 +1,6 @@ -import { Request, Response } from 'express' +import fs from 'fs' + +import { Response } from 'express' import Logger, { LoggerType } from '#application/logger' @@ -19,4 +21,18 @@ export abstract class BaseController { message }) } + + protected sendFile(res: Response, filePath: string) { + if (!fs.existsSync(filePath)) { + this.logger.error(`File not found: ${filePath}`) + return this.sendError(res, 'Asset not found', 404) + } + + res.sendFile(filePath, (error) => { + if (error) { + this.logger.error('Error sending file:' + error) + this.sendError(res, 'Error downloading the asset', 500) + } + }) + } } diff --git a/src/application/console/logReader.ts b/src/application/console/logReader.ts index 52faed3..83f8532 100644 --- a/src/application/console/logReader.ts +++ b/src/application/console/logReader.ts @@ -61,8 +61,8 @@ export class LogReader { }) stream.on('data', (data) => { - console.log(`[${filename}]`); - console.log(data.toString()); // + console.log(`[${filename}]`) + console.log(data.toString()) // }) currentPosition = newPosition diff --git a/src/commands/init.ts b/src/commands/init.ts index f7959b2..19751d6 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -259,8 +259,8 @@ export default class InitCommand extends BaseCommand { .setName('root') .setRole('gm') .setMap((await this.mapRepository.getFirst())!) - .setCharacterType((await this.characterTypeRepository.getFirst())) - .setCharacterHair((await this.characterHairRepository.getFirst())) + .setCharacterType(await this.characterTypeRepository.getFirst()) + .setCharacterHair(await this.characterHairRepository.getFirst()) .save() } } diff --git a/src/controllers/assets.ts b/src/controllers/assets.ts index 4c6c69b..e2ec4ac 100644 --- a/src/controllers/assets.ts +++ b/src/controllers/assets.ts @@ -1,10 +1,6 @@ -import fs from 'fs' - import { Request, Response } from 'express' import { BaseController } from '#application/base/baseController' -import Database from '#application/database' -import Storage from '#application/storage' import { AssetData, UUID } from '#application/types' import MapRepository from '#repositories/mapRepository' import SpriteRepository from '#repositories/spriteRepository' @@ -25,7 +21,7 @@ export class AssetsController extends BaseController { const tiles = await this.tileRepository.getAll() for (const tile of tiles) { - assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData) + assets.push({ key: tile.getId(), data: '/textures/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData) } return this.sendSuccess(res, assets) @@ -52,7 +48,7 @@ export class AssetsController extends BaseController { const tiles = await this.tileRepository.getByMapId(mapId) for (const tile of tiles) { - assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData) + assets.push({ key: tile.getId(), data: '/textures/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData) } return this.sendSuccess(res, assets) @@ -79,7 +75,7 @@ export class AssetsController extends BaseController { const assets: AssetData[] = sprite.getSpriteActions().map((spriteAction) => ({ key: sprite.getId() + '-' + spriteAction.getAction(), - data: '/assets/sprites/' + sprite.getId() + '/' + spriteAction.getAction() + '.png', + data: '/textures/sprites/' + sprite.getId() + '/' + spriteAction.getAction() + '.png', group: spriteAction.getIsAnimated() ? 'sprite_animations' : 'sprites', updatedAt: sprite.getUpdatedAt(), originX: Number(spriteAction.getOriginX().toString()), @@ -93,31 +89,4 @@ export class AssetsController extends BaseController { return this.sendSuccess(res, assets) } - - /** - * Download asset - * @param req - * @param res - */ - public async downloadAsset(req: Request, res: Response) { - const { type, spriteId, file } = req.params - - const assetPath = type === 'sprites' && spriteId ? Storage.getPublicPath(type, spriteId, file) : Storage.getPublicPath(type, file) - - if (!fs.existsSync(assetPath)) { - this.logger.error(`File not found: ${assetPath}`) - return this.sendError(res, 'Asset not found', 404) - } - - res.sendFile(assetPath, (err) => { - if (err) { - this.logger.error('Error sending file:' + err) - this.sendError(res, 'Error downloading the asset', 500) - } - }) - } - - public async downloadCache(req: Request, res: Response) { - - } } diff --git a/src/controllers/cache.ts b/src/controllers/cache.ts index d0b3c86..3a9ced2 100644 --- a/src/controllers/cache.ts +++ b/src/controllers/cache.ts @@ -1,92 +1,45 @@ import { Request, Response } from 'express' import { BaseController } from '#application/base/baseController' -import { AssetData, UUID } from '#application/types' +import MapObjectRepository from '#repositories/mapObjectRepository' import MapRepository from '#repositories/mapRepository' import SpriteRepository from '#repositories/spriteRepository' import TileRepository from '#repositories/tileRepository' -export class DataController extends BaseController { - private readonly mapRepository = new MapRepository() - private readonly spriteRepository = new SpriteRepository() - private readonly tileRepository = new TileRepository() - +export class CacheController extends BaseController { /** - * List tiles + * Serve a list of maps and send as JSON * @param req * @param res */ - public async tiles(req: Request, res: Response) { - const assets: AssetData[] = [] - const tiles = await this.tileRepository.getAll() + public async maps(req: Request, res: Response) { + const items: any[] = [] - for (const tile of tiles) { - assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData) + const mapRepository = new MapRepository() + const maps = await mapRepository.getAll() + + for (const map of maps) { + items.push(await map.cache()) } - return this.sendSuccess(res, assets) + return this.sendSuccess(res, items) } /** - * List tiles by map + * Serve a list of map objects and send as JSON * @param req * @param res */ - public async listTilesByMap(req: Request, res: Response) { - const mapId = req.params.mapId as UUID + public async mapObjects(req: Request, res: Response) { + const items: any[] = [] - if (!mapId) { - return this.sendError(res, 'Invalid map ID', 400) + const mapObjectRepository = new MapObjectRepository() + const mapObjects = await mapObjectRepository.getAll() + + for (const mapObject of mapObjects) { + items.push(await mapObject.cache()) } - const map = await this.mapRepository.getById(mapId) - if (!map) { - return this.sendError(res, 'Map not found', 404) - } - - const assets: AssetData[] = [] - const tiles = await this.tileRepository.getByMapId(mapId) - - for (const tile of tiles) { - assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData) - } - - return this.sendSuccess(res, assets) - } - - /** - * List sprite actions - * @param req - * @param res - */ - public async listSpriteActions(req: Request, res: Response) { - const spriteId = req.params.spriteId as UUID - - if (!spriteId) { - return this.sendError(res, 'Invalid sprite ID', 400) - } - - const sprite = await this.spriteRepository.getById(spriteId) - if (!sprite) { - return this.sendError(res, 'Sprite not found', 404) - } - - await this.spriteRepository.getEntityManager().populate(sprite, ['spriteActions']) - - const assets: AssetData[] = sprite.getSpriteActions().map((spriteAction) => ({ - key: sprite.getId() + '-' + spriteAction.getAction(), - data: '/assets/sprites/' + sprite.getId() + '/' + spriteAction.getAction() + '.png', - group: spriteAction.getIsAnimated() ? 'sprite_animations' : 'sprites', - updatedAt: sprite.getUpdatedAt(), - originX: Number(spriteAction.getOriginX().toString()), - originY: Number(spriteAction.getOriginY().toString()), - isAnimated: spriteAction.getIsAnimated(), - frameRate: spriteAction.getFrameRate(), - frameWidth: spriteAction.getFrameWidth(), - frameHeight: spriteAction.getFrameHeight(), - frameCount: spriteAction.getSprites()?.length - })) - - return this.sendSuccess(res, assets) + return this.sendSuccess(res, items) } } diff --git a/src/controllers/textures.ts b/src/controllers/textures.ts index e69de29..bbd04a1 100644 --- a/src/controllers/textures.ts +++ b/src/controllers/textures.ts @@ -0,0 +1,19 @@ +import { Request, Response } from 'express' + +import { BaseController } from '#application/base/baseController' +import Storage from '#application/storage' + +export class TexturesController extends BaseController { + /** + * Download texture + * @param req + * @param res + */ + public async download(req: Request, res: Response) { + const { type, spriteId, file } = req.params + + const texture = type === 'sprites' && spriteId ? Storage.getPublicPath(type, spriteId, file) : Storage.getPublicPath(type, file) + + this.sendFile(res, texture) + } +} diff --git a/src/entities/base/character.ts b/src/entities/base/character.ts index e0f6103..3f74f1f 100644 --- a/src/entities/base/character.ts +++ b/src/entities/base/character.ts @@ -12,7 +12,6 @@ import { Chat } from '#entities/chat' import { Map } from '#entities/map' import { User } from '#entities/user' - export class BaseCharacter extends BaseEntity { @PrimaryKey() id = randomUUID() diff --git a/src/entities/base/characterEquipment.ts b/src/entities/base/characterEquipment.ts index 34cd6e7..71d95db 100644 --- a/src/entities/base/characterEquipment.ts +++ b/src/entities/base/characterEquipment.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/characterHair.ts b/src/entities/base/characterHair.ts index fb5bf3f..8a924c2 100644 --- a/src/entities/base/characterHair.ts +++ b/src/entities/base/characterHair.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/characterItem.ts b/src/entities/base/characterItem.ts index ba0c0e9..9ae9871 100644 --- a/src/entities/base/characterItem.ts +++ b/src/entities/base/characterItem.ts @@ -8,7 +8,6 @@ import { Character } from '#entities/character' import { CharacterEquipment } from '#entities/characterEquipment' import { Item } from '#entities/item' - export class BaseCharacterItem extends BaseEntity { @PrimaryKey() id = randomUUID() diff --git a/src/entities/base/characterType.ts b/src/entities/base/characterType.ts index e10d94d..3c9ff97 100644 --- a/src/entities/base/characterType.ts +++ b/src/entities/base/characterType.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/chat.ts b/src/entities/base/chat.ts index 4522b1c..f6cc454 100644 --- a/src/entities/base/chat.ts +++ b/src/entities/base/chat.ts @@ -7,7 +7,6 @@ import { UUID } from '#application/types' import { Character } from '#entities/character' import { Map } from '#entities/map' - export class BaseChat extends BaseEntity { @PrimaryKey() id = randomUUID() diff --git a/src/entities/base/item.ts b/src/entities/base/item.ts index d039cab..e4da4d8 100644 --- a/src/entities/base/item.ts +++ b/src/entities/base/item.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/mapEffect.ts b/src/entities/base/mapEffect.ts index 82f5335..e9fae7b 100644 --- a/src/entities/base/mapEffect.ts +++ b/src/entities/base/mapEffect.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/mapEventTile.ts b/src/entities/base/mapEventTile.ts index a95bca6..f6f53dd 100644 --- a/src/entities/base/mapEventTile.ts +++ b/src/entities/base/mapEventTile.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/mapEventTileTeleport.ts b/src/entities/base/mapEventTileTeleport.ts index a921637..bf09d61 100644 --- a/src/entities/base/mapEventTileTeleport.ts +++ b/src/entities/base/mapEventTileTeleport.ts @@ -7,7 +7,6 @@ import { UUID } from '#application/types' import { Map } from '#entities/map' import { MapEventTile } from '#entities/mapEventTile' - export class BaseMapEventTileTeleport extends BaseEntity { @PrimaryKey() id = randomUUID() diff --git a/src/entities/base/passwordResetToken.ts b/src/entities/base/passwordResetToken.ts index 13ffed6..4133725 100644 --- a/src/entities/base/passwordResetToken.ts +++ b/src/entities/base/passwordResetToken.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/placedMapObject.ts b/src/entities/base/placedMapObject.ts index 8d91b13..3d2e170 100644 --- a/src/entities/base/placedMapObject.ts +++ b/src/entities/base/placedMapObject.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/sprite.ts b/src/entities/base/sprite.ts index d60cc18..dced5de 100644 --- a/src/entities/base/sprite.ts +++ b/src/entities/base/sprite.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/spriteAction.ts b/src/entities/base/spriteAction.ts index 06bdc08..ea2ea34 100644 --- a/src/entities/base/spriteAction.ts +++ b/src/entities/base/spriteAction.ts @@ -2,7 +2,6 @@ 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' diff --git a/src/entities/base/user.ts b/src/entities/base/user.ts index 99240f3..06fe160 100644 --- a/src/entities/base/user.ts +++ b/src/entities/base/user.ts @@ -8,7 +8,6 @@ import { UUID } from '#application/types' import { Character } from '#entities/character' import { PasswordResetToken } from '#entities/passwordResetToken' - export class BaseUser extends BaseEntity { @PrimaryKey() id = randomUUID() diff --git a/src/entities/map.ts b/src/entities/map.ts index aba9d82..a248485 100644 --- a/src/entities/map.ts +++ b/src/entities/map.ts @@ -3,4 +3,36 @@ import { Entity } from '@mikro-orm/core' import { BaseMap } from '#entities/base/map' @Entity() -export class Map extends BaseMap {} +export class Map extends BaseMap { + public async cache() { + try { + await this.getPlacedMapObjects().load() + await this.getMapEffects().load() + + return { + id: this.getId(), + name: this.getName(), + width: this.getWidth(), + height: this.getHeight(), + tiles: this.getTiles(), + pvp: this.getPvp(), + updatedAt: this.getUpdatedAt(), + placedMapObjects: this.getPlacedMapObjects().map((placedMapObject) => ({ + id: placedMapObject.getId(), + mapObject: placedMapObject.getMapObject().getId(), + depth: placedMapObject.getDepth(), + isRotated: placedMapObject.getIsRotated(), + positionX: placedMapObject.getPositionX(), + positionY: placedMapObject.getPositionY() + })), + mapEffects: this.getMapEffects().map((mapEffect) => ({ + effect: mapEffect.getEffect(), + strength: mapEffect.getStrength() + })) + } + } catch (error) { + console.error(error) + return {} + } + } +} diff --git a/src/entities/mapObject.ts b/src/entities/mapObject.ts index 4110afd..cfd7f90 100644 --- a/src/entities/mapObject.ts +++ b/src/entities/mapObject.ts @@ -3,4 +3,13 @@ import { Entity } from '@mikro-orm/core' import { BaseMapObject } from '#entities/base/mapObject' @Entity() -export class MapObject extends BaseMapObject {} +export class MapObject extends BaseMapObject { + public async cache() { + try { + return this + } catch (error) { + console.error(error) + return {} + } + } +} diff --git a/src/managers/httpManager.ts b/src/managers/httpManager.ts index 83e430a..ed50ba7 100644 --- a/src/managers/httpManager.ts +++ b/src/managers/httpManager.ts @@ -1,20 +1,25 @@ import { Application } from 'express' -import { AssetsController } from '#http/controllers/assets' -import { AuthController } from '#http/controllers/auth' -import { AvatarController } from '#http/controllers/avatar' +import { AssetsController } from '#controllers/assets' +import { AuthController } from '#controllers/auth' +import { AvatarController } from '#controllers/avatar' +import { CacheController } from '#controllers/cache' +import { TexturesController } from '#controllers/textures' +/** + * HTTP manager + */ class HttpManager { - private readonly authController: AuthController - private readonly avatarController: AvatarController - private readonly assetsController: AssetsController - - constructor() { - this.authController = new AuthController() - this.avatarController = new AvatarController() - this.assetsController = new AssetsController() - } + private readonly authController: AuthController = new AuthController() + private readonly avatarController: AvatarController = new AvatarController() + private readonly assetsController: AssetsController = new AssetsController() + private readonly texturesController: TexturesController = new TexturesController() + private readonly cacheController: CacheController = new CacheController() + /** + * Initialize HTTP manager + * @param app + */ public async boot(app: Application) { // Add routes await this.addRoutes(app) @@ -35,7 +40,13 @@ class HttpManager { app.get('/assets/list_tiles', (req, res) => this.assetsController.listTiles(req, res)) app.get('/assets/list_tiles/:mapId', (req, res) => this.assetsController.listTilesByMap(req, res)) app.get('/assets/list_sprite_actions/:spriteId', (req, res) => this.assetsController.listSpriteActions(req, res)) - app.get('/assets/:type/:spriteId?/:file', (req, res) => this.assetsController.downloadAsset(req, res)) + + // Download texture file + app.get('/textures/:type/:spriteId?/:file', (req, res) => this.texturesController.download(req, res)) + + // Cache routes + app.get('/cache/maps', (req, res) => this.cacheController.maps(req, res)) + app.get('/cache/map_objects', (req, res) => this.cacheController.mapObjects(req, res)) } } diff --git a/tsconfig.json b/tsconfig.json index ffeccaa..cfa92e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,7 @@ "#application/*": ["./src/application/*"], "#commands/*": ["./src/commands/*"], "#entities/*": ["./src/entities/*"], - "#http/*": ["./src/http/*"], + "#controllers/*": ["./src/controllers/*"], "#jobs/*": ["./src/jobs/*"], "#managers/*": ["./src/managers/*"], "#middleware/*": ["./src/middleware/*"],