server/src/managers/weatherManager.ts

138 lines
4.1 KiB
TypeScript

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'
type WeatherState = {
isRainEnabled: boolean
rainPercentage: number
isFogEnabled: boolean
fogDensity: number
}
class WeatherManager {
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,
isFogEnabled: false,
fogDensity: 0
}
public async boot(): Promise<void> {
this.io = SocketManager.getIO()
await this.loadWeather()
this.startWeatherLoop()
this.logger.info('Weather manager loaded')
}
public getWeatherState(): WeatherState {
return { ...this.weatherState }
}
public async toggleRain(): Promise<void> {
this.updateWeatherProperty('rain')
await this.saveAndEmitWeather()
}
public async toggleFog(): Promise<void> {
this.updateWeatherProperty('fog')
await this.saveAndEmitWeather()
}
public cleanup(): void {
this.intervalId && clearInterval(this.intervalId)
}
private async loadWeather(): Promise<void> {
try {
const worldRepository = new WorldRepository()
const world = await worldRepository.getFirst()
if (world) {
this.weatherState = {
isRainEnabled: world.isRainEnabled,
rainPercentage: world.rainPercentage,
isFogEnabled: world.isFogEnabled,
fogDensity: world.fogDensity
}
}
} catch (error) {
this.logError('load', error)
}
}
private startWeatherLoop(): void {
this.intervalId = setInterval(async () => {
this.updateRandomWeather()
await this.saveAndEmitWeather()
}, WeatherManager.CONFIG.UPDATE_INTERVAL_MS)
}
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 ? this.getRandomNumber(WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min, WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max) : 0
}
if (type === 'fog') {
this.weatherState.isFogEnabled = !this.weatherState.isFogEnabled
this.weatherState.fogDensity = this.weatherState.isFogEnabled ? this.getRandomNumber(WeatherManager.CONFIG.FOG_DENSITY_RANGE.min, WeatherManager.CONFIG.FOG_DENSITY_RANGE.max) : 0
}
}
private async saveAndEmitWeather(): Promise<void> {
await this.saveWeather()
this.emitWeather()
}
private emitWeather(): void {
this.io?.emit('weather', this.weatherState)
}
private async saveWeather(): Promise<void> {
try {
const worldRepository = new WorldRepository()
await (await worldRepository.getFirst())
?.setIsRainEnabled(this.weatherState.isRainEnabled)
.setRainPercentage(this.weatherState.rainPercentage)
.setIsFogEnabled(this.weatherState.isFogEnabled)
.setFogDensity(this.weatherState.fogDensity)
.save()
} catch (error) {
this.logError('save', error)
}
}
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()