From a72937174183388712109071a8d7e4c1cca881a5 Mon Sep 17 00:00:00 2001 From: Colin Kallemein Date: Sun, 22 Sep 2024 18:35:07 +0200 Subject: [PATCH] Converted socketEvents to new format Still need to rework 'any' type Promises --- src/socketEvents/character/connect.ts | 23 ++- src/socketEvents/character/create.ts | 29 ++- src/socketEvents/character/delete.ts | 25 ++- src/socketEvents/character/list.ts | 21 +- .../gameMaster/assetManager/object/list.ts | 24 ++- .../gameMaster/assetManager/object/remove.ts | 24 ++- .../gameMaster/assetManager/object/update.ts | 24 ++- .../gameMaster/assetManager/sprite/create.ts | 24 ++- .../gameMaster/assetManager/sprite/list.ts | 24 ++- .../gameMaster/assetManager/sprite/update.ts | 191 +++++++++--------- .../gameMaster/assetManager/tile/list.ts | 24 ++- .../gameMaster/assetManager/tile/update.ts | 24 ++- .../gameMaster/assetManager/tile/upload.ts | 24 ++- 13 files changed, 279 insertions(+), 202 deletions(-) diff --git a/src/socketEvents/character/connect.ts b/src/socketEvents/character/connect.ts index 7527efc..1d3a7f4 100644 --- a/src/socketEvents/character/connect.ts +++ b/src/socketEvents/character/connect.ts @@ -7,18 +7,27 @@ type SocketResponseT = { character_id: number } -export default function (io: Server, socket: TSocket) { - socket.on('character:connect', async (data: SocketResponseT) => { +export default class CharacterConnectEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('character:connect', this.handleCharacterConnect.bind(this)) + } + + private async handleCharacterConnect(data: SocketResponseT): Promise { console.log('character:connect requested', data) try { - const character = await CharacterRepository.getByUserAndId(socket?.user?.id as number, data.character_id) + const character = await CharacterRepository.getByUserAndId(this.socket?.user?.id as number, data.character_id) if (!character) return - socket.characterId = character.id + this.socket.characterId = character.id CharacterManager.initCharacter(character as ExtendedCharacter) - socket.emit('character:connect', character) + this.socket.emit('character:connect', character) } catch (error: any) { console.log('character:connect error', error) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/character/create.ts b/src/socketEvents/character/create.ts index 0126036..6fce99c 100644 --- a/src/socketEvents/character/create.ts +++ b/src/socketEvents/character/create.ts @@ -6,26 +6,35 @@ import { ZCharacterCreate } from '../../utilities/zodTypes' import prisma from '../../utilities/prisma' import { gameLogger } from '../../utilities/logger' -export default function (io: Server, socket: TSocket) { - socket.on('character:create', async (data: any) => { +export default class CharacterCreateEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('character:create', this.handleCharacterCreate.bind(this)) + } + + private async handleCharacterCreate(data: any): Promise { console.log('character:create requested', data) // zod validate try { data = ZCharacterCreate.parse(data) - const user_id = socket.user?.id as number + const user_id = this.socket.user?.id as number // Check if character name already exists const characterExists = await CharacterRepository.getByName(data.name) if (characterExists) { - return socket.emit('notification', { message: 'Character name already exists' }) + return this.socket.emit('notification', { message: 'Character name already exists' }) } let characters: Character[] = (await CharacterRepository.getByUserId(user_id)) as Character[] if (characters.length >= 4) { - return socket.emit('notification', { message: 'You can only have 4 characters' }) + return this.socket.emit('notification', { message: 'You can only have 4 characters' }) } const character: Character = await prisma.character.create({ @@ -38,14 +47,14 @@ export default function (io: Server, socket: TSocket) { characters = [...characters, character] - socket.emit('character:create:success') - socket.emit('character:list', characters) + this.socket.emit('character:create:success') + this.socket.emit('character:list', characters) gameLogger.info('character:create success') } catch (error: any) { console.log(error) gameLogger.error(`character:create error: ${error.message}`) - return socket.emit('notification', { message: 'Could not create character. Please try again (later).' }) + return this.socket.emit('notification', { message: 'Could not create character. Please try again (later).' }) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/character/delete.ts b/src/socketEvents/character/delete.ts index 04f2bac..55a2209 100644 --- a/src/socketEvents/character/delete.ts +++ b/src/socketEvents/character/delete.ts @@ -12,19 +12,28 @@ type TypeResponse = { characters: Character[] } -export default function (io: Server, socket: TSocket) { - socket.on('character:delete', async (data: TypePayload, callback: (response: TypeResponse) => void) => { +export default class CharacterDeleteEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('character:delete', this.handleCharacterDelete.bind(this)) + } + + private async handleCharacterDelete(data: TypePayload, callback: (response: TypeResponse) => void): Promise { // zod validate try { - await CharacterRepository.deleteByUserIdAndId(socket.user?.id as number, data.character_id as number) + await CharacterRepository.deleteByUserIdAndId(this.socket.user?.id as number, data.character_id as number) - const user_id = socket.user?.id as number + const user_id = this.socket.user?.id as number const characters: Character[] = (await CharacterRepository.getByUserId(user_id)) as Character[] - socket.emit('character:list', characters) + this.socket.emit('character:list', characters) } catch (error: any) { console.log(error) - return socket.emit('notification', { message: 'Character delete failed. Please try again.' }) + return this.socket.emit('notification', { message: 'Character delete failed. Please try again.' }) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/character/list.ts b/src/socketEvents/character/list.ts index 2243e6e..81fdab0 100644 --- a/src/socketEvents/character/list.ts +++ b/src/socketEvents/character/list.ts @@ -3,15 +3,24 @@ import { TSocket } from '../../utilities/types' import { Character } from '@prisma/client' import CharacterRepository from '../../repositories/characterRepository' -export default function CharacterList(io: Server, socket: TSocket) { - socket.on('character:list', async (data: any) => { +export default class CharacterListEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('character:list', this.handleCharacterList.bind(this)) + } + + private async handleCharacterList(data: any): Promise { try { console.log('character:list requested') - const user_id = socket.user?.id as number + const user_id = this.socket.user?.id as number const characters: Character[] = (await CharacterRepository.getByUserId(user_id)) as Character[] - socket.emit('character:list', characters) + this.socket.emit('character:list', characters) } catch (error: any) { console.log('character:list error', error) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/object/list.ts b/src/socketEvents/gameMaster/assetManager/object/list.ts index 472cf47..2a4088b 100644 --- a/src/socketEvents/gameMaster/assetManager/object/list.ts +++ b/src/socketEvents/gameMaster/assetManager/object/list.ts @@ -7,14 +7,18 @@ import characterRepository from '../../../../repositories/characterRepository' interface IPayload {} -/** - * Handle game master list object event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:object:list', async (data: any, callback: (response: Object[]) => void) => { - const character = await characterRepository.getById(socket.characterId as number) +export default class ObjectListEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:object:list', this.handleObjectList.bind(this)) + } + + private async handleObjectList(data: IPayload, callback: (response: Object[]) => void): Promise { + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return callback([]) if (character.role !== 'gm') { @@ -24,5 +28,5 @@ export default function (io: Server, socket: TSocket) { // get all objects const objects = await ObjectRepository.getAll() callback(objects) - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/object/remove.ts b/src/socketEvents/gameMaster/assetManager/object/remove.ts index aba06c2..bf79c2f 100644 --- a/src/socketEvents/gameMaster/assetManager/object/remove.ts +++ b/src/socketEvents/gameMaster/assetManager/object/remove.ts @@ -10,14 +10,18 @@ interface IPayload { object: string } -/** - * Handle game master remove object event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:object:remove', async (data: IPayload, callback: (response: boolean) => void) => { - const character = await characterRepository.getById(socket.characterId as number) +export default class ObjectRemoveEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:object:remove', this.handleObjectRemove.bind(this)) + } + + private async handleObjectRemove(data: IPayload, callback: (response: boolean) => void): Promise { + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return callback(false) if (character.role !== 'gm') { @@ -49,5 +53,5 @@ export default function (io: Server, socket: TSocket) { console.log(e) callback(false) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/object/update.ts b/src/socketEvents/gameMaster/assetManager/object/update.ts index 60bd1b7..2b4fff8 100644 --- a/src/socketEvents/gameMaster/assetManager/object/update.ts +++ b/src/socketEvents/gameMaster/assetManager/object/update.ts @@ -16,14 +16,18 @@ type Payload = { frameHeight: number } -/** - * Handle game master object update event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:object:update', async (data: Payload, callback: (success: boolean) => void) => { - const character = await characterRepository.getById(socket.characterId as number) +export default class ObjectUpdateEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:object:update', this.handleObjectUpdate.bind(this)) + } + + private async handleObjectUpdate(data: Payload, callback: (success: boolean) => void): Promise { + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return callback(false) if (character.role !== 'gm') { @@ -51,5 +55,5 @@ export default function (io: Server, socket: TSocket) { console.error(error) callback(false) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/sprite/create.ts b/src/socketEvents/gameMaster/assetManager/sprite/create.ts index ad0dda4..abf9ec6 100644 --- a/src/socketEvents/gameMaster/assetManager/sprite/create.ts +++ b/src/socketEvents/gameMaster/assetManager/sprite/create.ts @@ -6,15 +6,19 @@ import prisma from '../../../../utilities/prisma' import CharacterManager from '../../../../managers/characterManager' import characterRepository from '../../../../repositories/characterRepository' -/** - * Handle game master new sprite event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:sprite:create', async (data: undefined, callback: (response: boolean) => void) => { +export default class SpriteCreateEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:sprite:create', this.handleSpriteCreate.bind(this)) + } + + private async handleSpriteCreate(data: undefined, callback: (response: boolean) => void): Promise { try { - const character = await characterRepository.getById(socket.characterId as number) + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return callback(false) if (character.role !== 'gm') { @@ -42,5 +46,5 @@ export default function (io: Server, socket: TSocket) { console.error('Error creating sprite:', error) callback(false) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/sprite/list.ts b/src/socketEvents/gameMaster/assetManager/sprite/list.ts index ae68853..5cead27 100644 --- a/src/socketEvents/gameMaster/assetManager/sprite/list.ts +++ b/src/socketEvents/gameMaster/assetManager/sprite/list.ts @@ -7,14 +7,18 @@ import characterRepository from '../../../../repositories/characterRepository' interface IPayload {} -/** - * Handle game master list sprite event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:sprite:list', async (data: any, callback: (response: Sprite[]) => void) => { - const character = await characterRepository.getById(socket.characterId as number) +export default class SpriteListEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:sprite:list', this.handleSpriteList.bind(this)) + } + + private async handleSpriteList(data: any, callback: (response: Sprite[]) => void): Promise { + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return callback([]) if (character.role !== 'gm') { @@ -24,5 +28,5 @@ export default function (io: Server, socket: TSocket) { // get all sprites const sprites = await SpriteRepository.getAll() callback(sprites) - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/sprite/update.ts b/src/socketEvents/gameMaster/assetManager/sprite/update.ts index 37805bc..e2da274 100644 --- a/src/socketEvents/gameMaster/assetManager/sprite/update.ts +++ b/src/socketEvents/gameMaster/assetManager/sprite/update.ts @@ -27,9 +27,18 @@ interface ProcessedSpriteAction extends SpriteActionInput { }> } -export default function (io: Server, socket: TSocket) { - socket.on('gm:sprite:update', async (data: Payload, callback: (success: boolean) => void) => { - const character = CharacterManager.getCharacterFromSocket(socket) +export default class SpriteUpdateEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:sprite:update', this.handleSpriteUpdate.bind(this)) + } + + private async handleSpriteUpdate(data: Payload, callback: (success: boolean) => void): Promise { + const character = CharacterManager.getCharacterFromSocket(this.socket) if (character?.role !== 'gm') { return callback(false) } @@ -46,101 +55,101 @@ export default function (io: Server, socket: TSocket) { console.error('Error updating sprite:', error) callback(false) } - }) -} -function validateSpriteActions(spriteActions: Prisma.JsonValue): SpriteActionInput[] { - try { - const parsed = JSON.parse(JSON.stringify(spriteActions)) as SpriteActionInput[] - if (!Array.isArray(parsed)) { - throw new Error('spriteActions is not an array') - } - return parsed - } catch (error) { - console.error('Error parsing spriteActions:', error) - throw error - } -} - -async function processSprites(spriteActions: SpriteActionInput[]): Promise { - return Promise.all( - spriteActions.map(async (spriteAction) => { - const { action, sprites } = spriteAction - - if (!Array.isArray(sprites) || sprites.length === 0) { - throw new Error(`Invalid sprites array for action: ${action}`) + function validateSpriteActions(spriteActions: Prisma.JsonValue): SpriteActionInput[] { + try { + const parsed = JSON.parse(JSON.stringify(spriteActions)) as SpriteActionInput[] + if (!Array.isArray(parsed)) { + throw new Error('spriteActions is not an array') + } + return parsed + } catch (error) { + console.error('Error parsing spriteActions:', error) + throw error } + } - const buffersWithDimensions = await Promise.all( - sprites.map(async (sprite: string) => { - const buffer = Buffer.from(sprite.split(',')[1], 'base64') - const { width, height } = await sharp(buffer).metadata() - return { buffer, width, height } + async function processSprites(spriteActions: SpriteActionInput[]): Promise { + return Promise.all( + spriteActions.map(async (spriteAction) => { + const { action, sprites } = spriteAction + + if (!Array.isArray(sprites) || sprites.length === 0) { + throw new Error(`Invalid sprites array for action: ${action}`) + } + + const buffersWithDimensions = await Promise.all( + sprites.map(async (sprite: string) => { + const buffer = Buffer.from(sprite.split(',')[1], 'base64') + const { width, height } = await sharp(buffer).metadata() + return { buffer, width, height } + }) + ) + + const frameWidth = Math.max(...buffersWithDimensions.map((b) => b.width || 0)) + const frameHeight = Math.max(...buffersWithDimensions.map((b) => b.height || 0)) + + return { + ...spriteAction, + frameWidth, + frameHeight, + buffersWithDimensions + } }) ) - - const frameWidth = Math.max(...buffersWithDimensions.map((b) => b.width || 0)) - const frameHeight = Math.max(...buffersWithDimensions.map((b) => b.height || 0)) - - return { - ...spriteAction, - frameWidth, - frameHeight, - buffersWithDimensions - } - }) - ) -} - -async function updateDatabase(id: string, name: string, processedActions: ProcessedSpriteAction[]) { - await prisma.sprite.update({ - where: { id }, - data: { - name, - spriteActions: { - deleteMany: { spriteId: id }, - create: processedActions.map(({ action, sprites, originX, originY, isAnimated, isLooping, frameWidth, frameHeight, frameSpeed }) => ({ - action, - sprites, - originX, - originY, - isAnimated, - isLooping, - frameWidth, - frameHeight, - frameSpeed - })) - } } - }) -} -async function saveSpritesToDisk(id: string, processedActions: ProcessedSpriteAction[]) { - const publicFolder = path.join(process.cwd(), 'public', 'sprites', id) - await mkdir(publicFolder, { recursive: true }) - - await Promise.all( - processedActions.map(async ({ action, buffersWithDimensions, frameWidth, frameHeight }) => { - const combinedImage = await sharp({ - create: { - width: frameWidth * buffersWithDimensions.length, - height: frameHeight, - channels: 4, - background: { r: 0, g: 0, b: 0, alpha: 0 } + async function updateDatabase(id: string, name: string, processedActions: ProcessedSpriteAction[]) { + await prisma.sprite.update({ + where: { id }, + data: { + name, + spriteActions: { + deleteMany: { spriteId: id }, + create: processedActions.map(({ action, sprites, originX, originY, isAnimated, isLooping, frameWidth, frameHeight, frameSpeed }) => ({ + action, + sprites, + originX, + originY, + isAnimated, + isLooping, + frameWidth, + frameHeight, + frameSpeed + })) + } } }) - .composite( - buffersWithDimensions.map(({ buffer }, index) => ({ - input: buffer, - left: index * frameWidth, - top: 0 - })) - ) - .png() - .toBuffer() + } - const filename = path.join(publicFolder, `${action}.png`) - await writeFile(filename, combinedImage) - }) - ) -} + async function saveSpritesToDisk(id: string, processedActions: ProcessedSpriteAction[]) { + const publicFolder = path.join(process.cwd(), 'public', 'sprites', id) + await mkdir(publicFolder, { recursive: true }) + + await Promise.all( + processedActions.map(async ({ action, buffersWithDimensions, frameWidth, frameHeight }) => { + const combinedImage = await sharp({ + create: { + width: frameWidth * buffersWithDimensions.length, + height: frameHeight, + channels: 4, + background: { r: 0, g: 0, b: 0, alpha: 0 } + } + }) + .composite( + buffersWithDimensions.map(({ buffer }, index) => ({ + input: buffer, + left: index * frameWidth, + top: 0 + })) + ) + .png() + .toBuffer() + + const filename = path.join(publicFolder, `${action}.png`) + await writeFile(filename, combinedImage) + }) + ) + } + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/tile/list.ts b/src/socketEvents/gameMaster/assetManager/tile/list.ts index 1acf814..7623586 100644 --- a/src/socketEvents/gameMaster/assetManager/tile/list.ts +++ b/src/socketEvents/gameMaster/assetManager/tile/list.ts @@ -7,14 +7,18 @@ import characterRepository from '../../../../repositories/characterRepository' interface IPayload {} -/** - * Handle game master list tile event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:tile:list', async (data: any, callback: (response: Tile[]) => void) => { - const character = await characterRepository.getById(socket.characterId as number) +export default class TileListEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:tile:list', this.handleTileList.bind(this)) + } + + private async handleTileList(data: any, callback: (response: Tile[]) => void): Promise { + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return if (character.role !== 'gm') { @@ -24,5 +28,5 @@ export default function (io: Server, socket: TSocket) { // get all tiles const tiles = await TileRepository.getAll() callback(tiles) - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/tile/update.ts b/src/socketEvents/gameMaster/assetManager/tile/update.ts index 800973b..24d1995 100644 --- a/src/socketEvents/gameMaster/assetManager/tile/update.ts +++ b/src/socketEvents/gameMaster/assetManager/tile/update.ts @@ -10,14 +10,18 @@ type Payload = { tags: string[] } -/** - * Handle game master tile update event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:tile:update', async (data: Payload, callback: (success: boolean) => void) => { - const character = await characterRepository.getById(socket.characterId as number) +export default class TileUpdateEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:tile:update', this.handleTileUpdate.bind(this)) + } + + private async handleTileUpdate(data: Payload, callback: (success: boolean) => void): Promise { + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return callback(false) if (character.role !== 'gm') { @@ -40,5 +44,5 @@ export default function (io: Server, socket: TSocket) { console.error(error) callback(false) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/assetManager/tile/upload.ts b/src/socketEvents/gameMaster/assetManager/tile/upload.ts index d994da2..44e2a6f 100644 --- a/src/socketEvents/gameMaster/assetManager/tile/upload.ts +++ b/src/socketEvents/gameMaster/assetManager/tile/upload.ts @@ -11,15 +11,19 @@ interface ITileData { [key: string]: Buffer } -/** - * Handle game master upload tile event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:tile:upload', async (data: ITileData, callback: (response: boolean) => void) => { +export default class TileUploadEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('gm:tile:upload', this.handleTileUpload.bind(this)) + } + + private async handleTileUpload(data: ITileData, callback: (response: boolean) => void): Promise { try { - const character = await characterRepository.getById(socket.characterId as number) + const character = await characterRepository.getById(this.socket.characterId as number) if (!character) return callback(false) if (character.role !== 'gm') { @@ -50,5 +54,5 @@ export default function (io: Server, socket: TSocket) { gameMasterLogger.error('Error uploading tile:', error) callback(false) } - }) -} + } +} \ No newline at end of file