diff --git a/package-lock.json b/package-lock.json index 861d836..434d5b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1843,17 +1843,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.21.0.tgz", - "integrity": "sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/type-utils": "8.21.0", - "@typescript-eslint/utils": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1873,16 +1873,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", - "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4" }, "engines": { @@ -1898,14 +1898,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.21.0.tgz", - "integrity": "sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0" + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1916,14 +1916,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.21.0.tgz", - "integrity": "sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.21.0", - "@typescript-eslint/utils": "8.21.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -1940,9 +1940,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.21.0.tgz", - "integrity": "sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", "dev": true, "license": "MIT", "engines": { @@ -1954,14 +1954,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.21.0.tgz", - "integrity": "sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/visitor-keys": "8.21.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1981,16 +1981,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.21.0.tgz", - "integrity": "sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.21.0", - "@typescript-eslint/types": "8.21.0", - "@typescript-eslint/typescript-estree": "8.21.0" + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2005,13 +2005,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.21.0.tgz", - "integrity": "sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.21.0", + "@typescript-eslint/types": "8.22.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -2470,9 +2470,9 @@ "license": "BSD-3-Clause" }, "node_modules/bullmq": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.37.0.tgz", - "integrity": "sha512-h/wf979+9uROyYpB8oTE44Py6JERCluCSd+ZFpCZlPsYh+wxAkqrfHsHHHKBgsNJp9odWLIY4SG+280EXzXiCQ==", + "version": "5.38.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.38.0.tgz", + "integrity": "sha512-X6ApLQlSmX0LQ58xCpCoXE2nN57y8H9LD4EApJl9+8E6+FNWdKy6gykL8ofB9x4TcZeeGJhZOUZJAuhRV721bw==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -3042,16 +3042,16 @@ } }, "node_modules/engine.io": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.3.tgz", - "integrity": "sha512-2hkLItQMBkoYSagneiisupWGvsQlWXqzhSMvsjaM8GYbnfUsX7tzYQq9QARnate5LRedVTX+MbkSZAANAr3NtQ==", + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", "license": "MIT", "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~1.0.2", + "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", @@ -3071,12 +3071,12 @@ } }, "node_modules/engine.io/node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.6" } }, "node_modules/engine.io/node_modules/debug": { @@ -5265,9 +5265,9 @@ } }, "node_modules/mariadb/node_modules/@types/node": { - "version": "22.10.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", - "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" diff --git a/src/application/base/baseEntity.ts b/src/application/base/baseEntity.ts index 570859e..87f8a26 100644 --- a/src/application/base/baseEntity.ts +++ b/src/application/base/baseEntity.ts @@ -46,9 +46,9 @@ export abstract class BaseEntity { throw error } } catch (error) { - const errorMessage = error instanceof Error ? error.message : - error && typeof error === 'object' && 'toString' in error ? error.toString() : - String(error) + const errorMessage = error instanceof Error ? error.message : error && typeof error === 'object' && 'toString' in error ? error.toString() : String(error) + + console.log(errorMessage) this.logger.error(`Failed to ${actionDescription}: ${errorMessage}`) throw error } diff --git a/src/events/chat/gameMaster/toggleFogCommand.ts b/src/events/chat/gameMaster/toggleFogCommand.ts index 27c2ac3..92a7d47 100644 --- a/src/events/chat/gameMaster/toggleFogCommand.ts +++ b/src/events/chat/gameMaster/toggleFogCommand.ts @@ -21,7 +21,7 @@ export default class ToggleFogCommand extends BaseEvent { const args = ChatService.getArgs('fog', data.message) - await WeatherManager.setFogValue(args![0] ? Number(args![0]) : null); + await WeatherManager.setFogValue(args![0] ? Number(args![0]) : null) callback(true) } catch (error: any) { this.logger.error('command error', error.message) diff --git a/src/events/chat/gameMaster/toggleRainCommand.ts b/src/events/chat/gameMaster/toggleRainCommand.ts index f8bc3a7..1804676 100644 --- a/src/events/chat/gameMaster/toggleRainCommand.ts +++ b/src/events/chat/gameMaster/toggleRainCommand.ts @@ -21,7 +21,7 @@ export default class ToggleRainCommand extends BaseEvent { let args = ChatService.getArgs('rain', data.message) - await WeatherManager.setRainValue(args![0] ? Number(args![0]) : null); + await WeatherManager.setRainValue(args![0] ? Number(args![0]) : null) callback(true) } catch (error: any) { this.logger.error('command error', error.message) diff --git a/src/events/gameMaster/assetManager/sprite/update.ts b/src/events/gameMaster/assetManager/sprite/update.ts index 6cf75e1..bdd036e 100644 --- a/src/events/gameMaster/assetManager/sprite/update.ts +++ b/src/events/gameMaster/assetManager/sprite/update.ts @@ -1,6 +1,8 @@ import { BaseEvent } from '#application/base/baseEvent' import { UUID } from '#application/types' import SpriteRepository from '#repositories/spriteRepository' +import { SpriteAction } from '#entities/spriteAction' +import sharp from 'sharp' type Payload = { id: UUID @@ -27,13 +29,35 @@ export default class SpriteUpdateEvent extends BaseEvent { const sprite = await spriteRepository.getById(data.id) if (!sprite) return callback(false) - await spriteRepository.getEntityManager().populate(sprite, ['spriteActions']); + await spriteRepository.getEntityManager().populate(sprite, ['spriteActions']) // Update sprite in database - await sprite - .setName(data.name) - .setSpriteActions(data.spriteActions) - .save() + await sprite.setName(data.name).save() + + const existingActions = sprite.getSpriteActions() + + // Remove all existing actions - we'll recreate the ones we need + for (const existingAction of existingActions) { + await spriteRepository.getEntityManager().removeAndFlush(existingAction) + } + + // Create new actions + for (const actionData of data.spriteActions) { + const spriteAction = new SpriteAction() + spriteAction.setSprite(sprite) + sprite.getSpriteActions().add(spriteAction) + + spriteAction + .setAction(actionData.action) + .setSprites(actionData.sprites) + .setOriginX(actionData.originX) + .setOriginY(actionData.originY) + .setFrameWidth(await this.calculateWidth(actionData.sprites[0])) + .setFrameHeight(await this.calculateHeight(actionData.sprites[0])) + .setFrameRate(actionData.frameRate) + + await spriteRepository.getEntityManager().persistAndFlush(spriteAction) + } return callback(true) } catch (error) { @@ -41,4 +65,28 @@ export default class SpriteUpdateEvent extends BaseEvent { return callback(false) } } -} \ No newline at end of file + + private async calculateWidth(base64: string): Promise { + const uri = base64.split(';base64,').pop() + if (!uri) return 0 + + const imgBuffer = Buffer.from(uri, 'base64') + const image = await sharp(imgBuffer).metadata() + return image.width ?? 0 + } + + private async calculateHeight(base64: string): Promise { + const uri = base64.split(';base64,').pop() + if (!uri) return 0 + + const imgBuffer = Buffer.from(uri, 'base64') + const image = await sharp(imgBuffer).metadata() + return image.height ?? 0 + } + + private generateSpriteSheet(sprites: string[]) { + // In here comes a function that generates a sprite sheet from the given sprites using Sharp. + // This function takes an array of base64 encoded sprites and generates a single png sprite sheet. + // Then proceeds to save ths sprite sheet to the public/sprites/{spriteId}/ directory. The file name is {action}.png. + } +} diff --git a/src/managers/weatherManager.ts b/src/managers/weatherManager.ts index e8440bc..72c4d12 100644 --- a/src/managers/weatherManager.ts +++ b/src/managers/weatherManager.ts @@ -39,7 +39,7 @@ class WeatherManager { return { ...this.weatherState } } - public randomWeatherValue(type: 'rain' | 'fog' ) { + public randomWeatherValue(type: 'rain' | 'fog') { switch (type) { case 'rain': return this.getRandomNumber(WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.min, WeatherManager.CONFIG.RAIN_PERCENTAGE_RANGE.max) @@ -48,7 +48,7 @@ class WeatherManager { } } - public async setRainValue(value : number | null): Promise { + public async setRainValue(value: number | null): Promise { if (value === null) { value = this.randomWeatherValue('rain') } @@ -57,7 +57,7 @@ class WeatherManager { await this.saveAndEmitWeather() } - public async setFogValue(value : number | null): Promise { + public async setFogValue(value: number | null): Promise { if (value === null) { value = this.randomWeatherValue('fog') } @@ -90,7 +90,7 @@ class WeatherManager { private updateRandomWeather(): void { if (Math.random() < WeatherManager.CONFIG.RAIN_CHANCE) { - this.updateWeatherProperty('rain', this.randomWeatherValue('rain') ) + this.updateWeatherProperty('rain', this.randomWeatherValue('rain')) } if (Math.random() < WeatherManager.CONFIG.FOG_CHANCE) { this.updateWeatherProperty('fog', this.randomWeatherValue('fog')) @@ -99,11 +99,11 @@ class WeatherManager { private updateWeatherProperty(type: 'rain' | 'fog', value: number): void { if (type === 'rain') { - this.weatherState.rainPercentage = value + this.weatherState.rainPercentage = value } if (type === 'fog') { - this.weatherState.fogDensity = value + this.weatherState.fogDensity = value } } @@ -124,10 +124,7 @@ class WeatherManager { if (!world) world = new World() //the data model still contains the booleans - await world - .setRainPercentage(this.weatherState.rainPercentage) - .setFogDensity(this.weatherState.fogDensity) - .save() + await world.setRainPercentage(this.weatherState.rainPercentage).setFogDensity(this.weatherState.fogDensity).save() } catch (error) { this.logError('save', error) }