1
0
forked from noxious/server

Renamed zone > map

This commit is contained in:
2025-01-02 17:31:24 +01:00
parent 887da447e0
commit 11041fec83
54 changed files with 871 additions and 895 deletions

View File

@ -1,32 +1,18 @@
import { Object } from '@prisma/client'
import { Server } from 'socket.io'
import { TSocket } from '#application/types'
import characterRepository from '#repositories/characterRepository'
import ObjectRepository from '#repositories/objectRepository'
import { BaseEvent } from '#application/base/baseEvent'
interface IPayload {}
export default class ObjectListEvent {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
export default class ObjectListEvent extends BaseEvent{
public listen(): void {
this.socket.on('gm:object:list', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: Object[]) => void): Promise<void> {
const character = await characterRepository.getById(this.socket.characterId as number)
if (!character) return callback([])
if (character.role !== 'gm') {
return callback([])
}
if (!(await this.isCharacterGM())) return
// get all objects
const objects = await ObjectRepository.getAll()
callback(objects)
return callback(objects)
}
}

View File

@ -1,16 +1,10 @@
import fs from 'fs/promises'
import { Server } from 'socket.io'
import { BaseEvent } from '#application/base/baseEvent'
import Storage from '#application/storage'
import { Sprite } from '#entities/sprite'
export default class SpriteCreateEvent extends BaseEvent {
constructor(
private readonly io: Server,
private readonly socket: TSocket
) {}
public listen(): void {
this.socket.on('gm:sprite:create', this.handleEvent.bind(this))
}
@ -24,21 +18,19 @@ export default class SpriteCreateEvent extends BaseEvent {
// Ensure the folder exists
await fs.mkdir(public_folder, { recursive: true })
const sprite = await prisma.sprite.create({
data: {
name: 'New sprite'
}
})
const sprite = new Sprite()
await sprite.setName('New sprite').save()
const uuid = sprite.id
// Create folder with uuid
const sprite_folder = Storage.getPublicPath('sprites', uuid)
await fs.mkdir(sprite_folder, { recursive: true })
callback(true)
return callback(true)
} catch (error) {
console.error('Error creating sprite:', error)
callback(false)
return callback(false)
}
}
}

View File

@ -1,6 +1,6 @@
import { BaseEvent } from '#application/base/baseEvent'
import { Zone } from '#entities/zone'
import ZoneRepository from '#repositories/zoneRepository'
import { Map } from '#entities/map'
import MapRepository from '#repositories/mapRepository'
type Payload = {
name: string
@ -8,30 +8,30 @@ type Payload = {
height: number
}
export default class ZoneCreateEvent extends BaseEvent {
export default class MapCreateEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:zone_editor:zone:create', this.handleEvent.bind(this))
this.socket.on('gm:map_editor:map:create', this.handleEvent.bind(this))
}
private async handleEvent(data: Payload, callback: (response: Zone[]) => void): Promise<void> {
private async handleEvent(data: Payload, callback: (response: Map[]) => void): Promise<void> {
try {
if (!(await this.isCharacterGM())) return
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new zone via zone editor.`)
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new map via map editor.`)
const zone = new Zone()
await zone
const map = new Map()
await map
.setName(data.name)
.setWidth(data.width)
.setHeight(data.height)
.setTiles(Array.from({ length: data.height }, () => Array.from({ length: data.width }, () => 'blank_tile')))
.save()
const zoneList = await ZoneRepository.getAll()
return callback(zoneList)
const mapList = await MapRepository.getAll()
return callback(mapList)
} catch (error: any) {
this.logger.error('gm:zone_editor:zone:create error', error.message)
this.socket.emit('notification', { message: 'Failed to create zone.' })
this.logger.error('gm:map_editor:map:create error', error.message)
this.socket.emit('notification', { message: 'Failed to create map.' })
return callback([])
}
}

View File

@ -0,0 +1,29 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import MapRepository from '#repositories/mapRepository'
type Payload = {
mapId: UUID
}
export default class MapDeleteEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:map_editor:map:delete', this.handleEvent.bind(this))
}
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
if (!(await this.isCharacterGM())) return
try {
this.logger.info(`Deleting map ${data.mapId}`)
await (await MapRepository.getById(data.mapId))?.delete()
this.logger.info(`Map ${data.mapId} deleted successfully.`)
return callback(true)
} catch (error: unknown) {
this.logger.error('gm:map_editor:map:delete error', error)
return callback(false)
}
}
}

