forked from noxious/server
More event progress
This commit is contained in:
parent
ab89d0cbb0
commit
f7dbf09bf5
@ -2,6 +2,8 @@ import { Server } from 'socket.io'
|
|||||||
|
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
import { TSocket } from '#application/types'
|
import { TSocket } from '#application/types'
|
||||||
|
import { Character } from '#entities/character'
|
||||||
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
|
||||||
export abstract class BaseEvent {
|
export abstract class BaseEvent {
|
||||||
protected readonly logger = Logger.type(LoggerType.GAME)
|
protected readonly logger = Logger.type(LoggerType.GAME)
|
||||||
@ -11,6 +13,15 @@ export abstract class BaseEvent {
|
|||||||
readonly socket: TSocket
|
readonly socket: TSocket
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
protected async getCharacter(): Promise<Character | null> {
|
||||||
|
return CharacterRepository.getById(this.socket.characterId!)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async isCharacterGM(): Promise<boolean> {
|
||||||
|
const character = await this.getCharacter()
|
||||||
|
return character?.getRole() === 'gm'
|
||||||
|
}
|
||||||
|
|
||||||
protected emitError(message: string): void {
|
protected emitError(message: string): void {
|
||||||
this.socket.emit('notification', { title: 'Server message', message })
|
this.socket.emit('notification', { title: 'Server message', message })
|
||||||
this.logger.error('character:connect error', `Player ${this.socket.userId}: ${message}`)
|
this.logger.error('character:connect error', `Player ${this.socket.userId}: ${message}`)
|
||||||
|
@ -1,79 +1,35 @@
|
|||||||
import { Server } from 'socket.io'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
import type { Prisma } from '@prisma/client'
|
import { Sprite } from '#entities/sprite'
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
|
||||||
interface CopyPayload {
|
interface CopyPayload {
|
||||||
id: string
|
id: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SpriteCopyEvent {
|
export default class SpriteCopyEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:copy', this.handleEvent.bind(this))
|
this.socket.on('gm:sprite:copy', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(payload: CopyPayload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(payload: CopyPayload, callback: (success: boolean) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (!(await this.validateGameMasterAccess())) {
|
if (!(await this.isCharacterGM())) return
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const sourceSprite = await prisma.sprite.findUnique({
|
const sourceSprite = await SpriteRepository.getById(payload.id)
|
||||||
where: { id: payload.id },
|
|
||||||
include: {
|
|
||||||
spriteActions: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!sourceSprite) {
|
if (!sourceSprite) {
|
||||||
throw new Error('Source sprite not found')
|
throw new Error('Source sprite not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSprite = await prisma.sprite.create({
|
const newSprite = new Sprite()
|
||||||
data: {
|
await newSprite.setName(`${sourceSprite.getName()} (Copy)`).setSpriteActions(sourceSprite.getSpriteActions()).save()
|
||||||
name: `${sourceSprite.name} (Copy)`,
|
|
||||||
spriteActions: {
|
|
||||||
create: sourceSprite.spriteActions.map((action) => ({
|
|
||||||
action: action.action,
|
|
||||||
sprites: action.sprites as Prisma.InputJsonValue,
|
|
||||||
originX: action.originX,
|
|
||||||
originY: action.originY,
|
|
||||||
isAnimated: action.isAnimated,
|
|
||||||
isLooping: action.isLooping,
|
|
||||||
frameWidth: action.frameWidth,
|
|
||||||
frameHeight: action.frameHeight,
|
|
||||||
frameRate: action.frameRate
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
callback(true)
|
callback(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleError(error, payload.id, callback)
|
this.logger.error(`Error copying sprite:`, String(error))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async validateGameMasterAccess(): Promise<boolean> {
|
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId!)
|
|
||||||
return character?.role === 'gm'
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleError(error: unknown, spriteId: string, callback: (success: boolean) => void): void {
|
|
||||||
gameMasterLogger.error(`Error copying sprite ${spriteId}: ${this.getErrorMessage(error)}`)
|
|
||||||
callback(false)
|
callback(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getErrorMessage(error: unknown): string {
|
|
||||||
return error instanceof Error ? error.message : String(error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,10 @@ import fs from 'fs/promises'
|
|||||||
|
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import prisma from '#application/prisma'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import Storage from '#application/storage'
|
import Storage from '#application/storage'
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
|
|
||||||
export default class SpriteCreateEvent {
|
export default class SpriteCreateEvent extends BaseEvent {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly io: Server,
|
private readonly io: Server,
|
||||||
private readonly socket: TSocket
|
private readonly socket: TSocket
|
||||||
@ -19,12 +17,7 @@ export default class SpriteCreateEvent {
|
|||||||
|
|
||||||
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: undefined, callback: (response: boolean) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const character = await characterRepository.getById(this.socket.characterId!)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const public_folder = Storage.getPublicPath('sprites')
|
const public_folder = Storage.getPublicPath('sprites')
|
||||||
|
|
||||||
|
@ -1,45 +1,30 @@
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
import { Server } from 'socket.io'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import Storage from '#application/storage'
|
import Storage from '#application/storage'
|
||||||
import { TSocket } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
id: string
|
id: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class GMSpriteDeleteEvent {
|
export default class GMSpriteDeleteEvent extends BaseEvent {
|
||||||
private readonly public_folder: string
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {
|
|
||||||
this.public_folder = Storage.getPublicPath('sprites')
|
|
||||||
}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:delete', this.handleEvent.bind(this))
|
this.socket.on('gm:sprite:delete', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId!)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (character?.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.deleteSpriteFolder(data.id)
|
await this.deleteSpriteFolder(data.id)
|
||||||
await this.deleteSpriteFromDatabase(data.id)
|
await (await SpriteRepository.getById(data.id))?.delete()
|
||||||
|
|
||||||
gameMasterLogger.info(`Sprite ${data.id} deleted.`)
|
this.logger.info(`Sprite ${data.id} deleted.`)
|
||||||
callback(true)
|
callback(true)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
gameMasterLogger.error('gm:sprite:delete error', error.message)
|
this.logger.error('gm:sprite:delete error', error.message)
|
||||||
callback(false)
|
callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,12 +36,4 @@ export default class GMSpriteDeleteEvent {
|
|||||||
await fs.promises.rmdir(finalFilePath, { recursive: true })
|
await fs.promises.rmdir(finalFilePath, { recursive: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deleteSpriteFromDatabase(spriteId: string): Promise<void> {
|
|
||||||
await prisma.sprite.delete({
|
|
||||||
where: {
|
|
||||||
id: spriteId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,17 @@
|
|||||||
import { Sprite } from '@prisma/client'
|
import { Sprite } from '@prisma/client'
|
||||||
import { Server } from 'socket.io'
|
|
||||||
|
|
||||||
import { TSocket } from '#application/types'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
import SpriteRepository from '#repositories/spriteRepository'
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
|
||||||
interface IPayload {}
|
interface IPayload {}
|
||||||
|
|
||||||
export default class SpriteListEvent {
|
export default class SpriteListEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:list', this.handleEvent.bind(this))
|
this.socket.on('gm:sprite:list', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: any, callback: (response: Sprite[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Sprite[]) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId!)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) return callback([])
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback([])
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all sprites
|
// get all sprites
|
||||||
const sprites = await SpriteRepository.getAll()
|
const sprites = await SpriteRepository.getAll()
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
import { writeFile, mkdir } from 'node:fs/promises'
|
import { writeFile, mkdir } from 'node:fs/promises'
|
||||||
|
|
||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import { Server } from 'socket.io'
|
|
||||||
|
|
||||||
import type { Prisma, SpriteAction } from '@prisma/client'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import Storage from '#application/storage'
|
import Storage from '#application/storage'
|
||||||
import { TSocket } from '#application/types'
|
import { SpriteAction } from '#entities/spriteAction'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const ISOMETRIC_CONFIG = {
|
const ISOMETRIC_CONFIG = {
|
||||||
@ -41,7 +37,7 @@ interface SpriteActionInput extends Omit<SpriteAction, 'id' | 'spriteId' | 'fram
|
|||||||
interface UpdatePayload {
|
interface UpdatePayload {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
spriteActions: Prisma.JsonValue
|
spriteActions: SpriteAction[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProcessedSpriteAction extends SpriteActionInput {
|
interface ProcessedSpriteAction extends SpriteActionInput {
|
||||||
@ -62,21 +58,14 @@ interface SpriteAnalysis {
|
|||||||
contentBounds: ContentBounds
|
contentBounds: ContentBounds
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SpriteUpdateEvent {
|
export default class SpriteUpdateEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:sprite:update', this.handleEvent.bind(this))
|
this.socket.on('gm:sprite:update', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(payload: UpdatePayload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(payload: UpdatePayload, callback: (success: boolean) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (!(await this.validateGameMasterAccess())) {
|
if (!(await this.isCharacterGM())) return
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedActions = this.validateSpriteActions(payload.spriteActions)
|
const parsedActions = this.validateSpriteActions(payload.spriteActions)
|
||||||
|
|
||||||
@ -111,11 +100,6 @@ export default class SpriteUpdateEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async validateGameMasterAccess(): Promise<boolean> {
|
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId!)
|
|
||||||
return character?.role === 'gm'
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateSpriteActions(actions: Prisma.JsonValue): SpriteActionInput[] {
|
private validateSpriteActions(actions: Prisma.JsonValue): SpriteActionInput[] {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(JSON.stringify(actions)) as SpriteActionInput[]
|
const parsed = JSON.parse(JSON.stringify(actions)) as SpriteActionInput[]
|
||||||
@ -375,6 +359,8 @@ export default class SpriteUpdateEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await (await SpriteRepository.getById(id))?.setName(name).setSpriteActions(actions).update()
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapActionToDatabase(action: ProcessedSpriteAction) {
|
private mapActionToDatabase(action: ProcessedSpriteAction) {
|
||||||
@ -392,7 +378,7 @@ export default class SpriteUpdateEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleError(error: unknown, spriteId: string, callback: (success: boolean) => void): void {
|
private handleError(error: unknown, spriteId: string, callback: (success: boolean) => void): void {
|
||||||
gameMasterLogger.error(`Error updating sprite ${spriteId}: ${this.getErrorMessage(error)}`)
|
this.logger.error(`Error updating sprite ${spriteId}: ${this.getErrorMessage(error)}`)
|
||||||
callback(false)
|
callback(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,60 +1,36 @@
|
|||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
|
|
||||||
import { Server } from 'socket.io'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import Storage from '#application/storage'
|
import Storage from '#application/storage'
|
||||||
import { TSocket } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
import characterRepository from '#repositories/characterRepository'
|
import TileRepository from '#repositories/tileRepository'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
id: string
|
id: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class GMTileDeleteEvent {
|
export default class GMTileDeleteEvent extends BaseEvent {
|
||||||
private readonly public_folder: string
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {
|
|
||||||
this.public_folder = Storage.getPublicPath('tiles')
|
|
||||||
}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:tile:delete', this.handleEvent.bind(this))
|
this.socket.on('gm:tile:delete', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
gameMasterLogger.info(`Deleting tile ${data.id}`)
|
this.logger.info(`Deleting tile ${data.id}`)
|
||||||
await this.deleteTileFromDatabase(data.id)
|
|
||||||
await this.deleteTileFile(data.id)
|
await this.deleteTileFile(data.id)
|
||||||
|
await (await TileRepository.getById(data.id))?.delete()
|
||||||
|
|
||||||
gameMasterLogger.info(`Tile ${data.id} deleted successfully.`)
|
this.logger.info(`Tile ${data.id} deleted successfully.`)
|
||||||
callback(true)
|
return callback(true)
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
gameMasterLogger.error('gm:tile:delete error', error.message)
|
this.logger.error('gm:tile:delete error', error)
|
||||||
callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async deleteTileFromDatabase(tileId: string): Promise<void> {
|
|
||||||
await prisma.tile.delete({
|
|
||||||
where: {
|
|
||||||
id: tileId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private async deleteTileFile(tileId: string): Promise<void> {
|
private async deleteTileFile(tileId: string): Promise<void> {
|
||||||
const finalFilePath = Storage.getPublicPath('tiles', `${tileId}.png`)
|
const finalFilePath = Storage.getPublicPath('tiles', `${tileId}.png`)
|
||||||
try {
|
try {
|
||||||
@ -63,7 +39,7 @@ export default class GMTileDeleteEvent {
|
|||||||
if (error.code !== 'ENOENT') {
|
if (error.code !== 'ENOENT') {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
gameMasterLogger.warn(`File ${finalFilePath} does not exist.`)
|
this.logger.warn(`File ${finalFilePath} does not exist.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
import TileRepository from '#repositories/tileRepository'
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Tile } from '#entities/tile'
|
import { Tile } from '#entities/tile'
|
||||||
|
import TileRepository from '#repositories/tileRepository'
|
||||||
|
|
||||||
interface IPayload {}
|
interface IPayload {}
|
||||||
|
|
||||||
@ -11,15 +10,10 @@ export default class TileListEven extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Tile[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Tile[]) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId!)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) return
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all tiles
|
// get all tiles
|
||||||
const tiles = await TileRepository.getAll()
|
const tiles = await TileRepository.getAll()
|
||||||
callback(tiles)
|
return callback(tiles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import TileRepository from '#repositories/tileRepository'
|
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
import TileRepository from '#repositories/tileRepository'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
id: UUID
|
id: UUID
|
||||||
@ -9,29 +8,22 @@ type Payload = {
|
|||||||
tags: string[]
|
tags: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TileUpdateEvent extends BaseEvent{
|
export default class TileUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:tile:update', this.handleEvent.bind(this))
|
this.socket.on('gm:tile:update', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId!)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tile = await TileRepository.getById(data.id)
|
const tile = await TileRepository.getById(data.id)
|
||||||
if (!tile) return callback(false)
|
if (!tile) return callback(false)
|
||||||
|
|
||||||
await tile.setName(data.name).setTags(data.tags).update()
|
await tile.setName(data.name).setTags(data.tags).update()
|
||||||
|
return callback(true)
|
||||||
callback(true)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
return callback(false)
|
||||||
callback(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import { writeFile } from 'node:fs/promises'
|
import { writeFile } from 'node:fs/promises'
|
||||||
|
|
||||||
import Storage from '#application/storage'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import Storage from '#application/storage'
|
||||||
import { Tile } from '#entities/tile'
|
import { Tile } from '#entities/tile'
|
||||||
|
|
||||||
interface ITileData {
|
interface ITileData {
|
||||||
@ -17,12 +16,7 @@ export default class TileUploadEvent extends BaseEvent {
|
|||||||
|
|
||||||
private async handleEvent(data: ITileData, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: ITileData, callback: (response: boolean) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const character = await characterRepository.getById(this.socket.characterId!)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const public_folder = Storage.getPublicPath('tiles')
|
const public_folder = Storage.getPublicPath('tiles')
|
||||||
|
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import { Zone } from '@prisma/client'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Server } from 'socket.io'
|
import { Zone } from '#entities/zone'
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
|
||||||
import ZoneRepository from '#repositories/zoneRepository'
|
import ZoneRepository from '#repositories/zoneRepository'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
@ -13,49 +8,29 @@ type Payload = {
|
|||||||
height: number
|
height: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ZoneCreateEvent {
|
export default class ZoneCreateEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:zone_editor:zone:create', this.handleEvent.bind(this))
|
this.socket.on('gm:zone_editor:zone:create', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: Zone[]) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: Zone[]) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId as number)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) {
|
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:create error', 'Character not found')
|
|
||||||
callback([])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new zone via zone editor.`)
|
||||||
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 = new Zone()
|
||||||
|
await zone
|
||||||
const zone = await prisma.zone.create({
|
.setName(data.name)
|
||||||
data: {
|
.setWidth(data.width)
|
||||||
name: data.name,
|
.setHeight(data.height)
|
||||||
width: data.width,
|
.setTiles(Array.from({ length: data.height }, () => Array.from({ length: data.width }, () => 'blank_tile')))
|
||||||
height: data.height,
|
.save()
|
||||||
tiles: Array.from({ length: data.height }, () => Array.from({ length: data.width }, () => 'blank_tile'))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const zoneList = await ZoneRepository.getAll()
|
const zoneList = await ZoneRepository.getAll()
|
||||||
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) {
|
} catch (error: any) {
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:create error', error.message)
|
this.logger.error('gm:zone_editor:zone:create error', error.message)
|
||||||
this.socket.emit('notification', { message: 'Failed to create zone.' })
|
this.socket.emit('notification', { message: 'Failed to create zone.' })
|
||||||
callback([])
|
callback([])
|
||||||
}
|
}
|
||||||
|
@ -1,62 +1,29 @@
|
|||||||
import { Server } from 'socket.io'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
|
||||||
import ZoneRepository from '#repositories/zoneRepository'
|
import ZoneRepository from '#repositories/zoneRepository'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
zoneId: number
|
zoneId: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ZoneDeleteEvent {
|
export default class ZoneDeleteEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:zone_editor:zone:delete', this.handleEvent.bind(this))
|
this.socket.on('gm:zone_editor:zone:delete', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId as number)
|
this.logger.info(`Deleting zone ${data.zoneId}`)
|
||||||
if (!character) {
|
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:delete error', 'Character not found')
|
|
||||||
callback(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
await (await ZoneRepository.getById(data.zoneId))?.delete()
|
||||||
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.`)
|
this.logger.info(`Zone ${data.zoneId} deleted successfully.`)
|
||||||
|
return callback(true)
|
||||||
const zone = await ZoneRepository.getById(data.zoneId)
|
} catch (error: unknown) {
|
||||||
if (!zone) {
|
this.logger.error('gm:zone_editor:zone:delete error', error)
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:delete error', 'Zone not found')
|
return callback(false)
|
||||||
callback(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.zone.delete({
|
|
||||||
where: {
|
|
||||||
id: data.zoneId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
callback(true)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,24 @@
|
|||||||
import { Zone } from '@prisma/client'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Server } from 'socket.io'
|
import { Zone } from '#entities/zone'
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
|
||||||
import ZoneRepository from '#repositories/zoneRepository'
|
import ZoneRepository from '#repositories/zoneRepository'
|
||||||
|
|
||||||
interface IPayload {}
|
interface IPayload {}
|
||||||
|
|
||||||
export default class ZoneListEvent {
|
export default class ZoneListEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:zone_editor:zone:list', this.handleEvent.bind(this))
|
this.socket.on('gm:zone_editor:zone:list', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Zone[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Zone[]) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId as number)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) {
|
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:list error', 'Character not found')
|
|
||||||
callback([])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
this.logger.info(`User ${(await this.getCharacter())!.getId()} has created a new zone via zone editor.`)
|
||||||
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 (error: any) {
|
} catch (error: any) {
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:list error', error.message)
|
this.logger.error('gm:zone_editor:zone:list error', error.message)
|
||||||
callback([])
|
callback([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,39 @@
|
|||||||
import { Zone } from '@prisma/client'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Server } from 'socket.io'
|
import { UUID } from '#application/types'
|
||||||
|
import { Zone } from '#entities/zone'
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
|
||||||
import ZoneRepository from '#repositories/zoneRepository'
|
import ZoneRepository from '#repositories/zoneRepository'
|
||||||
|
|
||||||
interface IPayload {
|
interface IPayload {
|
||||||
zoneId: number
|
zoneId: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ZoneRequestEvent {
|
export default class ZoneRequestEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:zone_editor:zone:request', this.handleEvent.bind(this))
|
this.socket.on('gm:zone_editor:zone:request', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId as number)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) {
|
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:request error', 'Character not found')
|
|
||||||
callback(null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
this.logger.info(`User ${(await this.getCharacter())!.getId()} has requested zone via zone editor.`)
|
||||||
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) {
|
if (!data.zoneId) {
|
||||||
gameMasterLogger.info(`User ${character.id} tried to request zone but did not provide a zone id.`)
|
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request zone but did not provide a zone id.`)
|
||||||
callback(null)
|
return 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.`)
|
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request zone ${data.zoneId} but it does not exist.`)
|
||||||
callback(null)
|
return callback(null)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(zone)
|
return callback(zone)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:request error', error.message)
|
this.logger.error('gm:zone_editor:zone:request error', error.message)
|
||||||
callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import { Zone, ZoneEffect, ZoneEventTileType, ZoneObject } from '@prisma/client'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Server } from 'socket.io'
|
import { ZoneEventTileType } from '#application/enums'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
import { gameMasterLogger } from '#application/logger'
|
import { Zone } from '#entities/zone'
|
||||||
import prisma from '#application/prisma'
|
import { ZoneEffect } from '#entities/zoneEffect'
|
||||||
import { TSocket } from '#application/types'
|
import { ZoneEventTile } from '#entities/zoneEventTile'
|
||||||
|
import { ZoneEventTileTeleport } from '#entities/zoneEventTileTeleport'
|
||||||
|
import { ZoneObject } from '#entities/zoneObject'
|
||||||
import zoneManager from '#managers/zoneManager'
|
import zoneManager from '#managers/zoneManager'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
|
||||||
import ZoneRepository from '#repositories/zoneRepository'
|
import ZoneRepository from '#repositories/zoneRepository'
|
||||||
|
|
||||||
interface IPayload {
|
interface IPayload {
|
||||||
zoneId: number
|
zoneId: UUID
|
||||||
name: string
|
name: string
|
||||||
width: number
|
width: number
|
||||||
height: number
|
height: number
|
||||||
@ -20,7 +21,7 @@ interface IPayload {
|
|||||||
positionX: number
|
positionX: number
|
||||||
positionY: number
|
positionY: number
|
||||||
teleport?: {
|
teleport?: {
|
||||||
toZoneId: number
|
toZoneId: UUID
|
||||||
toPositionX: number
|
toPositionX: number
|
||||||
toPositionY: number
|
toPositionY: number
|
||||||
toRotation: number
|
toRotation: number
|
||||||
@ -33,44 +34,31 @@ interface IPayload {
|
|||||||
zoneObjects: ZoneObject[]
|
zoneObjects: ZoneObject[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ZoneUpdateEvent {
|
export default class ZoneUpdateEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:zone_editor:zone:update', this.handleEvent.bind(this))
|
this.socket.on('gm:zone_editor:zone:update', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Zone | null) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const character = await CharacterRepository.getById(this.socket.characterId as number)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) {
|
|
||||||
gameMasterLogger.error('gm:zone_editor:zone:update error', 'Character not found')
|
|
||||||
return callback(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
const character = await this.getCharacter()
|
||||||
gameMasterLogger.info(`User ${character.id} tried to update zone but is not a game master.`)
|
this.logger.info(`User ${character!.getId()} has updated zone via zone editor.`)
|
||||||
return callback(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
gameMasterLogger.info(`User ${character.id} has updated zone via zone editor.`)
|
|
||||||
|
|
||||||
if (!data.zoneId) {
|
if (!data.zoneId) {
|
||||||
gameMasterLogger.info(`User ${character.id} tried to update zone but did not provide a zone id.`)
|
this.logger.info(`User ${character!.getId()} tried to update zone but did not provide a zone id.`)
|
||||||
return callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
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.`)
|
this.logger.info(`User ${character!.getId()} tried to update zone ${data.zoneId} but it does not exist.`)
|
||||||
return callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If tiles are larger than the zone, remove the extra tiles
|
// Validation logic remains the same
|
||||||
if (data.tiles.length > data.height) {
|
if (data.tiles.length > data.height) {
|
||||||
data.tiles = data.tiles.slice(0, data.height)
|
data.tiles = data.tiles.slice(0, data.height)
|
||||||
}
|
}
|
||||||
@ -80,81 +68,65 @@ export default class ZoneUpdateEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If zone event tiles are placed outside the zone's bounds, remove these
|
|
||||||
data.zoneEventTiles = data.zoneEventTiles.filter((tile) => tile.positionX >= 0 && tile.positionX < data.width && tile.positionY >= 0 && tile.positionY < data.height)
|
data.zoneEventTiles = data.zoneEventTiles.filter((tile) => tile.positionX >= 0 && tile.positionX < data.width && tile.positionY >= 0 && tile.positionY < data.height)
|
||||||
|
|
||||||
// If zone objects are placed outside the zone's bounds, remove these
|
|
||||||
data.zoneObjects = data.zoneObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height)
|
data.zoneObjects = data.zoneObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height)
|
||||||
|
|
||||||
await prisma.zone.update({
|
// Clear existing collections
|
||||||
where: { id: data.zoneId },
|
zone.zoneEventTiles.removeAll()
|
||||||
data: {
|
zone.zoneObjects.removeAll()
|
||||||
name: data.name,
|
zone.zoneEffects.removeAll()
|
||||||
width: data.width,
|
|
||||||
height: data.height,
|
|
||||||
tiles: data.tiles,
|
|
||||||
pvp: data.pvp,
|
|
||||||
zoneEventTiles: {
|
|
||||||
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,
|
|
||||||
toRotation: zoneEventTile.teleport.toRotation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: {})
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
zoneObjects: {
|
|
||||||
deleteMany: { zoneId: data.zoneId },
|
|
||||||
create: data.zoneObjects.map((zoneObject) => ({
|
|
||||||
objectId: zoneObject.objectId,
|
|
||||||
depth: zoneObject.depth,
|
|
||||||
isRotated: zoneObject.isRotated,
|
|
||||||
positionX: zoneObject.positionX,
|
|
||||||
positionY: zoneObject.positionY
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
zoneEffects: {
|
|
||||||
deleteMany: { zoneId: data.zoneId },
|
|
||||||
create: data.zoneEffects.map((zoneEffect) => ({
|
|
||||||
effect: zoneEffect.effect,
|
|
||||||
strength: zoneEffect.strength
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
updatedAt: new Date()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// 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)
|
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 after update.`)
|
this.logger.info(`User ${character!.getId()} tried to update zone ${data.zoneId} but it does not exist after update.`)
|
||||||
callback(null)
|
return callback(null)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gameMasterLogger.info(`User ${character.id} has updated zone via zone editor.`)
|
// Reload zone for players
|
||||||
|
|
||||||
callback(zone)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @TODO #246: Reload zone for players who are currently in the zone
|
|
||||||
*/
|
|
||||||
zoneManager.unloadZone(data.zoneId)
|
zoneManager.unloadZone(data.zoneId)
|
||||||
await zoneManager.loadZone(zone)
|
await zoneManager.loadZone(zone)
|
||||||
|
|
||||||
|
return callback(zone)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
gameMasterLogger.error(`gm:zone_editor:zone:update error: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`gm:zone_editor:zone:update error: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
import SocketManager from '#managers/socketManager'
|
||||||
import worldRepository from '#repositories/worldRepository'
|
import worldRepository from '#repositories/worldRepository'
|
||||||
import worldService from '#services/worldService'
|
import worldService from '#services/worldService'
|
||||||
import SocketManager from '#managers/socketManager'
|
|
||||||
|
|
||||||
class DateManager {
|
class DateManager {
|
||||||
private static readonly CONFIG = {
|
private static readonly CONFIG = {
|
||||||
GAME_SPEED: 8, // 24 game hours / 3 real hours
|
GAME_SPEED: 8, // 24 game hours / 3 real hours
|
||||||
UPDATE_INTERVAL: 1000, // 1 second
|
UPDATE_INTERVAL: 1000 // 1 second
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
private io: Server | null = null
|
private io: Server | null = null
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
|
||||||
import Logger, { LoggerType } from '#application/logger'
|
import Logger, { LoggerType } from '#application/logger'
|
||||||
|
import SocketManager from '#managers/socketManager'
|
||||||
import worldRepository from '#repositories/worldRepository'
|
import worldRepository from '#repositories/worldRepository'
|
||||||
import worldService from '#services/worldService'
|
import worldService from '#services/worldService'
|
||||||
import SocketManager from '#managers/socketManager'
|
|
||||||
|
|
||||||
type WeatherState = {
|
type WeatherState = {
|
||||||
isRainEnabled: boolean
|
isRainEnabled: boolean
|
||||||
@ -91,22 +92,12 @@ class WeatherManager {
|
|||||||
private updateWeatherProperty(type: 'rain' | 'fog'): void {
|
private updateWeatherProperty(type: 'rain' | 'fog'): void {
|
||||||
if (type === 'rain') {
|
if (type === 'rain') {
|
||||||
this.weatherState.isRainEnabled = !this.weatherState.isRainEnabled
|
this.weatherState.isRainEnabled = !this.weatherState.isRainEnabled
|
||||||
this.weatherState.rainPercentage = this.weatherState.isRainEnabled
|
this.weatherState.rainPercentage = this.weatherState.isRainEnabled ? this.getRandomNumber(WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min, WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max) : 0
|
||||||
? this.getRandomNumber(
|
|
||||||
WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min,
|
|
||||||
WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max
|
|
||||||
)
|
|
||||||
: 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'fog') {
|
if (type === 'fog') {
|
||||||
this.weatherState.isFogEnabled = !this.weatherState.isFogEnabled
|
this.weatherState.isFogEnabled = !this.weatherState.isFogEnabled
|
||||||
this.weatherState.fogDensity = this.weatherState.isFogEnabled
|
this.weatherState.fogDensity = this.weatherState.isFogEnabled ? this.getRandomNumber(WeatherManager.CONFIG.FOG_DENSITY_RANGE.min, WeatherManager.CONFIG.FOG_DENSITY_RANGE.max) : 0
|
||||||
? this.getRandomNumber(
|
|
||||||
WeatherManager.CONFIG.FOG_DENSITY_RANGE.min,
|
|
||||||
WeatherManager.CONFIG.FOG_DENSITY_RANGE.max
|
|
||||||
)
|
|
||||||
: 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +123,7 @@ class WeatherManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private logError(operation: string, error: unknown): void {
|
private logError(operation: string, error: unknown): void {
|
||||||
this.logger.error(
|
this.logger.error(`Failed to ${operation} weather: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
`Failed to ${operation} weather: ${error instanceof Error ? error.message : String(error)}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +43,8 @@ export class Server {
|
|||||||
SocketManager.boot(this.app, this.http),
|
SocketManager.boot(this.app, this.http),
|
||||||
QueueManager.boot(),
|
QueueManager.boot(),
|
||||||
UserManager.boot(),
|
UserManager.boot(),
|
||||||
DateManager.boot(),
|
// DateManager.boot(),
|
||||||
WeatherManager.boot(),
|
// WeatherManager.boot(),
|
||||||
ZoneManager.boot(),
|
ZoneManager.boot(),
|
||||||
ConsoleManager.boot()
|
ConsoleManager.boot()
|
||||||
])
|
])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user