Converted more procedural programming to OOP
This commit is contained in:
parent
b7f448cb17
commit
e571cf2230
18
src/application/base/baseController.ts
Normal file
18
src/application/base/baseController.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
|
||||||
|
export abstract class BaseController {
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { User } from './user'
|
import { User } from './user'
|
||||||
import { Zone } from './zone'
|
import { Zone } from './zone'
|
||||||
import { CharacterType } from './characterType'
|
import { CharacterType } from './characterType'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Entity, Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
|
import { Entity, Enum, ManyToOne, PrimaryKey } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Character } from './character'
|
import { Character } from './character'
|
||||||
import { CharacterItem } from './characterItem'
|
import { CharacterItem } from './characterItem'
|
||||||
import { CharacterEquipmentSlotType } from '#application/enums'
|
import { CharacterEquipmentSlotType } from '#application/enums'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Character } from './character'
|
import { Character } from './character'
|
||||||
import { Sprite } from './sprite'
|
import { Sprite } from './sprite'
|
||||||
import { CharacterGender } from '#application/enums'
|
import { CharacterGender } from '#application/enums'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Character } from './character'
|
import { Character } from './character'
|
||||||
import { Item } from './item'
|
import { Item } from './item'
|
||||||
import { CharacterEquipment } from './characterEquipment'
|
import { CharacterEquipment } from './characterEquipment'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Character } from './character'
|
import { Character } from './character'
|
||||||
import { Sprite } from './sprite'
|
import { Sprite } from './sprite'
|
||||||
import { CharacterGender, CharacterRace } from '#application/enums'
|
import { CharacterGender, CharacterRace } from '#application/enums'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Character } from './character'
|
import { Character } from './character'
|
||||||
import { Zone } from './zone'
|
import { Zone } from './zone'
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Sprite } from './sprite'
|
import { Sprite } from './sprite'
|
||||||
import { CharacterItem } from './characterItem'
|
import { CharacterItem } from './characterItem'
|
||||||
import { ItemType, ItemRarity } from '#application/enums'
|
import { ItemType, ItemRarity } from '#application/enums'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { randomUUID } from 'node:crypto'
|
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { ZoneObject } from './zoneObject'
|
import { ZoneObject } from './zoneObject'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { User } from './user'
|
import { User } from './user'
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { randomUUID } from 'node:crypto'
|
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { SpriteAction } from './spriteAction'
|
import { SpriteAction } from './spriteAction'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { randomUUID } from 'node:crypto'
|
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Sprite } from './sprite'
|
import { Sprite } from './sprite'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { randomUUID } from 'node:crypto'
|
import { randomUUID } from 'node:crypto'
|
||||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Character } from './character'
|
import { Character } from './character'
|
||||||
import { PasswordResetToken } from './passwordResetToken'
|
import { PasswordResetToken } from './passwordResetToken'
|
||||||
import bcrypt from 'bcryptjs'
|
import bcrypt from 'bcryptjs'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Entity, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class World extends BaseEntity {
|
export class World extends BaseEntity {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { ZoneEffect } from './zoneEffect'
|
import { ZoneEffect } from './zoneEffect'
|
||||||
import { ZoneEventTile } from './zoneEventTile'
|
import { ZoneEventTile } from './zoneEventTile'
|
||||||
import { ZoneEventTileTeleport } from './zoneEventTileTeleport'
|
import { ZoneEventTileTeleport } from './zoneEventTileTeleport'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { randomUUID } from 'node:crypto'
|
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Zone } from './zone'
|
import { Zone } from './zone'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
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/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Zone } from './zone'
|
import { Zone } from './zone'
|
||||||
import { ZoneEventTileType } from '#application/enums'
|
import { ZoneEventTileType } from '#application/enums'
|
||||||
import { ZoneEventTileTeleport } from './zoneEventTileTeleport'
|
import { ZoneEventTileTeleport } from './zoneEventTileTeleport'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { randomUUID } from 'node:crypto'
|
import { randomUUID } from 'node:crypto'
|
||||||
import { Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Entity, ManyToOne, OneToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Zone } from './zone'
|
import { Zone } from './zone'
|
||||||
import { ZoneEventTile } from './zoneEventTile'
|
import { ZoneEventTile } from './zoneEventTile'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'
|
||||||
import { BaseEntity } from '#application/bases/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { Zone } from './zone'
|
import { Zone } from './zone'
|
||||||
import { MapObject } from '#entities/mapObject'
|
import { MapObject } from '#entities/mapObject'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
@ -1,114 +0,0 @@
|
|||||||
import { Router, Request, Response } from 'express'
|
|
||||||
import fs from 'fs'
|
|
||||||
import { httpLogger } from '#application/logger'
|
|
||||||
import { getPublicPath } from '#application/storage'
|
|
||||||
import TileRepository from '#repositories/tileRepository'
|
|
||||||
import ZoneRepository from '#repositories/zoneRepository'
|
|
||||||
import SpriteRepository from '#repositories/spriteRepository'
|
|
||||||
import { AssetData } from '#application/types'
|
|
||||||
import { FilterValue } from '@mikro-orm/core'
|
|
||||||
|
|
||||||
const router = Router()
|
|
||||||
|
|
||||||
// Get all tiles
|
|
||||||
router.get('/assets/list_tiles', async (req: Request, res: Response) => {
|
|
||||||
let assets: AssetData[] = []
|
|
||||||
const tiles = await TileRepository.getAll()
|
|
||||||
for (const tile of tiles) {
|
|
||||||
assets.push({
|
|
||||||
key: tile.id,
|
|
||||||
data: '/assets/tiles/' + tile.id + '.png',
|
|
||||||
group: 'tiles',
|
|
||||||
updatedAt: tile.updatedAt
|
|
||||||
} as AssetData)
|
|
||||||
}
|
|
||||||
res.json(assets)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get tiles by zone
|
|
||||||
router.get('/assets/list_tiles/:zoneId', async (req: Request, res: Response) => {
|
|
||||||
const zoneId = req.params.zoneId
|
|
||||||
|
|
||||||
if (!zoneId || parseInt(zoneId) === 0) {
|
|
||||||
return res.status(400).json({ message: 'Invalid zone ID' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const zone = await ZoneRepository.getById(parseInt(zoneId))
|
|
||||||
if (!zone) {
|
|
||||||
return res.status(404).json({ message: 'Zone not found' })
|
|
||||||
}
|
|
||||||
|
|
||||||
let assets: AssetData[] = []
|
|
||||||
const tiles = await TileRepository.getByZoneId(parseInt(zoneId))
|
|
||||||
for (const tile of tiles) {
|
|
||||||
assets.push({
|
|
||||||
key: tile.id,
|
|
||||||
data: '/assets/tiles/' + tile.id + '.png',
|
|
||||||
group: 'tiles',
|
|
||||||
updatedAt: tile.updatedAt
|
|
||||||
} as AssetData)
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json(assets)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get sprite actions
|
|
||||||
router.get('/assets/list_sprite_actions/:spriteId', async (req: Request, res: Response) => {
|
|
||||||
const spriteId = req.params.spriteId as FilterValue<`${string}-${string}-${string}-${string}-${string}`>
|
|
||||||
|
|
||||||
if (!spriteId) {
|
|
||||||
return res.status(400).json({ message: 'Invalid sprite ID' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const sprite = await SpriteRepository.getById(spriteId)
|
|
||||||
if (!sprite) {
|
|
||||||
return res.status(404).json({ message: 'Sprite not found' })
|
|
||||||
}
|
|
||||||
|
|
||||||
let assets: AssetData[] = []
|
|
||||||
sprite.spriteActions.getItems().forEach((spriteAction) => {
|
|
||||||
assets.push({
|
|
||||||
key: sprite.id + '-' + spriteAction.action,
|
|
||||||
data: '/assets/sprites/' + sprite.id + '/' + spriteAction.action + '.png',
|
|
||||||
group: spriteAction.isAnimated ? 'sprite_animations' : 'sprites',
|
|
||||||
updatedAt: sprite.updatedAt,
|
|
||||||
originX: Number(spriteAction.originX.toString()),
|
|
||||||
originY: Number(spriteAction.originY.toString()),
|
|
||||||
isAnimated: spriteAction.isAnimated,
|
|
||||||
frameCount: JSON.parse(JSON.stringify(spriteAction.sprites)).length,
|
|
||||||
frameWidth: spriteAction.frameWidth,
|
|
||||||
frameHeight: spriteAction.frameHeight,
|
|
||||||
frameRate: spriteAction.frameRate
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json(assets)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Download asset file
|
|
||||||
router.get('/assets/:type/:spriteId?/:file', (req: Request, res: Response) => {
|
|
||||||
const assetType = req.params.type
|
|
||||||
const spriteId = req.params.spriteId
|
|
||||||
const fileName = req.params.file
|
|
||||||
|
|
||||||
let assetPath
|
|
||||||
if (assetType === 'sprites' && spriteId) {
|
|
||||||
assetPath = getPublicPath(assetType, spriteId, fileName)
|
|
||||||
} else {
|
|
||||||
assetPath = getPublicPath(assetType, fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(assetPath)) {
|
|
||||||
httpLogger.error(`File not found: ${assetPath}`)
|
|
||||||
return res.status(404).send('Asset not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
res.sendFile(assetPath, (err) => {
|
|
||||||
if (err) {
|
|
||||||
httpLogger.error('Error sending file:', err)
|
|
||||||
res.status(500).send('Error downloading the asset')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
|
@ -1,90 +0,0 @@
|
|||||||
import { Router, Request, Response } from 'express'
|
|
||||||
import UserService from '#services/userService'
|
|
||||||
import jwt from 'jsonwebtoken'
|
|
||||||
import config from '#application/config'
|
|
||||||
import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from '#application/zodTypes'
|
|
||||||
|
|
||||||
const router = Router()
|
|
||||||
|
|
||||||
// Login endpoint
|
|
||||||
router.post('/login', async (req: Request, res: Response) => {
|
|
||||||
const { username, password } = req.body
|
|
||||||
|
|
||||||
try {
|
|
||||||
loginAccountSchema.parse({ username, password })
|
|
||||||
} catch (error: any) {
|
|
||||||
return res.status(400).json({ message: error.errors[0]?.message })
|
|
||||||
}
|
|
||||||
|
|
||||||
const userService = new UserService()
|
|
||||||
const user = await userService.login(username, password)
|
|
||||||
|
|
||||||
if (user && typeof user !== 'boolean') {
|
|
||||||
const token = jwt.sign({ id: user.getId() }, config.JWT_SECRET, { expiresIn: '4h' })
|
|
||||||
return res.status(200).json({ token })
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(400).json({ message: 'Failed to login' })
|
|
||||||
})
|
|
||||||
|
|
||||||
// Register endpoint
|
|
||||||
router.post('/register', async (req: Request, res: Response) => {
|
|
||||||
const { username, email, password } = req.body
|
|
||||||
|
|
||||||
try {
|
|
||||||
registerAccountSchema.parse({ username, email, password })
|
|
||||||
} catch (error: any) {
|
|
||||||
return res.status(400).json({ message: error.errors[0]?.message })
|
|
||||||
}
|
|
||||||
|
|
||||||
const userService = new UserService()
|
|
||||||
const user = await userService.register(username, email, password)
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
return res.status(200).json({ message: 'User registered' })
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(400).json({ message: 'Failed to register user' })
|
|
||||||
})
|
|
||||||
|
|
||||||
// Reset password endpoint
|
|
||||||
router.post('/reset-password', async (req: Request, res: Response) => {
|
|
||||||
const { email } = req.body
|
|
||||||
|
|
||||||
try {
|
|
||||||
resetPasswordSchema.parse({ email })
|
|
||||||
} catch (error: any) {
|
|
||||||
return res.status(400).json({ message: error.errors[0]?.message })
|
|
||||||
}
|
|
||||||
|
|
||||||
const userService = new UserService()
|
|
||||||
const sentEmail = await userService.requestPasswordReset(email)
|
|
||||||
|
|
||||||
if (sentEmail) {
|
|
||||||
return res.status(200).json({ message: 'Email has been sent' })
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(400).json({ message: 'Failed to send password reset request. Perhaps one has already been sent recently, check your spam folder.' })
|
|
||||||
})
|
|
||||||
|
|
||||||
// New password endpoint
|
|
||||||
router.post('/new-password', async (req: Request, res: Response) => {
|
|
||||||
const { urlToken, password } = req.body
|
|
||||||
|
|
||||||
try {
|
|
||||||
newPasswordSchema.parse({ urlToken, password })
|
|
||||||
} catch (error: any) {
|
|
||||||
return res.status(400).json({ message: error.errors[0]?.message })
|
|
||||||
}
|
|
||||||
|
|
||||||
const userService = new UserService()
|
|
||||||
const resetPassword = await userService.resetPassword(urlToken, password)
|
|
||||||
|
|
||||||
if (resetPassword) {
|
|
||||||
return res.status(200).json({ message: 'Password has been reset' })
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(400).json({ message: 'Failed to set new password' })
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
|
@ -1,85 +0,0 @@
|
|||||||
/**
|
|
||||||
* Avatar generator API routes
|
|
||||||
*/
|
|
||||||
import { Router, Request, Response } from 'express'
|
|
||||||
import sharp from 'sharp'
|
|
||||||
import fs from 'fs'
|
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
|
||||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
|
||||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
|
||||||
import { getPublicPath } from '#application/storage'
|
|
||||||
|
|
||||||
const router = Router()
|
|
||||||
|
|
||||||
interface AvatarOptions {
|
|
||||||
characterTypeId: number
|
|
||||||
characterHairId?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
async function generateAvatar(res: Response, options: AvatarOptions) {
|
|
||||||
try {
|
|
||||||
const characterType = await CharacterTypeRepository.getById(options.characterTypeId)
|
|
||||||
if (!characterType?.sprite?.id) {
|
|
||||||
return res.status(404).json({ message: 'Character type not found' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const bodySpritePath = getPublicPath('sprites', characterType.sprite.id, 'idle_right_down.png')
|
|
||||||
if (!fs.existsSync(bodySpritePath)) {
|
|
||||||
console.error(`Body sprite file not found: ${bodySpritePath}`)
|
|
||||||
return res.status(404).json({ message: 'Body sprite file not found' })
|
|
||||||
}
|
|
||||||
|
|
||||||
let avatar = sharp(bodySpritePath).extend({
|
|
||||||
top: 2,
|
|
||||||
bottom: 2,
|
|
||||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
|
||||||
})
|
|
||||||
|
|
||||||
if (options.characterHairId) {
|
|
||||||
const characterHair = await CharacterHairRepository.getById(options.characterHairId)
|
|
||||||
if (characterHair?.sprite?.id) {
|
|
||||||
const hairSpritePath = getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
|
||||||
if (fs.existsSync(hairSpritePath)) {
|
|
||||||
avatar = avatar.composite([
|
|
||||||
{
|
|
||||||
input: hairSpritePath,
|
|
||||||
gravity: 'north'
|
|
||||||
// Top is originY in min @TODO finish me #287
|
|
||||||
// top: Math.round(Number(characterHair.sprite!.spriteActions.find((action) => action.action === 'front')?.originY ?? 0)),
|
|
||||||
// left: 0
|
|
||||||
}
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
console.error(`Hair sprite file not found: ${hairSpritePath}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'image/png')
|
|
||||||
return avatar.pipe(res)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error generating avatar:', error)
|
|
||||||
return res.status(500).json({ message: 'Error generating avatar' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
router.get('/avatar/:characterName', async (req: Request, res: Response) => {
|
|
||||||
const character = await CharacterRepository.getByName(req.params.characterName)
|
|
||||||
if (!character?.characterType) {
|
|
||||||
return res.status(404).json({ message: 'Character or character type not found' })
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateAvatar(res, {
|
|
||||||
characterTypeId: character.characterType.id,
|
|
||||||
characterHairId: character.characterHair?.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
router.get('/avatar/s/:characterTypeId/:characterHairId?', async (req: Request, res: Response) => {
|
|
||||||
return generateAvatar(res, {
|
|
||||||
characterTypeId: parseInt(req.params.characterTypeId),
|
|
||||||
characterHairId: req.params.characterHairId ? parseInt(req.params.characterHairId) : undefined
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
|
122
src/http/controllers/assets.ts
Normal file
122
src/http/controllers/assets.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
import fs from 'fs'
|
||||||
|
import { BaseController } from '#application/base/baseController'
|
||||||
|
import { httpLogger } from '#application/logger'
|
||||||
|
import { getPublicPath } from '#application/storage'
|
||||||
|
import TileRepository from '#repositories/tileRepository'
|
||||||
|
import ZoneRepository from '#repositories/zoneRepository'
|
||||||
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
import { AssetData } from '#application/types'
|
||||||
|
import { FilterValue } from '@mikro-orm/core'
|
||||||
|
|
||||||
|
export class AssetsController extends BaseController {
|
||||||
|
/**
|
||||||
|
* List tiles
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async listTiles(req: Request, res: Response) {
|
||||||
|
const assets: AssetData[] = []
|
||||||
|
const tiles = await TileRepository.getAll()
|
||||||
|
|
||||||
|
for (const tile of tiles) {
|
||||||
|
assets.push({
|
||||||
|
key: tile.id,
|
||||||
|
data: '/assets/tiles/' + tile.getId() + '.png',
|
||||||
|
group: 'tiles',
|
||||||
|
updatedAt: tile.getUpdatedAt()
|
||||||
|
} as AssetData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendSuccess(res, assets)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List tiles by zone
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async listTilesByZone(req: Request, res: Response) {
|
||||||
|
const zoneId = parseInt(req.params.zoneId)
|
||||||
|
|
||||||
|
if (!zoneId || zoneId === 0) {
|
||||||
|
return this.sendError(res, 'Invalid zone ID', 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
const zone = await ZoneRepository.getById(zoneId)
|
||||||
|
if (!zone) {
|
||||||
|
return this.sendError(res, 'Zone not found', 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const assets: AssetData[] = []
|
||||||
|
const tiles = await TileRepository.getByZoneId(zoneId)
|
||||||
|
|
||||||
|
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 FilterValue<`${string}-${string}-${string}-${string}-${string}`>
|
||||||
|
|
||||||
|
if (!spriteId) {
|
||||||
|
return this.sendError(res, 'Invalid sprite ID', 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sprite = await SpriteRepository.getById(spriteId)
|
||||||
|
if (!sprite) {
|
||||||
|
return this.sendError(res, 'Sprite not found', 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const assets: AssetData[] = sprite.spriteActions.getItems().map((spriteAction) => ({
|
||||||
|
key: sprite.id + '-' + spriteAction.action,
|
||||||
|
data: '/assets/sprites/' + sprite.id + '/' + spriteAction.action + '.png',
|
||||||
|
group: spriteAction.isAnimated ? 'sprite_animations' : 'sprites',
|
||||||
|
updatedAt: sprite.updatedAt,
|
||||||
|
originX: Number(spriteAction.originX.toString()),
|
||||||
|
originY: Number(spriteAction.originY.toString()),
|
||||||
|
isAnimated: spriteAction.isAnimated,
|
||||||
|
frameCount: JSON.parse(JSON.stringify(spriteAction.sprites)).length,
|
||||||
|
frameWidth: spriteAction.frameWidth,
|
||||||
|
frameHeight: spriteAction.frameHeight,
|
||||||
|
frameRate: spriteAction.frameRate
|
||||||
|
}))
|
||||||
|
|
||||||
|
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 ? getPublicPath(type, spriteId, file) : getPublicPath(type, file)
|
||||||
|
|
||||||
|
if (!fs.existsSync(assetPath)) {
|
||||||
|
httpLogger.error(`File not found: ${assetPath}`)
|
||||||
|
return this.sendError(res, 'Asset not found', 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendFile(assetPath, (err) => {
|
||||||
|
if (err) {
|
||||||
|
httpLogger.error('Error sending file:', err)
|
||||||
|
this.sendError(res, 'Error downloading the asset', 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
104
src/http/controllers/auth.ts
Normal file
104
src/http/controllers/auth.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import jwt from 'jsonwebtoken'
|
||||||
|
import { Request, Response } from 'express'
|
||||||
|
import { BaseController } from '#application/base/baseController'
|
||||||
|
import UserService from '#services/userService'
|
||||||
|
import config from '#application/config'
|
||||||
|
import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from '#application/zodTypes'
|
||||||
|
|
||||||
|
export class AuthController extends BaseController {
|
||||||
|
private userService: UserService
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.userService = new UserService()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login user
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async login(req: Request, res: Response) {
|
||||||
|
const { username, password } = req.body
|
||||||
|
|
||||||
|
try {
|
||||||
|
loginAccountSchema.parse({ username, password })
|
||||||
|
const user = await this.userService.login(username, password)
|
||||||
|
|
||||||
|
if (user && typeof user !== 'boolean') {
|
||||||
|
const token = jwt.sign({ id: user.getId() }, config.JWT_SECRET, { expiresIn: '4h' })
|
||||||
|
return this.sendSuccess(res, { token })
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendError(res, 'Invalid credentials')
|
||||||
|
} catch (error: any) {
|
||||||
|
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register user
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async register(req: Request, res: Response) {
|
||||||
|
const { username, email, password } = req.body
|
||||||
|
|
||||||
|
try {
|
||||||
|
registerAccountSchema.parse({ username, email, password })
|
||||||
|
const user = await this.userService.register(username, email, password)
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return this.sendSuccess(res, null, 'User registered successfully')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendError(res, 'Failed to register user')
|
||||||
|
} catch (error: any) {
|
||||||
|
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request password reset
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async requestPasswordReset(req: Request, res: Response) {
|
||||||
|
const { email } = req.body
|
||||||
|
|
||||||
|
try {
|
||||||
|
resetPasswordSchema.parse({ email })
|
||||||
|
const sentEmail = await this.userService.requestPasswordReset(email)
|
||||||
|
|
||||||
|
if (sentEmail) {
|
||||||
|
return this.sendSuccess(res, null, 'Password reset email sent')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendError(res, 'Failed to send password reset request')
|
||||||
|
} catch (error: any) {
|
||||||
|
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset password
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async resetPassword(req: Request, res: Response) {
|
||||||
|
const { urlToken, password } = req.body
|
||||||
|
|
||||||
|
try {
|
||||||
|
newPasswordSchema.parse({ urlToken, password })
|
||||||
|
const resetPassword = await this.userService.resetPassword(urlToken, password)
|
||||||
|
|
||||||
|
if (resetPassword) {
|
||||||
|
return this.sendSuccess(res, null, 'Password has been reset')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.sendError(res, 'Failed to reset password')
|
||||||
|
} catch (error: any) {
|
||||||
|
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
src/http/controllers/avatar.ts
Normal file
85
src/http/controllers/avatar.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
import sharp from 'sharp'
|
||||||
|
import fs from 'fs'
|
||||||
|
import { BaseController } from '#application/base/baseController'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
|
import { getPublicPath } from '#application/storage'
|
||||||
|
|
||||||
|
interface AvatarOptions {
|
||||||
|
characterTypeId: number
|
||||||
|
characterHairId?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AvatarController extends BaseController {
|
||||||
|
/**
|
||||||
|
* Get avatar by character
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async getByName(req: Request, res: Response) {
|
||||||
|
const character = await CharacterRepository.getByName(req.params.characterName)
|
||||||
|
if (!character?.characterType) {
|
||||||
|
return this.sendError(res, 'Character or character type not found', 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.generateAvatar(res, {
|
||||||
|
characterTypeId: character.characterType.id,
|
||||||
|
characterHairId: character.characterHair?.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get avatar by character type and hair
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
public async getByParams(req: Request, res: Response) {
|
||||||
|
return this.generateAvatar(res, {
|
||||||
|
characterTypeId: parseInt(req.params.characterTypeId),
|
||||||
|
characterHairId: req.params.characterHairId ? parseInt(req.params.characterHairId) : undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate avatar
|
||||||
|
* @param res
|
||||||
|
* @param options
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private async generateAvatar(res: Response, options: AvatarOptions) {
|
||||||
|
try {
|
||||||
|
const characterType = await CharacterTypeRepository.getById(options.characterTypeId)
|
||||||
|
if (!characterType?.sprite?.id) {
|
||||||
|
return this.sendError(res, 'Character type not found', 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bodySpritePath = getPublicPath('sprites', characterType.sprite.id, 'idle_right_down.png')
|
||||||
|
if (!fs.existsSync(bodySpritePath)) {
|
||||||
|
return this.sendError(res, 'Body sprite file not found', 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
let avatar = sharp(bodySpritePath).extend({
|
||||||
|
top: 2,
|
||||||
|
bottom: 2,
|
||||||
|
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (options.characterHairId) {
|
||||||
|
const characterHair = await CharacterHairRepository.getById(options.characterHairId)
|
||||||
|
if (characterHair?.sprite?.id) {
|
||||||
|
const hairSpritePath = getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
||||||
|
if (fs.existsSync(hairSpritePath)) {
|
||||||
|
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', 'image/png')
|
||||||
|
return avatar.pipe(res)
|
||||||
|
} catch (error) {
|
||||||
|
return this.sendError(res, 'Error generating avatar', 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
import { Application } from 'express'
|
|
||||||
import { httpLogger } from '#application/logger'
|
|
||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
|
||||||
import { getAppPath } from '#application/storage'
|
|
||||||
|
|
||||||
async function addHttpRoutes(app: Application) {
|
|
||||||
const routeFiles = fs.readdirSync(__dirname).filter((file) => {
|
|
||||||
return file !== 'index.ts' && file !== 'index.js' && (file.endsWith('.ts') || file.endsWith('.js'))
|
|
||||||
})
|
|
||||||
|
|
||||||
for (const file of routeFiles) {
|
|
||||||
const route = await import(getAppPath('http', file))
|
|
||||||
// Use the router directly without additional path prefix
|
|
||||||
app.use('/', route.default)
|
|
||||||
httpLogger.info(`Loaded routes from ${file}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
httpLogger.info('Web routes added')
|
|
||||||
}
|
|
||||||
|
|
||||||
export { addHttpRoutes }
|
|
36
src/http/router.ts
Normal file
36
src/http/router.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Application } from 'express'
|
||||||
|
import { AuthController } from './controllers/auth'
|
||||||
|
import { AvatarController } from './controllers/avatar'
|
||||||
|
import { AssetsController } from './controllers/assets'
|
||||||
|
|
||||||
|
export class HttpRouter {
|
||||||
|
private readonly app: Application
|
||||||
|
private readonly authController: AuthController
|
||||||
|
private readonly avatarController: AvatarController
|
||||||
|
private readonly assetsController: AssetsController
|
||||||
|
|
||||||
|
constructor(app: Application) {
|
||||||
|
this.app = app
|
||||||
|
this.authController = new AuthController()
|
||||||
|
this.avatarController = new AvatarController()
|
||||||
|
this.assetsController = new AssetsController()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async boot() {
|
||||||
|
// Auth routes
|
||||||
|
this.app.post('/login', (req, res) => this.authController.login(req, res))
|
||||||
|
this.app.post('/register', (req, res) => this.authController.register(req, res))
|
||||||
|
this.app.post('/reset-password-request', (req, res) => this.authController.requestPasswordReset(req, res))
|
||||||
|
this.app.post('/reset-password', (req, res) => this.authController.resetPassword(req, res))
|
||||||
|
|
||||||
|
// Avatar routes
|
||||||
|
this.app.get('/avatar/:characterName', (req, res) => this.avatarController.getByName(req, res))
|
||||||
|
this.app.get('/avatar/s/:characterTypeId/:characterHairId?', (req, res) => this.avatarController.getByParams(req, res))
|
||||||
|
|
||||||
|
// Assets routes
|
||||||
|
this.app.get('/assets/list_tiles', (req, res) => this.assetsController.listTiles(req, res))
|
||||||
|
this.app.get('/assets/list_tiles/:zoneId', (req, res) => this.assetsController.listTilesByZone(req, res))
|
||||||
|
this.app.get('/assets/list_sprite_actions/:spriteId', (req, res) => this.assetsController.listSpriteActions(req, res))
|
||||||
|
this.app.get('/assets/:type/:spriteId?/:file', (req, res) => this.assetsController.downloadAsset(req, res))
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +1,55 @@
|
|||||||
import { verify } from 'jsonwebtoken'
|
import { verify } from 'jsonwebtoken'
|
||||||
import { TSocket } from '#application/types'
|
import { TSocket } from '#application/types'
|
||||||
import config from '#application/config'
|
import config from '#application/config'
|
||||||
import UserRepository from '#repositories/userRepository'
|
|
||||||
import { User } from '@prisma/client'
|
|
||||||
import { gameLogger } from '#application/logger'
|
import { gameLogger } from '#application/logger'
|
||||||
|
|
||||||
/**
|
export class SocketAuthenticator {
|
||||||
* Socket io jwt auth middleware
|
private socket: TSocket
|
||||||
* @param socket
|
private readonly next: any
|
||||||
* @param next
|
|
||||||
*/
|
constructor(socket: TSocket, next: any) {
|
||||||
export async function Authentication(socket: TSocket, next: any) {
|
this.socket = socket
|
||||||
if (!socket.request.headers.cookie) {
|
this.next = next
|
||||||
gameLogger.warn('No cookie provided')
|
|
||||||
return next(new Error('Authentication error'))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public async authenticate(): Promise<void> {
|
||||||
* Parse cookies
|
if (!this.socket.request.headers.cookie) {
|
||||||
*/
|
gameLogger.warn('No cookie provided')
|
||||||
const cookies = socket.request.headers.cookie.split('; ').reduce((prev: any, current: any) => {
|
return this.next(new Error('Authentication error'))
|
||||||
const [name, value] = current.split('=')
|
}
|
||||||
prev[name] = value
|
|
||||||
return prev
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
const token = cookies['token']
|
const token = this.parseCookies()['token']
|
||||||
|
|
||||||
/**
|
if (!token) {
|
||||||
* Verify token, if valid, set user on socket and continue
|
gameLogger.warn('No token provided')
|
||||||
*/
|
return this.next(new Error('Authentication error'))
|
||||||
if (token) {
|
}
|
||||||
verify(token, config.JWT_SECRET, async (err: any, decoded: any) => {
|
|
||||||
|
this.verifyToken(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseCookies(): Record<string, string> {
|
||||||
|
return this.socket.request.headers.cookie.split('; ').reduce((prev: any, current: any) => {
|
||||||
|
const [name, value] = current.split('=')
|
||||||
|
prev[name] = value
|
||||||
|
return prev
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
private verifyToken(token: string): void {
|
||||||
|
verify(token, config.JWT_SECRET, (err: any, decoded: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
gameLogger.error('Invalid token')
|
gameLogger.error('Invalid token')
|
||||||
return next(new Error('Authentication error'))
|
return this.next(new Error('Authentication error'))
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.userId = decoded.id
|
this.socket.userId = decoded.id
|
||||||
next()
|
this.next()
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
gameLogger.warn('No token provided')
|
|
||||||
return next(new Error('Authentication error'))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function Authentication(socket: TSocket, next: any) {
|
||||||
|
const authenticator = new SocketAuthenticator(socket, next)
|
||||||
|
await authenticator.authenticate()
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { CharacterHair } from '#entities/characterHair'
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
|
|
||||||
class CharacterHairRepository extends BaseRepository {
|
class CharacterHairRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { Character } from '#entities/character'
|
import { Character } from '#entities/character'
|
||||||
|
|
||||||
class CharacterRepository extends BaseRepository {
|
class CharacterRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { CharacterType } from '#entities/characterType'
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
|
||||||
class CharacterTypeRepository extends BaseRepository {
|
class CharacterTypeRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { Chat } from '#entities/chat'
|
import { Chat } from '#entities/chat'
|
||||||
|
|
||||||
class ChatRepository extends BaseRepository {
|
class ChatRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { Item } from '#entities/item'
|
import { Item } from '#entities/item'
|
||||||
|
|
||||||
class ItemRepository extends BaseRepository {
|
class ItemRepository extends BaseRepository {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
|
|
||||||
class ObjectRepository extends BaseRepository {
|
class ObjectRepository extends BaseRepository {
|
||||||
async getById(id: string): Promise<any> {
|
async getById(id: string): Promise<any> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository' // Import the global Prisma instance
|
import { BaseRepository } from '#application/base/baseRepository' // Import the global Prisma instance
|
||||||
import { PasswordResetToken } from '#entities/passwordResetToken'
|
import { PasswordResetToken } from '#entities/passwordResetToken'
|
||||||
|
|
||||||
class PasswordResetTokenRepository extends BaseRepository {
|
class PasswordResetTokenRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FilterValue } from '@mikro-orm/core'
|
import { FilterValue } from '@mikro-orm/core'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { Sprite } from '#entities/sprite'
|
import { Sprite } from '#entities/sprite'
|
||||||
|
|
||||||
class SpriteRepository extends BaseRepository {
|
class SpriteRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FilterValue } from '@mikro-orm/core'
|
import { FilterValue } from '@mikro-orm/core'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { Tile } from '#entities/tile'
|
import { Tile } from '#entities/tile'
|
||||||
import { Zone } from '#entities/zone'
|
import { Zone } from '#entities/zone'
|
||||||
import { unduplicateArray } from '#application/utilities'
|
import { unduplicateArray } from '#application/utilities'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { User } from '#entities/user'
|
import { User } from '#entities/user'
|
||||||
|
|
||||||
class UserRepository extends BaseRepository {
|
class UserRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { gameLogger } from '#application/logger'
|
import { gameLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { World } from '#entities/world'
|
import { World } from '#entities/world'
|
||||||
|
|
||||||
class WorldRepository extends BaseRepository {
|
class WorldRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { ZoneEventTile } from '#entities/zoneEventTile'
|
import { ZoneEventTile } from '#entities/zoneEventTile'
|
||||||
|
|
||||||
class ZoneEventTileRepository extends BaseRepository {
|
class ZoneEventTileRepository extends BaseRepository {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { appLogger } from '#application/logger'
|
import { appLogger } from '#application/logger'
|
||||||
import { BaseRepository } from '#application/bases/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { ZoneEventTile } from '#entities/zoneEventTile'
|
import { ZoneEventTile } from '#entities/zoneEventTile'
|
||||||
import { ZoneObject } from '#entities/zoneObject'
|
import { ZoneObject } from '#entities/zoneObject'
|
||||||
import { Zone } from '#entities/zone'
|
import { Zone } from '#entities/zone'
|
||||||
|
@ -3,7 +3,6 @@ import express, { Application } from 'express'
|
|||||||
import config from '#application/config'
|
import config from '#application/config'
|
||||||
import { getAppPath } from '#application/storage'
|
import { getAppPath } from '#application/storage'
|
||||||
import { createServer as httpServer, Server as HTTPServer } from 'http'
|
import { createServer as httpServer, Server as HTTPServer } from 'http'
|
||||||
import { addHttpRoutes } from './http'
|
|
||||||
import cors from 'cors'
|
import cors from 'cors'
|
||||||
import { Server as SocketServer } from 'socket.io'
|
import { Server as SocketServer } from 'socket.io'
|
||||||
import { Authentication } from '#middleware/authentication'
|
import { Authentication } from '#middleware/authentication'
|
||||||
@ -16,6 +15,7 @@ import CommandManager from '#managers/commandManager'
|
|||||||
import QueueManager from '#managers/queueManager'
|
import QueueManager from '#managers/queueManager'
|
||||||
import DateManager from '#managers/dateManager'
|
import DateManager from '#managers/dateManager'
|
||||||
import WeatherManager from '#managers/weatherManager'
|
import WeatherManager from '#managers/weatherManager'
|
||||||
|
import { HttpRouter } from '#http/router'
|
||||||
|
|
||||||
export class Server {
|
export class Server {
|
||||||
private readonly app: Application
|
private readonly app: Application
|
||||||
@ -64,8 +64,9 @@ export class Server {
|
|||||||
appLogger.error(`Socket.IO failed to start: ${error.message}`)
|
appLogger.error(`Socket.IO failed to start: ${error.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add http API routes
|
// Load HTTP routes
|
||||||
await addHttpRoutes(this.app)
|
const httpRouter = new HttpRouter(this.app)
|
||||||
|
await httpRouter.boot()
|
||||||
|
|
||||||
// Load queue manager
|
// Load queue manager
|
||||||
await QueueManager.boot(this.io)
|
await QueueManager.boot(this.io)
|
||||||
@ -77,7 +78,7 @@ export class Server {
|
|||||||
// await DateManager.boot(this.io)
|
// await DateManager.boot(this.io)
|
||||||
|
|
||||||
// Load weather manager
|
// Load weather manager
|
||||||
await WeatherManager.boot(this.io)
|
// await WeatherManager.boot(this.io)
|
||||||
|
|
||||||
// Load zoneEditor manager
|
// Load zoneEditor manager
|
||||||
await ZoneManager.boot()
|
await ZoneManager.boot()
|
||||||
|
@ -65,15 +65,19 @@ export default class CharacterMove {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
this.characterService.updatePosition(character, end)
|
// Update position first
|
||||||
// Send minimal data
|
character.positionX = end.x
|
||||||
|
character.positionY = end.y
|
||||||
|
|
||||||
|
// Then emit with the same properties
|
||||||
this.io.in(character.zone!.id.toString()).emit('character:move', {
|
this.io.in(character.zone!.id.toString()).emit('character:move', {
|
||||||
id: character.id,
|
id: character.id,
|
||||||
positionX: end.x,
|
positionX: character.positionX,
|
||||||
positionY: end.y,
|
positionY: character.positionY,
|
||||||
rotation: character.rotation,
|
rotation: character.rotation,
|
||||||
isMoving: true
|
isMoving: true
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.characterService.applyMovementDelay()
|
await this.characterService.applyMovementDelay()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user