diff --git a/src/socketEvents/chat/gameMaster/alertCommand.ts b/src/socketEvents/chat/gameMaster/alertCommand.ts index 3ffbdce..b3f2d2a 100644 --- a/src/socketEvents/chat/gameMaster/alertCommand.ts +++ b/src/socketEvents/chat/gameMaster/alertCommand.ts @@ -2,27 +2,47 @@ import { Server } from 'socket.io' import { TSocket } from '../../../utilities/types' import { getArgs, isCommand } from '../../../utilities/chat' import CharacterRepository from '../../../repositories/characterRepository' +import { gameLogger } from '../../../utilities/logger' type TypePayload = { message: string } -export default function (io: Server, socket: TSocket) { - socket.on('chat:send_message', async (data: TypePayload, callback: (response: boolean) => void) => { +export default class AlertCommandEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('chat:send_message', this.handleAlertCommand.bind(this)) + } + + private async handleAlertCommand(data: TypePayload, callback: (response: boolean) => void): Promise { try { - if (!isCommand(data.message, 'alert')) return + if (!isCommand(data.message, 'alert')) { + return + } const args = getArgs('alert', data.message) - if (!args) return + if (!args) { + callback(false) + return + } - const character = await CharacterRepository.getByUserAndId(socket.user?.id as number, socket.characterId as number) - if (!character) return + const character = await CharacterRepository.getByUserAndId(this.socket.user?.id as number, this.socket.characterId as number) + if (!character) { + gameLogger.error('chat:alert_command error', 'Character not found') + callback(false) + return + } - io.emit('notification', { title: 'Message from GM', message: args.join(' ') }) + this.io.emit('notification', { title: 'Message from GM', message: args.join(' ') }) callback(true) } catch (error: any) { - console.log(`---Error sending message: ${error.message}`) + gameLogger.error('chat:alert_command error', error.message) + callback(false) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/disconnect.ts b/src/socketEvents/disconnect.ts index 83585cf..873d2a9 100644 --- a/src/socketEvents/disconnect.ts +++ b/src/socketEvents/disconnect.ts @@ -1,28 +1,42 @@ import { Server } from 'socket.io' import { TSocket } from '../utilities/types' import CharacterManager from '../managers/characterManager' +import { gameLogger } from '../utilities/logger' -export default function (io: Server, socket: TSocket) { - socket.on('disconnect', async (data: any) => { - if (!socket.user) { - console.log('User disconnected but had no user set') - return +export default class DisconnectEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('disconnect', this.handleDisconnect.bind(this)) + } + + private async handleDisconnect(data: any): Promise { + try { + if (!this.socket.user) { + gameLogger.info('User disconnected but had no user set') + return + } + + this.io.emit('user:disconnect', this.socket.user.id) + + const character = CharacterManager.getCharacterFromSocket(this.socket) + + if (!character) { + gameLogger.info('User disconnected but had no character set') + return + } + + gameLogger.info('User disconnected along with their character') + + await CharacterManager.removeCharacter(character) + + this.io.in(character.zoneId.toString()).emit('zone:character:leave', character.id) + this.io.emit('character:disconnect', character.id) + } catch (error: any) { + gameLogger.error('disconnect error', error.message) } - - io.emit('user:disconnect', socket.user.id) - - const character = CharacterManager.getCharacterFromSocket(socket) - - if (!character) { - console.log('User disconnected but had no character set') - return - } - - console.log('User disconnected along with their character') - - await CharacterManager.removeCharacter(character) - - io.in(character.zoneId.toString()).emit('zone:character:leave', character.id) - io.emit('character:disconnect', character.id) - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/zoneEditor/create.ts b/src/socketEvents/gameMaster/zoneEditor/create.ts index 9d5e627..32e0532 100644 --- a/src/socketEvents/gameMaster/zoneEditor/create.ts +++ b/src/socketEvents/gameMaster/zoneEditor/create.ts @@ -3,7 +3,7 @@ import { TSocket } from '../../../utilities/types' import ZoneRepository from '../../../repositories/zoneRepository' import { Zone } from '@prisma/client' import prisma from '../../../utilities/prisma' -import characterRepository from '../../../repositories/characterRepository' +import CharacterRepository from '../../../repositories/characterRepository' import { gameMasterLogger } from '../../../utilities/logger' type Payload = { @@ -12,25 +12,33 @@ type Payload = { height: number } -/** - * Handle game master zone create event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:zone_editor:zone:create', async (data: Payload, callback: (response: Zone[]) => void) => { - const character = await characterRepository.getById(socket.characterId as number) - if (!character) return +export default class ZoneCreateEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} - if (character.role !== 'gm') { - gameMasterLogger.info(`User ${character.id} tried to create zone but is not a game master.`) - return - } + public listen(): void { + this.socket.on('gm:zone_editor:zone:create', this.handleZoneCreate.bind(this)) + } - gameMasterLogger.info(`User ${character.id} has created a new zone via zone editor.`) - - let zoneList: Zone[] = [] + private async handleZoneCreate(data: Payload, callback: (response: Zone[]) => void): Promise { try { + const character = await CharacterRepository.getById(this.socket.characterId as number) + if (!character) { + gameMasterLogger.error('gm:zone_editor:zone:create error', 'Character not found') + callback([]) + return + } + + if (character.role !== 'gm') { + gameMasterLogger.info(`User ${character.id} tried to create zone but is not a game master.`) + callback([]) + return + } + + gameMasterLogger.info(`User ${character.id} has created a new zone via zone editor.`) + const zone = await prisma.zone.create({ data: { name: data.name, @@ -40,13 +48,15 @@ export default function (io: Server, socket: TSocket) { } }) - zoneList = await ZoneRepository.getAll() - callback(zoneList) - // send over zone and characters to socket - } catch (e) { - console.error(e) - socket.emit('notification', { message: 'Failed to create zoneEditor.' }) + const zoneList = await ZoneRepository.getAll() callback(zoneList) + + // You might want to emit an event to notify other clients about the new zone + // this.io.emit('gm:zone_created', zone); + } catch (error: any) { + gameMasterLogger.error('gm:zone_editor:zone:create error', error.message) + this.socket.emit('notification', { message: 'Failed to create zone.' }) + callback([]) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/zoneEditor/delete.ts b/src/socketEvents/gameMaster/zoneEditor/delete.ts index 2825f5c..18c6a7a 100644 --- a/src/socketEvents/gameMaster/zoneEditor/delete.ts +++ b/src/socketEvents/gameMaster/zoneEditor/delete.ts @@ -2,35 +2,44 @@ import { Server } from 'socket.io' import { TSocket } from '../../../utilities/types' import ZoneRepository from '../../../repositories/zoneRepository' import prisma from '../../../utilities/prisma' -import characterRepository from '../../../repositories/characterRepository' +import CharacterRepository from '../../../repositories/characterRepository' import { gameMasterLogger } from '../../../utilities/logger' type Payload = { zoneId: number } -/** - * Handle game master zone delete event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:zone_editor:zone:delete', async (data: Payload, callback: (response: boolean) => void) => { - const character = await characterRepository.getById(socket.characterId as number) - if (!character) return +export default class ZoneDeleteEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} - if (character.role !== 'gm') { - gameMasterLogger.info(`User ${character.id} tried to delete zone but is not a game master.`) - return - } - - gameMasterLogger.info(`User ${character.id} has deleted a zone via zone editor.`) + public listen(): void { + this.socket.on('gm:zone_editor:zone:delete', this.handleZoneDelete.bind(this)) + } + private async handleZoneDelete(data: Payload, callback: (response: boolean) => void): Promise { try { - const zone = await ZoneRepository.getById(data.zoneId) + const character = await CharacterRepository.getById(this.socket.characterId as number) + if (!character) { + gameMasterLogger.error('gm:zone_editor:zone:delete error', 'Character not found') + callback(false) + return + } + if (character.role !== 'gm') { + gameMasterLogger.info(`User ${character.id} tried to delete zone but is not a game master.`) + callback(false) + return + } + + gameMasterLogger.info(`User ${character.id} has deleted a zone via zone editor.`) + + const zone = await ZoneRepository.getById(data.zoneId) if (!zone) { - console.log(`---Zone not found.`) + gameMasterLogger.error('gm:zone_editor:zone:delete error', 'Zone not found') + callback(false) return } @@ -41,9 +50,12 @@ export default function (io: Server, socket: TSocket) { }) callback(true) - } catch (e) { - console.error(e) + + // You might want to emit an event to notify other clients about the deleted zone + // this.io.emit('gm:zone_deleted', data.zoneId); + } catch (error: any) { + gameMasterLogger.error('gm:zone_editor:zone:delete error', error.message) callback(false) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/zoneEditor/list.ts b/src/socketEvents/gameMaster/zoneEditor/list.ts index 6ef2c3f..50d65f6 100644 --- a/src/socketEvents/gameMaster/zoneEditor/list.ts +++ b/src/socketEvents/gameMaster/zoneEditor/list.ts @@ -2,34 +2,43 @@ import { Server } from 'socket.io' import { TSocket } from '../../../utilities/types' import { Zone } from '@prisma/client' import ZoneRepository from '../../../repositories/zoneRepository' -import characterRepository from '../../../repositories/characterRepository' +import CharacterRepository from '../../../repositories/characterRepository' import { gameMasterLogger } from '../../../utilities/logger' interface IPayload {} -/** - * Handle game master list zones event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:zone_editor:zone:list', async (data: IPayload, callback: (response: Zone[]) => void) => { - const character = await characterRepository.getById(socket.characterId as number) - if (!character) return +export default class ZoneListEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} - if (character.role !== 'gm') { - gameMasterLogger.info(`User ${character.id} tried to list zones but is not a game master.`) - return - } - - gameMasterLogger.info(`User ${character.id} has requested zone list via zone editor.`) + public listen(): void { + this.socket.on('gm:zone_editor:zone:list', this.handleZoneList.bind(this)) + } + private async handleZoneList(data: IPayload, callback: (response: Zone[]) => void): Promise { try { + const character = await CharacterRepository.getById(this.socket.characterId as number) + if (!character) { + gameMasterLogger.error('gm:zone_editor:zone:list error', 'Character not found') + callback([]) + return + } + + if (character.role !== 'gm') { + gameMasterLogger.info(`User ${character.id} tried to list zones but is not a game master.`) + callback([]) + return + } + + gameMasterLogger.info(`User ${character.id} has requested zone list via zone editor.`) + const zones = await ZoneRepository.getAll() callback(zones) - } catch (e) { - console.error(e) + } catch (error: any) { + gameMasterLogger.error('gm:zone_editor:zone:list error', error.message) callback([]) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/zoneEditor/request.ts b/src/socketEvents/gameMaster/zoneEditor/request.ts index a6cc47c..7f9e664 100644 --- a/src/socketEvents/gameMaster/zoneEditor/request.ts +++ b/src/socketEvents/gameMaster/zoneEditor/request.ts @@ -2,46 +2,58 @@ import { Server } from 'socket.io' import { TSocket } from '../../../utilities/types' import ZoneRepository from '../../../repositories/zoneRepository' import { Zone } from '@prisma/client' -import characterRepository from '../../../repositories/characterRepository' +import CharacterRepository from '../../../repositories/characterRepository' import { gameMasterLogger } from '../../../utilities/logger' interface IPayload { zoneId: number } -/** - * Handle game master zone request event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:zone_editor:zone:request', async (data: IPayload, callback: (response: Zone) => void) => { - const character = await characterRepository.getById(socket.characterId as number) - if (!character) return +export default class ZoneRequestEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} - if (character.role !== 'gm') { - gameMasterLogger.info(`User ${character!.id} tried to request zone but is not a game master.`) - return - } - - gameMasterLogger.info(`User ${character.id} has requested zone via zone editor.`) - - if (!data.zoneId) { - gameMasterLogger.info(`User ${character.id} tried to request zone but did not provide a zone id.`) - return - } + public listen(): void { + this.socket.on('gm:zone_editor:zone:request', this.handleZoneRequest.bind(this)) + } + private async handleZoneRequest(data: IPayload, callback: (response: Zone | null) => void): Promise { try { + const character = await CharacterRepository.getById(this.socket.characterId as number) + if (!character) { + gameMasterLogger.error('gm:zone_editor:zone:request error', 'Character not found') + callback(null) + return + } + + if (character.role !== 'gm') { + gameMasterLogger.info(`User ${character.id} tried to request zone but is not a game master.`) + callback(null) + return + } + + gameMasterLogger.info(`User ${character.id} has requested zone via zone editor.`) + + if (!data.zoneId) { + gameMasterLogger.info(`User ${character.id} tried to request zone but did not provide a zone id.`) + callback(null) + return + } + const zone = await ZoneRepository.getById(data.zoneId) if (!zone) { gameMasterLogger.info(`User ${character.id} tried to request zone ${data.zoneId} but it does not exist.`) + callback(null) return } callback(zone) - } catch (e) { - console.error(e) + } catch (error: any) { + gameMasterLogger.error('gm:zone_editor:zone:request error', error.message) + callback(null) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/gameMaster/zoneEditor/update.ts b/src/socketEvents/gameMaster/zoneEditor/update.ts index 498908d..423f6ca 100644 --- a/src/socketEvents/gameMaster/zoneEditor/update.ts +++ b/src/socketEvents/gameMaster/zoneEditor/update.ts @@ -4,7 +4,7 @@ import ZoneRepository from '../../../repositories/zoneRepository' import { Zone, ZoneEventTileType, ZoneObject } from '@prisma/client' import prisma from '../../../utilities/prisma' import zoneManager from '../../../managers/zoneManager' -import characterRepository from '../../../repositories/characterRepository' +import CharacterRepository from '../../../repositories/characterRepository' import { gameMasterLogger } from '../../../utilities/logger' interface IPayload { @@ -27,40 +27,49 @@ interface IPayload { zoneObjects: ZoneObject[] } -/** - * Handle game master zone update event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('gm:zone_editor:zone:update', async (data: IPayload, callback: (response: Zone) => void) => { - const character = await characterRepository.getById(socket.characterId as number) - if (!character) return +export default class ZoneUpdateEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} - if (character.role !== 'gm') { - gameMasterLogger.info(`User ${character.id} tried to update zone but is not a game master.`) - return - } - - gameMasterLogger.info(`User ${character.id} has updated zone via zone editor.`) - - if (!data.zoneId) { - gameMasterLogger.info(`User ${character.id} tried to update zone but did not provide a zone id.`) - return - } + public listen(): void { + this.socket.on('gm:zone_editor:zone:update', this.handleZoneUpdate.bind(this)) + } + private async handleZoneUpdate(data: IPayload, callback: (response: Zone | null) => void): Promise { try { + const character = await CharacterRepository.getById(this.socket.characterId as number) + if (!character) { + gameMasterLogger.error('gm:zone_editor:zone:update error', 'Character not found') + callback(null) + return + } + + if (character.role !== 'gm') { + gameMasterLogger.info(`User ${character.id} tried to update zone but is not a game master.`) + callback(null) + return + } + + gameMasterLogger.info(`User ${character.id} has updated zone via zone editor.`) + + if (!data.zoneId) { + gameMasterLogger.info(`User ${character.id} tried to update zone but did not provide a zone id.`) + callback(null) + return + } + let zone = await ZoneRepository.getById(data.zoneId) if (!zone) { gameMasterLogger.info(`User ${character.id} tried to update zone ${data.zoneId} but it does not exist.`) + callback(null) return } await prisma.zone.update({ - where: { - id: data.zoneId - }, + where: { id: data.zoneId }, data: { name: data.name, width: data.width, @@ -68,32 +77,26 @@ export default function (io: Server, socket: TSocket) { tiles: data.tiles, pvp: data.pvp, zoneEventTiles: { - deleteMany: { - zoneId: data.zoneId // Ensure only event tiles related to the zone are deleted - }, - // Save new zone event tiles + deleteMany: { zoneId: data.zoneId }, create: data.zoneEventTiles.map((zoneEventTile) => ({ type: zoneEventTile.type, positionX: zoneEventTile.positionX, positionY: zoneEventTile.positionY, ...(zoneEventTile.type === 'TELEPORT' && zoneEventTile.teleport ? { - teleport: { - create: { - toZoneId: zoneEventTile.teleport.toZoneId, - toPositionX: zoneEventTile.teleport.toPositionX, - toPositionY: zoneEventTile.teleport.toPositionY - } + teleport: { + create: { + toZoneId: zoneEventTile.teleport.toZoneId, + toPositionX: zoneEventTile.teleport.toPositionX, + toPositionY: zoneEventTile.teleport.toPositionY } } + } : {}) })) }, zoneObjects: { - deleteMany: { - zoneId: data.zoneId // Ensure only objects related to the zone are deleted - }, - // Save new zone objects + deleteMany: { zoneId: data.zoneId }, create: data.zoneObjects.map((zoneObject) => ({ objectId: zoneObject.objectId, depth: zoneObject.depth, @@ -107,7 +110,8 @@ export default function (io: Server, socket: TSocket) { zone = await ZoneRepository.getById(data.zoneId) if (!zone) { - gameMasterLogger.info(`User ${character.id} tried to update zone ${data.zoneId} but it does not exist.`) + gameMasterLogger.info(`User ${character.id} tried to update zone ${data.zoneId} but it does not exist after update.`) + callback(null) return } @@ -116,7 +120,8 @@ export default function (io: Server, socket: TSocket) { zoneManager.unloadZone(data.zoneId) await zoneManager.loadZone(zone) } catch (error: any) { - gameMasterLogger.error(`Error updating zone: ${error.message}`) + gameMasterLogger.error('gm:zone_editor:zone:update error', error.message) + callback(null) } - }) -} + } +} \ No newline at end of file diff --git a/src/socketEvents/login.ts b/src/socketEvents/login.ts index 8cce6f8..ff71bf5 100644 --- a/src/socketEvents/login.ts +++ b/src/socketEvents/login.ts @@ -1,9 +1,28 @@ import { Server } from 'socket.io' import { TSocket } from '../utilities/types' +import { gameLogger } from '../utilities/logger' -export default function (io: Server, socket: TSocket) { - socket.on('login', () => { - if (!socket.user) return - socket.emit('logged_in', { user: socket.user }) - }) -} +export default class LoginEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + + public listen(): void { + this.socket.on('login', this.handleLogin.bind(this)) + } + + private handleLogin(): void { + try { + if (!this.socket.user) { + gameLogger.warn('Login attempt without user data') + return + } + + this.socket.emit('logged_in', { user: this.socket.user }) + gameLogger.info(`User logged in: ${this.socket.user.id}`) + } catch (error: any) { + gameLogger.error('login error', error.message) + } + } +} \ No newline at end of file diff --git a/src/socketEvents/zone/characterJoin.ts b/src/socketEvents/zone/characterJoin.ts index b3ce383..4de7ae5 100644 --- a/src/socketEvents/zone/characterJoin.ts +++ b/src/socketEvents/zone/characterJoin.ts @@ -5,55 +5,49 @@ import { Character, Zone } from '@prisma/client' import CharacterManager from '../../managers/characterManager' import { gameLogger } from '../../utilities/logger' -interface IPayload { - // zoneId: number -} - interface IResponse { zone: Zone characters: Character[] } -/** - * Handle character zone request event - * @param socket - * @param io - */ -export default function (io: Server, socket: TSocket) { - socket.on('zone:character:join', async (callback: (response: IResponse) => void) => { - try { - if (!socket.characterId) return - const character = CharacterManager.getCharacterFromSocket(socket) +export default class CharacterJoinEvent { + constructor( + private readonly io: Server, + private readonly socket: TSocket + ) {} + public listen(): void { + this.socket.on('zone:character:join', this.handleCharacterJoin.bind(this)) + } + + private async handleCharacterJoin(callback: (response: IResponse) => void): Promise { + try { + if (!this.socket.characterId) return + + const character = CharacterManager.getCharacterFromSocket(this.socket) if (!character) return const zone = await ZoneRepository.getById(character.zoneId) - if (!zone) { - console.log(`---Zone not found.`) + gameLogger.error('zone:character:join error', 'Zone not found') return } - if (character?.zoneId) { - socket.leave(character.zoneId.toString()) - io.to(character.zoneId.toString()).emit('zone:character:leave', character) + if (character.zoneId) { + this.socket.leave(character.zoneId.toString()) + this.io.to(character.zoneId.toString()).emit('zone:character:leave', character) } - socket.join(zone.id.toString()) + this.socket.join(zone.id.toString()) // let other clients know of new character - io.to(zone.id.toString()).emit('zone:character:join', character) - - // add character to zone manager - // ZoneManager.addCharacterToZone(zone.id, socket.character as Character) - // CharacterManager.initCharacter(character as ExtendedCharacter) - // ZoneManager.addCharacterToZone(zone.id, socket.character as Character) + this.io.to(zone.id.toString()).emit('zone:character:join', character) // send over zone and characters to socket callback({ zone, characters: CharacterManager.getCharactersInZone(zone) }) } catch (error: any) { - gameLogger.error(`Error requesting zone: ${error.message}`) - socket.disconnect() + gameLogger.error('zone:character:join error', error.message) + this.socket.disconnect() } - }) -} + } +} \ No newline at end of file