From c9e8b29f112568ee651960856a7696cf2f5f788c Mon Sep 17 00:00:00 2001 From: Dennis Postma Date: Sat, 8 Mar 2025 01:40:23 +0100 Subject: [PATCH] Simplified code --- .../gameMaster/assetManager/sprite/update.ts | 208 +++++++++++------- 1 file changed, 124 insertions(+), 84 deletions(-) diff --git a/src/events/gameMaster/assetManager/sprite/update.ts b/src/events/gameMaster/assetManager/sprite/update.ts index 2e64f0f..530d6dc 100644 --- a/src/events/gameMaster/assetManager/sprite/update.ts +++ b/src/events/gameMaster/assetManager/sprite/update.ts @@ -28,82 +28,109 @@ interface EffectiveDimensions { bottom: number } -type Payload = { +interface SpriteActionData { + action: string + sprites: SpriteImage[] + originX: number + originY: number + frameRate: number +} + +interface UpdateSpritePayload { id: UUID name: string - spriteActions: Array<{ - action: string - sprites: SpriteImage[] - originX: number - originY: number - frameRate: number - }> + spriteActions: SpriteActionData[] } export default class SpriteUpdateEvent extends BaseEvent { + private readonly spriteRepository: SpriteRepository = new SpriteRepository() + public listen(): void { this.socket.on(SocketEvent.GM_SPRITE_UPDATE, this.handleEvent.bind(this)) } - private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise { + private async handleEvent(data: UpdateSpritePayload, callback: (success: boolean) => void): Promise { try { - if (!(await this.isCharacterGM())) return + if (!(await this.isCharacterGM())) { + callback(false) + return + } - const spriteRepository = new SpriteRepository() - const sprite = await spriteRepository.getById(data.id) - if (!sprite) return callback(false) + const sprite = await this.spriteRepository.getById(data.id) + if (!sprite) { + callback(false) + return + } - await spriteRepository.getEntityManager().populate(sprite, ['spriteActions']) + await this.spriteRepository.getEntityManager().populate(sprite, ['spriteActions']) // Update sprite in database await sprite.setName(data.name).setUpdatedAt(new Date()).save() // First verify all sprite sheets can be generated - for (const actionData of data.spriteActions) { - if (!(await this.generateSpriteSheet(actionData.sprites, sprite.getId(), actionData.action))) { - return callback(false) - } + const allSheetsGenerated = await this.verifyAllSpriteSheets(data.spriteActions, sprite.getId()) + if (!allSheetsGenerated) { + callback(false) + return } - const existingActions = sprite.getSpriteActions() - - // Remove existing actions only after confirming sprite sheets generated successfully - for (const existingAction of existingActions) { - await spriteRepository.getEntityManager().removeAndFlush(existingAction) - } + // Remove existing actions + await this.removeExistingActions(sprite) // Create new actions - for (const actionData of data.spriteActions) { - // Process images and calculate dimensions - const imageData = await Promise.all(actionData.sprites.map((sprite) => this.processImage(sprite))) - const effectiveDimensions = imageData.map((dimensions) => this.calculateEffectiveDimensions(dimensions)) + await this.createNewSpriteActions(data.spriteActions, sprite) - // Calculate total height needed for the sprite sheet - const maxHeight = Math.max(...effectiveDimensions.map((d) => d.height)) - const maxTop = Math.max(...effectiveDimensions.map((d) => d.top)) - const maxBottom = Math.max(...effectiveDimensions.map((d) => d.bottom)) - const totalHeight = maxHeight + maxTop + maxBottom - - const spriteAction = new SpriteAction() - spriteAction.setSprite(sprite) - sprite.getSpriteActions().add(spriteAction) - - spriteAction - .setAction(actionData.action) - .setSprites(actionData.sprites) - .setOriginX(actionData.originX) - .setOriginY(actionData.originY) - .setFrameWidth(await this.calculateMaxWidth(actionData.sprites)) - .setFrameHeight(totalHeight) - .setFrameRate(actionData.frameRate) - - await spriteRepository.getEntityManager().persistAndFlush(spriteAction) - } - - return callback(true) + callback(true) } catch (error) { console.error(`Error updating sprite ${data.id}:`, error) - return callback(false) + callback(false) + } + } + + private async verifyAllSpriteSheets(actionsData: SpriteActionData[], spriteId: string): Promise { + for (const actionData of actionsData) { + if (!(await this.generateSpriteSheet(actionData.sprites, spriteId, actionData.action))) { + return false + } + } + return true + } + + private async removeExistingActions(sprite: any): Promise { + const existingActions = sprite.getSpriteActions() + for (const existingAction of existingActions) { + await this.spriteRepository.getEntityManager().removeAndFlush(existingAction) + } + } + + private async createNewSpriteActions(actionsData: SpriteActionData[], sprite: any): Promise { + for (const actionData of actionsData) { + // Process images and calculate dimensions + const imageData = await Promise.all(actionData.sprites.map((sprite) => this.processImage(sprite))) + const effectiveDimensions = imageData.map((dimensions) => this.calculateEffectiveDimensions(dimensions)) + + // Calculate total height needed for the sprite sheet + const maxHeight = Math.max(...effectiveDimensions.map((d) => d.height), 0) + const maxTop = Math.max(...effectiveDimensions.map((d) => d.top), 0) + const maxBottom = Math.max(...effectiveDimensions.map((d) => d.bottom), 0) + const totalHeight = maxHeight + maxTop + maxBottom + + const spriteAction = new SpriteAction() + spriteAction.setSprite(sprite) + sprite.getSpriteActions().add(spriteAction) + + const maxWidth = await this.calculateMaxWidth(actionData.sprites) + + spriteAction + .setAction(actionData.action) + .setSprites(actionData.sprites) + .setOriginX(actionData.originX) + .setOriginY(actionData.originY) + .setFrameWidth(maxWidth) + .setFrameHeight(totalHeight) + .setFrameRate(actionData.frameRate) + + await this.spriteRepository.getEntityManager().persistAndFlush(spriteAction) } } @@ -116,20 +143,19 @@ export default class SpriteUpdateEvent extends BaseEvent { const effectiveDimensions = imageData.map((dimensions) => this.calculateEffectiveDimensions(dimensions)) // Calculate maximum dimensions - const maxWidth = Math.max(...effectiveDimensions.map((d) => d.width)) - const maxHeight = Math.max(...effectiveDimensions.map((d) => d.height)) - const maxTop = Math.max(...effectiveDimensions.map((d) => d.top)) - const maxBottom = Math.max(...effectiveDimensions.map((d) => d.bottom)) + const maxWidth = Math.max(...effectiveDimensions.map((d) => d.width), 0) + const maxHeight = Math.max(...effectiveDimensions.map((d) => d.height), 0) + const maxTop = Math.max(...effectiveDimensions.map((d) => d.top), 0) + const maxBottom = Math.max(...effectiveDimensions.map((d) => d.bottom), 0) // Calculate total height needed const totalHeight = maxHeight + maxTop + maxBottom // Process images and create sprite sheet const processedImages = await Promise.all( - sprites.map(async (sprite, index) => { + sprites.map(async (sprite) => { const { width, height, offsetX, offsetY } = await this.processImage(sprite) - const uri = sprite.url.split(';base64,').pop() - if (!uri) throw new Error('Invalid base64 image') + const uri = this.extractBase64Data(sprite.url) const buffer = Buffer.from(uri, 'base64') // Create individual frame @@ -150,30 +176,10 @@ export default class SpriteUpdateEvent extends BaseEvent { ) // Combine frames into sprite sheet - const spriteSheet = await sharp({ - create: { - width: maxWidth * sprites.length, - height: totalHeight, - channels: 4, - background: { r: 0, g: 0, b: 0, alpha: 0 } - } - }) - .composite( - processedImages.map((buffer, index) => ({ - input: buffer, - left: index * maxWidth, - top: 0 - })) - ) - .png() - .toBuffer() - - // Ensure directory exists - const dir = `public/sprites/${spriteId}` - await fs.promises.mkdir(dir, { recursive: true }) + const spriteSheet = await this.createSpriteSheetImage(processedImages, maxWidth, totalHeight) // Save the sprite sheet - await fs.promises.writeFile(`${dir}/${action}.png`, spriteSheet) + await this.saveSpriteSheet(spriteId, action, spriteSheet) return true } catch (error) { @@ -182,9 +188,43 @@ export default class SpriteUpdateEvent extends BaseEvent { } } - private async processImage(sprite: SpriteImage): Promise { - const uri = sprite.url.split(';base64,').pop() + private async createSpriteSheetImage(processedImages: Buffer[], maxWidth: number, totalHeight: number): Promise { + return sharp({ + create: { + width: maxWidth * processedImages.length, + height: totalHeight, + channels: 4, + background: { r: 0, g: 0, b: 0, alpha: 0 } + } + }) + .composite( + processedImages.map((buffer, index) => ({ + input: buffer, + left: index * maxWidth, + top: 0 + })) + ) + .png() + .toBuffer() + } + + private async saveSpriteSheet(spriteId: string, action: string, spriteSheet: Buffer): Promise { + // Ensure directory exists + const dir = `public/sprites/${spriteId}` + await fs.promises.mkdir(dir, { recursive: true }) + + // Save the sprite sheet + await fs.promises.writeFile(`${dir}/${action}.png`, spriteSheet) + } + + private extractBase64Data(url: string): string { + const uri = url.split(';base64,').pop() if (!uri) throw new Error('Invalid base64 image') + return uri + } + + private async processImage(sprite: SpriteImage): Promise { + const uri = this.extractBase64Data(sprite.url) const buffer = Buffer.from(uri, 'base64') const metadata = await sharp(buffer).metadata() return { @@ -212,6 +252,6 @@ export default class SpriteUpdateEvent extends BaseEvent { const effectiveDimensions = imageData.map((dimensions) => this.calculateEffectiveDimensions(dimensions)) // Calculate maximum width needed - return Math.max(...effectiveDimensions.map((d) => d.width)) + return Math.max(...effectiveDimensions.map((d) => d.width), 0) } }