1
0
forked from noxious/server

POC working new caching method - moved controllers folder, renamed assets to textures, fixed HTTP bug, formatted code

This commit is contained in:
Dennis Postma 2025-01-07 03:58:32 +01:00
parent f47023dc81
commit 010454914b
25 changed files with 131 additions and 137 deletions

View File

@ -1,4 +1,6 @@
import { Request, Response } from 'express' import fs from 'fs'
import { Response } from 'express'
import Logger, { LoggerType } from '#application/logger' import Logger, { LoggerType } from '#application/logger'
@ -19,4 +21,18 @@ export abstract class BaseController {
message 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)
}
})
}
} }

View File

@ -61,8 +61,8 @@ export class LogReader {
}) })
stream.on('data', (data) => { stream.on('data', (data) => {
console.log(`[${filename}]`); console.log(`[${filename}]`)
console.log(data.toString()); // console.log(data.toString()) //
}) })
currentPosition = newPosition currentPosition = newPosition

View File

@ -259,8 +259,8 @@ export default class InitCommand extends BaseCommand {
.setName('root') .setName('root')
.setRole('gm') .setRole('gm')
.setMap((await this.mapRepository.getFirst())!) .setMap((await this.mapRepository.getFirst())!)
.setCharacterType((await this.characterTypeRepository.getFirst())) .setCharacterType(await this.characterTypeRepository.getFirst())
.setCharacterHair((await this.characterHairRepository.getFirst())) .setCharacterHair(await this.characterHairRepository.getFirst())
.save() .save()
} }
} }

View File

