forked from noxious/server
Renamed utilities to files, added datetimeManager, npm update
This commit is contained in:
parent
2008646a3f
commit
049b9de2b3
3
.gitignore
vendored
3
.gitignore
vendored
@ -310,6 +310,3 @@ $RECYCLE.BIN/
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,jetbrains+all,visualstudiocode,macos,windows
|
||||
|
||||
prisma/dev.db
|
||||
prisma/dev.db-journal
|
52
package-lock.json
generated
52
package-lock.json
generated
@ -44,9 +44,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.0.tgz",
|
||||
"integrity": "sha512-XMBySMuNZs3DM96xcJmLW4EfGnf+uGmFNjzpehMjuX5PLB5j87ar2Zc4e3PVeZ3I5g3tYtAqskB28manlF69Zw==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||
"integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@ -719,9 +719,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.16.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.10.tgz",
|
||||
"integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==",
|
||||
"version": "20.16.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz",
|
||||
"integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
@ -982,9 +982,9 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/bullmq": {
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.15.0.tgz",
|
||||
"integrity": "sha512-h53shVjx8s6wxYGtUfzAfENpSP7N5T0D4PMTvbZncozLjb8yUKhopfpa7PmcpQfq7SSO9dm/OZ9XQuGOCSGNug==",
|
||||
"version": "5.20.0",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.20.0.tgz",
|
||||
"integrity": "sha512-eCJyYJqNUl9swC39x2fVm1BUv5BuO/nv2eAcAsz58znue0ZCYgSG+yWXZeauRG98Jl0UIBcPgJtbF+c9Wd+Odg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron-parser": "^4.6.0",
|
||||
@ -1128,9 +1128,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@ -1282,9 +1282,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.6.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.1.tgz",
|
||||
"integrity": "sha512-NEpDCw9hrvBW+hVEOK4T7v0jFJ++KgtPl4jKFwsZVfG1XhS0dCrSb3VMb9gPAd7VAdW52VT1EnaNiU2vM8C0og==",
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
|
||||
"integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
@ -1292,7 +1292,7 @@
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.4.1",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
@ -1312,9 +1312,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/cookie": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@ -1398,9 +1398,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
@ -1408,7 +1408,7 @@
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@ -2867,9 +2867,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
|
||||
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
@ -38,6 +38,7 @@ model Zone {
|
||||
height Int @default(10)
|
||||
tiles Json?
|
||||
pvp Boolean @default(false)
|
||||
effects ZoneEffect[]
|
||||
zoneEventTiles ZoneEventTile[]
|
||||
zoneEventTileTeleports ZoneEventTileTeleport[]
|
||||
zoneObjects ZoneObject[]
|
||||
@ -47,6 +48,14 @@ model Zone {
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model ZoneEffect {
|
||||
id String @id @default(uuid())
|
||||
zoneId Int
|
||||
zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade)
|
||||
effect String
|
||||
strength Int
|
||||
}
|
||||
|
||||
model ZoneObject {
|
||||
id String @id @default(uuid())
|
||||
zoneId Int
|
||||
|
@ -2,7 +2,7 @@ import fs from 'fs'
|
||||
import sharp from 'sharp'
|
||||
import { commandLogger } from '../utilities/logger'
|
||||
import { Server } from 'socket.io'
|
||||
import { getPublicPath } from '../utilities/utilities'
|
||||
import { getPublicPath } from '../utilities/files'
|
||||
import path from 'path'
|
||||
|
||||
export default class TilesCommand {
|
||||
|
@ -3,7 +3,7 @@ import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import { Server } from 'socket.io'
|
||||
import { commandLogger } from '../utilities/logger'
|
||||
import { getAppPath } from '../utilities/utilities'
|
||||
import { getAppPath } from '../utilities/files'
|
||||
|
||||
class CommandManager {
|
||||
private commands: Map<string, any> = new Map()
|
||||
|
84
src/managers/datetimeManager.ts
Normal file
84
src/managers/datetimeManager.ts
Normal file
@ -0,0 +1,84 @@
|
||||
// src/managers/datetimeManager.ts
|
||||
|
||||
import fs from 'fs/promises'
|
||||
import { Server } from 'socket.io'
|
||||
import { appLogger } from '../utilities/logger'
|
||||
import { createDir, doesPathExist, getRootPath } from '../utilities/files'
|
||||
|
||||
class DatetimeManager {
|
||||
private static readonly GAME_SPEED = 24 / 3 // 24 hours / 3 hours = 8x speed
|
||||
private static readonly UPDATE_INTERVAL = 1000 // Update every second for smooth second transitions
|
||||
|
||||
private io: Server | null = null
|
||||
private intervalId: NodeJS.Timeout | null = null
|
||||
|
||||
public async boot(io: Server): Promise<void> {
|
||||
this.io = io
|
||||
this.startDateTimeLoop()
|
||||
appLogger.info('Datetime manager loaded')
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId)
|
||||
this.intervalId = null
|
||||
}
|
||||
}
|
||||
|
||||
public async loadDateTime(): Promise<Date> {
|
||||
try {
|
||||
const datetimeFilePath = this.getDatetimeFilePath()
|
||||
const content = await fs.readFile(datetimeFilePath, 'utf-8')
|
||||
return new Date(content.trim())
|
||||
} catch (error) {
|
||||
appLogger.error(`Failed to load datetime: ${error instanceof Error ? error.message : String(error)}`)
|
||||
return new Date() // Use current date as fallback
|
||||
}
|
||||
}
|
||||
|
||||
private startDateTimeLoop(): void {
|
||||
this.intervalId = setInterval(async () => {
|
||||
const currentDateTime = await this.loadDateTime()
|
||||
this.advanceGameTime(currentDateTime)
|
||||
this.emitDateTime(currentDateTime)
|
||||
this.saveDateTimeIfNeeded(currentDateTime)
|
||||
}, DatetimeManager.UPDATE_INTERVAL)
|
||||
}
|
||||
|
||||
private advanceGameTime(currentDateTime: Date): void {
|
||||
const advanceTime = (DatetimeManager.GAME_SPEED * DatetimeManager.UPDATE_INTERVAL) / 1000 * 1000
|
||||
currentDateTime.setTime(currentDateTime.getTime() + advanceTime)
|
||||
}
|
||||
|
||||
private emitDateTime(currentDateTime: Date): void {
|
||||
this.io?.emit('datetime', this.formatDateTime(currentDateTime))
|
||||
}
|
||||
|
||||
private formatDateTime(date: Date): string {
|
||||
return date.toISOString().slice(0, 19).replace('T', ' ')
|
||||
}
|
||||
|
||||
private saveDateTimeIfNeeded(currentDateTime: Date): void {
|
||||
if (currentDateTime.getMilliseconds() < DatetimeManager.UPDATE_INTERVAL) {
|
||||
this.saveDateTime(currentDateTime)
|
||||
}
|
||||
}
|
||||
|
||||
private async saveDateTime(currentDateTime: Date): Promise<void> {
|
||||
try {
|
||||
const datetimeFilePath = this.getDatetimeFilePath()
|
||||
await fs.writeFile(datetimeFilePath, this.formatDateTime(currentDateTime))
|
||||
} catch (error) {
|
||||
appLogger.error(`Failed to save datetime: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
private getDatetimeFilePath(): string {
|
||||
if (!doesPathExist(getRootPath('data'))) {
|
||||
createDir(getRootPath('data'))
|
||||
}
|
||||
return getRootPath('data', 'datetime.txt')
|
||||
}
|
||||
}
|
||||
|
||||
export default new DatetimeManager()
|
@ -5,7 +5,7 @@ import { Server as SocketServer } from 'socket.io'
|
||||
import { TSocket } from '../utilities/types'
|
||||
import { queueLogger } from '../utilities/logger'
|
||||
import fs from 'fs'
|
||||
import { getAppPath } from '../utilities/utilities'
|
||||
import { getAppPath } from '../utilities/files'
|
||||
|
||||
class QueueManager {
|
||||
private connection!: IORedis
|
||||
|
0
src/managers/weatherManager.ts
Normal file
0
src/managers/weatherManager.ts
Normal file
@ -1,6 +1,7 @@
|
||||
import fs from 'fs'
|
||||
import express, { Application } from 'express'
|
||||
import config from './utilities/config'
|
||||
import { getAppPath } from './utilities/files'
|
||||
import { createServer as httpServer, Server as HTTPServer } from 'http'
|
||||
import { addHttpRoutes } from './utilities/http'
|
||||
import cors from 'cors'
|
||||
@ -14,7 +15,7 @@ import UserManager from './managers/userManager'
|
||||
import CommandManager from './managers/commandManager'
|
||||
import CharacterManager from './managers/characterManager'
|
||||
import QueueManager from './managers/queueManager'
|
||||
import { getAppPath } from './utilities/utilities'
|
||||
import DatetimeManager from './managers/datetimeManager'
|
||||
|
||||
export class Server {
|
||||
private readonly app: Application
|
||||
@ -66,6 +67,9 @@ export class Server {
|
||||
// Load user manager
|
||||
await UserManager.boot()
|
||||
|
||||
// Load datetime manager
|
||||
await DatetimeManager.boot(this.io)
|
||||
|
||||
// Load zoneEditor manager
|
||||
await ZoneManager.boot()
|
||||
|
||||
|
@ -29,6 +29,8 @@ export default class DisconnectEvent {
|
||||
return
|
||||
}
|
||||
|
||||
character.resetMovement = true
|
||||
|
||||
gameLogger.info('User disconnected along with their character')
|
||||
|
||||
await CharacterManager.removeCharacter(character)
|
||||
|
@ -3,7 +3,7 @@ import { Server } from 'socket.io'
|
||||
import { TSocket } from '../../../../utilities/types'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { getPublicPath } from '../../../../utilities/utilities'
|
||||
import { getPublicPath } from '../../../../utilities/files'
|
||||
|
||||
interface IPayload {
|
||||
object: string
|
||||
|
@ -6,7 +6,7 @@ import prisma from '../../../../utilities/prisma'
|
||||
import sharp from 'sharp'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
import { getPublicPath } from '../../../../utilities/utilities'
|
||||
import { getPublicPath } from '../../../../utilities/files'
|
||||
|
||||
interface IObjectData {
|
||||
[key: string]: Buffer
|
||||
|
@ -3,7 +3,7 @@ import { TSocket } from '../../../../utilities/types'
|
||||
import fs from 'fs/promises'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { getPublicPath } from '../../../../utilities/utilities'
|
||||
import { getPublicPath } from '../../../../utilities/files'
|
||||
|
||||
export default class SpriteCreateEvent {
|
||||
constructor(
|
||||
|
@ -4,7 +4,7 @@ import fs from 'fs'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
import { getPublicPath } from '../../../../utilities/utilities'
|
||||
import { getPublicPath } from '../../../../utilities/files'
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
|
@ -5,7 +5,7 @@ import type { Prisma, SpriteAction } from '@prisma/client'
|
||||
import { writeFile, mkdir } from 'node:fs/promises'
|
||||
import sharp from 'sharp'
|
||||
import CharacterManager from '../../../../managers/characterManager'
|
||||
import { getPublicPath } from '../../../../utilities/utilities'
|
||||
import { getPublicPath } from '../../../../utilities/files'
|
||||
|
||||
type SpriteActionInput = Omit<SpriteAction, 'id' | 'spriteId' | 'frameWidth' | 'frameHeight'> & {
|
||||
sprites: string[]
|
||||
|
@ -4,7 +4,7 @@ import { TSocket } from '../../../../utilities/types'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
import { getPublicPath } from '../../../../utilities/utilities'
|
||||
import { getPublicPath } from '../../../../utilities/files'
|
||||
|
||||
type Payload = {
|
||||
id: string
|
||||
|
@ -5,7 +5,7 @@ import fs from 'fs/promises'
|
||||
import prisma from '../../../../utilities/prisma'
|
||||
import characterRepository from '../../../../repositories/characterRepository'
|
||||
import { gameMasterLogger } from '../../../../utilities/logger'
|
||||
import { getPublicPath } from '../../../../utilities/utilities'
|
||||
import { getPublicPath } from '../../../../utilities/files'
|
||||
|
||||
interface ITileData {
|
||||
[key: string]: Buffer
|
||||
|
@ -1,5 +1,6 @@
|
||||
import config from './config'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
export function getRootPath(folder: string, ...additionalSegments: string[]) {
|
||||
return path.join(process.cwd(), folder, ...additionalSegments)
|
||||
@ -13,3 +14,20 @@ export function getAppPath(folder: string, ...additionalSegments: string[]) {
|
||||
export function getPublicPath(folder: string, ...additionalSegments: string[]) {
|
||||
return path.join(process.cwd(), 'public', folder, ...additionalSegments)
|
||||
}
|
||||
|
||||
export function doesPathExist(path: string) {
|
||||
try {
|
||||
fs.accessSync(path, fs.constants.F_OK);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function createDir(path: string) {
|
||||
try {
|
||||
fs.mkdirSync(path, { recursive: true });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import fs from 'fs'
|
||||
import zoneRepository from '../repositories/zoneRepository'
|
||||
import zoneManager from '../managers/zoneManager'
|
||||
import { httpLogger } from './logger'
|
||||
import { getPublicPath } from './utilities'
|
||||
import { getPublicPath } from './files'
|
||||
|
||||
async function addHttpRoutes(app: Application) {
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import pino from 'pino'
|
||||
import fs from 'fs'
|
||||
import { getRootPath } from './utilities'
|
||||
import { getRootPath } from './files'
|
||||
|
||||
// Array of log types
|
||||
const LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue', 'command'] as const
|
||||
|
Loading…
x
Reference in New Issue
Block a user