Almost finalised refactoring
This commit is contained in:
parent
fecdf222d7
commit
a40b71140a
@ -1,6 +1,6 @@
|
|||||||
import { Migration } from '@mikro-orm/migrations';
|
import { Migration } from '@mikro-orm/migrations';
|
||||||
|
|
||||||
export class Migration20250102162954 extends Migration {
|
export class Migration20250103003053 extends Migration {
|
||||||
|
|
||||||
override async up(): Promise<void> {
|
override async up(): Promise<void> {
|
||||||
this.addSql(`create table \`map\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`width\` int not null default 10, \`height\` int not null default 10, \`tiles\` json null, \`pvp\` tinyint(1) not null default false, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
|
this.addSql(`create table \`map\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`width\` int not null default 10, \`height\` int not null default 10, \`tiles\` json null, \`pvp\` tinyint(1) not null default false, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
|
||||||
@ -15,7 +15,7 @@ export class Migration20250102162954 extends Migration {
|
|||||||
this.addSql(`alter table \`map_event_tile_teleport\` add unique \`map_event_tile_teleport_map_event_tile_id_unique\`(\`map_event_tile_id\`);`);
|
this.addSql(`alter table \`map_event_tile_teleport\` add unique \`map_event_tile_teleport_map_event_tile_id_unique\`(\`map_event_tile_id\`);`);
|
||||||
this.addSql(`alter table \`map_event_tile_teleport\` add index \`map_event_tile_teleport_to_map_id_index\`(\`to_map_id\`);`);
|
this.addSql(`alter table \`map_event_tile_teleport\` add index \`map_event_tile_teleport_to_map_id_index\`(\`to_map_id\`);`);
|
||||||
|
|
||||||
this.addSql(`create table \`map_object\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`tags\` json null, \`origin_x\` int not null default 0, \`origin_y\` int not null default 0, \`is_animated\` tinyint(1) not null default false, \`frame_rate\` int not null default 0, \`frame_width\` int not null default 0, \`frame_height\` int not null default 0, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
|
this.addSql(`create table \`map_object\` (\`id\` varchar(255) not null, \`name\` varchar(255) not null, \`tags\` json null, \`origin_x\` numeric(10,2) not null default 0, \`origin_y\` numeric(10,2) not null default 0, \`is_animated\` tinyint(1) not null default false, \`frame_rate\` int not null default 0, \`frame_width\` int not null default 0, \`frame_height\` int not null default 0, \`created_at\` datetime not null, \`updated_at\` datetime not null, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
|
||||||
|
|
||||||
this.addSql(`create table \`placed_map_object\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`map_object_id\` varchar(255) not null, \`depth\` int not null default 0, \`is_rotated\` tinyint(1) not null default false, \`position_x\` int not null default 0, \`position_y\` int not null default 0, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
|
this.addSql(`create table \`placed_map_object\` (\`id\` varchar(255) not null, \`map_id\` varchar(255) not null, \`map_object_id\` varchar(255) not null, \`depth\` int not null default 0, \`is_rotated\` tinyint(1) not null default false, \`position_x\` int not null default 0, \`position_y\` int not null default 0, primary key (\`id\`)) default character set utf8mb4 engine = InnoDB;`);
|
||||||
this.addSql(`alter table \`placed_map_object\` add index \`placed_map_object_map_id_index\`(\`map_id\`);`);
|
this.addSql(`alter table \`placed_map_object\` add index \`placed_map_object_map_id_index\`(\`map_id\`);`);
|
@ -17,6 +17,7 @@ export default defineConfig({
|
|||||||
password: serverConfig.DB_PASS,
|
password: serverConfig.DB_PASS,
|
||||||
dbName: serverConfig.DB_NAME,
|
dbName: serverConfig.DB_NAME,
|
||||||
debug: serverConfig.ENV !== 'production',
|
debug: serverConfig.ENV !== 'production',
|
||||||
|
// allowGlobalContext: true,
|
||||||
driverOptions: {
|
driverOptions: {
|
||||||
allowPublicKeyRetrieval: true
|
allowPublicKeyRetrieval: true
|
||||||
},
|
},
|
||||||
|
Binary file not shown.
@ -6,13 +6,11 @@ import config from '../../mikro-orm.config'
|
|||||||
|
|
||||||
class Database {
|
class Database {
|
||||||
private static orm: MikroORM
|
private static orm: MikroORM
|
||||||
private static em: EntityManager
|
|
||||||
private static logger = Logger.type(LoggerType.APP)
|
private static logger = Logger.type(LoggerType.APP)
|
||||||
|
|
||||||
public static async initialize(): Promise<void> {
|
public static async initialize(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
Database.orm = await MikroORM.init(config)
|
Database.orm = await MikroORM.init(config)
|
||||||
Database.em = Database.orm.em.fork()
|
|
||||||
this.logger.info('Database connection initialized')
|
this.logger.info('Database connection initialized')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`MikroORM connection failed: ${error}`)
|
this.logger.error(`MikroORM connection failed: ${error}`)
|
||||||
@ -20,18 +18,8 @@ class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getORM(): MikroORM {
|
|
||||||
if (!Database.orm) {
|
|
||||||
throw new Error('Database not initialized. Call Database.initialize() first.')
|
|
||||||
}
|
|
||||||
return Database.orm
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getEntityManager(): EntityManager {
|
public static getEntityManager(): EntityManager {
|
||||||
if (!Database.em) {
|
return Database.orm.em.fork()
|
||||||
throw new Error('Database not initialized. Call Database.initialize() first.')
|
|
||||||
}
|
|
||||||
return Database.em
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export default class InitCommand extends BaseCommand {
|
|||||||
public async execute(): Promise<void> {
|
public async execute(): Promise<void> {
|
||||||
// Assets
|
// Assets
|
||||||
await this.importTiles()
|
await this.importTiles()
|
||||||
await this.importObjects()
|
await this.importMapObjects()
|
||||||
await this.createCharacterType()
|
await this.createCharacterType()
|
||||||
await this.createCharacterHair()
|
await this.createCharacterHair()
|
||||||
// await this.createCharacterEquipment()
|
// await this.createCharacterEquipment()
|
||||||
@ -51,19 +51,19 @@ export default class InitCommand extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async importObjects(): Promise<void> {
|
private async importMapObjects(): Promise<void> {
|
||||||
for (const object of fs.readdirSync(Storage.getPublicPath('objects'))) {
|
for (const mapObject of fs.readdirSync(Storage.getPublicPath('map_objects'))) {
|
||||||
const newMapObject = new MapObject()
|
const newMapObject = new MapObject()
|
||||||
newMapObject
|
newMapObject
|
||||||
.setId(object.split('.')[0] as UUID)
|
.setId(mapObject.split('.')[0] as UUID)
|
||||||
.setName('New object')
|
.setName('New map object')
|
||||||
.setFrameWidth(
|
.setFrameWidth(
|
||||||
(await sharp(Storage.getPublicPath('objects', object))
|
(await sharp(Storage.getPublicPath('map_objects', mapObject))
|
||||||
.metadata()
|
.metadata()
|
||||||
.then((metadata) => metadata.height)) ?? 0
|
.then((metadata) => metadata.height)) ?? 0
|
||||||
)
|
)
|
||||||
.setFrameHeight(
|
.setFrameHeight(
|
||||||
(await sharp(Storage.getPublicPath('objects', object))
|
(await sharp(Storage.getPublicPath('map_objects', mapObject))
|
||||||
.metadata()
|
.metadata()
|
||||||
.then((metadata) => metadata.width)) ?? 0
|
.then((metadata) => metadata.width)) ?? 0
|
||||||
)
|
)
|
||||||
|
@ -26,10 +26,7 @@ export class CharacterType extends BaseEntity {
|
|||||||
@Property()
|
@Property()
|
||||||
isSelectable = false
|
isSelectable = false
|
||||||
|
|
||||||
@OneToMany(() => Character, (character) => character.characterType)
|
@ManyToOne({ nullable: true })
|
||||||
characters = new Collection<Character>(this)
|
|
||||||
|
|
||||||
@ManyToOne(() => Sprite, { nullable: true })
|
|
||||||
sprite?: Sprite
|
sprite?: Sprite
|
||||||
|
|
||||||
@Property()
|
@Property()
|
||||||
@ -109,13 +106,4 @@ export class CharacterType extends BaseEntity {
|
|||||||
getUpdatedAt() {
|
getUpdatedAt() {
|
||||||
return this.updatedAt
|
return this.updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
setCharacters(characters: Collection<Character>) {
|
|
||||||
this.characters = characters
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
getCharacters() {
|
|
||||||
return this.characters
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { MapEventTileTeleport } from './mapEventTileTeleport'
|
|||||||
|
|
||||||
import { BaseEntity } from '#application/base/baseEntity'
|
import { BaseEntity } from '#application/base/baseEntity'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
import { placedMapObject } from '#entities/placedMapObject'
|
import { PlacedMapObject } from '#entities/placedMapObject'
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Map extends BaseEntity {
|
export class Map extends BaseEntity {
|
||||||
@ -47,8 +47,8 @@ export class Map extends BaseEntity {
|
|||||||
@OneToMany(() => MapEventTileTeleport, (teleport) => teleport.toMap)
|
@OneToMany(() => MapEventTileTeleport, (teleport) => teleport.toMap)
|
||||||
mapEventTileTeleports = new Collection<MapEventTileTeleport>(this)
|
mapEventTileTeleports = new Collection<MapEventTileTeleport>(this)
|
||||||
|
|
||||||
@OneToMany(() => placedMapObject, (object) => object.map)
|
@OneToMany(() => PlacedMapObject, (object) => object.map)
|
||||||
placedMapObjects = new Collection<placedMapObject>(this)
|
placedMapObjects = new Collection<PlacedMapObject>(this)
|
||||||
|
|
||||||
@OneToMany(() => Character, (character) => character.map)
|
@OneToMany(() => Character, (character) => character.map)
|
||||||
characters = new Collection<Character>(this)
|
characters = new Collection<Character>(this)
|
||||||
@ -155,7 +155,7 @@ export class Map extends BaseEntity {
|
|||||||
return this.mapEventTileTeleports
|
return this.mapEventTileTeleports
|
||||||
}
|
}
|
||||||
|
|
||||||
setPlacedMapObjects(placedMapObjects: Collection<placedMapObject>) {
|
setPlacedMapObjects(placedMapObjects: Collection<PlacedMapObject>) {
|
||||||
this.placedMapObjects = placedMapObjects
|
this.placedMapObjects = placedMapObjects
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,10 @@ export class MapObject extends BaseEntity {
|
|||||||
@Property({ type: 'json', nullable: true })
|
@Property({ type: 'json', nullable: true })
|
||||||
tags?: any
|
tags?: any
|
||||||
|
|
||||||
@Property()
|
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||||
originX = 0
|
originX = 0
|
||||||
|
|
||||||
@Property()
|
@Property({ type: 'decimal', precision: 10, scale: 2 })
|
||||||
originY = 0
|
originY = 0
|
||||||
|
|
||||||
@Property()
|
@Property()
|
||||||
|
@ -10,7 +10,7 @@ import { MapObject } from '#entities/mapObject'
|
|||||||
|
|
||||||
//@TODO : Rename mapObject
|
//@TODO : Rename mapObject
|
||||||
@Entity()
|
@Entity()
|
||||||
export class placedMapObject extends BaseEntity {
|
export class PlacedMapObject extends BaseEntity {
|
||||||
@PrimaryKey()
|
@PrimaryKey()
|
||||||
id = randomUUID()
|
id = randomUUID()
|
||||||
|
|
||||||
|
@ -11,8 +11,13 @@ export default class characterHairListEvent extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
||||||
const items: CharacterHair[] = await characterHairRepository.getAllSelectable()
|
try {
|
||||||
await Database.getEntityManager().populate(items, ['sprite'])
|
const items: CharacterHair[] = await characterHairRepository.getAllSelectable()
|
||||||
callback(items)
|
await Database.getEntityManager().populate(items, ['sprite'])
|
||||||
|
return callback(items)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('character:hair:list error', error)
|
||||||
|
return callback([])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,30 +15,14 @@ export default class CharacterConnectEvent extends BaseEvent {
|
|||||||
this.socket.on('character:connect', this.handleEvent.bind(this))
|
this.socket.on('character:connect', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle character connect event
|
|
||||||
* @TODO:
|
|
||||||
* 1. Check if character is already connected
|
|
||||||
* 2. Update character hair if provided
|
|
||||||
* 3. Emit character connect event
|
|
||||||
* 4. Let other clients know of new character
|
|
||||||
* @param data
|
|
||||||
* @param callback
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> {
|
private async handleEvent(data: CharacterConnectPayload, callback: (response: any) => void): Promise<void> {
|
||||||
if (!this.socket.userId) {
|
|
||||||
this.emitError('User not authenticated')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (await this.checkForActiveCharacters()) {
|
if (await this.checkForActiveCharacters()) {
|
||||||
this.emitError('You are already connected to another character')
|
this.emitError('You are already connected to another character')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const character = await CharacterRepository.getByUserAndId(this.socket.userId, data.characterId)
|
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, data.characterId)
|
||||||
|
|
||||||
if (!character) {
|
if (!character) {
|
||||||
this.emitError('Character not found or does not belong to this user')
|
this.emitError('Character not found or does not belong to this user')
|
||||||
@ -57,8 +41,8 @@ export default class CharacterConnectEvent extends BaseEvent {
|
|||||||
// Emit character connect event
|
// Emit character connect event
|
||||||
callback({ character })
|
callback({ character })
|
||||||
|
|
||||||
// wait 300 ms, @TODO: Find a better way to do this
|
// wait 300 ms, @TODO: Find a better way to do this, race condition
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||||
|
|
||||||
await TeleportService.teleportCharacter(character.id, {
|
await TeleportService.teleportCharacter(character.id, {
|
||||||
targetMapId: character.map.id,
|
targetMapId: character.map.id,
|
||||||
|
@ -9,7 +9,6 @@ type TypePayload = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TypeResponse = {
|
type TypeResponse = {
|
||||||
map: Map
|
|
||||||
characters: Character[]
|
characters: Character[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,11 +19,7 @@ export default class CharacterDeleteEvent extends BaseEvent {
|
|||||||
|
|
||||||
private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
|
private async handleEvent(data: TypePayload, callback: (response: TypeResponse) => void): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const character = await CharacterRepository.getByUserAndId(this.socket.userId!, data.characterId)
|
await (await CharacterRepository.getByUserAndId(this.socket.userId!, data.characterId))?.delete()
|
||||||
if (character) {
|
|
||||||
await character.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
const characters: Character[] = await CharacterRepository.getByUserId(this.socket.userId!)
|
const characters: Character[] = await CharacterRepository.getByUserId(this.socket.userId!)
|
||||||
|
|
||||||
this.socket.emit('character:list', characters)
|
this.socket.emit('character:list', characters)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import Database from '#application/database'
|
|
||||||
import { Character } from '#entities/character'
|
import { Character } from '#entities/character'
|
||||||
import CharacterRepository from '#repositories/characterRepository'
|
import CharacterRepository from '#repositories/characterRepository'
|
||||||
|
|
||||||
@ -10,9 +9,7 @@ export default class CharacterListEvent extends BaseEvent {
|
|||||||
|
|
||||||
private async handleEvent(data: any): Promise<void> {
|
private async handleEvent(data: any): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const characters: Character[] = await CharacterRepository.getByUserId(this.socket.userId!)
|
let characters: Character[] = await CharacterRepository.getByUserId(this.socket.userId!, ['characterType', 'characterHair'])
|
||||||
await Database.getEntityManager().populate(characters, ['characterType', 'characterHair'])
|
|
||||||
|
|
||||||
this.socket.emit('character:list', characters)
|
this.socket.emit('character:list', characters)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('character:list error', error.message)
|
this.logger.error('character:list error', error.message)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||||
import characterRepository from '#repositories/characterRepository'
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
interface IPayload {
|
interface IPayload {
|
||||||
id: number
|
id: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class characterHairDeleteEvent extends BaseEvent {
|
export default class characterHairDeleteEvent extends BaseEvent {
|
||||||
@ -12,20 +12,13 @@ export default class characterHairDeleteEvent extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const characterHair = await CharacterHairRepository.getById(data.id)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (characterHair) {
|
|
||||||
await characterHair.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(true)
|
const characterHair = await CharacterHairRepository.getById(data.id)
|
||||||
|
await (await CharacterHairRepository.getById(data.id))?.delete()
|
||||||
|
|
||||||
|
return callback(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
callback(false)
|
callback(false)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { CharacterHair } from '#entities/characterHair'
|
import { CharacterHair } from '#entities/characterHair'
|
||||||
import characterHairRepository from '#repositories/characterHairRepository'
|
import characterHairRepository from '#repositories/characterHairRepository'
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
|
|
||||||
interface IPayload {}
|
interface IPayload {}
|
||||||
|
|
||||||
@ -11,19 +10,14 @@ export default class characterHairListEvent extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: CharacterHair[]) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
try {
|
||||||
if (!character) {
|
if (!(await this.isCharacterGM())) return
|
||||||
this.logger.error('gm:characterHair:list error', 'Character not found')
|
|
||||||
|
const items = await characterHairRepository.getAll()
|
||||||
|
return callback(items)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('gm:characterHair:list error', error)
|
||||||
return callback([])
|
return callback([])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
this.logger.info(`User ${character.id} tried to list character hair but is not a game master.`)
|
|
||||||
return callback([])
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all objects
|
|
||||||
const items = await characterHairRepository.getAll()
|
|
||||||
callback(items)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import characterRepository from '#repositories/characterRepository'
|
|||||||
import SpriteRepository from '#repositories/spriteRepository'
|
import SpriteRepository from '#repositories/spriteRepository'
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
id: number
|
id: UUID
|
||||||
name: string
|
name: string
|
||||||
gender: CharacterGender
|
gender: CharacterGender
|
||||||
isSelectable: boolean
|
isSelectable: boolean
|
||||||
@ -19,21 +19,17 @@ export default class CharacterHairUpdateEvent extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 as number)
|
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
const sprite = await SpriteRepository.getById(data.spriteId)
|
const sprite = await SpriteRepository.getById(data.spriteId)
|
||||||
const characterHair = await CharacterHairRepository.getById(data.id)
|
const characterHair = await CharacterHairRepository.getById(data.id)
|
||||||
|
|
||||||
if (characterHair) {
|
if (!characterHair) {
|
||||||
await characterHair.setName(data.name).setGender(data.gender).setIsSelectable(data.isSelectable).setSprite(sprite!).update()
|
return callback(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await characterHair.setName(data.name).setGender(data.gender).setIsSelectable(data.isSelectable).setSprite(sprite!).update()
|
||||||
return callback(true)
|
return callback(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Error updating character hair: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`Error updating character hair: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
@ -1,41 +1,22 @@
|
|||||||
import { CharacterGender, CharacterRace } from '@prisma/client'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
import { Server } from 'socket.io'
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
|
|
||||||
export default class CharacterTypeCreateEvent {
|
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
|
export default class CharacterTypeCreateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterType:create', this.handleEvent.bind(this))
|
this.socket.on('gm:characterType:create', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {
|
private async handleEvent(data: undefined, callback: (response: boolean, characterType?: any) => void): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
if (!(await this.isCharacterGM())) return
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
const newCharacterType = new CharacterType()
|
||||||
return callback(false)
|
await newCharacterType.setName('New character type').save()
|
||||||
}
|
|
||||||
|
|
||||||
const newCharacterType = await prisma.characterType.create({
|
return callback(true, newCharacterType)
|
||||||
data: {
|
|
||||||
name: 'New character type',
|
|
||||||
gender: CharacterGender.MALE,
|
|
||||||
race: CharacterRace.HUMAN
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
callback(true, newCharacterType)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating character type:', error)
|
console.error('Error creating character type:', error)
|
||||||
callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,28 @@
|
|||||||
import { Server } from 'socket.io'
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
|
||||||
interface IPayload {
|
interface IPayload {
|
||||||
id: number
|
id: UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CharacterTypeDeleteEvent {
|
export default class CharacterTypeDeleteEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterType:remove', this.handleEvent.bind(this))
|
this.socket.on('gm:characterType:remove', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId!)
|
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
const characterType = await CharacterTypeRepository.getById(data.id)
|
const characterType = await CharacterTypeRepository.getById(data.id)
|
||||||
if (!characterType) return callback(false)
|
if (!characterType) return callback(false)
|
||||||
|
|
||||||
await characterType.delete()
|
await characterType.delete()
|
||||||
callback(true)
|
return callback(true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
gameMasterLogger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`Error deleting character type ${data.id}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,23 @@
|
|||||||
import { CharacterType } from '@prisma/client'
|
|
||||||
import { Server } from 'socket.io'
|
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { CharacterType } from '#entities/characterType'
|
||||||
|
|
||||||
interface IPayload {}
|
interface IPayload {}
|
||||||
|
|
||||||
export default class CharacterTypeListEvent {
|
export default class CharacterTypeListEvent extends BaseEvent {
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:characterType:list', this.handleEvent.bind(this))
|
this.socket.on('gm:characterType:list', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: CharacterType[]) => void): Promise<void> {
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
try {
|
||||||
if (!character) {
|
if (!(await this.isCharacterGM())) return
|
||||||
gameMasterLogger.error('gm:characterType:list error', 'Character not found')
|
|
||||||
|
const items = await CharacterTypeRepository.getAll()
|
||||||
|
return callback(items)
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('gm:characterType:list error', error)
|
||||||
return callback([])
|
return callback([])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
gameMasterLogger.info(`User ${character.id} tried to list character types but is not a game master.`)
|
|
||||||
return callback([])
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all objects
|
|
||||||
const items = await CharacterTypeRepository.getAll()
|
|
||||||
callback(items)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import ObjectRepository from '#repositories/mapObjectRepository'
|
import ObjectRepository from '#repositories/mapObjectRepository'
|
||||||
import { BaseEvent } from '#application/base/baseEvent'
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { MapObject } from '#entities/mapObject'
|
||||||
|
|
||||||
interface IPayload {}
|
interface IPayload {}
|
||||||
|
|
||||||
export default class ObjectListEvent extends BaseEvent{
|
export default class MapObjectListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:object:list', this.handleEvent.bind(this))
|
this.socket.on('gm:mapObject:list', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Object[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: MapObject[]) => void): Promise<void> {
|
||||||
if (!(await this.isCharacterGM())) return
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
// get all objects
|
// get all objects
|
38
src/events/gameMaster/assetManager/mapObject/remove.ts
Normal file
38
src/events/gameMaster/assetManager/mapObject/remove.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import Storage from '#application/storage'
|
||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import MapObjectRepository from '#repositories/mapObjectRepository'
|
||||||
|
import { UUID } from '#application/types'
|
||||||
|
|
||||||
|
interface IPayload {
|
||||||
|
mapObjectId: UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MapObjectRemoveEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:mapObject:remove', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// remove the tile from the disk
|
||||||
|
const finalFilePath = Storage.getPublicPath('map_objects', data.mapObjectId + '.png')
|
||||||
|
fs.unlink(finalFilePath, async (err) => {
|
||||||
|
if (err) {
|
||||||
|
this.logger.error(`Error deleting object ${data.mapObjectId}: ${err.message}`)
|
||||||
|
callback(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await (await MapObjectRepository.getById(data.mapObjectId))?.delete()
|
||||||
|
|
||||||
|
return callback(true)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Error deleting object ${data.mapObjectId}: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/events/gameMaster/assetManager/mapObject/update.ts
Normal file
46
src/events/gameMaster/assetManager/mapObject/update.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { UUID } from '#application/types'
|
||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import MapObjectRepository from '#repositories/mapObjectRepository'
|
||||||
|
|
||||||
|
type Payload = {
|
||||||
|
id: UUID
|
||||||
|
name: string
|
||||||
|
tags: string[]
|
||||||
|
originX: number
|
||||||
|
originY: number
|
||||||
|
isAnimated: boolean
|
||||||
|
frameRate: number
|
||||||
|
frameWidth: number
|
||||||
|
frameHeight: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MapObjectUpdateEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:mapObject:update', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const mapObject = await MapObjectRepository.getById(data.id)
|
||||||
|
if (!mapObject) return callback(false)
|
||||||
|
|
||||||
|
await mapObject
|
||||||
|
.setName(data.name)
|
||||||
|
.setTags(data.tags)
|
||||||
|
.setOriginX(data.originX)
|
||||||
|
.setOriginY(data.originY)
|
||||||
|
.setIsAnimated(data.isAnimated)
|
||||||
|
.setFrameRate(data.frameRate)
|
||||||
|
.setFrameWidth(data.frameWidth)
|
||||||
|
.setFrameHeight(data.frameHeight)
|
||||||
|
.update()
|
||||||
|
|
||||||
|
return callback(true)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
src/events/gameMaster/assetManager/mapObject/upload.ts
Normal file
53
src/events/gameMaster/assetManager/mapObject/upload.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import fs from 'fs/promises'
|
||||||
|
import { writeFile } from 'node:fs/promises'
|
||||||
|
|
||||||
|
import sharp from 'sharp'
|
||||||
|
import Storage from '#application/storage'
|
||||||
|
import { BaseEvent } from '#application/base/baseEvent'
|
||||||
|
import { MapObject } from '#entities/mapObject'
|
||||||
|
|
||||||
|
interface IObjectData {
|
||||||
|
[key: string]: Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MapObjectUploadEvent extends BaseEvent {
|
||||||
|
public listen(): void {
|
||||||
|
this.socket.on('gm:mapObject:upload', this.handleEvent.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(data: IObjectData, callback: (response: boolean) => void): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!(await this.isCharacterGM())) return
|
||||||
|
|
||||||
|
const public_folder = Storage.getPublicPath('map_objects')
|
||||||
|
|
||||||
|
// Ensure the folder exists
|
||||||
|
await fs.mkdir(public_folder, { recursive: true })
|
||||||
|
|
||||||
|
const uploadPromises = Object.entries(data).map(async ([key, objectData]) => {
|
||||||
|
// Get image dimensions
|
||||||
|
const metadata = await sharp(objectData).metadata()
|
||||||
|
const width = metadata.width || 0
|
||||||
|
const height = metadata.height || 0
|
||||||
|
|
||||||
|
// Create new map object and save it to database
|
||||||
|
const mapObject = new MapObject()
|
||||||
|
await mapObject.setName(key).setTags([]).setOriginX(0).setOriginY(0).setFrameWidth(width).setFrameHeight(height).save()
|
||||||
|
|
||||||
|
// Save image to disk
|
||||||
|
const uuid = mapObject.getId()
|
||||||
|
const filename = `${uuid}.png`
|
||||||
|
const finalFilePath = Storage.getPublicPath('map_objects', filename)
|
||||||
|
await writeFile(finalFilePath, objectData)
|
||||||
|
|
||||||
|
this.logger.info('gm:mapObject:upload', `Object ${key} uploaded with id ${uuid}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all(uploadPromises)
|
||||||
|
return callback(true)
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.error('gm:mapObject:upload error', error.message)
|
||||||
|
return callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,59 +0,0 @@
|
|||||||
import fs from 'fs'
|
|
||||||
|
|
||||||
import { Server } from 'socket.io'
|
|
||||||
|
|
||||||
import { gameLogger, gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import Storage from '#application/storage'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
|
|
||||||
interface IPayload {
|
|
||||||
object: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ObjectRemoveEvent {
|
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
|
||||||
this.socket.on('gm:object:remove', this.handleEvent.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: boolean) => void): Promise<void> {
|
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await prisma.object.delete({
|
|
||||||
where: {
|
|
||||||
id: data.object
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// get root path
|
|
||||||
const public_folder = Storage.getPublicPath('objects')
|
|
||||||
|
|
||||||
// remove the tile from the disk
|
|
||||||
const finalFilePath = Storage.getPublicPath('objects', data.object + '.png')
|
|
||||||
fs.unlink(finalFilePath, (err) => {
|
|
||||||
if (err) {
|
|
||||||
gameMasterLogger.error(`Error deleting object ${data.object}: ${err.message}`)
|
|
||||||
callback(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(true)
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
gameLogger.error(`Error deleting object ${data.object}: ${error instanceof Error ? error.message : String(error)}`)
|
|
||||||
callback(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import { Server } from 'socket.io'
|
|
||||||
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
|
|
||||||
type Payload = {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
tags: string[]
|
|
||||||
originX: number
|
|
||||||
originY: number
|
|
||||||
isAnimated: boolean
|
|
||||||
frameRate: number
|
|
||||||
frameWidth: number
|
|
||||||
frameHeight: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ObjectUpdateEvent {
|
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
|
||||||
this.socket.on('gm:object:update', this.handleEvent.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (success: boolean) => void): Promise<void> {
|
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const object = await prisma.object.update({
|
|
||||||
where: {
|
|
||||||
id: data.id
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
name: data.name,
|
|
||||||
tags: data.tags,
|
|
||||||
originX: data.originX,
|
|
||||||
originY: data.originY,
|
|
||||||
isAnimated: data.isAnimated,
|
|
||||||
frameRate: data.frameRate,
|
|
||||||
frameWidth: data.frameWidth,
|
|
||||||
frameHeight: data.frameHeight
|
|
||||||
}
|
|
||||||
})
|
|
||||||
callback(true)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
callback(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
import fs from 'fs/promises'
|
|
||||||
import { writeFile } from 'node:fs/promises'
|
|
||||||
|
|
||||||
import sharp from 'sharp'
|
|
||||||
import { Server } from 'socket.io'
|
|
||||||
|
|
||||||
import { gameMasterLogger } from '#application/logger'
|
|
||||||
import prisma from '#application/prisma'
|
|
||||||
import Storage from '#application/storage'
|
|
||||||
import { TSocket } from '#application/types'
|
|
||||||
import characterRepository from '#repositories/characterRepository'
|
|
||||||
|
|
||||||
interface IObjectData {
|
|
||||||
[key: string]: Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ObjectUploadEvent {
|
|
||||||
constructor(
|
|
||||||
private readonly io: Server,
|
|
||||||
private readonly socket: TSocket
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public listen(): void {
|
|
||||||
this.socket.on('gm:object:upload', this.handleEvent.bind(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleEvent(data: IObjectData, callback: (response: boolean) => void): Promise<void> {
|
|
||||||
try {
|
|
||||||
const character = await characterRepository.getById(this.socket.characterId as number)
|
|
||||||
if (!character) return callback(false)
|
|
||||||
|
|
||||||
if (character.role !== 'gm') {
|
|
||||||
return callback(false)
|
|
||||||
}
|
|
||||||
const public_folder = Storage.getPublicPath('objects')
|
|
||||||
|
|
||||||
// Ensure the folder exists
|
|
||||||
await fs.mkdir(public_folder, { recursive: true })
|
|
||||||
|
|
||||||
const uploadPromises = Object.entries(data).map(async ([key, objectData]) => {
|
|
||||||
// Get image dimensions
|
|
||||||
const metadata = await sharp(objectData).metadata()
|
|
||||||
const width = metadata.width || 0
|
|
||||||
const height = metadata.height || 0
|
|
||||||
|
|
||||||
const object = await prisma.object.create({
|
|
||||||
data: {
|
|
||||||
name: key,
|
|
||||||
tags: [],
|
|
||||||
originX: 0,
|
|
||||||
originY: 0,
|
|
||||||
frameWidth: width,
|
|
||||||
frameHeight: height
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const uuid = object.id
|
|
||||||
const filename = `${uuid}.png`
|
|
||||||
const finalFilePath = Storage.getPublicPath('objects', filename)
|
|
||||||
await writeFile(finalFilePath, objectData)
|
|
||||||
|
|
||||||
gameMasterLogger.info('gm:object:upload', `Object ${key} uploaded with id ${uuid}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Promise.all(uploadPromises)
|
|
||||||
|
|
||||||
callback(true)
|
|
||||||
} catch (error: any) {
|
|
||||||
gameMasterLogger.error('gm:object:upload error', error.message)
|
|
||||||
callback(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class MapCreateEvent extends BaseEvent {
|
export default class MapCreateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map_editor:map:create', this.handleEvent.bind(this))
|
this.socket.on('gm:map:create', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: Payload, callback: (response: Map[]) => void): Promise<void> {
|
private async handleEvent(data: Payload, callback: (response: Map[]) => void): Promise<void> {
|
||||||
@ -30,7 +30,7 @@ export default class MapCreateEvent extends BaseEvent {
|
|||||||
const mapList = await MapRepository.getAll()
|
const mapList = await MapRepository.getAll()
|
||||||
return callback(mapList)
|
return callback(mapList)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('gm:map_editor:map:create error', error.message)
|
this.logger.error('gm:map:create error', error.message)
|
||||||
this.socket.emit('notification', { message: 'Failed to create map.' })
|
this.socket.emit('notification', { message: 'Failed to create map.' })
|
||||||
return callback([])
|
return callback([])
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ type Payload = {
|
|||||||
|
|
||||||
export default class MapDeleteEvent extends BaseEvent {
|
export default class MapDeleteEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map_editor:map:delete', this.handleEvent.bind(this))
|
this.socket.on('gm:map: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> {
|
||||||
@ -22,7 +22,7 @@ export default class MapDeleteEvent extends BaseEvent {
|
|||||||
this.logger.info(`Map ${data.mapId} deleted successfully.`)
|
this.logger.info(`Map ${data.mapId} deleted successfully.`)
|
||||||
return callback(true)
|
return callback(true)
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
this.logger.error('gm:map_editor:map:delete error', error)
|
this.logger.error('gm:map:delete error', error)
|
||||||
return callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ interface IPayload {}
|
|||||||
|
|
||||||
export default class MapListEvent extends BaseEvent {
|
export default class MapListEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map_editor:map:list', this.handleEvent.bind(this))
|
this.socket.on('gm:map:list', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Map[]) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Map[]) => void): Promise<void> {
|
||||||
@ -18,7 +18,7 @@ export default class MapListEvent extends BaseEvent {
|
|||||||
const maps = await MapRepository.getAll()
|
const maps = await MapRepository.getAll()
|
||||||
return callback(maps)
|
return callback(maps)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('gm:map_editor:map:list error', error.message)
|
this.logger.error('gm:map:list error', error.message)
|
||||||
return callback([])
|
return callback([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { BaseEvent } from '#application/base/baseEvent'
|
|||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
import { Map } from '#entities/map'
|
import { Map } from '#entities/map'
|
||||||
import MapRepository from '#repositories/mapRepository'
|
import MapRepository from '#repositories/mapRepository'
|
||||||
|
import Database from '#application/database'
|
||||||
|
|
||||||
interface IPayload {
|
interface IPayload {
|
||||||
mapId: UUID
|
mapId: UUID
|
||||||
@ -9,7 +10,7 @@ interface IPayload {
|
|||||||
|
|
||||||
export default class MapRequestEvent extends BaseEvent {
|
export default class MapRequestEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map_editor:map:request', this.handleEvent.bind(this))
|
this.socket.on('gm:map:request', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
||||||
@ -25,14 +26,18 @@ export default class MapRequestEvent extends BaseEvent {
|
|||||||
|
|
||||||
const map = await MapRepository.getById(data.mapId)
|
const map = await MapRepository.getById(data.mapId)
|
||||||
|
|
||||||
|
|
||||||
if (!map) {
|
if (!map) {
|
||||||
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map ${data.mapId} but it does not exist.`)
|
this.logger.info(`User ${(await this.getCharacter())!.getId()} tried to request map ${data.mapId} but it does not exist.`)
|
||||||
return callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(map)
|
||||||
|
await Database.getEntityManager().populate(map, ['mapEventTiles', 'placedMapObjects'])
|
||||||
|
|
||||||
return callback(map)
|
return callback(map)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error('gm:map_editor:map:request error', error.message)
|
this.logger.error('gm:map:request error', error.message)
|
||||||
return callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@ import { Map } from '#entities/map'
|
|||||||
import { MapEffect } from '#entities/mapEffect'
|
import { MapEffect } from '#entities/mapEffect'
|
||||||
import { MapEventTile } from '#entities/mapEventTile'
|
import { MapEventTile } from '#entities/mapEventTile'
|
||||||
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
|
import { MapEventTileTeleport } from '#entities/mapEventTileTeleport'
|
||||||
import { MapObject } from '#entities/mapObject'
|
|
||||||
import mapManager from '#managers/mapManager'
|
import mapManager from '#managers/mapManager'
|
||||||
import MapRepository from '#repositories/mapRepository'
|
import MapRepository from '#repositories/mapRepository'
|
||||||
|
import { PlacedMapObject } from '#entities/placedMapObject'
|
||||||
|
|
||||||
interface IPayload {
|
interface IPayload {
|
||||||
mapId: UUID
|
mapId: UUID
|
||||||
@ -31,12 +31,12 @@ interface IPayload {
|
|||||||
effect: string
|
effect: string
|
||||||
strength: number
|
strength: number
|
||||||
}[]
|
}[]
|
||||||
mapObjects: MapObject[]
|
placedMapObjects: PlacedMapObject[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MapUpdateEvent extends BaseEvent {
|
export default class MapUpdateEvent extends BaseEvent {
|
||||||
public listen(): void {
|
public listen(): void {
|
||||||
this.socket.on('gm:map_editor:map:update', this.handleEvent.bind(this))
|
this.socket.on('gm:map:update', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
private async handleEvent(data: IPayload, callback: (response: Map | null) => void): Promise<void> {
|
||||||
@ -70,11 +70,11 @@ export default class MapUpdateEvent extends BaseEvent {
|
|||||||
|
|
||||||
data.mapEventTiles = data.mapEventTiles.filter((tile) => tile.positionX >= 0 && tile.positionX < data.width && tile.positionY >= 0 && tile.positionY < data.height)
|
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)
|
data.placedMapObjects = data.placedMapObjects.filter((obj) => obj.positionX >= 0 && obj.positionX < data.width && obj.positionY >= 0 && obj.positionY < data.height)
|
||||||
|
|
||||||
// Clear existing collections
|
// Clear existing collections
|
||||||
map.mapEventTiles.removeAll()
|
map.mapEventTiles.removeAll()
|
||||||
map.mapObjects.removeAll()
|
map.placedMapObjects.removeAll()
|
||||||
map.mapEffects.removeAll()
|
map.mapEffects.removeAll()
|
||||||
|
|
||||||
// Create and add new map event tiles
|
// Create and add new map event tiles
|
||||||
@ -95,16 +95,14 @@ export default class MapUpdateEvent extends BaseEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create and add new map objects
|
// Create and add new map objects
|
||||||
for (const object of data.mapObjects) {
|
for (const object of data.placedMapObjects) {
|
||||||
const mapObject = new MapObject().setMapObject(object.mapObject).setDepth(object.depth).setIsRotated(object.isRotated).setPositionX(object.positionX).setPositionY(object.positionY).setMap(map)
|
const mapObject = new PlacedMapObject().setMapObject(object.mapObject).setDepth(object.depth).setIsRotated(object.isRotated).setPositionX(object.positionX).setPositionY(object.positionY).setMap(map)
|
||||||
|
map.placedMapObjects.add(mapObject)
|
||||||
map.mapObjects.add(mapObject)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and add new map effects
|
// Create and add new map effects
|
||||||
for (const effect of data.mapEffects) {
|
for (const effect of data.mapEffects) {
|
||||||
const mapEffect = new MapEffect().setEffect(effect.effect).setStrength(effect.strength).setMap(map)
|
const mapEffect = new MapEffect().setEffect(effect.effect).setStrength(effect.strength).setMap(map)
|
||||||
|
|
||||||
map.mapEffects.add(mapEffect)
|
map.mapEffects.add(mapEffect)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +123,7 @@ export default class MapUpdateEvent extends BaseEvent {
|
|||||||
|
|
||||||
return callback(map)
|
return callback(map)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`gm:map_editor:map:update error: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`gm:mapObject:update error: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
return callback(null)
|
return callback(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,17 +74,17 @@ export class AssetsController extends BaseController {
|
|||||||
await Database.getEntityManager().populate(sprite, ['spriteActions'])
|
await Database.getEntityManager().populate(sprite, ['spriteActions'])
|
||||||
|
|
||||||
const assets: AssetData[] = sprite.spriteActions.getItems().map((spriteAction) => ({
|
const assets: AssetData[] = sprite.spriteActions.getItems().map((spriteAction) => ({
|
||||||
key: sprite.id + '-' + spriteAction.action,
|
key: sprite.getId() + '-' + spriteAction.getAction(),
|
||||||
data: '/assets/sprites/' + sprite.getId() + '/' + spriteAction.getAction() + '.png',
|
data: '/assets/sprites/' + sprite.getId() + '/' + spriteAction.getAction() + '.png',
|
||||||
group: spriteAction.isAnimated ? 'sprite_animations' : 'sprites',
|
group: spriteAction.getIsAnimated() ? 'sprite_animations' : 'sprites',
|
||||||
updatedAt: sprite.getUpdatedAt(),
|
updatedAt: sprite.getUpdatedAt(),
|
||||||
originX: Number(spriteAction.originX.toString()),
|
originX: Number(spriteAction.getOriginX().toString()),
|
||||||
originY: Number(spriteAction.originY.toString()),
|
originY: Number(spriteAction.getOriginY().toString()),
|
||||||
isAnimated: spriteAction.getIsAnimated(),
|
isAnimated: spriteAction.getIsAnimated(),
|
||||||
frameRate: spriteAction.getFrameRate(),
|
frameRate: spriteAction.getFrameRate(),
|
||||||
frameWidth: spriteAction.getFrameWidth(),
|
frameWidth: spriteAction.getFrameWidth(),
|
||||||
frameHeight: spriteAction.getFrameHeight(),
|
frameHeight: spriteAction.getFrameHeight(),
|
||||||
frameCount: JSON.parse(JSON.stringify(spriteAction.getSprites())).length
|
frameCount: spriteAction.getSprites()?.length
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return this.sendSuccess(res, assets)
|
return this.sendSuccess(res, assets)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { BaseRepository } from '#application/base/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
import { Character } from '#entities/character'
|
import { Character } from '#entities/character'
|
||||||
|
import { LoadHint, Populate } from '@mikro-orm/core'
|
||||||
|
|
||||||
class CharacterRepository extends BaseRepository {
|
class CharacterRepository extends BaseRepository {
|
||||||
async getByUserId(userId: UUID): Promise<Character[]> {
|
async getByUserId(userId: UUID, populate?: LoadHint<Character, '*'>): Promise<Character[]> {
|
||||||
try {
|
try {
|
||||||
const repository = this.em.getRepository(Character)
|
const repository = this.em.getRepository(Character)
|
||||||
return await repository.find({ user: userId })
|
return await repository.find({ user: userId }, { populate: populate as Populate<Character> })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Failed to get character by user ID: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`Failed to get character by user ID: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
return []
|
return []
|
||||||
|
@ -19,7 +19,7 @@ class CharacterTypeRepository extends BaseRepository {
|
|||||||
return await repository.findAll()
|
return await repository.findAll()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
this.logger.error(`Failed to get all character types: ${error instanceof Error ? error.message : String(error)}`)
|
this.logger.error(`Failed to get all character types: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
return null
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
import { BaseRepository } from '#application/base/baseRepository'
|
import { BaseRepository } from '#application/base/baseRepository'
|
||||||
import { UUID } from '#application/types'
|
import { UUID } from '#application/types'
|
||||||
|
import { MapObject } from '#entities/mapObject'
|
||||||
|
|
||||||
class MapObjectRepository extends BaseRepository {
|
class MapObjectRepository extends BaseRepository {
|
||||||
async getById(id: UUID): Promise<any> {
|
async getById(id: UUID): Promise<MapObject | null> {
|
||||||
try {
|
try {
|
||||||
const repository = this.em.getRepository(Object)
|
const repository = this.em.getRepository(MapObject)
|
||||||
return await repository.findOne({ id })
|
return await repository.findOne({ id })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<any> {
|
async getAll(): Promise<MapObject[]> {
|
||||||
try {
|
try {
|
||||||
const repository = this.em.getRepository(Object)
|
const repository = this.em.getRepository(MapObject)
|
||||||
return await repository.findAll()
|
return await repository.findAll()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return null
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user