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) }