forked from noxious/server
Date manager refactor
This commit is contained in:
parent
8cca44a3b4
commit
14d296e5a9
@ -3,106 +3,64 @@ import Logger, { LoggerType } from '@/application/logger'
|
||||
import { World } from '@/entities/world'
|
||||
import SocketManager from '@/managers/socketManager'
|
||||
import WorldRepository from '@/repositories/worldRepository'
|
||||
import { Server } from 'socket.io'
|
||||
import DateService from '@/services/dateService'
|
||||
|
||||
interface GameTimeConfig {
|
||||
readonly GAME_SPEED: number
|
||||
readonly UPDATE_INTERVAL: number
|
||||
}
|
||||
|
||||
class DateManager {
|
||||
private static readonly CONFIG = {
|
||||
private static readonly CONFIG: GameTimeConfig = {
|
||||
GAME_SPEED: 8, // 24 game hours / 3 real hours
|
||||
UPDATE_INTERVAL: 1000 // 1 minute
|
||||
} as const
|
||||
UPDATE_INTERVAL: 1000 * 6 // 1 minute
|
||||
}
|
||||
|
||||
private readonly logger = Logger.type(LoggerType.APP)
|
||||
private readonly worldRepository = new WorldRepository()
|
||||
private currentDate = new Date()
|
||||
private intervalId: NodeJS.Timeout | null = null
|
||||
|
||||
public async boot(): Promise<void> {
|
||||
await this.loadDate()
|
||||
try {
|
||||
this.currentDate = (await this.worldRepository.getFirst())?.date ?? new Date()
|
||||
this.startDateLoop()
|
||||
this.logger.info('Date manager loaded')
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to boot date manager: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
|
||||
public getCurrentDate(): Date {
|
||||
return this.currentDate
|
||||
}
|
||||
|
||||
public async setTime(timeString: string): Promise<void> {
|
||||
try {
|
||||
const newDate = this.parseTimeString(timeString)
|
||||
if (!newDate) return
|
||||
const newDate = DateService.parseTimeString(timeString)
|
||||
if (!newDate) throw new Error('Invalid time format')
|
||||
|
||||
this.currentDate = newDate
|
||||
this.emitDate()
|
||||
await this.saveDate()
|
||||
await this.synchronize()
|
||||
} catch (error) {
|
||||
this.handleError('Failed to set time', error)
|
||||
this.logger.error(`Failed to set time: ${error instanceof Error ? error.message : String(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)
|
||||
if (!hours || !minutes) return null
|
||||
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()
|
||||
this.intervalId = setInterval(async () => {
|
||||
const { GAME_SPEED, UPDATE_INTERVAL } = DateManager.CONFIG
|
||||
this.currentDate = new Date(this.currentDate.getTime() + DateService.calculateGameTimeAdvance(GAME_SPEED, UPDATE_INTERVAL))
|
||||
await this.synchronize()
|
||||
}, 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 {
|
||||
const io = SocketManager.getIO()
|
||||
io?.emit(SocketEvent.DATE, this.currentDate)
|
||||
}
|
||||
|
||||
private async saveDate(): Promise<void> {
|
||||
private async synchronize(): Promise<void> {
|
||||
try {
|
||||
const worldRepository = new WorldRepository()
|
||||
|
||||
let world = await worldRepository.getFirst()
|
||||
if (!world) world = new World()
|
||||
|
||||
SocketManager.getIO().emit(SocketEvent.DATE, this.currentDate)
|
||||
const world = (await this.worldRepository.getFirst()) ?? new World()
|
||||
await world.setDate(this.currentDate).save()
|
||||
} catch (error) {
|
||||
this.handleError('Failed to save date', error)
|
||||
this.logger.error(`Failed to sync date state: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
private handleError(message: string, error: unknown): void {
|
||||
this.logger.error(`${message}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default new DateManager()
|
||||
|
45
src/services/dateService.ts
Normal file
45
src/services/dateService.ts
Normal file
@ -0,0 +1,45 @@
|
||||
interface TimeComponents {
|
||||
hours: number
|
||||
minutes: number
|
||||
}
|
||||
|
||||
export class DateService {
|
||||
public parseTimeString(timeString: string): Date | null {
|
||||
const timeRegex = /^(\d{1,2}):(\d{2})(?::\d{2})?$/
|
||||
const match = timeString.match(timeRegex)
|
||||
|
||||
if (match && match[1] && match[2]) {
|
||||
const components: TimeComponents = {
|
||||
hours: parseInt(match[1], 10),
|
||||
minutes: parseInt(match[2], 10)
|
||||
}
|
||||
|
||||
return this.isValidTimeComponents(components) ? this.createDateWithComponents(components, new Date()) : null
|
||||
}
|
||||
|
||||
return this.parseFullDate(timeString)
|
||||
}
|
||||
|
||||
public isValidTimeComponents(components: TimeComponents): boolean {
|
||||
const { hours, minutes } = components
|
||||
return hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60
|
||||
}
|
||||
|
||||
public createDateWithComponents(components: TimeComponents, baseDate: Date): Date {
|
||||
const { hours, minutes } = components
|
||||
const newDate = new Date(baseDate)
|
||||
newDate.setHours(hours, minutes, 0, 0)
|
||||
return newDate
|
||||
}
|
||||
|
||||
public parseFullDate(dateString: string): Date | null {
|
||||
const date = new Date(dateString)
|
||||
return isNaN(date.getTime()) ? null : date
|
||||
}
|
||||
|
||||
public calculateGameTimeAdvance(gameSpeed: number, updateInterval: number): number {
|
||||
return gameSpeed * updateInterval
|
||||
}
|
||||
}
|
||||
|
||||
export default new DateService()
|
Loading…
x
Reference in New Issue
Block a user