Improved readability of weather and date managers
This commit is contained in:
parent
0464538b1c
commit
2d6831b4ef
@ -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<void> {
|
||||
this.io = io
|
||||
public async boot(): Promise<void> {
|
||||
this.io = SocketManager.getIO()
|
||||
await this.loadDate()
|
||||
this.startDateLoop()
|
||||
this.logger.info('Date manager loaded')
|
||||
}
|
||||
|
||||
public async setTime(time: string): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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()
|
@ -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<void> {
|
||||
// this.io = io
|
||||
// await this.loadWeather()
|
||||
// this.startWeatherLoop()
|
||||
public async boot(): Promise<void> {
|
||||
this.io = SocketManager.getIO()
|
||||
await this.loadWeather()
|
||||
this.startWeatherLoop()
|
||||
this.logger.info('Weather manager loaded')
|
||||
}
|
||||
|
||||
public async toggleRain(): Promise<void> {
|
||||
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<void> {
|
||||
this.updateWeatherProperty('rain')
|
||||
await this.saveAndEmitWeather()
|
||||
}
|
||||
|
||||
public async toggleFog(): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.saveWeather()
|
||||
this.emitWeather()
|
||||
}
|
||||
|
||||
private emitWeather(): void {
|
||||
this.io?.emit('weather', this.weatherState)
|
||||
}
|
||||
|
||||
private async saveWeather(): Promise<void> {
|
||||
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()
|
@ -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()
|
||||
])
|
||||
|
Loading…
x
Reference in New Issue
Block a user