OOP is my passion ( ͡° ͜ʖ ͡°)

This commit is contained in:
Dennis Postma 2025-01-04 18:35:53 +01:00
parent 0b4420f956
commit 067976c54a
46 changed files with 165 additions and 164 deletions

View File

@ -6,8 +6,12 @@ import Logger, { LoggerType } from '#application/logger'
export abstract class BaseRepository {
protected readonly logger = Logger.type(LoggerType.REPOSITORY)
private entityManager?: EntityManager
protected get em(): EntityManager {
return Database.getEntityManager()
getEntityManager(): EntityManager {
if (!this.entityManager) {
this.entityManager = Database.getORM().em.fork()
}
return this.entityManager
}
}

View File

@ -10,7 +10,7 @@ class Database {
public static async initialize(): Promise<void> {
try {
Database.orm = await MikroORM.init(config)
this.orm = await MikroORM.init(config)
this.logger.info('Database connection initialized')
} catch (error) {
this.logger.error(`MikroORM connection failed: ${error}`)
@ -18,8 +18,8 @@ class Database {
}
}
public static getEntityManager(): EntityManager {
return Database.orm.em.fork()
public static getORM(): MikroORM {
return this.orm
}
}

View File

@ -9,13 +9,13 @@ import { UUID } from '#application/types'
import { Character } from '#entities/character'
import { CharacterHair } from '#entities/characterHair'
import { CharacterType } from '#entities/characterType'
import { Map } from '#entities/map'
import { MapEffect } from '#entities/mapEffect'
import { MapObject } from '#entities/mapObject'
import { Sprite } from '#entities/sprite'
import { SpriteAction } from '#entities/spriteAction'
import { Tile } from '#entities/tile'
import { User } from '#entities/user'
import { Map } from '#entities/map'
import { MapEffect } from '#entities/mapEffect'
import CharacterHairRepository from '#repositories/characterHairRepository'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import MapRepository from '#repositories/mapRepository'
@ -24,6 +24,10 @@ import MapRepository from '#repositories/mapRepository'
// https://mikro-orm.io/docs/seeding
export default class InitCommand extends BaseCommand {
private readonly mapRepository = new MapRepository()
private readonly characterTypeRepository = new CharacterTypeRepository()
private readonly characterHairRepository = new CharacterHairRepository()
public async execute(): Promise<void> {
// Assets
await this.importTiles()
@ -247,9 +251,9 @@ export default class InitCommand extends BaseCommand {
.setUser(user)
.setName('root')
.setRole('gm')
.setMap((await MapRepository.getFirst())!)
.setCharacterType((await CharacterTypeRepository.getFirst()) ?? undefined)
.setCharacterHair((await CharacterHairRepository.getFirst()) ?? undefined)
.setMap((await this.mapRepository.getFirst())!)
.setCharacterType((await this.characterTypeRepository.getFirst()) ?? undefined)
.setCharacterHair((await this.characterHairRepository.getFirst()) ?? undefined)
.save()
}
}

View File

@ -1,5 +1,3 @@
import { Server } from 'socket.io'
import { BaseCommand } from '#application/base/baseCommand'
import MapManager from '#managers/mapManager'

View File

@ -7,8 +7,8 @@ import { CharacterHair } from './characterHair'
import { CharacterItem } from './characterItem'
import { CharacterType } from './characterType'
import { Chat } from './chat'
import { User } from './user'
import { Map } from './map'
import { User } from './user'
import { BaseEntity } from '#application/base/baseEntity'
import { UUID } from '#application/types'

View File

@ -29,7 +29,7 @@ export class Item extends BaseEntity {
@Enum(() => ItemRarity)
rarity: ItemRarity = ItemRarity.COMMON
@ManyToOne(() => Sprite, )
@ManyToOne(() => Sprite)
sprite?: Sprite
@Property()

View File

@ -1,6 +1,6 @@
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterHair } from '#entities/characterHair'
import characterHairRepository from '#repositories/characterHairRepository'
import CharacterHairRepository from '#repositories/characterHairRepository'
interface IPayload {}
@ -11,6 +11,7 @@ export default class characterHairListEvent extends BaseEvent {
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
try {
const characterHairRepository = new CharacterHairRepository()
const items: CharacterHair[] = await characterHairRepository.getAllSelectable(['sprite'])
return callback(items)

View File

@ -11,6 +11,9 @@ interface CharacterConnectPayload {
}
export default class CharacterConnectEvent extends BaseEvent {
private readonly characterHairRepository = new CharacterHairRepository()
private readonly characterRepository = new CharacterRepository()
public listen(): void {
this.socket.on('character:connect', this.handleEvent.bind(this))
}
@ -22,10 +25,7 @@ export default class CharacterConnectEvent extends BaseEvent {
return
}
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, data.characterId, [
'characterType',
'characterHair'
])
const character = await this.characterRepository.getByUserAndId(this.socket.userId!, data.characterId, ['characterType', 'characterHair'])
if (!character) {
this.emitError('Character not found or does not belong to this user')
@ -37,7 +37,7 @@ export default class CharacterConnectEvent extends BaseEvent {
// Set character hair
if (data.characterHairId !== undefined && data.characterHairId !== null) {
const characterHair = await CharacterHairRepository.getById(data.characterHairId)
const characterHair = await this.characterHairRepository.getById(data.characterHairId)
await character.setCharacterHair(characterHair).update()
}
@ -61,7 +61,7 @@ export default class CharacterConnectEvent extends BaseEvent {
}
private async checkForActiveCharacters(): Promise<boolean> {
const characters = await CharacterRepository.getByUserId(this.socket.userId!)
const characters = await this.characterRepository.getByUserId(this.socket.userId!)
return characters?.some((char) => MapManager.getCharacterById(char.id)) ?? false
}
}

View File

@ -4,8 +4,8 @@ import { BaseEvent } from '#application/base/baseEvent'
import { ZCharacterCreate } from '#application/zodTypes'
import { Character } from '#entities/character'
import CharacterRepository from '#repositories/characterRepository'
import UserRepository from '#repositories/userRepository'
import MapRepository from '#repositories/mapRepository'
import UserRepository from '#repositories/userRepository'
export default class CharacterCreateEvent extends BaseEvent {
public listen(): void {
@ -17,27 +17,31 @@ export default class CharacterCreateEvent extends BaseEvent {
try {
data = ZCharacterCreate.parse(data)
const user = await UserRepository.getById(this.socket.userId!)
const userRepository = new UserRepository()
const characterRepository = new CharacterRepository()
const mapRepository = new MapRepository()
const user = await userRepository.getById(this.socket.userId!)
if (!user) {
return this.socket.emit('notification', { message: 'User not found' })
}
// Check if character name already exists
const characterExists = await CharacterRepository.getByName(data.name)
const characterExists = await characterRepository.getByName(data.name)
if (characterExists) {
return this.socket.emit('notification', { message: 'Character name already exists' })
}
let characters: Character[] = await CharacterRepository.getByUserId(user.getId())
let characters: Character[] = await characterRepository.getByUserId(user.getId())
if (characters.length >= 4) {
return this.socket.emit('notification', { message: 'You can only have 4 characters' })
}
// @TODO: Change to default location
const map = await MapRepository.getFirst()
const map = await mapRepository.getFirst()
const newCharacter = new Character()
await newCharacter.setName(data.name).setUser(user).setMap(map!).save()

View File

@ -9,11 +9,12 @@ export default class CharacterListEvent extends BaseEvent {
private async handleEvent(data: any): Promise<void> {
try {
let characters: Character[] = await CharacterRepository.getByUserId(this.socket.userId!, [
'characterType',
'characterHair'
])
const characterRepository = new CharacterRepository()
let characters: Character[] = await characterRepository.getByUserId(this.socket.userId!)
await characterRepository.getEntityManager().populate(characters, ['characterType', 'characterHair'])
console.log(characters)
this.socket.emit('character:list', characters)
} catch (error: any) {
this.logger.error('character:list error', error.message)

View File

@ -1,6 +1,6 @@
import { BaseEvent } from '#application/base/baseEvent'
import CharacterHairRepository from '#repositories/characterHairRepository'
import { UUID } from '#application/types'
import CharacterHairRepository from '#repositories/characterHairRepository'
interface IPayload {
id: UUID

View File

@ -29,7 +29,7 @@ export default class CharacterHairUpdateEvent extends BaseEvent {
return callback(false)
}
await characterHair.setName(data.name).setGender(data.gender).setIsSelectable(data.isSelectable).setSprite(sprite!).update()
await characterHair.setName(data.name).setGender(data.gender).setIsSelectable(data.isSelectable).setSprite(sprite).update()
return callback(true)
} catch (error) {
this.logger.error(`Error updating character hair: ${error instanceof Error ? error.message : String(error)}`)

View File

@ -1,6 +1,6 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import { BaseEvent } from '#application/base/baseEvent'
interface IPayload {
id: UUID

View File

@ -1,6 +1,6 @@
import CharacterTypeRepository from '#repositories/characterTypeRepository'
import { BaseEvent } from '#application/base/baseEvent'
import { CharacterType } from '#entities/characterType'
import CharacterTypeRepository from '#repositories/characterTypeRepository'
interface IPayload {}

View File

@ -1,6 +1,6 @@
import ObjectRepository from '#repositories/mapObjectRepository'
import { BaseEvent } from '#application/base/baseEvent'
import { MapObject } from '#entities/mapObject'
import ObjectRepository from '#repositories/mapObjectRepository'
interface IPayload {}

View File

@ -1,8 +1,9 @@
import fs from 'fs'
import Storage from '#application/storage'
import { BaseEvent } from '#application/base/baseEvent'
import MapObjectRepository from '#repositories/mapObjectRepository'
import Storage from '#application/storage'
import { UUID } from '#application/types'
import MapObjectRepository from '#repositories/mapObjectRepository'
interface IPayload {
mapObjectId: UUID

View File

@ -1,5 +1,5 @@
import { UUID } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapObjectRepository from '#repositories/mapObjectRepository'
type Payload = {
@ -26,16 +26,7 @@ export default class MapObjectUpdateEvent extends BaseEvent {
const mapObject = await MapObjectRepository.getById(data.id)
if (!mapObject) return callback(false)
await mapObject
.setName(data.name)
.setTags(data.tags)
.setOriginX(data.originX)
.setOriginY(data.originY)
.setIsAnimated(data.isAnimated)
.setFrameRate(data.frameRate)
.setFrameWidth(data.frameWidth)
.setFrameHeight(data.frameHeight)
.update()
await mapObject.setName(data.name).setTags(data.tags).setOriginX(data.originX).setOriginY(data.originY).setIsAnimated(data.isAnimated).setFrameRate(data.frameRate).setFrameWidth(data.frameWidth).setFrameHeight(data.frameHeight).update()
return callback(true)
} catch (error) {

View File

@ -2,8 +2,9 @@ import fs from 'fs/promises'
import { writeFile } from 'node:fs/promises'
import sharp from 'sharp'
import Storage from '#application/storage'
import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage'
import { MapObject } from '#entities/mapObject'
interface IObjectData {

View File

@ -1,8 +1,8 @@
import { BaseEvent } from '#application/base/baseEvent'
import Database from '#application/database'
import { UUID } from '#application/types'
import { Map } from '#entities/map'
import MapRepository from '#repositories/mapRepository'
import Database from '#application/database'
interface IPayload {
mapId: UUID
@ -26,14 +26,13 @@ export default class MapRequestEvent extends BaseEvent {
const map = await MapRepository.getById(data.mapId)
if (!map) {
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map ${data.mapId} but it does not exist.`)
return callback(null)
}
console.log(map)
await Database.getEntityManager().populate(map, ['mapEventTiles', 'placedMapObjects'])
// await Database.getEntityManager().populate(map, ['mapEventTiles', 'placedMapObjects'])
return callback(map)
} catch (error: any) {

View File

@ -5,9 +5,9 @@ import { Map } from '#entities/map'
import { MapEffect } from '#entities/mapEffect'
import { MapEventTile } from '#entities/mapEventTile'
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
import { PlacedMapObject } from '#entities/placedMapObject'
import mapManager from '#managers/mapManager'
import MapRepository from '#repositories/mapRepository'
import { PlacedMapObject } from '#entities/placedMapObject'
interface IPayload {
mapId: UUID
@ -83,7 +83,7 @@ export default class MapUpdateEvent extends BaseEvent {
if (tile.teleport) {
const teleport = new MapEventTileTeleport()
.setToMap((await MapRepository.getById(tile.teleport.toMapId))!)
.setToMap(await MapRepository.getById(tile.teleport.toMapId))
.setToPositionX(tile.teleport.toPositionX)
.setToPositionY(tile.teleport.toPositionY)
.setToRotation(tile.teleport.toRotation)

View File

@ -6,14 +6,15 @@ export default class LoginEvent extends BaseEvent {
this.socket.on('login', this.handleEvent.bind(this))
}
private handleEvent(): void {
private async handleEvent() {
try {
if (!this.socket.userId) {
this.logger.warn('Login attempt without user data')
return
}
this.socket.emit('logged_in', { user: UserRepository.getById(this.socket.userId) })
const userRepository = new UserRepository()
this.socket.emit('logged_in', { user: userRepository.getById(this.socket.userId) })
this.logger.info(`User logged in: ${this.socket.userId}`)
} catch (error: any) {
this.logger.error('login error: ' + error.message)

View File

@ -51,11 +51,7 @@ export default class CharacterMove extends BaseEvent {
const [start, end] = [path[i], path[i + 1]]
character.rotation = CharacterService.calculateRotation(start.x, start.y, end.x, end.y)
const zoneEventTile = await zoneEventTileRepository.getEventTileByZoneIdAndPosition(
character.zone.id,
Math.floor(end.x),
Math.floor(end.y)
)
const zoneEventTile = await zoneEventTileRepository.getEventTileByZoneIdAndPosition(character.zone.id, Math.floor(end.x), Math.floor(end.y))
if (zoneEventTile?.type === 'BLOCK') break
if (zoneEventTile?.type === 'TELEPORT' && zoneEventTile.teleport) {

View File

@ -6,11 +6,15 @@ 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'
import TileRepository from '#repositories/tileRepository'
import MapRepository from '#repositories/mapRepository'
export class AssetsController extends BaseController {
private readonly mapRepository = new MapRepository()
private readonly spriteRepository = new SpriteRepository()
private readonly tileRepository = new TileRepository()
/**
* List tiles
* @param req
@ -18,7 +22,7 @@ export class AssetsController extends BaseController {
*/
public async listTiles(req: Request, res: Response) {
const assets: AssetData[] = []
const tiles = await TileRepository.getAll()
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)
@ -39,13 +43,13 @@ export class AssetsController extends BaseController {
return this.sendError(res, 'Invalid map ID', 400)
}
const map = await MapRepository.getById(mapId)
const map = await this.mapRepository.getById(mapId)
if (!map) {
return this.sendError(res, 'Map not found', 404)
}
const assets: AssetData[] = []
const tiles = await TileRepository.getByMapId(mapId)
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)
@ -66,12 +70,12 @@ export class AssetsController extends BaseController {
return this.sendError(res, 'Invalid sprite ID', 400)
}
const sprite = await SpriteRepository.getById(spriteId)
const sprite = await this.spriteRepository.getById(spriteId)
if (!sprite) {
return this.sendError(res, 'Sprite not found', 404)
}
await Database.getEntityManager().populate(sprite, ['spriteActions'])
// await Database.getEntityManager().populate(sprite, ['spriteActions'])
const assets: AssetData[] = sprite.spriteActions.getItems().map((spriteAction) => ({
key: sprite.getId() + '-' + spriteAction.getAction(),

View File

@ -16,13 +16,17 @@ interface AvatarOptions {
}
export class AvatarController extends BaseController {
private readonly characterTypeRepository = new CharacterTypeRepository()
private readonly characterHairRepository = new CharacterHairRepository()
private readonly characterRepository = new CharacterRepository()
/**
* Get avatar by character
* @param req
* @param res
*/
public async getByName(req: Request, res: Response) {
const character = await CharacterRepository.getByName(req.params.characterName)
const character = await this.characterRepository.getByName(req.params.characterName)
if (!character?.characterType) {
return this.sendError(res, 'Character or character type not found', 404)
}
@ -53,7 +57,7 @@ export class AvatarController extends BaseController {
*/
private async generateAvatar(res: Response, options: AvatarOptions) {
try {
const characterType = await CharacterTypeRepository.getById(options.characterTypeId)
const characterType = await this.characterTypeRepository.getById(options.characterTypeId)
if (!characterType?.sprite?.id) {
return this.sendError(res, 'Character type not found', 404)
}
@ -70,7 +74,7 @@ export class AvatarController extends BaseController {
})
if (options.characterHairId) {
const characterHair = await CharacterHairRepository.getById(options.characterHairId)
const characterHair = await this.characterHairRepository.getById(options.characterHairId)
if (characterHair?.sprite?.id) {
const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png')
if (fs.existsSync(hairSpritePath)) {

View File

@ -1,5 +1,3 @@
import { Server } from 'socket.io'
import { CommandRegistry } from '#application/console/commandRegistry'
import { ConsolePrompt } from '#application/console/consolePrompt'
import { LogReader } from '#application/console/logReader'

View File

@ -2,8 +2,8 @@ import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger'
import SocketManager from '#managers/socketManager'
import worldRepository from '#repositories/worldRepository'
import worldService from '#services/worldService'
import WorldRepository from '#repositories/worldRepository'
import WorldService from '#services/worldService'
class DateManager {
private static readonly CONFIG = {
@ -47,6 +47,7 @@ class DateManager {
private async loadDate(): Promise<void> {
try {
const worldRepository = new WorldRepository()
const world = await worldRepository.getFirst()
this.currentDate = world?.date ?? new Date()
} catch (error) {
@ -88,7 +89,7 @@ class DateManager {
private async saveDate(): Promise<void> {
try {
await worldService.update({ date: this.currentDate })
await WorldService.update({ date: this.currentDate })
} catch (error) {
this.handleError('Failed to save date', error)
}

View File

@ -10,7 +10,8 @@ class MapManager {
private logger = Logger.type(LoggerType.GAME)
public async boot(): Promise<void> {
const maps = await MapRepository.getAll()
const mapRepository = new MapRepository()
const maps = await mapRepository.getAll()
await Promise.all(maps.map((map) => this.loadMap(map)))
this.logger.info(`Map manager loaded with ${Object.keys(this.maps).length} maps`)
@ -47,4 +48,4 @@ class MapManager {
}
}
export default new MapManager()
export default new MapManager()

View File

@ -2,8 +2,8 @@ import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger'
import SocketManager from '#managers/socketManager'
import worldRepository from '#repositories/worldRepository'
import worldService from '#services/worldService'
import WorldRepository from '#repositories/worldRepository'
import WorldService from '#services/worldService'
type WeatherState = {
isRainEnabled: boolean
@ -59,6 +59,7 @@ class WeatherManager {
private async loadWeather(): Promise<void> {
try {
const worldRepository = new WorldRepository()
const world = await worldRepository.getFirst()
if (world) {
this.weatherState = {
@ -112,7 +113,7 @@ class WeatherManager {
private async saveWeather(): Promise<void> {
try {
await worldService.update(this.weatherState)
await WorldService.update(this.weatherState)
} catch (error) {
this.logError('save', error)
}

View File

@ -2,8 +2,8 @@ import { Server } from 'socket.io'
import { TSocket } from '#application/types'
import { Character } from '#entities/character'
import SocketManager from '#managers/socketManager'
import MapManager from '#managers/mapManager'
import SocketManager from '#managers/socketManager'
import TeleportService from '#services/teleportService'
class MapCharacter {

View File

@ -5,7 +5,7 @@ import { CharacterHair } from '#entities/characterHair'
class CharacterHairRepository extends BaseRepository {
async getFirst() {
try {
const repository = this.em.getRepository(CharacterHair)
const repository = this.getEntityManager().getRepository(CharacterHair)
return await repository.findOne({ id: { $exists: true } })
} catch (error: any) {
this.logger.error(`Failed to get first character hair: ${error instanceof Error ? error.message : String(error)}`)
@ -15,7 +15,7 @@ class CharacterHairRepository extends BaseRepository {
async getAll(): Promise<CharacterHair[]> {
try {
const repository = this.em.getRepository(CharacterHair)
const repository = this.getEntityManager().getRepository(CharacterHair)
return await repository.findAll()
} catch (error: any) {
this.logger.error(`Failed to get all character hair: ${error instanceof Error ? error.message : String(error)}`)
@ -25,7 +25,7 @@ class CharacterHairRepository extends BaseRepository {
async getAllSelectable(populate?: any): Promise<CharacterHair[]> {
try {
const repository = this.em.getRepository(CharacterHair)
const repository = this.getEntityManager().getRepository(CharacterHair)
return await repository.find({ isSelectable: true }, { populate })
} catch (error: any) {
this.logger.error(`Failed to get selectable character hair: ${error instanceof Error ? error.message : String(error)}`)
@ -35,7 +35,7 @@ class CharacterHairRepository extends BaseRepository {
async getById(id: UUID): Promise<CharacterHair | null> {
try {
const repository = this.em.getRepository(CharacterHair)
const repository = this.getEntityManager().getRepository(CharacterHair)
return await repository.findOne({ id })
} catch (error: any) {
this.logger.error(`Failed to get character hair by ID: ${error instanceof Error ? error.message : String(error)}`)
@ -44,4 +44,4 @@ class CharacterHairRepository extends BaseRepository {
}
}
export default new CharacterHairRepository()
export default CharacterHairRepository

View File

@ -3,20 +3,20 @@ import { UUID } from '#application/types'
import { Character } from '#entities/character'
class CharacterRepository extends BaseRepository {
async getByUserId(userId: UUID, populate?: any): Promise<Character[]> {
async getByUserId(userId: UUID): Promise<Character[]> {
try {
const repository = this.em.getRepository(Character)
return await repository.find({ user: userId }, { populate })
const repository = this.getEntityManager().getRepository(Character)
return await repository.find({ user: userId })
} catch (error: any) {
this.logger.error(`Failed to get character by user ID: ${error instanceof Error ? error.message : String(error)}`)
return []
}
}
async getByUserAndId(userId: UUID, characterId: UUID, populate?: any): Promise<Character | null> {
async getByUserAndId(userId: UUID, characterId: UUID): Promise<Character | null> {
try {
const repository = this.em.getRepository(Character)
return await repository.findOne({ user: userId, id: characterId }, { populate })
const repository = this.getEntityManager().getRepository(Character)
return await repository.findOne({ user: userId, id: characterId })
} catch (error: any) {
this.logger.error(`Failed to get character by user ID and character ID: ${error instanceof Error ? error.message : String(error)}`)
return null
@ -25,7 +25,7 @@ class CharacterRepository extends BaseRepository {
async getById(id: UUID, populate?: any): Promise<Character | null> {
try {
const repository = this.em.getRepository(Character)
const repository = this.getEntityManager().getRepository(Character)
return await repository.findOne({ id }, { populate })
} catch (error: any) {
this.logger.error(`Failed to get character by ID: ${error instanceof Error ? error.message : String(error)}`)
@ -35,7 +35,7 @@ class CharacterRepository extends BaseRepository {
async getByName(name: string, populate?: any): Promise<Character | null> {
try {
const repository = this.em.getRepository(Character)
const repository = this.getEntityManager().getRepository(Character)
return await repository.findOne({ name }, { populate })
} catch (error: any) {
this.logger.error(`Failed to get character by name: ${error instanceof Error ? error.message : String(error)}`)
@ -44,4 +44,4 @@ class CharacterRepository extends BaseRepository {
}
}
export default new CharacterRepository()
export default CharacterRepository

View File

@ -5,7 +5,7 @@ import { CharacterType } from '#entities/characterType'
class CharacterTypeRepository extends BaseRepository {
async getFirst() {
try {
const repository = this.em.getRepository(CharacterType)
const repository = this.getEntityManager().getRepository(CharacterType)
return await repository.findOne({ id: { $exists: true } })
} catch (error: any) {
this.logger.error(`Failed to get first character type: ${error instanceof Error ? error.message : String(error)}`)
@ -15,7 +15,7 @@ class CharacterTypeRepository extends BaseRepository {
async getAll() {
try {
const repository = this.em.getRepository(CharacterType)
const repository = this.getEntityManager().getRepository(CharacterType)
return await repository.findAll()
} catch (error: any) {
this.logger.error(`Failed to get all character types: ${error instanceof Error ? error.message : String(error)}`)
@ -25,7 +25,7 @@ class CharacterTypeRepository extends BaseRepository {
async getById(id: UUID) {
try {
const repository = this.em.getRepository(CharacterType)
const repository = this.getEntityManager().getRepository(CharacterType)
return await repository.findOne({ id })
} catch (error: any) {
this.logger.error(`Failed to get character type by ID: ${error instanceof Error ? error.message : String(error)}`)
@ -34,4 +34,4 @@ class CharacterTypeRepository extends BaseRepository {
}
}
export default new CharacterTypeRepository()
export default CharacterTypeRepository

View File

@ -5,7 +5,7 @@ import { Chat } from '#entities/chat'
class ChatRepository extends BaseRepository {
async getById(id: UUID): Promise<Chat[]> {
try {
const repository = this.em.getRepository(Chat)
const repository = this.getEntityManager().getRepository(Chat)
return await repository.find({
id
})
@ -17,7 +17,7 @@ class ChatRepository extends BaseRepository {
async getAll(): Promise<Chat[]> {
try {
const repository = this.em.getRepository(Chat)
const repository = this.getEntityManager().getRepository(Chat)
return await repository.findAll()
} catch (error: any) {
this.logger.error(`Failed to get all chats: ${error instanceof Error ? error.message : String(error)}`)
@ -27,7 +27,7 @@ class ChatRepository extends BaseRepository {
async getByCharacterId(characterId: UUID): Promise<Chat[]> {
try {
const repository = this.em.getRepository(Chat)
const repository = this.getEntityManager().getRepository(Chat)
return await repository.find({ character: characterId })
} catch (error: any) {
this.logger.error(`Failed to get chats by character ID: ${error instanceof Error ? error.message : String(error)}`)
@ -37,7 +37,7 @@ class ChatRepository extends BaseRepository {
async getByMapId(mapId: UUID): Promise<Chat[]> {
try {
const repository = this.em.getRepository(Chat)
const repository = this.getEntityManager().getRepository(Chat)
return await repository.find({ map: mapId })
} catch (error: any) {
this.logger.error(`Failed to get chats by map ID: ${error instanceof Error ? error.message : String(error)}`)
@ -46,4 +46,4 @@ class ChatRepository extends BaseRepository {
}
}
export default new ChatRepository()
export default ChatRepository

View File

@ -5,7 +5,7 @@ import { Item } from '#entities/item'
class ItemRepository extends BaseRepository {
async getById(id: UUID): Promise<any> {
try {
const repository = this.em.getRepository(Item)
const repository = this.getEntityManager().getRepository(Item)
return await repository.findOne({ id })
} catch (error: any) {
this.logger.error(`Failed to get item by ID: ${error instanceof Error ? error.message : String(error)}`)
@ -15,7 +15,7 @@ class ItemRepository extends BaseRepository {
async getByIds(ids: UUID[]): Promise<any> {
try {
const repository = this.em.getRepository(Item)
const repository = this.getEntityManager().getRepository(Item)
return await repository.find({
id: ids
})
@ -27,7 +27,7 @@ class ItemRepository extends BaseRepository {
async getAll(): Promise<any> {
try {
const repository = this.em.getRepository(Item)
const repository = this.getEntityManager().getRepository(Item)
return await repository.findAll()
} catch (error: any) {
this.logger.error(`Failed to get all items: ${error instanceof Error ? error.message : String(error)}`)
@ -36,4 +36,4 @@ class ItemRepository extends BaseRepository {
}
}
export default new ItemRepository()
export default ItemRepository

View File

@ -5,7 +5,7 @@ import { MapEventTile } from '#entities/mapEventTile'
class MapEventTileRepository extends BaseRepository {
async getAll(id: UUID): Promise<MapEventTile[]> {
try {
const repository = this.em.getRepository(MapEventTile)
const repository = this.getEntityManager().getRepository(MapEventTile)
return await repository.find({
map: id
})
@ -17,7 +17,7 @@ class MapEventTileRepository extends BaseRepository {
async getEventTileByMapIdAndPosition(mapId: UUID, positionX: number, positionY: number) {
try {
const repository = this.em.getRepository(MapEventTile)
const repository = this.getEntityManager().getRepository(MapEventTile)
return await repository.findOne({
map: mapId,
positionX: positionX,
@ -30,4 +30,4 @@ class MapEventTileRepository extends BaseRepository {
}
}
export default new MapEventTileRepository()
export default MapEventTileRepository

View File

@ -5,7 +5,7 @@ import { MapObject } from '#entities/mapObject'
class MapObjectRepository extends BaseRepository {
async getById(id: UUID): Promise<MapObject | null> {
try {
const repository = this.em.getRepository(MapObject)
const repository = this.getEntityManager().getRepository(MapObject)
return await repository.findOne({ id })
} catch (error: any) {
return null
@ -14,7 +14,7 @@ class MapObjectRepository extends BaseRepository {
async getAll(): Promise<MapObject[]> {
try {
const repository = this.em.getRepository(MapObject)
const repository = this.getEntityManager().getRepository(MapObject)
return await repository.findAll()
} catch (error: any) {
return []
@ -22,4 +22,4 @@ class MapObjectRepository extends BaseRepository {
}
}
export default new MapObjectRepository()
export default MapObjectRepository

View File

@ -7,7 +7,7 @@ import { MapObject } from '#entities/mapObject'
class MapRepository extends BaseRepository {
async getFirst(): Promise<Map | null> {
try {
const repository = this.em.getRepository(Map)
const repository = this.getEntityManager().getRepository(Map)
return await repository.findOne({ id: { $exists: true } })
} catch (error: any) {
this.logger.error(`Failed to get first map: ${error instanceof Error ? error.message : String(error)}`)
@ -17,7 +17,7 @@ class MapRepository extends BaseRepository {
async getAll(): Promise<Map[]> {
try {
const repository = this.em.getRepository(Map)
const repository = this.getEntityManager().getRepository(Map)
return await repository.findAll()
} catch (error: any) {
this.logger.error(`Failed to get all map: ${error.message}`)
@ -27,7 +27,7 @@ class MapRepository extends BaseRepository {
async getById(id: UUID) {
try {
const repository = this.em.getRepository(Map)
const repository = this.getEntityManager().getRepository(Map)
return await repository.findOne({ id })
} catch (error: any) {
this.logger.error(`Failed to get map by id: ${error.message}`)
@ -37,7 +37,7 @@ class MapRepository extends BaseRepository {
async getEventTiles(id: UUID): Promise<MapEventTile[]> {
try {
const repository = this.em.getRepository(MapEventTile)
const repository = this.getEntityManager().getRepository(MapEventTile)
return await repository.find({ map: id })
} catch (error: any) {
this.logger.error(`Failed to get map event tiles: ${error.message}`)
@ -47,7 +47,7 @@ class MapRepository extends BaseRepository {
async getFirstEventTile(mapId: UUID, positionX: number, positionY: number): Promise<MapEventTile | null> {
try {
const repository = this.em.getRepository(MapEventTile)
const repository = this.getEntityManager().getRepository(MapEventTile)
return await repository.findOne({
map: mapId,
positionX: positionX,
@ -61,7 +61,7 @@ class MapRepository extends BaseRepository {
async getMapObjects(id: UUID): Promise<MapObject[]> {
try {
const repository = this.em.getRepository(MapObject)
const repository = this.getEntityManager().getRepository(MapObject)
return await repository.find({ map: id })
} catch (error: any) {
this.logger.error(`Failed to get map objects: ${error.message}`)
@ -70,4 +70,4 @@ class MapRepository extends BaseRepository {
}
}
export default new MapRepository()
export default MapRepository

View File

@ -5,7 +5,7 @@ import { PasswordResetToken } from '#entities/passwordResetToken'
class PasswordResetTokenRepository extends BaseRepository {
async getById(id: UUID): Promise<any> {
try {
const repository = this.em.getRepository(PasswordResetToken)
const repository = this.getEntityManager().getRepository(PasswordResetToken)
return await repository.findOne({ id })
} catch (error: any) {
// Handle error
@ -15,7 +15,7 @@ class PasswordResetTokenRepository extends BaseRepository {
async getByUserId(userId: UUID): Promise<any> {
try {
const repository = this.em.getRepository(PasswordResetToken)
const repository = this.getEntityManager().getRepository(PasswordResetToken)
return await repository.findOne({
user: userId
})
@ -27,7 +27,7 @@ class PasswordResetTokenRepository extends BaseRepository {
async getByToken(token: string): Promise<any> {
try {
const repository = this.em.getRepository(PasswordResetToken)
const repository = this.getEntityManager().getRepository(PasswordResetToken)
return await repository.findOne({ token })
} catch (error: any) {
// Handle error
@ -36,4 +36,4 @@ class PasswordResetTokenRepository extends BaseRepository {
}
}
export default new PasswordResetTokenRepository()
export default PasswordResetTokenRepository

View File

@ -5,7 +5,7 @@ import { Sprite } from '#entities/sprite'
class SpriteRepository extends BaseRepository {
async getById(id: UUID, populate?: any) {
try {
const repository = this.em.getRepository(Sprite)
const repository = this.getEntityManager().getRepository(Sprite)
return await repository.findOne({ id }, { populate })
} catch (error: any) {
return null
@ -14,7 +14,7 @@ class SpriteRepository extends BaseRepository {
async getAll(populate?: any): Promise<Sprite[]> {
try {
const repository = this.em.getRepository(Sprite)
const repository = this.getEntityManager().getRepository(Sprite)
return await repository.findAll({ populate })
} catch (error: any) {
return []
@ -22,4 +22,4 @@ class SpriteRepository extends BaseRepository {
}
}
export default new SpriteRepository()
export default SpriteRepository

View File

@ -3,14 +3,14 @@ import { FilterValue } from '@mikro-orm/core'
import { BaseRepository } from '#application/base/baseRepository'
import { UUID } from '#application/types'
import { unduplicateArray } from '#application/utilities'
import { Tile } from '#entities/tile'
import { Map } from '#entities/map'
import { Tile } from '#entities/tile'
import MapService from '#services/mapService'
class TileRepository extends BaseRepository {
async getById(id: UUID) {
try {
const repository = this.em.getRepository(Tile)
const repository = this.getEntityManager().getRepository(Tile)
return await repository.findOne({ id })
} catch (error: any) {
return null
@ -19,7 +19,7 @@ class TileRepository extends BaseRepository {
async getByIds(ids: UUID[]) {
try {
const repository = this.em.getRepository(Tile)
const repository = this.getEntityManager().getRepository(Tile)
return await repository.find({
id: ids
})
@ -30,7 +30,7 @@ class TileRepository extends BaseRepository {
async getAll() {
try {
const repository = this.em.getRepository(Tile)
const repository = this.getEntityManager().getRepository(Tile)
return await repository.findAll()
} catch (error: any) {
return []
@ -39,8 +39,8 @@ class TileRepository extends BaseRepository {
async getByMapId(mapId: UUID) {
try {
const repository = this.em.getRepository(Map)
const tileRepository = this.em.getRepository(Tile)
const repository = this.getEntityManager().getRepository(Map)
const tileRepository = this.getEntityManager().getRepository(Tile)
const map = await repository.findOne({ id: mapId })
if (!map) return []
@ -56,4 +56,4 @@ class TileRepository extends BaseRepository {
}
}
export default new TileRepository()
export default TileRepository

View File

@ -5,7 +5,7 @@ import { User } from '#entities/user'
class UserRepository extends BaseRepository {
async getById(id: UUID) {
try {
const repository = this.em.getRepository(User)
const repository = this.getEntityManager().getRepository(User)
return await repository.findOne({ id })
} catch (error: any) {
this.logger.error(`Failed to get user by ID: ${error instanceof Error ? error.message : String(error)}`)
@ -15,7 +15,7 @@ class UserRepository extends BaseRepository {
async getByUsername(username: string) {
try {
const repository = this.em.getRepository(User)
const repository = this.getEntityManager().getRepository(User)
return await repository.findOne({ username })
} catch (error: any) {
this.logger.error(`Failed to get user by username: ${error instanceof Error ? error.message : String(error)}`)
@ -25,7 +25,7 @@ class UserRepository extends BaseRepository {
async getByEmail(email: string) {
try {
const repository = this.em.getRepository(User)
const repository = this.getEntityManager().getRepository(User)
return await repository.findOne({ email })
} catch (error: any) {
this.logger.error(`Failed to get user by email: ${error instanceof Error ? error.message : String(error)}`)
@ -34,4 +34,4 @@ class UserRepository extends BaseRepository {
}
}
export default new UserRepository()
export default UserRepository

View File

@ -1,16 +1,15 @@
import { BaseRepository } from '#application/base/baseRepository'
import { gameLogger } from '#application/logger'
import { World } from '#entities/world'
class WorldRepository extends BaseRepository {
async getFirst() {
try {
const repository = this.em.getRepository(World)
const repository = this.getEntityManager().getRepository(World)
return await repository.findOne({ date: { $exists: true } })
} catch (error: any) {
gameLogger.error(`Failed to get first world: ${error instanceof Error ? error.message : String(error)}`)
this.logger.error(`Failed to get first world: ${error instanceof Error ? error.message : String(error)}`)
}
}
}
export default new WorldRepository()
export default WorldRepository

View File

@ -7,13 +7,11 @@ import config from '#application/config'
import Database from '#application/database'
import Logger, { LoggerType } from '#application/logger'
import ConsoleManager from '#managers/consoleManager'
import DateManager from '#managers/dateManager'
import HttpManager from '#managers/httpManager'
import MapManager from '#managers/mapManager'
import QueueManager from '#managers/queueManager'
import SocketManager from '#managers/socketManager'
import UserManager from '#managers/userManager'
import WeatherManager from '#managers/weatherManager'
import MapManager from '#managers/mapManager'
export class Server {
private readonly app: Application
@ -38,16 +36,7 @@ export class Server {
this.logger.info(`Server running on port ${config.PORT}`)
// Initialize managers
await Promise.all([
HttpManager.boot(this.app),
SocketManager.boot(this.app, this.http),
QueueManager.boot(),
UserManager.boot(),
// DateManager.boot(),
// WeatherManager.boot(),
MapManager.boot(),
ConsoleManager.boot()
])
await Promise.all([HttpManager.boot(this.app), SocketManager.boot(this.app, this.http), QueueManager.boot(), UserManager.boot(), MapManager.boot(), ConsoleManager.boot()])
} catch (error: any) {
this.logger.error(`Server failed to start: ${error.message}`)
process.exit(1)

View File

@ -2,8 +2,8 @@ import { BaseService } from '#application/base/baseService'
import config from '#application/config'
import { Character } from '#entities/character'
import { Map } from '#entities/map'
import SocketManager from '#managers/socketManager'
import MapManager from '#managers/mapManager'
import SocketManager from '#managers/socketManager'
import CharacterRepository from '#repositories/characterRepository'
import MapRepository from '#repositories/mapRepository'

View File

@ -1,8 +1,8 @@
import Logger, { LoggerType } from '#application/logger'
import { UUID } from '#application/types'
import { Character } from '#entities/character'
import SocketManager from '#managers/socketManager'
import MapManager from '#managers/mapManager'
import SocketManager from '#managers/socketManager'
import MapCharacter from '#models/mapCharacter'
interface TeleportOptions {

View File

@ -16,9 +16,12 @@ import UserRepository from '#repositories/userRepository'
* @class UserService
*/
class UserService extends BaseService {
protected readonly userRepository = new UserRepository()
protected readonly passwordResetTokenRepository = new PasswordResetTokenRepository()
async login(username: string, password: string): Promise<boolean | User> {
try {
const user = await UserRepository.getByUsername(username)
const user = await this.userRepository.getByUsername(username)
if (!user) {
return false
}
@ -39,7 +42,7 @@ class UserService extends BaseService {
async register(username: string, email: string, password: string): Promise<boolean | User> {
try {
// Check existing users
const [userByName, userByEmail] = await Promise.all([UserRepository.getByUsername(username), UserRepository.getByEmail(email)])
const [userByName, userByEmail] = await Promise.all([this.userRepository.getByUsername(username), this.userRepository.getByEmail(email)])
if (userByName || userByEmail) {
this.logger.error(`User already exists: ${userByEmail ? email : username}`)
@ -59,11 +62,11 @@ class UserService extends BaseService {
async requestPasswordReset(email: string): Promise<boolean> {
try {
const user = await UserRepository.getByEmail(email)
const user = await this.userRepository.getByEmail(email)
if (!user) return false
const token = await bcrypt.hash(new Date().getTime().toString(), 10)
const latestToken = await PasswordResetTokenRepository.getByUserId(user.id)
const latestToken = await this.passwordResetTokenRepository.getByUserId(user.id)
// Check if password reset has been requested recently
if (latestToken) {
@ -108,12 +111,12 @@ class UserService extends BaseService {
async resetPassword(urlToken: string, password: string): Promise<boolean> {
try {
const tokenData = await PasswordResetTokenRepository.getByToken(urlToken)
const tokenData = await this.passwordResetTokenRepository.getByToken(urlToken)
if (!tokenData) {
return false
}
const user = await UserRepository.getById(tokenData.userId)
const user = await this.userRepository.getById(tokenData.userId)
if (!user) {
return false
}