#16: Working PoC avatar image generator

This commit is contained in:
Dennis Postma 2024-12-22 00:12:43 +01:00
parent 68f7db7aa4
commit bf58fc4944
4 changed files with 94 additions and 4 deletions

6
package-lock.json generated
View File

@ -954,9 +954,9 @@
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/bullmq": { "node_modules/bullmq": {
"version": "5.34.3", "version": "5.34.4",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.34.3.tgz", "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.34.4.tgz",
"integrity": "sha512-S8/V11w7p6jYAGvv+00skLza/4inTOupWPe0uCD8mZSUiYKzvmW4/YEB+KVjZI2CC2oD3KJ3t7/KkUd31MxMig==", "integrity": "sha512-FPTN5eqsYO5/Blm6vh/bVJ0eADrVLjTBDjMkPCqZN3xx/5GqHov9NwxM6A6M3m6n2Vg+gNAoN98t7bTij5/UEA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cron-parser": "^4.6.0", "cron-parser": "^4.6.0",

73
src/http/avatar.ts Normal file
View File

@ -0,0 +1,73 @@
/**
* Avatar generator API routes
*/
import { Router, Request, Response } from 'express'
import sharp from 'sharp'
import fs from 'fs'
import CharacterRepository from '../repositories/characterRepository'
import CharacterHairRepository from '../repositories/characterHairRepository'
import CharacterTypeRepository from '../repositories/characterTypeRepository'
import { getPublicPath } from '../utilities/storage'
const router = Router()
interface AvatarOptions {
characterTypeId: number
characterHairId?: number
}
async function generateAvatar(res: Response, options: AvatarOptions) {
try {
const characterType = await CharacterTypeRepository.getById(options.characterTypeId)
if (!characterType?.spriteId) {
return res.status(404).json({ message: 'Character type not found' })
}
const bodySpritePath = getPublicPath('sprites', characterType.spriteId, 'idle_right_down.png')
if (!fs.existsSync(bodySpritePath)) {
console.error(`Body sprite file not found: ${bodySpritePath}`)
return res.status(404).json({ message: 'Body sprite file not found' })
}
let avatar = sharp(bodySpritePath)
if (options.characterHairId) {
const characterHair = await CharacterHairRepository.getById(options.characterHairId)
if (characterHair?.spriteId) {
const hairSpritePath = getPublicPath('sprites', characterHair.spriteId, 'front.png')
if (fs.existsSync(hairSpritePath)) {
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
} else {
console.error(`Hair sprite file not found: ${hairSpritePath}`)
}
}
}
res.setHeader('Content-Type', 'image/png')
return avatar.pipe(res)
} catch (error) {
console.error('Error generating avatar:', error)
return res.status(500).json({ message: 'Error generating avatar' })
}
}
router.get('/avatar/:characterName', async (req: Request, res: Response) => {
const character = await CharacterRepository.getByName(req.params.characterName)
if (!character?.characterType) {
return res.status(404).json({ message: 'Character or character type not found' })
}
return generateAvatar(res, {
characterTypeId: character.characterType.id,
characterHairId: character.characterHair?.id
})
})
router.get('/avatar/s/:characterTypeId/:characterHairId?', async (req: Request, res: Response) => {
return generateAvatar(res, {
characterTypeId: parseInt(req.params.characterTypeId),
characterHairId: req.params.characterHairId ? parseInt(req.params.characterHairId) : undefined
})
})
export default router

View File

@ -12,6 +12,11 @@ class CharacterHairRepository {
} }
}) })
} }
async getById(id: number): Promise<CharacterHair | null> {
return prisma.characterHair.findUnique({
where: { id }
})
}
} }
export default new CharacterHairRepository() export default new CharacterHairRepository()

View File

@ -3,7 +3,19 @@ import { CharacterType } from '@prisma/client'
class CharacterTypeRepository { class CharacterTypeRepository {
async getAll(): Promise<CharacterType[]> { async getAll(): Promise<CharacterType[]> {
return prisma.characterType.findMany() return prisma.characterType.findMany({
include: {
sprite: true
}
})
}
async getById(id: number): Promise<CharacterType | null> {
return prisma.characterType.findUnique({
where: { id },
include: {
sprite: true
}
})
} }
} }