Updated several events to new event format

This commit is contained in:
Dennis Postma 2024-09-22 00:46:17 +02:00
parent 50d13af5d6
commit 81428ea0c2
9 changed files with 298 additions and 203 deletions

View File

@ -2,27 +2,47 @@ import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../utilities/types'
import { getArgs, isCommand } from '../../../utilities/chat' import { getArgs, isCommand } from '../../../utilities/chat'
import CharacterRepository from '../../../repositories/characterRepository' import CharacterRepository from '../../../repositories/characterRepository'
import { gameLogger } from '../../../utilities/logger'
type TypePayload = { type TypePayload = {
message: string message: string
} }
export default function (io: Server, socket: TSocket) { export default class AlertCommandEvent {
socket.on('chat:send_message', async (data: TypePayload, callback: (response: boolean) => void) => { 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<void> {
try { try {
if (!isCommand(data.message, 'alert')) return if (!isCommand(data.message, 'alert')) {
return
}
const args = getArgs('alert', data.message) 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) const character = await CharacterRepository.getByUserAndId(this.socket.user?.id as number, this.socket.characterId as number)
if (!character) return 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) callback(true)
} catch (error: any) { } catch (error: any) {
console.log(`---Error sending message: ${error.message}`) gameLogger.error('chat:alert_command error', error.message)
callback(false)
} }
}) }
} }

View File

