forked from noxious/server
Joining, leaving rooms and teleporting works again + refactor
This commit is contained in:
parent
30b2028bd8
commit
495e9f192e
@ -3,6 +3,7 @@ import Database from '#application/database'
|
|||||||
import ZoneManager from '#managers/zoneManager'
|
import ZoneManager from '#managers/zoneManager'
|
||||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import TeleportService from '#services/teleportService'
|
||||||
|
|
||||||
interface CharacterConnectPayload {
|
interface CharacterConnectPayload {
|
||||||
characterId: number
|
characterId: number
|
||||||
@ -59,21 +60,14 @@ export default class CharacterConnectEvent extends BaseEvent {
|
|||||||
// wait 300 ms, @TODO: Find a better way to do this
|
// wait 300 ms, @TODO: Find a better way to do this
|
||||||
await new Promise(resolve => setTimeout(resolve, 100))
|
await new Promise(resolve => setTimeout(resolve, 100))
|
||||||
|
|
||||||
const zone = ZoneManager.getZoneById(character.zone!.id)
|
await TeleportService.teleportCharacter(character.id, {
|
||||||
if (!zone) {
|
targetZoneId: character.zone!.id,
|
||||||
this.logger.error('zone:character:join error: Zone not found')
|
targetX: character.positionX,
|
||||||
return
|
targetY: character.positionY,
|
||||||
}
|
rotation: character.rotation,
|
||||||
|
isInitialJoin: true,
|
||||||
zone.addCharacter(character)
|
character
|
||||||
|
})
|
||||||
const zoneCharacter = ZoneManager.getCharacterById(character.id)
|
|
||||||
if (!zoneCharacter) {
|
|
||||||
this.logger.error('zone:character:join error: Zone character not found')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await zoneCharacter.teleport(character.zone!.id, character.positionX, character.positionY)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleError('Failed to connect character', error)
|
this.handleError('Failed to connect character', error)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import ZoneManager from '#managers/zoneManager'
|
import ZoneManager from '#managers/zoneManager'
|
||||||
import zoneManager from '#managers/zoneManager'
|
|
||||||
import ZoneCharacter from '#models/zoneCharacter'
|
|
||||||
import ZoneRepository from '#repositories/zoneRepository'
|
import ZoneRepository from '#repositories/zoneRepository'
|
||||||
import ChatService from '#services/chatService'
|
import ChatService from '#services/chatService'
|
||||||
|
import TeleportService from '#services/teleportService'
|
||||||
|
|
||||||
type TypePayload = {
|
type TypePayload = {
|
||||||
message: string
|
message: string
|
||||||
@ -14,9 +13,8 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
this.socket.on('chat:message', this.handleEvent.bind(this))
|
this.socket.on('chat:message', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: TypePayload, callback: (response: boolean) => void) {
|
||||||
try {
|
try {
|
||||||
// Check if character exists
|
|
||||||
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
|
const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
|
||||||
if (!zoneCharacter) {
|
if (!zoneCharacter) {
|
||||||
this.logger.error('chat:message error', 'Character not found')
|
this.logger.error('chat:message error', 'Character not found')
|
||||||
@ -25,7 +23,6 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
|
|
||||||
const character = zoneCharacter.character
|
const character = zoneCharacter.character
|
||||||
|
|
||||||
// Check if the user is the GM
|
|
||||||
if (character.role !== 'gm') {
|
if (character.role !== 'gm') {
|
||||||
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
|
||||||
return
|
return
|
||||||
@ -35,54 +32,68 @@ export default class TeleportCommandEvent extends BaseEvent {
|
|||||||
|
|
||||||
const args = ChatService.getArgs('teleport', data.message)
|
const args = ChatService.getArgs('teleport', data.message)
|
||||||
|
|
||||||
if (!args || args.length !== 1) {
|
if (!args || args.length === 0 || args.length > 3) {
|
||||||
this.socket.emit('notification', { title: 'Server message', message: 'Usage: /teleport <zoneId>' })
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Usage: /teleport <zoneId> [x] [y]'
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoneId = parseInt(args[0], 10)
|
const zoneId = parseInt(args[0], 10)
|
||||||
if (isNaN(zoneId)) {
|
const targetX = args[1] ? parseInt(args[1], 10) : 0
|
||||||
this.socket.emit('notification', { title: 'Server message', message: 'Invalid zone ID' })
|
const targetY = args[2] ? parseInt(args[2], 10) : 0
|
||||||
|
|
||||||
|
if (isNaN(zoneId) || isNaN(targetX) || isNaN(targetY)) {
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Invalid parameters. All values must be numbers.'
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const zone = await ZoneRepository.getById(zoneId)
|
const zone = await ZoneRepository.getById(zoneId)
|
||||||
if (!zone) {
|
if (!zone) {
|
||||||
this.socket.emit('notification', { title: 'Server message', message: 'Zone not found' })
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Zone not found'
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (character.zoneId === zone.id) {
|
if (character.zone.id === zone.id && targetX === character.positionX && targetY === character.positionY) {
|
||||||
this.socket.emit('notification', { title: 'Server message', message: 'You are already in that zone' })
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'You are already at that location'
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove character from current zone
|
const success = await TeleportService.teleportCharacter(character.id, {
|
||||||
zoneManager.removeCharacter(character.id)
|
targetZoneId: zone.id,
|
||||||
this.io.to(character.zoneId.toString()).emit('zone:character:leave', character.id)
|
targetX,
|
||||||
this.socket.leave(character.zoneId.toString())
|
targetY,
|
||||||
|
rotation: character.rotation
|
||||||
// Add character to new zone
|
|
||||||
zoneManager.getZoneById(zone.id)?.addCharacter(character)
|
|
||||||
this.io.to(zone.id.toString()).emit('zone:character:join', character)
|
|
||||||
this.socket.join(zone.id.toString())
|
|
||||||
|
|
||||||
character.zoneId = zone.id
|
|
||||||
character.positionX = 0
|
|
||||||
character.positionY = 0
|
|
||||||
|
|
||||||
zoneCharacter.isMoving = false
|
|
||||||
|
|
||||||
this.socket.emit('zone:character:teleport', {
|
|
||||||
zone,
|
|
||||||
characters: ZoneManager.getZoneById(zone.id)?.getCharactersInZone()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.socket.emit('notification', { title: 'Server message', message: `You have been teleported to ${zone.name}` })
|
if (!success) {
|
||||||
this.logger.info('teleport', `Character ${character.id} teleported to zone ${zone.id}`)
|
return this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'Failed to teleport'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: `Teleported to ${zone.name} (${targetX}, ${targetY})`
|
||||||
|
})
|
||||||
|
this.logger.info('teleport', `Character ${character.id} teleported to zone ${zone.id} at position (${targetX}, ${targetY})`)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Error in teleport command: ${error.message}`)
|
this.logger.error(`Error in teleport command: ${error.message}`)
|
||||||
this.socket.emit('notification', { title: 'Server message', message: 'An error occurred while teleporting' })
|
this.socket.emit('notification', {
|
||||||
|
title: 'Server message',
|
||||||
|
message: 'An error occurred while teleporting'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export default class DisconnectEvent extends BaseEvent {
|
|||||||
this.socket.on('disconnect', this.handleEvent.bind(this))
|
this.socket.on('disconnect', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: any): Promise<void> {
|
private async handleEvent(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (!this.socket.userId) {
|
if (!this.socket.userId) {
|
||||||
this.logger.info('User disconnected but had no user set')
|
this.logger.info('User disconnected but had no user set')
|
||||||
@ -21,18 +21,8 @@ export default class DisconnectEvent extends BaseEvent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const character = zoneCharacter.character
|
await zoneCharacter.disconnect(this.socket, this.io)
|
||||||
|
|
||||||
// Save character position and remove from zone
|
|
||||||
zoneCharacter.isMoving = false
|
|
||||||
await zoneCharacter.savePosition()
|
|
||||||
ZoneManager.removeCharacter(this.socket.characterId!)
|
|
||||||
|
|
||||||
this.logger.info('User disconnected along with their character')
|
this.logger.info('User disconnected along with their character')
|
||||||
|
|
||||||
// Inform other clients that the character has left
|
|
||||||
this.io.in(character.zone!.id.toString()).emit('zone:character:leave', character.id)
|
|
||||||
this.io.emit('character:disconnect', character.id)
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('disconnect error: ' + error.message)
|
this.logger.error('disconnect error: ' + error.message)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Character } from '#entities/character'
|
import { Character } from '#entities/character'
|
||||||
|
import TeleportService from '#services/teleportService'
|
||||||
import ZoneManager from '#managers/zoneManager'
|
import ZoneManager from '#managers/zoneManager'
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
|
||||||
import SocketManager from '#managers/socketManager'
|
import SocketManager from '#managers/socketManager'
|
||||||
|
|
||||||
class ZoneCharacter {
|
class ZoneCharacter {
|
||||||
@ -13,43 +13,43 @@ class ZoneCharacter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async savePosition() {
|
public async savePosition() {
|
||||||
await this.character.setPositionX(this.character.positionX).setPositionY(this.character.positionY).setRotation(this.character.rotation).setZone(this.character.zone).update()
|
await this.character
|
||||||
|
.setPositionX(this.character.positionX)
|
||||||
|
.setPositionY(this.character.positionY)
|
||||||
|
.setRotation(this.character.rotation)
|
||||||
|
.setZone(this.character.zone)
|
||||||
|
.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
public async teleport(zoneId: number, targetX: number, targetY: number): Promise<void> {
|
public async teleport(zoneId: number, targetX: number, targetY: number): Promise<void> {
|
||||||
const io = SocketManager.getIO()
|
await TeleportService.teleportCharacter(this.character.id, {
|
||||||
const socket = SocketManager.getSocketByCharacterId(this.character.id)
|
targetZoneId: zoneId,
|
||||||
const zone = ZoneManager.getZoneById(zoneId)?.getZone()
|
targetX,
|
||||||
const logger = Logger.type(LoggerType.APP)
|
targetY
|
||||||
console.log('teleporting')
|
|
||||||
|
|
||||||
if (!socket) {
|
|
||||||
logger.error('zone:character:move error: Socket not found')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!zone) {
|
|
||||||
logger.error('zone:character:move error: Zone not found')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let other clients know of new character
|
|
||||||
io.to(zone.id.toString()).emit('zone:character:join', 'ewaewa')
|
|
||||||
|
|
||||||
// Update zoneCharacter properties
|
|
||||||
this.currentPath = null
|
|
||||||
this.isMoving = false
|
|
||||||
|
|
||||||
// Update local character object
|
|
||||||
await this.character.setPositionX(targetX).setPositionY(targetY).setRotation(this.character.rotation).setZone(zone).update()
|
|
||||||
|
|
||||||
// Emit teleport event
|
|
||||||
socket.emit('zone:character:teleport', {
|
|
||||||
zone,
|
|
||||||
characters: ZoneManager.getZoneById(zone.id)?.getCharactersInZone()
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
console.log('teleported')
|
public async disconnect(socket: Socket, io: Server): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Stop any movement and save final position
|
||||||
|
this.isMoving = false
|
||||||
|
this.currentPath = null
|
||||||
|
await this.savePosition()
|
||||||
|
|
||||||
|
// Leave zone and remove from manager
|
||||||
|
if (this.character.zone) {
|
||||||
|
socket.leave(this.character.zone.id.toString())
|
||||||
|
ZoneManager.removeCharacter(this.character.id)
|
||||||
|
|
||||||
|
// Notify zone players
|
||||||
|
io.in(this.character.zone.id.toString()).emit('zone:character:leave', this.character.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all players
|
||||||
|
io.emit('character:disconnect', this.character.id)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error disconnecting character ${this.character.id}:`, error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
83
src/services/teleportService.ts
Normal file
83
src/services/teleportService.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { Character } from '#entities/character'
|
||||||
|
import ZoneManager from '#managers/zoneManager'
|
||||||
|
import SocketManager from '#managers/socketManager'
|
||||||
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
import ZoneCharacter from '#models/zoneCharacter'
|
||||||
|
|
||||||
|
interface TeleportOptions {
|
||||||
|
targetZoneId: number
|
||||||
|
targetX: number
|
||||||
|
targetY: number
|
||||||
|
rotation?: number
|
||||||
|
isInitialJoin?: boolean
|
||||||
|
character?: Character
|
||||||
|
}
|
||||||
|
|
||||||
|
class TeleportService {
|
||||||
|
private readonly logger = Logger.type(LoggerType.GAME)
|
||||||
|
|
||||||
|
public async teleportCharacter(characterId: number, options: TeleportOptions): Promise<boolean> {
|
||||||
|
const { targetZoneId, targetX, targetY, rotation = 0, isInitialJoin = false, character } = options
|
||||||
|
|
||||||
|
const socket = SocketManager.getSocketByCharacterId(characterId)
|
||||||
|
const targetZone = ZoneManager.getZoneById(targetZoneId)
|
||||||
|
|
||||||
|
if (!socket || !targetZone) {
|
||||||
|
this.logger.error(`Teleport failed - Missing socket or target zone for character ${characterId}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInitialJoin && !character) {
|
||||||
|
this.logger.error('Initial join requires character data')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingCharacter = !isInitialJoin && ZoneManager.getCharacterById(characterId)
|
||||||
|
const zoneCharacter = isInitialJoin
|
||||||
|
? new ZoneCharacter(character!)
|
||||||
|
: existingCharacter || (() => {
|
||||||
|
this.logger.error(`Teleport failed - Character ${characterId} not found in ZoneManager`)
|
||||||
|
return null
|
||||||
|
})()
|
||||||
|
|
||||||
|
if (!zoneCharacter) return false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentZoneId = zoneCharacter.character.zone?.id
|
||||||
|
const io = SocketManager.getIO()
|
||||||
|
|
||||||
|
// Handle current zone cleanup
|
||||||
|
if (currentZoneId) {
|
||||||
|
socket.leave(currentZoneId.toString())
|
||||||
|
ZoneManager.removeCharacter(characterId)
|
||||||
|
io.in(currentZoneId.toString()).emit('zone:character:leave', characterId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update character position and zone
|
||||||
|
await zoneCharacter.character
|
||||||
|
.setPositionX(targetX)
|
||||||
|
.setPositionY(targetY)
|
||||||
|
.setRotation(rotation)
|
||||||
|
.setZone(targetZone.getZone())
|
||||||
|
.update()
|
||||||
|
|
||||||
|
// Join new zone
|
||||||
|
socket.join(targetZoneId.toString())
|
||||||
|
targetZone.addCharacter(zoneCharacter.character)
|
||||||
|
|
||||||
|
// Notify clients
|
||||||
|
io.in(targetZoneId.toString()).emit('zone:character:join', zoneCharacter)
|
||||||
|
socket.emit('zone:character:teleport', {
|
||||||
|
zone: targetZone.getZone(),
|
||||||
|
characters: targetZone.getCharactersInZone()
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Teleport error for character ${characterId}: ${error}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TeleportService()
|
Loading…
x
Reference in New Issue
Block a user