server/src/managers/weatherManager.ts

152 lines
4.3 KiB
TypeScript

import { Server } from 'socket.io'
import Logger, { LoggerType } from '#application/logger'
import { World } from '#entities/world'
import SocketManager from '#managers/socketManager'
import WorldRepository from '#repositories/worldRepository'
type WeatherState = {
rainPercentage: number
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 = {
rainPercentage: 0,
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 randomWeatherValue(type: 'rain' | 'fog' ) {
switch (type) {
case 'rain':
return this.getRandomNumber(WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min, WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max)
case 'fog':
return this.getRandomNumber(WeatherManager.CONFIG.FOG_DENSITY_RANGE.min, WeatherManager.CONFIG.FOG_DENSITY_RANGE.max)
}
}
public async setRainValue(value? : number): Promise<void> {
if (value === undefined) {
value = this.weatherState.fogDensity > 0 ? 0 : this.randomWeatherValue('fog')
}
this.updateWeatherProperty('rain', value)
await this.saveAndEmitWeather()
}
public async setFogValue(value? : number): Promise<void> {
if (value === undefined) {
value = this.weatherState.fogDensity > 0 ? 0 : this.randomWeatherValue('fog')
}
this.updateWeatherProperty('fog', value)
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 = {
rainPercentage: world.rainPercentage,
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', this.randomWeatherValue('rain') )
}
if (Math.random() < WeatherManager.CONFIG.FOG_CHANCE) {
this.updateWeatherProperty('fog', this.randomWeatherValue('fog'))
}
}
private updateWeatherProperty(type: 'rain' | 'fog', value: number): void {
if (type === 'rain') {
this.weatherState.rainPercentage = value
}
if (type === 'fog') {
this.weatherState.fogDensity = value
}
}
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()
let world = await worldRepository.getFirst()
if (!world) world = new World()
//left them in here because the
await world
.setRainPercentage(this.weatherState.rainPercentage)
.setIsRainEnabled(this.weatherState.rainPercentage > 0)
.setFogDensity(this.weatherState.fogDensity)
.setIsFogEnabled(this.weatherState.fogDensity > 0)
.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()