View File

@ -0,0 +1,25 @@
import { BaseEvent } from '#application/base/baseEvent'
import { Map } from '#entities/map'
import MapRepository from '#repositories/mapRepository'
interface IPayload {}
export default class MapListEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:map_editor:map:list', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: Map[]) => void): Promise<void> {
try {
if (!(await this.isCharacterGM())) return
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new map via map editor.`)
const maps = await MapRepository.getAll()
return callback(maps)
} catch (error: any) {
this.logger.error('gm:map_editor:map:list error', error.message)
return callback([])
}
}
}

View File

@ -0,0 +1,39 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import { Map } from '#entities/map'
import MapRepository from '#repositories/mapRepository'
interface IPayload {
mapId: UUID
}
export default class MapRequestEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:map_editor:map:request', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
try {
if (!(await this.isCharacterGM())) return
this.logger.info(`User ${(await this.getCharacter())!.getId()} has requested map via map editor.`)
if (!data.mapId) {
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map but did not provide a map id.`)
return callback(null)
}
const map = await MapRepository.getById(data.mapId)
if (!map) {
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map ${data.mapId} but it does not exist.`)
return callback(null)
}
return callback(map)
} catch (error: any) {
this.logger.error('gm:map_editor:map:request error', error.message)
return callback(null)
}
}
}

View File

@ -0,0 +1,132 @@
import { BaseEvent } from '#application/base/baseEvent'
import { MapEventTileType } from '#application/enums'
import { UUID } from '#application/types'
import { Map } from '#entities/map'
import { MapEffect } from '#entities/mapEffect'
import { MapEventTile } from '#entities/mapEventTile'
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
import { MapObject } from '#entities/mapObject'
import mapManager from '#managers/mapManager'
import MapRepository from '#repositories/mapRepository'
interface IPayload {
mapId: UUID
name: string
width: number
height: number
tiles: string[][]
pvp: boolean
mapEventTiles: {
type: MapEventTileType
positionX: number
positionY: number
teleport?: {
toMapId: UUID
toPositionX: number
toPositionY: number
toRotation: number
}
}[]
mapEffects: {
effect: string
strength: number
}[]
mapObjects: MapObject[]
}
export default class MapUpdateEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:map_editor:map:update', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
try {
if (!(await this.isCharacterGM())) return
const character = await this.getCharacter()
this.logger.info(`User ${character!.getId()} has updated map via map editor.`)
if (!data.mapId) {
this.logger.info(`User ${character!.getId()} tried to update map but did not provide a map id.`)
return callback(null)
}
let map = await MapRepository.getById(data.mapId)
if (!map) {
this.logger.info(`User ${character!.getId()} tried to update map ${data.mapId} but it does not exist.`)
return callback(null)
}
// Validation logic remains the same
if (data.tiles.length > data.height) {
data.tiles = data.tiles.slice(0, data.height)
}
for (let i = 0; i < data.tiles.length; i++) {
if (data.tiles[i].length > data.width) {
data.tiles[i] = data.tiles[i].slice(0, data.width)
}
}
data.mapEventTiles = data.mapEventTiles.filter((tile) => tile.positionX >= 0 && tile.positionX < data.width && tile.positionY >= 0 && tile.positionY < data.height)
data.mapObjects = data.mapObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height)
// Clear existing collections
map.mapEventTiles.removeAll()
map.mapObjects.removeAll()
map.mapEffects.removeAll()
// Create and add new map event tiles
for (const tile of data.mapEventTiles) {
const mapEventTile = new MapEventTile().setType(tile.type).setPositionX(tile.positionX).setPositionY(tile.positionY).setMap(map)
if (tile.teleport) {
const teleport = new MapEventTileTeleport()
.setToMap((await MapRepository.getById(tile.teleport.toMapId))!)
.setToPositionX(tile.teleport.toPositionX)
.setToPositionY(tile.teleport.toPositionY)
.setToRotation(tile.teleport.toRotation)
mapEventTile.setTeleport(teleport)
}
map.mapEventTiles.add(mapEventTile)
}
// Create and add new map objects
for (const object of data.mapObjects) {
const mapObject = new MapObject().setMapObject(object.mapObject).setDepth(object.depth).setIsRotated(object.isRotated).setPositionX(object.positionX).setPositionY(object.positionY).setMap(map)
map.mapObjects.add(mapObject)
}
// Create and add new map effects
for (const effect of data.mapEffects) {
const mapEffect = new MapEffect().setEffect(effect.effect).setStrength(effect.strength).setMap(map)
map.mapEffects.add(mapEffect)
}
// Update map properties
await map.setName(data.name).setWidth(data.width).setHeight(data.height).setTiles(data.tiles).setPvp(data.pvp).setUpdatedAt(new Date()).update()
// Reload map from database to get fresh data
map = await MapRepository.getById(data.mapId)
if (!map) {
this.logger.info(`User ${character!.getId()} tried to update map ${data.mapId} but it does not exist after update.`)
return callback(null)
}
// Reload map for players
mapManager.unloadMap(data.mapId)
await mapManager.loadMap(map)
return callback(map)
} catch (error: any) {
this.logger.error(`gm:map_editor:map:update error: ${error instanceof Error ? error.message : String(error)}`)
return callback(null)
}
}
}

View File

@ -1,29 +0,0 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import ZoneRepository from '#repositories/zoneRepository'
type Payload = {
zoneId: UUID
}
export default class ZoneDeleteEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:zone_editor:zone:delete', this.handleEvent.bind(this))
}
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
if (!(await this.isCharacterGM())) return
try {
this.logger.info(`Deleting zone ${data.zoneId}`)
await (await ZoneRepository.getById(data.zoneId))?.delete()
this.logger.info(`Zone ${data.zoneId} deleted successfully.`)
return callback(true)
} catch (error: unknown) {
this.logger.error('gm:zone_editor:zone:delete error', error)
return callback(false)
}
}
}

View File

@ -1,25 +0,0 @@
import { BaseEvent } from '#application/base/baseEvent'
import { Zone } from '#entities/zone'
import ZoneRepository from '#repositories/zoneRepository'
interface IPayload {}
export default class ZoneListEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:zone_editor:zone:list', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: Zone[]) => void): Promise<void> {
try {
if (!(await this.isCharacterGM())) return
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new zone via zone editor.`)
const zones = await ZoneRepository.getAll()
return callback(zones)
} catch (error: any) {
this.logger.error('gm:zone_editor:zone:list error', error.message)
return callback([])
}
}
}

