1
0
forked from noxious/server
noxious_server/src/server.ts

135 lines
3.9 KiB
TypeScript

import fs from 'fs'
import path from 'path'
import express, { Application } from 'express'
import { createServer as httpServer, Server as HTTPServer } from 'http'
import { addHttpRoutes } from './utilities/http'
import cors from 'cors'
import { Server as SocketServer } from 'socket.io'
import { TSocket } from './utilities/types'
import config from './utilities/config'
import prisma from './utilities/prisma'
import ZoneManager from './managers/zoneManager'
import UserManager from './managers/userManager'
import { Authentication } from './middleware/authentication'
// import CommandManager from './managers/CommandManager'
import { Dirent } from 'node:fs'
import logger from './utilities/logger'
export class Server {
private readonly app: Application
private readonly http: HTTPServer
private readonly io: SocketServer
/**
* Creates an instance of GameServer.
*/
constructor() {
this.app = express()
this.app.use(cors())
this.app.use(express.json())
this.app.use(express.urlencoded({ extended: true }))
this.http = httpServer(this.app)
this.io = new SocketServer(this.http)
this.io.use(Authentication)
}
/**
* Start the server
*/
public async start() {
// Read log file and print to console for debugging
const logFile = path.join(__dirname, '../logs/app.log')
fs.watchFile(logFile, (curr, prev) => {
if (curr.size > prev.size) {
const stream = fs.createReadStream(logFile, { start: prev.size, end: curr.size })
stream.on('data', (chunk) => {
console.log(chunk.toString())
})
}
})
// Check prisma connection
try {
await prisma.$connect()
logger.info('Database connected')
} catch (error: any) {
logger.error(`Database connection failed: ${error.message}`)
}
// Start the server
try {
this.http.listen(config.PORT, config.HOST)
logger.info(`Socket.IO running on port ${config.PORT}`)
} catch (error: any) {
logger.error(`Socket.IO failed to start: ${error.message}`)
}
// Add http API routes
await addHttpRoutes(this.app)
// Load user manager
await UserManager.boot()
// Load zoneEditor manager
await ZoneManager.boot()
// Load command manager - Disabled for now
// await CommandManager.boot(this.io);
// Listen for socket connections
this.io.on('connection', this.handleConnection.bind(this))
}
/**
* Handle socket connection
* @param socket
* @private
*/
private async handleConnection(socket: TSocket) {
const eventsPath = path.join(__dirname, 'events')
try {
await this.loadEventHandlers(eventsPath, socket)
} catch (error: any) {
logger.error(`Failed to load event handlers: ${error.message}`)
}
}
private async loadEventHandlers(dir: string, socket: TSocket) {
const files: Dirent[] = await fs.promises.readdir(dir, { withFileTypes: true })
for (const file of files) {
const fullPath = path.join(dir, file.name)
if (file.isDirectory()) {
await this.loadEventHandlers(fullPath, socket)
} else if (file.isFile() && file.name.endsWith('.ts')) {
try {
const module = await import(fullPath)
if (typeof module.default === 'function') {
if (module.default.prototype && module.default.prototype.listen) {
// This is a class-based event
const EventClass = module.default
const eventInstance = new EventClass(this.io, socket)
eventInstance.listen()
} else {
// This is a function-based event
module.default(socket, this.io)
}
} else {
logger.warn(`Unrecognized export in ${file.name}`)
}
} catch (error: any) {
logger.error(`Error loading event handler ${file.name}: ${error.message}`)
}
}
}
}
}
// Start the server
const server = new Server()
server.start()
console.log('Server started')