@ -1,28 +1,42 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../utilities/types' import { TSocket } from '../utilities/types'
import CharacterManager from '../managers/characterManager' import CharacterManager from '../managers/characterManager'
import { gameLogger } from '../utilities/logger'
export default function (io: Server, socket: TSocket) { export default class DisconnectEvent {
socket.on('disconnect', async (data: any) => { constructor(
if (!socket.user) { private readonly io: Server,
console.log('User disconnected but had no user set') private readonly socket: TSocket
return ) {}
public listen(): void {
this.socket.on('disconnect', this.handleDisconnect.bind(this))
}
private async handleDisconnect(data: any): Promise<void> {
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)
})
} }

View File

@ -3,7 +3,7 @@ import { TSocket } from '../../../utilities/types'
import ZoneRepository from '../../../repositories/zoneRepository' import ZoneRepository from '../../../repositories/zoneRepository'
import { Zone } from '@prisma/client' import { Zone } from '@prisma/client'
import prisma from '../../../utilities/prisma' import prisma from '../../../utilities/prisma'
import characterRepository from '../../../repositories/characterRepository' import CharacterRepository from '../../../repositories/characterRepository'
import { gameMasterLogger } from '../../../utilities/logger' import { gameMasterLogger } from '../../../utilities/logger'
type Payload = { type Payload = {
@ -12,25 +12,33 @@ type Payload = {
height: number height: number
} }
/** export default class ZoneCreateEvent {
* Handle game master zone create event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
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
if (character.role !== 'gm') { public listen(): void {
gameMasterLogger.info(`User ${character.id} tried to create zone but is not a game master.`) this.socket.on('gm:zone_editor:zone:create', this.handleZoneCreate.bind(this))
return }
}
gameMasterLogger.info(`User ${character.id} has created a new zone via zone editor.`) private async handleZoneCreate(data: Payload, callback: (response: Zone[]) => void): Promise<void> {
let zoneList: Zone[] = []
try { 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({ const zone = await prisma.zone.create({
data: { data: {
name: data.name, name: data.name,
@ -40,13 +48,15 @@ export default function (io: Server, socket: TSocket) {
} }
}) })
zoneList = await ZoneRepository.getAll() const 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.' })
callback(zoneList) 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([])
} }
}) }
} }

View File

@ -2,35 +2,44 @@ import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../utilities/types'
import ZoneRepository from '../../../repositories/zoneRepository' import ZoneRepository from '../../../repositories/zoneRepository'
import prisma from '../../../utilities/prisma' import prisma from '../../../utilities/prisma'
import characterRepository from '../../../repositories/characterRepository' import CharacterRepository from '../../../repositories/characterRepository'
import { gameMasterLogger } from '../../../utilities/logger' import { gameMasterLogger } from '../../../utilities/logger'
type Payload = { type Payload = {
zoneId: number zoneId: number
} }
/** export default class ZoneDeleteEvent {
* Handle game master zone delete event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
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
if (character.role !== 'gm') { public listen(): void {
gameMasterLogger.info(`User ${character.id} tried to delete zone but is not a game master.`) this.socket.on('gm:zone_editor:zone:delete', this.handleZoneDelete.bind(this))
return }
}
gameMasterLogger.info(`User ${character.id} has deleted a zone via zone editor.`)
private async handleZoneDelete(data: Payload, callback: (response: boolean) => void): Promise<void> {
try { 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) { if (!zone) {
console.log(`---Zone not found.`) gameMasterLogger.error('gm:zone_editor:zone:delete error', 'Zone not found')
callback(false)
return return
} }
@ -41,9 +50,12 @@ export default function (io: Server, socket: TSocket) {
}) })
callback(true) 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) callback(false)
} }
}) }
} }

View File

@ -2,34 +2,43 @@ import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../utilities/types'
import { Zone } from '@prisma/client' import { Zone } from '@prisma/client'
import ZoneRepository from '../../../repositories/zoneRepository' import ZoneRepository from '../../../repositories/zoneRepository'
import characterRepository from '../../../repositories/characterRepository' import CharacterRepository from '../../../repositories/characterRepository'
import { gameMasterLogger } from '../../../utilities/logger' import { gameMasterLogger } from '../../../utilities/logger'
interface IPayload {} interface IPayload {}
/** export default class ZoneListEvent {
* Handle game master list zones event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
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
if (character.role !== 'gm') { public listen(): void {
gameMasterLogger.info(`User ${character.id} tried to list zones but is not a game master.`) this.socket.on('gm:zone_editor:zone:list', this.handleZoneList.bind(this))
return }
}
gameMasterLogger.info(`User ${character.id} has requested zone list via zone editor.`)
private async handleZoneList(data: IPayload, callback: (response: Zone[]) => void): Promise<void> {
try { 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() const zones = await ZoneRepository.getAll()
callback(zones) callback(zones)
} catch (e) { } catch (error: any) {
console.error(e) gameMasterLogger.error('gm:zone_editor:zone:list error', error.message)
callback([]) callback([])
} }
}) }
} }

View File

@ -2,46 +2,58 @@ import { Server } from 'socket.io'
import { TSocket } from '../../../utilities/types' import { TSocket } from '../../../utilities/types'
import ZoneRepository from '../../../repositories/zoneRepository' import ZoneRepository from '../../../repositories/zoneRepository'
import { Zone } from '@prisma/client' import { Zone } from '@prisma/client'
import characterRepository from '../../../repositories/characterRepository' import CharacterRepository from '../../../repositories/characterRepository'
import { gameMasterLogger } from '../../../utilities/logger' import { gameMasterLogger } from '../../../utilities/logger'
interface IPayload { interface IPayload {
zoneId: number zoneId: number
} }
/** export default class ZoneRequestEvent {
* Handle game master zone request event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
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
if (character.role !== 'gm') { public listen(): void {
gameMasterLogger.info(`User ${character!.id} tried to request zone but is not a game master.`) this.socket.on('gm:zone_editor:zone:request', this.handleZoneRequest.bind(this))
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
}
private async handleZoneRequest(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
try { 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) const zone = await ZoneRepository.getById(data.zoneId)
if (!zone) { if (!zone) {
gameMasterLogger.info(`User ${character.id} tried to request zone ${data.zoneId} but it does not exist.`) gameMasterLogger.info(`User ${character.id} tried to request zone ${data.zoneId} but it does not exist.`)
callback(null)
return return
} }
callback(zone) callback(zone)
} catch (e) { } catch (error: any) {
console.error(e) gameMasterLogger.error('gm:zone_editor:zone:request error', error.message)
callback(null)
} }
}) }
} }

View File

@ -4,7 +4,7 @@ import ZoneRepository from '../../../repositories/zoneRepository'
import { Zone, ZoneEventTileType, ZoneObject } from '@prisma/client' import { Zone, ZoneEventTileType, ZoneObject } from '@prisma/client'
import prisma from '../../../utilities/prisma' import prisma from '../../../utilities/prisma'
import zoneManager from '../../../managers/zoneManager' import zoneManager from '../../../managers/zoneManager'
import characterRepository from '../../../repositories/characterRepository' import CharacterRepository from '../../../repositories/characterRepository'
import { gameMasterLogger } from '../../../utilities/logger' import { gameMasterLogger } from '../../../utilities/logger'
interface IPayload { interface IPayload {
@ -27,40 +27,49 @@ interface IPayload {
zoneObjects: ZoneObject[] zoneObjects: ZoneObject[]
} }
/** export default class ZoneUpdateEvent {
* Handle game master zone 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:zone_editor:zone:update', async (data: IPayload, callback: (response: Zone) => void) => {
const character = await characterRepository.getById(socket.characterId as number)
if (!character) return
if (character.role !== 'gm') { public listen(): void {
gameMasterLogger.info(`User ${character.id} tried to update zone but is not a game master.`) this.socket.on('gm:zone_editor:zone:update', this.handleZoneUpdate.bind(this))
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
}
private async handleZoneUpdate(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
try { 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) let zone = await ZoneRepository.getById(data.zoneId)
if (!zone) { 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.`)
callback(null)
return return
} }
await prisma.zone.update({ await prisma.zone.update({
where: { where: { id: data.zoneId },
id: data.zoneId
},
data: { data: {
name: data.name, name: data.name,
width: data.width, width: data.width,
@ -68,32 +77,26 @@ export default function (io: Server, socket: TSocket) {
tiles: data.tiles, tiles: data.tiles,
pvp: data.pvp, pvp: data.pvp,
zoneEventTiles: { zoneEventTiles: {
deleteMany: { deleteMany: { zoneId: data.zoneId },
zoneId: data.zoneId // Ensure only event tiles related to the zone are deleted
},
// Save new zone event tiles
create: data.zoneEventTiles.map((zoneEventTile) => ({ create: data.zoneEventTiles.map((zoneEventTile) => ({
type: zoneEventTile.type, type: zoneEventTile.type,
positionX: zoneEventTile.positionX, positionX: zoneEventTile.positionX,
positionY: zoneEventTile.positionY, positionY: zoneEventTile.positionY,
...(zoneEventTile.type === 'TELEPORT' && zoneEventTile.teleport ...(zoneEventTile.type === 'TELEPORT' && zoneEventTile.teleport
? { ? {
teleport: { teleport: {
create: { create: {
toZoneId: zoneEventTile.teleport.toZoneId, toZoneId: zoneEventTile.teleport.toZoneId,
toPositionX: zoneEventTile.teleport.toPositionX, toPositionX: zoneEventTile.teleport.toPositionX,
toPositionY: zoneEventTile.teleport.toPositionY toPositionY: zoneEventTile.teleport.toPositionY
}
} }
} }
}
: {}) : {})
})) }))
}, },
zoneObjects: { zoneObjects: {
deleteMany: { deleteMany: { zoneId: data.zoneId },
zoneId: data.zoneId // Ensure only objects related to the zone are deleted
},
// Save new zone objects
create: data.zoneObjects.map((zoneObject) => ({ create: data.zoneObjects.map((zoneObject) => ({
objectId: zoneObject.objectId, objectId: zoneObject.objectId,
depth: zoneObject.depth, depth: zoneObject.depth,
@ -107,7 +110,8 @@ export default function (io: Server, socket: TSocket) {
zone = await ZoneRepository.getById(data.zoneId) zone = await ZoneRepository.getById(data.zoneId)
if (!zone) { 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 return
} }
@ -116,7 +120,8 @@ export default function (io: Server, socket: TSocket) {
zoneManager.unloadZone(data.zoneId) zoneManager.unloadZone(data.zoneId)
await zoneManager.loadZone(zone) await zoneManager.loadZone(zone)
} catch (error: any) { } catch (error: any) {
gameMasterLogger.error(`Error updating zone: ${error.message}`) gameMasterLogger.error('gm:zone_editor:zone:update error', error.message)
callback(null)
} }
}) }
} }

View File

@ -1,9 +1,28 @@
import { Server } from 'socket.io' import { Server } from 'socket.io'
import { TSocket } from '../utilities/types' import { TSocket } from '../utilities/types'
import { gameLogger } from '../utilities/logger'
export default function (io: Server, socket: TSocket) { export default class LoginEvent {
socket.on('login', () => { constructor(
if (!socket.user) return private readonly io: Server,
socket.emit('logged_in', { user: socket.user }) 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)
}
}
} }

View File

@ -5,55 +5,49 @@ import { Character, Zone } from '@prisma/client'
import CharacterManager from '../../managers/characterManager' import CharacterManager from '../../managers/characterManager'
import { gameLogger } from '../../utilities/logger' import { gameLogger } from '../../utilities/logger'
interface IPayload {
// zoneId: number
}
interface IResponse { interface IResponse {
zone: Zone zone: Zone
characters: Character[] characters: Character[]
} }
/** export default class CharacterJoinEvent {
* Handle character zone request event constructor(
* @param socket private readonly io: Server,
* @param io private readonly socket: TSocket
*/ ) {}
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)
public listen(): void {
this.socket.on('zone:character:join', this.handleCharacterJoin.bind(this))
}
private async handleCharacterJoin(callback: (response: IResponse) => void): Promise<void> {
try {
if (!this.socket.characterId) return
const character = CharacterManager.getCharacterFromSocket(this.socket)
if (!character) return if (!character) return
const zone = await ZoneRepository.getById(character.zoneId) const zone = await ZoneRepository.getById(character.zoneId)
if (!zone) { if (!zone) {
console.log(`---Zone not found.`) gameLogger.error('zone:character:join error', 'Zone not found')
return return
} }
if (character?.zoneId) { if (character.zoneId) {
socket.leave(character.zoneId.toString()) this.socket.leave(character.zoneId.toString())
io.to(character.zoneId.toString()).emit('zone:character:leave', character) 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 // let other clients know of new character
io.to(zone.id.toString()).emit('zone:character:join', character) this.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)
// send over zone and characters to socket // send over zone and characters to socket
callback({ zone, characters: CharacterManager.getCharactersInZone(zone) }) callback({ zone, characters: CharacterManager.getCharactersInZone(zone) })
} catch (error: any) { } catch (error: any) {
gameLogger.error(`Error requesting zone: ${error.message}`) gameLogger.error('zone:character:join error', error.message)
socket.disconnect() this.socket.disconnect()
} }
}) }
} }