1
0
forked from noxious/server
This commit is contained in:
Dennis Postma 2025-02-21 01:46:53 +01:00
parent d6681f9af7
commit c59b391a6a
4 changed files with 60 additions and 40 deletions

Binary file not shown.

View File

@ -489,7 +489,7 @@ export default class InitCommand extends BaseCommand {
private async createCharacterHair(): Promise<void> { private async createCharacterHair(): Promise<void> {
const hairSprite = new Sprite() const hairSprite = new Sprite()
hairSprite.setId('922ee95f-1500-49c0-8ead-f8cc46dad136').setName('Hair 1') hairSprite.setId('922ee95f-1500-49c0-8ead-f8cc46dad136').setName('Hair 1').setWidth(30).setHeight(40)
await hairSprite.save() await hairSprite.save()
const frontAction = new SpriteAction() const frontAction = new SpriteAction()

View File

@ -65,9 +65,12 @@ export class AvatarController extends BaseController {
return this.sendError(res, 'Body sprite file not found', 404) return this.sendError(res, 'Body sprite file not found', 404)
} }
// Get body sprite metadata
const bodyMetadata = await sharp(bodySpritePath).metadata()
let avatar = sharp(bodySpritePath).extend({ let avatar = sharp(bodySpritePath).extend({
top: 2, top: 0,
bottom: 2, bottom: 0,
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
}) })
@ -76,7 +79,21 @@ export class AvatarController extends BaseController {
if (characterHair?.sprite?.id) { if (characterHair?.sprite?.id) {
const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png') const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png')
if (fs.existsSync(hairSpritePath)) { if (fs.existsSync(hairSpritePath)) {
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }]) // Resize hair sprite to match body dimensions
const resizedHair = await sharp(hairSpritePath)
.resize(bodyMetadata.width, bodyMetadata.height, {
fit: 'contain',
background: { r: 0, g: 0, b: 0, alpha: 0 }
})
.toBuffer()
avatar = avatar.composite([
{
input: resizedHair,
left: 0,
top: -27 // Apply vertical offset
}
])
} }
} }
} }
@ -84,6 +101,7 @@ export class AvatarController extends BaseController {
res.setHeader('Content-Type', 'image/png') res.setHeader('Content-Type', 'image/png')
return avatar.pipe(res) return avatar.pipe(res)
} catch (error) { } catch (error) {
console.error('Avatar generation error:', error)
return this.sendError(res, 'Error generating avatar', 500) return this.sendError(res, 'Error generating avatar', 500)
} }
} }

View File

@ -31,6 +31,8 @@ interface EffectiveDimensions {
type Payload = { type Payload = {
id: UUID id: UUID
name: string name: string
width: number
height: number
spriteActions: Array<{ spriteActions: Array<{
action: string action: string
sprites: SpriteImage[] sprites: SpriteImage[]
@ -56,7 +58,7 @@ export default class SpriteUpdateEvent extends BaseEvent {
await spriteRepository.getEntityManager().populate(sprite, ['spriteActions']) await spriteRepository.getEntityManager().populate(sprite, ['spriteActions'])
// Update sprite in database // Update sprite in database
await sprite.setName(data.name).setUpdatedAt(new Date()).save() await sprite.setName(data.name).setWidth(data.width).setHeight(data.height).setUpdatedAt(new Date()).save()
// First verify all sprite sheets can be generated // First verify all sprite sheets can be generated
for (const actionData of data.spriteActions) { for (const actionData of data.spriteActions) {
@ -89,13 +91,13 @@ export default class SpriteUpdateEvent extends BaseEvent {
sprite.getSpriteActions().add(spriteAction) sprite.getSpriteActions().add(spriteAction)
spriteAction spriteAction
.setAction(actionData.action) .setAction(actionData.action)
.setSprites(actionData.sprites) .setSprites(actionData.sprites)
.setOriginX(actionData.originX) .setOriginX(actionData.originX)
.setOriginY(actionData.originY) .setOriginY(actionData.originY)
.setFrameWidth(await this.calculateMaxWidth(actionData.sprites)) .setFrameWidth(await this.calculateMaxWidth(actionData.sprites))
.setFrameHeight(totalHeight) .setFrameHeight(totalHeight)
.setFrameRate(actionData.frameRate) .setFrameRate(actionData.frameRate)
await spriteRepository.getEntityManager().persistAndFlush(spriteAction) await spriteRepository.getEntityManager().persistAndFlush(spriteAction)
} }
@ -126,27 +128,27 @@ export default class SpriteUpdateEvent extends BaseEvent {
// Process images and create sprite sheet // Process images and create sprite sheet
const processedImages = await Promise.all( const processedImages = await Promise.all(
sprites.map(async (sprite, index) => { sprites.map(async (sprite, index) => {
const { width, height, offsetX, offsetY } = await this.processImage(sprite) const { width, height, offsetX, offsetY } = await this.processImage(sprite)
const uri = sprite.url.split(';base64,').pop() const uri = sprite.url.split(';base64,').pop()
if (!uri) throw new Error('Invalid base64 image') if (!uri) throw new Error('Invalid base64 image')
const buffer = Buffer.from(uri, 'base64') const buffer = Buffer.from(uri, 'base64')
// Create individual frame // Create individual frame
const left = offsetX >= 0 ? offsetX : 0 const left = offsetX >= 0 ? offsetX : 0
const verticalOffset = totalHeight - height - (offsetY >= 0 ? offsetY : 0) const verticalOffset = totalHeight - height - (offsetY >= 0 ? offsetY : 0)
return sharp({ return sharp({
create: { create: {
width: maxWidth, width: maxWidth,
height: totalHeight, height: totalHeight,
channels: 4, channels: 4,
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
} }
})
.composite([{ input: buffer, left, top: verticalOffset }])
.png()
.toBuffer()
}) })
.composite([{ input: buffer, left, top: verticalOffset }])
.png()
.toBuffer()
})
) )
// Combine frames into sprite sheet // Combine frames into sprite sheet
@ -158,15 +160,15 @@ export default class SpriteUpdateEvent extends BaseEvent {
background: { r: 0, g: 0, b: 0, alpha: 0 } background: { r: 0, g: 0, b: 0, alpha: 0 }
} }
}) })
.composite( .composite(
processedImages.map((buffer, index) => ({ processedImages.map((buffer, index) => ({
input: buffer, input: buffer,
left: index * maxWidth, left: index * maxWidth,
top: 0 top: 0
})) }))
) )
.png() .png()
.toBuffer() .toBuffer()
// Ensure directory exists // Ensure directory exists
const dir = `public/sprites/${spriteId}` const dir = `public/sprites/${spriteId}`