@ -1,10 +1,6 @@
import fs from 'fs'
import { Request, Response } from 'express' import { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController' import { BaseController } from '#application/base/baseController'
import Database from '#application/database'
import Storage from '#application/storage'
import { AssetData, UUID } from '#application/types' import { AssetData, UUID } from '#application/types'
import MapRepository from '#repositories/mapRepository' import MapRepository from '#repositories/mapRepository'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
@ -25,7 +21,7 @@ export class AssetsController extends BaseController {
const tiles = await this.tileRepository.getAll() const tiles = await this.tileRepository.getAll()
for (const tile of tiles) { 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) return this.sendSuccess(res, assets)
@ -52,7 +48,7 @@ export class AssetsController extends BaseController {
const tiles = await this.tileRepository.getByMapId(mapId) const tiles = await this.tileRepository.getByMapId(mapId)
for (const tile of tiles) { 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) return this.sendSuccess(res, assets)
@ -79,7 +75,7 @@ export class AssetsController extends BaseController {
const assets: AssetData[] = sprite.getSpriteActions().map((spriteAction) => ({ const assets: AssetData[] = sprite.getSpriteActions().map((spriteAction) => ({
key: sprite.getId() + '-' + spriteAction.getAction(), 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', group: spriteAction.getIsAnimated() ? 'sprite_animations' : 'sprites',
updatedAt: sprite.getUpdatedAt(), updatedAt: sprite.getUpdatedAt(),
originX: Number(spriteAction.getOriginX().toString()), originX: Number(spriteAction.getOriginX().toString()),
@ -93,31 +89,4 @@ export class AssetsController extends BaseController {
return this.sendSuccess(res, assets) 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) {
}
} }

View File

@ -1,92 +1,45 @@
import { Request, Response } from 'express' import { Request, Response } from 'express'
import { BaseController } from '#application/base/baseController' import { BaseController } from '#application/base/baseController'
import { AssetData, UUID } from '#application/types' import MapObjectRepository from '#repositories/mapObjectRepository'
import MapRepository from '#repositories/mapRepository' import MapRepository from '#repositories/mapRepository'
import SpriteRepository from '#repositories/spriteRepository' import SpriteRepository from '#repositories/spriteRepository'
import TileRepository from '#repositories/tileRepository' import TileRepository from '#repositories/tileRepository'
export class DataController extends BaseController { export class CacheController extends BaseController {
private readonly mapRepository = new MapRepository()
private readonly spriteRepository = new SpriteRepository()
private readonly tileRepository = new TileRepository()
/** /**
* List tiles * Serve a list of maps and send as JSON
* @param req * @param req
* @param res * @param res
*/ */
public async tiles(req: Request, res: Response) { public async maps(req: Request, res: Response) {
const assets: AssetData[] = [] const items: any[] = []
const tiles = await this.tileRepository.getAll()
for (const tile of tiles) { const mapRepository = new MapRepository()
assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData) 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 req
* @param res * @param res
*/ */
public async listTilesByMap(req: Request, res: Response) { public async mapObjects(req: Request, res: Response) {
const mapId = req.params.mapId as UUID const items: any[] = []
if (!mapId) { const mapObjectRepository = new MapObjectRepository()
return this.sendError(res, 'Invalid map ID', 400) const mapObjects = await mapObjectRepository.getAll()
for (const mapObject of mapObjects) {
items.push(await mapObject.cache())
} }
const map = await this.mapRepository.getById(mapId) return this.sendSuccess(res, items)
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)
} }
} }

View File

@ -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)
}
}

View File

@ -12,7 +12,6 @@ import { Chat } from '#entities/chat'
import { Map } from '#entities/map' import { Map } from '#entities/map'
import { User } from '#entities/user' import { User } from '#entities/user'
export class BaseCharacter extends BaseEntity { export class BaseCharacter extends BaseEntity {
@PrimaryKey() @PrimaryKey()
id = randomUUID() id = randomUUID()

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core' import { Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { CharacterEquipmentSlotType } from '#application/enums' import { CharacterEquipmentSlotType } from '#application/enums'
import { UUID } from '#application/types' import { UUID } from '#application/types'

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { CharacterGender } from '#application/enums' import { CharacterGender } from '#application/enums'
import { UUID } from '#application/types' import { UUID } from '#application/types'

View File

@ -8,7 +8,6 @@ import { Character } from '#entities/character'
import { CharacterEquipment } from '#entities/characterEquipment' import { CharacterEquipment } from '#entities/characterEquipment'
import { Item } from '#entities/item' import { Item } from '#entities/item'
export class BaseCharacterItem extends BaseEntity { export class BaseCharacterItem extends BaseEntity {
@PrimaryKey() @PrimaryKey()
id = randomUUID() id = randomUUID()

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { CharacterGender, CharacterRace } from '#application/enums' import { CharacterGender, CharacterRace } from '#application/enums'
import { UUID } from '#application/types' import { UUID } from '#application/types'

View File

@ -7,7 +7,6 @@ import { UUID } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import { Map } from '#entities/map' import { Map } from '#entities/map'
export class BaseChat extends BaseEntity { export class BaseChat extends BaseEntity {
@PrimaryKey() @PrimaryKey()
id = randomUUID() id = randomUUID()

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { ItemType, ItemRarity } from '#application/enums' import { ItemType, ItemRarity } from '#application/enums'
import { UUID } from '#application/types' import { UUID } from '#application/types'

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core' import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types' import { UUID } from '#application/types'
import { Map } from '#entities/map' import { Map } from '#entities/map'

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Entity, Enum, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core' import { Entity, Enum, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { MapEventTileType } from '#application/enums' import { MapEventTileType } from '#application/enums'
import { UUID } from '#application/types' import { UUID } from '#application/types'

View File

@ -7,7 +7,6 @@ import { UUID } from '#application/types'
import { Map } from '#entities/map' import { Map } from '#entities/map'
import { MapEventTile } from '#entities/mapEventTile' import { MapEventTile } from '#entities/mapEventTile'
export class BaseMapEventTileTeleport extends BaseEntity { export class BaseMapEventTileTeleport extends BaseEntity {
@PrimaryKey() @PrimaryKey()
id = randomUUID() id = randomUUID()

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core' import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types' import { UUID } from '#application/types'
import { User } from '#entities/user' import { User } from '#entities/user'

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core' import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types' import { UUID } from '#application/types'
import { Map } from '#entities/map' import { Map } from '#entities/map'

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types' import { UUID } from '#application/types'
import { SpriteAction } from '#entities/spriteAction' import { SpriteAction } from '#entities/spriteAction'

View File

@ -2,7 +2,6 @@ import { randomUUID } from 'node:crypto'
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core' import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
import { BaseEntity } from '#application/base/baseEntity' import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types' import { UUID } from '#application/types'
import { Sprite } from '#entities/sprite' import { Sprite } from '#entities/sprite'

View File

@ -8,7 +8,6 @@ import { UUID } from '#application/types'
import { Character } from '#entities/character' import { Character } from '#entities/character'
import { PasswordResetToken } from '#entities/passwordResetToken' import { PasswordResetToken } from '#entities/passwordResetToken'
export class BaseUser extends BaseEntity { export class BaseUser extends BaseEntity {
@PrimaryKey() @PrimaryKey()
id = randomUUID() id = randomUUID()

View File

@ -3,4 +3,36 @@ import { Entity } from '@mikro-orm/core'
import { BaseMap } from '#entities/base/map' import { BaseMap } from '#entities/base/map'
@Entity() @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 {}
}
}
}

View File

@ -3,4 +3,13 @@ import { Entity } from '@mikro-orm/core'
import { BaseMapObject } from '#entities/base/mapObject' import { BaseMapObject } from '#entities/base/mapObject'
@Entity() @Entity()
export class MapObject extends BaseMapObject {} export class MapObject extends BaseMapObject {
public async cache() {
try {
return this
} catch (error) {
console.error(error)
return {}
}
}
}

View File

@ -1,20 +1,25 @@
import { Application } from 'express' import { Application } from 'express'
import { AssetsController } from '#http/controllers/assets' import { AssetsController } from '#controllers/assets'
import { AuthController } from '#http/controllers/auth' import { AuthController } from '#controllers/auth'
import { AvatarController } from '#http/controllers/avatar' import { AvatarController } from '#controllers/avatar'
import { CacheController } from '#controllers/cache'
import { TexturesController } from '#controllers/textures'
/**
* HTTP manager
*/
class HttpManager { class HttpManager {
private readonly authController: AuthController private readonly authController: AuthController = new AuthController()
private readonly avatarController: AvatarController private readonly avatarController: AvatarController = new AvatarController()
private readonly assetsController: AssetsController private readonly assetsController: AssetsController = new AssetsController()
private readonly texturesController: TexturesController = new TexturesController()
constructor() { private readonly cacheController: CacheController = new CacheController()
this.authController = new AuthController()
this.avatarController = new AvatarController()
this.assetsController = new AssetsController()
}
/**
* Initialize HTTP manager
* @param app
*/
public async boot(app: Application) { public async boot(app: Application) {
// Add routes // Add routes
await this.addRoutes(app) 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', (req, res) => this.assetsController.listTiles(req, res))
app.get('/assets/list_tiles/:mapId', (req, res) => this.assetsController.listTilesByMap(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/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))
} }
} }

View File

@ -19,7 +19,7 @@
"#application/*": ["./src/application/*"], "#application/*": ["./src/application/*"],
"#commands/*": ["./src/commands/*"], "#commands/*": ["./src/commands/*"],
"#entities/*": ["./src/entities/*"], "#entities/*": ["./src/entities/*"],
"#http/*": ["./src/http/*"], "#controllers/*": ["./src/controllers/*"],
"#jobs/*": ["./src/jobs/*"], "#jobs/*": ["./src/jobs/*"],
"#managers/*": ["./src/managers/*"], "#managers/*": ["./src/managers/*"],
"#middleware/*": ["./src/middleware/*"], "#middleware/*": ["./src/middleware/*"],