From 3a83f2c1ffd021a3f4a993a9d30fd667683a0b81 Mon Sep 17 00:00:00 2001 From: Dennis Postma Date: Mon, 30 Sep 2024 19:02:55 +0200 Subject: [PATCH] #169 : Re-enabled command manager, created extrudeTiles command for testing --- package-lock.json | 6 +- src/commands/extrudeTiles.ts | 136 +++++++++++++++++++++++++++++++++ src/managers/commandManager.ts | 14 ++-- src/server.ts | 34 ++++----- src/utilities/logger.ts | 11 ++- 5 files changed, 169 insertions(+), 32 deletions(-) create mode 100644 src/commands/extrudeTiles.ts diff --git a/package-lock.json b/package-lock.json index b4e2868..b32a5f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -982,9 +982,9 @@ "license": "BSD-3-Clause" }, "node_modules/bullmq": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.13.2.tgz", - "integrity": "sha512-McGE8k3mrCvdUHdU0sHkTKDS1xr4pff+hbEKBY51wk5S6Za0gkuejYA620VQTo3Zz37E/NVWMgumwiXPQ3yZcA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.14.0.tgz", + "integrity": "sha512-qxZHtRuGEp0oHM1aNokuZ4gA0xr6vcZQPe1OLuQoDTuhaEXB4faxApUoo85v/PHnzrniAAqNT9kqD+UBbmECDQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.6.0", diff --git a/src/commands/extrudeTiles.ts b/src/commands/extrudeTiles.ts new file mode 100644 index 0000000..6739934 --- /dev/null +++ b/src/commands/extrudeTiles.ts @@ -0,0 +1,136 @@ +import { Server } from 'socket.io' +import ZoneManager from '../managers/zoneManager' +import sharp from 'sharp' +import path from 'path' +import fs from 'fs' +import { commandLogger } from '../utilities/logger' + +type CommandInput = string[] + +/** + * Extrude tiles command + * This command will get all the tiles inside process.cwd() > public > tiles + * And overwrite the existing tiles. + * @param input + * @param io + */ +export default async function extrudeTiles(input: CommandInput, io: Server) { + const [tileWidth, tileHeight, extrusion = '1', color = '0xffffff00'] = input + + if (!tileWidth || !tileHeight) { + console.log('Missing tileWidth or tileHeight! Usage: extrudeTiles [extrusion] [color]') + return + } + + const options = { + tileWidth: parseInt(tileWidth), + tileHeight: parseInt(tileHeight), + extrusion: parseInt(extrusion), + color: parseInt(color, 16) + } + + const tilesDir = path.join(process.cwd(), 'public', 'tiles') + const files = fs.readdirSync(tilesDir).filter(file => file.endsWith('.png')) + + for (const file of files) { + const inputPath = path.join(tilesDir, file) + const outputPath = path.join(tilesDir, `${path.parse(file).name}.png`) + + try { + await extrudeTile( + options.tileWidth, + options.tileHeight, + inputPath, + outputPath, + options.extrusion, + options.color + ) + commandLogger.info(`Extruded tile ${file}`) + } catch (error: any) { + console.log(error) + commandLogger.error('Error extruding tile:', error.message) + } + } +} + +async function extrudeTile( + tileWidth: number, + tileHeight: number, + inputPath: string, + outputPath: string, + extrusion: number, + backgroundColor: number +) { + const borderWidth = 4; // You can adjust this value to change the border width + + const image = sharp(inputPath) + const metadata = await image.metadata() + + if (!metadata.width || !metadata.height) { + throw new Error('Unable to get image dimensions') + } + + const tilesX = Math.floor(metadata.width / tileWidth) + const tilesY = Math.floor(metadata.height / tileHeight) + + // Use borderWidth in the new dimensions calculation + const newWidth = metadata.width + (tilesX + 1) * extrusion + tilesX * borderWidth * 2 + const newHeight = metadata.height + (tilesY + 1) * extrusion + tilesY * borderWidth * 2 + + const background = { + r: (backgroundColor >> 24) & 0xff, + g: (backgroundColor >> 16) & 0xff, + b: (backgroundColor >> 8) & 0xff, + alpha: backgroundColor & 0xff + } + + const newImage = sharp({ + create: { + width: newWidth, + height: newHeight, + channels: 4, + background + } + }) + + const compositeOperations = [] + + for (let y = 0; y < tilesY; y++) { + for (let x = 0; x < tilesX; x++) { + const tileBuffer = await image + .extract({ + left: x * tileWidth, + top: y * tileHeight, + width: tileWidth, + height: tileHeight + }) + .extend({ + top: borderWidth, + bottom: borderWidth, + left: borderWidth, + right: borderWidth, + background: { r: 0, g: 0, b: 0, alpha: 0 } + }) + .raw() + .toBuffer() + + compositeOperations.push({ + input: tileBuffer, + // Use borderWidth in positioning calculation + left: x * (tileWidth + extrusion + borderWidth * 2), + top: y * (tileHeight + extrusion + borderWidth * 2), + raw: { + width: tileWidth + borderWidth * 2, + height: tileHeight + borderWidth * 2, + channels: 4 + } + }) + } + } + + await newImage + // @ts-ignore + .composite(compositeOperations) + .png() + .toFile(outputPath) +} \ No newline at end of file diff --git a/src/managers/commandManager.ts b/src/managers/commandManager.ts index 1507755..a5c02c7 100644 --- a/src/managers/commandManager.ts +++ b/src/managers/commandManager.ts @@ -2,6 +2,7 @@ import * as readline from 'readline' import * as fs from 'fs' import * as path from 'path' import { Server } from 'socket.io' +import { commandLogger } from '../utilities/logger' class CommandManager { private commands: Map = new Map() @@ -23,7 +24,7 @@ class CommandManager { public async boot(io: Server) { this.io = io await this.loadCommands() - console.log('[✅] Command manager loaded') + commandLogger.info('Command manager loaded') this.startPrompt() } @@ -48,7 +49,6 @@ class CommandManager { private handleUnknownCommand(command: string) { switch (command) { case 'exit': - console.log('Goodbye!') this.rl.close() break default: @@ -58,7 +58,7 @@ class CommandManager { } private async loadCommands() { - const commandsDir = path.resolve(__dirname, 'commands') + const commandsDir = path.resolve(process.cwd(), 'src', 'commands') try { const files: string[] = await fs.promises.readdir(commandsDir) @@ -66,7 +66,7 @@ class CommandManager { await this.loadCommand(commandsDir, file) } } catch (error) { - console.error('[❌] Failed to read commands directory:', error) + commandLogger.error(`Failed to read commands directory: ${error}`) } } @@ -79,16 +79,16 @@ class CommandManager { this.registerCommand(commandName, module.default) } catch (error) { - console.error('[❌] Failed to load command:', file, error) + commandLogger.error(`Failed to load command: ${file}: ${error}`) } } private registerCommand(name: string, command: (args: string[], io: Server) => void) { if (this.commands.has(name)) { - console.warn(`Command '${name}' is already registered. Overwriting...`) + commandLogger.warn(`Command '${name}' is already registered. Overwriting...`) } this.commands.set(name, command) - console.log(`Registered command: ${name}`) + commandLogger.info(`Registered command: ${name}`) } } diff --git a/src/server.ts b/src/server.ts index d6af995..de1628a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,19 +1,19 @@ import fs from 'fs' import path from 'path' import express, { Application } from 'express' +import config from './utilities/config' 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 { TSocket } from './utilities/types' +import prisma from './utilities/prisma' import { Dirent } from 'node:fs' import { appLogger, watchLogs } from './utilities/logger' +import ZoneManager from './managers/zoneManager' +import UserManager from './managers/userManager' +import CommandManager from './managers/commandManager' import CharacterManager from './managers/characterManager' import QueueManager from './managers/queueManager' @@ -58,12 +58,12 @@ export class Server { appLogger.error(`Socket.IO failed to start: ${error.message}`) } - // Load queue manager - await QueueManager.boot(this.io) - // Add http API routes await addHttpRoutes(this.app) + // Load queue manager + await QueueManager.boot(this.io) + // Load user manager await UserManager.boot() @@ -73,8 +73,8 @@ export class Server { // Load character manager await CharacterManager.boot() - // Load command manager - Disabled for now - // await CommandManager.boot(this.io); + // Load command manager + await CommandManager.boot(this.io); // Listen for socket connections this.io.on('connection', this.handleConnection.bind(this)) @@ -106,15 +106,9 @@ export class Server { 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(this.io, socket) - } + const EventClass = module.default + const eventInstance = new EventClass(this.io, socket) + eventInstance.listen() } else { appLogger.warn(`Unrecognized export in ${file.name}`) } diff --git a/src/utilities/logger.ts b/src/utilities/logger.ts index 803bdf3..44b1b0e 100644 --- a/src/utilities/logger.ts +++ b/src/utilities/logger.ts @@ -3,7 +3,7 @@ import fs from 'fs' import path from 'path' // Array of log types -const LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue'] as const +const LOG_TYPES = ['http', 'game', 'gameMaster', 'app', 'queue', 'command'] as const type LogType = (typeof LOG_TYPES)[number] const createLogger = (name: LogType) => @@ -43,6 +43,13 @@ const watchLogs = () => { }) } -export const { http: httpLogger, game: gameLogger, gameMaster: gameMasterLogger, app: appLogger, queue: queueLogger } = loggers +export const { + http: httpLogger, + game: gameLogger, + gameMaster: gameMasterLogger, + app: appLogger, + queue: queueLogger, + command: commandLogger +} = loggers export { watchLogs }