View File

@ -1,39 +0,0 @@
import { BaseEvent } from '#application/base/baseEvent'
import { UUID } from '#application/types'
import { Zone } from '#entities/zone'
import ZoneRepository from '#repositories/zoneRepository'
interface IPayload {
zoneId: UUID
}
export default class ZoneRequestEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:zone_editor:zone:request', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
try {
if (!(await this.isCharacterGM())) return
this.logger.info(`User ${(await this.getCharacter())!.getId()} has requested zone via zone editor.`)
if (!data.zoneId) {
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request zone but did not provide a zone id.`)
return callback(null)
}
const zone = await ZoneRepository.getById(data.zoneId)
if (!zone) {
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request zone ${data.zoneId} but it does not exist.`)
return callback(null)
}
return callback(zone)
} catch (error: any) {
this.logger.error('gm:zone_editor:zone:request error', error.message)
return callback(null)
}
}
}

View File

@ -1,132 +0,0 @@
import { BaseEvent } from '#application/base/baseEvent'
import { ZoneEventTileType } from '#application/enums'
import { UUID } from '#application/types'
import { Zone } from '#entities/zone'
import { ZoneEffect } from '#entities/zoneEffect'
import { ZoneEventTile } from '#entities/zoneEventTile'
import { ZoneEventTileTeleport } from '#entities/zoneEventTileTeleport'
import { ZoneObject } from '#entities/zoneObject'
import zoneManager from '#managers/zoneManager'
import ZoneRepository from '#repositories/zoneRepository'
interface IPayload {
zoneId: UUID
name: string
width: number
height: number
tiles: string[][]
pvp: boolean
zoneEventTiles: {
type: ZoneEventTileType
positionX: number
positionY: number
teleport?: {
toZoneId: UUID
toPositionX: number
toPositionY: number
toRotation: number
}
}[]
zoneEffects: {
effect: string
strength: number
}[]
zoneObjects: ZoneObject[]
}
export default class ZoneUpdateEvent extends BaseEvent {
public listen(): void {
this.socket.on('gm:zone_editor:zone:update', this.handleEvent.bind(this))
}
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
try {
if (!(await this.isCharacterGM())) return
const character = await this.getCharacter()
this.logger.info(`User ${character!.getId()} has updated zone via zone editor.`)
if (!data.zoneId) {
this.logger.info(`User ${character!.getId()} tried to update zone but did not provide a zone id.`)
return callback(null)
}
let zone = await ZoneRepository.getById(data.zoneId)
if (!zone) {
this.logger.info(`User ${character!.getId()} tried to update zone ${data.zoneId} but it does not exist.`)
return callback(null)
}
// Validation logic remains the same
if (data.tiles.length > data.height) {
data.tiles = data.tiles.slice(0, data.height)
}
for (let i = 0; i < data.tiles.length; i++) {
if (data.tiles[i].length > data.width) {
data.tiles[i] = data.tiles[i].slice(0, data.width)
}
}
data.zoneEventTiles = data.zoneEventTiles.filter((tile) => tile.positionX >= 0 && tile.positionX < data.width && tile.positionY >= 0 && tile.positionY < data.height)
data.zoneObjects = data.zoneObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height)
// Clear existing collections
zone.zoneEventTiles.removeAll()
zone.zoneObjects.removeAll()
zone.zoneEffects.removeAll()
// Create and add new zone event tiles
for (const tile of data.zoneEventTiles) {
const zoneEventTile = new ZoneEventTile().setType(tile.type).setPositionX(tile.positionX).setPositionY(tile.positionY).setZone(zone)
if (tile.teleport) {
const teleport = new ZoneEventTileTeleport()
.setToZone((await ZoneRepository.getById(tile.teleport.toZoneId))!)
.setToPositionX(tile.teleport.toPositionX)
.setToPositionY(tile.teleport.toPositionY)
.setToRotation(tile.teleport.toRotation)
zoneEventTile.setTeleport(teleport)
}
zone.zoneEventTiles.add(zoneEventTile)
}
// Create and add new zone objects
for (const object of data.zoneObjects) {
const zoneObject = new ZoneObject().setMapObject(object.mapObject).setDepth(object.depth).setIsRotated(object.isRotated).setPositionX(object.positionX).setPositionY(object.positionY).setZone(zone)
zone.zoneObjects.add(zoneObject)
}
// Create and add new zone effects
for (const effect of data.zoneEffects) {
const zoneEffect = new ZoneEffect().setEffect(effect.effect).setStrength(effect.strength).setZone(zone)
zone.zoneEffects.add(zoneEffect)
}
// Update zone properties
await zone.setName(data.name).setWidth(data.width).setHeight(data.height).setTiles(data.tiles).setPvp(data.pvp).setUpdatedAt(new Date()).update()
// Reload zone from database to get fresh data
zone = await ZoneRepository.getById(data.zoneId)
if (!zone) {
this.logger.info(`User ${character!.getId()} tried to update zone ${data.zoneId} but it does not exist after update.`)
return callback(null)
}
// Reload zone for players
zoneManager.unloadZone(data.zoneId)
await zoneManager.loadZone(zone)
return callback(zone)
} catch (error: any) {
this.logger.error(`gm:zone_editor:zone:update error: ${error instanceof Error ? error.message : String(error)}`)
return callback(null)
}
}
}