forked from noxious/server
Renamed files to storage, re-worked datetimeManager, added json help utilities
This commit is contained in:
parent
049b9de2b3
commit
bb9f62a9c8
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npx prisma migrate deploy && node dist/server.js",
|
"start": "npx prisma migrate deploy && node dist/server.js",
|
||||||
"dev": "nodemon --exec ts-node src/server.ts",
|
"dev": "nodemon --ignore 'data/*' --exec ts-node src/server.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ import fs from 'fs'
|
|||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import { commandLogger } from '../utilities/logger'
|
import { commandLogger } from '../utilities/logger'
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
import { getPublicPath } from '../utilities/files'
|
import { getPublicPath } from '../utilities/storage'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export default class TilesCommand {
|
export default class TilesCommand {
|
||||||
|
@ -3,7 +3,7 @@ import * as fs from 'fs'
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
import { commandLogger } from '../utilities/logger'
|
import { commandLogger } from '../utilities/logger'
|
||||||
import { getAppPath } from '../utilities/files'
|
import { getAppPath } from '../utilities/storage'
|
||||||
|
|
||||||
class CommandManager {
|
class CommandManager {
|
||||||
private commands: Map<string, any> = new Map()
|
private commands: Map<string, any> = new Map()
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
// src/managers/datetimeManager.ts
|
// src/managers/datetimeManager.ts
|
||||||
|
|
||||||
import fs from 'fs/promises'
|
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
import { appLogger } from '../utilities/logger'
|
import { appLogger } from '../utilities/logger'
|
||||||
import { createDir, doesPathExist, getRootPath } from '../utilities/files'
|
import { getRootPath } from '../utilities/storage'
|
||||||
|
import { readJsonValue, setJsonValue } from '../utilities/json'
|
||||||
|
|
||||||
class DatetimeManager {
|
class DatetimeManager {
|
||||||
private static readonly GAME_SPEED = 24 / 3 // 24 hours / 3 hours = 8x speed
|
private static readonly GAME_SPEED = 8 // 24 game hours / 3 real hours
|
||||||
private static readonly UPDATE_INTERVAL = 1000 // Update every second for smooth second transitions
|
private static readonly UPDATE_INTERVAL = 1000 // 1 second
|
||||||
|
|
||||||
private io: Server | null = null
|
private io: Server | null = null
|
||||||
private intervalId: NodeJS.Timeout | null = null
|
private intervalId: NodeJS.Timeout | null = null
|
||||||
|
private currentDateTime: Date = new Date()
|
||||||
|
|
||||||
public async boot(io: Server): Promise<void> {
|
public async boot(io: Server): Promise<void> {
|
||||||
this.io = io
|
this.io = io
|
||||||
|
await this.loadDateTime()
|
||||||
this.startDateTimeLoop()
|
this.startDateTimeLoop()
|
||||||
appLogger.info('Datetime manager loaded')
|
appLogger.info('Datetime manager loaded')
|
||||||
}
|
}
|
||||||
@ -25,59 +27,47 @@ class DatetimeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadDateTime(): Promise<Date> {
|
private async loadDateTime(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const datetimeFilePath = this.getDatetimeFilePath()
|
const datetimeString = await readJsonValue<string>(this.getWorldFilePath(), 'datetime')
|
||||||
const content = await fs.readFile(datetimeFilePath, 'utf-8')
|
this.currentDateTime = new Date(datetimeString)
|
||||||
return new Date(content.trim())
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
appLogger.error(`Failed to load datetime: ${error instanceof Error ? error.message : String(error)}`)
|
appLogger.error(`Failed to load datetime: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
return new Date() // Use current date as fallback
|
this.currentDateTime = new Date() // Use current date as fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private startDateTimeLoop(): void {
|
private startDateTimeLoop(): void {
|
||||||
this.intervalId = setInterval(async () => {
|
this.intervalId = setInterval(() => {
|
||||||
const currentDateTime = await this.loadDateTime()
|
this.advanceGameTime()
|
||||||
this.advanceGameTime(currentDateTime)
|
this.emitDateTime()
|
||||||
this.emitDateTime(currentDateTime)
|
this.saveDateTime()
|
||||||
this.saveDateTimeIfNeeded(currentDateTime)
|
|
||||||
}, DatetimeManager.UPDATE_INTERVAL)
|
}, DatetimeManager.UPDATE_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
private advanceGameTime(currentDateTime: Date): void {
|
private advanceGameTime(): void {
|
||||||
const advanceTime = (DatetimeManager.GAME_SPEED * DatetimeManager.UPDATE_INTERVAL) / 1000 * 1000
|
const advanceMilliseconds = DatetimeManager.GAME_SPEED * DatetimeManager.UPDATE_INTERVAL
|
||||||
currentDateTime.setTime(currentDateTime.getTime() + advanceTime)
|
this.currentDateTime = new Date(this.currentDateTime.getTime() + advanceMilliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
private emitDateTime(currentDateTime: Date): void {
|
private emitDateTime(): void {
|
||||||
this.io?.emit('datetime', this.formatDateTime(currentDateTime))
|
this.io?.emit('datetime', this.formatDateTime(this.currentDateTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
private formatDateTime(date: Date): string {
|
private formatDateTime(date: Date): string {
|
||||||
return date.toISOString().slice(0, 19).replace('T', ' ')
|
return date.toISOString().slice(0, 19).replace('T', ' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveDateTimeIfNeeded(currentDateTime: Date): void {
|
private async saveDateTime(): Promise<void> {
|
||||||
if (currentDateTime.getMilliseconds() < DatetimeManager.UPDATE_INTERVAL) {
|
|
||||||
this.saveDateTime(currentDateTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveDateTime(currentDateTime: Date): Promise<void> {
|
|
||||||
try {
|
try {
|
||||||
const datetimeFilePath = this.getDatetimeFilePath()
|
await setJsonValue(this.getWorldFilePath(), 'datetime', this.formatDateTime(this.currentDateTime))
|
||||||
await fs.writeFile(datetimeFilePath, this.formatDateTime(currentDateTime))
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
appLogger.error(`Failed to save datetime: ${error instanceof Error ? error.message : String(error)}`)
|
appLogger.error(`Failed to save datetime: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDatetimeFilePath(): string {
|
private getWorldFilePath(): string {
|
||||||
if (!doesPathExist(getRootPath('data'))) {
|
return getRootPath('data', 'world.json')
|
||||||
createDir(getRootPath('data'))
|
|
||||||
}
|
|
||||||
return getRootPath('data', 'datetime.txt')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { Server as SocketServer } from 'socket.io'
|
|||||||
import { TSocket } from '../utilities/types'
|
import { TSocket } from '../utilities/types'
|
||||||
import { queueLogger } from '../utilities/logger'
|
import { queueLogger } from '../utilities/logger'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { getAppPath } from '../utilities/files'
|
import { getAppPath } from '../utilities/storage'
|
||||||
|
|
||||||
class QueueManager {
|
class QueueManager {
|
||||||
private connection!: IORedis
|
private connection!: IORedis
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import express, { Application } from 'express'
|
import express, { Application } from 'express'
|
||||||
import config from './utilities/config'
|
import config from './utilities/config'
|
||||||
import { getAppPath } from './utilities/files'
|
import { getAppPath } from './utilities/storage'
|
||||||
import { createServer as httpServer, Server as HTTPServer } from 'http'
|
import { createServer as httpServer, Server as HTTPServer } from 'http'
|
||||||
import { addHttpRoutes } from './utilities/http'
|
import { addHttpRoutes } from './utilities/http'
|
||||||
import cors from 'cors'
|
import cors from 'cors'
|
||||||
|
@ -3,7 +3,7 @@ import { Server } from 'socket.io'
|
|||||||
import { TSocket } from '../../../../utilities/types'
|
import { TSocket } from '../../../../utilities/types'
|
||||||
import prisma from '../../../../utilities/prisma'
|
import prisma from '../../../../utilities/prisma'
|
||||||
import characterRepository from '../../../../repositories/characterRepository'
|
import characterRepository from '../../../../repositories/characterRepository'
|
||||||
import { getPublicPath } from '../../../../utilities/files'
|
import { getPublicPath } from '../../../../utilities/storage'
|
||||||
|
|
||||||
interface IPayload {
|
interface IPayload {
|
||||||
object: string
|
object: string
|
||||||
|
@ -6,7 +6,7 @@ import prisma from '../../../../utilities/prisma'
|
|||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import characterRepository from '../../../../repositories/characterRepository'
|
import characterRepository from '../../../../repositories/characterRepository'
|
||||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||||
import { getPublicPath } from '../../../../utilities/files'
|
import { getPublicPath } from '../../../../utilities/storage'
|
||||||
|
|
||||||
interface IObjectData {
|
interface IObjectData {
|
||||||
[key: string]: Buffer
|
[key: string]: Buffer
|
||||||
|
@ -3,7 +3,7 @@ import { TSocket } from '../../../../utilities/types'
|
|||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import prisma from '../../../../utilities/prisma'
|
import prisma from '../../../../utilities/prisma'
|
||||||
import characterRepository from '../../../../repositories/characterRepository'
|
import characterRepository from '../../../../repositories/characterRepository'
|
||||||
import { getPublicPath } from '../../../../utilities/files'
|
import { getPublicPath } from '../../../../utilities/storage'
|
||||||
|
|
||||||
export default class SpriteCreateEvent {
|
export default class SpriteCreateEvent {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -4,7 +4,7 @@ import fs from 'fs'
|
|||||||
import prisma from '../../../../utilities/prisma'
|
import prisma from '../../../../utilities/prisma'
|
||||||
import CharacterManager from '../../../../managers/characterManager'
|
import CharacterManager from '../../../../managers/characterManager'
|
||||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||||
import { getPublicPath } from '../../../../utilities/files'
|
import { getPublicPath } from '../../../../utilities/storage'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
id: string
|
id: string
|
||||||
|
@ -5,7 +5,7 @@ import type { Prisma, SpriteAction } from '@prisma/client'
|
|||||||
import { writeFile, mkdir } from 'node:fs/promises'
|
import { writeFile, mkdir } from 'node:fs/promises'
|
||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import CharacterManager from '../../../../managers/characterManager'
|
import CharacterManager from '../../../../managers/characterManager'
|
||||||
import { getPublicPath } from '../../../../utilities/files'
|
import { getPublicPath } from '../../../../utilities/storage'
|
||||||
|
|
||||||
type SpriteActionInput = Omit<SpriteAction, 'id' | 'spriteId' | 'frameWidth' | 'frameHeight'> & {
|
type SpriteActionInput = Omit<SpriteAction, 'id' | 'spriteId' | 'frameWidth' | 'frameHeight'> & {
|
||||||
sprites: string[]
|
sprites: string[]
|
||||||
|
@ -4,7 +4,7 @@ import { TSocket } from '../../../../utilities/types'
|
|||||||
import prisma from '../../../../utilities/prisma'
|
import prisma from '../../../../utilities/prisma'
|
||||||
import characterRepository from '../../../../repositories/characterRepository'
|
import characterRepository from '../../../../repositories/characterRepository'
|
||||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||||
import { getPublicPath } from '../../../../utilities/files'
|
import { getPublicPath } from '../../../../utilities/storage'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
id: string
|
id: string
|
||||||
|
@ -5,7 +5,7 @@ import fs from 'fs/promises'
|
|||||||
import prisma from '../../../../utilities/prisma'
|
import prisma from '../../../../utilities/prisma'
|
||||||
import characterRepository from '../../../../repositories/characterRepository'
|
import characterRepository from '../../../../repositories/characterRepository'
|
||||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||||
import { getPublicPath } from '../../../../utilities/files'
|
import { getPublicPath } from '../../../../utilities/storage'
|
||||||
|
|
||||||
interface ITileData {
|
interface ITileData {
|
||||||
[key: string]: Buffer
|
[key: string]: Buffer
|
||||||
|
@ -12,7 +12,7 @@ import fs from 'fs'
|
|||||||
import zoneRepository from '../repositories/zoneRepository'
|
import zoneRepository from '../repositories/zoneRepository'
|
||||||
import zoneManager from '../managers/zoneManager'
|
import zoneManager from '../managers/zoneManager'
|
||||||
import { httpLogger } from './logger'
|
import { httpLogger } from './logger'
|
||||||
import { getPublicPath } from './files'
|
import { getPublicPath } from './storage'
|
||||||
|
|
||||||
async function addHttpRoutes(app: Application) {
|
async function addHttpRoutes(app: Application) {
|
||||||
/**
|
/**
|
||||||
|
49
src/utilities/json.ts
Normal file
49
src/utilities/json.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import * as fs from 'fs/promises';
|
||||||
|
import { appLogger } from './logger';
|
||||||
|
|
||||||
|
export async function readJsonFile<T>(filePath: string): Promise<T> {
|
||||||
|
try {
|
||||||
|
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||||
|
return JSON.parse(fileContent) as T;
|
||||||
|
} catch (error) {
|
||||||
|
appLogger.error(`Error reading JSON file: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function writeJsonFile<T>(filePath: string, data: T): Promise<void> {
|
||||||
|
try {
|
||||||
|
const jsonString = JSON.stringify(data, null, 2);
|
||||||
|
await fs.writeFile(filePath, jsonString, 'utf-8');
|
||||||
|
} catch (error) {
|
||||||
|
appLogger.error(`Error writing JSON file: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readJsonValue<T>(filePath: string, paramPath: string): Promise<T> {
|
||||||
|
try {
|
||||||
|
const jsonContent = await readJsonFile<any>(filePath);
|
||||||
|
const paramValue = paramPath.split('.').reduce((obj, key) => obj && obj[key], jsonContent);
|
||||||
|
|
||||||
|
if (paramValue === undefined) {
|
||||||
|
throw new Error(`Parameter ${paramPath} not found in the JSON file`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return paramValue as T;
|
||||||
|
} catch (error) {
|
||||||
|
appLogger.error(`Error reading JSON parameter: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setJsonValue<T>(filePath: string, key: string, value: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const data = await readJsonFile<T>(filePath);
|
||||||
|
const updatedData = { ...data, [key]: value };
|
||||||
|
await writeJsonFile(filePath, updatedData);
|
||||||
|
} catch (error) {
|
||||||
|
appLogger.error(`Error setting JSON value: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import pino from 'pino'
|
import pino from 'pino'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { getRootPath } from './files'
|
import { getRootPath } from './storage'
|
||||||
|
|
||||||
// Array of log types
|
// Array of log types
|
||||||
const LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue', 'command'] as const
|
const LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue', 'command'] as const
|
||||||
|
Loading…
x
Reference in New Issue
Block a user