diff --git a/src/application/base/baseController.ts b/src/application/base/baseController.ts index 4236f7b..addb62e 100644 --- a/src/application/base/baseController.ts +++ b/src/application/base/baseController.ts @@ -1,6 +1,10 @@ import { Request, Response } from 'express' +import { Logger } from '#application/logger' + export abstract class BaseController { + protected readonly logger: Logger = new Logger('http') + protected sendSuccess(res: Response, data?: any, message?: string, status: number = 200) { return res.status(status).json({ success: true, diff --git a/src/application/base/baseEntity.ts b/src/application/base/baseEntity.ts index c03bb74..cd65480 100644 --- a/src/application/base/baseEntity.ts +++ b/src/application/base/baseEntity.ts @@ -1,9 +1,11 @@ import { EntityManager } from '@mikro-orm/core' import Database from '#application/database' -import { appLogger } from '#application/logger' +import { Logger } from '#application/logger' export abstract class BaseEntity { + protected readonly logger: Logger = new Logger('entity') + private getEntityManager(): EntityManager { return Database.getEntityManager() } @@ -35,7 +37,7 @@ export abstract class BaseEntity { throw error } } catch (error) { - appLogger.error(`Failed to ${actionDescription}: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to ${actionDescription}: ${error instanceof Error ? error.message : String(error)}`) throw error } } diff --git a/src/application/base/baseEvent.ts b/src/application/base/baseEvent.ts index 7ab8f0a..b9b84bd 100644 --- a/src/application/base/baseEvent.ts +++ b/src/application/base/baseEvent.ts @@ -1,8 +1,11 @@ import { Server } from 'socket.io' +import { Logger } from '#application/logger' import { TSocket } from '#application/types' export abstract class BaseEvent { + protected readonly logger: Logger = new Logger('game') + constructor( readonly io: Server, readonly socket: TSocket diff --git a/src/application/base/baseRepository.ts b/src/application/base/baseRepository.ts index cb22eae..646aee4 100644 --- a/src/application/base/baseRepository.ts +++ b/src/application/base/baseRepository.ts @@ -2,7 +2,11 @@ import { EntityManager } from '@mikro-orm/core' import Database from '../database' +import { Logger } from '#application/logger' + export abstract class BaseRepository { + protected readonly logger: Logger = new Logger('repository') + protected get em(): EntityManager { return Database.getEntityManager() } diff --git a/src/application/base/baseService.ts b/src/application/base/baseService.ts new file mode 100644 index 0000000..73def07 --- /dev/null +++ b/src/application/base/baseService.ts @@ -0,0 +1,5 @@ +import { Logger } from '#application/logger' + +export abstract class BaseService { + protected readonly logger: Logger = new Logger('game') +} diff --git a/src/application/logger.ts b/src/application/logger.ts index bb9888d..f04a3e0 100644 --- a/src/application/logger.ts +++ b/src/application/logger.ts @@ -1,68 +1,73 @@ -import fs from 'fs' - import pino from 'pino' import { getRootPath } from './storage' -// Array of log types -const LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue', 'command'] as const -type LogType = (typeof LOG_TYPES)[number] +export class Logger { + private static readonly LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue', 'command', 'repository', 'entity'] as const + private readonly logger: ReturnType -const createLogger = (name: LogType) => - pino({ - level: process.env.LOG_LEVEL || 'debug', - transport: { - target: 'pino/file', - options: { - destination: `./logs/${name}.log`, - mkdir: true - } - }, - formatters: { - level: (label) => { - return { level: label.toUpperCase() } - } - }, - timestamp: pino.stdTimeFunctions.isoTime, - base: null - }) - -// Create logger instances -const loggers = Object.fromEntries(LOG_TYPES.map((type) => [type, createLogger(type)])) as Record> - -const watchLogs = () => { - LOG_TYPES.forEach((type) => { - const logFile = getRootPath('logs', `${type}.log`) - - // Get initial file size - const stats = fs.statSync(logFile) - let lastPosition = stats.size - - fs.watch(logFile, (eventType) => { - if (eventType !== 'change') { - return - } - - fs.stat(logFile, (err, stats) => { - if (err) return - - if (stats.size > lastPosition) { - const stream = fs.createReadStream(logFile, { - start: lastPosition, - end: stats.size - }) - - stream.on('data', (chunk) => { - console.log(`[${type}]\n${chunk.toString()}`) - }) - - lastPosition = stats.size + constructor(type: (typeof Logger.LOG_TYPES)[number]) { + this.logger = pino({ + level: process.env.LOG_LEVEL || 'debug', + transport: { + target: 'pino/file', + options: { + destination: `./logs/${type}.log`, + mkdir: true } + }, + formatters: { + level: (label) => ({ level: label.toUpperCase() }) + }, + timestamp: pino.stdTimeFunctions.isoTime, + base: null + }) + } + + info(message: string) { + this.logger.info(message) + } + + error(message: string) { + this.logger.error(message) + } + + warn(message: string) { + this.logger.warn(message) + } + + debug(message: string) { + this.logger.debug(message) + } + + static watch() { + const fs = require('fs') + + this.LOG_TYPES.forEach((type) => { + const logFile = getRootPath('logs', `${type}.log`) + const stats = fs.statSync(logFile) + let lastPosition = stats.size + + fs.watch(logFile, (eventType: string) => { + if (eventType !== 'change') return + + fs.stat(logFile, (err: Error, stats: { size: number }) => { + if (err) return + + if (stats.size > lastPosition) { + const stream = fs.createReadStream(logFile, { + start: lastPosition, + end: stats.size + }) + + stream.on('data', (chunk: Buffer) => { + console.log(`[${type}]\n${chunk.toString()}`) + }) + + lastPosition = stats.size + } + }) }) }) - }) + } } - -export const { http: httpLogger, game: gameLogger, gameMaster: gameMasterLogger, app: appLogger, queue: queueLogger, command: commandLogger } = loggers - -export { watchLogs } diff --git a/src/http/controllers/assets.ts b/src/http/controllers/assets.ts index a7ba4bf..a031cb7 100644 --- a/src/http/controllers/assets.ts +++ b/src/http/controllers/assets.ts @@ -3,7 +3,6 @@ import fs from 'fs' import { Request, Response } from 'express' import { BaseController } from '#application/base/baseController' -import { httpLogger } from '#application/logger' import { getPublicPath } from '#application/storage' import { AssetData, UUID } from '#application/types' import SpriteRepository from '#repositories/spriteRepository' @@ -99,13 +98,13 @@ export class AssetsController extends BaseController { const assetPath = type === 'sprites' && spriteId ? getPublicPath(type, spriteId, file) : getPublicPath(type, file) if (!fs.existsSync(assetPath)) { - httpLogger.error(`File not found: ${assetPath}`) + this.logger.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.logger.error('Error sending file:' + err) this.sendError(res, 'Error downloading the asset', 500) } }) diff --git a/src/models/zoneCharacter.ts b/src/models/zoneCharacter.ts index 24b8051..7a4c05c 100644 --- a/src/models/zoneCharacter.ts +++ b/src/models/zoneCharacter.ts @@ -1,5 +1,4 @@ import { Character } from '#entities/character' -import { CharacterService } from '#services/characterService' class ZoneCharacter { public readonly character: Character @@ -11,8 +10,7 @@ class ZoneCharacter { } public async savePosition() { - const characterService = new CharacterService() - await characterService.updateCharacterPosition(this.character.id, this.character.positionX, this.character.positionY, this.character.rotation, this.character.zone!.id) + await this.character.setPositionX(this.character.positionX).setPositionY(this.character.positionY).setRotation(this.character.rotation).setZone(this.character.zone).update() } } diff --git a/src/repositories/characterHairRepository.ts b/src/repositories/characterHairRepository.ts index fd48ac6..f8127e0 100644 --- a/src/repositories/characterHairRepository.ts +++ b/src/repositories/characterHairRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { CharacterHair } from '#entities/characterHair' class CharacterHairRepository extends BaseRepository { @@ -8,7 +7,7 @@ class CharacterHairRepository extends BaseRepository { const repository = this.em.getRepository(CharacterHair) return await repository.findOne({ id: { $exists: true } }) } catch (error: any) { - appLogger.error(`Failed to get first character hair: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get first character hair: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -18,7 +17,7 @@ class CharacterHairRepository extends BaseRepository { const repository = this.em.getRepository(CharacterHair) return await repository.findAll() } catch (error: any) { - appLogger.error(`Failed to get all character hair: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get all character hair: ${error instanceof Error ? error.message : String(error)}`) return [] } } @@ -28,7 +27,7 @@ class CharacterHairRepository extends BaseRepository { const repository = this.em.getRepository(CharacterHair) return await repository.find({ isSelectable: true }) } catch (error: any) { - appLogger.error(`Failed to get selectable character hair: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get selectable character hair: ${error instanceof Error ? error.message : String(error)}`) return [] } } @@ -38,7 +37,7 @@ class CharacterHairRepository extends BaseRepository { const repository = this.em.getRepository(CharacterHair) return await repository.findOne({ id }) } catch (error: any) { - appLogger.error(`Failed to get character hair by ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get character hair by ID: ${error instanceof Error ? error.message : String(error)}`) return null } } diff --git a/src/repositories/characterRepository.ts b/src/repositories/characterRepository.ts index 229eb19..f4c2579 100644 --- a/src/repositories/characterRepository.ts +++ b/src/repositories/characterRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { Character } from '#entities/character' class CharacterRepository extends BaseRepository { @@ -8,7 +7,7 @@ class CharacterRepository extends BaseRepository { const repository = this.em.getRepository(Character) return await repository.find({ user: userId }) } catch (error: any) { - appLogger.error(`Failed to get character by user ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get character by user ID: ${error instanceof Error ? error.message : String(error)}`) return [] } } @@ -18,7 +17,7 @@ class CharacterRepository extends BaseRepository { const repository = this.em.getRepository(Character) return await repository.findOne({ user: userId, id: characterId }) } catch (error: any) { - appLogger.error(`Failed to get character by user ID and character ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get character by user ID and character ID: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -28,7 +27,7 @@ class CharacterRepository extends BaseRepository { const repository = this.em.getRepository(Character) return await repository.findOne({ id }) } catch (error: any) { - appLogger.error(`Failed to get character by ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get character by ID: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -38,7 +37,7 @@ class CharacterRepository extends BaseRepository { const repository = this.em.getRepository(Character) return await repository.findOne({ name }) } catch (error: any) { - appLogger.error(`Failed to get character by name: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get character by name: ${error instanceof Error ? error.message : String(error)}`) return null } } diff --git a/src/repositories/characterTypeRepository.ts b/src/repositories/characterTypeRepository.ts index 635e892..5cefe18 100644 --- a/src/repositories/characterTypeRepository.ts +++ b/src/repositories/characterTypeRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { CharacterType } from '#entities/characterType' class CharacterTypeRepository extends BaseRepository { @@ -8,7 +7,7 @@ class CharacterTypeRepository extends BaseRepository { const repository = this.em.getRepository(CharacterType) return await repository.findOne({ id: { $exists: true } }) } catch (error: any) { - appLogger.error(`Failed to get first character type: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get first character type: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -18,7 +17,7 @@ class CharacterTypeRepository extends BaseRepository { const repository = this.em.getRepository(CharacterType) return await repository.findAll() } catch (error: any) { - appLogger.error(`Failed to get all character types: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get all character types: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -28,7 +27,7 @@ class CharacterTypeRepository extends BaseRepository { const repository = this.em.getRepository(CharacterType) return await repository.findOne({ id }) } catch (error: any) { - appLogger.error(`Failed to get character type by ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get character type by ID: ${error instanceof Error ? error.message : String(error)}`) return null } } diff --git a/src/repositories/chatRepository.ts b/src/repositories/chatRepository.ts index 3e2b169..b7d88ea 100644 --- a/src/repositories/chatRepository.ts +++ b/src/repositories/chatRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { Chat } from '#entities/chat' class ChatRepository extends BaseRepository { @@ -10,7 +9,7 @@ class ChatRepository extends BaseRepository { id }) } catch (error: any) { - appLogger.error(`Failed to get chat by ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get chat by ID: ${error instanceof Error ? error.message : String(error)}`) return [] } } @@ -20,7 +19,7 @@ class ChatRepository extends BaseRepository { const repository = this.em.getRepository(Chat) return await repository.findAll() } catch (error: any) { - appLogger.error(`Failed to get all chats: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get all chats: ${error instanceof Error ? error.message : String(error)}`) return [] } } @@ -30,7 +29,7 @@ class ChatRepository extends BaseRepository { const repository = this.em.getRepository(Chat) return await repository.find({ character: characterId }) } catch (error: any) { - appLogger.error(`Failed to get chats by character ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get chats by character ID: ${error instanceof Error ? error.message : String(error)}`) return [] } } @@ -40,7 +39,7 @@ class ChatRepository extends BaseRepository { const repository = this.em.getRepository(Chat) return await repository.find({ zone: zoneId }) } catch (error: any) { - appLogger.error(`Failed to get chats by zone ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get chats by zone ID: ${error instanceof Error ? error.message : String(error)}`) return [] } } diff --git a/src/repositories/itemRepository.ts b/src/repositories/itemRepository.ts index 81c8dc3..256e5bc 100644 --- a/src/repositories/itemRepository.ts +++ b/src/repositories/itemRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { Item } from '#entities/item' class ItemRepository extends BaseRepository { @@ -8,7 +7,7 @@ class ItemRepository extends BaseRepository { const repository = this.em.getRepository(Item) return await repository.findOne({ id }) } catch (error: any) { - appLogger.error(`Failed to get item by ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get item by ID: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -20,7 +19,7 @@ class ItemRepository extends BaseRepository { id: ids }) } catch (error: any) { - appLogger.error(`Failed to get items by IDs: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get items by IDs: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -30,7 +29,7 @@ class ItemRepository extends BaseRepository { const repository = this.em.getRepository(Item) return await repository.findAll() } catch (error: any) { - appLogger.error(`Failed to get all items: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get all items: ${error instanceof Error ? error.message : String(error)}`) return null } } diff --git a/src/repositories/passwordResetTokenRepository.ts b/src/repositories/passwordResetTokenRepository.ts index 004bd91..91f18ba 100644 --- a/src/repositories/passwordResetTokenRepository.ts +++ b/src/repositories/passwordResetTokenRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' // Import the global Prisma instance -import { appLogger } from '#application/logger' import { PasswordResetToken } from '#entities/passwordResetToken' class PasswordResetTokenRepository extends BaseRepository { @@ -9,7 +8,7 @@ class PasswordResetTokenRepository extends BaseRepository { return await repository.findOne({ id }) } catch (error: any) { // Handle error - appLogger.error(`Failed to get password reset token by ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get password reset token by ID: ${error instanceof Error ? error.message : String(error)}`) } } @@ -21,7 +20,7 @@ class PasswordResetTokenRepository extends BaseRepository { }) } catch (error: any) { // Handle error - appLogger.error(`Failed to get password reset token by user ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get password reset token by user ID: ${error instanceof Error ? error.message : String(error)}`) } } @@ -31,7 +30,7 @@ class PasswordResetTokenRepository extends BaseRepository { return await repository.findOne({ token }) } catch (error: any) { // Handle error - appLogger.error(`Failed to get password reset token by token: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get password reset token by token: ${error instanceof Error ? error.message : String(error)}`) } } } diff --git a/src/repositories/userRepository.ts b/src/repositories/userRepository.ts index ff2828e..8191ec7 100644 --- a/src/repositories/userRepository.ts +++ b/src/repositories/userRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { User } from '#entities/user' class UserRepository extends BaseRepository { @@ -8,7 +7,7 @@ class UserRepository extends BaseRepository { const repository = this.em.getRepository(User) return await repository.findOne({ id }) } catch (error: any) { - appLogger.error(`Failed to get user by ID: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get user by ID: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -18,7 +17,7 @@ class UserRepository extends BaseRepository { const repository = this.em.getRepository(User) return await repository.findOne({ username }) } catch (error: any) { - appLogger.error(`Failed to get user by username: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get user by username: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -28,7 +27,7 @@ class UserRepository extends BaseRepository { const repository = this.em.getRepository(User) return await repository.findOne({ email }) } catch (error: any) { - appLogger.error(`Failed to get user by email: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get user by email: ${error instanceof Error ? error.message : String(error)}`) return null } } diff --git a/src/repositories/zoneEventTileRepository.ts b/src/repositories/zoneEventTileRepository.ts index 1bbb477..677d6ef 100644 --- a/src/repositories/zoneEventTileRepository.ts +++ b/src/repositories/zoneEventTileRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { ZoneEventTile } from '#entities/zoneEventTile' class ZoneEventTileRepository extends BaseRepository { @@ -10,7 +9,7 @@ class ZoneEventTileRepository extends BaseRepository { zone: id }) } catch (error: any) { - appLogger.error(`Failed to get zone event tiles: ${error.message}`) + this.logger.error(`Failed to get zone event tiles: ${error.message}`) return [] } } @@ -24,7 +23,7 @@ class ZoneEventTileRepository extends BaseRepository { positionY: positionY }) } catch (error: any) { - appLogger.error(`Failed to get zone event tile: ${error.message}`) + this.logger.error(`Failed to get zone event tile: ${error.message}`) return null } } diff --git a/src/repositories/zoneRepository.ts b/src/repositories/zoneRepository.ts index 17ae904..fac70d0 100644 --- a/src/repositories/zoneRepository.ts +++ b/src/repositories/zoneRepository.ts @@ -1,5 +1,4 @@ import { BaseRepository } from '#application/base/baseRepository' -import { appLogger } from '#application/logger' import { Zone } from '#entities/zone' import { ZoneEventTile } from '#entities/zoneEventTile' import { ZoneObject } from '#entities/zoneObject' @@ -10,7 +9,7 @@ class ZoneRepository extends BaseRepository { const repository = this.em.getRepository(Zone) return await repository.findOne({ id: { $exists: true } }) } catch (error: any) { - appLogger.error(`Failed to get first zone: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to get first zone: ${error instanceof Error ? error.message : String(error)}`) return null } } @@ -20,7 +19,7 @@ class ZoneRepository extends BaseRepository { const repository = this.em.getRepository(Zone) return await repository.findAll() } catch (error: any) { - appLogger.error(`Failed to get all zone: ${error.message}`) + this.logger.error(`Failed to get all zone: ${error.message}`) return [] } } @@ -30,7 +29,7 @@ class ZoneRepository extends BaseRepository { const repository = this.em.getRepository(Zone) return await repository.findOne({ id }) } catch (error: any) { - appLogger.error(`Failed to get zone by id: ${error.message}`) + this.logger.error(`Failed to get zone by id: ${error.message}`) return null } } @@ -40,7 +39,7 @@ class ZoneRepository extends BaseRepository { const repository = this.em.getRepository(ZoneEventTile) return await repository.find({ zone: id }) } catch (error: any) { - appLogger.error(`Failed to get zone event tiles: ${error.message}`) + this.logger.error(`Failed to get zone event tiles: ${error.message}`) return [] } } @@ -54,7 +53,7 @@ class ZoneRepository extends BaseRepository { positionY: positionY }) } catch (error: any) { - appLogger.error(`Failed to get zone event tile: ${error.message}`) + this.logger.error(`Failed to get zone event tile: ${error.message}`) return null } } @@ -64,7 +63,7 @@ class ZoneRepository extends BaseRepository { const repository = this.em.getRepository(ZoneObject) return await repository.find({ zone: id }) } catch (error: any) { - appLogger.error(`Failed to get zone objects: ${error.message}`) + this.logger.error(`Failed to get zone objects: ${error.message}`) return [] } } diff --git a/src/server.ts b/src/server.ts index 83cb496..6f86590 100644 --- a/src/server.ts +++ b/src/server.ts @@ -7,7 +7,7 @@ import { Server as SocketServer } from 'socket.io' import config from '#application/config' import Database from '#application/database' -import { appLogger, watchLogs } from '#application/logger' +import { Logger } from '#application/logger' import { getAppPath } from '#application/storage' import { TSocket } from '#application/types' import { HttpRouter } from '#http/router' @@ -23,6 +23,7 @@ export class Server { private readonly app: Application private readonly http: HTTPServer private readonly io: SocketServer + private readonly logger: Logger = new Logger('app') /** * Creates an instance of GameServer. @@ -49,22 +50,22 @@ export class Server { */ public async start() { // Read log file and print to console for debugging - watchLogs() + Logger.watch() // Connect to database try { await Database.initialize() } catch (error: any) { - appLogger.error(`Database connection failed: ${error.message}`) + this.logger.error(`Database connection failed: ${error.message}`) process.exit(1) // Exit if database connection fails } // Start the server try { this.http.listen(config.PORT, config.HOST) - appLogger.info(`Socket.IO running on port ${config.PORT}`) + this.logger.info(`Socket.IO running on port ${config.PORT}`) } catch (error: any) { - appLogger.error(`Socket.IO failed to start: ${error.message}`) + this.logger.error(`Socket.IO failed to start: ${error.message}`) } // Load HTTP routes @@ -102,7 +103,7 @@ export class Server { try { await this.loadEventHandlers('socketEvents', '', socket) } catch (error: any) { - appLogger.error(`Failed to load event handlers: ${error.message}`) + this.logger.error(`Failed to load event handlers: ${error.message}`) } } @@ -126,7 +127,7 @@ export class Server { try { const module = await import(filePath) if (typeof module.default !== 'function') { - appLogger.warn(`Unrecognized export in ${file.name}`) + this.logger.warn(`Unrecognized export in ${file.name}`) continue } @@ -134,11 +135,11 @@ export class Server { const eventInstance = new EventClass(this.io, socket) eventInstance.listen() } catch (error) { - appLogger.error(`Error loading event handler ${file.name}: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Error loading event handler ${file.name}: ${error instanceof Error ? error.message : String(error)}`) } } } catch (error) { - appLogger.error(`Error reading directory: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Error reading directory: ${error instanceof Error ? error.message : String(error)}`) } } } diff --git a/src/services/characterService.ts b/src/services/characterService.ts index 4d32c60..106cf23 100644 --- a/src/services/characterService.ts +++ b/src/services/characterService.ts @@ -1,5 +1,5 @@ +import { BaseService } from '#application/base/baseService' import config from '#application/config' -import { gameLogger } from '#application/logger' import { Character } from '#entities/character' import { Zone } from '#entities/zone' import ZoneManager from '#managers/zoneManager' @@ -9,40 +9,25 @@ import ZoneRepository from '#repositories/zoneRepository' type Position = { x: number; y: number } export type Node = Position & { parent?: Node; g: number; h: number; f: number } -export class CharacterService { +class CharacterService extends BaseService { private readonly MOVEMENT_DELAY_MS = 250 private readonly DIRECTIONS = [ { x: 0, y: -1 }, // Up { x: 0, y: 1 }, // Down { x: -1, y: 0 }, // Left { x: 1, y: 0 }, // Right - { x: -1, y: -1 }, - { x: -1, y: 1 }, - { x: 1, y: -1 }, - { x: 1, y: 1 } + { x: -1, y: -1 }, // Up left + { x: -1, y: 1 }, // Up right + { x: 1, y: -1 }, // Down left + { x: 1, y: 1 } // Down right ] - public async updateCharacterPosition(id: number, positionX: number, positionY: number, rotation: number, zoneId: number) { - const character = await CharacterRepository.getById(id) - if (!character) return null - - character - .setPositionX(positionX) - .setPositionY(positionY) - .setRotation(rotation) - .setZone((await ZoneRepository.getById(zoneId)) as Zone) - - await character.save() - - return character - } - public async calculatePath(character: Character, targetX: number, targetY: number): Promise { const zone = ZoneManager.getZoneById(character.zone!.id) const grid = await zone?.getGrid() if (!grid?.length) { - gameLogger.error('character:move error', 'Grid not found or empty') + this.logger.error('character:move error: Grid not found or empty') return null } @@ -144,3 +129,5 @@ export class CharacterService { return path } } + +export default new CharacterService() diff --git a/src/services/chatService.ts b/src/services/chatService.ts index c613e62..0a3e94f 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,13 +1,13 @@ import { Server } from 'socket.io' -import { gameLogger } from '#application/logger' +import { BaseService } from '#application/base/baseService' import { TSocket } from '#application/types' import { Chat } from '#entities/chat' import CharacterRepository from '#repositories/characterRepository' import ChatRepository from '#repositories/chatRepository' import ZoneRepository from '#repositories/zoneRepository' -class ChatService { +class ChatService extends BaseService { async sendZoneMessage(io: Server, socket: TSocket, message: string, characterId: number, zoneId: number): Promise { try { const character = await CharacterRepository.getById(characterId) @@ -28,7 +28,7 @@ class ChatService { io.to(zoneId.toString()).emit('chat:message', chat) return true } catch (error: any) { - gameLogger.error(`Failed to save chat message: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to save chat message: ${error instanceof Error ? error.message : String(error)}`) return false } } @@ -46,4 +46,4 @@ class ChatService { } } -export default ChatService +export default new ChatService() diff --git a/src/services/passwordResetTokenService.ts b/src/services/passwordResetTokenService.ts index 5b37ddf..39bbec4 100644 --- a/src/services/passwordResetTokenService.ts +++ b/src/services/passwordResetTokenService.ts @@ -1,7 +1,7 @@ -import { appLogger } from '#application/logger' +import { BaseService } from '#application/base/baseService' import passwordResetTokenRepository from '#repositories/passwordResetTokenRepository' -class PasswordResetTokenService { +class PasswordResetTokenService extends BaseService { /** * Delete token * @param token @@ -17,10 +17,10 @@ class PasswordResetTokenService { return true } catch (error: any) { - appLogger.error(`Error deleting password reset token: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Error deleting password reset token: ${error instanceof Error ? error.message : String(error)}`) return false } } } -export default PasswordResetTokenService +export default new PasswordResetTokenService() diff --git a/src/services/userService.ts b/src/services/userService.ts index e88fb25..30b69a0 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -3,8 +3,8 @@ import NodeMailer from 'nodemailer' import PasswordResetTokenService from './passwordResetTokenService' // @TODO: Correctly implement this +import { BaseService } from '#application/base/baseService' import config from '#application/config' -import { httpLogger } from '#application/logger' import { PasswordResetToken } from '#entities/passwordResetToken' import { User } from '#entities/user' import PasswordResetTokenRepository from '#repositories/passwordResetTokenRepository' @@ -15,7 +15,7 @@ import UserRepository from '#repositories/userRepository' * Handles user login and registration * @class UserService */ -class UserService { +class UserService extends BaseService { async login(username: string, password: string): Promise { try { const user = await UserRepository.getByUsername(username) @@ -25,13 +25,13 @@ class UserService { const passwordMatch = await bcrypt.compare(password, user.password) if (!passwordMatch) { - httpLogger.error(`Failed to login user: ${username}`) + this.logger.error(`Failed to login user: ${username}`) return false } return user } catch (error: any) { - httpLogger.error(`Error logging in user: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Error logging in user: ${error instanceof Error ? error.message : String(error)}`) return false } } @@ -42,7 +42,7 @@ class UserService { const [userByName, userByEmail] = await Promise.all([UserRepository.getByUsername(username), UserRepository.getByEmail(email)]) if (userByName || userByEmail) { - httpLogger.error(`User already exists: ${userByEmail ? email : username}`) + this.logger.error(`User already exists: ${userByEmail ? email : username}`) return false } @@ -52,7 +52,7 @@ class UserService { return newUser } catch (error: any) { - httpLogger.error(`Error registering user: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Error registering user: ${error instanceof Error ? error.message : String(error)}`) return false } } @@ -101,7 +101,7 @@ class UserService { return true } catch (error: any) { - httpLogger.error(`Error sending password reset email: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Error sending password reset email: ${error instanceof Error ? error.message : String(error)}`) return false } } @@ -126,10 +126,10 @@ class UserService { return true } catch (error: any) { - httpLogger.error(`Error setting new password: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Error setting new password: ${error instanceof Error ? error.message : String(error)}`) return false } } } -export default UserService +export default new UserService() diff --git a/src/services/worldService.ts b/src/services/worldService.ts index 7aecd80..95f7165 100644 --- a/src/services/worldService.ts +++ b/src/services/worldService.ts @@ -1,8 +1,8 @@ -import { gameLogger } from '#application/logger' +import { BaseService } from '#application/base/baseService' import { World } from '#entities/world' import WorldRepository from '#repositories/worldRepository' -class WorldService { +class WorldService extends BaseService { async update(worldData: Partial): Promise { try { let world = await WorldRepository.getFirst() @@ -29,7 +29,7 @@ class WorldService { return true } catch (error: any) { - gameLogger.error(`Failed to update world: ${error instanceof Error ? error.message : String(error)}`) + this.logger.error(`Failed to update world: ${error instanceof Error ? error.message : String(error)}`) return false } } diff --git a/src/services/zoneEventTileService.ts b/src/services/zoneEventTileService.ts index 4a007f2..5b3d923 100644 --- a/src/services/zoneEventTileService.ts +++ b/src/services/zoneEventTileService.ts @@ -1,17 +1,17 @@ import { Server } from 'socket.io' -import { gameLogger } from '#application/logger' +import { BaseService } from '#application/base/baseService' import { ExtendedCharacter, TSocket } from '#application/types' import { ZoneEventTileTeleport } from '#entities/zoneEventTileTeleport' import ZoneManager from '#managers/zoneManager' -export class ZoneEventTileService { +class ZoneEventTileService extends BaseService { public async handleTeleport(io: Server, socket: TSocket, character: ExtendedCharacter, teleport: ZoneEventTileTeleport): Promise { if (teleport.toZone.id === character.zone!.id) return const loadedZone = ZoneManager.getZoneById(teleport.toZone.id) if (!loadedZone) { - gameLogger.error('zone:character:join error', 'Loaded zone not found') + this.logger.error('zone:character:join error', 'Loaded zone not found') return } @@ -45,3 +45,5 @@ export class ZoneEventTileService { }) } } + +export default new ZoneEventTileService() diff --git a/src/services/zoneService.ts b/src/services/zoneService.ts index 3fc02d5..d658017 100644 --- a/src/services/zoneService.ts +++ b/src/services/zoneService.ts @@ -1,4 +1,6 @@ -class ZoneService { +import { BaseService } from '#application/base/baseService' + +class ZoneService extends BaseService { public flattenZoneArray(tiles: string[][]) { const normalArray = [] @@ -10,4 +12,4 @@ class ZoneService { } } -export default ZoneService +export default new ZoneService() diff --git a/src/socketEvents/disconnect.ts b/src/socketEvents/disconnect.ts index a535d2d..18eca99 100644 --- a/src/socketEvents/disconnect.ts +++ b/src/socketEvents/disconnect.ts @@ -1,15 +1,7 @@ -import { Server } from 'socket.io' - -import { gameLogger } from '#application/logger' -import { TSocket } from '#application/types' import ZoneManager from '#managers/zoneManager' +import { BaseEvent } from '#application/base/baseEvent' -export default class DisconnectEvent { - constructor( - private readonly io: Server, - private readonly socket: TSocket - ) {} - +export default class DisconnectEvent extends BaseEvent { public listen(): void { this.socket.on('disconnect', this.handleEvent.bind(this)) } @@ -17,7 +9,7 @@ export default class DisconnectEvent { private async handleEvent(data: any): Promise { try { if (!this.socket.userId) { - gameLogger.info('User disconnected but had no user set') + this.logger.info('User disconnected but had no user set') return } @@ -25,7 +17,7 @@ export default class DisconnectEvent { const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!) if (!zoneCharacter) { - gameLogger.info('User disconnected but had no character set') + this.logger.info('User disconnected but had no character set') return } @@ -36,13 +28,13 @@ export default class DisconnectEvent { await zoneCharacter.savePosition() ZoneManager.removeCharacter(this.socket.characterId!) - gameLogger.info('User disconnected along with their character') + this.logger.info('User disconnected along with their character') // Inform other clients that the character has left this.io.in(character.zone!.id.toString()).emit('zone:character:leave', character.id) this.io.emit('character:disconnect', character.id) } catch (error: any) { - gameLogger.error('disconnect error', error.message) + this.logger.error('disconnect error: ' + error.message) } } } diff --git a/src/socketEvents/login.ts b/src/socketEvents/login.ts index f256832..85c3d6b 100644 --- a/src/socketEvents/login.ts +++ b/src/socketEvents/login.ts @@ -1,15 +1,7 @@ -import { Server } from 'socket.io' - -import { gameLogger } from '#application/logger' -import { TSocket } from '#application/types' import UserRepository from '#repositories/userRepository' +import { BaseEvent } from '#application/base/baseEvent' -export default class LoginEvent { - constructor( - private readonly io: Server, - private readonly socket: TSocket - ) {} - +export default class LoginEvent extends BaseEvent { public listen(): void { this.socket.on('login', this.handleLogin.bind(this)) } @@ -17,14 +9,14 @@ export default class LoginEvent { private handleLogin(): void { try { if (!this.socket.userId) { - gameLogger.warn('Login attempt without user data') + this.logger.warn('Login attempt without user data') return } this.socket.emit('logged_in', { user: UserRepository.getById(this.socket.userId) }) - gameLogger.info(`User logged in: ${this.socket.userId}`) + this.logger.info(`User logged in: ${this.socket.userId}`) } catch (error: any) { - gameLogger.error('login error', error.message) + this.logger.error('login error: ' + error.message) } } }