105 lines
2.9 KiB
TypeScript
105 lines
2.9 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'
|
|
|
|
class DateManager {
|
|
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 = new Date()
|
|
private readonly logger = Logger.type(LoggerType.APP)
|
|
|
|
public async boot(): Promise<void> {
|
|
this.io = SocketManager.getIO()
|
|
await this.loadDate()
|
|
this.startDateLoop()
|
|
this.logger.info('Date manager loaded')
|
|
}
|
|
|
|
public getCurrentDate(): Date {
|
|
return this.currentDate
|
|
}
|
|
|
|
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.handleError('Failed to set time', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
public cleanup(): void {
|
|
this.intervalId && clearInterval(this.intervalId)
|
|
}
|
|
|
|
private async loadDate(): Promise<void> {
|
|
try {
|
|
const worldRepository = new WorldRepository()
|
|
const world = await worldRepository.getFirst()
|
|
this.currentDate = world?.date ?? new Date()
|
|
} catch (error) {
|
|
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.CONFIG.UPDATE_INTERVAL)
|
|
}
|
|
|
|
private advanceGameTime(): void {
|
|
const advanceMilliseconds = DateManager.CONFIG.GAME_SPEED * DateManager.CONFIG.UPDATE_INTERVAL
|
|
this.currentDate = new Date(this.currentDate.getTime() + advanceMilliseconds)
|
|
}
|
|
|
|
private emitDate(): void {
|
|
this.io?.emit('date', this.currentDate)
|
|
}
|
|
|
|
private async saveDate(): Promise<void> {
|
|
try {
|
|
const worldRepository = new WorldRepository()
|
|
await (await worldRepository.getFirst())?.setDate(this.currentDate).save()
|
|
} catch (error) {
|
|
this.handleError('Failed to save date', error)
|
|
}
|
|
}
|
|
|
|
private handleError(message: string, error: unknown): void {
|
|
this.logger.error(`${message}: ${error instanceof Error ? error.message : String(error)}`)
|
|
}
|
|
}
|
|
|
|
export default new DateManager()
|