diff --git a/src/managers/dateManager.ts b/src/managers/dateManager.ts index 3a554e8..2bafddd 100644 --- a/src/managers/dateManager.ts +++ b/src/managers/dateManager.ts @@ -1,75 +1,83 @@ import { Server } from 'socket.io' - import Logger, { LoggerType } from '#application/logger' import worldRepository from '#repositories/worldRepository' import worldService from '#services/worldService' +import SocketManager from '#managers/socketManager' class DateManager { - private static readonly GAME_SPEED = 8 // 24 game hours / 3 real hours - private static readonly UPDATE_INTERVAL = 1000 // 1 second + private static readonly CONFIG = { + GAME_SPEED: 8, // 24 game hours / 3 real hours + UPDATE_INTERVAL: 1000, // 1 second + } as const private io: Server | null = null private intervalId: NodeJS.Timeout | null = null - private currentDate: Date = new Date() - private logger = Logger.type(LoggerType.APP) + private currentDate = new Date() + private readonly logger = Logger.type(LoggerType.APP) - public async boot(io: Server): Promise { - this.io = io + public async boot(): Promise { + this.io = SocketManager.getIO() await this.loadDate() this.startDateLoop() this.logger.info('Date manager loaded') } - public async setTime(time: string): Promise { - try { - let newDate: Date + public getCurrentDate(): Date { + return this.currentDate + } - // Check if it's just a time (HH:mm or HH:mm:ss format) - if (/^\d{1,2}:\d{2}(:\d{2})?$/.test(time)) { - const [hours, minutes] = time.split(':').map(Number) - newDate = new Date(this.currentDate) // Clone current date - newDate.setHours(hours, minutes) - } else { - // Treat as full datetime string - newDate = new Date(time) - if (isNaN(newDate.getTime())) return - } + public async setTime(timeString: string): Promise { + try { + const newDate = this.parseTimeString(timeString) + if (!newDate) return this.currentDate = newDate this.emitDate() await this.saveDate() } catch (error) { - this.logger.error(`Failed to set time: ${error instanceof Error ? error.message : String(error)}`) + this.handleError('Failed to set time', error) throw error } } + public cleanup(): void { + this.intervalId && clearInterval(this.intervalId) + } + private async loadDate(): Promise { try { const world = await worldRepository.getFirst() - - if (world) { - this.currentDate = world.date - } + this.currentDate = world?.date ?? new Date() } catch (error) { - this.logger.error(`Failed to load date: ${error instanceof Error ? error.message : String(error)}`) - this.currentDate = new Date() // Use current date as fallback + this.handleError('Failed to load date', error) + this.currentDate = new Date() } } + private parseTimeString(timeString: string): Date | null { + const timeOnlyPattern = /^\d{1,2}:\d{2}(:\d{2})?$/ + + if (timeOnlyPattern.test(timeString)) { + const [hours, minutes] = timeString.split(':').map(Number) + const newDate = new Date(this.currentDate) + newDate.setHours(hours, minutes) + return newDate + } + + const fullDate = new Date(timeString) + return isNaN(fullDate.getTime()) ? null : fullDate + } + private startDateLoop(): void { this.intervalId = setInterval(() => { this.advanceGameTime() this.emitDate() void this.saveDate() - }, DateManager.UPDATE_INTERVAL) + }, DateManager.CONFIG.UPDATE_INTERVAL) } private advanceGameTime(): void { - if (!this.currentDate) { - this.currentDate = new Date() - } - const advanceMilliseconds = DateManager.GAME_SPEED * DateManager.UPDATE_INTERVAL + const advanceMilliseconds = DateManager.CONFIG.GAME_SPEED * DateManager.CONFIG.UPDATE_INTERVAL this.currentDate = new Date(this.currentDate.getTime() + advanceMilliseconds) } @@ -79,23 +87,15 @@ class DateManager { private async saveDate(): Promise { try { - await worldService.update({ - date: this.currentDate - }) + await worldService.update({ date: this.currentDate }) } catch (error) { - this.logger.error(`Failed to save date: ${error instanceof Error ? error.message : String(error)}`) + this.handleError('Failed to save date', error) } } - public cleanup(): void { - if (this.intervalId) { - clearInterval(this.intervalId) - } - } - - public getCurrentDate(): Date { - return this.currentDate + private handleError(message: string, error: unknown): void { + this.logger.error(`${message}: ${error instanceof Error ? error.message : String(error)}`) } } -export default new DateManager() +export default new DateManager() \ No newline at end of file diff --git a/src/managers/weatherManager.ts b/src/managers/weatherManager.ts index eaf1230..bc9c9b7 100644 --- a/src/managers/weatherManager.ts +++ b/src/managers/weatherManager.ts @@ -1,10 +1,10 @@ import { Server } from 'socket.io' - import Logger, { LoggerType } from '#application/logger' import worldRepository from '#repositories/worldRepository' import worldService from '#services/worldService' +import SocketManager from '#managers/socketManager' -interface WeatherState { +type WeatherState = { isRainEnabled: boolean rainPercentage: number isFogEnabled: boolean @@ -12,13 +12,18 @@ interface WeatherState { } class WeatherManager { - private static readonly UPDATE_INTERVAL = 60000 // Check weather every minute - private static readonly RAIN_CHANCE = 0.2 // 20% chance of rain - private static readonly FOG_CHANCE = 0.15 // 15% chance of fog - private readonly logger = Logger.type(LoggerType.APP) + private static readonly CONFIG = { + UPDATE_INTERVAL_MS: 60_000, // Check weather every minute + RAIN_CHANCE: 0.2, // 20% chance + FOG_CHANCE: 0.15, // 15% chance + RAIN_PERCENTAGE_RANGE: { min: 50, max: 100 }, + FOG_DENSITY_RANGE: { min: 30, max: 100 } + } as const + private readonly logger = Logger.type(LoggerType.APP) private io: Server | null = null private intervalId: NodeJS.Timeout | null = null + private weatherState: WeatherState = { isRainEnabled: false, rainPercentage: 0, @@ -26,37 +31,34 @@ class WeatherManager { fogDensity: 0 } - public async boot(io: Server): Promise { - // this.io = io - // await this.loadWeather() - // this.startWeatherLoop() + public async boot(): Promise { + this.io = SocketManager.getIO() + await this.loadWeather() + this.startWeatherLoop() this.logger.info('Weather manager loaded') } - public async toggleRain(): Promise { - this.weatherState.isRainEnabled = !this.weatherState.isRainEnabled - this.weatherState.rainPercentage = this.weatherState.isRainEnabled - ? Math.floor(Math.random() * 50) + 50 // 50-100% - : 0 + public getWeatherState(): WeatherState { + return { ...this.weatherState } + } - await this.saveWeather() - this.emitWeather() + public async toggleRain(): Promise { + this.updateWeatherProperty('rain') + await this.saveAndEmitWeather() } public async toggleFog(): Promise { - this.weatherState.isFogEnabled = !this.weatherState.isFogEnabled - this.weatherState.fogDensity = this.weatherState.isFogEnabled - ? Math.floor((Math.random() * 0.7 + 0.3) * 100) // Convert 0.3-1.0 to 30-100 - : 0 + this.updateWeatherProperty('fog') + await this.saveAndEmitWeather() + } - await this.saveWeather() - this.emitWeather() + public cleanup(): void { + this.intervalId && clearInterval(this.intervalId) } private async loadWeather(): Promise { try { const world = await worldRepository.getFirst() - if (world) { this.weatherState = { isRainEnabled: world.isRainEnabled, @@ -66,64 +68,74 @@ class WeatherManager { } } } catch (error) { - this.logger.error(`Failed to load weather: ${error instanceof Error ? error.message : String(error)}`) + this.logError('load', error) } } - public getWeatherState(): WeatherState { - return this.weatherState - } - private startWeatherLoop(): void { this.intervalId = setInterval(async () => { - this.updateWeather() - this.emitWeather() - await this.saveWeather().catch((error) => { - this.logger.error(`Failed to save weather: ${error instanceof Error ? error.message : String(error)}`) - }) - }, WeatherManager.UPDATE_INTERVAL) + this.updateRandomWeather() + await this.saveAndEmitWeather() + }, WeatherManager.CONFIG.UPDATE_INTERVAL_MS) } - private updateWeather(): void { - // Update rain - if (Math.random() < WeatherManager.RAIN_CHANCE) { + private updateRandomWeather(): void { + if (Math.random() < WeatherManager.CONFIG.RAIN_CHANCE) { + this.updateWeatherProperty('rain') + } + if (Math.random() < WeatherManager.CONFIG.FOG_CHANCE) { + this.updateWeatherProperty('fog') + } + } + + private updateWeatherProperty(type: 'rain' | 'fog'): void { + if (type === 'rain') { this.weatherState.isRainEnabled = !this.weatherState.isRainEnabled this.weatherState.rainPercentage = this.weatherState.isRainEnabled - ? Math.floor(Math.random() * 50) + 50 // 50-100% + ? this.getRandomNumber( + WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min, + WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max + ) : 0 } - // Update fog - if (Math.random() < WeatherManager.FOG_CHANCE) { + if (type === 'fog') { this.weatherState.isFogEnabled = !this.weatherState.isFogEnabled this.weatherState.fogDensity = this.weatherState.isFogEnabled - ? Math.floor((Math.random() * 0.7 + 0.3) * 100) // Convert 0.3-1.0 to 30-100 + ? this.getRandomNumber( + WeatherManager.CONFIG.FOG_DENSITY_RANGE.min, + WeatherManager.CONFIG.FOG_DENSITY_RANGE.max + ) : 0 } } + private async saveAndEmitWeather(): Promise { + await this.saveWeather() + this.emitWeather() + } + private emitWeather(): void { this.io?.emit('weather', this.weatherState) } private async saveWeather(): Promise { try { - await worldService.update({ - isRainEnabled: this.weatherState.isRainEnabled, - rainPercentage: this.weatherState.rainPercentage, - isFogEnabled: this.weatherState.isFogEnabled, - fogDensity: this.weatherState.fogDensity - }) + await worldService.update(this.weatherState) } catch (error) { - this.logger.error(`Failed to save weather: ${error instanceof Error ? error.message : String(error)}`) + this.logError('save', error) } } - public cleanup(): void { - if (this.intervalId) { - clearInterval(this.intervalId) - } + private getRandomNumber(min: number, max: number): number { + return Math.floor(Math.random() * (max - min + 1)) + min + } + + private logError(operation: string, error: unknown): void { + this.logger.error( + `Failed to ${operation} weather: ${error instanceof Error ? error.message : String(error)}` + ) } } -export default new WeatherManager() +export default new WeatherManager() \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 2d62f0e..39c46f1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -43,8 +43,8 @@ export class Server { SocketManager.boot(this.app, this.http), QueueManager.boot(), UserManager.boot(), - // DateManager.boot(SocketManager.getIO()), - // WeatherManager.boot(SocketManager.getIO()), + DateManager.boot(), + WeatherManager.boot(), ZoneManager.boot(), ConsoleManager.boot() ])