Continuation of refactor

This commit is contained in:
Dennis Postma 2024-12-28 20:40:05 +01:00
parent 6dda79f8b2
commit e1a6f650fb
27 changed files with 158 additions and 236 deletions

View File

@ -1,5 +1,8 @@
import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseCommand {
protected readonly logger = Logger.type(LoggerType.COMMAND)
constructor(readonly io: Server) {}
}

View File

@ -1,9 +1,9 @@
import { Request, Response } from 'express'
import { Logger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseController {
protected readonly logger: Logger = new Logger('http')
protected readonly logger = Logger.type(LoggerType.HTTP)
protected sendSuccess(res: Response, data?: any, message?: string, status: number = 200) {
return res.status(status).json({

View File

@ -1,10 +1,10 @@
import { EntityManager } from '@mikro-orm/core'
import Database from '#application/database'
import { Logger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseEntity {
protected readonly logger: Logger = new Logger('entity')
protected readonly logger = Logger.type(LoggerType.ENTITY)
private getEntityManager(): EntityManager {
return Database.getEntityManager()

View File

@ -1,10 +1,10 @@
import { Server } from 'socket.io'
import { Logger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
import { TSocket } from '#application/types'
export abstract class BaseEvent {
protected readonly logger: Logger = new Logger('game')
protected readonly logger = Logger.type(LoggerType.GAME)
constructor(
readonly io: Server,

View File

@ -2,10 +2,10 @@ import { EntityManager } from '@mikro-orm/core'
import Database from '../database'
import { Logger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseRepository {
protected readonly logger: Logger = new Logger('repository')
protected readonly logger = Logger.type(LoggerType.REPOSITORY)
protected get em(): EntityManager {
return Database.getEntityManager()

View File

@ -1,5 +1,5 @@
import { Logger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
export abstract class BaseService {
protected readonly logger: Logger = new Logger('game')
protected readonly logger = Logger.type(LoggerType.GAME)
}

View File

@ -1,20 +1,21 @@
import { EntityManager } from '@mikro-orm/core'
import { MikroORM } from '@mikro-orm/mysql'
import { appLogger } from './logger'
import Logger, { LoggerType } from './logger'
import config from '../../mikro-orm.config'
class Database {
private static orm: MikroORM
private static em: EntityManager
private static logger = Logger.type(LoggerType.APP)
public static async initialize(): Promise<void> {
try {
Database.orm = await MikroORM.init(config)
Database.em = Database.orm.em.fork()
appLogger.info('Database connection initialized')
this.logger.info('Database connection initialized')
} catch (error) {
appLogger.error(`MikroORM connection failed: ${error}`)
this.logger.error(`MikroORM connection failed: ${error}`)
throw error
}
}

View File

@ -1,73 +1,51 @@
import pino from 'pino'
import { getRootPath } from './storage'
export enum LoggerType {
HTTP = 'http',
GAME = 'game',
GAME_MASTER = 'gameMaster',
APP = 'app',
QUEUE = 'queue',
COMMAND = 'command',
REPOSITORY = 'repository',
ENTITY = 'entity'
}
export class Logger {
private static readonly LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue', 'command', 'repository', 'entity'] as const
private readonly logger: ReturnType<typeof pino>
class Logger {
private instances: Map<LoggerType, ReturnType<typeof pino>> = new Map()
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
}
private getLogger(type: LoggerType): ReturnType<typeof pino> {
if (!this.instances.has(type)) {
this.instances.set(
type,
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
})
})
})
)
}
return this.instances.get(type)!
}
type(type: LoggerType) {
return {
info: (message: string, ...args: any[]) => this.getLogger(type).info(message, ...args),
error: (message: string, ...args: any[]) => this.getLogger(type).error(message, ...args),
warn: (message: string, ...args: any[]) => this.getLogger(type).warn(message, ...args),
debug: (message: string, ...args: any[]) => this.getLogger(type).debug(message, ...args)
}
}
}
export default new Logger()

View File

@ -7,13 +7,6 @@ import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPass
import UserService from '#services/userService'
export class AuthController extends BaseController {
private userService: UserService
constructor() {
super()
this.userService = new UserService()
}
/**
* Login user
* @param req
@ -24,7 +17,7 @@ export class AuthController extends BaseController {
try {
loginAccountSchema.parse({ username, password })
const user = await this.userService.login(username, password)
const user = await UserService.login(username, password)
if (user && typeof user !== 'boolean') {
const token = jwt.sign({ id: user.getId() }, config.JWT_SECRET, { expiresIn: '4h' })
@ -47,7 +40,7 @@ export class AuthController extends BaseController {
try {
registerAccountSchema.parse({ username, email, password })
const user = await this.userService.register(username, email, password)
const user = await UserService.register(username, email, password)
if (user) {
return this.sendSuccess(res, null, 'User registered successfully')
@ -69,7 +62,7 @@ export class AuthController extends BaseController {
try {
resetPasswordSchema.parse({ email })
const sentEmail = await this.userService.requestPasswordReset(email)
const sentEmail = await UserService.requestPasswordReset(email)
if (sentEmail) {
return this.sendSuccess(res, null, 'Password reset email sent')
@ -91,7 +84,7 @@ export class AuthController extends BaseController {
try {
newPasswordSchema.parse({ urlToken, password })
const resetPassword = await this.userService.resetPassword(urlToken, password)
const resetPassword = await UserService.resetPassword(urlToken, password)
if (resetPassword) {
return this.sendSuccess(res, null, 'Password has been reset')

View File

@ -4,10 +4,11 @@ import * as readline from 'readline'
import { Server } from 'socket.io'
import { commandLogger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
import { getAppPath } from '#application/storage'
class CommandManager {
private logger = Logger.type(LoggerType.COMMAND)
private commands: Map<string, any> = new Map()
private rl: readline.Interface
private io: Server | null = null
@ -27,7 +28,7 @@ class CommandManager {
public async boot(io: Server) {
this.io = io
await this.loadCommands()
commandLogger.info('Command manager loaded')
this.logger.info('Command manager loaded')
this.startPrompt()
}
@ -64,7 +65,7 @@ class CommandManager {
private async loadCommands() {
const directory = getAppPath('commands')
commandLogger.info(`Loading commands from: ${directory}`)
this.logger.info(`Loading commands from: ${directory}`)
try {
const files = await fs.promises.readdir(directory, { withFileTypes: true })
@ -80,26 +81,26 @@ class CommandManager {
try {
const module = await import(fullPath)
if (typeof module.default !== 'function') {
commandLogger.warn(`Unrecognized export in ${file.name}`)
this.logger.warn(`Unrecognized export in ${file.name}`)
continue
}
this.registerCommand(commandName, module.default)
} catch (error) {
commandLogger.error(`Error loading command ${file.name}: ${error instanceof Error ? error.message : String(error)}`)
this.logger.error(`Error loading command ${file.name}: ${error instanceof Error ? error.message : String(error)}`)
}
}
} catch (error) {
commandLogger.error(`Failed to read commands directory: ${error instanceof Error ? error.message : String(error)}`)
this.logger.error(`Failed to read commands directory: ${error instanceof Error ? error.message : String(error)}`)
}
}
private registerCommand(name: string, CommandClass: any) {
if (this.commands.has(name)) {
commandLogger.warn(`Command '${name}' is already registered. Overwriting...`)
this.logger.warn(`Command '${name}' is already registered. Overwriting...`)
}
this.commands.set(name, CommandClass)
commandLogger.info(`Registered command: ${name}`)
this.logger.info(`Registered command: ${name}`)
}
}

View File

@ -1,6 +1,6 @@
import { Server } from 'socket.io'
import { appLogger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
import worldRepository from '#repositories/worldRepository'
import worldService from '#services/worldService'
@ -11,12 +11,13 @@ class DateManager {
private io: Server | null = null
private intervalId: NodeJS.Timeout | null = null
private currentDate: Date = new Date()
private logger = Logger.type(LoggerType.APP)
public async boot(io: Server): Promise<void> {
this.io = io
await this.loadDate()
this.startDateLoop()
appLogger.info('Date manager loaded')
this.logger.info('Date manager loaded')
}
public async setTime(time: string): Promise<void> {
@ -38,7 +39,7 @@ class DateManager {
this.emitDate()
await this.saveDate()
} catch (error) {
appLogger.error(`Failed to set time: ${error instanceof Error ? error.message : String(error)}`)
this.logger.error(`Failed to set time: ${error instanceof Error ? error.message : String(error)}`)
throw error
}
}
@ -51,7 +52,7 @@ class DateManager {
this.currentDate = world.date
}
} catch (error) {
appLogger.error(`Failed to load date: ${error instanceof Error ? error.message : String(error)}`)
this.logger.error(`Failed to load date: ${error instanceof Error ? error.message : String(error)}`)
this.currentDate = new Date() // Use current date as fallback
}
}
@ -82,7 +83,7 @@ class DateManager {
date: this.currentDate
})
} catch (error) {
appLogger.error(`Failed to save date: ${error instanceof Error ? error.message : String(error)}`)
this.logger.error(`Failed to save date: ${error instanceof Error ? error.message : String(error)}`)
}
}

View File

@ -5,7 +5,7 @@ import IORedis from 'ioredis'
import { Server as SocketServer } from 'socket.io'
import config from '#application/config'
import { queueLogger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
import { getAppPath } from '#application/storage'
import { TSocket } from '#application/types'
@ -14,6 +14,7 @@ class QueueManager {
private queue!: Queue
private worker!: Worker
private io!: SocketServer
private logger = Logger.type(LoggerType.QUEUE)
public async boot(io: SocketServer) {
this.io = io
@ -24,9 +25,9 @@ class QueueManager {
try {
await this.connection.ping()
queueLogger.info('Successfully connected to Redis')
this.logger.info('Successfully connected to Redis')
} catch (error) {
queueLogger.error('Failed to connect to Redis:', error)
this.logger.error('Failed to connect to Redis:', error)
process.exit(1)
}
@ -40,14 +41,14 @@ class QueueManager {
})
this.worker.on('completed', (job) => {
queueLogger.info(`Job ${job?.id} has completed`)
this.logger.info(`Job ${job?.id} has completed`)
})
this.worker.on('failed', (job, err) => {
queueLogger.error(`Job ${job?.id} failed with error: ${err}`)
this.logger.error(`Job ${job?.id} failed with error: ${err}`)
})
queueLogger.info('Queue manager loaded')
this.logger.info('Queue manager loaded')
}
private async processJob(job: Job) {
@ -59,7 +60,7 @@ class QueueManager {
const jobPath = getAppPath('jobs', `${jobName}${extension}`)
if (!fs.existsSync(jobPath)) {
queueLogger.warn(`Job file not found: ${jobPath}`)
this.logger.warn(`Job file not found: ${jobPath}`)
return
}
@ -67,7 +68,7 @@ class QueueManager {
const JobClass = JobModule.default
if (!JobClass || typeof JobClass !== 'function') {
queueLogger.warn(`Invalid job class in file: ${jobPath}`)
this.logger.warn(`Invalid job class in file: ${jobPath}`)
return
}
@ -78,14 +79,14 @@ class QueueManager {
if (socket) {
await jobInstance.execute(this.io, socket)
} else {
queueLogger.warn(`Socket not found for job: ${socketId}`)
this.logger.warn(`Socket not found for job: ${socketId}`)
await jobInstance.execute(this.io)
}
} else {
await jobInstance.execute(this.io)
}
} catch (error: any) {
queueLogger.error(`Error processing job ${jobName}: ${error.message}`)
this.logger.error(`Error processing job ${jobName}: ${error.message}`)
}
}

View File

@ -1,6 +1,6 @@
import { User } from '@prisma/client'
import { appLogger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
type TLoggedInUsers = {
users: User[]
@ -8,10 +8,11 @@ type TLoggedInUsers = {
class UserManager {
private loggedInUsers: TLoggedInUsers[] = []
private logger = Logger.type(LoggerType.APP)
// Method to initialize user manager
public async boot() {
appLogger.info('User manager loaded')
this.logger.info('User manager loaded')
}
// Function that adds user to logged in users

View File

@ -1,4 +1,4 @@
import { gameLogger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
import { Zone } from '#entities/zone'
import LoadedZone from '#models/loadedZone'
import ZoneCharacter from '#models/zoneCharacter'
@ -6,23 +6,24 @@ import ZoneRepository from '#repositories/zoneRepository'
class ZoneManager {
private readonly zones = new Map<number, LoadedZone>()
private logger = Logger.type(LoggerType.GAME)
public async boot(): Promise<void> {
const zones = await ZoneRepository.getAll()
await Promise.all(zones.map((zone) => this.loadZone(zone)))
gameLogger.info(`Zone manager loaded with ${this.zones.size} zones`)
this.logger.info(`Zone manager loaded with ${this.zones.size} zones`)
}
public async loadZone(zone: Zone): Promise<void> {
const loadedZone = new LoadedZone(zone)
this.zones.set(zone.id, loadedZone)
gameLogger.info(`Zone ID ${zone.id} loaded`)
this.logger.info(`Zone ID ${zone.id} loaded`)
}
public unloadZone(zoneId: number): void {
this.zones.delete(zoneId)
gameLogger.info(`Zone ID ${zoneId} unloaded`)
this.logger.info(`Zone ID ${zoneId} unloaded`)
}
public getLoadedZones(): LoadedZone[] {

View File

@ -7,7 +7,7 @@ import { Server as SocketServer } from 'socket.io'
import config from '#application/config'
import Database from '#application/database'
import { Logger } from '#application/logger'
import Logger, { LoggerType } from '#application/logger'
import { getAppPath } from '#application/storage'
import { TSocket } from '#application/types'
import { HttpRouter } from '#http/router'
@ -23,7 +23,7 @@ export class Server {
private readonly app: Application
private readonly http: HTTPServer
private readonly io: SocketServer
private readonly logger: Logger = new Logger('app')
private readonly logger = Logger.type(LoggerType.APP)
/**
* Creates an instance of GameServer.
@ -50,7 +50,6 @@ export class Server {
*/
public async start() {
// Read log file and print to console for debugging
Logger.watch()
// Connect to database
try {

View File

@ -1,19 +1,12 @@
import { Server } from 'socket.io'
import { ZodError } from 'zod'
import { gameLogger } from '#application/logger'
import { TSocket } from '#application/types'
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'
export default class CharacterCreateEvent {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class CharacterCreateEvent extends BaseEvent {
public listen(): void {
this.socket.on('character:create', this.handleCharacterCreate.bind(this))
}
@ -52,9 +45,9 @@ export default class CharacterCreateEvent {
this.socket.emit('character:create:success')
this.socket.emit('character:list', characters)
gameLogger.info('character:create success')
this.logger.info('character:create success')
} catch (error: any) {
gameLogger.error(`character:create error: ${error.message}`)
this.logger.error(`character:create error: ${error.message}`)
if (error instanceof ZodError) {
return this.socket.emit('notification', { message: error.issues[0].message })
}

View File

@ -1,44 +1,36 @@
import { Server } from 'socket.io'
import { getArgs, isCommand } from '#application/chat'
import { gameLogger } from '#application/logger'
import { TSocket } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import CharacterRepository from '#repositories/characterRepository'
import ChatService from '#services/chatService'
type TypePayload = {
message: string
}
export default class AlertCommandEvent {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class AlertCommandEvent extends BaseEvent {
public listen(): void {
this.socket.on('chat:message', this.handleAlertCommand.bind(this))
}
private async handleAlertCommand(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
try {
if (!isCommand(data.message, 'alert')) {
if (!ChatService.isCommand(data.message, 'alert')) {
return
}
// Check if character exists
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
if (!character) {
gameLogger.error('chat:alert_command error', 'Character not found')
this.logger.error('chat:alert_command error', 'Character not found')
return callback(false)
}
// Check if the user is the GM
if (character.role !== 'gm') {
gameLogger.info(`User ${character.id} tried to set time but is not a game master.`)
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
return callback(false)
}
const args = getArgs('alert', data.message)
const args = ChatService.getArgs('alert', data.message)
if (!args) {
return callback(false)
@ -47,7 +39,7 @@ export default class AlertCommandEvent {
this.io.emit('notification', { title: 'Message from GM', message: args.join(' ') })
return callback(true)
} catch (error: any) {
gameLogger.error('chat:alert_command error', error.message)
this.logger.error('chat:alert_command error', error.message)
callback(false)
}
}

View File

@ -1,46 +1,38 @@
import { Server } from 'socket.io'
import { getArgs, isCommand } from '#application/chat'
import { gameLogger } from '#application/logger'
import { TSocket } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import DateManager from '#managers/dateManager'
import CharacterRepository from '#repositories/characterRepository'
import ChatService from '#services/chatService'
type TypePayload = {
message: string
}
export default class SetTimeCommand {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class SetTimeCommand extends BaseEvent {
public listen(): void {
this.socket.on('chat:message', this.handleAlertCommand.bind(this))
}
private async handleAlertCommand(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
try {
if (!isCommand(data.message, 'time')) {
if (!ChatService.isCommand(data.message, 'time')) {
return
}
// Check if character exists
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
if (!character) {
gameLogger.error('chat:alert_command error', 'Character not found')
this.logger.error('chat:alert_command error', 'Character not found')
return
}
// Check if the user is the GM
if (character.role !== 'gm') {
gameLogger.info(`User ${character.id} tried to set time but is not a game master.`)
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
return
}
// Get arguments
const args = getArgs('time', data.message)
const args = ChatService.getArgs('time', data.message)
if (!args) {
return
@ -54,7 +46,7 @@ export default class SetTimeCommand {
await DateManager.setTime(time)
} catch (error: any) {
gameLogger.error('command error', error.message)
this.logger.error('command error', error.message)
callback(false)
}
}

View File

@ -1,23 +1,15 @@
import { Server } from 'socket.io'
import { getArgs, isCommand } from '#application/chat'
import { gameLogger, gameMasterLogger } from '#application/logger'
import { TSocket } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import ZoneManager from '#managers/zoneManager'
import zoneManager from '#managers/zoneManager'
import ZoneCharacter from '#models/zoneCharacter'
import ZoneRepository from '#repositories/zoneRepository'
import ChatService from '#services/chatService'
type TypePayload = {
message: string
}
export default class TeleportCommandEvent {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class TeleportCommandEvent extends BaseEvent {
public listen(): void {
this.socket.on('chat:message', this.handleTeleportCommand.bind(this))
}
@ -27,7 +19,7 @@ export default class TeleportCommandEvent {
// Check if character exists
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
if (!zoneCharacter) {
gameLogger.error('chat:message error', 'Character not found')
this.logger.error('chat:message error', 'Character not found')
return
}
@ -35,13 +27,13 @@ export default class TeleportCommandEvent {
// Check if the user is the GM
if (character.role !== 'gm') {
gameLogger.info(`User ${character.id} tried to set time but is not a game master.`)
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
return
}
if (!isCommand(data.message, 'teleport')) return
if (!ChatService.isCommand(data.message, 'teleport')) return
const args = getArgs('teleport', data.message)
const args = ChatService.getArgs('teleport', data.message)
if (!args || args.length !== 1) {
this.socket.emit('notification', { title: 'Server message', message: 'Usage: /teleport <zoneId>' })
@ -87,9 +79,9 @@ export default class TeleportCommandEvent {
})
this.socket.emit('notification', { title: 'Server message', message: `You have been teleported to ${zone.name}` })
gameMasterLogger.info('teleport', `Character ${character.id} teleported to zone ${zone.id}`)
this.logger.info('teleport', `Character ${character.id} teleported to zone ${zone.id}`)
} catch (error: any) {
gameMasterLogger.error(`Error in teleport command: ${error.message}`)
this.logger.error(`Error in teleport command: ${error.message}`)
this.socket.emit('notification', { title: 'Server message', message: 'An error occurred while teleporting' })
}
}

View File

@ -1,47 +1,39 @@
import { Server } from 'socket.io'
import { isCommand } from '#application/chat'
import { gameLogger } from '#application/logger'
import { TSocket } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager'
import CharacterRepository from '#repositories/characterRepository'
import ChatService from '#services/chatService'
type TypePayload = {
message: string
}
export default class ToggleFogCommand {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class ToggleFogCommand extends BaseEvent {
public listen(): void {
this.socket.on('chat:message', this.handleAlertCommand.bind(this))
}
private async handleAlertCommand(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
try {
if (!isCommand(data.message, 'fog')) {
if (!ChatService.isCommand(data.message, 'fog')) {
return
}
// Check if character exists
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
if (!character) {
gameLogger.error('chat:alert_command error', 'Character not found')
this.logger.error('chat:alert_command error', 'Character not found')
return
}
// Check if the user is the GM
if (character.role !== 'gm') {
gameLogger.info(`User ${character.id} tried to set time but is not a game master.`)
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
return
}
await WeatherManager.toggleFog()
} catch (error: any) {
gameLogger.error('command error', error.message)
this.logger.error('command error', error.message)
callback(false)
}
}

View File

@ -1,47 +1,39 @@
import { Server } from 'socket.io'
import { isCommand } from '#application/chat'
import { gameLogger } from '#application/logger'
import { TSocket } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager'
import CharacterRepository from '#repositories/characterRepository'
import ChatService from '#services/chatService'
type TypePayload = {
message: string
}
export default class ToggleRainCommand {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class ToggleRainCommand extends BaseEvent {
public listen(): void {
this.socket.on('chat:message', this.handleAlertCommand.bind(this))
}
private async handleAlertCommand(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
try {
if (!isCommand(data.message, 'rain')) {
if (!ChatService.isCommand(data.message, 'rain')) {
return
}
// Check if character exists
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, this.socket.characterId!)
if (!character) {
gameLogger.error('chat:alert_command error', 'Character not found')
this.logger.error('chat:alert_command error', 'Character not found')
return
}
// Check if the user is the GM
if (character.role !== 'gm') {
gameLogger.info(`User ${character.id} tried to set time but is not a game master.`)
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
return
}
await WeatherManager.toggleRain()
} catch (error: any) {
gameLogger.error('command error', error.message)
this.logger.error('command error', error.message)
callback(false)
}
}

View File

@ -1,8 +1,4 @@
import { Server } from 'socket.io'
import { isCommand } from '#application/chat'
import { gameLogger } from '#application/logger'
import { TSocket } from '#application/types'
import { BaseEvent } from '#application/base/baseEvent'
import ZoneManager from '#managers/zoneManager'
import ZoneRepository from '#repositories/zoneRepository'
import ChatService from '#services/chatService'
@ -11,44 +7,38 @@ type TypePayload = {
message: string
}
export default class ChatMessageEvent {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class ChatMessageEvent extends BaseEvent {
public listen(): void {
this.socket.on('chat:message', this.handleEvent.bind(this))
}
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
try {
if (!data.message || isCommand(data.message)) {
if (!data.message || ChatService.isCommand(data.message)) {
return callback(false)
}
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
if (!zoneCharacter) {
gameLogger.error('chat:message error', 'Character not found')
this.logger.error('chat:message error', 'Character not found')
return callback(false)
}
const character = zoneCharacter.character
const zone = await ZoneRepository.getById(character.zoneId)
const zone = await ZoneRepository.getById(character.zone?.id!)
if (!zone) {
gameLogger.error('chat:message error', 'Zone not found')
this.logger.error('chat:message error', 'Zone not found')
return callback(false)
}
const chatService = new ChatService()
if (await chatService.sendZoneMessage(this.io, this.socket, data.message, character.id, zone.id)) {
if (await ChatService.sendZoneMessage(this.io, this.socket, data.message, character.id, zone.id)) {
return callback(true)
}
callback(false)
} catch (error: any) {
gameLogger.error('chat:message error', error.message)
this.logger.error('chat:message error', error.message)
callback(false)
}
}

View File

@ -1,5 +1,5 @@
import ZoneManager from '#managers/zoneManager'
import { BaseEvent } from '#application/base/baseEvent'
import ZoneManager from '#managers/zoneManager'
export default class DisconnectEvent extends BaseEvent {
public listen(): void {

View File

@ -1,5 +1,5 @@
import UserRepository from '#repositories/userRepository'
import { BaseEvent } from '#application/base/baseEvent'
import UserRepository from '#repositories/userRepository'
export default class LoginEvent extends BaseEvent {
public listen(): void {

View File

@ -1,6 +1,6 @@
import { BaseEvent } from '#application/base/baseEvent'
import ZoneManager from '#managers/zoneManager'
import CharacterRepository from '#repositories/characterRepository'
import { BaseEvent } from '#application/base/baseEvent'
export default class ZoneLeaveEvent extends BaseEvent {
public listen(): void {

View File

@ -1,10 +1,10 @@
import { BaseEvent } from '#application/base/baseEvent'
import { ZoneEventTileWithTeleport } from '#application/types'
import ZoneManager from '#managers/zoneManager'
import ZoneCharacter from '#models/zoneCharacter'
import zoneEventTileRepository from '#repositories/zoneEventTileRepository'
import CharacterService from '#services/characterService'
import ZoneEventTileService from '#services/zoneEventTileService'
import { BaseEvent } from '#application/base/baseEvent'
export default class CharacterMove extends BaseEvent {
private readonly characterService = CharacterService

View File

@ -1,5 +1,5 @@
import WeatherManager from '#managers/weatherManager'
import { BaseEvent } from '#application/base/baseEvent'
import WeatherManager from '#managers/weatherManager'
export default class Weather extends BaseEvent {
public listen(): void {