1
0
forked from noxious/server

Converted socketEvents to new format

Still need to rework 'any' type Promises
This commit is contained in:
Colin Kallemein 2024-09-22 18:35:07 +02:00
parent 81428ea0c2
commit a729371741
13 changed files with 279 additions and 202 deletions

View File

@ -7,18 +7,27 @@ type SocketResponseT = {
character_id: number character_id: number
} }
export default function (io: Server, socket: TSocket) { export default class CharacterConnectEvent {
socket.on('character:connect', async (data: SocketResponseT) => { 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<void> {
console.log('character:connect requested', data) console.log('character:connect requested', data)
try { 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 if (!character) return
socket.characterId = character.id this.socket.characterId = character.id
CharacterManager.initCharacter(character as ExtendedCharacter) CharacterManager.initCharacter(character as ExtendedCharacter)
socket.emit('character:connect', character) this.socket.emit('character:connect', character)
} catch (error: any) { } catch (error: any) {
console.log('character:connect error', error) console.log('character:connect error', error)
} }
}) }
} }

View File

@ -6,26 +6,35 @@ import { ZCharacterCreate } from '../../utilities/zodTypes'
import prisma from '../../utilities/prisma' import prisma from '../../utilities/prisma'
import { gameLogger } from '../../utilities/logger' import { gameLogger } from '../../utilities/logger'
export default function (io: Server, socket: TSocket) { export default class CharacterCreateEvent {
socket.on('character:create', async (data: any) => { 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<any> {
console.log('character:create requested', data) console.log('character:create requested', data)
// zod validate // zod validate
try { try {
data = ZCharacterCreate.parse(data) 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 // Check if character name already exists
const characterExists = await CharacterRepository.getByName(data.name) const characterExists = await CharacterRepository.getByName(data.name)
if (characterExists) { 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[] let characters: Character[] = (await CharacterRepository.getByUserId(user_id)) as Character[]
if (characters.length >= 4) { 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({ const character: Character = await prisma.character.create({
@ -38,14 +47,14 @@ export default function (io: Server, socket: TSocket) {
characters = [...characters, character] characters = [...characters, character]
socket.emit('character:create:success') this.socket.emit('character:create:success')
socket.emit('character:list', characters) this.socket.emit('character:list', characters)
gameLogger.info('character:create success') gameLogger.info('character:create success')
} catch (error: any) { } catch (error: any) {
console.log(error) console.log(error)
gameLogger.error(`character:create error: ${error.message}`) 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).' })
}
} }
})
} }

View File

@ -12,19 +12,28 @@ type TypeResponse = {
characters: Character[] characters: Character[]
} }
export default function (io: Server, socket: TSocket) { export default class CharacterDeleteEvent {
socket.on('character:delete', async (data: TypePayload, callback: (response: TypeResponse) => void) => { 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<any> {
// zod validate // zod validate
try { 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[] const characters: Character[] = (await CharacterRepository.getByUserId(user_id)) as Character[]
socket.emit('character:list', characters) this.socket.emit('character:list', characters)
} catch (error: any) { } catch (error: any) {
console.log(error) 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.' })
}
} }
})
} }

View File

@ -3,15 +3,24 @@ import { TSocket } from '../../utilities/types'
import { Character } from '@prisma/client' import { Character } from '@prisma/client'
import CharacterRepository from '../../repositories/characterRepository' import CharacterRepository from '../../repositories/characterRepository'
export default function CharacterList(io: Server, socket: TSocket) { export default class CharacterListEvent {
socket.on('character:list', async (data: any) => { 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<void> {
try { try {
console.log('character:list requested') 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[] const characters: Character[] = (await CharacterRepository.getByUserId(user_id)) as Character[]
socket.emit('character:list', characters) this.socket.emit('character:list', characters)
} catch (error: any) { } catch (error: any) {
console.log('character:list error', error) console.log('character:list error', error)
} }
}) }
} }

View File

@ -7,14 +7,18 @@ import characterRepository from '../../../../repositories/characterRepository'
interface IPayload {} interface IPayload {}
/** export default class ObjectListEvent {
* Handle game master list object event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:object:list', async (data: any, callback: (response: Object[]) => void) => { public listen(): void {
const character = await characterRepository.getById(socket.characterId as number) this.socket.on('gm:object:list', this.handleObjectList.bind(this))
}
private async handleObjectList(data: IPayload, callback: (response: Object[]) => void): Promise<void> {
const character = await characterRepository.getById(this.socket.characterId as number)
if (!character) return callback([]) if (!character) return callback([])
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -24,5 +28,5 @@ export default function (io: Server, socket: TSocket) {
// get all objects // get all objects
const objects = await ObjectRepository.getAll() const objects = await ObjectRepository.getAll()
callback(objects) callback(objects)
}) }
} }

View File

@ -10,14 +10,18 @@ interface IPayload {
object: string object: string
} }
/** export default class ObjectRemoveEvent {
* Handle game master remove object event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:object:remove', async (data: IPayload, callback: (response: boolean) => void) => { public listen(): void {
const character = await characterRepository.getById(socket.characterId as number) this.socket.on('gm:object:remove', this.handleObjectRemove.bind(this))
}
private async handleObjectRemove(data: IPayload, callback: (response: boolean) => void): Promise<void> {
const character = await characterRepository.getById(this.socket.characterId as number)
if (!character) return callback(false) if (!character) return callback(false)
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -49,5 +53,5 @@ export default function (io: Server, socket: TSocket) {
console.log(e) console.log(e)
callback(false) callback(false)
} }
}) }
} }

View File

@ -16,14 +16,18 @@ type Payload = {
frameHeight: number frameHeight: number
} }
/** export default class ObjectUpdateEvent {
* Handle game master object update event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:object:update', async (data: Payload, callback: (success: boolean) => void) => { public listen(): void {
const character = await characterRepository.getById(socket.characterId as number) this.socket.on('gm:object:update', this.handleObjectUpdate.bind(this))
}
private async handleObjectUpdate(data: Payload, callback: (success: boolean) => void): Promise<void> {
const character = await characterRepository.getById(this.socket.characterId as number)
if (!character) return callback(false) if (!character) return callback(false)
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -51,5 +55,5 @@ export default function (io: Server, socket: TSocket) {
console.error(error) console.error(error)
callback(false) callback(false)
} }
}) }
} }

View File

@ -6,15 +6,19 @@ import prisma from '../../../../utilities/prisma'
import CharacterManager from '../../../../managers/characterManager' import CharacterManager from '../../../../managers/characterManager'
import characterRepository from '../../../../repositories/characterRepository' import characterRepository from '../../../../repositories/characterRepository'
/** export default class SpriteCreateEvent {
* Handle game master new sprite event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:sprite:create', async (data: undefined, callback: (response: boolean) => void) => { public listen(): void {
this.socket.on('gm:sprite:create', this.handleSpriteCreate.bind(this))
}
private async handleSpriteCreate(data: undefined, callback: (response: boolean) => void): Promise<void> {
try { 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) return callback(false)
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -42,5 +46,5 @@ export default function (io: Server, socket: TSocket) {
console.error('Error creating sprite:', error) console.error('Error creating sprite:', error)
callback(false) callback(false)
} }
}) }
} }

View File

@ -7,14 +7,18 @@ import characterRepository from '../../../../repositories/characterRepository'
interface IPayload {} interface IPayload {}
/** export default class SpriteListEvent {
* Handle game master list sprite event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:sprite:list', async (data: any, callback: (response: Sprite[]) => void) => { public listen(): void {
const character = await characterRepository.getById(socket.characterId as number) this.socket.on('gm:sprite:list', this.handleSpriteList.bind(this))
}
private async handleSpriteList(data: any, callback: (response: Sprite[]) => void): Promise<void> {
const character = await characterRepository.getById(this.socket.characterId as number)
if (!character) return callback([]) if (!character) return callback([])
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -24,5 +28,5 @@ export default function (io: Server, socket: TSocket) {
// get all sprites // get all sprites
const sprites = await SpriteRepository.getAll() const sprites = await SpriteRepository.getAll()
callback(sprites) callback(sprites)
}) }
} }

View File

@ -27,9 +27,18 @@ interface ProcessedSpriteAction extends SpriteActionInput {
}> }>
} }
export default function (io: Server, socket: TSocket) { export default class SpriteUpdateEvent {
socket.on('gm:sprite:update', async (data: Payload, callback: (success: boolean) => void) => { constructor(
const character = CharacterManager.getCharacterFromSocket(socket) 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<void> {
const character = CharacterManager.getCharacterFromSocket(this.socket)
if (character?.role !== 'gm') { if (character?.role !== 'gm') {
return callback(false) return callback(false)
} }
@ -46,10 +55,8 @@ export default function (io: Server, socket: TSocket) {
console.error('Error updating sprite:', error) console.error('Error updating sprite:', error)
callback(false) callback(false)
} }
})
}
function validateSpriteActions(spriteActions: Prisma.JsonValue): SpriteActionInput[] { function validateSpriteActions(spriteActions: Prisma.JsonValue): SpriteActionInput[] {
try { try {
const parsed = JSON.parse(JSON.stringify(spriteActions)) as SpriteActionInput[] const parsed = JSON.parse(JSON.stringify(spriteActions)) as SpriteActionInput[]
if (!Array.isArray(parsed)) { if (!Array.isArray(parsed)) {
@ -60,9 +67,9 @@ function validateSpriteActions(spriteActions: Prisma.JsonValue): SpriteActionInp
console.error('Error parsing spriteActions:', error) console.error('Error parsing spriteActions:', error)
throw error throw error
} }
} }
async function processSprites(spriteActions: SpriteActionInput[]): Promise<ProcessedSpriteAction[]> { async function processSprites(spriteActions: SpriteActionInput[]): Promise<ProcessedSpriteAction[]> {
return Promise.all( return Promise.all(
spriteActions.map(async (spriteAction) => { spriteActions.map(async (spriteAction) => {
const { action, sprites } = spriteAction const { action, sprites } = spriteAction
@ -90,9 +97,9 @@ async function processSprites(spriteActions: SpriteActionInput[]): Promise<Proce
} }
}) })
) )
} }
async function updateDatabase(id: string, name: string, processedActions: ProcessedSpriteAction[]) { async function updateDatabase(id: string, name: string, processedActions: ProcessedSpriteAction[]) {
await prisma.sprite.update({ await prisma.sprite.update({
where: { id }, where: { id },
data: { data: {
@ -113,9 +120,9 @@ async function updateDatabase(id: string, name: string, processedActions: Proces
} }
} }
}) })
} }
async function saveSpritesToDisk(id: string, processedActions: ProcessedSpriteAction[]) { async function saveSpritesToDisk(id: string, processedActions: ProcessedSpriteAction[]) {
const publicFolder = path.join(process.cwd(), 'public', 'sprites', id) const publicFolder = path.join(process.cwd(), 'public', 'sprites', id)
await mkdir(publicFolder, { recursive: true }) await mkdir(publicFolder, { recursive: true })
@ -143,4 +150,6 @@ async function saveSpritesToDisk(id: string, processedActions: ProcessedSpriteAc
await writeFile(filename, combinedImage) await writeFile(filename, combinedImage)
}) })
) )
}
}
} }

View File

@ -7,14 +7,18 @@ import characterRepository from '../../../../repositories/characterRepository'
interface IPayload {} interface IPayload {}
/** export default class TileListEvent {
* Handle game master list tile event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:tile:list', async (data: any, callback: (response: Tile[]) => void) => { public listen(): void {
const character = await characterRepository.getById(socket.characterId as number) this.socket.on('gm:tile:list', this.handleTileList.bind(this))
}
private async handleTileList(data: any, callback: (response: Tile[]) => void): Promise<void> {
const character = await characterRepository.getById(this.socket.characterId as number)
if (!character) return if (!character) return
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -24,5 +28,5 @@ export default function (io: Server, socket: TSocket) {
// get all tiles // get all tiles
const tiles = await TileRepository.getAll() const tiles = await TileRepository.getAll()
callback(tiles) callback(tiles)
}) }
} }

View File

@ -10,14 +10,18 @@ type Payload = {
tags: string[] tags: string[]
} }
/** export default class TileUpdateEvent {
* Handle game master tile update event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:tile:update', async (data: Payload, callback: (success: boolean) => void) => { public listen(): void {
const character = await characterRepository.getById(socket.characterId as number) this.socket.on('gm:tile:update', this.handleTileUpdate.bind(this))
}
private async handleTileUpdate(data: Payload, callback: (success: boolean) => void): Promise<void> {
const character = await characterRepository.getById(this.socket.characterId as number)
if (!character) return callback(false) if (!character) return callback(false)
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -40,5 +44,5 @@ export default function (io: Server, socket: TSocket) {
console.error(error) console.error(error)
callback(false) callback(false)
} }
}) }
} }

View File

@ -11,15 +11,19 @@ interface ITileData {
[key: string]: Buffer [key: string]: Buffer
} }
/** export default class TileUploadEvent {
* Handle game master upload tile event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
export default function (io: Server, socket: TSocket) {
socket.on('gm:tile:upload', async (data: ITileData, callback: (response: boolean) => void) => { public listen(): void {
this.socket.on('gm:tile:upload', this.handleTileUpload.bind(this))
}
private async handleTileUpload(data: ITileData, callback: (response: boolean) => void): Promise<void> {
try { 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) return callback(false)
if (character.role !== 'gm') { if (character.role !== 'gm') {
@ -50,5 +54,5 @@ export default function (io: Server, socket: TSocket) {
gameMasterLogger.error('Error uploading tile:', error) gameMasterLogger.error('Error uploading tile:', error)
callback(false) callback(false)
} }
}) }
} }