1
0
forked from noxious/server

#184: Added weather manager that periodically changes the weather in-game and emits this to players

This commit is contained in:
Dennis Postma 2024-11-05 22:45:57 +01:00
parent 3a566dae5a
commit ae0241fecb
3 changed files with 126 additions and 0 deletions

View File

@ -0,0 +1,98 @@
import { Server } from 'socket.io'
import { appLogger } from '../utilities/logger'
import { getRootPath } from '../utilities/storage'
import { readJsonValue, setJsonValue } from '../utilities/json'
interface WeatherState {
isRainEnabled: boolean
rainPercentage: number
isFogEnabled: boolean
fogDensity: number
}
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 io: Server | null = null
private intervalId: NodeJS.Timeout | null = null
private weatherState: WeatherState = {
isRainEnabled: false,
rainPercentage: 0,
isFogEnabled: false,
fogDensity: 0
}
public async boot(io: Server): Promise<void> {
this.io = io
await this.loadWeather()
this.startWeatherLoop()
appLogger.info('Weather manager loaded')
}
private async loadWeather(): Promise<void> {
try {
this.weatherState.isRainEnabled = await readJsonValue<boolean>(this.getWorldFilePath(), 'isRainEnabled')
this.weatherState.rainPercentage = await readJsonValue<number>(this.getWorldFilePath(), 'rainPercentage')
this.weatherState.isFogEnabled = await readJsonValue<boolean>(this.getWorldFilePath(), 'isFogEnabled')
this.weatherState.fogDensity = await readJsonValue<number>(this.getWorldFilePath(), 'fogDensity')
} catch (error) {
appLogger.error(`Failed to load weather: ${error instanceof Error ? error.message : String(error)}`)
}
}
public async getWeatherState(): Promise<WeatherState> {
return this.weatherState
}
private startWeatherLoop(): void {
this.intervalId = setInterval(() => {
this.updateWeather()
this.emitWeather()
this.saveWeather()
}, WeatherManager.UPDATE_INTERVAL)
}
private updateWeather(): void {
// Update rain
if (Math.random() < WeatherManager.RAIN_CHANCE) {
this.weatherState.isRainEnabled = !this.weatherState.isRainEnabled
this.weatherState.rainPercentage = this.weatherState.isRainEnabled
? Math.floor(Math.random() * 50) + 50 // 50-100%
: 0
}
// Update fog
if (Math.random() < WeatherManager.FOG_CHANCE) {
this.weatherState.isFogEnabled = !this.weatherState.isFogEnabled
this.weatherState.fogDensity = this.weatherState.isFogEnabled
? Math.random() * 0.7 + 0.3 // 0.3-1.0
: 0
}
}
private emitWeather(): void {
this.io?.emit('weather', this.weatherState)
}
private async saveWeather(): Promise<void> {
try {
const promises = [
await setJsonValue(this.getWorldFilePath(), 'isRainEnabled', this.weatherState.isRainEnabled),
await setJsonValue(this.getWorldFilePath(), 'rainPercentage', this.weatherState.rainPercentage),
await setJsonValue(this.getWorldFilePath(), 'isFogEnabled', this.weatherState.isFogEnabled),
await setJsonValue(this.getWorldFilePath(), 'fogDensity', this.weatherState.fogDensity)
]
await Promise.all(promises)
} catch (error) {
appLogger.error(`Failed to save weather: ${error instanceof Error ? error.message : String(error)}`)
}
}
private getWorldFilePath(): string {
return getRootPath('data', 'world.json')
}
}
export default new WeatherManager()

View File

@ -16,6 +16,7 @@ import CommandManager from './managers/commandManager'
import CharacterManager from './managers/characterManager'
import QueueManager from './managers/queueManager'
import DateManager from './managers/dateManager'
import WeatherManager from './managers/weatherManager'
export class Server {
private readonly app: Application
@ -72,6 +73,9 @@ export class Server {
// Load date manager
await DateManager.boot(this.io)
// Load weather manager
await WeatherManager.boot(this.io)
// Load zoneEditor manager
await ZoneManager.boot()

View File

@ -0,0 +1,24 @@
import { Server } from 'socket.io'
import { TSocket } from '../utilities/types'
import { gameLogger } from '../utilities/logger'
import WeatherManager from '../managers/weatherManager'
export default class Weather {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
public listen(): void {
this.socket.on('weather', this.handleEvent.bind(this))
}
private async handleEvent(): Promise<void> {
try {
const weather = await WeatherManager.getWeatherState()
this.socket.emit('weather', weather)
} catch (error: any) {
gameLogger.error('error', error.message)
}
}
}